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

Merge branch 'xfrm: interface: Add unstable helpers for XFRM metadata'

Eyal Birger says:

====================

This patch series adds xfrm metadata helpers using the unstable kfunc
call interface for the TC-BPF hooks.

This allows steering traffic towards different IPsec connections based
on logic implemented in bpf programs.

The helpers are integrated into the xfrm_interface module. For this
purpose the main functionality of this module is moved to
xfrm_interface_core.c.
---

changes in v6: fix sparse warning in patch 2
changes in v5:
- avoid cleanup of percpu dsts as detailed in patch 2
changes in v3:
- tag bpf-next tree instead of ipsec-next
- add IFLA_XFRM_COLLECT_METADATA sync patch
====================

Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>

+574 -2
+1
include/net/dst_metadata.h
··· 26 26 struct xfrm_md_info { 27 27 u32 if_id; 28 28 int link; 29 + struct dst_entry *dst_orig; 29 30 }; 30 31 31 32 struct metadata_dst {
+17
include/net/xfrm.h
··· 2086 2086 return false; 2087 2087 } 2088 2088 #endif 2089 + 2090 + #if (IS_BUILTIN(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) || \ 2091 + (IS_MODULE(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) 2092 + 2093 + extern struct metadata_dst __percpu *xfrm_bpf_md_dst; 2094 + 2095 + int register_xfrm_interface_bpf(void); 2096 + 2097 + #else 2098 + 2099 + static inline int register_xfrm_interface_bpf(void) 2100 + { 2101 + return 0; 2102 + } 2103 + 2104 + #endif 2105 + 2089 2106 #endif /* _NET_XFRM_H */
+6 -2
net/core/dst.c
··· 316 316 if (md_dst->type == METADATA_IP_TUNNEL) 317 317 dst_cache_destroy(&md_dst->u.tun_info.dst_cache); 318 318 #endif 319 + if (md_dst->type == METADATA_XFRM) 320 + dst_release(md_dst->u.xfrm_info.dst_orig); 319 321 kfree(md_dst); 320 322 } 321 323 EXPORT_SYMBOL_GPL(metadata_dst_free); ··· 342 340 343 341 void metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst) 344 342 { 345 - #ifdef CONFIG_DST_CACHE 346 343 int cpu; 347 344 348 345 for_each_possible_cpu(cpu) { 349 346 struct metadata_dst *one_md_dst = per_cpu_ptr(md_dst, cpu); 350 347 348 + #ifdef CONFIG_DST_CACHE 351 349 if (one_md_dst->type == METADATA_IP_TUNNEL) 352 350 dst_cache_destroy(&one_md_dst->u.tun_info.dst_cache); 353 - } 354 351 #endif 352 + if (one_md_dst->type == METADATA_XFRM) 353 + dst_release(one_md_dst->u.xfrm_info.dst_orig); 354 + } 355 355 free_percpu(md_dst); 356 356 } 357 357 EXPORT_SYMBOL_GPL(metadata_dst_free_percpu);
+9
net/core/filter.c
··· 5631 5631 }; 5632 5632 5633 5633 #ifdef CONFIG_XFRM 5634 + 5635 + #if (IS_BUILTIN(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) || \ 5636 + (IS_MODULE(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) 5637 + 5638 + struct metadata_dst __percpu *xfrm_bpf_md_dst; 5639 + EXPORT_SYMBOL_GPL(xfrm_bpf_md_dst); 5640 + 5641 + #endif 5642 + 5634 5643 BPF_CALL_5(bpf_skb_get_xfrm_state, struct sk_buff *, skb, u32, index, 5635 5644 struct bpf_xfrm_state *, to, u32, size, u64, flags) 5636 5645 {
+8
net/xfrm/Makefile
··· 3 3 # Makefile for the XFRM subsystem. 4 4 # 5 5 6 + xfrm_interface-$(CONFIG_XFRM_INTERFACE) += xfrm_interface_core.o 7 + 8 + ifeq ($(CONFIG_XFRM_INTERFACE),m) 9 + xfrm_interface-$(CONFIG_DEBUG_INFO_BTF_MODULES) += xfrm_interface_bpf.o 10 + else ifeq ($(CONFIG_XFRM_INTERFACE),y) 11 + xfrm_interface-$(CONFIG_DEBUG_INFO_BTF) += xfrm_interface_bpf.o 12 + endif 13 + 6 14 obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \ 7 15 xfrm_input.o xfrm_output.o \ 8 16 xfrm_sysctl.o xfrm_replay.o xfrm_device.o
+14
net/xfrm/xfrm_interface.c net/xfrm/xfrm_interface_core.c
··· 396 396 397 397 if_id = md_info->if_id; 398 398 fl->flowi_oif = md_info->link; 399 + if (md_info->dst_orig) { 400 + struct dst_entry *tmp_dst = dst; 401 + 402 + dst = md_info->dst_orig; 403 + skb_dst_set(skb, dst); 404 + md_info->dst_orig = NULL; 405 + dst_release(tmp_dst); 406 + } 399 407 } else { 400 408 if_id = xi->p.if_id; 401 409 } ··· 1170 1162 if (err < 0) 1171 1163 goto rtnl_link_failed; 1172 1164 1165 + err = register_xfrm_interface_bpf(); 1166 + if (err < 0) 1167 + goto kfunc_failed; 1168 + 1173 1169 lwtunnel_encap_add_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM); 1174 1170 1175 1171 xfrm_if_register_cb(&xfrm_if_cb); 1176 1172 1177 1173 return err; 1178 1174 1175 + kfunc_failed: 1176 + rtnl_link_unregister(&xfrmi_link_ops); 1179 1177 rtnl_link_failed: 1180 1178 xfrmi6_fini(); 1181 1179 xfrmi6_failed:
+115
net/xfrm/xfrm_interface_bpf.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* Unstable XFRM Helpers for TC-BPF hook 3 + * 4 + * These are called from SCHED_CLS BPF programs. Note that it is 5 + * allowed to break compatibility for these functions since the interface they 6 + * are exposed through to BPF programs is explicitly unstable. 7 + */ 8 + 9 + #include <linux/bpf.h> 10 + #include <linux/btf_ids.h> 11 + 12 + #include <net/dst_metadata.h> 13 + #include <net/xfrm.h> 14 + 15 + /* bpf_xfrm_info - XFRM metadata information 16 + * 17 + * Members: 18 + * @if_id - XFRM if_id: 19 + * Transmit: if_id to be used in policy and state lookups 20 + * Receive: if_id of the state matched for the incoming packet 21 + * @link - Underlying device ifindex: 22 + * Transmit: used as the underlying device in VRF routing 23 + * Receive: the device on which the packet had been received 24 + */ 25 + struct bpf_xfrm_info { 26 + u32 if_id; 27 + int link; 28 + }; 29 + 30 + __diag_push(); 31 + __diag_ignore_all("-Wmissing-prototypes", 32 + "Global functions as their definitions will be in xfrm_interface BTF"); 33 + 34 + /* bpf_skb_get_xfrm_info - Get XFRM metadata 35 + * 36 + * Parameters: 37 + * @skb_ctx - Pointer to ctx (__sk_buff) in TC program 38 + * Cannot be NULL 39 + * @to - Pointer to memory to which the metadata will be copied 40 + * Cannot be NULL 41 + */ 42 + __used noinline 43 + int bpf_skb_get_xfrm_info(struct __sk_buff *skb_ctx, struct bpf_xfrm_info *to) 44 + { 45 + struct sk_buff *skb = (struct sk_buff *)skb_ctx; 46 + struct xfrm_md_info *info; 47 + 48 + info = skb_xfrm_md_info(skb); 49 + if (!info) 50 + return -EINVAL; 51 + 52 + to->if_id = info->if_id; 53 + to->link = info->link; 54 + return 0; 55 + } 56 + 57 + /* bpf_skb_get_xfrm_info - Set XFRM metadata 58 + * 59 + * Parameters: 60 + * @skb_ctx - Pointer to ctx (__sk_buff) in TC program 61 + * Cannot be NULL 62 + * @from - Pointer to memory from which the metadata will be copied 63 + * Cannot be NULL 64 + */ 65 + __used noinline 66 + int bpf_skb_set_xfrm_info(struct __sk_buff *skb_ctx, 67 + const struct bpf_xfrm_info *from) 68 + { 69 + struct sk_buff *skb = (struct sk_buff *)skb_ctx; 70 + struct metadata_dst *md_dst; 71 + struct xfrm_md_info *info; 72 + 73 + if (unlikely(skb_metadata_dst(skb))) 74 + return -EINVAL; 75 + 76 + if (!xfrm_bpf_md_dst) { 77 + struct metadata_dst __percpu *tmp; 78 + 79 + tmp = metadata_dst_alloc_percpu(0, METADATA_XFRM, GFP_ATOMIC); 80 + if (!tmp) 81 + return -ENOMEM; 82 + if (cmpxchg(&xfrm_bpf_md_dst, NULL, tmp)) 83 + metadata_dst_free_percpu(tmp); 84 + } 85 + md_dst = this_cpu_ptr(xfrm_bpf_md_dst); 86 + 87 + info = &md_dst->u.xfrm_info; 88 + 89 + info->if_id = from->if_id; 90 + info->link = from->link; 91 + skb_dst_force(skb); 92 + info->dst_orig = skb_dst(skb); 93 + 94 + dst_hold((struct dst_entry *)md_dst); 95 + skb_dst_set(skb, (struct dst_entry *)md_dst); 96 + return 0; 97 + } 98 + 99 + __diag_pop() 100 + 101 + BTF_SET8_START(xfrm_ifc_kfunc_set) 102 + BTF_ID_FLAGS(func, bpf_skb_get_xfrm_info) 103 + BTF_ID_FLAGS(func, bpf_skb_set_xfrm_info) 104 + BTF_SET8_END(xfrm_ifc_kfunc_set) 105 + 106 + static const struct btf_kfunc_id_set xfrm_interface_kfunc_set = { 107 + .owner = THIS_MODULE, 108 + .set = &xfrm_ifc_kfunc_set, 109 + }; 110 + 111 + int __init register_xfrm_interface_bpf(void) 112 + { 113 + return register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, 114 + &xfrm_interface_kfunc_set); 115 + }
+1
tools/include/uapi/linux/if_link.h
··· 673 673 IFLA_XFRM_UNSPEC, 674 674 IFLA_XFRM_LINK, 675 675 IFLA_XFRM_IF_ID, 676 + IFLA_XFRM_COLLECT_METADATA, 676 677 __IFLA_XFRM_MAX 677 678 }; 678 679
+1
tools/testing/selftests/bpf/DENYLIST.s390x
··· 85 85 xdp_bpf2bpf # failed to auto-attach program 'trace_on_entry': -524 (trampoline) 86 86 xdp_do_redirect # prog_run_max_size unexpected error: -22 (errno 22) 87 87 xdp_synproxy # JIT does not support calling kernel function (kfunc) 88 + xfrm_info # JIT does not support calling kernel function (kfunc)
+2
tools/testing/selftests/bpf/config
··· 23 23 CONFIG_IMA=y 24 24 CONFIG_IMA_READ_POLICY=y 25 25 CONFIG_IMA_WRITE_POLICY=y 26 + CONFIG_INET_ESP=y 26 27 CONFIG_IP_NF_FILTER=y 27 28 CONFIG_IP_NF_RAW=y 28 29 CONFIG_IP_NF_TARGET_SYNPROXY=y ··· 75 74 CONFIG_USERFAULTFD=y 76 75 CONFIG_VXLAN=y 77 76 CONFIG_XDP_SOCKETS=y 77 + CONFIG_XFRM_INTERFACE=y
+362
tools/testing/selftests/bpf/prog_tests/xfrm_info.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 + 3 + /* 4 + * Topology: 5 + * --------- 6 + * NS0 namespace | NS1 namespace | NS2 namespace 7 + * | | 8 + * +---------------+ | +---------------+ | 9 + * | ipsec0 |---------| ipsec0 | | 10 + * | 192.168.1.100 | | | 192.168.1.200 | | 11 + * | if_id: bpf | | +---------------+ | 12 + * +---------------+ | | 13 + * | | | +---------------+ 14 + * | | | | ipsec0 | 15 + * \------------------------------------------| 192.168.1.200 | 16 + * | | +---------------+ 17 + * | | 18 + * | | (overlay network) 19 + * ------------------------------------------------------ 20 + * | | (underlay network) 21 + * +--------------+ | +--------------+ | 22 + * | veth01 |----------| veth10 | | 23 + * | 172.16.1.100 | | | 172.16.1.200 | | 24 + * ---------------+ | +--------------+ | 25 + * | | 26 + * +--------------+ | | +--------------+ 27 + * | veth02 |-----------------------------------| veth20 | 28 + * | 172.16.2.100 | | | | 172.16.2.200 | 29 + * +--------------+ | | +--------------+ 30 + * 31 + * 32 + * Test Packet flow 33 + * ----------- 34 + * The tests perform 'ping 192.168.1.200' from the NS0 namespace: 35 + * 1) request is routed to NS0 ipsec0 36 + * 2) NS0 ipsec0 tc egress BPF program is triggered and sets the if_id based 37 + * on the requested value. This makes the ipsec0 device in external mode 38 + * select the destination tunnel 39 + * 3) ping reaches the other namespace (NS1 or NS2 based on which if_id was 40 + * used) and response is sent 41 + * 4) response is received on NS0 ipsec0, tc ingress program is triggered and 42 + * records the response if_id 43 + * 5) requested if_id is compared with received if_id 44 + */ 45 + 46 + #include <net/if.h> 47 + #include <linux/rtnetlink.h> 48 + #include <linux/if_link.h> 49 + 50 + #include "test_progs.h" 51 + #include "network_helpers.h" 52 + #include "xfrm_info.skel.h" 53 + 54 + #define NS0 "xfrm_test_ns0" 55 + #define NS1 "xfrm_test_ns1" 56 + #define NS2 "xfrm_test_ns2" 57 + 58 + #define IF_ID_0_TO_1 1 59 + #define IF_ID_0_TO_2 2 60 + #define IF_ID_1 3 61 + #define IF_ID_2 4 62 + 63 + #define IP4_ADDR_VETH01 "172.16.1.100" 64 + #define IP4_ADDR_VETH10 "172.16.1.200" 65 + #define IP4_ADDR_VETH02 "172.16.2.100" 66 + #define IP4_ADDR_VETH20 "172.16.2.200" 67 + 68 + #define ESP_DUMMY_PARAMS \ 69 + "proto esp aead 'rfc4106(gcm(aes))' " \ 70 + "0xe4d8f4b4da1df18a3510b3781496daa82488b713 128 mode tunnel " 71 + 72 + #define SYS(fmt, ...) \ 73 + ({ \ 74 + char cmd[1024]; \ 75 + snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \ 76 + if (!ASSERT_OK(system(cmd), cmd)) \ 77 + goto fail; \ 78 + }) 79 + 80 + #define SYS_NOFAIL(fmt, ...) \ 81 + ({ \ 82 + char cmd[1024]; \ 83 + snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \ 84 + system(cmd); \ 85 + }) 86 + 87 + static int attach_tc_prog(struct bpf_tc_hook *hook, int igr_fd, int egr_fd) 88 + { 89 + LIBBPF_OPTS(bpf_tc_opts, opts1, .handle = 1, .priority = 1, 90 + .prog_fd = igr_fd); 91 + LIBBPF_OPTS(bpf_tc_opts, opts2, .handle = 1, .priority = 1, 92 + .prog_fd = egr_fd); 93 + int ret; 94 + 95 + ret = bpf_tc_hook_create(hook); 96 + if (!ASSERT_OK(ret, "create tc hook")) 97 + return ret; 98 + 99 + if (igr_fd >= 0) { 100 + hook->attach_point = BPF_TC_INGRESS; 101 + ret = bpf_tc_attach(hook, &opts1); 102 + if (!ASSERT_OK(ret, "bpf_tc_attach")) { 103 + bpf_tc_hook_destroy(hook); 104 + return ret; 105 + } 106 + } 107 + 108 + if (egr_fd >= 0) { 109 + hook->attach_point = BPF_TC_EGRESS; 110 + ret = bpf_tc_attach(hook, &opts2); 111 + if (!ASSERT_OK(ret, "bpf_tc_attach")) { 112 + bpf_tc_hook_destroy(hook); 113 + return ret; 114 + } 115 + } 116 + 117 + return 0; 118 + } 119 + 120 + static void cleanup(void) 121 + { 122 + SYS_NOFAIL("test -f /var/run/netns/" NS0 " && ip netns delete " NS0); 123 + SYS_NOFAIL("test -f /var/run/netns/" NS1 " && ip netns delete " NS1); 124 + SYS_NOFAIL("test -f /var/run/netns/" NS2 " && ip netns delete " NS2); 125 + } 126 + 127 + static int config_underlay(void) 128 + { 129 + SYS("ip netns add " NS0); 130 + SYS("ip netns add " NS1); 131 + SYS("ip netns add " NS2); 132 + 133 + /* NS0 <-> NS1 [veth01 <-> veth10] */ 134 + SYS("ip link add veth01 netns " NS0 " type veth peer name veth10 netns " NS1); 135 + SYS("ip -net " NS0 " addr add " IP4_ADDR_VETH01 "/24 dev veth01"); 136 + SYS("ip -net " NS0 " link set dev veth01 up"); 137 + SYS("ip -net " NS1 " addr add " IP4_ADDR_VETH10 "/24 dev veth10"); 138 + SYS("ip -net " NS1 " link set dev veth10 up"); 139 + 140 + /* NS0 <-> NS2 [veth02 <-> veth20] */ 141 + SYS("ip link add veth02 netns " NS0 " type veth peer name veth20 netns " NS2); 142 + SYS("ip -net " NS0 " addr add " IP4_ADDR_VETH02 "/24 dev veth02"); 143 + SYS("ip -net " NS0 " link set dev veth02 up"); 144 + SYS("ip -net " NS2 " addr add " IP4_ADDR_VETH20 "/24 dev veth20"); 145 + SYS("ip -net " NS2 " link set dev veth20 up"); 146 + 147 + return 0; 148 + fail: 149 + return -1; 150 + } 151 + 152 + static int setup_xfrm_tunnel_ns(const char *ns, const char *ipv4_local, 153 + const char *ipv4_remote, int if_id) 154 + { 155 + /* State: local -> remote */ 156 + SYS("ip -net %s xfrm state add src %s dst %s spi 1 " 157 + ESP_DUMMY_PARAMS "if_id %d", ns, ipv4_local, ipv4_remote, if_id); 158 + 159 + /* State: local <- remote */ 160 + SYS("ip -net %s xfrm state add src %s dst %s spi 1 " 161 + ESP_DUMMY_PARAMS "if_id %d", ns, ipv4_remote, ipv4_local, if_id); 162 + 163 + /* Policy: local -> remote */ 164 + SYS("ip -net %s xfrm policy add dir out src 0.0.0.0/0 dst 0.0.0.0/0 " 165 + "if_id %d tmpl src %s dst %s proto esp mode tunnel if_id %d", ns, 166 + if_id, ipv4_local, ipv4_remote, if_id); 167 + 168 + /* Policy: local <- remote */ 169 + SYS("ip -net %s xfrm policy add dir in src 0.0.0.0/0 dst 0.0.0.0/0 " 170 + "if_id %d tmpl src %s dst %s proto esp mode tunnel if_id %d", ns, 171 + if_id, ipv4_remote, ipv4_local, if_id); 172 + 173 + return 0; 174 + fail: 175 + return -1; 176 + } 177 + 178 + static int setup_xfrm_tunnel(const char *ns_a, const char *ns_b, 179 + const char *ipv4_a, const char *ipv4_b, 180 + int if_id_a, int if_id_b) 181 + { 182 + return setup_xfrm_tunnel_ns(ns_a, ipv4_a, ipv4_b, if_id_a) || 183 + setup_xfrm_tunnel_ns(ns_b, ipv4_b, ipv4_a, if_id_b); 184 + } 185 + 186 + static struct rtattr *rtattr_add(struct nlmsghdr *nh, unsigned short type, 187 + unsigned short len) 188 + { 189 + struct rtattr *rta = 190 + (struct rtattr *)((uint8_t *)nh + RTA_ALIGN(nh->nlmsg_len)); 191 + rta->rta_type = type; 192 + rta->rta_len = RTA_LENGTH(len); 193 + nh->nlmsg_len = RTA_ALIGN(nh->nlmsg_len) + RTA_ALIGN(rta->rta_len); 194 + return rta; 195 + } 196 + 197 + static struct rtattr *rtattr_add_str(struct nlmsghdr *nh, unsigned short type, 198 + const char *s) 199 + { 200 + struct rtattr *rta = rtattr_add(nh, type, strlen(s)); 201 + 202 + memcpy(RTA_DATA(rta), s, strlen(s)); 203 + return rta; 204 + } 205 + 206 + static struct rtattr *rtattr_begin(struct nlmsghdr *nh, unsigned short type) 207 + { 208 + return rtattr_add(nh, type, 0); 209 + } 210 + 211 + static void rtattr_end(struct nlmsghdr *nh, struct rtattr *attr) 212 + { 213 + uint8_t *end = (uint8_t *)nh + nh->nlmsg_len; 214 + 215 + attr->rta_len = end - (uint8_t *)attr; 216 + } 217 + 218 + static int setup_xfrmi_external_dev(const char *ns) 219 + { 220 + struct { 221 + struct nlmsghdr nh; 222 + struct ifinfomsg info; 223 + unsigned char data[128]; 224 + } req; 225 + struct rtattr *link_info, *info_data; 226 + struct nstoken *nstoken; 227 + int ret = -1, sock = -1; 228 + struct nlmsghdr *nh; 229 + 230 + memset(&req, 0, sizeof(req)); 231 + nh = &req.nh; 232 + nh->nlmsg_len = NLMSG_LENGTH(sizeof(req.info)); 233 + nh->nlmsg_type = RTM_NEWLINK; 234 + nh->nlmsg_flags |= NLM_F_CREATE | NLM_F_REQUEST; 235 + 236 + rtattr_add_str(nh, IFLA_IFNAME, "ipsec0"); 237 + link_info = rtattr_begin(nh, IFLA_LINKINFO); 238 + rtattr_add_str(nh, IFLA_INFO_KIND, "xfrm"); 239 + info_data = rtattr_begin(nh, IFLA_INFO_DATA); 240 + rtattr_add(nh, IFLA_XFRM_COLLECT_METADATA, 0); 241 + rtattr_end(nh, info_data); 242 + rtattr_end(nh, link_info); 243 + 244 + nstoken = open_netns(ns); 245 + if (!ASSERT_OK_PTR(nstoken, "setns")) 246 + goto done; 247 + 248 + sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); 249 + if (!ASSERT_GE(sock, 0, "netlink socket")) 250 + goto done; 251 + ret = send(sock, nh, nh->nlmsg_len, 0); 252 + if (!ASSERT_EQ(ret, nh->nlmsg_len, "netlink send length")) 253 + goto done; 254 + 255 + ret = 0; 256 + done: 257 + if (sock != -1) 258 + close(sock); 259 + if (nstoken) 260 + close_netns(nstoken); 261 + return ret; 262 + } 263 + 264 + static int config_overlay(void) 265 + { 266 + if (setup_xfrm_tunnel(NS0, NS1, IP4_ADDR_VETH01, IP4_ADDR_VETH10, 267 + IF_ID_0_TO_1, IF_ID_1)) 268 + goto fail; 269 + if (setup_xfrm_tunnel(NS0, NS2, IP4_ADDR_VETH02, IP4_ADDR_VETH20, 270 + IF_ID_0_TO_2, IF_ID_2)) 271 + goto fail; 272 + 273 + /* Older iproute2 doesn't support this option */ 274 + if (!ASSERT_OK(setup_xfrmi_external_dev(NS0), "xfrmi")) 275 + goto fail; 276 + 277 + SYS("ip -net " NS0 " addr add 192.168.1.100/24 dev ipsec0"); 278 + SYS("ip -net " NS0 " link set dev ipsec0 up"); 279 + 280 + SYS("ip -net " NS1 " link add ipsec0 type xfrm if_id %d", IF_ID_1); 281 + SYS("ip -net " NS1 " addr add 192.168.1.200/24 dev ipsec0"); 282 + SYS("ip -net " NS1 " link set dev ipsec0 up"); 283 + 284 + SYS("ip -net " NS2 " link add ipsec0 type xfrm if_id %d", IF_ID_2); 285 + SYS("ip -net " NS2 " addr add 192.168.1.200/24 dev ipsec0"); 286 + SYS("ip -net " NS2 " link set dev ipsec0 up"); 287 + 288 + return 0; 289 + fail: 290 + return -1; 291 + } 292 + 293 + static int test_xfrm_ping(struct xfrm_info *skel, u32 if_id) 294 + { 295 + skel->bss->req_if_id = if_id; 296 + 297 + SYS("ping -i 0.01 -c 3 -w 10 -q 192.168.1.200 > /dev/null"); 298 + 299 + if (!ASSERT_EQ(skel->bss->resp_if_id, if_id, "if_id")) 300 + goto fail; 301 + 302 + return 0; 303 + fail: 304 + return -1; 305 + } 306 + 307 + static void _test_xfrm_info(void) 308 + { 309 + LIBBPF_OPTS(bpf_tc_hook, tc_hook, .attach_point = BPF_TC_INGRESS); 310 + int get_xfrm_info_prog_fd, set_xfrm_info_prog_fd; 311 + struct nstoken *nstoken = NULL; 312 + struct xfrm_info *skel; 313 + int ifindex; 314 + 315 + /* load and attach bpf progs to ipsec dev tc hook point */ 316 + skel = xfrm_info__open_and_load(); 317 + if (!ASSERT_OK_PTR(skel, "xfrm_info__open_and_load")) 318 + goto done; 319 + nstoken = open_netns(NS0); 320 + if (!ASSERT_OK_PTR(nstoken, "setns " NS0)) 321 + goto done; 322 + ifindex = if_nametoindex("ipsec0"); 323 + if (!ASSERT_NEQ(ifindex, 0, "ipsec0 ifindex")) 324 + goto done; 325 + tc_hook.ifindex = ifindex; 326 + set_xfrm_info_prog_fd = bpf_program__fd(skel->progs.set_xfrm_info); 327 + get_xfrm_info_prog_fd = bpf_program__fd(skel->progs.get_xfrm_info); 328 + if (!ASSERT_GE(set_xfrm_info_prog_fd, 0, "bpf_program__fd")) 329 + goto done; 330 + if (!ASSERT_GE(get_xfrm_info_prog_fd, 0, "bpf_program__fd")) 331 + goto done; 332 + if (attach_tc_prog(&tc_hook, get_xfrm_info_prog_fd, 333 + set_xfrm_info_prog_fd)) 334 + goto done; 335 + 336 + /* perform test */ 337 + if (!ASSERT_EQ(test_xfrm_ping(skel, IF_ID_0_TO_1), 0, "ping " NS1)) 338 + goto done; 339 + if (!ASSERT_EQ(test_xfrm_ping(skel, IF_ID_0_TO_2), 0, "ping " NS2)) 340 + goto done; 341 + 342 + done: 343 + if (nstoken) 344 + close_netns(nstoken); 345 + xfrm_info__destroy(skel); 346 + } 347 + 348 + void test_xfrm_info(void) 349 + { 350 + cleanup(); 351 + 352 + if (!ASSERT_OK(config_underlay(), "config_underlay")) 353 + goto done; 354 + if (!ASSERT_OK(config_overlay(), "config_overlay")) 355 + goto done; 356 + 357 + if (test__start_subtest("xfrm_info")) 358 + _test_xfrm_info(); 359 + 360 + done: 361 + cleanup(); 362 + }
+3
tools/testing/selftests/bpf/progs/bpf_tracing_net.h
··· 25 25 #define IPV6_TCLASS 67 26 26 #define IPV6_AUTOFLOWLABEL 70 27 27 28 + #define TC_ACT_UNSPEC (-1) 29 + #define TC_ACT_SHOT 2 30 + 28 31 #define SOL_TCP 6 29 32 #define TCP_NODELAY 1 30 33 #define TCP_MAXSEG 2
+35
tools/testing/selftests/bpf/progs/xfrm_info.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include "vmlinux.h" 3 + #include "bpf_tracing_net.h" 4 + #include <bpf/bpf_helpers.h> 5 + 6 + __u32 req_if_id; 7 + __u32 resp_if_id; 8 + 9 + int bpf_skb_set_xfrm_info(struct __sk_buff *skb_ctx, 10 + const struct bpf_xfrm_info *from) __ksym; 11 + int bpf_skb_get_xfrm_info(struct __sk_buff *skb_ctx, 12 + struct bpf_xfrm_info *to) __ksym; 13 + 14 + SEC("tc") 15 + int set_xfrm_info(struct __sk_buff *skb) 16 + { 17 + struct bpf_xfrm_info info = { .if_id = req_if_id }; 18 + 19 + return bpf_skb_set_xfrm_info(skb, &info) ? TC_ACT_SHOT : TC_ACT_UNSPEC; 20 + } 21 + 22 + SEC("tc") 23 + int get_xfrm_info(struct __sk_buff *skb) 24 + { 25 + struct bpf_xfrm_info info = {}; 26 + 27 + if (bpf_skb_get_xfrm_info(skb, &info) < 0) 28 + return TC_ACT_SHOT; 29 + 30 + resp_if_id = info.if_id; 31 + 32 + return TC_ACT_UNSPEC; 33 + } 34 + 35 + char _license[] SEC("license") = "GPL";