Merge branch 'netlink-add-lwtunnel-encap-sub-message-support-to-rt-route'

Hangbin Liu says:

====================
netlink: add lwtunnel encap sub-message support to rt-route

This series adds YNL sub-message support for lwtunnel encap data in
the rt-route netlink family, and describes the tunnel-specific
attribute sets that were previously opaque binary blobs.

The kernel currently emits encap_type after the encap payload nest,
but YNL sub-message parsing needs the selector (encap_type) first to
dispatch on the correct attribute set. Patch 1 reorders the kernel
output, and patch 2 teaches the YNL C code generator to convert enum
selectors to their string form for sub-message dispatch.

Patches 3-6 extend the rt-route YAML spec.

Tested with ynl selftest and checked with following cmds

 # modprobe ila
 # modprobe xfrm_interface
 # ip addr add 10.1.0.1/24 dev lo
 # ip link add geneve1 type geneve id 1 remote 10.1.0.2 ttl 64
 # ip link set geneve1 up
 # ip route add 10.1.0.0/24 dev lo encap mpls 100 via 10.1.0.254
 # ip route add 1.1.1.0/24 encap ip id 1 geneve_opts 1:1:1212121234567891,2:2:1212121234567892,3:3:1212121234567893 dst 10.1.0.2 dev geneve1
 # ip route add 2001:db8:3::/64 dev lo encap ila 1:2:3:4 csum-mode no-action ident-type luid hook-type output
 # ip route add 2001:db8:4::/64 dev lo encap ip6 id 1 dst 2001:db8::5 src 2001:db8::1 hoplimit 64
 # ip route add 2001:db8:5::/64 dev lo encap seg6 mode inline segs 2001:db8::1
 # ip route add 2001:db8:6::/65 dev lo encap seg6local action End flavors psp,next-csid lblen 32 nflen 16
 # ip route add 2001:db8:7::/65 dev lo encap bpf in ob /tmp/kself/net/lib/xdp_dummy.bpf.o sec xdp
 # ip route add 2001:db8:8::/64 dev lo encap rpl segs 2001:db8::1
 # ip route add 2001:db8:9::/64 encap ioam6 trace prealloc type 0x800000 ns 0 size 4 dev lo
 # ip route add 2001:db8:10::/64 dev lo encap xfrm if_id 100
 # ip route show
 1.1.1.0/24  encap ip id 1 src 0.0.0.0 dst 10.1.0.2 ttl 0 tos 0
        geneve_opts 1:1:1212121234567891,2:2:1212121234567892,3:3:1212121234567893 dev geneve1 scope link
 10.1.0.0/24  encap mpls  100 via 10.1.0.254 dev lo
 # ip -6 route show
 2001:db8:3::/64  encap ila  1:2:3:4  csum-mode no-action  ident-type luid  hook-type output dev lo metric 1024 pref medium
 2001:db8:4::/64  encap ip6 id 1 src 2001:db8::1 dst 2001:db8::5 hoplimit 64 tc 0 dev lo metric 1024 pref medium
 2001:db8:5::/64  encap seg6 mode inline segs 2 [ 2001:db8::1 :: ] dev lo metric 1024 pref medium
 2001:db8:6::/65  encap seg6local action End flavors psp,next-csid lblen 32 nflen 16 dev lo metric 1024 pref medium
 2001:db8:7::/65  encap bpf in xdp_dummy.bpf.o:[xdp] dev lo metric 1024 pref medium
 2001:db8:8::/64  encap rpl segs 1 [ 2001:db8::1 ] dev lo metric 1024 pref medium
 2001:db8:9::/64  encap ioam6 freq 1/1 mode inline trace prealloc type 0x800000 ns 0 size 4 dev lo metric 1024 pref medium
 2001:db8:10::/64  encap xfrm if_id 100 dev lo metric 1024 pref medium
 # ./tools/net/ynl/pyynl/cli.py --family rt-route --dump getroute > /tmp/route_dump.json

All the result looks good.
====================

Link: https://patch.msgid.link/20261008-ynl_rt_encap-v4-0-4ed2ccf7f949@kylinos.cn
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-10-15 17:00:00 -07:00
commit 7684c2c796
4 changed files with 485 additions and 25 deletions

