From nobody Fri Sep 25 00:03:39 2026 Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 84BEF4E3EF8; Fri, 18 Sep 2026 15:35:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=94.136.29.106 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789745758; cv=none; b=dRp4CAgbrk+M6qJNp6IprjS9+WfaTvKqIFSNUmzMkDC7Z7ktJzAVQlUhLE6KKUMiFaucbLxAVPTrYUuDVA7R9wt43f4ejXp48YyWqrcHBS1er9+HfZJb28Es44exatoQ4wmba3BmtEPSRcCtGGLMm/yyHGo9SWMAlntnO8t0HtY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789745758; c=relaxed/simple; bh=arNIToacKElOQahNOt9zlouzS+KB1Q9mT0MiOCiuiK4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VVFUZqkLhlVSbR8UNx08uqo3P0dE50c2dbRe6HlubuLYZM4K5POPLtft2HsmPqbeNURsRHQBQCi113FiCzdA0kWlTjvk2JatyACBctgVzHjeu6rr1rCPfWAm76jCUxSERjfLjq+tCWvqTFygXgKCRPAbs22ZvQBGrefTgvs5LXc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=proxmox.com; spf=pass smtp.mailfrom=proxmox.com; arc=none smtp.client-ip=94.136.29.106 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=proxmox.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=proxmox.com Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 7F23F4771C; Fri, 18 Sep 2026 17:35:52 +0200 (CEST) From: Gabriel Goller To: Andrea Mayer , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH net-next 1/2] net: ipv6: seg6: report lwtunnel setup errors via extack Date: Fri, 18 Sep 2026 17:35:15 +0200 Message-ID: <20260918153544.1178884-2-g.goller@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260918153544.1178884-1-g.goller@proxmox.com> References: <20260918153544.1178884-1-g.goller@proxmox.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1789745751756 Content-Type: text/plain; charset="utf-8" seg6_build_state() rejected invalid configurations with a generic -EINVAL, so "ip route add ... encap seg6 ..." always reported "Invalid argument". Attach an extack message to each of them. The checks themselves and their return values are unchanged. Signed-off-by: Gabriel Goller --- net/ipv6/seg6_iptunnel.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index 61c6a27bf202..e7f75970e26e 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -756,8 +756,12 @@ static int seg6_build_state(struct net *net, struct nl= attr *nla, struct seg6_lwt *slwt; int err; =20 - if (family !=3D AF_INET && family !=3D AF_INET6) + if (family !=3D AF_INET && family !=3D AF_INET6) { + NL_SET_ERR_MSG( + extack, + "unsupported address family for SRv6 encapsulation"); return -EINVAL; + } =20 err =3D nla_parse_nested_deprecated(tb, SEG6_IPTUNNEL_MAX, nla, seg6_iptunnel_policy, extack); @@ -765,8 +769,10 @@ static int seg6_build_state(struct net *net, struct nl= attr *nla, if (err < 0) return err; =20 - if (!tb[SEG6_IPTUNNEL_SRH]) + if (!tb[SEG6_IPTUNNEL_SRH]) { + NL_SET_ERR_MSG(extack, "missing SRv6 SRH attribute"); return -EINVAL; + } =20 tuninfo =3D nla_data(tb[SEG6_IPTUNNEL_SRH]); tuninfo_len =3D nla_len(tb[SEG6_IPTUNNEL_SRH]); @@ -776,13 +782,18 @@ static int seg6_build_state(struct net *net, struct n= lattr *nla, */ min_size =3D sizeof(*tuninfo) + sizeof(struct ipv6_sr_hdr) + sizeof(struct in6_addr); - if (tuninfo_len < min_size) + if (tuninfo_len < min_size) { + NL_SET_ERR_MSG(extack, "truncated SRv6 SRH attribute"); return -EINVAL; + } =20 switch (tuninfo->mode) { case SEG6_IPTUN_MODE_INLINE: - if (family !=3D AF_INET6) + if (family !=3D AF_INET6) { + NL_SET_ERR_MSG(extack, + "inline mode requires an IPv6 route"); return -EINVAL; + } =20 if (tb[SEG6_IPTUNNEL_SRC]) { NL_SET_ERR_MSG(extack, "incompatible mode for tunsrc"); @@ -798,12 +809,16 @@ static int seg6_build_state(struct net *net, struct n= lattr *nla, case SEG6_IPTUN_MODE_L2ENCAP_RED: break; default: + NL_SET_ERR_MSG(extack, "invalid SRv6 encapsulation mode"); return -EINVAL; } =20 /* verify that SRH is consistent */ - if (!seg6_validate_srh(tuninfo->srh, tuninfo_len - sizeof(*tuninfo), fals= e)) + if (!seg6_validate_srh(tuninfo->srh, tuninfo_len - sizeof(*tuninfo), + false)) { + NL_SET_ERR_MSG(extack, "invalid SRv6 segment routing header"); return -EINVAL; + } =20 newts =3D lwtunnel_state_alloc(tuninfo_len + sizeof(*slwt)); if (!newts) --=20 2.47.3 From nobody Fri Sep 25 00:03:39 2026 Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8A2054F93AE; Fri, 18 Sep 2026 15:35:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=94.136.29.106 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789745758; cv=none; b=gUxMNNQysMOoiKeOSOaCaDTFriZV5SpyajyIyNZPPHoC1hyx2L6XkIbweIjbGZEE9LaWxeGU1V5CcPACNHQgeu9+Z9Iv1CMAHmcMm+IPI35hjSWa4r1Ujm1Bm278mTRbkIBL4CyeAsBu+DwVe72b0vRdbBFtZA6xq/yekGiuDx0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789745758; c=relaxed/simple; bh=cpIBgXULYb03HMA48vKKBNv/NqLBKIiN2QOCfiRgYDk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rXmEGnASyIXlrM1I/cklgvWiLBjcKswy6Zcb+OHMTbwmczolV4INn+B+MG61e/PUAb75VXV3Sovbo5Th0Ih+VAUX6LNuVgPF3wzJ38oQg7gG9gyenWBLQXf5itGL7T0HbWw3MXZG/s/pCQqp/1+5tkX+C3qOqTm1vn7lkjKdhD8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=proxmox.com; spf=pass smtp.mailfrom=proxmox.com; arc=none smtp.client-ip=94.136.29.106 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=proxmox.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=proxmox.com Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 5E0564772C; Fri, 18 Sep 2026 17:35:53 +0200 (CEST) From: Gabriel Goller To: Andrea Mayer , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Shuah Khan Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH net-next 2/2] selftests: seg6: check extack messages on SRv6 lwtunnel setup Date: Fri, 18 Sep 2026 17:35:16 +0200 Message-ID: <20260918153544.1178884-3-g.goller@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260918153544.1178884-1-g.goller@proxmox.com> References: <20260918153544.1178884-1-g.goller@proxmox.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1789745751870 Content-Type: text/plain; charset="utf-8" Add a ksft/ynl test that shoves invalid RTA_ENCAP payloads to RTM_NEWROUTE and asserts both the errno and the extack message are correct.Also verify that the rejected route was not installed. Signed-off-by: Gabriel Goller --- tools/testing/selftests/net/Makefile | 1 + .../selftests/net/srv6_iptunnel_extack.py | 168 ++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100755 tools/testing/selftests/net/srv6_iptunnel_extack.py diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests= /net/Makefile index 3ee3378f8b26..45639f46b003 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -101,6 +101,7 @@ TEST_PROGS :=3D \ srv6_hencap_red_l3vpn_test.sh \ srv6_hl2encap_red_l2vpn_test.sh \ srv6_iptunnel_cache.sh \ + srv6_iptunnel_extack.py \ stress_reuseport_listen.sh \ tcp_ecmp_failover.sh \ tcp_fastopen_backup_key.sh \ diff --git a/tools/testing/selftests/net/srv6_iptunnel_extack.py b/tools/te= sting/selftests/net/srv6_iptunnel_extack.py new file mode 100755 index 000000000000..77e708a08c3e --- /dev/null +++ b/tools/testing/selftests/net/srv6_iptunnel_extack.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# +# author: Gabriel Goller + +"""Exercise SRv6 tunnel validation with invalid netlink attributes.""" + +import errno +import os +import socket +import struct +from contextlib import contextmanager + +from lib.py import KsftNamedVariant, KsftSkipEx, Netlink, NetNS, NetNSEnter +from lib.py import NlError, RtnlRouteFamily, ip, ksft_eq, ksft_exit, ksft_= raises +from lib.py import ksft_run, ksft_variants + + +LWTUNNEL_ENCAP_SEG6 =3D 5 +SEG6_IPTUNNEL_SRH =3D 1 +SEG6_IPTUNNEL_SRC =3D 2 +SEG6_IPTUNNEL_TABLE =3D 3 +SEG6_IPTUN_MODE_INLINE =3D 0 +SEG6_IPTUN_MODE_ENCAP =3D 1 +SEG6_IPTUN_MODE_L2ENCAP =3D 2 +SEG6_IPTUN_MODE_ENCAP_RED =3D 3 +SEG6_IPTUN_MODE_L2ENCAP_RED =3D 4 + + +def encap(mode=3DSEG6_IPTUN_MODE_ENCAP, hdrlen=3D2, routing_type=3D4): + # seg6_iptunnel_encap followed by an SRH containing one segment. + srh =3D struct.pack('!BBBBBBH', 0, hdrlen, routing_type, 0, 0, 0, 0) + srh +=3D socket.inet_pton(socket.AF_INET6, '2001:db8::1') + return struct.pack('=3Di', mode) + srh + + +def nlattr(attr_type, payload): + length =3D 4 + len(payload) + return (struct.pack('=3DHH', length, attr_type) + payload + + bytes(-length % 4)) + + +def route(family, oif, payload, tunsrc=3DNone, table=3DNone): + attrs =3D b'' + if payload is not None: + attrs +=3D nlattr(SEG6_IPTUNNEL_SRH, payload) + if tunsrc is not None: + attrs +=3D nlattr(SEG6_IPTUNNEL_SRC, + socket.inet_pton(socket.AF_INET6, tunsrc)) + if table is not None: + attrs +=3D nlattr(SEG6_IPTUNNEL_TABLE, struct.pack('=3DI', table)) + + return { + 'rtm-family': family, + 'rtm-dst-len': 32 if family =3D=3D socket.AF_INET else 128, + 'rtm-table': 254, + 'rtm-protocol': 4, # RTPROT_STATIC + 'rtm-type': 1, # RTN_UNICAST + 'dst': '192.0.2.1' if family =3D=3D socket.AF_INET else '2001:db8:= 1::1', + 'oif': oif, + 'encap-type': LWTUNNEL_ENCAP_SEG6, + 'encap': attrs, + } + + +def add_route(rtnl, attrs): + rtnl.newroute(attrs.copy(), + flags=3D[Netlink.NLM_F_CREATE, Netlink.NLM_F_EXCL]) + + +def matching_routes(rtnl, attrs): + routes =3D rtnl.getroute({'rtm-family': attrs['rtm-family']}, dump=3DT= rue) + return [entry for entry in routes + if entry.get('dst') =3D=3D attrs['dst'] and + entry['rtm-table'] =3D=3D attrs['rtm-table']] + + +@contextmanager +def setup(): + if os.geteuid() !=3D 0: + raise KsftSkipEx('Root privileges are required') + + with NetNS() as ns, NetNSEnter(str(ns)): + ip('link set lo up') + oif =3D socket.if_nametoindex('lo') + rtnl =3D RtnlRouteFamily() + try: + # Probe with a valid route so missing SRv6 support is a skip, + # whereas missing diagnostics remain a test failure. + probe =3D route(socket.AF_INET6, oif, encap()) + try: + add_route(rtnl, probe) + except NlError as error: + if error.error =3D=3D errno.EOPNOTSUPP: + raise KsftSkipEx('SRv6 tunnels are not supported') fro= m error + raise + rtnl.delroute(probe.copy()) + yield rtnl, oif + finally: + rtnl.close() + + +# (name, SRH payload, extra encap attributes, expected extack message) +INVALID =3D [ + ('missing_srh', None, {}, 'missing SRv6 SRH attribute'), + ('empty_srh', b'', {}, 'truncated SRv6 SRH attribute'), + ('short_srh', encap()[:-1], {}, 'truncated SRv6 SRH attribute'), + ('invalid_mode', encap(mode=3D255), {}, 'invalid SRv6 encapsulation mo= de'), + ('invalid_type', encap(routing_type=3D0), {}, + 'invalid SRv6 segment routing header'), + ('invalid_length', encap(hdrlen=3D4), {}, + 'invalid SRv6 segment routing header'), + ('invalid_tunsrc', encap(), {'tunsrc': '::'}, 'invalid tunsrc address'= ), + ('invalid_table', encap(), {'table': 0}, 'invalid lookup table'), +] + + +@ksft_variants([ + KsftNamedVariant(f'{name}_{af_name}', family, payload, extra, message) + for name, payload, extra, message in INVALID + for af_name, family in [('ipv4', socket.AF_INET), ('ipv6', socket.AF_I= NET6)] +] + [KsftNamedVariant('inline_ipv4', socket.AF_INET, + encap(mode=3DSEG6_IPTUN_MODE_INLINE), {}, + 'inline mode requires an IPv6 route'), + # Inline mode rejects a non-IPv6 route before it looks at tunsrc, + # so only IPv6 reaches the tunsrc check. + KsftNamedVariant('inline_tunsrc_ipv6', socket.AF_INET6, + encap(mode=3DSEG6_IPTUN_MODE_INLINE), + {'tunsrc': '2001:db8::2'}, + 'incompatible mode for tunsrc')]) +def invalid_config(family, payload, extra, message): + with setup() as (rtnl, oif): + attrs =3D route(family, oif, payload, **extra) + with ksft_raises(NlError) as caught: + add_route(rtnl, attrs) + ksft_eq(matching_routes(rtnl, attrs), [], 'Rejected route was inst= alled') + if caught.exception is None: + return + ksft_eq(caught.exception.error, errno.EINVAL) + ksft_eq((caught.exception.nl_msg.extack or {}).get('msg'), message) + + +@ksft_variants([ + KsftNamedVariant(f'{name}_{af_name}', family, mode) + for name, mode in [('inline', SEG6_IPTUN_MODE_INLINE), + ('encap', SEG6_IPTUN_MODE_ENCAP), + ('l2encap', SEG6_IPTUN_MODE_L2ENCAP), + ('encap_red', SEG6_IPTUN_MODE_ENCAP_RED), + ('l2encap_red', SEG6_IPTUN_MODE_L2ENCAP_RED)] + for af_name, family in [('ipv4', socket.AF_INET), ('ipv6', socket.AF_I= NET6)] + if mode !=3D SEG6_IPTUN_MODE_INLINE or family =3D=3D socket.AF_INET6 +]) +def valid_config(family, mode): + with setup() as (rtnl, oif): + attrs =3D route(family, oif, encap(mode=3Dmode)) + add_route(rtnl, attrs) + routes =3D matching_routes(rtnl, attrs) + ksft_eq(len(routes), 1) + if routes: + ksft_eq(routes[0].get('encap-type'), LWTUNNEL_ENCAP_SEG6) + ksft_eq(routes[0].get('encap'), attrs['encap']) + rtnl.delroute(attrs.copy()) + ksft_eq(matching_routes(rtnl, attrs), []) + + +if __name__ =3D=3D '__main__': + ksft_run([invalid_config, valid_config]) + ksft_exit() --=20 2.47.3