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

selftests/bpf: Add vrf_socket_lookup tests

Verify that socket lookup via TC/XDP with all BPF APIs is VRF aware.

Signed-off-by: Gilad Sever <gilad9366@gmail.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Reviewed-by: Eyal Birger <eyal.birger@gmail.com>
Acked-by: Stanislav Fomichev <sdf@google.com>
Link: https://lore.kernel.org/bpf/20230621104211.301902-5-gilad9366@gmail.com

authored by

Gilad Sever and committed by
Daniel Borkmann
3d5786ea 9a5cb797

+400
+312
tools/testing/selftests/bpf/prog_tests/vrf_socket_lookup.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 + 3 + /* 4 + * Topology: 5 + * --------- 6 + * NS0 namespace | NS1 namespace 7 + * | 8 + * +--------------+ | +--------------+ 9 + * | veth01 |----------| veth10 | 10 + * | 172.16.1.100 | | | 172.16.1.200 | 11 + * | bpf | | +--------------+ 12 + * +--------------+ | 13 + * server(UDP/TCP) | 14 + * +-------------------+ | 15 + * | vrf1 | | 16 + * | +--------------+ | | +--------------+ 17 + * | | veth02 |----------| veth20 | 18 + * | | 172.16.2.100 | | | | 172.16.2.200 | 19 + * | | bpf | | | +--------------+ 20 + * | +--------------+ | | 21 + * | server(UDP/TCP) | | 22 + * +-------------------+ | 23 + * 24 + * Test flow 25 + * ----------- 26 + * The tests verifies that socket lookup via TC is VRF aware: 27 + * 1) Creates two veth pairs between NS0 and NS1: 28 + * a) veth01 <-> veth10 outside the VRF 29 + * b) veth02 <-> veth20 in the VRF 30 + * 2) Attaches to veth01 and veth02 a program that calls: 31 + * a) bpf_skc_lookup_tcp() with TCP and tcp_skc is true 32 + * b) bpf_sk_lookup_tcp() with TCP and tcp_skc is false 33 + * c) bpf_sk_lookup_udp() with UDP 34 + * The program stores the lookup result in bss->lookup_status. 35 + * 3) Creates a socket TCP/UDP server in/outside the VRF. 36 + * 4) The test expects lookup_status to be: 37 + * a) 0 from device in VRF to server outside VRF 38 + * b) 0 from device outside VRF to server in VRF 39 + * c) 1 from device in VRF to server in VRF 40 + * d) 1 from device outside VRF to server outside VRF 41 + */ 42 + 43 + #include <net/if.h> 44 + 45 + #include "test_progs.h" 46 + #include "network_helpers.h" 47 + #include "vrf_socket_lookup.skel.h" 48 + 49 + #define NS0 "vrf_socket_lookup_0" 50 + #define NS1 "vrf_socket_lookup_1" 51 + 52 + #define IP4_ADDR_VETH01 "172.16.1.100" 53 + #define IP4_ADDR_VETH10 "172.16.1.200" 54 + #define IP4_ADDR_VETH02 "172.16.2.100" 55 + #define IP4_ADDR_VETH20 "172.16.2.200" 56 + 57 + #define NON_VRF_PORT 5000 58 + #define IN_VRF_PORT 5001 59 + 60 + #define TIMEOUT_MS 3000 61 + 62 + static int make_socket(int sotype, const char *ip, int port, 63 + struct sockaddr_storage *addr) 64 + { 65 + int err, fd; 66 + 67 + err = make_sockaddr(AF_INET, ip, port, addr, NULL); 68 + if (!ASSERT_OK(err, "make_address")) 69 + return -1; 70 + 71 + fd = socket(AF_INET, sotype, 0); 72 + if (!ASSERT_GE(fd, 0, "socket")) 73 + return -1; 74 + 75 + if (!ASSERT_OK(settimeo(fd, TIMEOUT_MS), "settimeo")) 76 + goto fail; 77 + 78 + return fd; 79 + fail: 80 + close(fd); 81 + return -1; 82 + } 83 + 84 + static int make_server(int sotype, const char *ip, int port, const char *ifname) 85 + { 86 + int err, fd = -1; 87 + 88 + fd = start_server(AF_INET, sotype, ip, port, TIMEOUT_MS); 89 + if (!ASSERT_GE(fd, 0, "start_server")) 90 + return -1; 91 + 92 + if (ifname) { 93 + err = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, 94 + ifname, strlen(ifname) + 1); 95 + if (!ASSERT_OK(err, "setsockopt(SO_BINDTODEVICE)")) 96 + goto fail; 97 + } 98 + 99 + return fd; 100 + fail: 101 + close(fd); 102 + return -1; 103 + } 104 + 105 + static int attach_progs(char *ifname, int tc_prog_fd, int xdp_prog_fd) 106 + { 107 + LIBBPF_OPTS(bpf_tc_hook, hook, .attach_point = BPF_TC_INGRESS); 108 + LIBBPF_OPTS(bpf_tc_opts, opts, .handle = 1, .priority = 1, 109 + .prog_fd = tc_prog_fd); 110 + int ret, ifindex; 111 + 112 + ifindex = if_nametoindex(ifname); 113 + if (!ASSERT_NEQ(ifindex, 0, "if_nametoindex")) 114 + return -1; 115 + hook.ifindex = ifindex; 116 + 117 + ret = bpf_tc_hook_create(&hook); 118 + if (!ASSERT_OK(ret, "bpf_tc_hook_create")) 119 + return ret; 120 + 121 + ret = bpf_tc_attach(&hook, &opts); 122 + if (!ASSERT_OK(ret, "bpf_tc_attach")) { 123 + bpf_tc_hook_destroy(&hook); 124 + return ret; 125 + } 126 + ret = bpf_xdp_attach(ifindex, xdp_prog_fd, 0, NULL); 127 + if (!ASSERT_OK(ret, "bpf_xdp_attach")) { 128 + bpf_tc_hook_destroy(&hook); 129 + return ret; 130 + } 131 + 132 + return 0; 133 + } 134 + 135 + static void cleanup(void) 136 + { 137 + SYS_NOFAIL("test -f /var/run/netns/" NS0 " && ip netns delete " 138 + NS0); 139 + SYS_NOFAIL("test -f /var/run/netns/" NS1 " && ip netns delete " 140 + NS1); 141 + } 142 + 143 + static int setup(struct vrf_socket_lookup *skel) 144 + { 145 + int tc_prog_fd, xdp_prog_fd, ret = 0; 146 + struct nstoken *nstoken = NULL; 147 + 148 + SYS(fail, "ip netns add " NS0); 149 + SYS(fail, "ip netns add " NS1); 150 + 151 + /* NS0 <-> NS1 [veth01 <-> veth10] */ 152 + SYS(fail, "ip link add veth01 netns " NS0 " type veth peer name veth10" 153 + " netns " NS1); 154 + SYS(fail, "ip -net " NS0 " addr add " IP4_ADDR_VETH01 "/24 dev veth01"); 155 + SYS(fail, "ip -net " NS0 " link set dev veth01 up"); 156 + SYS(fail, "ip -net " NS1 " addr add " IP4_ADDR_VETH10 "/24 dev veth10"); 157 + SYS(fail, "ip -net " NS1 " link set dev veth10 up"); 158 + 159 + /* NS0 <-> NS1 [veth02 <-> veth20] */ 160 + SYS(fail, "ip link add veth02 netns " NS0 " type veth peer name veth20" 161 + " netns " NS1); 162 + SYS(fail, "ip -net " NS0 " addr add " IP4_ADDR_VETH02 "/24 dev veth02"); 163 + SYS(fail, "ip -net " NS0 " link set dev veth02 up"); 164 + SYS(fail, "ip -net " NS1 " addr add " IP4_ADDR_VETH20 "/24 dev veth20"); 165 + SYS(fail, "ip -net " NS1 " link set dev veth20 up"); 166 + 167 + /* veth02 -> vrf1 */ 168 + SYS(fail, "ip -net " NS0 " link add vrf1 type vrf table 11"); 169 + SYS(fail, "ip -net " NS0 " route add vrf vrf1 unreachable default" 170 + " metric 4278198272"); 171 + SYS(fail, "ip -net " NS0 " link set vrf1 alias vrf"); 172 + SYS(fail, "ip -net " NS0 " link set vrf1 up"); 173 + SYS(fail, "ip -net " NS0 " link set veth02 master vrf1"); 174 + 175 + /* Attach TC and XDP progs to veth devices in NS0 */ 176 + nstoken = open_netns(NS0); 177 + if (!ASSERT_OK_PTR(nstoken, "setns " NS0)) 178 + goto fail; 179 + tc_prog_fd = bpf_program__fd(skel->progs.tc_socket_lookup); 180 + if (!ASSERT_GE(tc_prog_fd, 0, "bpf_program__tc_fd")) 181 + goto fail; 182 + xdp_prog_fd = bpf_program__fd(skel->progs.xdp_socket_lookup); 183 + if (!ASSERT_GE(xdp_prog_fd, 0, "bpf_program__xdp_fd")) 184 + goto fail; 185 + 186 + if (attach_progs("veth01", tc_prog_fd, xdp_prog_fd)) 187 + goto fail; 188 + 189 + if (attach_progs("veth02", tc_prog_fd, xdp_prog_fd)) 190 + goto fail; 191 + 192 + goto close; 193 + fail: 194 + ret = -1; 195 + close: 196 + if (nstoken) 197 + close_netns(nstoken); 198 + return ret; 199 + } 200 + 201 + static int test_lookup(struct vrf_socket_lookup *skel, int sotype, 202 + const char *ip, int port, bool test_xdp, bool tcp_skc, 203 + int lookup_status_exp) 204 + { 205 + static const char msg[] = "Hello Server"; 206 + struct sockaddr_storage addr = {}; 207 + int fd, ret = 0; 208 + 209 + fd = make_socket(sotype, ip, port, &addr); 210 + if (fd < 0) 211 + return -1; 212 + 213 + skel->bss->test_xdp = test_xdp; 214 + skel->bss->tcp_skc = tcp_skc; 215 + skel->bss->lookup_status = -1; 216 + 217 + if (sotype == SOCK_STREAM) 218 + connect(fd, (void *)&addr, sizeof(struct sockaddr_in)); 219 + else 220 + sendto(fd, msg, sizeof(msg), 0, (void *)&addr, 221 + sizeof(struct sockaddr_in)); 222 + 223 + if (!ASSERT_EQ(skel->bss->lookup_status, lookup_status_exp, 224 + "lookup_status")) 225 + goto fail; 226 + 227 + goto close; 228 + 229 + fail: 230 + ret = -1; 231 + close: 232 + close(fd); 233 + return ret; 234 + } 235 + 236 + static void _test_vrf_socket_lookup(struct vrf_socket_lookup *skel, int sotype, 237 + bool test_xdp, bool tcp_skc) 238 + { 239 + int in_vrf_server = -1, non_vrf_server = -1; 240 + struct nstoken *nstoken = NULL; 241 + 242 + nstoken = open_netns(NS0); 243 + if (!ASSERT_OK_PTR(nstoken, "setns " NS0)) 244 + goto done; 245 + 246 + /* Open sockets in and outside VRF */ 247 + non_vrf_server = make_server(sotype, "0.0.0.0", NON_VRF_PORT, NULL); 248 + if (!ASSERT_GE(non_vrf_server, 0, "make_server__outside_vrf_fd")) 249 + goto done; 250 + 251 + in_vrf_server = make_server(sotype, "0.0.0.0", IN_VRF_PORT, "veth02"); 252 + if (!ASSERT_GE(in_vrf_server, 0, "make_server__in_vrf_fd")) 253 + goto done; 254 + 255 + /* Perform test from NS1 */ 256 + close_netns(nstoken); 257 + nstoken = open_netns(NS1); 258 + if (!ASSERT_OK_PTR(nstoken, "setns " NS1)) 259 + goto done; 260 + 261 + if (!ASSERT_OK(test_lookup(skel, sotype, IP4_ADDR_VETH02, NON_VRF_PORT, 262 + test_xdp, tcp_skc, 0), "in_to_out")) 263 + goto done; 264 + if (!ASSERT_OK(test_lookup(skel, sotype, IP4_ADDR_VETH02, IN_VRF_PORT, 265 + test_xdp, tcp_skc, 1), "in_to_in")) 266 + goto done; 267 + if (!ASSERT_OK(test_lookup(skel, sotype, IP4_ADDR_VETH01, NON_VRF_PORT, 268 + test_xdp, tcp_skc, 1), "out_to_out")) 269 + goto done; 270 + if (!ASSERT_OK(test_lookup(skel, sotype, IP4_ADDR_VETH01, IN_VRF_PORT, 271 + test_xdp, tcp_skc, 0), "out_to_in")) 272 + goto done; 273 + 274 + done: 275 + if (non_vrf_server >= 0) 276 + close(non_vrf_server); 277 + if (in_vrf_server >= 0) 278 + close(in_vrf_server); 279 + if (nstoken) 280 + close_netns(nstoken); 281 + } 282 + 283 + void test_vrf_socket_lookup(void) 284 + { 285 + struct vrf_socket_lookup *skel; 286 + 287 + cleanup(); 288 + 289 + skel = vrf_socket_lookup__open_and_load(); 290 + if (!ASSERT_OK_PTR(skel, "vrf_socket_lookup__open_and_load")) 291 + return; 292 + 293 + if (!ASSERT_OK(setup(skel), "setup")) 294 + goto done; 295 + 296 + if (test__start_subtest("tc_socket_lookup_tcp")) 297 + _test_vrf_socket_lookup(skel, SOCK_STREAM, false, false); 298 + if (test__start_subtest("tc_socket_lookup_tcp_skc")) 299 + _test_vrf_socket_lookup(skel, SOCK_STREAM, false, false); 300 + if (test__start_subtest("tc_socket_lookup_udp")) 301 + _test_vrf_socket_lookup(skel, SOCK_STREAM, false, false); 302 + if (test__start_subtest("xdp_socket_lookup_tcp")) 303 + _test_vrf_socket_lookup(skel, SOCK_STREAM, true, false); 304 + if (test__start_subtest("xdp_socket_lookup_tcp_skc")) 305 + _test_vrf_socket_lookup(skel, SOCK_STREAM, true, false); 306 + if (test__start_subtest("xdp_socket_lookup_udp")) 307 + _test_vrf_socket_lookup(skel, SOCK_STREAM, true, false); 308 + 309 + done: 310 + vrf_socket_lookup__destroy(skel); 311 + cleanup(); 312 + }
+88
tools/testing/selftests/bpf/progs/vrf_socket_lookup.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/bpf.h> 3 + #include <linux/ip.h> 4 + #include <linux/in.h> 5 + #include <linux/if_ether.h> 6 + #include <linux/pkt_cls.h> 7 + #include <bpf/bpf_helpers.h> 8 + #include <bpf/bpf_endian.h> 9 + #include <stdbool.h> 10 + 11 + int lookup_status; 12 + bool test_xdp; 13 + bool tcp_skc; 14 + 15 + #define CUR_NS BPF_F_CURRENT_NETNS 16 + 17 + static void socket_lookup(void *ctx, void *data_end, void *data) 18 + { 19 + struct ethhdr *eth = data; 20 + struct bpf_sock_tuple *tp; 21 + struct bpf_sock *sk; 22 + struct iphdr *iph; 23 + int tplen; 24 + 25 + if (eth + 1 > data_end) 26 + return; 27 + 28 + if (eth->h_proto != bpf_htons(ETH_P_IP)) 29 + return; 30 + 31 + iph = (struct iphdr *)(eth + 1); 32 + if (iph + 1 > data_end) 33 + return; 34 + 35 + tp = (struct bpf_sock_tuple *)&iph->saddr; 36 + tplen = sizeof(tp->ipv4); 37 + if ((void *)tp + tplen > data_end) 38 + return; 39 + 40 + switch (iph->protocol) { 41 + case IPPROTO_TCP: 42 + if (tcp_skc) 43 + sk = bpf_skc_lookup_tcp(ctx, tp, tplen, CUR_NS, 0); 44 + else 45 + sk = bpf_sk_lookup_tcp(ctx, tp, tplen, CUR_NS, 0); 46 + break; 47 + case IPPROTO_UDP: 48 + sk = bpf_sk_lookup_udp(ctx, tp, tplen, CUR_NS, 0); 49 + break; 50 + default: 51 + return; 52 + } 53 + 54 + lookup_status = 0; 55 + 56 + if (sk) { 57 + bpf_sk_release(sk); 58 + lookup_status = 1; 59 + } 60 + } 61 + 62 + SEC("tc") 63 + int tc_socket_lookup(struct __sk_buff *skb) 64 + { 65 + void *data_end = (void *)(long)skb->data_end; 66 + void *data = (void *)(long)skb->data; 67 + 68 + if (test_xdp) 69 + return TC_ACT_UNSPEC; 70 + 71 + socket_lookup(skb, data_end, data); 72 + return TC_ACT_UNSPEC; 73 + } 74 + 75 + SEC("xdp") 76 + int xdp_socket_lookup(struct xdp_md *xdp) 77 + { 78 + void *data_end = (void *)(long)xdp->data_end; 79 + void *data = (void *)(long)xdp->data; 80 + 81 + if (!test_xdp) 82 + return XDP_PASS; 83 + 84 + socket_lookup(xdp, data_end, data); 85 + return XDP_PASS; 86 + } 87 + 88 + char _license[] SEC("license") = "GPL";