View File

@ -99,6 +99,97 @@ definitions:
name: ra-withdrawn
doc: A Router Advertisement withdrew the route with a zero
lifetime.
-
name: encap-type
type: enum
name-prefix: lwtunnel-encap-
enum-name:
entries:
- none
- mpls
- ip
- ila
- ip6
- seg6
- bpf
- seg6-local
- rpl
- ioam6
- xfrm
-
name: seg6-local-actions
type: enum
name-prefix: seg6-local-action-
enum-name:
entries:
- unspec
- end
- end-x
- end-t
- end-dx2
- end-dx6
- end-dx4
- end-dt6
- end-dt4
- end-b6
- end-b6-encap
- end-bm
- end-s
- end-as
- end-am
- end-bpf
- end-dt46
-
name: seg6-local-flv-ops
type: enum
name-prefix: seg6-local-flv-op-
enum-name:
entries:
-
name: unspec
-
name: psp
-
name: usp
-
name: usd
-
name: next-csid
sub-messages:
-
name: encap-data
formats:
-
value: mpls
attribute-set: mpls-iptunnel
-
value: ip
attribute-set: lwtunnel-ip
-
value: ila
attribute-set: ila-attrs
-
value: ip6
attribute-set: lwtunnel-ip6
-
value: seg6
attribute-set: seg6-iptunnel
-
value: bpf
attribute-set: lwt-bpf
-
value: seg6-local
attribute-set: seg6-local
-
value: rpl
attribute-set: rpl-iptunnel
-
value: ioam6
attribute-set: ioam6-iptunnel
-
value: xfrm
attribute-set: lwt-xfrm
attribute-sets:
-
@ -174,9 +265,12 @@ attribute-sets:
-
name: encap-type
type: u16
enum: encap-type
-
name: encap
type: binary # tunnel specific nest
type: sub-message
sub-message: encap-data
selector: encap-type
-
name: expires
type: u32
@ -277,6 +371,340 @@ attribute-sets:
-
name: fastopen-no-cookie
type: u32
-
name: mpls-iptunnel
name-prefix: mpls-iptunnel-
header: linux/mpls_iptunnel.h
attributes:
-
name: dst
type: binary
-
name: ttl
type: u8
-
name: lwtunnel-ip
name-prefix: lwtunnel-ip-
header: linux/lwtunnel.h
attributes:
-
name: id
type: u64
byte-order: big-endian
-
name: dst
type: u32
byte-order: big-endian
display-hint: ipv4
-
name: src
type: u32
byte-order: big-endian
display-hint: ipv4
-
name: ttl
type: u8
-
name: tos
type: u8
-
name: flags
type: u16
byte-order: big-endian
-
name: pad
type: pad
-
name: opts
type: nest
nested-attributes: lwtunnel-ip-opts
-
name: ila-attrs
name-prefix: ila-attr-
header: linux/ila.h
attributes:
-
name: locator
type: u64
byte-order: big-endian
-
name: identifier
type: u64
-
name: locator-match
type: u64
byte-order: big-endian
-
name: ifindex
type: s32
-
name: dir
type: u32
-
name: pad
type: pad
-
name: csum-mode
type: u8
-
name: ident-type
type: u8
-
name: hook-type
type: u8
-
name: lwtunnel-ip6
name-prefix: lwtunnel-ip6-
header: linux/lwtunnel.h
attributes:
-
name: id
type: u64
byte-order: big-endian
-
name: dst
type: binary
display-hint: ipv6
-
name: src
type: binary
display-hint: ipv6
-
name: hoplimit
type: u8
-
name: tc
type: u8
-
name: flags
type: u16
byte-order: big-endian
-
name: pad
type: pad
-
name: opts
type: nest
nested-attributes: lwtunnel-ip-opts
-
name: seg6-iptunnel
name-prefix: seg6-iptunnel-
header: linux/seg6_iptunnel.h
attributes:
-
name: srh
type: binary
-
name: src
type: binary
display-hint: ipv6
-
name: table
type: u32
-
name: lwt-bpf
name-prefix: lwt-bpf-
header: linux/lwtunnel.h
attributes:
-
name: in
type: nest
nested-attributes: lwt-bpf-prog
-
name: out
type: nest
nested-attributes: lwt-bpf-prog
-
name: xmit
type: nest
nested-attributes: lwt-bpf-prog
-
name: xmit-headroom
type: u32
-
name: seg6-local
name-prefix: seg6-local-
header: linux/seg6_local.h
attributes:
-
name: action
type: u32
enum: seg6-local-actions
-
name: srh
type: binary
-
name: table
type: u32
-
name: nh4
type: u32
byte-order: big-endian
display-hint: ipv4
-
name: nh6
type: binary
display-hint: ipv6
-
name: iif
type: u32
-
name: oif
type: u32
-
name: bpf
type: nest
nested-attributes: seg6-local-bpf
-
name: vrftable
type: u32
-
name: counters
type: nest
nested-attributes: seg6-local-cnt
-
name: flavors
type: nest
nested-attributes: seg6-local-flv
-
name: rpl-iptunnel
name-prefix: rpl-iptunnel-
header: linux/rpl_iptunnel.h
attributes:
-
name: srh
type: binary
-
name: ioam6-iptunnel
name-prefix: ioam6-iptunnel-
header: linux/ioam6_iptunnel.h
attributes:
-
name: mode
type: u8
-
name: dst
type: binary
display-hint: ipv6
-
name: trace
type: binary
-
name: freq-k
type: u32
-
name: freq-n
type: u32
-
name: src
type: binary
display-hint: ipv6
-
name: lwt-xfrm
name-prefix: lwt-xfrm-
header: linux/lwtunnel.h
attributes:
-
name: if-id
type: u32
-
name: link
type: u32
-
name: lwtunnel-ip-opts
name-prefix: lwtunnel-ip-opts-
attributes:
-
name: geneve
type: binary
-
name: vxlan
type: nest
nested-attributes: lwtunnel-ip-opt-vxlan
-
name: erspan
type: nest
nested-attributes: lwtunnel-ip-opt-erspan
-
name: lwtunnel-ip-opt-vxlan
name-prefix: lwtunnel-ip-opt-vxlan-
attributes:
-
name: gbp
type: u32
-
name: lwtunnel-ip-opt-erspan
name-prefix: lwtunnel-ip-opt-erspan-
attributes:
-
name: ver
type: u8
-
name: index
type: u32
byte-order: big-endian
-
name: dir
type: u8
-
name: hwid
type: u8
-
name: lwt-bpf-prog
name-prefix: lwt-bpf-prog-
attributes:
-
name: fd
type: u32
-
name: name
type: string
checks:
max-len: 256
-
name: seg6-local-bpf
name-prefix: seg6-local-bpf-
attr-max-name: seg6-local-bpf-prog-max
attributes:
-
name: prog
type: u32
-
name: prog-name
type: string
checks:
max-len: 256
-
name: seg6-local-cnt
name-prefix: seg6-local-cnt-
attributes:
-
name: pad
type: pad
-
name: packets
type: u64
-
name: bytes
type: u64
-
name: errors
type: u64
-
name: seg6-local-flv
name-prefix: seg6-local-flv-
attributes:
-
name: operation
type: u32
enum: seg6-local-flv-ops
enum-as-flags: true
-
name: lcblock-bits
type: u8
-
name: lcnode-fn-bits
type: u8
operations:
enum-model: directional

