| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/12 08:54 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"IPV6_SEG6_LWTUNNEL"
],
"FocusSymbols": [
"seg6_table_lookup",
"seg6_input_route",
"seg6_output_dst_lookup",
"seg6_build_state",
"seg6_fill_encap_info",
"seg6_encap_nlsize",
"seg6_encap_cmp"
],
"Reasoning": "The patch adds a new Netlink attribute `SEG6_IPTUNNEL_TABLE` to specify a FIB table for post-encap SID lookup in SRv6 tunnels. It modifies the SRv6 encapsulation logic in `net/ipv6/seg6_iptunnel.c` to perform route lookups in the specified table during both input and output paths. This is a functional change to reachable core kernel networking logic, specifically the IPv6 Segment Routing subsystem, and should be fuzzed.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/12 08:54 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit bd0f47a88b0d5094b9fd431ca04c21e8f66c7f01\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sun Jul 12 08:54:14 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/include/uapi/linux/seg6_iptunnel.h b/include/uapi/linux/seg6_iptunnel.h\nindex 485889b19900ea..e1964b3a4bb0af 100644\n--- a/include/uapi/linux/seg6_iptunnel.h\n+++ b/include/uapi/linux/seg6_iptunnel.h\n@@ -21,6 +21,7 @@ enum {\n \tSEG6_IPTUNNEL_UNSPEC,\n \tSEG6_IPTUNNEL_SRH,\n \tSEG6_IPTUNNEL_SRC,\t/* struct in6_addr */\n+\tSEG6_IPTUNNEL_TABLE,\t/* __u32 FIB table for post-encap SID lookup */\n \t__SEG6_IPTUNNEL_MAX,\n };\n #define SEG6_IPTUNNEL_MAX (__SEG6_IPTUNNEL_MAX - 1)\ndiff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c\nindex 4c45c0a77d75d0..61c6a27bf202dd 100644\n--- a/net/ipv6/seg6_iptunnel.c\n+++ b/net/ipv6/seg6_iptunnel.c\n@@ -51,6 +51,7 @@ struct seg6_lwt {\n \tstruct dst_cache cache_input;\n \tstruct dst_cache cache_output;\n \tstruct in6_addr tunsrc;\n+\tu32 table;\n \tstruct seg6_iptunnel_encap tuninfo[];\n };\n \n@@ -68,6 +69,7 @@ seg6_encap_lwtunnel(struct lwtunnel_state *lwt)\n static const struct nla_policy seg6_iptunnel_policy[SEG6_IPTUNNEL_MAX + 1] = {\n \t[SEG6_IPTUNNEL_SRH]\t= { .type = NLA_BINARY },\n \t[SEG6_IPTUNNEL_SRC]\t= NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),\n+\t[SEG6_IPTUNNEL_TABLE]\t= { .type = NLA_U32 },\n };\n \n static int nla_put_srh(struct sk_buff *skb, int attrtype,\n@@ -479,6 +481,73 @@ int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)\n }\n EXPORT_SYMBOL_GPL(seg6_do_srh_inline);\n \n+/* look up a route in a specific FIB table.\n+ * Returns a refcounted dst, or NULL if the table does not exist.\n+ */\n+static struct dst_entry *seg6_table_lookup(struct net *net,\n+\t\t\t\t\t struct sk_buff *skb,\n+\t\t\t\t\t struct flowi6 *fl6, u32 tbl_id)\n+{\n+\tstruct fib6_table *table;\n+\tstruct rt6_info *rt;\n+\n+\ttable = fib6_get_table(net, tbl_id);\n+\tif (!table)\n+\t\treturn NULL;\n+\n+\trt = ip6_pol_route(net, table, 0, fl6, skb, RT6_LOOKUP_F_HAS_SADDR);\n+\treturn \u0026rt-\u003edst;\n+}\n+\n+static void seg6_init_flowi6(struct sk_buff *skb, struct ipv6hdr *hdr,\n+\t\t\t struct flowi6 *fl6)\n+{\n+\tmemset(fl6, 0, sizeof(*fl6));\n+\n+\tfl6-\u003edaddr = hdr-\u003edaddr;\n+\tfl6-\u003esaddr = hdr-\u003esaddr;\n+\tfl6-\u003eflowlabel = ip6_flowinfo(hdr);\n+\tfl6-\u003eflowi6_mark = skb-\u003emark;\n+\tfl6-\u003eflowi6_proto = hdr-\u003enexthdr;\n+}\n+\n+/* look up the route for the first SID on the input path and set it on the skb.\n+ * Returns the refcounted dst, or NULL if a reference could not be safely taken.\n+ */\n+static struct dst_entry *seg6_input_route(struct net *net,\n+\t\t\t\t\t struct sk_buff *skb,\n+\t\t\t\t\t struct seg6_lwt *slwt)\n+{\n+\tu32 table = slwt-\u003etable;\n+\n+\tif (table) {\n+\t\tstruct ipv6hdr *hdr = ipv6_hdr(skb);\n+\t\tstruct dst_entry *dst;\n+\t\tstruct flowi6 fl6;\n+\n+\t\tseg6_init_flowi6(skb, hdr, \u0026fl6);\n+\t\tfl6.flowi6_iif = skb-\u003edev-\u003eifindex;\n+\n+\t\tdst = seg6_table_lookup(net, skb, \u0026fl6, table);\n+\t\tif (!dst) {\n+\t\t\tdst = \u0026net-\u003eipv6.ip6_blk_hole_entry-\u003edst;\n+\t\t\tdst_hold(dst);\n+\t\t}\n+\n+\t\tskb_dst_drop(skb);\n+\t\tskb_dst_set(skb, dst);\n+\t} else {\n+\t\tip6_route_input(skb);\n+\n+\t\t/* ip6_route_input() sets a NOREF dst; force a refcount on it\n+\t\t * before caching or further use.\n+\t\t */\n+\t\tskb_dst_force(skb);\n+\t}\n+\n+\treturn skb_dst(skb);\n+}\n+\n static int seg6_input_finish(struct net *net, struct sock *sk,\n \t\t\t struct sk_buff *skb)\n {\n@@ -513,15 +582,9 @@ static int seg6_input_core(struct net *net, struct sock *sk,\n \t\tgoto drop;\n \t}\n \n-\tif (!dst) {\n-\t\tip6_route_input(skb);\n-\n-\t\t/* ip6_route_input() sets a NOREF dst; force a refcount on it\n-\t\t * before caching or further use.\n-\t\t */\n-\t\tskb_dst_force(skb);\n-\t\tdst = skb_dst(skb);\n-\t\tif (unlikely(!dst)) {\n+\tif (unlikely(!dst)) {\n+\t\tdst = seg6_input_route(net, skb, slwt);\n+\t\tif (!dst) {\n \t\t\terr = -ENETUNREACH;\n \t\t\tgoto drop;\n \t\t}\n@@ -578,6 +641,29 @@ static int seg6_input(struct sk_buff *skb)\n \treturn seg6_input_core(dev_net(skb-\u003edev), NULL, skb);\n }\n \n+/* look up the route for the first SID on the output path. Always returns a\n+ * refcounted dst.\n+ */\n+static struct dst_entry *seg6_output_dst_lookup(struct net *net,\n+\t\t\t\t\t\tstruct sk_buff *skb,\n+\t\t\t\t\t\tstruct flowi6 *fl6,\n+\t\t\t\t\t\tstruct seg6_lwt *slwt)\n+{\n+\tstruct dst_entry *dst;\n+\n+\tif (slwt-\u003etable) {\n+\t\tdst = seg6_table_lookup(net, skb, fl6, slwt-\u003etable);\n+\t\tif (!dst) {\n+\t\t\tdst = \u0026net-\u003eipv6.ip6_blk_hole_entry-\u003edst;\n+\t\t\tdst_hold(dst);\n+\t\t}\n+\t} else {\n+\t\tdst = ip6_route_output(net, NULL, fl6);\n+\t}\n+\n+\treturn dst;\n+}\n+\n static int seg6_output_core(struct net *net, struct sock *sk,\n \t\t\t struct sk_buff *skb)\n {\n@@ -600,14 +686,9 @@ static int seg6_output_core(struct net *net, struct sock *sk,\n \t\tstruct ipv6hdr *hdr = ipv6_hdr(skb);\n \t\tstruct flowi6 fl6;\n \n-\t\tmemset(\u0026fl6, 0, sizeof(fl6));\n-\t\tfl6.daddr = hdr-\u003edaddr;\n-\t\tfl6.saddr = hdr-\u003esaddr;\n-\t\tfl6.flowlabel = ip6_flowinfo(hdr);\n-\t\tfl6.flowi6_mark = skb-\u003emark;\n-\t\tfl6.flowi6_proto = hdr-\u003enexthdr;\n+\t\tseg6_init_flowi6(skb, hdr, \u0026fl6);\n \n-\t\tdst = ip6_route_output(net, NULL, \u0026fl6);\n+\t\tdst = seg6_output_dst_lookup(net, skb, \u0026fl6, slwt);\n \t\tif (dst-\u003eerror) {\n \t\t\terr = dst-\u003eerror;\n \t\t\tgoto drop;\n@@ -752,6 +833,15 @@ static int seg6_build_state(struct net *net, struct nlattr *nla,\n \t\t}\n \t}\n \n+\tif (tb[SEG6_IPTUNNEL_TABLE]) {\n+\t\tslwt-\u003etable = nla_get_u32(tb[SEG6_IPTUNNEL_TABLE]);\n+\t\tif (!slwt-\u003etable) {\n+\t\t\tNL_SET_ERR_MSG(extack, \"invalid lookup table\");\n+\t\t\terr = -EINVAL;\n+\t\t\tgoto err_destroy_output;\n+\t\t}\n+\t}\n+\n \tnewts-\u003etype = LWTUNNEL_ENCAP_SEG6;\n \tnewts-\u003eflags |= LWTUNNEL_STATE_INPUT_REDIRECT;\n \n@@ -795,6 +885,10 @@ static int seg6_fill_encap_info(struct sk_buff *skb,\n \t nla_put_in6_addr(skb, SEG6_IPTUNNEL_SRC, \u0026slwt-\u003etunsrc))\n \t\treturn -EMSGSIZE;\n \n+\tif (slwt-\u003etable \u0026\u0026\n+\t nla_put_u32(skb, SEG6_IPTUNNEL_TABLE, slwt-\u003etable))\n+\t\treturn -EMSGSIZE;\n+\n \treturn 0;\n }\n \n@@ -809,6 +903,9 @@ static int seg6_encap_nlsize(struct lwtunnel_state *lwtstate)\n \tif (!ipv6_addr_any(\u0026slwt-\u003etunsrc))\n \t\tnlsize += nla_total_size(sizeof(slwt-\u003etunsrc));\n \n+\tif (slwt-\u003etable)\n+\t\tnlsize += nla_total_size(sizeof(u32));\n+\n \treturn nlsize;\n }\n \n@@ -826,6 +923,9 @@ static int seg6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)\n \tif (!ipv6_addr_equal(\u0026a_slwt-\u003etunsrc, \u0026b_slwt-\u003etunsrc))\n \t\treturn 1;\n \n+\tif (a_slwt-\u003etable != b_slwt-\u003etable)\n+\t\treturn 1;\n+\n \treturn memcmp(a_hdr, b_hdr, len);\n }\n \ndiff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile\nindex 708d960ae07d5b..5df0c94ab06018 100644\n--- a/tools/testing/selftests/net/Makefile\n+++ b/tools/testing/selftests/net/Makefile\n@@ -85,6 +85,7 @@ TEST_PROGS := \\\n \trxtimestamp.sh \\\n \tsctp_vrf.sh \\\n \tskf_net_off.sh \\\n+\tsrv6_encap_lookup_l3vpn_test.sh \\\n \tsrv6_end_dt46_l3vpn_test.sh \\\n \tsrv6_end_dt4_l3vpn_test.sh \\\n \tsrv6_end_dt6_l3vpn_test.sh \\\ndiff --git a/tools/testing/selftests/net/srv6_encap_lookup_l3vpn_test.sh b/tools/testing/selftests/net/srv6_encap_lookup_l3vpn_test.sh\nnew file mode 100755\nindex 00000000000000..d6249303b7eaa1\n--- /dev/null\n+++ b/tools/testing/selftests/net/srv6_encap_lookup_l3vpn_test.sh\n@@ -0,0 +1,1027 @@\n+#!/bin/bash\n+# SPDX-License-Identifier: GPL-2.0\n+#\n+# author: Andrea Mayer \u003candrea.mayer@uniroma2.it\u003e\n+\n+# This test evaluates the SRv6 encap \"lookup\" attribute. After encapsulation\n+# the router looks up the route for the first SID, that is the outer IPv6\n+# destination of the encapsulated packet. The attribute selects the FIB table\n+# used for this post-encap SID route lookup.\n+#\n+# Two routers (rt-1, rt-2) provide L3 VPN services over an IPv6 underlay\n+# (fd00::/64). Each router uses a separate VRF per tenant, with default\n+# blackhole routes (IPv4 and IPv6) to prevent traffic from leaking out of\n+# the VRF. Tenant traffic is encapsulated, then decapsulated with an\n+# End.DT46. Each router proxies both NDP and ARP.\n+#\n+# The routes that match the first SIDs are installed in a dedicated underlay\n+# table (500) rather than the main table (254). The encap routes use\n+# \"lookup 500\" to select this table for the post-encap SID route lookup.\n+#\n+# Without the \"lookup\" attribute, the route for the first SID cannot be found:\n+# - on the input path (forwarded traffic), the lookup stays in the VRF\n+# and hits the blackhole;\n+# - on the output path (locally originated traffic), the lookup falls\n+# through to the main table, with no route to the first SID.\n+#\n+#\n+# Legend (specific per-tenant addresses are in the instantiation tables below):\n+# X = tenant id and VRF table id; two tenants: 100 and 200\n+# (\"tX\" means tenant X, e.g. t100, t200)\n+# a, b = the two host ids of the tenant\n+# HA, HB = addresses of host a, host b\n+# RLO1, RLO2 = rlo-X router loopback address on rt-1, rt-2 (tenant gateway\n+# for the output path, dual-stack)\n+# vrf-X = per-tenant VRF on each router (table X)\n+#\n+# Constants (same for every tenant):\n+# underlay = table 500; post-encap SID route lookup (via \"lookup 500\")\n+# localsid = table 90; holds the decap SIDs (End.DT46)\n+# fd00::/64 = underlay link between rt-1 and rt-2\n+# veth-tX = cafe::254/10.0.0.254 (tenant gateway on veth, both routers)\n+#\n+#\n+# +-------------------+ +-------------------+\n+# | | | |\n+# | hs-tX-a netns | | hs-tX-b netns |\n+# | | | |\n+# | +-------------+ | | +-------------+ |\n+# | | veth0 | | | | veth0 | |\n+# | | HA | | | | HB | |\n+# | +-------------+ | | +-------------+ |\n+# | . | | . |\n+# +-------------------+ +-------------------+\n+# . .\n+# . .\n+# +-----------------------------------+ +-----------------------------------+\n+# | . | | . |\n+# | +---------------+ | | +---------------+ |\n+# | | veth-tX | +----------+ | | +----------+ | veth-tX | |\n+# | | ::254/.254 | | localsid | | | | localsid | | ::254/.254 | |\n+# | +-------+-------+ +----------+ | | +----------+ +-------+-------+ |\n+# | | +----------+ | | +----------+ | |\n+# | +----+----+ | underlay | | | | underlay | +----+----+ |\n+# | | vrf-X | +----------+ | | +----------+ | vrf-X | |\n+# | +----+----+ | | +----+----+ |\n+# | | | | | |\n+# | +-----+----+ +------------+ | | +------------+ +----+-----+ |\n+# | | rlo-X | | veth0 | | | | veth0 | | rlo-X | |\n+# | | RLO1 | | fd00::1/64 |..|...|..| fd00::2/64 | | RLO2 | |\n+# | +----------+ +------------+ | | +------------+ +----------+ |\n+# | rt-1 netns | | rt-2 netns |\n+# +-----------------------------------+ +-----------------------------------+\n+#\n+#\n+# Per-tenant instantiation:\n+# +-----+------+-------------------+-------------------+\n+# | X | a, b | HA | HB |\n+# +-----+------+-------------------+-------------------+\n+# | 100 | 1, 2 | cafe::1, 10.0.0.1 | cafe::2, 10.0.0.2 |\n+# | 200 | 3, 4 | cafe::3, 10.0.0.3 | cafe::4, 10.0.0.4 |\n+# +-----+------+-------------------+-------------------+\n+#\n+# Router loopback (rlo-X) addresses, per tenant:\n+# +-----+-----------------------+-----------------------+\n+# | X | RLO1 (rt-1) | RLO2 (rt-2) |\n+# +-----+-----------------------+-----------------------+\n+# | 100 | cafe::101, 10.0.0.101 | cafe::102, 10.0.0.102 |\n+# | 200 | cafe::201, 10.0.0.201 | cafe::202, 10.0.0.202 |\n+# +-----+-----------------------+-----------------------+\n+#\n+#\n+# Network configuration\n+# =====================\n+#\n+# rt-1: localsid table (table 90)\n+# +--------+--------------------+----------------------------------+\n+# | tenant | SID | Action |\n+# +--------+--------------------+----------------------------------+\n+# | 100 | fc00:2:1:100::0d46 | apply SRv6 End.DT46 vrftable 100 |\n+# | 200 | fc00:2:1:200::0d46 | apply SRv6 End.DT46 vrftable 200 |\n+# +--------+--------------------+----------------------------------+\n+#\n+# rt-1: underlay table (table 500) - post-encap SID route lookup\n+# +--------+--------------------+-------------------------------+\n+# | tenant | SID | Action |\n+# +--------+--------------------+-------------------------------+\n+# | 100 | fc00:1:2:100::0d46 | forward via fd00::2 dev veth0 |\n+# | 200 | fc00:1:2:200::0d46 | forward via fd00::2 dev veth0 |\n+# +--------+--------------------+-------------------------------+\n+#\n+# rt-1: VRF tables (per tenant: vrf-X = table X)\n+# +--------+------------+------------------------------------------+\n+# | tenant | dst | encap action |\n+# +--------+------------+------------------------------------------+\n+# | 100 | cafe::2 | encap segs fc00:1:2:100::0d46 lookup 500 |\n+# | | 10.0.0.2 | |\n+# | | cafe::102 | |\n+# | | 10.0.0.102 | |\n+# | 200 | cafe::4 | encap segs fc00:1:2:200::0d46 lookup 500 |\n+# | | 10.0.0.4 | |\n+# | | cafe::202 | |\n+# | | 10.0.0.202 | |\n+# +--------+------------+------------------------------------------+\n+#\n+#\n+# rt-2: localsid table (table 90)\n+# +--------+--------------------+----------------------------------+\n+# | tenant | SID | Action |\n+# +--------+--------------------+----------------------------------+\n+# | 100 | fc00:1:2:100::0d46 | apply SRv6 End.DT46 vrftable 100 |\n+# | 200 | fc00:1:2:200::0d46 | apply SRv6 End.DT46 vrftable 200 |\n+# +--------+--------------------+----------------------------------+\n+#\n+# rt-2: underlay table (table 500) - post-encap SID route lookup\n+# +--------+--------------------+-------------------------------+\n+# | tenant | SID | Action |\n+# +--------+--------------------+-------------------------------+\n+# | 100 | fc00:2:1:100::0d46 | forward via fd00::1 dev veth0 |\n+# | 200 | fc00:2:1:200::0d46 | forward via fd00::1 dev veth0 |\n+# +--------+--------------------+-------------------------------+\n+#\n+# rt-2: VRF tables (per tenant: vrf-X = table X)\n+# +--------+------------+------------------------------------------+\n+# | tenant | dst | encap action |\n+# +--------+------------+------------------------------------------+\n+# | 100 | cafe::1 | encap segs fc00:2:1:100::0d46 lookup 500 |\n+# | | 10.0.0.1 | |\n+# | | cafe::101 | |\n+# | | 10.0.0.101 | |\n+# | 200 | cafe::3 | encap segs fc00:2:1:200::0d46 lookup 500 |\n+# | | 10.0.0.3 | |\n+# | | cafe::201 | |\n+# | | 10.0.0.201 | |\n+# +--------+------------+------------------------------------------+\n+# Within a tenant, a single SID reaches the adjacent router (its loopback)\n+# and the remote host connected to it, in both IPv4 and IPv6.\n+#\n+# For both rt-1 and rt-2, each VRF also has the connected host prefix (cafe::/64\n+# or 10.0.0.0/24) and a default blackhole (IPv4 and IPv6).\n+#\n+#\n+# Locally originated traffic (output path)\n+# ========================================\n+#\n+# The configuration above covers forwarded traffic, where packets arrive from\n+# a host and are encapsulated by the router. To also test router-originated\n+# traffic, each router pings the other router's loopback address through\n+# the VPN.\n+#\n+# Example (tenant 100), rt-1 pings cafe::102 (rt-2's loopback):\n+# 1. rt-1 looks up cafe::102 in vrf-100 and encapsulates it (SID\n+# fc00:1:2:100::0d46), then \"lookup 500\" finds the route for the SID in the\n+# underlay table (next hop fd00::2) and forwards it;\n+# 2. rt-2 decapsulates it (localsid, End.DT46) and delivers it locally\n+# (cafe::102 is on the rlo-100 interface);\n+# 3. rt-2 replies with destination cafe::101 (rt-1's loopback). rt-2 looks up\n+# cafe::101 in vrf-100 and encapsulates it back to rt-1 (again via \"lookup\n+# 500\"). rt-1 decapsulates it and delivers it.\n+\n+# shellcheck source=lib.sh\n+source lib.sh\n+\n+readonly LOCALSID_TABLE_ID=90\n+readonly UNDERLAY_TABLE_ID=500\n+readonly IPv6_RT_NETWORK=fd00\n+readonly IPv6_HS_NETWORK=cafe\n+readonly IPv4_HS_NETWORK=10.0.0\n+readonly VPN_LOCATOR_SERVICE=fc00\n+readonly DT46_FUNC=0d46\n+readonly DUMMY_DEVNAME=dum0\n+readonly IPv6_TESTS_ADDR=2001:db8::1\n+readonly TESTS_TABLE_ID=54321\n+PING_TIMEOUT_SEC=4\n+\n+SETUP_ERR=1\n+\n+ret=${ksft_skip}\n+nsuccess=0\n+nfail=0\n+\n+PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}\n+\n+log_test()\n+{\n+\tlocal rc=\"$1\"\n+\tlocal expected=\"$2\"\n+\tlocal msg=\"$3\"\n+\n+\tif [ \"${rc}\" -eq \"${expected}\" ]; then\n+\t\tnsuccess=$((nsuccess+1))\n+\t\tprintf \"\\n TEST: %-60s [ OK ]\\n\" \"${msg}\"\n+\telse\n+\t\tret=1\n+\t\tnfail=$((nfail+1))\n+\t\tprintf \"\\n TEST: %-60s [FAIL]\\n\" \"${msg}\"\n+\t\tif [ \"${PAUSE_ON_FAIL}\" = \"yes\" ]; then\n+\t\t\techo\n+\t\t\techo \"hit enter to continue, 'q' to quit\"\n+\t\t\tread -r a\n+\t\t\t[ \"$a\" = \"q\" ] \u0026\u0026 exit 1\n+\t\tfi\n+\tfi\n+}\n+\n+print_log_test_results()\n+{\n+\tprintf \"\\nTests passed: %3d\\n\" \"${nsuccess}\"\n+\tprintf \"Tests failed: %3d\\n\" \"${nfail}\"\n+\n+\t# when a test fails, the value of 'ret' is set to 1 (error code).\n+\t# Conversely, when all tests are passed successfully, the 'ret' value\n+\t# is set to 0 (success code).\n+\tif [ \"${ret}\" -ne 1 ]; then\n+\t\tret=0\n+\tfi\n+}\n+\n+log_section()\n+{\n+\techo\n+\techo \"################################################################################\"\n+\techo \"TEST SECTION: $*\"\n+\techo \"################################################################################\"\n+}\n+\n+get_rtname()\n+{\n+\tlocal rtid=\"$1\"\n+\n+\techo \"rt_${rtid}\"\n+}\n+\n+get_rt_nsname()\n+{\n+\tlocal rtid=\"$1\"\n+\tlocal varname\n+\n+\tvarname=\"$(get_rtname \"${rtid}\")\"\n+\techo \"${!varname}\"\n+}\n+\n+get_hsname()\n+{\n+\tlocal tid=\"$1\"\n+\tlocal hsid=\"$2\"\n+\n+\techo \"hs_t${tid}_${hsid}\"\n+}\n+\n+get_hs_nsname()\n+{\n+\tlocal tid=\"$1\"\n+\tlocal hsid=\"$2\"\n+\tlocal varname\n+\n+\tvarname=\"$(get_hsname \"${tid}\" \"${hsid}\")\"\n+\techo \"${!varname}\"\n+}\n+\n+cleanup()\n+{\n+\tip link del veth-rt-1 2\u003e/dev/null || true\n+\tip link del veth-rt-2 2\u003e/dev/null || true\n+\n+\tcleanup_all_ns\n+\n+\t# check whether the setup phase was completed successfully or not. In\n+\t# case of an error during the setup phase of the testing environment,\n+\t# the selftest is considered as \"skipped\".\n+\tif [ \"${SETUP_ERR}\" -ne 0 ]; then\n+\t\techo \"SKIP: Setting up the testing environment failed\"\n+\t\texit \"${ksft_skip}\"\n+\tfi\n+\n+\texit \"${ret}\"\n+}\n+\n+# Host id of the router loopback (rlo) for a (router, tenant) pair.\n+# E.g. rt-1/tenant 100 -\u003e 101, rt-2/tenant 200 -\u003e 202.\n+get_rlo_hostid()\n+{\n+\tlocal rtid=\"$1\"\n+\tlocal tid=\"$2\"\n+\n+\techo \"$((tid + rtid))\"\n+}\n+\n+build_vpn_sid()\n+{\n+\tlocal rtsrc=\"$1\"\n+\tlocal rtdst=\"$2\"\n+\tlocal tid=\"$3\"\n+\n+\techo \"${VPN_LOCATOR_SERVICE}:${rtsrc}:${rtdst}:${tid}::${DT46_FUNC}\"\n+}\n+\n+# Install a dual-stack (IPv6 and IPv4) encap route in a VRF on the given\n+# router.\n+# args:\n+# $1 - router id\n+# $2 - host part of the IPv6 destination\n+# $3 - host part of the IPv4 destination\n+# $4 - SRv6 SID used as the encap destination\n+# $5 - tenant id\n+# $6 - if \"true\", add the \"lookup\" attribute to the encap route\n+__set_encap_route()\n+{\n+\tlocal rt=\"$1\"\n+\tlocal dst6=\"$2\"\n+\tlocal dst4=\"$3\"\n+\tlocal sid=\"$4\"\n+\tlocal tid=\"$5\"\n+\tlocal use_lookup=\"$6\"\n+\tlocal lookup=''\n+\tlocal rtname\n+\n+\trtname=\"$(get_rt_nsname \"${rt}\")\"\n+\n+\tif [ \"${use_lookup}\" = \"true\" ]; then\n+\t\tlookup=\"lookup ${UNDERLAY_TABLE_ID}\"\n+\tfi\n+\n+\t# shellcheck disable=SC2086\n+\tip -netns \"${rtname}\" -6 route replace \\\n+\t\t\"${IPv6_HS_NETWORK}::${dst6}/128\" vrf \"vrf-${tid}\" \\\n+\t\tencap seg6 mode encap segs \"${sid}\" ${lookup} dev veth0\n+\n+\t# shellcheck disable=SC2086\n+\tip -netns \"${rtname}\" -4 route replace \\\n+\t\t\"${IPv4_HS_NETWORK}.${dst4}/32\" vrf \"vrf-${tid}\" \\\n+\t\tencap seg6 mode encap segs \"${sid}\" ${lookup} dev veth0\n+}\n+\n+# Install the dual-stack encap route for a tenant host on rt, with the\n+# \"lookup\" attribute so the first SID is looked up in the underlay table.\n+# args:\n+# $1 - router id where the encap route is installed\n+# $2 - host destination id (host part of cafe::\u003cid\u003e/128 and 10.0.0.\u003cid\u003e/32)\n+# $3 - SRv6 SID used as the encap destination\n+# $4 - tenant id\n+set_host_encap_route()\n+{\n+\tlocal rt=\"$1\"\n+\tlocal hsdst=\"$2\"\n+\tlocal sid=\"$3\"\n+\tlocal tid=\"$4\"\n+\n+\t__set_encap_route \"${rt}\" \"${hsdst}\" \"${hsdst}\" \"${sid}\" \"${tid}\" true\n+}\n+\n+set_host_encap_route_nolookup()\n+{\n+\tlocal rt=\"$1\"\n+\tlocal hsdst=\"$2\"\n+\tlocal sid=\"$3\"\n+\tlocal tid=\"$4\"\n+\n+\t__set_encap_route \"${rt}\" \"${hsdst}\" \"${hsdst}\" \"${sid}\" \"${tid}\" false\n+}\n+\n+# Install the dual-stack encap route on rtsrc toward rtdst's rlo loopback\n+# (RLO1 or RLO2, see header), with the \"lookup\" attribute so the first\n+# SID is looked up in the underlay table.\n+# args:\n+# $1 - router id where the encap route is installed\n+# $2 - router id whose loopback address is the route destination\n+# $3 - SRv6 SID used as the encap destination\n+# $4 - tenant id\n+set_gw_encap_route()\n+{\n+\tlocal rtsrc=\"$1\"\n+\tlocal rtdst=\"$2\"\n+\tlocal sid=\"$3\"\n+\tlocal tid=\"$4\"\n+\tlocal dst\n+\n+\tdst=\"$(get_rlo_hostid \"${rtdst}\" \"${tid}\")\"\n+\n+\t__set_encap_route \"${rtsrc}\" \"${dst}\" \"${dst}\" \"${sid}\" \"${tid}\" true\n+}\n+\n+set_gw_encap_route_nolookup()\n+{\n+\tlocal rtsrc=\"$1\"\n+\tlocal rtdst=\"$2\"\n+\tlocal sid=\"$3\"\n+\tlocal tid=\"$4\"\n+\tlocal dst\n+\n+\tdst=\"$(get_rlo_hostid \"${rtdst}\" \"${tid}\")\"\n+\n+\t__set_encap_route \"${rtsrc}\" \"${dst}\" \"${dst}\" \"${sid}\" \"${tid}\" false\n+}\n+\n+# Setup the basic networking for a router\n+setup_rt_networking()\n+{\n+\tlocal id=\"$1\"\n+\tlocal nsname\n+\n+\tnsname=\"$(get_rt_nsname \"${id}\")\"\n+\n+\tip link set \"veth-rt-${id}\" netns \"${nsname}\"\n+\tip -netns \"${nsname}\" link set \"veth-rt-${id}\" name veth0\n+\n+\tip netns exec \"${nsname}\" sysctl -wq net.ipv6.conf.all.accept_dad=0\n+\tip netns exec \"${nsname}\" sysctl -wq net.ipv6.conf.default.accept_dad=0\n+\n+\tip -netns \"${nsname}\" addr add \"${IPv6_RT_NETWORK}::${id}/64\" dev veth0 nodad\n+\tip -netns \"${nsname}\" link set veth0 up\n+\n+\tip netns exec \"${nsname}\" sysctl -wq net.ipv4.ip_forward=1\n+\tip netns exec \"${nsname}\" sysctl -wq net.ipv6.conf.all.forwarding=1\n+}\n+\n+# Setup a host namespace and attach it to its gateway\n+setup_hs()\n+{\n+\tlocal hid=\"$1\"\n+\tlocal rid=\"$2\"\n+\tlocal tid=\"$3\"\n+\tlocal rtveth=\"veth-t${tid}\"\n+\tlocal hsname\n+\tlocal rtname\n+\n+\thsname=\"$(get_hs_nsname \"${tid}\" \"${hid}\")\"\n+\trtname=\"$(get_rt_nsname \"${rid}\")\"\n+\n+\tip netns exec \"${hsname}\" sysctl -wq net.ipv6.conf.all.accept_dad=0\n+\tip netns exec \"${hsname}\" sysctl -wq net.ipv6.conf.default.accept_dad=0\n+\n+\tip -netns \"${hsname}\" link add veth0 type veth peer name \"${rtveth}\"\n+\tip -netns \"${hsname}\" link set \"${rtveth}\" netns \"${rtname}\"\n+\n+\tip -netns \"${hsname}\" addr add \\\n+\t\t\"${IPv6_HS_NETWORK}::${hid}/64\" dev veth0 nodad\n+\tip -netns \"${hsname}\" addr add \\\n+\t\t\"${IPv4_HS_NETWORK}.${hid}/24\" dev veth0\n+\n+\tip -netns \"${hsname}\" link set veth0 up\n+}\n+\n+# Setup the per-tenant VRF on a router (gateway, loopback, blackhole)\n+setup_rt()\n+{\n+\tlocal rid=\"$1\"\n+\tlocal tid=\"$2\"\n+\tlocal rtveth=\"veth-t${tid}\"\n+\tlocal rlo_dev=\"rlo-${tid}\"\n+\tlocal rtname\n+\tlocal gw_addr_v6\n+\tlocal gw_addr_v4\n+\n+\trtname=\"$(get_rt_nsname \"${rid}\")\"\n+\n+\tgw_addr_v6=\"${IPv6_HS_NETWORK}::$(get_rlo_hostid \"${rid}\" \"${tid}\")\"\n+\tgw_addr_v4=\"${IPv4_HS_NETWORK}.$(get_rlo_hostid \"${rid}\" \"${tid}\")\"\n+\n+\tip -netns \"${rtname}\" link add \"vrf-${tid}\" type vrf table \"${tid}\"\n+\tip -netns \"${rtname}\" link set \"vrf-${tid}\" up\n+\n+\tip -netns \"${rtname}\" link set \"${rtveth}\" master \"vrf-${tid}\"\n+\n+\tip -netns \"${rtname}\" addr add \\\n+\t\t\"${IPv6_HS_NETWORK}::254/64\" dev \"${rtveth}\" nodad\n+\tip -netns \"${rtname}\" addr add \\\n+\t\t\"${IPv4_HS_NETWORK}.254/24\" dev \"${rtveth}\"\n+\n+\tip -netns \"${rtname}\" link set \"${rtveth}\" up\n+\n+\tip netns exec \"${rtname}\" \\\n+\t\tsysctl -wq \"net.ipv6.conf.${rtveth}.proxy_ndp=1\"\n+\tip netns exec \"${rtname}\" \\\n+\t\tsysctl -wq \"net.ipv4.conf.${rtveth}.proxy_arp=1\"\n+\n+\tip netns exec \"${rtname}\" sh -c \"echo 1 \u003e /proc/sys/net/vrf/strict_mode\"\n+\n+\t# router loopback interface for locally originated traffic\n+\tip -netns \"${rtname}\" link add \"${rlo_dev}\" type dummy\n+\tip -netns \"${rtname}\" link set \"${rlo_dev}\" master \"vrf-${tid}\"\n+\n+\tip -netns \"${rtname}\" addr add \"${gw_addr_v6}/128\" \\\n+\t\tdev \"${rlo_dev}\" nodad\n+\tip -netns \"${rtname}\" addr add \"${gw_addr_v4}/32\" \\\n+\t\tdev \"${rlo_dev}\"\n+\n+\tip -netns \"${rtname}\" link set \"${rlo_dev}\" up\n+\n+\t# default blackhole routes in the VRF: any traffic that does not match\n+\t# a specific route is dropped. Without the \"lookup\" attribute on the\n+\t# encap route, the route for the first SID cannot be found from within\n+\t# the VRF.\n+\tip -netns \"${rtname}\" -6 route add blackhole default metric 4278198272 \\\n+\t\tvrf \"vrf-${tid}\"\n+\tip -netns \"${rtname}\" -4 route add blackhole default metric 4278198272 \\\n+\t\tvrf \"vrf-${tid}\"\n+}\n+\n+# Configure a one-way VPN path towards hsdst (on rtdst) for tenant tid.\n+# The encap side is set up on rtsrc and the decap side on rtdst.\n+# args:\n+# $1 - router id where the encap side is set up\n+# $2 - host id of the destination host\n+# $3 - router id of the destination router (connected to the destination host)\n+# $4 - tenant id\n+setup_vpn_config()\n+{\n+\tlocal rtsrc=\"$1\"\n+\tlocal hsdst=\"$2\"\n+\tlocal rtdst=\"$3\"\n+\tlocal tid=\"$4\"\n+\tlocal rtveth=\"veth-t${tid}\"\n+\tlocal rtsrc_name\n+\tlocal rtdst_name\n+\tlocal vpn_sid\n+\n+\trtsrc_name=\"$(get_rt_nsname \"${rtsrc}\")\"\n+\trtdst_name=\"$(get_rt_nsname \"${rtdst}\")\"\n+\tvpn_sid=\"$(build_vpn_sid \"${rtsrc}\" \"${rtdst}\" \"${tid}\")\"\n+\n+\tip -netns \"${rtsrc_name}\" -6 neigh add proxy \\\n+\t\t\"${IPv6_HS_NETWORK}::${hsdst}\" dev \"${rtveth}\"\n+\tset_host_encap_route \"${rtsrc}\" \"${hsdst}\" \"${vpn_sid}\" \"${tid}\"\n+\n+\tip -netns \"${rtsrc_name}\" -6 route add \"${vpn_sid}/128\" \\\n+\t\ttable \"${UNDERLAY_TABLE_ID}\" \\\n+\t\tvia \"fd00::${rtdst}\" dev veth0\n+\n+\t# set the decap route for decapsulating packets arriving from rtsrc\n+\t# and destined to hsdst\n+\tip -netns \"${rtdst_name}\" -6 route add \"${vpn_sid}/128\" \\\n+\t\ttable \"${LOCALSID_TABLE_ID}\" \\\n+\t\tencap seg6local action End.DT46 \\\n+\t\tvrftable \"${tid}\" dev \"vrf-${tid}\"\n+\n+\t# all SIDs for VPNs start with a common locator which is fc00::/16.\n+\t# Routes for handling the SRv6 End.DT* behavior instances are grouped\n+\t# together in the 'localsid' table.\n+\t#\n+\t# NOTE: added only once\n+\tif ! ip -netns \"${rtdst_name}\" -6 rule show | \\\n+\t grep -q \"to ${VPN_LOCATOR_SERVICE}::/16 lookup ${LOCALSID_TABLE_ID}\"; then\n+\t\tip -netns \"${rtdst_name}\" -6 rule add \\\n+\t\t\tto \"${VPN_LOCATOR_SERVICE}::/16\" \\\n+\t\t\tlookup \"${LOCALSID_TABLE_ID}\" prio 999\n+\tfi\n+}\n+\n+# Configure rtsrc to reach rtdst's loopback address through the VPN.\n+# args:\n+# $1 - router id where the encap route is installed\n+# $2 - router id whose loopback is the destination\n+# $3 - tenant id\n+setup_vpn_gw_encap()\n+{\n+\tlocal rtsrc=\"$1\"\n+\tlocal rtdst=\"$2\"\n+\tlocal tid=\"$3\"\n+\tlocal sid\n+\n+\tsid=\"$(build_vpn_sid \"${rtsrc}\" \"${rtdst}\" \"${tid}\")\"\n+\n+\tset_gw_encap_route \"${rtsrc}\" \"${rtdst}\" \"${sid}\" \"${tid}\"\n+}\n+\n+setup()\n+{\n+\tip link add veth-rt-1 type veth peer name veth-rt-2\n+\tsetup_ns rt_1 rt_2\n+\tsetup_rt_networking 1\n+\tsetup_rt_networking 2\n+\n+\t# setup two hosts for the tenant 100.\n+\t# - host hs-t100-1 is directly connected to the router rt-1;\n+\t# - host hs-t100-2 is directly connected to the router rt-2.\n+\tsetup_ns hs_t100_1 hs_t100_2\n+\tsetup_hs 1 1 100\n+\tsetup_hs 2 2 100\n+\n+\t# setup two hosts for the tenant 200.\n+\t# - host hs-t200-3 is directly connected to the router rt-1;\n+\t# - host hs-t200-4 is directly connected to the router rt-2.\n+\tsetup_ns hs_t200_3 hs_t200_4\n+\tsetup_hs 3 1 200\n+\tsetup_hs 4 2 200\n+\n+\t# configure each router for each tenant: VRF, blackhole routes,\n+\t# router loopback interface\n+\tsetup_rt 1 100\n+\tsetup_rt 2 100\n+\tsetup_rt 1 200\n+\tsetup_rt 2 200\n+\n+\t# setup the L3 VPN which connects the host hs-t100-1 and host hs-t100-2\n+\t# within the same tenant 100.\n+\tsetup_vpn_config 1 2 2 100\n+\tsetup_vpn_config 2 1 1 100\n+\n+\t# setup the L3 VPN which connects the host hs-t200-3 and host hs-t200-4\n+\t# within the same tenant 200.\n+\tsetup_vpn_config 1 4 2 200\n+\tsetup_vpn_config 2 3 1 200\n+\n+\t# allow each router to reach the other's loopback through the VPN\n+\tsetup_vpn_gw_encap 2 1 100\n+\tsetup_vpn_gw_encap 1 2 100\n+\tsetup_vpn_gw_encap 2 1 200\n+\tsetup_vpn_gw_encap 1 2 200\n+\n+\t# testing environment was set up successfully\n+\tSETUP_ERR=0\n+}\n+\n+check_rt_connectivity()\n+{\n+\tlocal rtsrc=\"$1\"\n+\tlocal rtdst=\"$2\"\n+\tlocal nsname\n+\n+\tnsname=\"$(get_rt_nsname \"${rtsrc}\")\"\n+\n+\tip netns exec \"${nsname}\" ping -c 1 -W 1 \"${IPv6_RT_NETWORK}::${rtdst}\" \\\n+\t\t\u003e/dev/null 2\u003e\u00261\n+}\n+\n+check_and_log_rt_connectivity()\n+{\n+\tlocal rtsrc=\"$1\"\n+\tlocal rtdst=\"$2\"\n+\n+\tcheck_rt_connectivity \"${rtsrc}\" \"${rtdst}\"\n+\tlog_test $? 0 \"Routers connectivity: rt-${rtsrc} -\u003e rt-${rtdst}\"\n+}\n+\n+check_hs_ipv6_connectivity()\n+{\n+\tlocal hssrc=\"$1\"\n+\tlocal hsdst=\"$2\"\n+\tlocal tid=\"$3\"\n+\tlocal nsname\n+\n+\tnsname=\"$(get_hs_nsname \"${tid}\" \"${hssrc}\")\"\n+\n+\tip netns exec \"${nsname}\" ping -c 1 -W \"${PING_TIMEOUT_SEC}\" \\\n+\t\t\"${IPv6_HS_NETWORK}::${hsdst}\" \u003e/dev/null 2\u003e\u00261\n+}\n+\n+check_hs_ipv4_connectivity()\n+{\n+\tlocal hssrc=\"$1\"\n+\tlocal hsdst=\"$2\"\n+\tlocal tid=\"$3\"\n+\tlocal nsname\n+\n+\tnsname=\"$(get_hs_nsname \"${tid}\" \"${hssrc}\")\"\n+\n+\tip netns exec \"${nsname}\" ping -c 1 -W \"${PING_TIMEOUT_SEC}\" \\\n+\t\t\"${IPv4_HS_NETWORK}.${hsdst}\" \u003e/dev/null 2\u003e\u00261\n+}\n+\n+check_and_log_hs_connectivity()\n+{\n+\tlocal hssrc=\"$1\"\n+\tlocal hsdst=\"$2\"\n+\tlocal tid=\"$3\"\n+\n+\tcheck_hs_ipv6_connectivity \"${hssrc}\" \"${hsdst}\" \"${tid}\"\n+\tlog_test $? 0 \"IPv6 connectivity: hs-t${tid}-${hssrc} -\u003e hs-t${tid}-${hsdst} (tenant ${tid})\"\n+\n+\tcheck_hs_ipv4_connectivity \"${hssrc}\" \"${hsdst}\" \"${tid}\"\n+\tlog_test $? 0 \"IPv4 connectivity: hs-t${tid}-${hssrc} -\u003e hs-t${tid}-${hsdst} (tenant ${tid})\"\n+}\n+\n+check_and_log_hs_isolation()\n+{\n+\tlocal hssrc=\"$1\"\n+\tlocal tidsrc=\"$2\"\n+\tlocal hsdst=\"$3\"\n+\tlocal tiddst=\"$4\"\n+\n+\tcheck_hs_ipv6_connectivity \"${hssrc}\" \"${hsdst}\" \"${tidsrc}\"\n+\tlog_test $? 1 \"IPv6 isolation: hs-t${tidsrc}-${hssrc} -X-\u003e hs-t${tiddst}-${hsdst}\"\n+\n+\tcheck_hs_ipv4_connectivity \"${hssrc}\" \"${hsdst}\" \"${tidsrc}\"\n+\tlog_test $? 1 \"IPv4 isolation: hs-t${tidsrc}-${hssrc} -X-\u003e hs-t${tiddst}-${hsdst}\"\n+}\n+\n+check_and_log_hs2gw_connectivity()\n+{\n+\tlocal hssrc=\"$1\"\n+\tlocal tid=\"$2\"\n+\n+\tcheck_hs_ipv6_connectivity \"${hssrc}\" 254 \"${tid}\"\n+\tlog_test $? 0 \"IPv6 connectivity: hs-t${tid}-${hssrc} -\u003e gw (tenant ${tid})\"\n+\n+\tcheck_hs_ipv4_connectivity \"${hssrc}\" 254 \"${tid}\"\n+\tlog_test $? 0 \"IPv4 connectivity: hs-t${tid}-${hssrc} -\u003e gw (tenant ${tid})\"\n+}\n+\n+router_tests()\n+{\n+\tlog_section \"IPv6 routers connectivity test\"\n+\n+\tcheck_and_log_rt_connectivity 1 2\n+\tcheck_and_log_rt_connectivity 2 1\n+}\n+\n+host2gateway_tests()\n+{\n+\tlog_section \"Connectivity test among hosts and gateway\"\n+\n+\tcheck_and_log_hs2gw_connectivity 1 100\n+\tcheck_and_log_hs2gw_connectivity 2 100\n+\n+\tcheck_and_log_hs2gw_connectivity 3 200\n+\tcheck_and_log_hs2gw_connectivity 4 200\n+}\n+\n+host_vpn_tests()\n+{\n+\tlog_section \"SRv6 VPN connectivity test among hosts in the same tenant\"\n+\n+\tcheck_and_log_hs_connectivity 1 2 100\n+\tcheck_and_log_hs_connectivity 2 1 100\n+\n+\tcheck_and_log_hs_connectivity 3 4 200\n+\tcheck_and_log_hs_connectivity 4 3 200\n+}\n+\n+host_vpn_isolation_tests()\n+{\n+\tlocal l1=\"1 2\"\n+\tlocal l2=\"3 4\"\n+\tlocal t1=100\n+\tlocal t2=200\n+\tlocal i\n+\tlocal j\n+\tlocal tmp\n+\n+\tlog_section \"SRv6 VPN isolation test among hosts in different tenants\"\n+\n+\tfor _ in 0 1; do\n+\t\tfor i in ${l1}; do\n+\t\t\tfor j in ${l2}; do\n+\t\t\t\tcheck_and_log_hs_isolation \"${i}\" \"${t1}\" \"${j}\" \"${t2}\"\n+\t\t\tdone\n+\t\tdone\n+\n+\t\t# let us test the reverse path\n+\t\ttmp=\"${l1}\"; l1=\"${l2}\"; l2=\"${tmp}\"\n+\t\ttmp=${t1}; t1=${t2}; t2=${tmp}\n+\tdone\n+}\n+\n+__test_nolookup()\n+{\n+\tlocal hssrc=\"$1\"\n+\tlocal hsdst=\"$2\"\n+\tlocal rtsrc=\"$3\"\n+\tlocal rtdst=\"$4\"\n+\tlocal tid=\"$5\"\n+\tlocal vpn_sid\n+\n+\tvpn_sid=\"$(build_vpn_sid \"${rtsrc}\" \"${rtdst}\" \"${tid}\")\"\n+\n+\t# replace encap route(s) without \"lookup\" attribute\n+\tset_host_encap_route_nolookup \"${rtsrc}\" \"${hsdst}\" \"${vpn_sid}\" \"${tid}\"\n+\n+\tcheck_hs_ipv6_connectivity \"${hssrc}\" \"${hsdst}\" \"${tid}\"\n+\tlog_test $? 1 \"IPv6 w/o lookup: hs-t${tid}-${hssrc} -X-\u003e hs-t${tid}-${hsdst} (tenant ${tid})\"\n+\n+\tcheck_hs_ipv4_connectivity \"${hssrc}\" \"${hsdst}\" \"${tid}\"\n+\tlog_test $? 1 \"IPv4 w/o lookup: hs-t${tid}-${hssrc} -X-\u003e hs-t${tid}-${hsdst} (tenant ${tid})\"\n+\n+\t# restore encap route(s) with \"lookup\" for subsequent tests\n+\tset_host_encap_route \"${rtsrc}\" \"${hsdst}\" \"${vpn_sid}\" \"${tid}\"\n+}\n+\n+host_vpn_nolookup_tests()\n+{\n+\tlog_section \"SRv6 VPN connectivity test among hosts w/o lookup\"\n+\n+\t__test_nolookup 1 2 1 2 100\n+\t__test_nolookup 2 1 2 1 100\n+\n+\t__test_nolookup 3 4 1 2 200\n+\t__test_nolookup 4 3 2 1 200\n+}\n+\n+check_gw_ipv6_connectivity()\n+{\n+\tlocal rtsrc=\"$1\"\n+\tlocal rtdst=\"$2\"\n+\tlocal tidsrc=\"$3\"\n+\tlocal tiddst=\"$4\"\n+\tlocal rtname\n+\tlocal src_v6\n+\tlocal dst_v6\n+\n+\trtname=\"$(get_rt_nsname \"${rtsrc}\")\"\n+\tsrc_v6=\"${IPv6_HS_NETWORK}::$(get_rlo_hostid \"${rtsrc}\" \"${tidsrc}\")\"\n+\tdst_v6=\"${IPv6_HS_NETWORK}::$(get_rlo_hostid \"${rtdst}\" \"${tiddst}\")\"\n+\n+\tip netns exec \"${rtname}\" ip vrf exec \"vrf-${tidsrc}\" \\\n+\t\tping -c 1 -W \"${PING_TIMEOUT_SEC}\" \\\n+\t\t-I \"${src_v6}\" \"${dst_v6}\" \u003e/dev/null 2\u003e\u00261\n+}\n+\n+check_gw_ipv4_connectivity()\n+{\n+\tlocal rtsrc=\"$1\"\n+\tlocal rtdst=\"$2\"\n+\tlocal tidsrc=\"$3\"\n+\tlocal tiddst=\"$4\"\n+\tlocal rtname\n+\tlocal src_v4\n+\tlocal dst_v4\n+\n+\trtname=\"$(get_rt_nsname \"${rtsrc}\")\"\n+\tsrc_v4=\"${IPv4_HS_NETWORK}.$(get_rlo_hostid \"${rtsrc}\" \"${tidsrc}\")\"\n+\tdst_v4=\"${IPv4_HS_NETWORK}.$(get_rlo_hostid \"${rtdst}\" \"${tiddst}\")\"\n+\n+\tip netns exec \"${rtname}\" ip vrf exec \"vrf-${tidsrc}\" \\\n+\t\tping -c 1 -W \"${PING_TIMEOUT_SEC}\" \\\n+\t\t-I \"${src_v4}\" \"${dst_v4}\" \u003e/dev/null 2\u003e\u00261\n+}\n+\n+check_and_log_gw_connectivity()\n+{\n+\tlocal rtsrc=\"$1\"\n+\tlocal rtdst=\"$2\"\n+\tlocal tid=\"$3\"\n+\n+\tcheck_gw_ipv6_connectivity \"${rtsrc}\" \"${rtdst}\" \"${tid}\" \"${tid}\"\n+\tlog_test $? 0 \"IPv6 connectivity: rt-${rtsrc} -\u003e rt-${rtdst} (tenant ${tid})\"\n+\n+\tcheck_gw_ipv4_connectivity \"${rtsrc}\" \"${rtdst}\" \"${tid}\" \"${tid}\"\n+\tlog_test $? 0 \"IPv4 connectivity: rt-${rtsrc} -\u003e rt-${rtdst} (tenant ${tid})\"\n+}\n+\n+check_and_log_gw_isolation()\n+{\n+\tlocal rtsrc=\"$1\"\n+\tlocal rtdst=\"$2\"\n+\tlocal tidsrc=\"$3\"\n+\tlocal tiddst=\"$4\"\n+\n+\tcheck_gw_ipv6_connectivity \"${rtsrc}\" \"${rtdst}\" \"${tidsrc}\" \"${tiddst}\"\n+\tlog_test $? 1 \"IPv6 isolation: rt-${rtsrc} -X-\u003e rt-${rtdst} (tenants ${tidsrc}/${tiddst})\"\n+\n+\tcheck_gw_ipv4_connectivity \"${rtsrc}\" \"${rtdst}\" \"${tidsrc}\" \"${tiddst}\"\n+\tlog_test $? 1 \"IPv4 isolation: rt-${rtsrc} -X-\u003e rt-${rtdst} (tenants ${tidsrc}/${tiddst})\"\n+}\n+\n+gw_vpn_isolation_tests()\n+{\n+\tlog_section \"SRv6 VPN isolation test among routers in different tenants\"\n+\n+\tcheck_and_log_gw_isolation 1 2 100 200\n+\tcheck_and_log_gw_isolation 2 1 100 200\n+\n+\tcheck_and_log_gw_isolation 1 2 200 100\n+\tcheck_and_log_gw_isolation 2 1 200 100\n+}\n+\n+gw_vpn_tests()\n+{\n+\tlog_section \"SRv6 VPN connectivity test among routers in the same tenant\"\n+\n+\tcheck_and_log_gw_connectivity 1 2 100\n+\tcheck_and_log_gw_connectivity 2 1 100\n+\n+\tcheck_and_log_gw_connectivity 1 2 200\n+\tcheck_and_log_gw_connectivity 2 1 200\n+}\n+\n+__test_gw_nolookup()\n+{\n+\tlocal rtsrc=\"$1\"\n+\tlocal rtdst=\"$2\"\n+\tlocal tid=\"$3\"\n+\tlocal sid\n+\n+\tsid=\"$(build_vpn_sid \"${rtsrc}\" \"${rtdst}\" \"${tid}\")\"\n+\n+\t# replace gw encap route without \"lookup\" attribute\n+\tset_gw_encap_route_nolookup \"${rtsrc}\" \"${rtdst}\" \"${sid}\" \"${tid}\"\n+\n+\tcheck_gw_ipv6_connectivity \"${rtsrc}\" \"${rtdst}\" \"${tid}\" \"${tid}\"\n+\tlog_test $? 1 \"IPv6 w/o lookup: rt-${rtsrc} -X-\u003e rt-${rtdst} (tenant ${tid})\"\n+\n+\tcheck_gw_ipv4_connectivity \"${rtsrc}\" \"${rtdst}\" \"${tid}\" \"${tid}\"\n+\tlog_test $? 1 \"IPv4 w/o lookup: rt-${rtsrc} -X-\u003e rt-${rtdst} (tenant ${tid})\"\n+\n+\t# restore gw encap route with \"lookup\" for subsequent tests\n+\tset_gw_encap_route \"${rtsrc}\" \"${rtdst}\" \"${sid}\" \"${tid}\"\n+}\n+\n+gw_vpn_nolookup_tests()\n+{\n+\tlog_section \"SRv6 VPN connectivity test among routers w/o lookup\"\n+\n+\t__test_gw_nolookup 1 2 100\n+\t__test_gw_nolookup 2 1 100\n+\n+\t__test_gw_nolookup 1 2 200\n+\t__test_gw_nolookup 2 1 200\n+}\n+\n+test_command_or_ksft_skip()\n+{\n+\tlocal cmd=\"$1\"\n+\n+\tif [ ! -x \"$(command -v \"${cmd}\")\" ]; then\n+\t\techo \"SKIP: Could not run test without \\\"${cmd}\\\" tool\"\n+\t\texit \"${ksft_skip}\"\n+\tfi\n+}\n+\n+test_vrf_or_ksft_skip()\n+{\n+\tmodprobe vrf \u0026\u003e/dev/null || true\n+\tif [ ! -e /proc/sys/net/vrf/strict_mode ]; then\n+\t\techo \"SKIP: vrf sysctl does not exist\"\n+\t\texit \"${ksft_skip}\"\n+\tfi\n+}\n+\n+test_dummy_dev_or_ksft_skip()\n+{\n+\tlocal test_netns\n+\n+\tsetup_ns test_netns\n+\n+\tmodprobe dummy \u0026\u003e/dev/null || true\n+\tif ! ip -netns \"${test_netns}\" link add \"${DUMMY_DEVNAME}\" \\\n+\t\t\ttype dummy; then\n+\t\tcleanup_ns \"${test_netns}\"\n+\t\techo \"SKIP: dummy dev not supported\"\n+\t\texit \"${ksft_skip}\"\n+\tfi\n+\n+\tcleanup_ns \"${test_netns}\"\n+}\n+\n+test_encap_lookup_supp_or_ksft_skip()\n+{\n+\tlocal nsname\n+\n+\tsetup_ns nsname\n+\n+\tip -netns \"${nsname}\" link add \"${DUMMY_DEVNAME}\" type dummy\n+\tip -netns \"${nsname}\" link set \"${DUMMY_DEVNAME}\" up\n+\n+\tif ! ip -netns \"${nsname}\" -6 route add \"${IPv6_TESTS_ADDR}/128\" \\\n+\t\t\tencap seg6 mode encap segs fc00::1 \\\n+\t\t\tlookup \"${TESTS_TABLE_ID}\" \\\n+\t\t\tdev \"${DUMMY_DEVNAME}\" 2\u003e/dev/null; then\n+\t\tcleanup_ns \"${nsname}\"\n+\t\techo \"SKIP: seg6 encap lookup attribute not supported\"\n+\t\texit \"${ksft_skip}\"\n+\tfi\n+\n+\t# An old kernel with a recent iproute2 accepts the route but\n+\t# silently ignores the lookup attribute. Dump the route and check\n+\t# the attribute is really there, otherwise the test falsely passes.\n+\tif ! ip -netns \"${nsname}\" -6 route show \"${IPv6_TESTS_ADDR}/128\" | \\\n+\t\t\tgrep -q \"lookup ${TESTS_TABLE_ID}\"; then\n+\t\tcleanup_ns \"${nsname}\"\n+\t\techo \"SKIP: seg6 encap lookup attribute not supported\"\n+\t\texit \"${ksft_skip}\"\n+\tfi\n+\n+\tcleanup_ns \"${nsname}\"\n+}\n+\n+if [ \"$(id -u)\" -ne 0 ]; then\n+\techo \"SKIP: Need root privileges\"\n+\texit \"${ksft_skip}\"\n+fi\n+\n+# required programs to carry out this selftest\n+test_command_or_ksft_skip ip\n+test_command_or_ksft_skip ping\n+test_command_or_ksft_skip sysctl\n+test_command_or_ksft_skip grep\n+\n+test_dummy_dev_or_ksft_skip\n+test_vrf_or_ksft_skip\n+test_encap_lookup_supp_or_ksft_skip\n+\n+set -e\n+trap cleanup EXIT\n+\n+setup\n+set +e\n+\n+router_tests\n+host2gateway_tests\n+host_vpn_tests\n+host_vpn_isolation_tests\n+host_vpn_nolookup_tests\n+gw_vpn_tests\n+gw_vpn_isolation_tests\n+gw_vpn_nolookup_tests\n+\n+print_log_test_results\n"
} |
| 2/1 | 2026/07/12 08:54 | agent | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": [
"IPV6_SEG6_LWTUNNEL"
],
"FocusSymbols": [
"seg6_table_lookup",
"seg6_input_route",
"seg6_output_dst_lookup",
"seg6_build_state",
"seg6_fill_encap_info",
"seg6_encap_nlsize",
"seg6_encap_cmp"
],
"Reasoning": "The patch adds a new Netlink attribute `SEG6_IPTUNNEL_TABLE` to specify a FIB table for post-encap SID lookup in SRv6 tunnels. It modifies the SRv6 encapsulation logic in `net/ipv6/seg6_iptunnel.c` to perform route lookups in the specified table during both input and output paths. This is a functional change to reachable core kernel networking logic, specifically the IPv6 Segment Routing subsystem, and should be fuzzed.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit bd0f47a88b0d5094b9fd431ca04c21e8f66c7f01
Author: syz-cluster <triage@syzkaller.com>
Date: Sun Jul 12 08:54:14 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/uapi/linux/seg6_iptunnel.h b/include/uapi/linux/seg6_iptunnel.h
index 485889b19900ea..e1964b3a4bb0af 100644
--- a/include/uapi/linux/seg6_iptunnel.h
+++ b/include/uapi/linux/seg6_iptunnel.h
@@ -21,6 +21,7 @@ enum {
SEG6_IPTUNNEL_UNSPEC,
SEG6_IPTUNNEL_SRH,
SEG6_IPTUNNEL_SRC, /* struct in6_addr */
+ SEG6_IPTUNNEL_TABLE, /* __u32 FIB table for post-encap SID lookup */
__SEG6_IPTUNNEL_MAX,
};
#define SEG6_IPTUNNEL_MAX (__SEG6_IPTUNNEL_MAX - 1)
diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
index 4c45c0a77d75d0..61c6a27bf202dd 100644
--- a/net/ipv6/seg6_iptunnel.c
+++ b/net/ipv6/seg6_iptunnel.c
@@ -51,6 +51,7 @@ struct seg6_lwt {
struct dst_cache cache_input;
struct dst_cache cache_output;
struct in6_addr tunsrc;
+ u32 table;
struct seg6_iptunnel_encap tuninfo[];
};
@@ -68,6 +69,7 @@ seg6_encap_lwtunnel(struct lwtunnel_state *lwt)
static const struct nla_policy seg6_iptunnel_policy[SEG6_IPTUNNEL_MAX + 1] = {
[SEG6_IPTUNNEL_SRH] = { .type = NLA_BINARY },
[SEG6_IPTUNNEL_SRC] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),
+ [SEG6_IPTUNNEL_TABLE] = { .type = NLA_U32 },
};
static int nla_put_srh(struct sk_buff *skb, int attrtype,
@@ -479,6 +481,73 @@ int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
}
EXPORT_SYMBOL_GPL(seg6_do_srh_inline);
+/* look up a route in a specific FIB table.
+ * Returns a refcounted dst, or NULL if the table does not exist.
+ */
+static struct dst_entry *seg6_table_lookup(struct net *net,
+ struct sk_buff *skb,
+ struct flowi6 *fl6, u32 tbl_id)
+{
+ struct fib6_table *table;
+ struct rt6_info *rt;
+
+ table = fib6_get_table(net, tbl_id);
+ if (!table)
+ return NULL;
+
+ rt = ip6_pol_route(net, table, 0, fl6, skb, RT6_LOOKUP_F_HAS_SADDR);
+ return &rt->dst;
+}
+
+static void seg6_init_flowi6(struct sk_buff *skb, struct ipv6hdr *hdr,
+ struct flowi6 *fl6)
+{
+ memset(fl6, 0, sizeof(*fl6));
+
+ fl6->daddr = hdr->daddr;
+ fl6->saddr = hdr->saddr;
+ fl6->flowlabel = ip6_flowinfo(hdr);
+ fl6->flowi6_mark = skb->mark;
+ fl6->flowi6_proto = hdr->nexthdr;
+}
+
+/* look up the route for the first SID on the input path and set it on the skb.
+ * Returns the refcounted dst, or NULL if a reference could not be safely taken.
+ */
+static struct dst_entry *seg6_input_route(struct net *net,
+ struct sk_buff *skb,
+ struct seg6_lwt *slwt)
+{
+ u32 table = slwt->table;
+
+ if (table) {
+ struct ipv6hdr *hdr = ipv6_hdr(skb);
+ struct dst_entry *dst;
+ struct flowi6 fl6;
+
+ seg6_init_flowi6(skb, hdr, &fl6);
+ fl6.flowi6_iif = skb->dev->ifindex;
+
+ dst = seg6_table_lookup(net, skb, &fl6, table);
+ if (!dst) {
+ dst = &net->ipv6.ip6_blk_hole_entry->dst;
+ dst_hold(dst);
+ }
+
+ skb_dst_drop(skb);
+ skb_dst_set(skb, dst);
+ } else {
+ ip6_route_input(skb);
+
+ /* ip6_route_input() sets a NOREF dst; force a refcount on it
+ * before caching or further use.
+ */
+ skb_dst_force(skb);
+ }
+
+ return skb_dst(skb);
+}
+
static int seg6_input_finish(struct net *net, struct sock *sk,
struct sk_buff *skb)
{
@@ -513,15 +582,9 @@ static int seg6_input_core(struct net *net, struct sock *sk,
goto drop;
}
- if (!dst) {
- ip6_route_input(skb);
-
- /* ip6_route_input() sets a NOREF dst; force a refcount on it
- * before caching or further use.
- */
- skb_dst_force(skb);
- dst = skb_dst(skb);
- if (unlikely(!dst)) {
+ if (unlikely(!dst)) {
+ dst = seg6_input_route(net, skb, slwt);
+ if (!dst) {
err = -ENETUNREACH;
goto drop;
}
@@ -578,6 +641,29 @@ static int seg6_input(struct sk_buff *skb)
return seg6_input_core(dev_net(skb->dev), NULL, skb);
}
+/* look up the route for the first SID on the output path. Always returns a
+ * refcounted dst.
+ */
+static struct dst_entry *seg6_output_dst_lookup(struct net *net,
+ struct sk_buff *skb,
+ struct flowi6 *fl6,
+ struct seg6_lwt *slwt)
+{
+ struct dst_entry *dst;
+
+ if (slwt->table) {
+ dst = seg6_table_lookup(net, skb, fl6, slwt->table);
+ if (!dst) {
+ dst = &net->ipv6.ip6_blk_hole_entry->dst;
+ dst_hold(dst);
+ }
+ } else {
+ dst = ip6_route_output(net, NULL, fl6);
+ }
+
+ return dst;
+}
+
static int seg6_output_core(struct net *net, struct sock *sk,
struct sk_buff *skb)
{
@@ -600,14 +686,9 @@ static int seg6_output_core(struct net *net, struct sock *sk,
struct ipv6hdr *hdr = ipv6_hdr(skb);
struct flowi6 fl6;
- memset(&fl6, 0, sizeof(fl6));
- fl6.daddr = hdr->daddr;
- fl6.saddr = hdr->saddr;
- fl6.flowlabel = ip6_flowinfo(hdr);
- fl6.flowi6_mark = skb->mark;
- fl6.flowi6_proto = hdr->nexthdr;
+ seg6_init_flowi6(skb, hdr, &fl6);
- dst = ip6_route_output(net, NULL, &fl6);
+ dst = seg6_output_dst_lookup(net, skb, &fl6, slwt);
if (dst->error) {
err = dst->error;
goto drop;
@@ -752,6 +833,15 @@ static int seg6_build_state(struct net *net, struct nlattr *nla,
}
}
+ if (tb[SEG6_IPTUNNEL_TABLE]) {
+ slwt->table = nla_get_u32(tb[SEG6_IPTUNNEL_TABLE]);
+ if (!slwt->table) {
+ NL_SET_ERR_MSG(extack, "invalid lookup table");
+ err = -EINVAL;
+ goto err_destroy_output;
+ }
+ }
+
newts->type = LWTUNNEL_ENCAP_SEG6;
newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT;
@@ -795,6 +885,10 @@ static int seg6_fill_encap_info(struct sk_buff *skb,
nla_put_in6_addr(skb, SEG6_IPTUNNEL_SRC, &slwt->tunsrc))
return -EMSGSIZE;
+ if (slwt->table &&
+ nla_put_u32(skb, SEG6_IPTUNNEL_TABLE, slwt->table))
+ return -EMSGSIZE;
+
return 0;
}
@@ -809,6 +903,9 @@ static int seg6_encap_nlsize(struct lwtunnel_state *lwtstate)
if (!ipv6_addr_any(&slwt->tunsrc))
nlsize += nla_total_size(sizeof(slwt->tunsrc));
+ if (slwt->table)
+ nlsize += nla_total_size(sizeof(u32));
+
return nlsize;
}
@@ -826,6 +923,9 @@ static int seg6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
if (!ipv6_addr_equal(&a_slwt->tunsrc, &b_slwt->tunsrc))
return 1;
+ if (a_slwt->table != b_slwt->table)
+ return 1;
+
return memcmp(a_hdr, b_hdr, len);
}
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 708d960ae07d5b..5df0c94ab06018 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -85,6 +85,7 @@ TEST_PROGS := \
rxtimestamp.sh \
sctp_vrf.sh \
skf_net_off.sh \
+ srv6_encap_lookup_l3vpn_test.sh \
srv6_end_dt46_l3vpn_test.sh \
srv6_end_dt4_l3vpn_test.sh \
srv6_end_dt6_l3vpn_test.sh \
diff --git a/tools/testing/selftests/net/srv6_encap_lookup_l3vpn_test.sh b/tools/testing/selftests/net/srv6_encap_lookup_l3vpn_test.sh
new file mode 100755
index 00000000000000..d6249303b7eaa1
--- /dev/null
+++ b/tools/testing/selftests/net/srv6_encap_lookup_l3vpn_test.sh
@@ -0,0 +1,1027 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# author: Andrea Mayer <andrea.mayer@uniroma2.it>
+
+# This test evaluates the SRv6 encap "lookup" attribute. After encapsulation
+# the router looks up the route for the first SID, that is the outer IPv6
+# destination of the encapsulated packet. The attribute selects the FIB table
+# used for this post-encap SID route lookup.
+#
+# Two routers (rt-1, rt-2) provide L3 VPN services over an IPv6 underlay
+# (fd00::/64). Each router uses a separate VRF per tenant, with default
+# blackhole routes (IPv4 and IPv6) to prevent traffic from leaking out of
+# the VRF. Tenant traffic is encapsulated, then decapsulated with an
+# End.DT46. Each router proxies both NDP and ARP.
+#
+# The routes that match the first SIDs are installed in a dedicated underlay
+# table (500) rather than the main table (254). The encap routes use
+# "lookup 500" to select this table for the post-encap SID route lookup.
+#
+# Without the "lookup" attribute, the route for the first SID cannot be found:
+# - on the input path (forwarded traffic), the lookup stays in the VRF
+# and hits the blackhole;
+# - on the output path (locally originated traffic), the lookup falls
+# through to the main table, with no route to the first SID.
+#
+#
+# Legend (specific per-tenant addresses are in the instantiation tables below):
+# X = tenant id and VRF table id; two tenants: 100 and 200
+# ("tX" means tenant X, e.g. t100, t200)
+# a, b = the two host ids of the tenant
+# HA, HB = addresses of host a, host b
+# RLO1, RLO2 = rlo-X router loopback address on rt-1, rt-2 (tenant gateway
+# for the output path, dual-stack)
+# vrf-X = per-tenant VRF on each router (table X)
+#
+# Constants (same for every tenant):
+# underlay = table 500; post-encap SID route lookup (via "lookup 500")
+# localsid = table 90; holds the decap SIDs (End.DT46)
+# fd00::/64 = underlay link between rt-1 and rt-2
+# veth-tX = cafe::254/10.0.0.254 (tenant gateway on veth, both routers)
+#
+#
+# +-------------------+ +-------------------+
+# | | | |
+# | hs-tX-a netns | | hs-tX-b netns |
+# | | | |
+# | +-------------+ | | +-------------+ |
+# | | veth0 | | | | veth0 | |
+# | | HA | | | | HB | |
+# | +-------------+ | | +-------------+ |
+# | . | | . |
+# +-------------------+ +-------------------+
+# . .
+# . .
+# +-----------------------------------+ +-----------------------------------+
+# | . | | . |
+# | +---------------+ | | +---------------+ |
+# | | veth-tX | +----------+ | | +----------+ | veth-tX | |
+# | | ::254/.254 | | localsid | | | | localsid | | ::254/.254 | |
+# | +-------+-------+ +----------+ | | +----------+ +-------+-------+ |
+# | | +----------+ | | +----------+ | |
+# | +----+----+ | underlay | | | | underlay | +----+----+ |
+# | | vrf-X | +----------+ | | +----------+ | vrf-X | |
+# | +----+----+ | | +----+----+ |
+# | | | | | |
+# | +-----+----+ +------------+ | | +------------+ +----+-----+ |
+# | | rlo-X | | veth0 | | | | veth0 | | rlo-X | |
+# | | RLO1 | | fd00::1/64 |..|...|..| fd00::2/64 | | RLO2 | |
+# | +----------+ +------------+ | | +------------+ +----------+ |
+# | rt-1 netns | | rt-2 netns |
+# +-----------------------------------+ +-----------------------------------+
+#
+#
+# Per-tenant instantiation:
+# +-----+------+-------------------+-------------------+
+# | X | a, b | HA | HB |
+# +-----+------+-------------------+-------------------+
+# | 100 | 1, 2 | cafe::1, 10.0.0.1 | cafe::2, 10.0.0.2 |
+# | 200 | 3, 4 | cafe::3, 10.0.0.3 | cafe::4, 10.0.0.4 |
+# +-----+------+-------------------+-------------------+
+#
+# Router loopback (rlo-X) addresses, per tenant:
+# +-----+-----------------------+-----------------------+
+# | X | RLO1 (rt-1) | RLO2 (rt-2) |
+# +-----+-----------------------+-----------------------+
+# | 100 | cafe::101, 10.0.0.101 | cafe::102, 10.0.0.102 |
+# | 200 | cafe::201, 10.0.0.201 | cafe::202, 10.0.0.202 |
+# +-----+-----------------------+-----------------------+
+#
+#
+# Network configuration
+# =====================
+#
+# rt-1: localsid table (table 90)
+# +--------+--------------------+----------------------------------+
+# | tenant | SID | Action |
+# +--------+--------------------+----------------------------------+
+# | 100 | fc00:2:1:100::0d46 | apply SRv6 End.DT46 vrftable 100 |
+# | 200 | fc00:2:1:200::0d46 | apply SRv6 End.DT46 vrftable 200 |
+# +--------+--------------------+----------------------------------+
+#
+# rt-1: underlay table (table 500) - post-encap SID route lookup
+# +--------+--------------------+-------------------------------+
+# | tenant | SID | Action |
+# +--------+--------------------+-------------------------------+
+# | 100 | fc00:1:2:100::0d46 | forward via fd00::2 dev veth0 |
+# | 200 | fc00:1:2:200::0d46 | forward via fd00::2 dev veth0 |
+# +--------+--------------------+-------------------------------+
+#
+# rt-1: VRF tables (per tenant: vrf-X = table X)
+# +--------+------------+------------------------------------------+
+# | tenant | dst | encap action |
+# +--------+------------+------------------------------------------+
+# | 100 | cafe::2 | encap segs fc00:1:2:100::0d46 lookup 500 |
+# | | 10.0.0.2 | |
+# | | cafe::102 | |
+# | | 10.0.0.102 | |
+# | 200 | cafe::4 | encap segs fc00:1:2:200::0d46 lookup 500 |
+# | | 10.0.0.4 | |
+# | | cafe::202 | |
+# | | 10.0.0.202 | |
+# +--------+------------+------------------------------------------+
+#
+#
+# rt-2: localsid table (table 90)
+# +--------+--------------------+----------------------------------+
+# | tenant | SID | Action |
+# +--------+--------------------+----------------------------------+
+# | 100 | fc00:1:2:100::0d46 | apply SRv6 End.DT46 vrftable 100 |
+# | 200 | fc00:1:2:200::0d46 | apply SRv6 End.DT46 vrftable 200 |
+# +--------+--------------------+----------------------------------+
+#
+# rt-2: underlay table (table 500) - post-encap SID route lookup
+# +--------+--------------------+-------------------------------+
+# | tenant | SID | Action |
+# +--------+--------------------+-------------------------------+
+# | 100 | fc00:2:1:100::0d46 | forward via fd00::1 dev veth0 |
+# | 200 | fc00:2:1:200::0d46 | forward via fd00::1 dev veth0 |
+# +--------+--------------------+-------------------------------+
+#
+# rt-2: VRF tables (per tenant: vrf-X = table X)
+# +--------+------------+------------------------------------------+
+# | tenant | dst | encap action |
+# +--------+------------+------------------------------------------+
+# | 100 | cafe::1 | encap segs fc00:2:1:100::0d46 lookup 500 |
+# | | 10.0.0.1 | |
+# | | cafe::101 | |
+# | | 10.0.0.101 | |
+# | 200 | cafe::3 | encap segs fc00:2:1:200::0d46 lookup 500 |
+# | | 10.0.0.3 | |
+# | | cafe::201 | |
+# | | 10.0.0.201 | |
+# +--------+------------+------------------------------------------+
+# Within a tenant, a single SID reaches the adjacent router (its loopback)
+# and the remote host connected to it, in both IPv4 and IPv6.
+#
+# For both rt-1 and rt-2, each VRF also has the connected host prefix (cafe::/64
+# or 10.0.0.0/24) and a default blackhole (IPv4 and IPv6).
+#
+#
+# Locally originated traffic (output path)
+# ========================================
+#
+# The configuration above covers forwarded traffic, where packets arrive from
+# a host and are encapsulated by the router. To also test router-originated
+# traffic, each router pings the other router's loopback address through
+# the VPN.
+#
+# Example (tenant 100), rt-1 pings cafe::102 (rt-2's loopback):
+# 1. rt-1 looks up cafe::102 in vrf-100 and encapsulates it (SID
+# fc00:1:2:100::0d46), then "lookup 500" finds the route for the SID in the
+# underlay table (next hop fd00::2) and forwards it;
+# 2. rt-2 decapsulates it (localsid, End.DT46) and delivers it locally
+# (cafe::102 is on the rlo-100 interface);
+# 3. rt-2 replies with destination cafe::101 (rt-1's loopback). rt-2 looks up
+# cafe::101 in vrf-100 and encapsulates it back to rt-1 (again via "lookup
+# 500"). rt-1 decapsulates it and delivers it.
+
+# shellcheck source=lib.sh
+source lib.sh
+
+readonly LOCALSID_TABLE_ID=90
+readonly UNDERLAY_TABLE_ID=500
+readonly IPv6_RT_NETWORK=fd00
+readonly IPv6_HS_NETWORK=cafe
+readonly IPv4_HS_NETWORK=10.0.0
+readonly VPN_LOCATOR_SERVICE=fc00
+readonly DT46_FUNC=0d46
+readonly DUMMY_DEVNAME=dum0
+readonly IPv6_TESTS_ADDR=2001:db8::1
+readonly TESTS_TABLE_ID=54321
+PING_TIMEOUT_SEC=4
+
+SETUP_ERR=1
+
+ret=${ksft_skip}
+nsuccess=0
+nfail=0
+
+PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
+
+log_test()
+{
+ local rc="$1"
+ local expected="$2"
+ local msg="$3"
+
+ if [ "${rc}" -eq "${expected}" ]; then
+ nsuccess=$((nsuccess+1))
+ printf "\n TEST: %-60s [ OK ]\n" "${msg}"
+ else
+ ret=1
+ nfail=$((nfail+1))
+ printf "\n TEST: %-60s [FAIL]\n" "${msg}"
+ if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
+ echo
+ echo "hit enter to continue, 'q' to quit"
+ read -r a
+ [ "$a" = "q" ] && exit 1
+ fi
+ fi
+}
+
+print_log_test_results()
+{
+ printf "\nTests passed: %3d\n" "${nsuccess}"
+ printf "Tests failed: %3d\n" "${nfail}"
+
+ # when a test fails, the value of 'ret' is set to 1 (error code).
+ # Conversely, when all tests are passed successfully, the 'ret' value
+ # is set to 0 (success code).
+ if [ "${ret}" -ne 1 ]; then
+ ret=0
+ fi
+}
+
+log_section()
+{
+ echo
+ echo "################################################################################"
+ echo "TEST SECTION: $*"
+ echo "################################################################################"
+}
+
+get_rtname()
+{
+ local rtid="$1"
+
+ echo "rt_${rtid}"
+}
+
+get_rt_nsname()
+{
+ local rtid="$1"
+ local varname
+
+ varname="$(get_rtname "${rtid}")"
+ echo "${!varname}"
+}
+
+get_hsname()
+{
+ local tid="$1"
+ local hsid="$2"
+
+ echo "hs_t${tid}_${hsid}"
+}
+
+get_hs_nsname()
+{
+ local tid="$1"
+ local hsid="$2"
+ local varname
+
+ varname="$(get_hsname "${tid}" "${hsid}")"
+ echo "${!varname}"
+}
+
+cleanup()
+{
+ ip link del veth-rt-1 2>/dev/null || true
+ ip link del veth-rt-2 2>/dev/null || true
+
+ cleanup_all_ns
+
+ # check whether the setup phase was completed successfully or not. In
+ # case of an error during the setup phase of the testing environment,
+ # the selftest is considered as "skipped".
+ if [ "${SETUP_ERR}" -ne 0 ]; then
+ echo "SKIP: Setting up the testing environment failed"
+ exit "${ksft_skip}"
+ fi
+
+ exit "${ret}"
+}
+
+# Host id of the router loopback (rlo) for a (router, tenant) pair.
+# E.g. rt-1/tenant 100 -> 101, rt-2/tenant 200 -> 202.
+get_rlo_hostid()
+{
+ local rtid="$1"
+ local tid="$2"
+
+ echo "$((tid + rtid))"
+}
+
+build_vpn_sid()
+{
+ local rtsrc="$1"
+ local rtdst="$2"
+ local tid="$3"
+
+ echo "${VPN_LOCATOR_SERVICE}:${rtsrc}:${rtdst}:${tid}::${DT46_FUNC}"
+}
+
+# Install a dual-stack (IPv6 and IPv4) encap route in a VRF on the given
+# router.
+# args:
+# $1 - router id
+# $2 - host part of the IPv6 destination
+# $3 - host part of the IPv4 destination
+# $4 - SRv6 SID used as the encap destination
+# $5 - tenant id
+# $6 - if "true", add the "lookup" attribute to the encap route
+__set_encap_route()
+{
+ local rt="$1"
+ local dst6="$2"
+ local dst4="$3"
+ local sid="$4"
+ local tid="$5"
+ local use_lookup="$6"
+ local lookup=''
+ local rtname
+
+ rtname="$(get_rt_nsname "${rt}")"
+
+ if [ "${use_lookup}" = "true" ]; then
+ lookup="lookup ${UNDERLAY_TABLE_ID}"
+ fi
+
+ # shellcheck disable=SC2086
+ ip -netns "${rtname}" -6 route replace \
+ "${IPv6_HS_NETWORK}::${dst6}/128" vrf "vrf-${tid}" \
+ encap seg6 mode encap segs "${sid}" ${lookup} dev veth0
+
+ # shellcheck disable=SC2086
+ ip -netns "${rtname}" -4 route replace \
+ "${IPv4_HS_NETWORK}.${dst4}/32" vrf "vrf-${tid}" \
+ encap seg6 mode encap segs "${sid}" ${lookup} dev veth0
+}
+
+# Install the dual-stack encap route for a tenant host on rt, with the
+# "lookup" attribute so the first SID is looked up in the underlay table.
+# args:
+# $1 - router id where the encap route is installed
+# $2 - host destination id (host part of cafe::<id>/128 and 10.0.0.<id>/32)
+# $3 - SRv6 SID used as the encap destination
+# $4 - tenant id
+set_host_encap_route()
+{
+ local rt="$1"
+ local hsdst="$2"
+ local sid="$3"
+ local tid="$4"
+
+ __set_encap_route "${rt}" "${hsdst}" "${hsdst}" "${sid}" "${tid}" true
+}
+
+set_host_encap_route_nolookup()
+{
+ local rt="$1"
+ local hsdst="$2"
+ local sid="$3"
+ local tid="$4"
+
+ __set_encap_route "${rt}" "${hsdst}" "${hsdst}" "${sid}" "${tid}" false
+}
+
+# Install the dual-stack encap route on rtsrc toward rtdst's rlo loopback
+# (RLO1 or RLO2, see header), with the "lookup" attribute so the first
+# SID is looked up in the underlay table.
+# args:
+# $1 - router id where the encap route is installed
+# $2 - router id whose loopback address is the route destination
+# $3 - SRv6 SID used as the encap destination
+# $4 - tenant id
+set_gw_encap_route()
+{
+ local rtsrc="$1"
+ local rtdst="$2"
+ local sid="$3"
+ local tid="$4"
+ local dst
+
+ dst="$(get_rlo_hostid "${rtdst}" "${tid}")"
+
+ __set_encap_route "${rtsrc}" "${dst}" "${dst}" "${sid}" "${tid}" true
+}
+
+set_gw_encap_route_nolookup()
+{
+ local rtsrc="$1"
+ local rtdst="$2"
+ local sid="$3"
+ local tid="$4"
+ local dst
+
+ dst="$(get_rlo_hostid "${rtdst}" "${tid}")"
+
+ __set_encap_route "${rtsrc}" "${dst}" "${dst}" "${sid}" "${tid}" false
+}
+
+# Setup the basic networking for a router
+setup_rt_networking()
+{
+ local id="$1"
+ local nsname
+
+ nsname="$(get_rt_nsname "${id}")"
+
+ ip link set "veth-rt-${id}" netns "${nsname}"
+ ip -netns "${nsname}" link set "veth-rt-${id}" name veth0
+
+ ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.accept_dad=0
+ ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.default.accept_dad=0
+
+ ip -netns "${nsname}" addr add "${IPv6_RT_NETWORK}::${id}/64" dev veth0 nodad
+ ip -netns "${nsname}" link set veth0 up
+
+ ip netns exec "${nsname}" sysctl -wq net.ipv4.ip_forward=1
+ ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.forwarding=1
+}
+
+# Setup a host namespace and attach it to its gateway
+setup_hs()
+{
+ local hid="$1"
+ local rid="$2"
+ local tid="$3"
+ local rtveth="veth-t${tid}"
+ local hsname
+ local rtname
+
+ hsname="$(get_hs_nsname "${tid}" "${hid}")"
+ rtname="$(get_rt_nsname "${rid}")"
+
+ ip netns exec "${hsname}" sysctl -wq net.ipv6.conf.all.accept_dad=0
+ ip netns exec "${hsname}" sysctl -wq net.ipv6.conf.default.accept_dad=0
+
+ ip -netns "${hsname}" link add veth0 type veth peer name "${rtveth}"
+ ip -netns "${hsname}" link set "${rtveth}" netns "${rtname}"
+
+ ip -netns "${hsname}" addr add \
+ "${IPv6_HS_NETWORK}::${hid}/64" dev veth0 nodad
+ ip -netns "${hsname}" addr add \
+ "${IPv4_HS_NETWORK}.${hid}/24" dev veth0
+
+ ip -netns "${hsname}" link set veth0 up
+}
+
+# Setup the per-tenant VRF on a router (gateway, loopback, blackhole)
+setup_rt()
+{
+ local rid="$1"
+ local tid="$2"
+ local rtveth="veth-t${tid}"
+ local rlo_dev="rlo-${tid}"
+ local rtname
+ local gw_addr_v6
+ local gw_addr_v4
+
+ rtname="$(get_rt_nsname "${rid}")"
+
+ gw_addr_v6="${IPv6_HS_NETWORK}::$(get_rlo_hostid "${rid}" "${tid}")"
+ gw_addr_v4="${IPv4_HS_NETWORK}.$(get_rlo_hostid "${rid}" "${tid}")"
+
+ ip -netns "${rtname}" link add "vrf-${tid}" type vrf table "${tid}"
+ ip -netns "${rtname}" link set "vrf-${tid}" up
+
+ ip -netns "${rtname}" link set "${rtveth}" master "vrf-${tid}"
+
+ ip -netns "${rtname}" addr add \
+ "${IPv6_HS_NETWORK}::254/64" dev "${rtveth}" nodad
+ ip -netns "${rtname}" addr add \
+ "${IPv4_HS_NETWORK}.254/24" dev "${rtveth}"
+
+ ip -netns "${rtname}" link set "${rtveth}" up
+
+ ip netns exec "${rtname}" \
+ sysctl -wq "net.ipv6.conf.${rtveth}.proxy_ndp=1"
+ ip netns exec "${rtname}" \
+ sysctl -wq "net.ipv4.conf.${rtveth}.proxy_arp=1"
+
+ ip netns exec "${rtname}" sh -c "echo 1 > /proc/sys/net/vrf/strict_mode"
+
+ # router loopback interface for locally originated traffic
+ ip -netns "${rtname}" link add "${rlo_dev}" type dummy
+ ip -netns "${rtname}" link set "${rlo_dev}" master "vrf-${tid}"
+
+ ip -netns "${rtname}" addr add "${gw_addr_v6}/128" \
+ dev "${rlo_dev}" nodad
+ ip -netns "${rtname}" addr add "${gw_addr_v4}/32" \
+ dev "${rlo_dev}"
+
+ ip -netns "${rtname}" link set "${rlo_dev}" up
+
+ # default blackhole routes in the VRF: any traffic that does not match
+ # a specific route is dropped. Without the "lookup" attribute on the
+ # encap route, the route for the first SID cannot be found from within
+ # the VRF.
+ ip -netns "${rtname}" -6 route add blackhole default metric 4278198272 \
+ vrf "vrf-${tid}"
+ ip -netns "${rtname}" -4 route add blackhole default metric 4278198272 \
+ vrf "vrf-${tid}"
+}
+
+# Configure a one-way VPN path towards hsdst (on rtdst) for tenant tid.
+# The encap side is set up on rtsrc and the decap side on rtdst.
+# args:
+# $1 - router id where the encap side is set up
+# $2 - host id of the destination host
+# $3 - router id of the destination router (connected to the destination host)
+# $4 - tenant id
+setup_vpn_config()
+{
+ local rtsrc="$1"
+ local hsdst="$2"
+ local rtdst="$3"
+ local tid="$4"
+ local rtveth="veth-t${tid}"
+ local rtsrc_name
+ local rtdst_name
+ local vpn_sid
+
+ rtsrc_name="$(get_rt_nsname "${rtsrc}")"
+ rtdst_name="$(get_rt_nsname "${rtdst}")"
+ vpn_sid="$(build_vpn_sid "${rtsrc}" "${rtdst}" "${tid}")"
+
+ ip -netns "${rtsrc_name}" -6 neigh add proxy \
+ "${IPv6_HS_NETWORK}::${hsdst}" dev "${rtveth}"
+ set_host_encap_route "${rtsrc}" "${hsdst}" "${vpn_sid}" "${tid}"
+
+ ip -netns "${rtsrc_name}" -6 route add "${vpn_sid}/128" \
+ table "${UNDERLAY_TABLE_ID}" \
+ via "fd00::${rtdst}" dev veth0
+
+ # set the decap route for decapsulating packets arriving from rtsrc
+ # and destined to hsdst
+ ip -netns "${rtdst_name}" -6 route add "${vpn_sid}/128" \
+ table "${LOCALSID_TABLE_ID}" \
+ encap seg6local action End.DT46 \
+ vrftable "${tid}" dev "vrf-${tid}"
+
+ # all SIDs for VPNs start with a common locator which is fc00::/16.
+ # Routes for handling the SRv6 End.DT* behavior instances are grouped
+ # together in the 'localsid' table.
+ #
+ # NOTE: added only once
+ if ! ip -netns "${rtdst_name}" -6 rule show | \
+ grep -q "to ${VPN_LOCATOR_SERVICE}::/16 lookup ${LOCALSID_TABLE_ID}"; then
+ ip -netns "${rtdst_name}" -6 rule add \
+ to "${VPN_LOCATOR_SERVICE}::/16" \
+ lookup "${LOCALSID_TABLE_ID}" prio 999
+ fi
+}
+
+# Configure rtsrc to reach rtdst's loopback address through the VPN.
+# args:
+# $1 - router id where the encap route is installed
+# $2 - router id whose loopback is the destination
+# $3 - tenant id
+setup_vpn_gw_encap()
+{
+ local rtsrc="$1"
+ local rtdst="$2"
+ local tid="$3"
+ local sid
+
+ sid="$(build_vpn_sid "${rtsrc}" "${rtdst}" "${tid}")"
+
+ set_gw_encap_route "${rtsrc}" "${rtdst}" "${sid}" "${tid}"
+}
+
+setup()
+{
+ ip link add veth-rt-1 type veth peer name veth-rt-2
+ setup_ns rt_1 rt_2
+ setup_rt_networking 1
+ setup_rt_networking 2
+
+ # setup two hosts for the tenant 100.
+ # - host hs-t100-1 is directly connected to the router rt-1;
+ # - host hs-t100-2 is directly connected to the router rt-2.
+ setup_ns hs_t100_1 hs_t100_2
+ setup_hs 1 1 100
+ setup_hs 2 2 100
+
+ # setup two hosts for the tenant 200.
+ # - host hs-t200-3 is directly connected to the router rt-1;
+ # - host hs-t200-4 is directly connected to the router rt-2.
+ setup_ns hs_t200_3 hs_t200_4
+ setup_hs 3 1 200
+ setup_hs 4 2 200
+
+ # configure each router for each tenant: VRF, blackhole routes,
+ # router loopback interface
+ setup_rt 1 100
+ setup_rt 2 100
+ setup_rt 1 200
+ setup_rt 2 200
+
+ # setup the L3 VPN which connects the host hs-t100-1 and host hs-t100-2
+ # within the same tenant 100.
+ setup_vpn_config 1 2 2 100
+ setup_vpn_config 2 1 1 100
+
+ # setup the L3 VPN which connects the host hs-t200-3 and host hs-t200-4
+ # within the same tenant 200.
+ setup_vpn_config 1 4 2 200
+ setup_vpn_config 2 3 1 200
+
+ # allow each router to reach the other's loopback through the VPN
+ setup_vpn_gw_encap 2 1 100
+ setup_vpn_gw_encap 1 2 100
+ setup_vpn_gw_encap 2 1 200
+ setup_vpn_gw_encap 1 2 200
+
+ # testing environment was set up successfully
+ SETUP_ERR=0
+}
+
+check_rt_connectivity()
+{
+ local rtsrc="$1"
+ local rtdst="$2"
+ local nsname
+
+ nsname="$(get_rt_nsname "${rtsrc}")"
+
+ ip netns exec "${nsname}" ping -c 1 -W 1 "${IPv6_RT_NETWORK}::${rtdst}" \
+ >/dev/null 2>&1
+}
+
+check_and_log_rt_connectivity()
+{
+ local rtsrc="$1"
+ local rtdst="$2"
+
+ check_rt_connectivity "${rtsrc}" "${rtdst}"
+ log_test $? 0 "Routers connectivity: rt-${rtsrc} -> rt-${rtdst}"
+}
+
+check_hs_ipv6_connectivity()
+{
+ local hssrc="$1"
+ local hsdst="$2"
+ local tid="$3"
+ local nsname
+
+ nsname="$(get_hs_nsname "${tid}" "${hssrc}")"
+
+ ip netns exec "${nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
+ "${IPv6_HS_NETWORK}::${hsdst}" >/dev/null 2>&1
+}
+
+check_hs_ipv4_connectivity()
+{
+ local hssrc="$1"
+ local hsdst="$2"
+ local tid="$3"
+ local nsname
+
+ nsname="$(get_hs_nsname "${tid}" "${hssrc}")"
+
+ ip netns exec "${nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
+ "${IPv4_HS_NETWORK}.${hsdst}" >/dev/null 2>&1
+}
+
+check_and_log_hs_connectivity()
+{
+ local hssrc="$1"
+ local hsdst="$2"
+ local tid="$3"
+
+ check_hs_ipv6_connectivity "${hssrc}" "${hsdst}" "${tid}"
+ log_test $? 0 "IPv6 connectivity: hs-t${tid}-${hssrc} -> hs-t${tid}-${hsdst} (tenant ${tid})"
+
+ check_hs_ipv4_connectivity "${hssrc}" "${hsdst}" "${tid}"
+ log_test $? 0 "IPv4 connectivity: hs-t${tid}-${hssrc} -> hs-t${tid}-${hsdst} (tenant ${tid})"
+}
+
+check_and_log_hs_isolation()
+{
+ local hssrc="$1"
+ local tidsrc="$2"
+ local hsdst="$3"
+ local tiddst="$4"
+
+ check_hs_ipv6_connectivity "${hssrc}" "${hsdst}" "${tidsrc}"
+ log_test $? 1 "IPv6 isolation: hs-t${tidsrc}-${hssrc} -X-> hs-t${tiddst}-${hsdst}"
+
+ check_hs_ipv4_connectivity "${hssrc}" "${hsdst}" "${tidsrc}"
+ log_test $? 1 "IPv4 isolation: hs-t${tidsrc}-${hssrc} -X-> hs-t${tiddst}-${hsdst}"
+}
+
+check_and_log_hs2gw_connectivity()
+{
+ local hssrc="$1"
+ local tid="$2"
+
+ check_hs_ipv6_connectivity "${hssrc}" 254 "${tid}"
+ log_test $? 0 "IPv6 connectivity: hs-t${tid}-${hssrc} -> gw (tenant ${tid})"
+
+ check_hs_ipv4_connectivity "${hssrc}" 254 "${tid}"
+ log_test $? 0 "IPv4 connectivity: hs-t${tid}-${hssrc} -> gw (tenant ${tid})"
+}
+
+router_tests()
+{
+ log_section "IPv6 routers connectivity test"
+
+ check_and_log_rt_connectivity 1 2
+ check_and_log_rt_connectivity 2 1
+}
+
+host2gateway_tests()
+{
+ log_section "Connectivity test among hosts and gateway"
+
+ check_and_log_hs2gw_connectivity 1 100
+ check_and_log_hs2gw_connectivity 2 100
+
+ check_and_log_hs2gw_connectivity 3 200
+ check_and_log_hs2gw_connectivity 4 200
+}
+
+host_vpn_tests()
+{
+ log_section "SRv6 VPN connectivity test among hosts in the same tenant"
+
+ check_and_log_hs_connectivity 1 2 100
+ check_and_log_hs_connectivity 2 1 100
+
+ check_and_log_hs_connectivity 3 4 200
+ check_and_log_hs_connectivity 4 3 200
+}
+
+host_vpn_isolation_tests()
+{
+ local l1="1 2"
+ local l2="3 4"
+ local t1=100
+ local t2=200
+ local i
+ local j
+ local tmp
+
+ log_section "SRv6 VPN isolation test among hosts in different tenants"
+
+ for _ in 0 1; do
+ for i in ${l1}; do
+ for j in ${l2}; do
+ check_and_log_hs_isolation "${i}" "${t1}" "${j}" "${t2}"
+ done
+ done
+
+ # let us test the reverse path
+ tmp="${l1}"; l1="${l2}"; l2="${tmp}"
+ tmp=${t1}; t1=${t2}; t2=${tmp}
+ done
+}
+
+__test_nolookup()
+{
+ local hssrc="$1"
+ local hsdst="$2"
+ local rtsrc="$3"
+ local rtdst="$4"
+ local tid="$5"
+ local vpn_sid
+
+ vpn_sid="$(build_vpn_sid "${rtsrc}" "${rtdst}" "${tid}")"
+
+ # replace encap route(s) without "lookup" attribute
+ set_host_encap_route_nolookup "${rtsrc}" "${hsdst}" "${vpn_sid}" "${tid}"
+
+ check_hs_ipv6_connectivity "${hssrc}" "${hsdst}" "${tid}"
+ log_test $? 1 "IPv6 w/o lookup: hs-t${tid}-${hssrc} -X-> hs-t${tid}-${hsdst} (tenant ${tid})"
+
+ check_hs_ipv4_connectivity "${hssrc}" "${hsdst}" "${tid}"
+ log_test $? 1 "IPv4 w/o lookup: hs-t${tid}-${hssrc} -X-> hs-t${tid}-${hsdst} (tenant ${tid})"
+
+ # restore encap route(s) with "lookup" for subsequent tests
+ set_host_encap_route "${rtsrc}" "${hsdst}" "${vpn_sid}" "${tid}"
+}
+
+host_vpn_nolookup_tests()
+{
+ log_section "SRv6 VPN connectivity test among hosts w/o lookup"
+
+ __test_nolookup 1 2 1 2 100
+ __test_nolookup 2 1 2 1 100
+
+ __test_nolookup 3 4 1 2 200
+ __test_nolookup 4 3 2 1 200
+}
+
+check_gw_ipv6_connectivity()
+{
+ local rtsrc="$1"
+ local rtdst="$2"
+ local tidsrc="$3"
+ local tiddst="$4"
+ local rtname
+ local src_v6
+ local dst_v6
+
+ rtname="$(get_rt_nsname "${rtsrc}")"
+ src_v6="${IPv6_HS_NETWORK}::$(get_rlo_hostid "${rtsrc}" "${tidsrc}")"
+ dst_v6="${IPv6_HS_NETWORK}::$(get_rlo_hostid "${rtdst}" "${tiddst}")"
+
+ ip netns exec "${rtname}" ip vrf exec "vrf-${tidsrc}" \
+ ping -c 1 -W "${PING_TIMEOUT_SEC}" \
+ -I "${src_v6}" "${dst_v6}" >/dev/null 2>&1
+}
+
+check_gw_ipv4_connectivity()
+{
+ local rtsrc="$1"
+ local rtdst="$2"
+ local tidsrc="$3"
+ local tiddst="$4"
+ local rtname
+ local src_v4
+ local dst_v4
+
+ rtname="$(get_rt_nsname "${rtsrc}")"
+ src_v4="${IPv4_HS_NETWORK}.$(get_rlo_hostid "${rtsrc}" "${tidsrc}")"
+ dst_v4="${IPv4_HS_NETWORK}.$(get_rlo_hostid "${rtdst}" "${tiddst}")"
+
+ ip netns exec "${rtname}" ip vrf exec "vrf-${tidsrc}" \
+ ping -c 1 -W "${PING_TIMEOUT_SEC}" \
+ -I "${src_v4}" "${dst_v4}" >/dev/null 2>&1
+}
+
+check_and_log_gw_connectivity()
+{
+ local rtsrc="$1"
+ local rtdst="$2"
+ local tid="$3"
+
+ check_gw_ipv6_connectivity "${rtsrc}" "${rtdst}" "${tid}" "${tid}"
+ log_test $? 0 "IPv6 connectivity: rt-${rtsrc} -> rt-${rtdst} (tenant ${tid})"
+
+ check_gw_ipv4_connectivity "${rtsrc}" "${rtdst}" "${tid}" "${tid}"
+ log_test $? 0 "IPv4 connectivity: rt-${rtsrc} -> rt-${rtdst} (tenant ${tid})"
+}
+
+check_and_log_gw_isolation()
+{
+ local rtsrc="$1"
+ local rtdst="$2"
+ local tidsrc="$3"
+ local tiddst="$4"
+
+ check_gw_ipv6_connectivity "${rtsrc}" "${rtdst}" "${tidsrc}" "${tiddst}"
+ log_test $? 1 "IPv6 isolation: rt-${rtsrc} -X-> rt-${rtdst} (tenants ${tidsrc}/${tiddst})"
+
+ check_gw_ipv4_connectivity "${rtsrc}" "${rtdst}" "${tidsrc}" "${tiddst}"
+ log_test $? 1 "IPv4 isolation: rt-${rtsrc} -X-> rt-${rtdst} (tenants ${tidsrc}/${tiddst})"
+}
+
+gw_vpn_isolation_tests()
+{
+ log_section "SRv6 VPN isolation test among routers in different tenants"
+
+ check_and_log_gw_isolation 1 2 100 200
+ check_and_log_gw_isolation 2 1 100 200
+
+ check_and_log_gw_isolation 1 2 200 100
+ check_and_log_gw_isolation 2 1 200 100
+}
+
+gw_vpn_tests()
+{
+ log_section "SRv6 VPN connectivity test among routers in the same tenant"
+
+ check_and_log_gw_connectivity 1 2 100
+ check_and_log_gw_connectivity 2 1 100
+
+ check_and_log_gw_connectivity 1 2 200
+ check_and_log_gw_connectivity 2 1 200
+}
+
+__test_gw_nolookup()
+{
+ local rtsrc="$1"
+ local rtdst="$2"
+ local tid="$3"
+ local sid
+
+ sid="$(build_vpn_sid "${rtsrc}" "${rtdst}" "${tid}")"
+
+ # replace gw encap route without "lookup" attribute
+ set_gw_encap_route_nolookup "${rtsrc}" "${rtdst}" "${sid}" "${tid}"
+
+ check_gw_ipv6_connectivity "${rtsrc}" "${rtdst}" "${tid}" "${tid}"
+ log_test $? 1 "IPv6 w/o lookup: rt-${rtsrc} -X-> rt-${rtdst} (tenant ${tid})"
+
+ check_gw_ipv4_connectivity "${rtsrc}" "${rtdst}" "${tid}" "${tid}"
+ log_test $? 1 "IPv4 w/o lookup: rt-${rtsrc} -X-> rt-${rtdst} (tenant ${tid})"
+
+ # restore gw encap route with "lookup" for subsequent tests
+ set_gw_encap_route "${rtsrc}" "${rtdst}" "${sid}" "${tid}"
+}
+
+gw_vpn_nolookup_tests()
+{
+ log_section "SRv6 VPN connectivity test among routers w/o lookup"
+
+ __test_gw_nolookup 1 2 100
+ __test_gw_nolookup 2 1 100
+
+ __test_gw_nolookup 1 2 200
+ __test_gw_nolookup 2 1 200
+}
+
+test_command_or_ksft_skip()
+{
+ local cmd="$1"
+
+ if [ ! -x "$(command -v "${cmd}")" ]; then
+ echo "SKIP: Could not run test without \"${cmd}\" tool"
+ exit "${ksft_skip}"
+ fi
+}
+
+test_vrf_or_ksft_skip()
+{
+ modprobe vrf &>/dev/null || true
+ if [ ! -e /proc/sys/net/vrf/strict_mode ]; then
+ echo "SKIP: vrf sysctl does not exist"
+ exit "${ksft_skip}"
+ fi
+}
+
+test_dummy_dev_or_ksft_skip()
+{
+ local test_netns
+
+ setup_ns test_netns
+
+ modprobe dummy &>/dev/null || true
+ if ! ip -netns "${test_netns}" link add "${DUMMY_DEVNAME}" \
+ type dummy; then
+ cleanup_ns "${test_netns}"
+ echo "SKIP: dummy dev not supported"
+ exit "${ksft_skip}"
+ fi
+
+ cleanup_ns "${test_netns}"
+}
+
+test_encap_lookup_supp_or_ksft_skip()
+{
+ local nsname
+
+ setup_ns nsname
+
+ ip -netns "${nsname}" link add "${DUMMY_DEVNAME}" type dummy
+ ip -netns "${nsname}" link set "${DUMMY_DEVNAME}" up
+
+ if ! ip -netns "${nsname}" -6 route add "${IPv6_TESTS_ADDR}/128" \
+ encap seg6 mode encap segs fc00::1 \
+ lookup "${TESTS_TABLE_ID}" \
+ dev "${DUMMY_DEVNAME}" 2>/dev/null; then
+ cleanup_ns "${nsname}"
+ echo "SKIP: seg6 encap lookup attribute not supported"
+ exit "${ksft_skip}"
+ fi
+
+ # An old kernel with a recent iproute2 accepts the route but
+ # silently ignores the lookup attribute. Dump the route and check
+ # the attribute is really there, otherwise the test falsely passes.
+ if ! ip -netns "${nsname}" -6 route show "${IPv6_TESTS_ADDR}/128" | \
+ grep -q "lookup ${TESTS_TABLE_ID}"; then
+ cleanup_ns "${nsname}"
+ echo "SKIP: seg6 encap lookup attribute not supported"
+ exit "${ksft_skip}"
+ fi
+
+ cleanup_ns "${nsname}"
+}
+
+if [ "$(id -u)" -ne 0 ]; then
+ echo "SKIP: Need root privileges"
+ exit "${ksft_skip}"
+fi
+
+# required programs to carry out this selftest
+test_command_or_ksft_skip ip
+test_command_or_ksft_skip ping
+test_command_or_ksft_skip sysctl
+test_command_or_ksft_skip grep
+
+test_dummy_dev_or_ksft_skip
+test_vrf_or_ksft_skip
+test_encap_lookup_supp_or_ksft_skip
+
+set -e
+trap cleanup EXIT
+
+setup
+set +e
+
+router_tests
+host2gateway_tests
+host_vpn_tests
+host_vpn_isolation_tests
+host_vpn_nolookup_tests
+gw_vpn_tests
+gw_vpn_isolation_tests
+gw_vpn_nolookup_tests
+
+print_log_test_results
|
| 3/2 | 2026/07/12 08:54 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17305 output: 30 thoughts: 590 Thoughts: **My Assessment of the SRv6 Table Attribute Patch** Okay, so I'm looking at this patch and it's straightforward. They've introduced a critical feature: the ability to specify a FIB table for post-encapsulation SID lookup within SRv6 tunnels. This is a game-changer for sophisticated network designs, allowing for much more granular control over routing within the tunnel. The meat of the work is in `net/ipv6/seg6_iptunnel.c`. They've added a `seg6_table_lookup` function, which I like. It's clean and focused on retrieving a route from a specific table, handling the lookup and ensuring the table actually exists. The use of `fib6_get_table` and `ip6_pol_route` is correct here, reusing existing kernel infrastructure. The Netlink attribute `SEG6_IPTUNNEL_TABLE` is the user-facing part, and the code looks sane. It appears the attribute is parsed in `seg6_build_state`. Validation is included (checking for non-zero `table` value), and they're using `NL_SET_ERR_MSG` for proper error reporting back to the user space. Good. I'll need to figure out how this is enabled. Time to check the kernel configuration. I'll search for `seg6_iptunnel.c` within the `net/ipv6/Makefile` and `net/ipv6/Kconfig` files. That should give me the configuration options I need to enable this. This is critical to ensure this code is actually built and available for use in deployments. |
| 4/2 | 2026/07/12 08:54 | tool | grepper |
0mArgs:{
"Expression": "seg6_iptunnel",
"PathPrefix": "net/ipv6/"
}Results: {
"Output": "net/ipv6/Makefile=24=ipv6-$(CONFIG_NETLABEL) += calipso.o\nnet/ipv6/Makefile:25:ipv6-$(CONFIG_IPV6_SEG6_LWTUNNEL) += seg6_iptunnel.o seg6_local.o\nnet/ipv6/Makefile-26-ipv6-$(CONFIG_IPV6_SEG6_HMAC) += seg6_hmac.o\n--\nnet/ipv6/seg6.c=508=int __init seg6_init(void)\n--\nnet/ipv6/seg6.c-519-\nnet/ipv6/seg6.c:520:\terr = seg6_iptunnel_init();\nnet/ipv6/seg6.c-521-\tif (err)\n--\nnet/ipv6/seg6.c-532-out_unregister_iptun:\nnet/ipv6/seg6.c:533:\tseg6_iptunnel_exit();\nnet/ipv6/seg6.c-534-out_unregister_genl:\n--\nnet/ipv6/seg6.c=541=void seg6_exit(void)\n--\nnet/ipv6/seg6.c-543-\tseg6_local_exit();\nnet/ipv6/seg6.c:544:\tseg6_iptunnel_exit();\nnet/ipv6/seg6.c-545-\tgenl_unregister_family(\u0026seg6_genl_family);\n--\nnet/ipv6/seg6_iptunnel.c-21-#include \u003clinux/seg6.h\u003e\nnet/ipv6/seg6_iptunnel.c:22:#include \u003clinux/seg6_iptunnel.h\u003e\nnet/ipv6/seg6_iptunnel.c-23-#include \u003cnet/addrconf.h\u003e\n--\nnet/ipv6/seg6_iptunnel.c-30-\nnet/ipv6/seg6_iptunnel.c:31:static size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo)\nnet/ipv6/seg6_iptunnel.c-32-{\n--\nnet/ipv6/seg6_iptunnel.c=50=struct seg6_lwt {\n--\nnet/ipv6/seg6_iptunnel.c-54-\tu32 table;\nnet/ipv6/seg6_iptunnel.c:55:\tstruct seg6_iptunnel_encap tuninfo[];\nnet/ipv6/seg6_iptunnel.c-56-};\n--\nnet/ipv6/seg6_iptunnel.c=58=static inline struct seg6_lwt *seg6_lwt_lwtunnel(struct lwtunnel_state *lwt)\n--\nnet/ipv6/seg6_iptunnel.c-62-\nnet/ipv6/seg6_iptunnel.c:63:static inline struct seg6_iptunnel_encap *\nnet/ipv6/seg6_iptunnel.c-64-seg6_encap_lwtunnel(struct lwtunnel_state *lwt)\n--\nnet/ipv6/seg6_iptunnel.c-68-\nnet/ipv6/seg6_iptunnel.c:69:static const struct nla_policy seg6_iptunnel_policy[SEG6_IPTUNNEL_MAX + 1] = {\nnet/ipv6/seg6_iptunnel.c-70-\t[SEG6_IPTUNNEL_SRH]\t= { .type = NLA_BINARY },\n--\nnet/ipv6/seg6_iptunnel.c=75=static int nla_put_srh(struct sk_buff *skb, int attrtype,\nnet/ipv6/seg6_iptunnel.c:76:\t\t struct seg6_iptunnel_encap *tuninfo)\nnet/ipv6/seg6_iptunnel.c-77-{\nnet/ipv6/seg6_iptunnel.c:78:\tstruct seg6_iptunnel_encap *data;\nnet/ipv6/seg6_iptunnel.c-79-\tstruct nlattr *nla;\n--\nnet/ipv6/seg6_iptunnel.c=225=static int seg6_do_srh_encap_red(struct sk_buff *skb,\n--\nnet/ipv6/seg6_iptunnel.c-247-\t\t/* NOTE: if tag/flags and/or other TLVs are introduced in the\nnet/ipv6/seg6_iptunnel.c:248:\t\t * seg6_iptunnel infrastructure, they should be considered when\nnet/ipv6/seg6_iptunnel.c-249-\t\t * deciding to skip the SRH.\n--\nnet/ipv6/seg6_iptunnel.c=398=static int seg6_do_srh(struct sk_buff *skb, struct dst_entry *cache_dst)\n--\nnet/ipv6/seg6_iptunnel.c-400-\tstruct dst_entry *dst = skb_dst(skb);\nnet/ipv6/seg6_iptunnel.c:401:\tstruct seg6_iptunnel_encap *tinfo;\nnet/ipv6/seg6_iptunnel.c-402-\tstruct seg6_lwt *slwt;\n--\nnet/ipv6/seg6_iptunnel.c=747=static int seg6_build_state(struct net *net, struct nlattr *nla,\n--\nnet/ipv6/seg6_iptunnel.c-752-\tstruct nlattr *tb[SEG6_IPTUNNEL_MAX + 1];\nnet/ipv6/seg6_iptunnel.c:753:\tstruct seg6_iptunnel_encap *tuninfo;\nnet/ipv6/seg6_iptunnel.c-754-\tstruct lwtunnel_state *newts;\n--\nnet/ipv6/seg6_iptunnel.c-762-\terr = nla_parse_nested_deprecated(tb, SEG6_IPTUNNEL_MAX, nla,\nnet/ipv6/seg6_iptunnel.c:763:\t\t\t\t\t seg6_iptunnel_policy, extack);\nnet/ipv6/seg6_iptunnel.c-764-\n--\nnet/ipv6/seg6_iptunnel.c=875=static int seg6_fill_encap_info(struct sk_buff *skb,\n--\nnet/ipv6/seg6_iptunnel.c-877-{\nnet/ipv6/seg6_iptunnel.c:878:\tstruct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate);\nnet/ipv6/seg6_iptunnel.c-879-\tstruct seg6_lwt *slwt = seg6_lwt_lwtunnel(lwtstate);\n--\nnet/ipv6/seg6_iptunnel.c=895=static int seg6_encap_nlsize(struct lwtunnel_state *lwtstate)\nnet/ipv6/seg6_iptunnel.c-896-{\nnet/ipv6/seg6_iptunnel.c:897:\tstruct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate);\nnet/ipv6/seg6_iptunnel.c-898-\tstruct seg6_lwt *slwt = seg6_lwt_lwtunnel(lwtstate);\n--\nnet/ipv6/seg6_iptunnel.c=912=static int seg6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)\nnet/ipv6/seg6_iptunnel.c-913-{\nnet/ipv6/seg6_iptunnel.c:914:\tstruct seg6_iptunnel_encap *a_hdr = seg6_encap_lwtunnel(a);\nnet/ipv6/seg6_iptunnel.c:915:\tstruct seg6_iptunnel_encap *b_hdr = seg6_encap_lwtunnel(b);\nnet/ipv6/seg6_iptunnel.c-916-\tstruct seg6_lwt *a_slwt = seg6_lwt_lwtunnel(a);\n--\nnet/ipv6/seg6_iptunnel.c=932=static const struct lwtunnel_encap_ops seg6_iptun_ops = {\n--\nnet/ipv6/seg6_iptunnel.c-942-\nnet/ipv6/seg6_iptunnel.c:943:int __init seg6_iptunnel_init(void)\nnet/ipv6/seg6_iptunnel.c-944-{\n--\nnet/ipv6/seg6_iptunnel.c-947-\nnet/ipv6/seg6_iptunnel.c:948:void seg6_iptunnel_exit(void)\nnet/ipv6/seg6_iptunnel.c-949-{\n"
} |
| 5/2 | 2026/07/12 08:54 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20487 output: 37 thoughts: 45 Thoughts: Here's my take on those thoughts: **Investigating the IPv6 Segment Routing with IPv6 LWTunnel Configuration** Okay, so I need to nail down the configuration option for the Segment Routing with IPv6 LWTunnel feature. Right, I remember seeing something like `CONFIG_IPV6_SEG6_LWTUNNEL`. Let me just double-check that. Where is the authoritative source for these kernel config options? Ah, that would be `net/ipv6/Kconfig`. Time to crack that file open and confirm the exact name and any dependencies or related options surrounding `IPV6_SEG6_LWTUNNEL`. Gotta make sure I'm using the right knob, and understand the context within the larger IPv6 stack configuration. |
| 6/2 | 2026/07/12 08:54 | tool | grepper |
0mArgs:{
"Expression": "IPV6_SEG6_LWTUNNEL",
"PathPrefix": "net/ipv6/Kconfig"
}Results: {
"Output": "net/ipv6/Kconfig=279=config IPV6_PIMSM_V2\n--\nnet/ipv6/Kconfig-285-\nnet/ipv6/Kconfig:286:config IPV6_SEG6_LWTUNNEL\nnet/ipv6/Kconfig-287-\tbool \"IPv6: Segment Routing Header encapsulation support\"\n--\nnet/ipv6/Kconfig=312=config IPV6_SEG6_BPF\nnet/ipv6/Kconfig-313-\tdef_bool y\nnet/ipv6/Kconfig:314:\tdepends on IPV6_SEG6_LWTUNNEL\nnet/ipv6/Kconfig-315-\tdepends on IPV6 = y\n"
} |
| 7/2 | 2026/07/12 08:54 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20919 output: 39 thoughts: 64 Thoughts: Okay, let's break this down. **IP v6 Segment Routing Configuration Considerations** Right, so I'm focusing on setting up segment routing with IPv6. My primary config option here is `IPV6_SEG6_LWTUNNEL`. That's the big one. But I know from experience that these things rarely stand alone. I need to make sure I haven't missed any other crucial dependencies. `LWTUNNEL` is a likely candidate; I'll flag that as something to verify. To be absolutely thorough and make sure I've got everything nailed down, I'm going to dig into `net/ipv6/Kconfig`. That's where all the dependencies and configuration options are defined for the IPv6 stack. I'll need to double check `IPV6_SEG6_LWTUNNEL` to see which other options are connected, and if anything else is needed or recommended to get this thing working correctly. It's best to be sure of that. |
| 8/2 | 2026/07/12 08:54 | tool | grepper |
0mArgs:{
"Expression": "config IPV6_SEG6_LWTUNNEL",
"PathPrefix": "net/ipv6/Kconfig"
}Results: {
"Output": "net/ipv6/Kconfig=279=config IPV6_PIMSM_V2\n--\nnet/ipv6/Kconfig-285-\nnet/ipv6/Kconfig:286:config IPV6_SEG6_LWTUNNEL\nnet/ipv6/Kconfig-287-\tbool \"IPv6: Segment Routing Header encapsulation support\"\n"
} |
| 9/2 | 2026/07/12 08:54 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 21334 output: 35 thoughts: 21 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience like yourself: **Analyzing the Configuration Dependency** Alright, my brain's kicking into gear. First, let's zero in on those lines surrounding `config IPV6_SEG6_LWTUNNEL`. My immediate focus is on the surrounding context. I need to understand the dependencies and interactions around that configuration option. What other features or settings are involved? Are there any prerequisites I should be aware of, like specific kernel modules or other networking stack components? Are there any obvious conflicts to look for? We can also check the Kconfig, to see if there is any help text to assist. |
| 10/2 | 2026/07/12 08:54 | tool | read-file |
0mArgs:{
"File": "net/ipv6/Kconfig",
"FirstLine": 280,
"LineCount": 20
}Results: {
"Contents": " 280:\t\tbool \"IPv6: PIM-SM version 2 support\"\n 281:\t\tdepends on IPV6_MROUTE\n 282:\t\thelp\n 283:\t\t Support for IPv6 PIM multicast routing protocol PIM-SMv2.\n 284:\t\t If unsure, say N.\n 285:\t\n 286:\tconfig IPV6_SEG6_LWTUNNEL\n 287:\t\tbool \"IPv6: Segment Routing Header encapsulation support\"\n 288:\t\tdepends on IPV6\n 289:\t\tselect LWTUNNEL\n 290:\t\tselect DST_CACHE\n 291:\t\tselect IPV6_MULTIPLE_TABLES\n 292:\t\thelp\n 293:\t\t Support for encapsulation of packets within an outer IPv6\n 294:\t\t header and a Segment Routing Header using the lightweight\n 295:\t\t tunnels mechanism. Also enable support for advanced local\n 296:\t\t processing of SRv6 packets based on their active segment.\n 297:\t\n 298:\t\t If unsure, say N.\n 299:\t\n"
} |
| 11/2 | 2026/07/12 08:54 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 21831 output: 215 thoughts: 761 Thoughts: **Fuzzing Strategy for SRv6 Encapsulation Routing Patch** Okay, so I've got this patch introducing a new Netlink attribute, `SEG6_IPTUNNEL_TABLE`, to allow specifying a FIB table for post-encap SID lookup within SRv6 tunnels. This is a functional addition that directly impacts the IPv6 Segment Routing subsystem, so fuzzing is definitely warranted. The core configuration is `IPV6_SEG6_LWTUNNEL`, which seems sufficient given that the patch leverages standard IPv6 routing functions like `fib6_get_table` (requiring `CONFIG_IPV6_MULTIPLE_TABLES`, which is selected by `IPV6_SEG6_LWTUNNEL`) and `ip6_pol_route`. My immediate thought is to zero in on the functions most directly affected by this new attribute and logic. The primary file to examine is `net/ipv6/seg6_iptunnel.c`. Here's my target list: * `seg6_table_lookup`: Critical for the new FIB table lookup. * `seg6_input_route`: Called from `seg6_input_core`, so it's directly involved in the input path. * `seg6_output_dst_lookup`: Similar to `seg6_input_route`, but for the output path. It's called from `seg6_output_core`. * `seg6_build_state`: This handles the Netlink configuration, so it's a prime target for fuzzing the new attribute. * `seg6_fill_encap_info` * `seg6_encap_nlsize` * `seg6_encap_cmp` While `seg6_input_core` and `seg6_output_core` are the core forwarding functions, I'm going to focus on the `seg6_table_lookup`, `seg6_input_route`, `seg6_output_dst_lookup` and `seg6_build_state` functions, as they are specific to the new functionality and, thus, more likely to expose novel vulnerabilities related to this patch. The other functions are related to the core logic, which is also worth testing. The high-level reasoning is this: the patch fundamentally alters the SRv6 encapsulation path to incorporate a user-specified FIB table lookup. This introduces a new code path and potential for unexpected behavior in the routing logic, particularly if the provided table is malformed or invalid. The functions listed above are going to be key to testing the new functionality. I'll use the `set-results` tool to finalize the analysis and feed these targets into the fuzzer. |
| 12/2 | 2026/07/12 08:54 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"IPV6_SEG6_LWTUNNEL"
],
"FocusSymbols": [
"seg6_table_lookup",
"seg6_input_route",
"seg6_output_dst_lookup",
"seg6_build_state",
"seg6_fill_encap_info",
"seg6_encap_nlsize",
"seg6_encap_cmp"
],
"Reasoning": "The patch adds a new Netlink attribute `SEG6_IPTUNNEL_TABLE` to specify a FIB table for post-encap SID lookup in SRv6 tunnels. It modifies the SRv6 encapsulation logic in `net/ipv6/seg6_iptunnel.c` to perform route lookups in the specified table during both input and output paths. This is a functional change to reachable core kernel networking logic, specifically the IPv6 Segment Routing subsystem, and should be fuzzed.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"IPV6_SEG6_LWTUNNEL"
],
"FocusSymbols": [
"seg6_table_lookup",
"seg6_input_route",
"seg6_output_dst_lookup",
"seg6_build_state",
"seg6_fill_encap_info",
"seg6_encap_nlsize",
"seg6_encap_cmp"
],
"Reasoning": "The patch adds a new Netlink attribute `SEG6_IPTUNNEL_TABLE` to specify a FIB table for post-encap SID lookup in SRv6 tunnels. It modifies the SRv6 encapsulation logic in `net/ipv6/seg6_iptunnel.c` to perform route lookups in the specified table during both input and output paths. This is a functional change to reachable core kernel networking logic, specifically the IPv6 Segment Routing subsystem, and should be fuzzed.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|