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

selftests/bpf: Add xdp_redirect_multi test

Add a bpf selftest for new helper xdp_redirect_map_multi(). In this
test there are 3 forward groups and 1 exclude group. The test will
redirect each interface's packets to all the interfaces in the forward
group, and exclude the interface in exclude map.

Two maps (DEVMAP, DEVMAP_HASH) and two xdp modes (generic, drive) will
be tested. XDP egress program will also be tested by setting pkt src MAC
to egress interface's MAC address.

For more test details, you can find it in the test script. Here is
the test result.
]# time ./test_xdp_redirect_multi.sh
Pass: xdpgeneric arp(F_BROADCAST) ns1-1
Pass: xdpgeneric arp(F_BROADCAST) ns1-2
Pass: xdpgeneric arp(F_BROADCAST) ns1-3
Pass: xdpgeneric IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-1
Pass: xdpgeneric IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-2
Pass: xdpgeneric IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-3
Pass: xdpgeneric IPv6 (no flags) ns1-1
Pass: xdpgeneric IPv6 (no flags) ns1-2
Pass: xdpdrv arp(F_BROADCAST) ns1-1
Pass: xdpdrv arp(F_BROADCAST) ns1-2
Pass: xdpdrv arp(F_BROADCAST) ns1-3
Pass: xdpdrv IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-1
Pass: xdpdrv IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-2
Pass: xdpdrv IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-3
Pass: xdpdrv IPv6 (no flags) ns1-1
Pass: xdpdrv IPv6 (no flags) ns1-2
Pass: xdpegress mac ns1-2
Pass: xdpegress mac ns1-3
Summary: PASS 18, FAIL 0

real 1m18.321s
user 0m0.123s
sys 0m0.350s

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Toke Høiland-Jørgensen <toke@redhat.com>
Link: https://lore.kernel.org/bpf/20210519090747.1655268-5-liuhangbin@gmail.com

authored by

Hangbin Liu and committed by
Daniel Borkmann
d2329247 e48cfe4b

