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

sample/bpf: Add xdp_redirect_map_multi for redirect_map broadcast test

This is a sample for xdp redirect broadcast. In the sample we could forward
all packets between given interfaces. There is also an option -X that could
enable 2nd xdp_prog on egress interface.

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-4-liuhangbin@gmail.com

authored by

Hangbin Liu and committed by
Daniel Borkmann
e48cfe4b e624d4ed

+393
+3
samples/bpf/Makefile
··· 41 41 tprogs-y += per_socket_stats_example 42 42 tprogs-y += xdp_redirect 43 43 tprogs-y += xdp_redirect_map 44 + tprogs-y += xdp_redirect_map_multi 44 45 tprogs-y += xdp_redirect_cpu 45 46 tprogs-y += xdp_monitor 46 47 tprogs-y += xdp_rxq_info ··· 100 99 per_socket_stats_example-objs := cookie_uid_helper_example.o 101 100 xdp_redirect-objs := xdp_redirect_user.o 102 101 xdp_redirect_map-objs := xdp_redirect_map_user.o 102 + xdp_redirect_map_multi-objs := xdp_redirect_map_multi_user.o 103 103 xdp_redirect_cpu-objs := xdp_redirect_cpu_user.o 104 104 xdp_monitor-objs := xdp_monitor_user.o 105 105 xdp_rxq_info-objs := xdp_rxq_info_user.o ··· 162 160 always-y += tcp_dumpstats_kern.o 163 161 always-y += xdp_redirect_kern.o 164 162 always-y += xdp_redirect_map_kern.o 163 + always-y += xdp_redirect_map_multi_kern.o 165 164 always-y += xdp_redirect_cpu_kern.o 166 165 always-y += xdp_monitor_kern.o 167 166 always-y += xdp_rxq_info_kern.o
+88
samples/bpf/xdp_redirect_map_multi_kern.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #define KBUILD_MODNAME "foo" 3 + #include <uapi/linux/bpf.h> 4 + #include <linux/in.h> 5 + #include <linux/if_ether.h> 6 + #include <linux/ip.h> 7 + #include <linux/ipv6.h> 8 + #include <bpf/bpf_helpers.h> 9 + 10 + struct { 11 + __uint(type, BPF_MAP_TYPE_DEVMAP_HASH); 12 + __uint(key_size, sizeof(int)); 13 + __uint(value_size, sizeof(int)); 14 + __uint(max_entries, 32); 15 + } forward_map_general SEC(".maps"); 16 + 17 + struct { 18 + __uint(type, BPF_MAP_TYPE_DEVMAP_HASH); 19 + __uint(key_size, sizeof(int)); 20 + __uint(value_size, sizeof(struct bpf_devmap_val)); 21 + __uint(max_entries, 32); 22 + } forward_map_native SEC(".maps"); 23 + 24 + struct { 25 + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 26 + __type(key, u32); 27 + __type(value, long); 28 + __uint(max_entries, 1); 29 + } rxcnt SEC(".maps"); 30 + 31 + /* map to store egress interfaces mac addresses, set the 32 + * max_entries to 1 and extend it in user sapce prog. 33 + */ 34 + struct { 35 + __uint(type, BPF_MAP_TYPE_ARRAY); 36 + __type(key, u32); 37 + __type(value, __be64); 38 + __uint(max_entries, 1); 39 + } mac_map SEC(".maps"); 40 + 41 + static int xdp_redirect_map(struct xdp_md *ctx, void *forward_map) 42 + { 43 + long *value; 44 + u32 key = 0; 45 + 46 + /* count packet in global counter */ 47 + value = bpf_map_lookup_elem(&rxcnt, &key); 48 + if (value) 49 + *value += 1; 50 + 51 + return bpf_redirect_map(forward_map, key, 52 + BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS); 53 + } 54 + 55 + SEC("xdp_redirect_general") 56 + int xdp_redirect_map_general(struct xdp_md *ctx) 57 + { 58 + return xdp_redirect_map(ctx, &forward_map_general); 59 + } 60 + 61 + SEC("xdp_redirect_native") 62 + int xdp_redirect_map_native(struct xdp_md *ctx) 63 + { 64 + return xdp_redirect_map(ctx, &forward_map_native); 65 + } 66 + 67 + SEC("xdp_devmap/map_prog") 68 + int xdp_devmap_prog(struct xdp_md *ctx) 69 + { 70 + void *data_end = (void *)(long)ctx->data_end; 71 + void *data = (void *)(long)ctx->data; 72 + u32 key = ctx->egress_ifindex; 73 + struct ethhdr *eth = data; 74 + __be64 *mac; 75 + u64 nh_off; 76 + 77 + nh_off = sizeof(*eth); 78 + if (data + nh_off > data_end) 79 + return XDP_DROP; 80 + 81 + mac = bpf_map_lookup_elem(&mac_map, &key); 82 + if (mac) 83 + __builtin_memcpy(eth->h_source, mac, ETH_ALEN); 84 + 85 + return XDP_PASS; 86 + } 87 + 88 + char _license[] SEC("license") = "GPL";
+302
samples/bpf/xdp_redirect_map_multi_user.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 + 25 + static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; 26 + static int ifaces[MAX_IFACE_NUM] = {}; 27 + static int rxcnt_map_fd; 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 void poll_stats(int interval) 47 + { 48 + unsigned int nr_cpus = bpf_num_possible_cpus(); 49 + __u64 values[nr_cpus], prev[nr_cpus]; 50 + 51 + memset(prev, 0, sizeof(prev)); 52 + 53 + while (1) { 54 + __u64 sum = 0; 55 + __u32 key = 0; 56 + int i; 57 + 58 + sleep(interval); 59 + assert(bpf_map_lookup_elem(rxcnt_map_fd, &key, values) == 0); 60 + for (i = 0; i < nr_cpus; i++) 61 + sum += (values[i] - prev[i]); 62 + if (sum) 63 + printf("Forwarding %10llu pkt/s\n", sum / interval); 64 + memcpy(prev, values, sizeof(values)); 65 + } 66 + } 67 + 68 + static int get_mac_addr(unsigned int ifindex, void *mac_addr) 69 + { 70 + char ifname[IF_NAMESIZE]; 71 + struct ifreq ifr; 72 + int fd, ret = -1; 73 + 74 + fd = socket(AF_INET, SOCK_DGRAM, 0); 75 + if (fd < 0) 76 + return ret; 77 + 78 + if (!if_indextoname(ifindex, ifname)) 79 + goto err_out; 80 + 81 + strcpy(ifr.ifr_name, ifname); 82 + 83 + if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) 84 + goto err_out; 85 + 86 + memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char)); 87 + ret = 0; 88 + 89 + err_out: 90 + close(fd); 91 + return ret; 92 + } 93 + 94 + static int update_mac_map(struct bpf_object *obj) 95 + { 96 + int i, ret = -1, mac_map_fd; 97 + unsigned char mac_addr[6]; 98 + unsigned int ifindex; 99 + 100 + mac_map_fd = bpf_object__find_map_fd_by_name(obj, "mac_map"); 101 + if (mac_map_fd < 0) { 102 + printf("find mac map fd failed\n"); 103 + return ret; 104 + } 105 + 106 + for (i = 0; ifaces[i] > 0; i++) { 107 + ifindex = ifaces[i]; 108 + 109 + ret = get_mac_addr(ifindex, mac_addr); 110 + if (ret < 0) { 111 + printf("get interface %d mac failed\n", ifindex); 112 + return ret; 113 + } 114 + 115 + ret = bpf_map_update_elem(mac_map_fd, &ifindex, mac_addr, 0); 116 + if (ret) { 117 + perror("bpf_update_elem mac_map_fd"); 118 + return ret; 119 + } 120 + } 121 + 122 + return 0; 123 + } 124 + 125 + static void usage(const char *prog) 126 + { 127 + fprintf(stderr, 128 + "usage: %s [OPTS] <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n" 129 + "OPTS:\n" 130 + " -S use skb-mode\n" 131 + " -N enforce native mode\n" 132 + " -F force loading prog\n" 133 + " -X load xdp program on egress\n", 134 + prog); 135 + } 136 + 137 + int main(int argc, char **argv) 138 + { 139 + int i, ret, opt, forward_map_fd, max_ifindex = 0; 140 + struct bpf_program *ingress_prog, *egress_prog; 141 + int ingress_prog_fd, egress_prog_fd = 0; 142 + struct bpf_devmap_val devmap_val; 143 + bool attach_egress_prog = false; 144 + char ifname[IF_NAMESIZE]; 145 + struct bpf_map *mac_map; 146 + struct bpf_object *obj; 147 + unsigned int ifindex; 148 + char filename[256]; 149 + 150 + while ((opt = getopt(argc, argv, "SNFX")) != -1) { 151 + switch (opt) { 152 + case 'S': 153 + xdp_flags |= XDP_FLAGS_SKB_MODE; 154 + break; 155 + case 'N': 156 + /* default, set below */ 157 + break; 158 + case 'F': 159 + xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; 160 + break; 161 + case 'X': 162 + attach_egress_prog = true; 163 + break; 164 + default: 165 + usage(basename(argv[0])); 166 + return 1; 167 + } 168 + } 169 + 170 + if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) { 171 + xdp_flags |= XDP_FLAGS_DRV_MODE; 172 + } else if (attach_egress_prog) { 173 + printf("Load xdp program on egress with SKB mode not supported yet\n"); 174 + return 1; 175 + } 176 + 177 + if (optind == argc) { 178 + printf("usage: %s <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n", argv[0]); 179 + return 1; 180 + } 181 + 182 + printf("Get interfaces"); 183 + for (i = 0; i < MAX_IFACE_NUM && argv[optind + i]; i++) { 184 + ifaces[i] = if_nametoindex(argv[optind + i]); 185 + if (!ifaces[i]) 186 + ifaces[i] = strtoul(argv[optind + i], NULL, 0); 187 + if (!if_indextoname(ifaces[i], ifname)) { 188 + perror("Invalid interface name or i"); 189 + return 1; 190 + } 191 + 192 + /* Find the largest index number */ 193 + if (ifaces[i] > max_ifindex) 194 + max_ifindex = ifaces[i]; 195 + 196 + printf(" %d", ifaces[i]); 197 + } 198 + printf("\n"); 199 + 200 + snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 201 + 202 + obj = bpf_object__open(filename); 203 + if (libbpf_get_error(obj)) { 204 + printf("ERROR: opening BPF object file failed\n"); 205 + obj = NULL; 206 + goto err_out; 207 + } 208 + 209 + /* Reset the map size to max ifindex + 1 */ 210 + if (attach_egress_prog) { 211 + mac_map = bpf_object__find_map_by_name(obj, "mac_map"); 212 + ret = bpf_map__resize(mac_map, max_ifindex + 1); 213 + if (ret < 0) { 214 + printf("ERROR: reset mac map size failed\n"); 215 + goto err_out; 216 + } 217 + } 218 + 219 + /* load BPF program */ 220 + if (bpf_object__load(obj)) { 221 + printf("ERROR: loading BPF object file failed\n"); 222 + goto err_out; 223 + } 224 + 225 + if (xdp_flags & XDP_FLAGS_SKB_MODE) { 226 + ingress_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_general"); 227 + forward_map_fd = bpf_object__find_map_fd_by_name(obj, "forward_map_general"); 228 + } else { 229 + ingress_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_native"); 230 + forward_map_fd = bpf_object__find_map_fd_by_name(obj, "forward_map_native"); 231 + } 232 + if (!ingress_prog || forward_map_fd < 0) { 233 + printf("finding ingress_prog/forward_map in obj file failed\n"); 234 + goto err_out; 235 + } 236 + 237 + ingress_prog_fd = bpf_program__fd(ingress_prog); 238 + if (ingress_prog_fd < 0) { 239 + printf("find ingress_prog fd failed\n"); 240 + goto err_out; 241 + } 242 + 243 + rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt"); 244 + if (rxcnt_map_fd < 0) { 245 + printf("bpf_object__find_map_fd_by_name failed\n"); 246 + goto err_out; 247 + } 248 + 249 + if (attach_egress_prog) { 250 + /* Update mac_map with all egress interfaces' mac addr */ 251 + if (update_mac_map(obj) < 0) { 252 + printf("Error: update mac map failed"); 253 + goto err_out; 254 + } 255 + 256 + /* Find egress prog fd */ 257 + egress_prog = bpf_object__find_program_by_name(obj, "xdp_devmap_prog"); 258 + if (!egress_prog) { 259 + printf("finding egress_prog in obj file failed\n"); 260 + goto err_out; 261 + } 262 + egress_prog_fd = bpf_program__fd(egress_prog); 263 + if (egress_prog_fd < 0) { 264 + printf("find egress_prog fd failed\n"); 265 + goto err_out; 266 + } 267 + } 268 + 269 + /* Remove attached program when program is interrupted or killed */ 270 + signal(SIGINT, int_exit); 271 + signal(SIGTERM, int_exit); 272 + 273 + /* Init forward multicast groups */ 274 + for (i = 0; ifaces[i] > 0; i++) { 275 + ifindex = ifaces[i]; 276 + 277 + /* bind prog_fd to each interface */ 278 + ret = bpf_set_link_xdp_fd(ifindex, ingress_prog_fd, xdp_flags); 279 + if (ret) { 280 + printf("Set xdp fd failed on %d\n", ifindex); 281 + goto err_out; 282 + } 283 + 284 + /* Add all the interfaces to forward group and attach 285 + * egress devmap programe if exist 286 + */ 287 + devmap_val.ifindex = ifindex; 288 + devmap_val.bpf_prog.fd = egress_prog_fd; 289 + ret = bpf_map_update_elem(forward_map_fd, &ifindex, &devmap_val, 0); 290 + if (ret) { 291 + perror("bpf_map_update_elem forward_map"); 292 + goto err_out; 293 + } 294 + } 295 + 296 + poll_stats(2); 297 + 298 + return 0; 299 + 300 + err_out: 301 + return 1; 302 + }