0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #define _GNU_SOURCE
0013 #include <sched.h>
0014 #include <net/if.h>
0015 #include <linux/if_link.h>
0016 #include "test_progs.h"
0017 #include "network_helpers.h"
0018 #include <linux/if_bonding.h>
0019 #include <linux/limits.h>
0020 #include <linux/udp.h>
0021
0022 #include "xdp_dummy.skel.h"
0023 #include "xdp_redirect_multi_kern.skel.h"
0024 #include "xdp_tx.skel.h"
0025
0026 #define BOND1_MAC {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}
0027 #define BOND1_MAC_STR "00:11:22:33:44:55"
0028 #define BOND2_MAC {0x00, 0x22, 0x33, 0x44, 0x55, 0x66}
0029 #define BOND2_MAC_STR "00:22:33:44:55:66"
0030 #define NPACKETS 100
0031
0032 static int root_netns_fd = -1;
0033
0034 static void restore_root_netns(void)
0035 {
0036 ASSERT_OK(setns(root_netns_fd, CLONE_NEWNET), "restore_root_netns");
0037 }
0038
0039 static int setns_by_name(char *name)
0040 {
0041 int nsfd, err;
0042 char nspath[PATH_MAX];
0043
0044 snprintf(nspath, sizeof(nspath), "%s/%s", "/var/run/netns", name);
0045 nsfd = open(nspath, O_RDONLY | O_CLOEXEC);
0046 if (nsfd < 0)
0047 return -1;
0048
0049 err = setns(nsfd, CLONE_NEWNET);
0050 close(nsfd);
0051 return err;
0052 }
0053
0054 static int get_rx_packets(const char *iface)
0055 {
0056 FILE *f;
0057 char line[512];
0058 int iface_len = strlen(iface);
0059
0060 f = fopen("/proc/net/dev", "r");
0061 if (!f)
0062 return -1;
0063
0064 while (fgets(line, sizeof(line), f)) {
0065 char *p = line;
0066
0067 while (*p == ' ')
0068 p++;
0069 if (!strncmp(p, iface, iface_len)) {
0070 p += iface_len;
0071 if (*p++ != ':')
0072 continue;
0073 while (*p == ' ')
0074 p++;
0075 while (*p && *p != ' ')
0076 p++;
0077 while (*p == ' ')
0078 p++;
0079 fclose(f);
0080 return atoi(p);
0081 }
0082 }
0083 fclose(f);
0084 return -1;
0085 }
0086
0087 #define MAX_BPF_LINKS 8
0088
0089 struct skeletons {
0090 struct xdp_dummy *xdp_dummy;
0091 struct xdp_tx *xdp_tx;
0092 struct xdp_redirect_multi_kern *xdp_redirect_multi_kern;
0093
0094 int nlinks;
0095 struct bpf_link *links[MAX_BPF_LINKS];
0096 };
0097
0098 static int xdp_attach(struct skeletons *skeletons, struct bpf_program *prog, char *iface)
0099 {
0100 struct bpf_link *link;
0101 int ifindex;
0102
0103 ifindex = if_nametoindex(iface);
0104 if (!ASSERT_GT(ifindex, 0, "get ifindex"))
0105 return -1;
0106
0107 if (!ASSERT_LE(skeletons->nlinks+1, MAX_BPF_LINKS, "too many XDP programs attached"))
0108 return -1;
0109
0110 link = bpf_program__attach_xdp(prog, ifindex);
0111 if (!ASSERT_OK_PTR(link, "attach xdp program"))
0112 return -1;
0113
0114 skeletons->links[skeletons->nlinks++] = link;
0115 return 0;
0116 }
0117
0118 enum {
0119 BOND_ONE_NO_ATTACH = 0,
0120 BOND_BOTH_AND_ATTACH,
0121 };
0122
0123 static const char * const mode_names[] = {
0124 [BOND_MODE_ROUNDROBIN] = "balance-rr",
0125 [BOND_MODE_ACTIVEBACKUP] = "active-backup",
0126 [BOND_MODE_XOR] = "balance-xor",
0127 [BOND_MODE_BROADCAST] = "broadcast",
0128 [BOND_MODE_8023AD] = "802.3ad",
0129 [BOND_MODE_TLB] = "balance-tlb",
0130 [BOND_MODE_ALB] = "balance-alb",
0131 };
0132
0133 static const char * const xmit_policy_names[] = {
0134 [BOND_XMIT_POLICY_LAYER2] = "layer2",
0135 [BOND_XMIT_POLICY_LAYER34] = "layer3+4",
0136 [BOND_XMIT_POLICY_LAYER23] = "layer2+3",
0137 [BOND_XMIT_POLICY_ENCAP23] = "encap2+3",
0138 [BOND_XMIT_POLICY_ENCAP34] = "encap3+4",
0139 };
0140
0141 static int bonding_setup(struct skeletons *skeletons, int mode, int xmit_policy,
0142 int bond_both_attach)
0143 {
0144 #define SYS(fmt, ...) \
0145 ({ \
0146 char cmd[1024]; \
0147 snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
0148 if (!ASSERT_OK(system(cmd), cmd)) \
0149 return -1; \
0150 })
0151
0152 SYS("ip netns add ns_dst");
0153 SYS("ip link add veth1_1 type veth peer name veth2_1 netns ns_dst");
0154 SYS("ip link add veth1_2 type veth peer name veth2_2 netns ns_dst");
0155
0156 SYS("ip link add bond1 type bond mode %s xmit_hash_policy %s",
0157 mode_names[mode], xmit_policy_names[xmit_policy]);
0158 SYS("ip link set bond1 up address " BOND1_MAC_STR " addrgenmode none");
0159 SYS("ip -netns ns_dst link add bond2 type bond mode %s xmit_hash_policy %s",
0160 mode_names[mode], xmit_policy_names[xmit_policy]);
0161 SYS("ip -netns ns_dst link set bond2 up address " BOND2_MAC_STR " addrgenmode none");
0162
0163 SYS("ip link set veth1_1 master bond1");
0164 if (bond_both_attach == BOND_BOTH_AND_ATTACH) {
0165 SYS("ip link set veth1_2 master bond1");
0166 } else {
0167 SYS("ip link set veth1_2 up addrgenmode none");
0168
0169 if (xdp_attach(skeletons, skeletons->xdp_dummy->progs.xdp_dummy_prog, "veth1_2"))
0170 return -1;
0171 }
0172
0173 SYS("ip -netns ns_dst link set veth2_1 master bond2");
0174
0175 if (bond_both_attach == BOND_BOTH_AND_ATTACH)
0176 SYS("ip -netns ns_dst link set veth2_2 master bond2");
0177 else
0178 SYS("ip -netns ns_dst link set veth2_2 up addrgenmode none");
0179
0180
0181
0182
0183 if (xdp_attach(skeletons, skeletons->xdp_dummy->progs.xdp_dummy_prog, "bond1"))
0184 return -1;
0185
0186 if (bond_both_attach == BOND_BOTH_AND_ATTACH) {
0187 if (!ASSERT_OK(setns_by_name("ns_dst"), "set netns to ns_dst"))
0188 return -1;
0189
0190 if (xdp_attach(skeletons, skeletons->xdp_tx->progs.xdp_tx, "bond2"))
0191 return -1;
0192
0193 restore_root_netns();
0194 }
0195
0196 return 0;
0197
0198 #undef SYS
0199 }
0200
0201 static void bonding_cleanup(struct skeletons *skeletons)
0202 {
0203 restore_root_netns();
0204 while (skeletons->nlinks) {
0205 skeletons->nlinks--;
0206 bpf_link__destroy(skeletons->links[skeletons->nlinks]);
0207 }
0208 ASSERT_OK(system("ip link delete bond1"), "delete bond1");
0209 ASSERT_OK(system("ip link delete veth1_1"), "delete veth1_1");
0210 ASSERT_OK(system("ip link delete veth1_2"), "delete veth1_2");
0211 ASSERT_OK(system("ip netns delete ns_dst"), "delete ns_dst");
0212 }
0213
0214 static int send_udp_packets(int vary_dst_ip)
0215 {
0216 struct ethhdr eh = {
0217 .h_source = BOND1_MAC,
0218 .h_dest = BOND2_MAC,
0219 .h_proto = htons(ETH_P_IP),
0220 };
0221 struct iphdr iph = {};
0222 struct udphdr uh = {};
0223 uint8_t buf[128];
0224 int i, s = -1;
0225 int ifindex;
0226
0227 s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
0228 if (!ASSERT_GE(s, 0, "socket"))
0229 goto err;
0230
0231 ifindex = if_nametoindex("bond1");
0232 if (!ASSERT_GT(ifindex, 0, "get bond1 ifindex"))
0233 goto err;
0234
0235 iph.ihl = 5;
0236 iph.version = 4;
0237 iph.tos = 16;
0238 iph.id = 1;
0239 iph.ttl = 64;
0240 iph.protocol = IPPROTO_UDP;
0241 iph.saddr = 1;
0242 iph.daddr = 2;
0243 iph.tot_len = htons(sizeof(buf) - ETH_HLEN);
0244 iph.check = 0;
0245
0246 for (i = 1; i <= NPACKETS; i++) {
0247 int n;
0248 struct sockaddr_ll saddr_ll = {
0249 .sll_ifindex = ifindex,
0250 .sll_halen = ETH_ALEN,
0251 .sll_addr = BOND2_MAC,
0252 };
0253
0254
0255 uh.dest++;
0256
0257 if (vary_dst_ip)
0258 iph.daddr++;
0259
0260
0261 memcpy(buf, &eh, sizeof(eh));
0262 memcpy(buf + sizeof(eh), &iph, sizeof(iph));
0263 memcpy(buf + sizeof(eh) + sizeof(iph), &uh, sizeof(uh));
0264
0265 n = sendto(s, buf, sizeof(buf), 0, (struct sockaddr *)&saddr_ll, sizeof(saddr_ll));
0266 if (!ASSERT_EQ(n, sizeof(buf), "sendto"))
0267 goto err;
0268 }
0269
0270 return 0;
0271
0272 err:
0273 if (s >= 0)
0274 close(s);
0275 return -1;
0276 }
0277
0278 static void test_xdp_bonding_with_mode(struct skeletons *skeletons, int mode, int xmit_policy)
0279 {
0280 int bond1_rx;
0281
0282 if (bonding_setup(skeletons, mode, xmit_policy, BOND_BOTH_AND_ATTACH))
0283 goto out;
0284
0285 if (send_udp_packets(xmit_policy != BOND_XMIT_POLICY_LAYER34))
0286 goto out;
0287
0288 bond1_rx = get_rx_packets("bond1");
0289 ASSERT_EQ(bond1_rx, NPACKETS, "expected more received packets");
0290
0291 switch (mode) {
0292 case BOND_MODE_ROUNDROBIN:
0293 case BOND_MODE_XOR: {
0294 int veth1_rx = get_rx_packets("veth1_1");
0295 int veth2_rx = get_rx_packets("veth1_2");
0296 int diff = abs(veth1_rx - veth2_rx);
0297
0298 ASSERT_GE(veth1_rx + veth2_rx, NPACKETS, "expected more packets");
0299
0300 switch (xmit_policy) {
0301 case BOND_XMIT_POLICY_LAYER2:
0302 ASSERT_GE(diff, NPACKETS,
0303 "expected packets on only one of the interfaces");
0304 break;
0305 case BOND_XMIT_POLICY_LAYER23:
0306 case BOND_XMIT_POLICY_LAYER34:
0307 ASSERT_LT(diff, NPACKETS/2,
0308 "expected even distribution of packets");
0309 break;
0310 default:
0311 PRINT_FAIL("Unimplemented xmit_policy=%d\n", xmit_policy);
0312 break;
0313 }
0314 break;
0315 }
0316 case BOND_MODE_ACTIVEBACKUP: {
0317 int veth1_rx = get_rx_packets("veth1_1");
0318 int veth2_rx = get_rx_packets("veth1_2");
0319 int diff = abs(veth1_rx - veth2_rx);
0320
0321 ASSERT_GE(diff, NPACKETS,
0322 "expected packets on only one of the interfaces");
0323 break;
0324 }
0325 default:
0326 PRINT_FAIL("Unimplemented xmit_policy=%d\n", xmit_policy);
0327 break;
0328 }
0329
0330 out:
0331 bonding_cleanup(skeletons);
0332 }
0333
0334
0335
0336
0337
0338 static void test_xdp_bonding_redirect_multi(struct skeletons *skeletons)
0339 {
0340 static const char * const ifaces[] = {"bond2", "veth2_1", "veth2_2"};
0341 int veth1_1_rx, veth1_2_rx;
0342 int err;
0343
0344 if (bonding_setup(skeletons, BOND_MODE_ROUNDROBIN, BOND_XMIT_POLICY_LAYER23,
0345 BOND_ONE_NO_ATTACH))
0346 goto out;
0347
0348
0349 if (!ASSERT_OK(setns_by_name("ns_dst"), "could not set netns to ns_dst"))
0350 goto out;
0351
0352
0353 for (int i = 0; i < ARRAY_SIZE(ifaces); i++) {
0354 int ifindex = if_nametoindex(ifaces[i]);
0355 int map_fd = bpf_map__fd(skeletons->xdp_redirect_multi_kern->maps.map_all);
0356
0357 if (!ASSERT_GT(ifindex, 0, "could not get interface index"))
0358 goto out;
0359
0360 err = bpf_map_update_elem(map_fd, &ifindex, &ifindex, 0);
0361 if (!ASSERT_OK(err, "add interface to map_all"))
0362 goto out;
0363 }
0364
0365 if (xdp_attach(skeletons,
0366 skeletons->xdp_redirect_multi_kern->progs.xdp_redirect_map_multi_prog,
0367 "bond2"))
0368 goto out;
0369
0370 restore_root_netns();
0371
0372 if (send_udp_packets(BOND_MODE_ROUNDROBIN))
0373 goto out;
0374
0375 veth1_1_rx = get_rx_packets("veth1_1");
0376 veth1_2_rx = get_rx_packets("veth1_2");
0377
0378 ASSERT_EQ(veth1_1_rx, 0, "expected no packets on veth1_1");
0379 ASSERT_GE(veth1_2_rx, NPACKETS, "expected packets on veth1_2");
0380
0381 out:
0382 restore_root_netns();
0383 bonding_cleanup(skeletons);
0384 }
0385
0386
0387 static void test_xdp_bonding_attach(struct skeletons *skeletons)
0388 {
0389 struct bpf_link *link = NULL;
0390 struct bpf_link *link2 = NULL;
0391 int veth, bond, err;
0392
0393 if (!ASSERT_OK(system("ip link add veth type veth"), "add veth"))
0394 goto out;
0395 if (!ASSERT_OK(system("ip link add bond type bond"), "add bond"))
0396 goto out;
0397
0398 veth = if_nametoindex("veth");
0399 if (!ASSERT_GE(veth, 0, "if_nametoindex veth"))
0400 goto out;
0401 bond = if_nametoindex("bond");
0402 if (!ASSERT_GE(bond, 0, "if_nametoindex bond"))
0403 goto out;
0404
0405
0406 link = bpf_program__attach_xdp(skeletons->xdp_dummy->progs.xdp_dummy_prog, veth);
0407 if (!ASSERT_OK_PTR(link, "attach program to veth"))
0408 goto out;
0409
0410 err = system("ip link set veth master bond");
0411 if (!ASSERT_OK(err, "set veth master"))
0412 goto out;
0413
0414 bpf_link__destroy(link);
0415 link = NULL;
0416
0417
0418 link = bpf_program__attach_xdp(skeletons->xdp_dummy->progs.xdp_dummy_prog, veth);
0419 if (!ASSERT_OK_PTR(link, "attach program to slave when enslaved"))
0420 goto out;
0421
0422
0423 link2 = bpf_program__attach_xdp(skeletons->xdp_dummy->progs.xdp_dummy_prog, bond);
0424 if (!ASSERT_ERR_PTR(link2, "attach program to master when slave has program"))
0425 goto out;
0426
0427 bpf_link__destroy(link);
0428 link = NULL;
0429
0430
0431 link = bpf_program__attach_xdp(skeletons->xdp_dummy->progs.xdp_dummy_prog, bond);
0432 if (!ASSERT_OK_PTR(link, "attach program to master"))
0433 goto out;
0434
0435
0436 link2 = bpf_program__attach_xdp(skeletons->xdp_dummy->progs.xdp_dummy_prog, veth);
0437 if (!ASSERT_ERR_PTR(link2, "attach program to slave when master has program"))
0438 goto out;
0439
0440 bpf_link__destroy(link);
0441 link = NULL;
0442
0443
0444 if (!ASSERT_OK(system("ip link add vxlan type vxlan id 1 remote 1.2.3.4 dstport 0 dev lo"),
0445 "add vxlan"))
0446 goto out;
0447
0448 err = system("ip link set vxlan master bond");
0449 if (!ASSERT_OK(err, "set vxlan master"))
0450 goto out;
0451
0452
0453 link = bpf_program__attach_xdp(skeletons->xdp_dummy->progs.xdp_dummy_prog, bond);
0454 if (!ASSERT_ERR_PTR(link, "attach program to master when slave does not support XDP"))
0455 goto out;
0456
0457 out:
0458 bpf_link__destroy(link);
0459 bpf_link__destroy(link2);
0460
0461 system("ip link del veth");
0462 system("ip link del bond");
0463 system("ip link del vxlan");
0464 }
0465
0466
0467 static void test_xdp_bonding_nested(struct skeletons *skeletons)
0468 {
0469 struct bpf_link *link = NULL;
0470 int bond, err;
0471
0472 if (!ASSERT_OK(system("ip link add bond type bond"), "add bond"))
0473 goto out;
0474
0475 bond = if_nametoindex("bond");
0476 if (!ASSERT_GE(bond, 0, "if_nametoindex bond"))
0477 goto out;
0478
0479 if (!ASSERT_OK(system("ip link add bond_nest1 type bond"), "add bond_nest1"))
0480 goto out;
0481
0482 err = system("ip link set bond_nest1 master bond");
0483 if (!ASSERT_OK(err, "set bond_nest1 master"))
0484 goto out;
0485
0486 if (!ASSERT_OK(system("ip link add bond_nest2 type bond"), "add bond_nest1"))
0487 goto out;
0488
0489 err = system("ip link set bond_nest2 master bond_nest1");
0490 if (!ASSERT_OK(err, "set bond_nest2 master"))
0491 goto out;
0492
0493 link = bpf_program__attach_xdp(skeletons->xdp_dummy->progs.xdp_dummy_prog, bond);
0494 ASSERT_OK_PTR(link, "attach program to master");
0495
0496 out:
0497 bpf_link__destroy(link);
0498 system("ip link del bond");
0499 system("ip link del bond_nest1");
0500 system("ip link del bond_nest2");
0501 }
0502
0503 static int libbpf_debug_print(enum libbpf_print_level level,
0504 const char *format, va_list args)
0505 {
0506 if (level != LIBBPF_WARN)
0507 vprintf(format, args);
0508 return 0;
0509 }
0510
0511 struct bond_test_case {
0512 char *name;
0513 int mode;
0514 int xmit_policy;
0515 };
0516
0517 static struct bond_test_case bond_test_cases[] = {
0518 { "xdp_bonding_roundrobin", BOND_MODE_ROUNDROBIN, BOND_XMIT_POLICY_LAYER23, },
0519 { "xdp_bonding_activebackup", BOND_MODE_ACTIVEBACKUP, BOND_XMIT_POLICY_LAYER23 },
0520
0521 { "xdp_bonding_xor_layer2", BOND_MODE_XOR, BOND_XMIT_POLICY_LAYER2, },
0522 { "xdp_bonding_xor_layer23", BOND_MODE_XOR, BOND_XMIT_POLICY_LAYER23, },
0523 { "xdp_bonding_xor_layer34", BOND_MODE_XOR, BOND_XMIT_POLICY_LAYER34, },
0524 };
0525
0526 void serial_test_xdp_bonding(void)
0527 {
0528 libbpf_print_fn_t old_print_fn;
0529 struct skeletons skeletons = {};
0530 int i;
0531
0532 old_print_fn = libbpf_set_print(libbpf_debug_print);
0533
0534 root_netns_fd = open("/proc/self/ns/net", O_RDONLY);
0535 if (!ASSERT_GE(root_netns_fd, 0, "open /proc/self/ns/net"))
0536 goto out;
0537
0538 skeletons.xdp_dummy = xdp_dummy__open_and_load();
0539 if (!ASSERT_OK_PTR(skeletons.xdp_dummy, "xdp_dummy__open_and_load"))
0540 goto out;
0541
0542 skeletons.xdp_tx = xdp_tx__open_and_load();
0543 if (!ASSERT_OK_PTR(skeletons.xdp_tx, "xdp_tx__open_and_load"))
0544 goto out;
0545
0546 skeletons.xdp_redirect_multi_kern = xdp_redirect_multi_kern__open_and_load();
0547 if (!ASSERT_OK_PTR(skeletons.xdp_redirect_multi_kern,
0548 "xdp_redirect_multi_kern__open_and_load"))
0549 goto out;
0550
0551 if (test__start_subtest("xdp_bonding_attach"))
0552 test_xdp_bonding_attach(&skeletons);
0553
0554 if (test__start_subtest("xdp_bonding_nested"))
0555 test_xdp_bonding_nested(&skeletons);
0556
0557 for (i = 0; i < ARRAY_SIZE(bond_test_cases); i++) {
0558 struct bond_test_case *test_case = &bond_test_cases[i];
0559
0560 if (test__start_subtest(test_case->name))
0561 test_xdp_bonding_with_mode(
0562 &skeletons,
0563 test_case->mode,
0564 test_case->xmit_policy);
0565 }
0566
0567 if (test__start_subtest("xdp_bonding_redirect_multi"))
0568 test_xdp_bonding_redirect_multi(&skeletons);
0569
0570 out:
0571 xdp_dummy__destroy(skeletons.xdp_dummy);
0572 xdp_tx__destroy(skeletons.xdp_tx);
0573 xdp_redirect_multi_kern__destroy(skeletons.xdp_redirect_multi_kern);
0574
0575 libbpf_set_print(old_print_fn);
0576 if (root_netns_fd >= 0)
0577 close(root_netns_fd);
0578 }