From nobody Tue Apr 7 02:59:23 2026 Received: from mail-wm1-f47.google.com (mail-wm1-f47.google.com [209.85.128.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 615E93A1D01 for ; Mon, 16 Mar 2026 15:16:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773674163; cv=none; b=Sf+Nn4Jrsubgt1OLJgD4zpiXsVHrdNBX3FaXzL0loAG1HlO1MYK5fLrcidEmjTGzxcjO1RaKD7OHS936zYQlAVj57vE69oxGAAmMtvDVLvDf5iRdofOKla4cp9q3GJ6TqQ62XTbpXpuMkad1TvqPu3nnTbEMZVKvwsqe1pDJvKU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773674163; c=relaxed/simple; bh=mPv1alicbNfO5ZT97r+vW90N+4T9V37S+/8vB5Ju2lA=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version:Content-Type; b=JB6RbP1qBOkd7idE5GXlu1k7W915iDDpbrD2/3vGW/QCkl1PHsPYanxKBrZ7oEBONOUoAjq52xC9PJ2bZ252fH5V71ZLaJITS18c3zc60PmQTuPhavc1IKu0aGbUGnzAC3CH2B+g45RBFumAHePSazFZl/3p3UHtmri1apB2lkQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linbit.com; spf=pass smtp.mailfrom=linbit.com; dkim=pass (2048-bit key) header.d=linbit-com.20230601.gappssmtp.com header.i=@linbit-com.20230601.gappssmtp.com header.b=GFU3sjQH; arc=none smtp.client-ip=209.85.128.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linbit.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linbit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linbit-com.20230601.gappssmtp.com header.i=@linbit-com.20230601.gappssmtp.com header.b="GFU3sjQH" Received: by mail-wm1-f47.google.com with SMTP id 5b1f17b1804b1-4855dbfc129so22736275e9.0 for ; Mon, 16 Mar 2026 08:16:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linbit-com.20230601.gappssmtp.com; s=20230601; t=1773674159; x=1774278959; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=gmUzIWhvYQPmLKIFrKQ+CmNW3KoiMo1xA15Z136wUAM=; b=GFU3sjQHFKJzcqlSdDtP6B8voW53yHHz4+riZoadtnhfGCUsMZVonHWE1xCZYGOiyP cEMjKCzCs2tppcNRRsb0SaRp1PdtKGbRCc0avxAEbWidsJHw2Njbt8V4gINOZDnpGlpi V4/WPB4omsBcL+q02+1VGM0hBG/9Q1qz0D67sPQ4wsUGWIwJVzyTtm/r9FN1rt0NOKvE IORVvKuP8b9aeob2urBQfgRyfo/cchw1qh1K46VypM/08X8BNcKoCCAeGe4q7hA/iYJ6 3XihWK6AjR9UgyQt/3uPhsvz2+GyghCQehKVCkIkN7tlA50jyhsbJb2saqrBGqPSbMkn ACPw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773674159; x=1774278959; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=gmUzIWhvYQPmLKIFrKQ+CmNW3KoiMo1xA15Z136wUAM=; b=dP6Tsw5ojcQHvrKrlYNejPuJkBYO/kI4KBHYyygiPPTwfBk7r+yI79GOpQjtfrUimf OrQ1WQRR0odxNwG0iF8Rmmzz4kji6GvNmqtyXcy/ZN3YOy2mh7WnkKEIBaC2s6Vh5R/d YUoVO2AiABTZEcbOZXYRlSgjfEOlC5xAAXkb56ycHb3jLgbl/RUwuJjVQlBkpeV3fchK MD/u4WclXL7hh4Z3GZJ2yzOUTKrqi1mc8kHqfem6kvzXGnXvO9xa0OfWjTeFsFyR23xi P4OWY0a5zxH27CPSLyK5ye2NsYeXk66BW7sJBHpKrKwqaamO4siPIeorVyTaGXa+Cop2 JW3w== X-Forwarded-Encrypted: i=1; AJvYcCVO1uKKRzGfxjG+VkUSseQK+9HZH6YQsIkR3zfx2KeEGTSI/bvMPnQdycA1lvWxiNLj6Hhh54DMtZNyhIk=@vger.kernel.org X-Gm-Message-State: AOJu0Yywd9Mb5wpPcJ6KSRCakFvAtNmXiRtels8iCIoGAYLF7g5Eo/+i GuV+nRRBSFN9jcVZd8DKvQbfA5sQq+1PzW/gIRZ8UlaV9fSMk5HEobLPdc6n8PQanSY= X-Gm-Gg: ATEYQzwK09hBL0XeCp26L/rou0i9oUN4/lQtc8eH4kV4biSyJop9aC8VPw9rCTjfa83 C6ilV3vK5eHzSD1EUmD2v1pY+ikydkpev+3NziW2IOiaBG9Uhw9YxYlt6dpAWglQ0qnhFc1RIjP RIrXalcIh6mFMcQzlZUTnjQIuVpbEObTW3Lhm5tTetA9COPhjD6jMBZ3ptA8vADcg/Air/Z7U6W 9YQPPJsNXb9eeDMdlxV9uNVKtqA/zEN4PLne+C1Lzk4gNzdAggg/DLXEORnLSndnKSKyyggNIEF FO3Br2qY822FUjIvaEL3EPPrLJ41d7q76L1wGtO0BDkzUE5OJalHcv+BV9WcMHFyq4QQWqvP7lb E9hzDfxumAwvJRxJAL5qPSk5H6dUJDLpM7FtlgMbpFrCKK9ZnCYG3ieWDzf0ptvS6n4LQOZxnD1 jPtQgLmeJ39e1JpC5XqDp8tsA+PeOBSKuhz06qEzp+8TqFdj4+yoaiS81EsagVmzSF5B20JWYZi +XGsp8m9YJfNoviIqIHKw== X-Received: by 2002:a05:600c:3b14:b0:485:17a7:b9c7 with SMTP id 5b1f17b1804b1-485566d5178mr225381115e9.10.1773674158634; Mon, 16 Mar 2026 08:15:58 -0700 (PDT) Received: from localhost.localdomain (h082218028181.host.wavenet.at. [82.218.28.181]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-439fe20bd9csm42594654f8f.21.2026.03.16.08.15.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Mar 2026 08:15:58 -0700 (PDT) From: =?UTF-8?q?Christoph=20B=C3=B6hmwalder?= To: Jakub Kicinski Cc: Philipp Reisner , =?UTF-8?q?Christoph=20B=C3=B6hmwalder?= , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Alok Tiwari , Kees Cook , netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH] genetlink: add multi-version family support for protocol negotiation Date: Mon, 16 Mar 2026 16:15:06 +0100 Message-ID: <20260316151507.3958299-2-christoph.boehmwalder@linbit.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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=3D1, max_version=3D2 and handle both protocol dialects. Signed-off-by: Christoph B=C3=B6hmwalder --- 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, st= ruct sk_buff *skb, =20 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); =20 static inline void * __genlmsg_iput(struct sk_buff *skb, const struct genl_info *info, int flag= s) { - return genlmsg_put(skb, info->snd_portid, info->snd_seq, info->family, - flags, info->genlhdr->cmd); + u8 version =3D 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); } =20 /** @@ -442,8 +457,8 @@ static inline void *genlmsg_put_reply(struct sk_buff *s= kb, 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); } =20 /** 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, }; =20 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); =20 /** - * 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, u3= 2 seq, =20 hdr =3D nlmsg_data(nlh); hdr->cmd =3D cmd; - hdr->version =3D family->version; + hdr->version =3D version; hdr->reserved =3D 0; =20 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); =20 static struct genl_dumpit_info *genl_dumpit_info_alloc(void) @@ -1237,7 +1262,9 @@ static int ctrl_fill_info(const struct genl_family *f= amily, u32 portid, u32 seq, =20 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 --=20 2.53.0