+526 -1
+2 -1
tools/testing/selftests/bpf/Makefile
··· 54 54 # Order correspond to 'make run_tests' order 55 55 TEST_PROGS := test_kmod.sh \ 56 56 test_xdp_redirect.sh \ 57 + test_xdp_redirect_multi.sh \ 57 58 test_xdp_meta.sh \ 58 59 test_xdp_veth.sh \ 59 60 test_offload.py \ ··· 85 84 TEST_GEN_PROGS_EXTENDED = test_sock_addr test_skb_cgroup_id_user \ 86 85 flow_dissector_load test_flow_dissector test_tcp_check_syncookie_user \ 87 86 test_lirc_mode2_user xdping test_cpp runqslower bench bpf_testmod.ko \ 88 - xdpxceiver 87 + xdpxceiver xdp_redirect_multi 89 88 90 89 TEST_CUSTOM_PROGS = $(OUTPUT)/urandom_read 91 90
+94
tools/testing/selftests/bpf/progs/xdp_redirect_multi_kern.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #define KBUILD_MODNAME "foo" 3 + #include <string.h> 4 + #include <linux/in.h> 5 + #include <linux/if_ether.h> 6 + #include <linux/if_packet.h> 7 + #include <linux/ip.h> 8 + #include <linux/ipv6.h> 9 + 10 + #include <linux/bpf.h> 11 + #include <bpf/bpf_helpers.h> 12 + #include <bpf/bpf_endian.h> 13 + 14 + /* One map use devmap, another one use devmap_hash for testing */ 15 + struct { 16 + __uint(type, BPF_MAP_TYPE_DEVMAP); 17 + __uint(key_size, sizeof(int)); 18 + __uint(value_size, sizeof(int)); 19 + __uint(max_entries, 1024); 20 + } map_all SEC(".maps"); 21 + 22 + struct { 23 + __uint(type, BPF_MAP_TYPE_DEVMAP_HASH); 24 + __uint(key_size, sizeof(int)); 25 + __uint(value_size, sizeof(struct bpf_devmap_val)); 26 + __uint(max_entries, 128); 27 + } map_egress SEC(".maps"); 28 + 29 + /* map to store egress interfaces mac addresses */ 30 + struct { 31 + __uint(type, BPF_MAP_TYPE_HASH); 32 + __type(key, __u32); 33 + __type(value, __be64); 34 + __uint(max_entries, 128); 35 + } mac_map SEC(".maps"); 36 + 37 + SEC("xdp_redirect_map_multi") 38 + int xdp_redirect_map_multi_prog(struct xdp_md *ctx) 39 + { 40 + void *data_end = (void *)(long)ctx->data_end; 41 + void *data = (void *)(long)ctx->data; 42 + int if_index = ctx->ingress_ifindex; 43 + struct ethhdr *eth = data; 44 + __u16 h_proto; 45 + __u64 nh_off; 46 + 47 + nh_off = sizeof(*eth); 48 + if (data + nh_off > data_end) 49 + return XDP_DROP; 50 + 51 + h_proto = eth->h_proto; 52 + 53 + /* Using IPv4 for (BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS) testing */ 54 + if (h_proto == bpf_htons(ETH_P_IP)) 55 + return bpf_redirect_map(&map_all, 0, 56 + BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS); 57 + /* Using IPv6 for none flag testing */ 58 + else if (h_proto == bpf_htons(ETH_P_IPV6)) 59 + return bpf_redirect_map(&map_all, if_index, 0); 60 + /* All others for BPF_F_BROADCAST testing */ 61 + else 62 + return bpf_redirect_map(&map_all, 0, BPF_F_BROADCAST); 63 + } 64 + 65 + /* The following 2 progs are for 2nd devmap prog testing */ 66 + SEC("xdp_redirect_map_ingress") 67 + int xdp_redirect_map_all_prog(struct xdp_md *ctx) 68 + { 69 + return bpf_redirect_map(&map_egress, 0, 70 + BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS); 71 + } 72 + 73 + SEC("xdp_devmap/map_prog") 74 + int xdp_devmap_prog(struct xdp_md *ctx) 75 + { 76 + void *data_end = (void *)(long)ctx->data_end; 77 + void *data = (void *)(long)ctx->data; 78 + __u32 key = ctx->egress_ifindex; 79 + struct ethhdr *eth = data; 80 + __u64 nh_off; 81 + __be64 *mac; 82 + 83 + nh_off = sizeof(*eth); 84 + if (data + nh_off > data_end) 85 + return XDP_DROP; 86 + 87 + mac = bpf_map_lookup_elem(&mac_map, &key); 88 + if (mac) 89 + __builtin_memcpy(eth->h_source, mac, ETH_ALEN); 90 + 91 + return XDP_PASS; 92 + } 93 + 94 + char _license[] SEC("license") = "GPL";
+204
tools/testing/selftests/bpf/test_xdp_redirect_multi.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Test topology: 5 + # - - - - - - - - - - - - - - - - - - - - - - - - - 6 + # | veth1 veth2 veth3 | ... init net 7 + # - -| - - - - - - | - - - - - - | - - 8 + # --------- --------- --------- 9 + # | veth0 | | veth0 | | veth0 | ... 10 + # --------- --------- --------- 11 + # ns1 ns2 ns3 12 + # 13 + # Test modules: 14 + # XDP modes: generic, native, native + egress_prog 15 + # 16 + # Test cases: 17 + # ARP: Testing BPF_F_BROADCAST, the ingress interface also should receive 18 + # the redirects. 19 + # ns1 -> gw: ns1, ns2, ns3, should receive the arp request 20 + # IPv4: Testing BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS, the ingress 21 + # interface should not receive the redirects. 22 + # ns1 -> gw: ns1 should not receive, ns2, ns3 should receive redirects. 23 + # IPv6: Testing none flag, all the pkts should be redirected back 24 + # ping test: ns1 -> ns2 (block), echo requests will be redirect back 25 + # egress_prog: 26 + # all src mac should be egress interface's mac 27 + 28 + # netns numbers 29 + NUM=3 30 + IFACES="" 31 + DRV_MODE="xdpgeneric xdpdrv xdpegress" 32 + PASS=0 33 + FAIL=0 34 + 35 + test_pass() 36 + { 37 + echo "Pass: $@" 38 + PASS=$((PASS + 1)) 39 + } 40 + 41 + test_fail() 42 + { 43 + echo "fail: $@" 44 + FAIL=$((FAIL + 1)) 45 + } 46 + 47 + clean_up() 48 + { 49 + for i in $(seq $NUM); do 50 + ip link del veth$i 2> /dev/null 51 + ip netns del ns$i 2> /dev/null 52 + done 53 + } 54 + 55 + # Kselftest framework requirement - SKIP code is 4. 56 + check_env() 57 + { 58 + ip link set dev lo xdpgeneric off &>/dev/null 59 + if [ $? -ne 0 ];then 60 + echo "selftests: [SKIP] Could not run test without the ip xdpgeneric support" 61 + exit 4 62 + fi 63 + 64 + which tcpdump &>/dev/null 65 + if [ $? -ne 0 ];then 66 + echo "selftests: [SKIP] Could not run test without tcpdump" 67 + exit 4 68 + fi 69 + } 70 + 71 + setup_ns() 72 + { 73 + local mode=$1 74 + IFACES="" 75 + 76 + if [ "$mode" = "xdpegress" ]; then 77 + mode="xdpdrv" 78 + fi 79 + 80 + for i in $(seq $NUM); do 81 + ip netns add ns$i 82 + ip link add veth$i type veth peer name veth0 netns ns$i 83 + ip link set veth$i up 84 + ip -n ns$i link set veth0 up 85 + 86 + ip -n ns$i addr add 192.0.2.$i/24 dev veth0 87 + ip -n ns$i addr add 2001:db8::$i/64 dev veth0 88 + # Add a neigh entry for IPv4 ping test 89 + ip -n ns$i neigh add 192.0.2.253 lladdr 00:00:00:00:00:01 dev veth0 90 + ip -n ns$i link set veth0 $mode obj \ 91 + xdp_dummy.o sec xdp_dummy &> /dev/null || \ 92 + { test_fail "Unable to load dummy xdp" && exit 1; } 93 + IFACES="$IFACES veth$i" 94 + veth_mac[$i]=$(ip link show veth$i | awk '/link\/ether/ {print $2}') 95 + done 96 + } 97 + 98 + do_egress_tests() 99 + { 100 + local mode=$1 101 + 102 + # mac test 103 + ip netns exec ns2 tcpdump -e -i veth0 -nn -l -e &> mac_ns1-2_${mode}.log & 104 + ip netns exec ns3 tcpdump -e -i veth0 -nn -l -e &> mac_ns1-3_${mode}.log & 105 + sleep 0.5 106 + ip netns exec ns1 ping 192.0.2.254 -i 0.1 -c 4 &> /dev/null 107 + sleep 0.5 108 + pkill -9 tcpdump 109 + 110 + # mac check 111 + grep -q "${veth_mac[2]} > ff:ff:ff:ff:ff:ff" mac_ns1-2_${mode}.log && \ 112 + test_pass "$mode mac ns1-2" || test_fail "$mode mac ns1-2" 113 + grep -q "${veth_mac[3]} > ff:ff:ff:ff:ff:ff" mac_ns1-3_${mode}.log && \ 114 + test_pass "$mode mac ns1-3" || test_fail "$mode mac ns1-3" 115 + } 116 + 117 + do_ping_tests() 118 + { 119 + local mode=$1 120 + 121 + # ping6 test: echo request should be redirect back to itself, not others 122 + ip netns exec ns1 ip neigh add 2001:db8::2 dev veth0 lladdr 00:00:00:00:00:02 123 + 124 + ip netns exec ns1 tcpdump -i veth0 -nn -l -e &> ns1-1_${mode}.log & 125 + ip netns exec ns2 tcpdump -i veth0 -nn -l -e &> ns1-2_${mode}.log & 126 + ip netns exec ns3 tcpdump -i veth0 -nn -l -e &> ns1-3_${mode}.log & 127 + sleep 0.5 128 + # ARP test 129 + ip netns exec ns1 ping 192.0.2.254 -i 0.1 -c 4 &> /dev/null 130 + # IPv4 test 131 + ip netns exec ns1 ping 192.0.2.253 -i 0.1 -c 4 &> /dev/null 132 + # IPv6 test 133 + ip netns exec ns1 ping6 2001:db8::2 -i 0.1 -c 2 &> /dev/null 134 + sleep 0.5 135 + pkill -9 tcpdump 136 + 137 + # All netns should receive the redirect arp requests 138 + [ $(grep -c "who-has 192.0.2.254" ns1-1_${mode}.log) -gt 4 ] && \ 139 + test_pass "$mode arp(F_BROADCAST) ns1-1" || \ 140 + test_fail "$mode arp(F_BROADCAST) ns1-1" 141 + [ $(grep -c "who-has 192.0.2.254" ns1-2_${mode}.log) -le 4 ] && \ 142 + test_pass "$mode arp(F_BROADCAST) ns1-2" || \ 143 + test_fail "$mode arp(F_BROADCAST) ns1-2" 144 + [ $(grep -c "who-has 192.0.2.254" ns1-3_${mode}.log) -le 4 ] && \ 145 + test_pass "$mode arp(F_BROADCAST) ns1-3" || \ 146 + test_fail "$mode arp(F_BROADCAST) ns1-3" 147 + 148 + # ns1 should not receive the redirect echo request, others should 149 + [ $(grep -c "ICMP echo request" ns1-1_${mode}.log) -eq 4 ] && \ 150 + test_pass "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-1" || \ 151 + test_fail "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-1" 152 + [ $(grep -c "ICMP echo request" ns1-2_${mode}.log) -eq 4 ] && \ 153 + test_pass "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-2" || \ 154 + test_fail "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-2" 155 + [ $(grep -c "ICMP echo request" ns1-3_${mode}.log) -eq 4 ] && \ 156 + test_pass "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-3" || \ 157 + test_fail "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-3" 158 + 159 + # ns1 should receive the echo request, ns2 should not 160 + [ $(grep -c "ICMP6, echo request" ns1-1_${mode}.log) -eq 4 ] && \ 161 + test_pass "$mode IPv6 (no flags) ns1-1" || \ 162 + test_fail "$mode IPv6 (no flags) ns1-1" 163 + [ $(grep -c "ICMP6, echo request" ns1-2_${mode}.log) -eq 0 ] && \ 164 + test_pass "$mode IPv6 (no flags) ns1-2" || \ 165 + test_fail "$mode IPv6 (no flags) ns1-2" 166 + } 167 + 168 + do_tests() 169 + { 170 + local mode=$1 171 + local drv_p 172 + 173 + case ${mode} in 174 + xdpdrv) drv_p="-N";; 175 + xdpegress) drv_p="-X";; 176 + xdpgeneric) drv_p="-S";; 177 + esac 178 + 179 + ./xdp_redirect_multi $drv_p $IFACES &> xdp_redirect_${mode}.log & 180 + xdp_pid=$! 181 + sleep 1 182 + 183 + if [ "$mode" = "xdpegress" ]; then 184 + do_egress_tests $mode 185 + else 186 + do_ping_tests $mode 187 + fi 188 + 189 + kill $xdp_pid 190 + } 191 + 192 + trap clean_up 0 2 3 6 9 193 + 194 + check_env 195 + rm -f xdp_redirect_*.log ns*.log mac_ns*.log 196 + 197 + for mode in ${DRV_MODE}; do 198 + setup_ns $mode 199 + do_tests $mode 200 + clean_up 201 + done 202 + 203 + echo "Summary: PASS $PASS, FAIL $FAIL" 204 + [ $FAIL -eq 0 ] && exit 0 || exit 1
+226
tools/testing/selftests/bpf/xdp_redirect_multi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/bpf.h> 3 + #include <linux/if_link.h> 4 + #include <assert.h> 5 + #include <errno.h> 6 + #include <signal.h> 7 + #include <stdio.h> 8 + #include <stdlib.h> 9 + #include <string.h> 10 + #include <net/if.h> 11 + #include <unistd.h> 12 + #include <libgen.h> 13 + #include <sys/resource.h> 14 + #include <sys/ioctl.h> 15 + #include <sys/types.h> 16 + #include <sys/socket.h> 17 + #include <netinet/in.h> 18 + 19 + #include "bpf_util.h" 20 + #include <bpf/bpf.h> 21 + #include <bpf/libbpf.h> 22 + 23 + #define MAX_IFACE_NUM 32 24 + #define MAX_INDEX_NUM 1024 25 + 26 + static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; 27 + static int ifaces[MAX_IFACE_NUM] = {}; 28 + 29 + static void int_exit(int sig) 30 + { 31 + __u32 prog_id = 0; 32 + int i; 33 + 34 + for (i = 0; ifaces[i] > 0; i++) { 35 + if (bpf_get_link_xdp_id(ifaces[i], &prog_id, xdp_flags)) { 36 + printf("bpf_get_link_xdp_id failed\n"); 37 + exit(1); 38 + } 39 + if (prog_id) 40 + bpf_set_link_xdp_fd(ifaces[i], -1, xdp_flags); 41 + } 42 + 43 + exit(0); 44 + } 45 + 46 + static int get_mac_addr(unsigned int ifindex, void *mac_addr) 47 + { 48 + char ifname[IF_NAMESIZE]; 49 + struct ifreq ifr; 50 + int fd, ret = -1; 51 + 52 + fd = socket(AF_INET, SOCK_DGRAM, 0); 53 + if (fd < 0) 54 + return ret; 55 + 56 + if (!if_indextoname(ifindex, ifname)) 57 + goto err_out; 58 + 59 + strcpy(ifr.ifr_name, ifname); 60 + 61 + if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) 62 + goto err_out; 63 + 64 + memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char)); 65 + ret = 0; 66 + 67 + err_out: 68 + close(fd); 69 + return ret; 70 + } 71 + 72 + static void usage(const char *prog) 73 + { 74 + fprintf(stderr, 75 + "usage: %s [OPTS] <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n" 76 + "OPTS:\n" 77 + " -S use skb-mode\n" 78 + " -N enforce native mode\n" 79 + " -F force loading prog\n" 80 + " -X load xdp program on egress\n", 81 + prog); 82 + } 83 + 84 + int main(int argc, char **argv) 85 + { 86 + int prog_fd, group_all, mac_map; 87 + struct bpf_program *ingress_prog, *egress_prog; 88 + struct bpf_prog_load_attr prog_load_attr = { 89 + .prog_type = BPF_PROG_TYPE_UNSPEC, 90 + }; 91 + int i, ret, opt, egress_prog_fd = 0; 92 + struct bpf_devmap_val devmap_val; 93 + bool attach_egress_prog = false; 94 + unsigned char mac_addr[6]; 95 + char ifname[IF_NAMESIZE]; 96 + struct bpf_object *obj; 97 + unsigned int ifindex; 98 + char filename[256]; 99 + 100 + while ((opt = getopt(argc, argv, "SNFX")) != -1) { 101 + switch (opt) { 102 + case 'S': 103 + xdp_flags |= XDP_FLAGS_SKB_MODE; 104 + break; 105 + case 'N': 106 + /* default, set below */ 107 + break; 108 + case 'F': 109 + xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; 110 + break; 111 + case 'X': 112 + attach_egress_prog = true; 113 + break; 114 + default: 115 + usage(basename(argv[0])); 116 + return 1; 117 + } 118 + } 119 + 120 + if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) { 121 + xdp_flags |= XDP_FLAGS_DRV_MODE; 122 + } else if (attach_egress_prog) { 123 + printf("Load xdp program on egress with SKB mode not supported yet\n"); 124 + goto err_out; 125 + } 126 + 127 + if (optind == argc) { 128 + printf("usage: %s <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n", argv[0]); 129 + goto err_out; 130 + } 131 + 132 + printf("Get interfaces"); 133 + for (i = 0; i < MAX_IFACE_NUM && argv[optind + i]; i++) { 134 + ifaces[i] = if_nametoindex(argv[optind + i]); 135 + if (!ifaces[i]) 136 + ifaces[i] = strtoul(argv[optind + i], NULL, 0); 137 + if (!if_indextoname(ifaces[i], ifname)) { 138 + perror("Invalid interface name or i"); 139 + goto err_out; 140 + } 141 + if (ifaces[i] > MAX_INDEX_NUM) { 142 + printf("Interface index to large\n"); 143 + goto err_out; 144 + } 145 + printf(" %d", ifaces[i]); 146 + } 147 + printf("\n"); 148 + 149 + snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 150 + prog_load_attr.file = filename; 151 + 152 + if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) 153 + goto err_out; 154 + 155 + if (attach_egress_prog) 156 + group_all = bpf_object__find_map_fd_by_name(obj, "map_egress"); 157 + else 158 + group_all = bpf_object__find_map_fd_by_name(obj, "map_all"); 159 + mac_map = bpf_object__find_map_fd_by_name(obj, "mac_map"); 160 + 161 + if (group_all < 0 || mac_map < 0) { 162 + printf("bpf_object__find_map_fd_by_name failed\n"); 163 + goto err_out; 164 + } 165 + 166 + if (attach_egress_prog) { 167 + /* Find ingress/egress prog for 2nd xdp prog */ 168 + ingress_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_all_prog"); 169 + egress_prog = bpf_object__find_program_by_name(obj, "xdp_devmap_prog"); 170 + if (!ingress_prog || !egress_prog) { 171 + printf("finding ingress/egress_prog in obj file failed\n"); 172 + goto err_out; 173 + } 174 + prog_fd = bpf_program__fd(ingress_prog); 175 + egress_prog_fd = bpf_program__fd(egress_prog); 176 + if (prog_fd < 0 || egress_prog_fd < 0) { 177 + printf("find egress_prog fd failed\n"); 178 + goto err_out; 179 + } 180 + } 181 + 182 + signal(SIGINT, int_exit); 183 + signal(SIGTERM, int_exit); 184 + 185 + /* Init forward multicast groups and exclude group */ 186 + for (i = 0; ifaces[i] > 0; i++) { 187 + ifindex = ifaces[i]; 188 + 189 + if (attach_egress_prog) { 190 + ret = get_mac_addr(ifindex, mac_addr); 191 + if (ret < 0) { 192 + printf("get interface %d mac failed\n", ifindex); 193 + goto err_out; 194 + } 195 + ret = bpf_map_update_elem(mac_map, &ifindex, mac_addr, 0); 196 + if (ret) { 197 + perror("bpf_update_elem mac_map failed\n"); 198 + goto err_out; 199 + } 200 + } 201 + 202 + /* Add all the interfaces to group all */ 203 + devmap_val.ifindex = ifindex; 204 + devmap_val.bpf_prog.fd = egress_prog_fd; 205 + ret = bpf_map_update_elem(group_all, &ifindex, &devmap_val, 0); 206 + if (ret) { 207 + perror("bpf_map_update_elem"); 208 + goto err_out; 209 + } 210 + 211 + /* bind prog_fd to each interface */ 212 + ret = bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags); 213 + if (ret) { 214 + printf("Set xdp fd failed on %d\n", ifindex); 215 + goto err_out; 216 + } 217 + } 218 + 219 + /* sleep some time for testing */ 220 + sleep(999); 221 + 222 + return 0; 223 + 224 + err_out: 225 + return 1; 226 + }