View File

@ -231,7 +231,7 @@ int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate,
{
const struct lwtunnel_encap_ops *ops;
struct nlattr *nest;
int ret;
int ret = 0;
if (!lwtstate)
return 0;
@ -240,30 +240,31 @@ int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate,
lwtstate->type > LWTUNNEL_ENCAP_MAX)
return 0;
nest = nla_nest_start_noflag(skb, encap_attr);
if (!nest)
return -EMSGSIZE;
ret = -EOPNOTSUPP;
rcu_read_lock();
ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
if (likely(ops && ops->fill_encap))
ret = ops->fill_encap(skb, lwtstate);
rcu_read_unlock();
if (ret)
goto nla_put_failure;
nla_nest_end(skb, nest);
ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
if (unlikely(!ops || !ops->fill_encap))
goto unlock_out;
ret = nla_put_u16(skb, encap_type_attr, lwtstate->type);
if (ret)
goto nla_put_failure;
goto unlock_out;
return 0;
nest = nla_nest_start_noflag(skb, encap_attr);
if (!nest) {
ret = -EMSGSIZE;
goto unlock_out;
}
nla_put_failure:
nla_nest_cancel(skb, nest);
ret = ops->fill_encap(skb, lwtstate);
if (ret)
nla_nest_cancel(skb, nest);
else
nla_nest_end(skb, nest);
return (ret == -EOPNOTSUPP ? 0 : ret);
unlock_out:
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL_GPL(lwtunnel_fill_encap);

