Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xfrm: lwtunnel: add lwtunnel support for xfrm interfaces in collect_md mode

Allow specifying the xfrm interface if_id and link as part of a route
metadata using the lwtunnel infrastructure.

This allows for example using a single xfrm interface in collect_md
mode as the target of multiple routes each specifying a different if_id.

With the appropriate changes to iproute2, considering an xfrm device
ipsec1 in collect_md mode one can for example add a route specifying
an if_id like so:

ip route add <SUBNET> dev ipsec1 encap xfrm if_id 1

In which case traffic routed to the device via this route would use
if_id in the xfrm interface policy lookup.

Or in the context of vrf, one can also specify the "link" property:

ip route add <SUBNET> dev ipsec1 encap xfrm if_id 1 link_dev eth15

Note: LWT_XFRM_LINK uses NLA_U32 similar to IFLA_XFRM_LINK even though
internally "link" is signed. This is consistent with other _LINK
attributes in other devices as well as in bpf and should not have an
effect as device indexes can't be negative.

Reviewed-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Eyal Birger <eyal.birger@gmail.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>

authored by

Eyal Birger and committed by
Steffen Klassert
2c2493b9 abc340b3

+107
+11
include/net/dst_metadata.h
··· 60 60 return NULL; 61 61 } 62 62 63 + static inline struct xfrm_md_info *lwt_xfrm_info(struct lwtunnel_state *lwt) 64 + { 65 + return (struct xfrm_md_info *)lwt->data; 66 + } 67 + 63 68 static inline struct xfrm_md_info *skb_xfrm_md_info(const struct sk_buff *skb) 64 69 { 65 70 struct metadata_dst *md_dst = skb_metadata_dst(skb); 71 + struct dst_entry *dst; 66 72 67 73 if (md_dst && md_dst->type == METADATA_XFRM) 68 74 return &md_dst->u.xfrm_info; 75 + 76 + dst = skb_dst(skb); 77 + if (dst && dst->lwtstate && 78 + dst->lwtstate->type == LWTUNNEL_ENCAP_XFRM) 79 + return lwt_xfrm_info(dst->lwtstate); 69 80 70 81 return NULL; 71 82 }
+10
include/uapi/linux/lwtunnel.h
··· 15 15 LWTUNNEL_ENCAP_SEG6_LOCAL, 16 16 LWTUNNEL_ENCAP_RPL, 17 17 LWTUNNEL_ENCAP_IOAM6, 18 + LWTUNNEL_ENCAP_XFRM, 18 19 __LWTUNNEL_ENCAP_MAX, 19 20 }; 20 21 ··· 111 110 #define LWT_BPF_MAX (__LWT_BPF_MAX - 1) 112 111 113 112 #define LWT_BPF_MAX_HEADROOM 256 113 + 114 + enum { 115 + LWT_XFRM_UNSPEC, 116 + LWT_XFRM_IF_ID, 117 + LWT_XFRM_LINK, 118 + __LWT_XFRM_MAX, 119 + }; 120 + 121 + #define LWT_XFRM_MAX (__LWT_XFRM_MAX - 1) 114 122 115 123 #endif /* _UAPI_LWTUNNEL_H_ */
+1
net/core/lwtunnel.c
··· 50 50 return "IOAM6"; 51 51 case LWTUNNEL_ENCAP_IP6: 52 52 case LWTUNNEL_ENCAP_IP: 53 + case LWTUNNEL_ENCAP_XFRM: 53 54 case LWTUNNEL_ENCAP_NONE: 54 55 case __LWTUNNEL_ENCAP_MAX: 55 56 /* should not have got here */
+85
net/xfrm/xfrm_interface.c
··· 60 60 struct xfrm_if __rcu *collect_md_xfrmi; 61 61 }; 62 62 63 + static const struct nla_policy xfrm_lwt_policy[LWT_XFRM_MAX + 1] = { 64 + [LWT_XFRM_IF_ID] = NLA_POLICY_MIN(NLA_U32, 1), 65 + [LWT_XFRM_LINK] = NLA_POLICY_MIN(NLA_U32, 1), 66 + }; 67 + 68 + static void xfrmi_destroy_state(struct lwtunnel_state *lwt) 69 + { 70 + } 71 + 72 + static int xfrmi_build_state(struct net *net, struct nlattr *nla, 73 + unsigned int family, const void *cfg, 74 + struct lwtunnel_state **ts, 75 + struct netlink_ext_ack *extack) 76 + { 77 + struct nlattr *tb[LWT_XFRM_MAX + 1]; 78 + struct lwtunnel_state *new_state; 79 + struct xfrm_md_info *info; 80 + int ret; 81 + 82 + ret = nla_parse_nested(tb, LWT_XFRM_MAX, nla, xfrm_lwt_policy, extack); 83 + if (ret < 0) 84 + return ret; 85 + 86 + if (!tb[LWT_XFRM_IF_ID]) { 87 + NL_SET_ERR_MSG(extack, "if_id must be set"); 88 + return -EINVAL; 89 + } 90 + 91 + new_state = lwtunnel_state_alloc(sizeof(*info)); 92 + if (!new_state) { 93 + NL_SET_ERR_MSG(extack, "failed to create encap info"); 94 + return -ENOMEM; 95 + } 96 + 97 + new_state->type = LWTUNNEL_ENCAP_XFRM; 98 + 99 + info = lwt_xfrm_info(new_state); 100 + 101 + info->if_id = nla_get_u32(tb[LWT_XFRM_IF_ID]); 102 + 103 + if (tb[LWT_XFRM_LINK]) 104 + info->link = nla_get_u32(tb[LWT_XFRM_LINK]); 105 + 106 + *ts = new_state; 107 + return 0; 108 + } 109 + 110 + static int xfrmi_fill_encap_info(struct sk_buff *skb, 111 + struct lwtunnel_state *lwt) 112 + { 113 + struct xfrm_md_info *info = lwt_xfrm_info(lwt); 114 + 115 + if (nla_put_u32(skb, LWT_XFRM_IF_ID, info->if_id) || 116 + (info->link && nla_put_u32(skb, LWT_XFRM_LINK, info->link))) 117 + return -EMSGSIZE; 118 + 119 + return 0; 120 + } 121 + 122 + static int xfrmi_encap_nlsize(struct lwtunnel_state *lwtstate) 123 + { 124 + return nla_total_size(sizeof(u32)) + /* LWT_XFRM_IF_ID */ 125 + nla_total_size(sizeof(u32)); /* LWT_XFRM_LINK */ 126 + } 127 + 128 + static int xfrmi_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) 129 + { 130 + struct xfrm_md_info *a_info = lwt_xfrm_info(a); 131 + struct xfrm_md_info *b_info = lwt_xfrm_info(b); 132 + 133 + return memcmp(a_info, b_info, sizeof(*a_info)); 134 + } 135 + 136 + static const struct lwtunnel_encap_ops xfrmi_encap_ops = { 137 + .build_state = xfrmi_build_state, 138 + .destroy_state = xfrmi_destroy_state, 139 + .fill_encap = xfrmi_fill_encap_info, 140 + .get_encap_size = xfrmi_encap_nlsize, 141 + .cmp_encap = xfrmi_encap_cmp, 142 + .owner = THIS_MODULE, 143 + }; 144 + 63 145 #define for_each_xfrmi_rcu(start, xi) \ 64 146 for (xi = rcu_dereference(start); xi; xi = rcu_dereference(xi->next)) 65 147 ··· 1162 1080 if (err < 0) 1163 1081 goto rtnl_link_failed; 1164 1082 1083 + lwtunnel_encap_add_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM); 1084 + 1165 1085 xfrm_if_register_cb(&xfrm_if_cb); 1166 1086 1167 1087 return err; ··· 1182 1098 static void __exit xfrmi_fini(void) 1183 1099 { 1184 1100 xfrm_if_unregister_cb(); 1101 + lwtunnel_encap_del_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM); 1185 1102 rtnl_link_unregister(&xfrmi_link_ops); 1186 1103 xfrmi4_fini(); 1187 1104 xfrmi6_fini();