public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH] genetlink: add multi-version family support for protocol negotiation
@ 2026-03-16 15:15 Christoph Böhmwalder
  2026-03-16 18:37 ` Jakub Kicinski
  0 siblings, 1 reply; 6+ messages in thread
From: Christoph Böhmwalder @ 2026-03-16 15:15 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Philipp Reisner, Christoph Böhmwalder, David S. Miller,
	Eric Dumazet, Paolo Abeni, Simon Horman, Alok Tiwari, Kees Cook,
	netdev, linux-kernel

Extend the genetlink infrastructure to allow families to advertise a
range of supported protocol versions [min_version, max_version]. This
enables a single kernel module to serve both old and new userspace by
letting userspace discover the maximum version via CTRL_ATTR_MAX_VERSION
and select the desired protocol dialect in genlmsghdr.version.

Rename genl_family.version to .min_version across all families (pure
rename, no behavioral change). This avoids updating all callers.

Add genl_family.max_version. When 0 (default) the family behaves
exactly as before. When > min_version, the controller advertises
CTRL_ATTR_MAX_VERSION in CTRL_CMD_GETFAMILY replies.

When sending a reply, echo the request's genlmsghdr.version, so
userspace receives responses in the protocol version it spoke.
Notifications (where info->nlhdr is NULL) continue to use min_version.

Add genlmsg_put_ver() for families that need to stamp a specific
version on notifications.

The immediate motivation is upstreaming DRBD 9 (genl family v1)
while keeping unmodified DRBD 8 userspace (family v2) working. The
DRBD family would register with min_version=1, max_version=2 and
handle both protocol dialects.

Signed-off-by: Christoph Böhmwalder <christoph.boehmwalder@linbit.com>
---
 include/net/genetlink.h        | 27 +++++++++++++++++------
 include/uapi/linux/genetlink.h |  1 +
 net/netlink/genetlink.c        | 39 ++++++++++++++++++++++++++++------
 3 files changed, 55 insertions(+), 12 deletions(-)

diff --git a/include/net/genetlink.h b/include/net/genetlink.h
index 7b84f2cef8b1..7e306bf8436a 100644
--- a/include/net/genetlink.h
+++ b/include/net/genetlink.h
@@ -38,7 +38,11 @@ struct genl_info;
  * struct genl_family - generic netlink family
  * @hdrsize: length of user specific header in bytes
  * @name: name of family
- * @version: protocol version
+ * @version: Only supported version (alias of min_version)
+ * @min_version: lowest protocol version supported
+ * @max_version: highest protocol version supported; when set and
+ *	greater than @min_version the family accepts versions
+ *	[@min_version, @max_version] from userspace
  * @maxattr: maximum number of attributes supported
  * @policy: netlink policy
  * @netnsok: set to true if the family can handle network
@@ -78,7 +82,11 @@ struct genl_info;
 struct genl_family {
 	unsigned int		hdrsize;
 	char			name[GENL_NAMSIZ];
-	unsigned int		version;
+	union {
+		unsigned int	version;
+		unsigned int	min_version;
+	};
+	unsigned int		max_version;
 	unsigned int		maxattr;
 	u8			netnsok:1;
 	u8			parallel_ops:1;
@@ -335,12 +343,19 @@ void genl_notify(const struct genl_family *family, struct sk_buff *skb,
 
 void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
 		  const struct genl_family *family, int flags, u8 cmd);
+void *genlmsg_put_ver(struct sk_buff *skb, u32 portid, u32 seq,
+		      const struct genl_family *family, int flags,
+		      u8 cmd, u8 version);
 
 static inline void *
 __genlmsg_iput(struct sk_buff *skb, const struct genl_info *info, int flags)
 {
-	return genlmsg_put(skb, info->snd_portid, info->snd_seq, info->family,
-			   flags, info->genlhdr->cmd);
+	u8 version = info->nlhdr ? info->genlhdr->version
+				 : info->family->version;
+
+	return genlmsg_put_ver(skb, info->snd_portid, info->snd_seq,
+			       info->family, flags, info->genlhdr->cmd,
+			       version);
 }
 
 /**
@@ -442,8 +457,8 @@ static inline void *genlmsg_put_reply(struct sk_buff *skb,
 				      const struct genl_family *family,
 				      int flags, u8 cmd)
 {
-	return genlmsg_put(skb, info->snd_portid, info->snd_seq, family,
-			   flags, cmd);
+	return genlmsg_put_ver(skb, info->snd_portid, info->snd_seq, family,
+			       flags, cmd, info->genlhdr->version);
 }
 
 /**
diff --git a/include/uapi/linux/genetlink.h b/include/uapi/linux/genetlink.h
index ddba3ca01e39..659aa31592ff 100644
--- a/include/uapi/linux/genetlink.h
+++ b/include/uapi/linux/genetlink.h
@@ -66,6 +66,7 @@ enum {
 	CTRL_ATTR_POLICY,
 	CTRL_ATTR_OP_POLICY,
 	CTRL_ATTR_OP,
+	CTRL_ATTR_MAX_VERSION,
 	__CTRL_ATTR_MAX,
 };
 
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index a23d4c51c089..50ba0c99f648 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -880,18 +880,24 @@ int genl_unregister_family(const struct genl_family *family)
 EXPORT_SYMBOL(genl_unregister_family);
 
 /**
- * genlmsg_put - Add generic netlink header to netlink message
+ * genlmsg_put_ver - Add generic netlink header with explicit version
  * @skb: socket buffer holding the message
  * @portid: netlink portid the message is addressed to
  * @seq: sequence number (usually the one of the sender)
  * @family: generic netlink family
  * @flags: netlink message flags
  * @cmd: generic netlink command
+ * @version: protocol version to stamp on the message
  *
- * Returns pointer to user specific header
+ * Like genlmsg_put() but allows the caller to specify the version field
+ * in the genetlink header.  Useful for families that support multiple
+ * protocol versions and need to send notifications in a specific version.
+ *
+ * Returns: pointer to user specific header, or %NULL on failure.
  */
-void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
-		  const struct genl_family *family, int flags, u8 cmd)
+void *genlmsg_put_ver(struct sk_buff *skb, u32 portid, u32 seq,
+		      const struct genl_family *family, int flags,
+		      u8 cmd, u8 version)
 {
 	struct nlmsghdr *nlh;
 	struct genlmsghdr *hdr;
@@ -903,11 +909,30 @@ void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
 
 	hdr = nlmsg_data(nlh);
 	hdr->cmd = cmd;
-	hdr->version = family->version;
+	hdr->version = version;
 	hdr->reserved = 0;
 
 	return (char *) hdr + GENL_HDRLEN;
 }
+EXPORT_SYMBOL(genlmsg_put_ver);
+
+/**
+ * genlmsg_put - Add generic netlink header to netlink message
+ * @skb: socket buffer holding the message
+ * @portid: netlink portid the message is addressed to
+ * @seq: sequence number (usually the one of the sender)
+ * @family: generic netlink family
+ * @flags: netlink message flags
+ * @cmd: generic netlink command
+ *
+ * Returns pointer to user specific header
+ */
+void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
+		  const struct genl_family *family, int flags, u8 cmd)
+{
+	return genlmsg_put_ver(skb, portid, seq, family, flags, cmd,
+			       family->version);
+}
 EXPORT_SYMBOL(genlmsg_put);
 
 static struct genl_dumpit_info *genl_dumpit_info_alloc(void)
@@ -1237,7 +1262,9 @@ static int ctrl_fill_info(const struct genl_family *family, u32 portid, u32 seq,
 
 	if (nla_put_string(skb, CTRL_ATTR_FAMILY_NAME, family->name) ||
 	    nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, family->id) ||
-	    nla_put_u32(skb, CTRL_ATTR_VERSION, family->version) ||
+	    nla_put_u32(skb, CTRL_ATTR_VERSION, family->min_version) ||
+	    (family->max_version > family->min_version &&
+	     nla_put_u32(skb, CTRL_ATTR_MAX_VERSION, family->max_version)) ||
 	    nla_put_u32(skb, CTRL_ATTR_HDRSIZE, family->hdrsize) ||
 	    nla_put_u32(skb, CTRL_ATTR_MAXATTR, family->maxattr))
 		goto nla_put_failure;

base-commit: f338e77383789c0cae23ca3d48adcc5e9e137e3c
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-03-18 22:27 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-16 15:15 [RFC PATCH] genetlink: add multi-version family support for protocol negotiation Christoph Böhmwalder
2026-03-16 18:37 ` Jakub Kicinski
2026-03-17 15:47   ` Christoph Böhmwalder
2026-03-17 22:17     ` Jakub Kicinski
2026-03-18 14:54       ` Christoph Böhmwalder
2026-03-18 22:27         ` Jakub Kicinski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox