From: Max Kellermann <max.kellermann@ionos.com>
To: idryomov@gmail.com, amarkuze@redhat.com, xiubo.li@clyso.com,
ceph-devel@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: Max Kellermann <max.kellermann@ionos.com>
Subject: [PATCH v4] net/ceph/messenger: support IPv6 Link-Local with scope identifier
Date: Wed, 12 Aug 2026 23:36:57 +0200 [thread overview]
Message-ID: <20260812213657.1821175-1-max.kellermann@ionos.com> (raw)
Implement ceph_pton() using inet_pton_with_scope(). This minimal
patch preserves the ceph_pton() API and thus requires copying the
parameter to a null-terminated string.
Since struct sockaddr_storage is part of the Ceph wire protocol and a
non-zero scope id can break the memcmp() checks in the banner
handlers, we need to clear all scope ids before using memcmp().
Therefore, this patch introduces the helper function
ceph_addr_equal_no_scope().
Signed-off-by: Max Kellermann <max.kellermann@ionos.com>
---
v1->v2:
- check "ipend" before dereferencing it
v2->v3:
- fix indentation
- use the caller's network namespace instead of init_ns
- stop parsing at the port separator
v3->v4:
- fall back to DNS only on EINVAL
- use GFP_KERNEL instead of GFP_NOFS
- ignore the IPv6 scope id when checking the banner peer address
Signed-off-by: Max Kellermann <max.kellermann@ionos.com>
---
include/linux/ceph/messenger.h | 6 ++++
net/ceph/messenger.c | 64 ++++++++++++++++++++++++++++------
net/ceph/messenger_v1.c | 3 +-
net/ceph/messenger_v2.c | 2 +-
4 files changed, 62 insertions(+), 13 deletions(-)
diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h
index 6aa4c6478c9f..ab56a04993d0 100644
--- a/include/linux/ceph/messenger.h
+++ b/include/linux/ceph/messenger.h
@@ -570,6 +570,12 @@ bool ceph_con_v2_opened(struct ceph_connection *con);
void ceph_con_v2_reset_session(struct ceph_connection *con);
void ceph_con_v2_reset_protocol(struct ceph_connection *con);
+/**
+ * Check whether the two addresses are equal, but ignore different
+ * IPv6 scope identifiers.
+ */
+bool ceph_addr_equal_no_scope(const struct ceph_entity_addr *lhs,
+ const struct ceph_entity_addr *rhs);
extern const char *ceph_pr_addr(const struct ceph_entity_addr *addr);
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index 34b3097b4c7b..c2a18f50bc92 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -161,6 +161,23 @@ static atomic_t addr_str_seq = ATOMIC_INIT(0);
struct page *ceph_zero_page; /* used in certain error cases */
+static void ceph_addr_clear_scope(const struct ceph_entity_addr *addr)
+{
+ if (addr->in_addr.ss_family == AF_INET6) {
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr->in_addr;
+ in6->sin6_scope_id = 0;
+ }
+}
+
+bool ceph_addr_equal_no_scope(const struct ceph_entity_addr *lhs,
+ const struct ceph_entity_addr *rhs)
+{
+ struct ceph_entity_addr lhs_copy = *lhs, rhs_copy = *rhs;
+ ceph_addr_clear_scope(&lhs_copy);
+ ceph_addr_clear_scope(&rhs_copy);
+ return memcmp(&lhs_copy, &rhs_copy, sizeof(lhs_copy)) == 0;
+}
+
const char *ceph_pr_addr(const struct ceph_entity_addr *addr)
{
int i;
@@ -1221,19 +1238,46 @@ void ceph_addr_set_port(struct ceph_entity_addr *addr, int p)
static int ceph_pton(const char *str, size_t len, struct ceph_entity_addr *addr,
char delim, const char **ipend)
{
- memset(&addr->in_addr, 0, sizeof(addr->in_addr));
+ const char *delim_p;
+ char *copy;
+ struct sockaddr_storage stor = { 0 };
+ int ret;
- if (in4_pton(str, len, (u8 *)&((struct sockaddr_in *)&addr->in_addr)->sin_addr.s_addr, delim, ipend)) {
- put_unaligned(AF_INET, &addr->in_addr.ss_family);
- return 0;
- }
+ delim_p = memchr(str, delim, len);
+ if (delim_p)
+ /* delimiter was found - stop parsing there */
+ len = delim_p - str;
- if (in6_pton(str, len, (u8 *)&((struct sockaddr_in6 *)&addr->in_addr)->sin6_addr.s6_addr, delim, ipend)) {
- put_unaligned(AF_INET6, &addr->in_addr.ss_family);
- return 0;
+ delim_p = memchr(str, ':', len);
+ if (delim_p != NULL && memchr(delim_p + 1, ':', str + len - delim_p - 1) == NULL)
+ /* a single colon was found - stop parsing there
+ * because the caller wants to parse the port
+ * number
+ */
+ len = delim_p - str;
+
+ /* the input string might not be null terminated, so copy it
+ * to a null-terminated string
+ */
+ copy = kstrndup(str, len, GFP_KERNEL);
+ if (!copy)
+ return -ENOMEM;
+
+ ret = inet_pton_with_scope(current->nsproxy->net_ns, AF_UNSPEC, copy, NULL, &stor);
+ kfree(copy);
+
+ if (!ret) {
+ /* ceph_entity_addr might be misaligned, so we have to
+ * parse to a stack variable first
+ */
+ memcpy(&addr->in_addr, &stor, sizeof(stor));
+
+ /* return the end of the parsed portion to the caller on success */
+ if (ipend)
+ *ipend = str + len;
}
- return -EINVAL;
+ return ret;
}
/*
@@ -1302,7 +1346,7 @@ static int ceph_parse_server_name(const char *name, size_t namelen,
int ret;
ret = ceph_pton(name, namelen, addr, delim, ipend);
- if (ret)
+ if (ret == -EINVAL)
ret = ceph_dns_resolve_name(name, namelen, addr, delim, ipend);
return ret;
diff --git a/net/ceph/messenger_v1.c b/net/ceph/messenger_v1.c
index c9e002d96319..309bca24b82f 100644
--- a/net/ceph/messenger_v1.c
+++ b/net/ceph/messenger_v1.c
@@ -708,8 +708,7 @@ static int process_banner(struct ceph_connection *con)
* end may not yet know their ip address, so if it's 0.0.0.0, give
* them the benefit of the doubt.
*/
- if (memcmp(&con->peer_addr, &con->v1.actual_peer_addr,
- sizeof(con->peer_addr)) != 0 &&
+ if (!ceph_addr_equal_no_scope(&con->peer_addr, &con->v1.actual_peer_addr) &&
!(ceph_addr_is_blank(&con->v1.actual_peer_addr) &&
con->v1.actual_peer_addr.nonce == con->peer_addr.nonce)) {
pr_warn("wrong peer, want %s/%u, got %s/%u\n",
diff --git a/net/ceph/messenger_v2.c b/net/ceph/messenger_v2.c
index 05f6eea299fc..dbcf20d623a7 100644
--- a/net/ceph/messenger_v2.c
+++ b/net/ceph/messenger_v2.c
@@ -2504,7 +2504,7 @@ static int process_server_ident(struct ceph_connection *con,
global_id, global_seq, features, required_features, flags, cookie);
/* is this who we intended to talk to? */
- if (memcmp(&addr, &con->peer_addr, sizeof(con->peer_addr))) {
+ if (!ceph_addr_equal_no_scope(&addr, &con->peer_addr)) {
pr_err("bad peer addr/nonce, want %s/%u, got %s/%u\n",
ceph_pr_addr(&con->peer_addr),
le32_to_cpu(con->peer_addr.nonce),
--
2.47.3
reply other threads:[~2026-08-12 21:37 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260812213657.1821175-1-max.kellermann@ionos.com \
--to=max.kellermann@ionos.com \
--cc=amarkuze@redhat.com \
--cc=ceph-devel@vger.kernel.org \
--cc=idryomov@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=xiubo.li@clyso.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.