/** * Test XDP bonding support * * Sets up two bonded veth pairs between two fresh namespaces * and verifies that XDP_TX program loaded on a bond device * are correctly loaded onto the slave devices and XDP_TX'd * packets are balanced using bonding.
*/
staticint bonding_setup(struct skeletons *skeletons, int mode, int xmit_policy, int bond_both_attach)
{
SYS(fail, "ip netns add ns_dst");
SYS(fail, "ip link add veth1_1 type veth peer name veth2_1 netns ns_dst");
SYS(fail, "ip link add veth1_2 type veth peer name veth2_2 netns ns_dst");
SYS(fail, "ip link add bond1 type bond mode %s xmit_hash_policy %s",
mode_names[mode], xmit_policy_names[xmit_policy]);
SYS(fail, "ip link set bond1 up address " BOND1_MAC_STR " addrgenmode none");
SYS(fail, "ip -netns ns_dst link add bond2 type bond mode %s xmit_hash_policy %s",
mode_names[mode], xmit_policy_names[xmit_policy]);
SYS(fail, "ip -netns ns_dst link set bond2 up address " BOND2_MAC_STR " addrgenmode none");
SYS(fail, "ip link set veth1_1 master bond1"); if (bond_both_attach == BOND_BOTH_AND_ATTACH) {
SYS(fail, "ip link set veth1_2 master bond1");
} else {
SYS(fail, "ip link set veth1_2 up addrgenmode none");
if (xdp_attach(skeletons, skeletons->xdp_dummy->progs.xdp_dummy_prog, "veth1_2")) return -1;
}
SYS(fail, "ip -netns ns_dst link set veth2_1 master bond2");
if (bond_both_attach == BOND_BOTH_AND_ATTACH)
SYS(fail, "ip -netns ns_dst link set veth2_2 master bond2"); else
SYS(fail, "ip -netns ns_dst link set veth2_2 up addrgenmode none");
/* Load a dummy program on sending side as with veth peer needs to have a * XDP program loaded as well.
*/ if (xdp_attach(skeletons, skeletons->xdp_dummy->progs.xdp_dummy_prog, "bond1")) return -1;
if (bond_both_attach == BOND_BOTH_AND_ATTACH) { if (!ASSERT_OK(setns_by_name("ns_dst"), "set netns to ns_dst")) return -1;
if (xdp_attach(skeletons, skeletons->xdp_tx->progs.xdp_tx, "bond2")) return -1;
restore_root_netns();
}
return 0;
fail: return -1;
}
staticvoid bonding_cleanup(struct skeletons *skeletons)
{
restore_root_netns(); while (skeletons->nlinks) {
skeletons->nlinks--;
bpf_link__destroy(skeletons->links[skeletons->nlinks]);
}
ASSERT_OK(system("ip link delete bond1"), "delete bond1");
ASSERT_OK(system("ip link delete veth1_1"), "delete veth1_1");
ASSERT_OK(system("ip link delete veth1_2"), "delete veth1_2");
ASSERT_OK(system("ip netns delete ns_dst"), "delete ns_dst");
}
staticint send_udp_packets(int vary_dst_ip)
{ struct ethhdr eh = {
.h_source = BOND1_MAC,
.h_dest = BOND2_MAC,
.h_proto = htons(ETH_P_IP),
}; struct iphdr iph = {}; struct udphdr uh = {};
uint8_t buf[128]; int i, s = -1; int ifindex;
s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW); if (!ASSERT_GE(s, 0, "socket")) goto err;
n = sendto(s, buf, sizeof(buf), 0, (struct sockaddr *)&saddr_ll, sizeof(saddr_ll)); if (!ASSERT_EQ(n, sizeof(buf), "sendto")) goto err;
}
return 0;
err: if (s >= 0)
close(s); return -1;
}
staticvoid test_xdp_bonding_with_mode(struct skeletons *skeletons, int mode, int xmit_policy)
{ int bond1_rx;
if (bonding_setup(skeletons, mode, xmit_policy, BOND_BOTH_AND_ATTACH)) goto out;
if (send_udp_packets(xmit_policy != BOND_XMIT_POLICY_LAYER34)) goto out;
bond1_rx = get_rx_packets("bond1");
ASSERT_EQ(bond1_rx, NPACKETS, "expected more received packets");
switch (mode) { case BOND_MODE_ROUNDROBIN: case BOND_MODE_XOR: { int veth1_rx = get_rx_packets("veth1_1"); int veth2_rx = get_rx_packets("veth1_2"); int diff = abs(veth1_rx - veth2_rx);
ASSERT_GE(veth1_rx + veth2_rx, NPACKETS, "expected more packets");
switch (xmit_policy) { case BOND_XMIT_POLICY_LAYER2:
ASSERT_GE(diff, NPACKETS, "expected packets on only one of the interfaces"); break; case BOND_XMIT_POLICY_LAYER23: case BOND_XMIT_POLICY_LAYER34:
ASSERT_LT(diff, NPACKETS/2, "expected even distribution of packets"); break; default:
PRINT_FAIL("Unimplemented xmit_policy=%d\n", xmit_policy); break;
} break;
} case BOND_MODE_ACTIVEBACKUP: { int veth1_rx = get_rx_packets("veth1_1"); int veth2_rx = get_rx_packets("veth1_2"); int diff = abs(veth1_rx - veth2_rx);
ASSERT_GE(diff, NPACKETS, "expected packets on only one of the interfaces"); break;
} default:
PRINT_FAIL("Unimplemented xmit_policy=%d\n", xmit_policy); break;
}
out:
bonding_cleanup(skeletons);
}
/* Test the broadcast redirection using xdp_redirect_map_multi_prog and adding * all the interfaces to it and checking that broadcasting won't send the packet * to neither the ingress bond device (bond2) or its slave (veth2_1).
*/ staticvoid test_xdp_bonding_redirect_multi(struct skeletons *skeletons)
{ staticconstchar * const ifaces[] = {"bond2", "veth2_1", "veth2_2"}; int veth1_1_rx, veth1_2_rx; int err;
if (bonding_setup(skeletons, BOND_MODE_ROUNDROBIN, BOND_XMIT_POLICY_LAYER23,
BOND_ONE_NO_ATTACH)) goto out;
if (!ASSERT_OK(setns_by_name("ns_dst"), "could not set netns to ns_dst")) goto out;
/* populate the devmap with the relevant interfaces */ for (int i = 0; i < ARRAY_SIZE(ifaces); i++) { int ifindex = if_nametoindex(ifaces[i]); int map_fd = bpf_map__fd(skeletons->xdp_redirect_multi_kern->maps.map_all);
if (!ASSERT_GT(ifindex, 0, "could not get interface index")) goto out;
err = bpf_map_update_elem(map_fd, &ifindex, &ifindex, 0); if (!ASSERT_OK(err, "add interface to map_all")) goto out;
}
if (xdp_attach(skeletons,
skeletons->xdp_redirect_multi_kern->progs.xdp_redirect_map_multi_prog, "bond2")) goto out;
restore_root_netns();
if (send_udp_packets(BOND_MODE_ROUNDROBIN)) goto out;
/* Test that XDP programs cannot be attached to both the bond master and slaves simultaneously */ staticvoid test_xdp_bonding_attach(struct skeletons *skeletons)
{ struct bpf_link *link = NULL; struct bpf_link *link2 = NULL; int veth, bond, err;
if (!ASSERT_OK(system("ip link add veth type veth"), "add veth")) goto out; if (!ASSERT_OK(system("ip link add bond type bond"), "add bond")) goto out;
veth = if_nametoindex("veth"); if (!ASSERT_GE(veth, 0, "if_nametoindex veth")) goto out;
bond = if_nametoindex("bond"); if (!ASSERT_GE(bond, 0, "if_nametoindex bond")) goto out;
/* enslaving with a XDP program loaded is allowed */
link = bpf_program__attach_xdp(skeletons->xdp_dummy->progs.xdp_dummy_prog, veth); if (!ASSERT_OK_PTR(link, "attach program to veth")) goto out;
err = system("ip link set veth master bond"); if (!ASSERT_OK(err, "set veth master")) goto out;
bpf_link__destroy(link);
link = NULL;
/* attaching to slave when master has no program is allowed */
link = bpf_program__attach_xdp(skeletons->xdp_dummy->progs.xdp_dummy_prog, veth); if (!ASSERT_OK_PTR(link, "attach program to slave when enslaved")) goto out;
/* attaching to master not allowed when slave has program loaded */
link2 = bpf_program__attach_xdp(skeletons->xdp_dummy->progs.xdp_dummy_prog, bond); if (!ASSERT_ERR_PTR(link2, "attach program to master when slave has program")) goto out;
bpf_link__destroy(link);
link = NULL;
/* attaching XDP program to master allowed when slave has no program */
link = bpf_program__attach_xdp(skeletons->xdp_dummy->progs.xdp_dummy_prog, bond); if (!ASSERT_OK_PTR(link, "attach program to master")) goto out;
/* attaching to slave not allowed when master has program loaded */
link2 = bpf_program__attach_xdp(skeletons->xdp_dummy->progs.xdp_dummy_prog, veth); if (!ASSERT_ERR_PTR(link2, "attach program to slave when master has program")) goto out;
bpf_link__destroy(link);
link = NULL;
/* test program unwinding with a non-XDP slave */ if (!ASSERT_OK(system("ip link add vxlan type vxlan id 1 remote 1.2.3.4 dstport 0 dev lo"), "add vxlan")) goto out;
err = system("ip link set vxlan master bond"); if (!ASSERT_OK(err, "set vxlan master")) goto out;
/* attaching not allowed when one slave does not support XDP */
link = bpf_program__attach_xdp(skeletons->xdp_dummy->progs.xdp_dummy_prog, bond); if (!ASSERT_ERR_PTR(link, "attach program to master when slave does not support XDP")) goto out;
if (!ASSERT_OK(system("ip link add bond type bond"), "add bond")) goto out;
bond_idx = if_nametoindex("bond"); if (!ASSERT_GE(bond_idx, 0, "if_nametoindex bond")) goto out;
/* query default xdp-feature for bond device */
err = bpf_xdp_query(bond_idx, XDP_FLAGS_DRV_MODE, &query_opts); if (!ASSERT_OK(err, "bond bpf_xdp_query")) goto out;
if (!ASSERT_EQ(query_opts.feature_flags, 0, "bond query_opts.feature_flags")) goto out;
if (!ASSERT_OK(system("ip link add veth0 type veth peer name veth1"), "add veth{0,1} pair")) goto out;
if (!ASSERT_OK(system("ip link add veth2 type veth peer name veth3"), "add veth{2,3} pair")) goto out;
if (!ASSERT_OK(system("ip link set veth0 master bond"), "add veth0 to master bond")) goto out;
/* xdp-feature for bond device should be obtained from the single slave * device (veth0)
*/
err = bpf_xdp_query(bond_idx, XDP_FLAGS_DRV_MODE, &query_opts); if (!ASSERT_OK(err, "bond bpf_xdp_query")) goto out;
/* xdp-feature for bond device should be set to the most restrict * value obtained from attached slave devices (veth0 and veth2)
*/ if (!ASSERT_EQ(query_opts.feature_flags,
NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
NETDEV_XDP_ACT_RX_SG, "bond query_opts.feature_flags")) goto out;
if (!ASSERT_OK(system("ip link set veth2 nomaster"), "del veth2 to master bond")) goto out;
ASSERT_EQ(query_opts.feature_flags, 0, "bond query_opts.feature_flags");
out:
bpf_link__destroy(link);
system("ip link del veth0");
system("ip link del veth2");
system("ip link del bond");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.