1// SPDX-License-Identifier: GPL-2.0 2#include <stdio.h> 3#include <stdlib.h> 4#include <string.h> 5#include <linux/perf_event.h> 6#include <linux/bpf.h> 7#include <net/if.h> 8#include <errno.h> 9#include <assert.h> 10#include <sys/sysinfo.h> 11#include <sys/ioctl.h> 12#include <signal.h> 13#include <bpf/libbpf.h> 14#include <bpf/bpf.h> 15#include <sys/resource.h> 16#include <libgen.h> 17#include <linux/if_link.h> 18 19#include "perf-sys.h" 20 21static int if_idx; 22static char *if_name; 23static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; 24static __u32 prog_id; 25static struct perf_buffer *pb = NULL; 26 27static int do_attach(int idx, int fd, const char *name) 28{ 29 struct bpf_prog_info info = {}; 30 __u32 info_len = sizeof(info); 31 int err; 32 33 err = bpf_set_link_xdp_fd(idx, fd, xdp_flags); 34 if (err < 0) { 35 printf("ERROR: failed to attach program to %s\n", name); 36 return err; 37 } 38 39 err = bpf_obj_get_info_by_fd(fd, &info, &info_len); 40 if (err) { 41 printf("can't get prog info - %s\n", strerror(errno)); 42 return err; 43 } 44 prog_id = info.id; 45 46 return err; 47} 48 49static int do_detach(int idx, const char *name) 50{ 51 __u32 curr_prog_id = 0; 52 int err = 0; 53 54 err = bpf_get_link_xdp_id(idx, &curr_prog_id, xdp_flags); 55 if (err) { 56 printf("bpf_get_link_xdp_id failed\n"); 57 return err; 58 } 59 if (prog_id == curr_prog_id) { 60 err = bpf_set_link_xdp_fd(idx, -1, xdp_flags); 61 if (err < 0) 62 printf("ERROR: failed to detach prog from %s\n", name); 63 } else if (!curr_prog_id) { 64 printf("couldn't find a prog id on a %s\n", name); 65 } else { 66 printf("program on interface changed, not removing\n"); 67 } 68 69 return err; 70} 71 72#define SAMPLE_SIZE 64 73 74static void print_bpf_output(void *ctx, int cpu, void *data, __u32 size) 75{ 76 struct { 77 __u16 cookie; 78 __u16 pkt_len; 79 __u8 pkt_data[SAMPLE_SIZE]; 80 } __packed *e = data; 81 int i; 82 83 if (e->cookie != 0xdead) { 84 printf("BUG cookie %x sized %d\n", e->cookie, size); 85 return; 86 } 87 88 printf("Pkt len: %-5d bytes. Ethernet hdr: ", e->pkt_len); 89 for (i = 0; i < 14 && i < e->pkt_len; i++) 90 printf("%02x ", e->pkt_data[i]); 91 printf("\n"); 92} 93 94static void sig_handler(int signo) 95{ 96 do_detach(if_idx, if_name); 97 perf_buffer__free(pb); 98 exit(0); 99} 100 101static void usage(const char *prog) 102{ 103 fprintf(stderr, 104 "%s: %s [OPTS] <ifname|ifindex>\n\n" 105 "OPTS:\n" 106 " -F force loading prog\n", 107 __func__, prog); 108} 109 110int main(int argc, char **argv) 111{ 112 struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; 113 struct bpf_prog_load_attr prog_load_attr = { 114 .prog_type = BPF_PROG_TYPE_XDP, 115 }; 116 struct perf_buffer_opts pb_opts = {}; 117 const char *optstr = "FS"; 118 int prog_fd, map_fd, opt; 119 struct bpf_object *obj; 120 struct bpf_map *map; 121 char filename[256]; 122 int ret, err; 123 124 while ((opt = getopt(argc, argv, optstr)) != -1) { 125 switch (opt) { 126 case 'F': 127 xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; 128 break; 129 case 'S': 130 xdp_flags |= XDP_FLAGS_SKB_MODE; 131 break; 132 default: 133 usage(basename(argv[0])); 134 return 1; 135 } 136 } 137 138 if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) 139 xdp_flags |= XDP_FLAGS_DRV_MODE; 140 141 if (optind == argc) { 142 usage(basename(argv[0])); 143 return 1; 144 } 145 146 if (setrlimit(RLIMIT_MEMLOCK, &r)) { 147 perror("setrlimit(RLIMIT_MEMLOCK)"); 148 return 1; 149 } 150 151 snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 152 prog_load_attr.file = filename; 153 154 if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) 155 return 1; 156 157 if (!prog_fd) { 158 printf("bpf_prog_load_xattr: %s\n", strerror(errno)); 159 return 1; 160 } 161 162 map = bpf_map__next(NULL, obj); 163 if (!map) { 164 printf("finding a map in obj file failed\n"); 165 return 1; 166 } 167 map_fd = bpf_map__fd(map); 168 169 if_idx = if_nametoindex(argv[optind]); 170 if (!if_idx) 171 if_idx = strtoul(argv[optind], NULL, 0); 172 173 if (!if_idx) { 174 fprintf(stderr, "Invalid ifname\n"); 175 return 1; 176 } 177 if_name = argv[optind]; 178 err = do_attach(if_idx, prog_fd, if_name); 179 if (err) 180 return err; 181 182 if (signal(SIGINT, sig_handler) || 183 signal(SIGHUP, sig_handler) || 184 signal(SIGTERM, sig_handler)) { 185 perror("signal"); 186 return 1; 187 } 188 189 pb_opts.sample_cb = print_bpf_output; 190 pb = perf_buffer__new(map_fd, 8, &pb_opts); 191 err = libbpf_get_error(pb); 192 if (err) { 193 perror("perf_buffer setup failed"); 194 return 1; 195 } 196 197 while ((ret = perf_buffer__poll(pb, 1000)) >= 0) { 198 } 199 200 kill(0, SIGINT); 201 return ret; 202} 203