Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 
0003 /**
0004  * Test XDP bonding support
0005  *
0006  * Sets up two bonded veth pairs between two fresh namespaces
0007  * and verifies that XDP_TX program loaded on a bond device
0008  * are correctly loaded onto the slave devices and XDP_TX'd
0009  * packets are balanced using bonding.
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++; /* skip whitespace */
0069         if (!strncmp(p, iface, iface_len)) {
0070             p += iface_len;
0071             if (*p++ != ':')
0072                 continue;
0073             while (*p == ' ')
0074                 p++; /* skip whitespace */
0075             while (*p && *p != ' ')
0076                 p++; /* skip rx bytes */
0077             while (*p == ' ')
0078                 p++; /* skip whitespace */
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     /* Load a dummy program on sending side as with veth peer needs to have a
0181      * XDP program loaded as well.
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         /* vary the UDP destination port for even distribution with roundrobin/xor modes */
0255         uh.dest++;
0256 
0257         if (vary_dst_ip)
0258             iph.daddr++;
0259 
0260         /* construct a packet */
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 /* Test the broadcast redirection using xdp_redirect_map_multi_prog and adding
0335  * all the interfaces to it and checking that broadcasting won't send the packet
0336  * to neither the ingress bond device (bond2) or its slave (veth2_1).
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     /* populate the devmap with the relevant interfaces */
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 /* Test that XDP programs cannot be attached to both the bond master and slaves simultaneously */
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     /* enslaving with a XDP program loaded is allowed */
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     /* attaching to slave when master has no program is allowed */
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     /* attaching to master not allowed when slave has program loaded */
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     /* attaching XDP program to master allowed when slave has no program */
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     /* attaching to slave not allowed when master has program loaded */
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     /* test program unwinding with a non-XDP slave */
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     /* attaching not allowed when one slave does not support XDP */
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 /* Test with nested bonding devices to catch issue with negative jump label count */
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 }