* [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
* Re: [RFC PATCH] genetlink: add multi-version family support for protocol negotiation
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
0 siblings, 1 reply; 6+ messages in thread
From: Jakub Kicinski @ 2026-03-16 18:37 UTC (permalink / raw)
To: Christoph Böhmwalder
Cc: Philipp Reisner, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Alok Tiwari, Kees Cook, netdev, linux-kernel
On Mon, 16 Mar 2026 16:15:06 +0100 Christoph Böhmwalder wrote:
> 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.
What's the delta between the families? Do you have any examples of
the the version ends up being used?
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [RFC PATCH] genetlink: add multi-version family support for protocol negotiation
2026-03-16 18:37 ` Jakub Kicinski
@ 2026-03-17 15:47 ` Christoph Böhmwalder
2026-03-17 22:17 ` Jakub Kicinski
0 siblings, 1 reply; 6+ messages in thread
From: Christoph Böhmwalder @ 2026-03-17 15:47 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Philipp Reisner, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Alok Tiwari, Kees Cook, netdev, linux-kernel
Am 16.03.26 um 19:37 schrieb Jakub Kicinski:
> What's the delta between the families? Do you have any examples of
> the the version ends up being used?
The main difference between DRBD 8 and 9 in general is that 8 only
replicates to a single peer, while 9 has support for multiple peers.
This also introduces major changes in the genl family. For example, the
"connect" command works completely differently between the v1 and v2
families.
In v1, it takes all the net_conf parameters and immediately sets up the
connection.
In v2, you need a new_peer / new_path command first; the net_conf is
passed to new_peer in this case. The connect command then only
"activates" the connection.
So the semantics are completely different.
Other commands (like "state_info") were removed entirely and split up
into different commands.
The specific exact diff is available as part of our prototype DRBD 9
branch (included in linux-next):
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/diff/include/linux/drbd_genl.h?id=11b8887a3efae2868037f2bd8dbbc68a8591f66c
As for the usage: the plan is for drbd to register its family with
min_version=1, max_version=2. Then we would dispatch the v2 commands
normally, and the v1 commands through a thin compat layer.
In the in-tree DRBD, the version is currently unused. In the (still
out-of-tree) DRBD 9 though, we do check that the version is what we expect:
https://github.com/LINBIT/drbd/blob/master/drbd/drbd_nl.c#L426
This would be replaced by the mentioned compat dispatch logic once we
have in-kernel infrastructure for supporting multiple versions.
Best Regards,
Christoph
--
Christoph Böhmwalder
LINBIT | Keeping the Digital World Running
DRBD HA — Disaster Recovery — Software defined Storage
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [RFC PATCH] genetlink: add multi-version family support for protocol negotiation
2026-03-17 15:47 ` Christoph Böhmwalder
@ 2026-03-17 22:17 ` Jakub Kicinski
2026-03-18 14:54 ` Christoph Böhmwalder
0 siblings, 1 reply; 6+ messages in thread
From: Jakub Kicinski @ 2026-03-17 22:17 UTC (permalink / raw)
To: Christoph Böhmwalder
Cc: Philipp Reisner, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Alok Tiwari, Kees Cook, netdev, linux-kernel
On Tue, 17 Mar 2026 16:47:44 +0100 Christoph Böhmwalder wrote:
> Am 16.03.26 um 19:37 schrieb Jakub Kicinski:
> > What's the delta between the families? Do you have any examples of
> > the the version ends up being used?
>
> The main difference between DRBD 8 and 9 in general is that 8 only
> replicates to a single peer, while 9 has support for multiple peers.
> This also introduces major changes in the genl family. For example, the
> "connect" command works completely differently between the v1 and v2
> families.
> In v1, it takes all the net_conf parameters and immediately sets up the
> connection.
> In v2, you need a new_peer / new_path command first; the net_conf is
> passed to new_peer in this case. The connect command then only
> "activates" the connection.
> So the semantics are completely different.
>
> Other commands (like "state_info") were removed entirely and split up
> into different commands.
>
> The specific exact diff is available as part of our prototype DRBD 9
> branch (included in linux-next):
> https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/diff/include/linux/drbd_genl.h?id=11b8887a3efae2868037f2bd8dbbc68a8591f66c
This uAPI header wrapped in DRDB magic tells me nothing.
The macro wrapping that DRBD does never gained broader adoption and
should be considered deprecated. Please take a look at YNL and the YAML
specs. (I mean this as a nack).
> As for the usage: the plan is for drbd to register its family with
> min_version=1, max_version=2. Then we would dispatch the v2 commands
> normally, and the v1 commands through a thin compat layer.
> In the in-tree DRBD, the version is currently unused. In the (still
> out-of-tree) DRBD 9 though, we do check that the version is what we expect:
> https://github.com/LINBIT/drbd/blob/master/drbd/drbd_nl.c#L426
>
> This would be replaced by the mentioned compat dispatch logic once we
> have in-kernel infrastructure for supporting multiple versions.
I still feel a little bit in the dark as to what you're doing but the
normal path for Netlink is to add new attributes and commands in a
backward-compat manner. In case of DRBD given how "special" the
existing code is we could probably start a new family using modern
constructs.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [RFC PATCH] genetlink: add multi-version family support for protocol negotiation
2026-03-17 22:17 ` Jakub Kicinski
@ 2026-03-18 14:54 ` Christoph Böhmwalder
2026-03-18 22:27 ` Jakub Kicinski
0 siblings, 1 reply; 6+ messages in thread
From: Christoph Böhmwalder @ 2026-03-18 14:54 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Philipp Reisner, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Alok Tiwari, Kees Cook, netdev, linux-kernel
Am 17.03.26 um 23:17 schrieb Jakub Kicinski:
> On Tue, 17 Mar 2026 16:47:44 +0100 Christoph Böhmwalder wrote:
>> Am 16.03.26 um 19:37 schrieb Jakub Kicinski:
>>> What's the delta between the families? Do you have any examples of
>>> the the version ends up being used?
>>
>> The main difference between DRBD 8 and 9 in general is that 8 only
>> replicates to a single peer, while 9 has support for multiple peers.
>> This also introduces major changes in the genl family. For example, the
>> "connect" command works completely differently between the v1 and v2
>> families.
>> In v1, it takes all the net_conf parameters and immediately sets up the
>> connection.
>> In v2, you need a new_peer / new_path command first; the net_conf is
>> passed to new_peer in this case. The connect command then only
>> "activates" the connection.
>> So the semantics are completely different.
>>
>> Other commands (like "state_info") were removed entirely and split up
>> into different commands.
>>
>> The specific exact diff is available as part of our prototype DRBD 9
>> branch (included in linux-next):
>> https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/diff/include/linux/drbd_genl.h?id=11b8887a3efae2868037f2bd8dbbc68a8591f66c
>
> This uAPI header wrapped in DRDB magic tells me nothing.
>
> The macro wrapping that DRBD does never gained broader adoption and
> should be considered deprecated. Please take a look at YNL and the YAML
> specs. (I mean this as a nack).
We are actually working on exactly that as part of our upstreaming
efforts (implementing YNL-based generation of the genl headers for
DRBD). So the macro magic will be removed soon.
>> As for the usage: the plan is for drbd to register its family with
>> min_version=1, max_version=2. Then we would dispatch the v2 commands
>> normally, and the v1 commands through a thin compat layer.
>> In the in-tree DRBD, the version is currently unused. In the (still
>> out-of-tree) DRBD 9 though, we do check that the version is what we expect:
>> https://github.com/LINBIT/drbd/blob/master/drbd/drbd_nl.c#L426
>>
>> This would be replaced by the mentioned compat dispatch logic once we
>> have in-kernel infrastructure for supporting multiple versions.
>
> I still feel a little bit in the dark as to what you're doing but the
> normal path for Netlink is to add new attributes and commands in a
> backward-compat manner. In case of DRBD given how "special" the
> existing code is we could probably start a new family using modern
> constructs.
The new family indeed sounds like a way out. So we would register two
families: the old "drbd" for the compat case, and a new "drbd2" with the
new command set. Would that be more in line with the "standard approach"?
Thanks,
Christoph
--
Christoph Böhmwalder
LINBIT | Keeping the Digital World Running
DRBD HA — Disaster Recovery — Software defined Storage
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [RFC PATCH] genetlink: add multi-version family support for protocol negotiation
2026-03-18 14:54 ` Christoph Böhmwalder
@ 2026-03-18 22:27 ` Jakub Kicinski
0 siblings, 0 replies; 6+ messages in thread
From: Jakub Kicinski @ 2026-03-18 22:27 UTC (permalink / raw)
To: Christoph Böhmwalder
Cc: Philipp Reisner, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Alok Tiwari, Kees Cook, netdev, linux-kernel
On Wed, 18 Mar 2026 15:54:04 +0100 Christoph Böhmwalder wrote:
> > I still feel a little bit in the dark as to what you're doing but the
> > normal path for Netlink is to add new attributes and commands in a
> > backward-compat manner. In case of DRBD given how "special" the
> > existing code is we could probably start a new family using modern
> > constructs.
>
> The new family indeed sounds like a way out. So we would register two
> families: the old "drbd" for the compat case, and a new "drbd2" with the
> new command set. Would that be more in line with the "standard approach"?
If we really can't keep backward compatibility then new family sounds
like the next best thing.
^ permalink raw reply [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