View File

@ -45,7 +45,14 @@ CFLAGS_rt-link:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
$(call get_hdr_inc,_LINUX_IF_LINK_H,if_link.h)
CFLAGS_rt-neigh:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
$(call get_hdr_inc,__LINUX_NEIGHBOUR_H,neighbour.h)
CFLAGS_rt-route:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h)
CFLAGS_rt-route:=$(call get_hdr_inc,_LINUX_ILA_H,ila.h) \
$(call get_hdr_inc,_LINUX_IOAM6_IPTUNNEL_H,ioam6_iptunnel.h) \
$(call get_hdr_inc,_LINUX_MPLS_IPTUNNEL_H,mpls_iptunnel.h) \
$(call get_hdr_inc,_LINUX_RPL_IPTUNNEL_H,rpl_iptunnel.h) \
$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
$(call get_hdr_inc,_LINUX_SEG6_IPTUNNEL_H,seg6_iptunnel.h) \
$(call get_hdr_inc,_LINUX_SEG6_LOCAL_H,seg6_local.h) \
$(call get_hdr_inc,_LWTUNNEL_H_,lwtunnel.h)
CFLAGS_rt-rule:=$(call get_hdr_inc,__LINUX_FIB_RULES_H,fib_rules.h)
CFLAGS_tc:= $(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
$(call get_hdr_inc,__LINUX_PKT_SCHED_H,pkt_sched.h) \

View File

@ -951,13 +951,31 @@ class TypeSubMessage(TypeNest):
sel_var = f"_sel_{sel}"
else:
sel_var = f"{var}->{sel}"
get_lines = [f'if (!{sel_var})',
f'return ynl_submsg_failed(yarg, "{self.name}", "{selector}");',
f"if ({self.nested_render_name}_parse(&parg, {sel_var}, attr))",
"return YNL_PARSE_CB_ERROR;"]
local_vars = None
if self.selector.is_enum_val() and not self.selector.is_external():
enum = self.family.consts[self.selector.get_enum_name()]
pres_var = f"{var}->_present.{sel}"
parse_sel = f"{sel}_str"
local_vars = [f'const char *{parse_sel};']
get_lines = [
f'if (!{pres_var})',
f'return ynl_submsg_failed(yarg, "{self.name}", "{selector}");',
f'{parse_sel} = {enum.render_name}_str({sel_var});',
f'if (!{parse_sel})',
f'return ynl_submsg_failed(yarg, "{self.name}", "enum-lookup-failed");']
else:
parse_sel = sel_var
get_lines = [f'if (!{parse_sel})',
f'return ynl_submsg_failed(yarg, "{self.name}", "{selector}");']
get_lines += [f"if ({self.nested_render_name}_parse(&parg, {parse_sel}, attr))",
"return YNL_PARSE_CB_ERROR;"]
init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;",
f"parg.data = &{var}->{self.c_name};"]
return get_lines, init_lines, None
return get_lines, init_lines, local_vars
class Selector:
@ -979,6 +997,12 @@ class Selector:
def is_external(self):
return self._external
def is_enum_val(self):
return self.get_enum_name() is not None
def get_enum_name(self):
return self.attr and self.attr.attr.get("enum")
class Struct:
def __init__(self, family, space_name, type_list=None, fixed_header=None,