18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <stdio.h>
38c2ecf20Sopenharmony_ci#include <stdlib.h>
48c2ecf20Sopenharmony_ci#include <string.h>
58c2ecf20Sopenharmony_ci#include <linux/perf_event.h>
68c2ecf20Sopenharmony_ci#include <linux/bpf.h>
78c2ecf20Sopenharmony_ci#include <net/if.h>
88c2ecf20Sopenharmony_ci#include <errno.h>
98c2ecf20Sopenharmony_ci#include <assert.h>
108c2ecf20Sopenharmony_ci#include <sys/sysinfo.h>
118c2ecf20Sopenharmony_ci#include <sys/ioctl.h>
128c2ecf20Sopenharmony_ci#include <signal.h>
138c2ecf20Sopenharmony_ci#include <bpf/libbpf.h>
148c2ecf20Sopenharmony_ci#include <bpf/bpf.h>
158c2ecf20Sopenharmony_ci#include <sys/resource.h>
168c2ecf20Sopenharmony_ci#include <libgen.h>
178c2ecf20Sopenharmony_ci#include <linux/if_link.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "perf-sys.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic int if_idx;
228c2ecf20Sopenharmony_cistatic char *if_name;
238c2ecf20Sopenharmony_cistatic __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
248c2ecf20Sopenharmony_cistatic __u32 prog_id;
258c2ecf20Sopenharmony_cistatic struct perf_buffer *pb = NULL;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int do_attach(int idx, int fd, const char *name)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct bpf_prog_info info = {};
308c2ecf20Sopenharmony_ci	__u32 info_len = sizeof(info);
318c2ecf20Sopenharmony_ci	int err;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	err = bpf_set_link_xdp_fd(idx, fd, xdp_flags);
348c2ecf20Sopenharmony_ci	if (err < 0) {
358c2ecf20Sopenharmony_ci		printf("ERROR: failed to attach program to %s\n", name);
368c2ecf20Sopenharmony_ci		return err;
378c2ecf20Sopenharmony_ci	}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	err = bpf_obj_get_info_by_fd(fd, &info, &info_len);
408c2ecf20Sopenharmony_ci	if (err) {
418c2ecf20Sopenharmony_ci		printf("can't get prog info - %s\n", strerror(errno));
428c2ecf20Sopenharmony_ci		return err;
438c2ecf20Sopenharmony_ci	}
448c2ecf20Sopenharmony_ci	prog_id = info.id;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	return err;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic int do_detach(int idx, const char *name)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	__u32 curr_prog_id = 0;
528c2ecf20Sopenharmony_ci	int err = 0;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	err = bpf_get_link_xdp_id(idx, &curr_prog_id, xdp_flags);
558c2ecf20Sopenharmony_ci	if (err) {
568c2ecf20Sopenharmony_ci		printf("bpf_get_link_xdp_id failed\n");
578c2ecf20Sopenharmony_ci		return err;
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci	if (prog_id == curr_prog_id) {
608c2ecf20Sopenharmony_ci		err = bpf_set_link_xdp_fd(idx, -1, xdp_flags);
618c2ecf20Sopenharmony_ci		if (err < 0)
628c2ecf20Sopenharmony_ci			printf("ERROR: failed to detach prog from %s\n", name);
638c2ecf20Sopenharmony_ci	} else if (!curr_prog_id) {
648c2ecf20Sopenharmony_ci		printf("couldn't find a prog id on a %s\n", name);
658c2ecf20Sopenharmony_ci	} else {
668c2ecf20Sopenharmony_ci		printf("program on interface changed, not removing\n");
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	return err;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#define SAMPLE_SIZE 64
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic void print_bpf_output(void *ctx, int cpu, void *data, __u32 size)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct {
778c2ecf20Sopenharmony_ci		__u16 cookie;
788c2ecf20Sopenharmony_ci		__u16 pkt_len;
798c2ecf20Sopenharmony_ci		__u8  pkt_data[SAMPLE_SIZE];
808c2ecf20Sopenharmony_ci	} __packed *e = data;
818c2ecf20Sopenharmony_ci	int i;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (e->cookie != 0xdead) {
848c2ecf20Sopenharmony_ci		printf("BUG cookie %x sized %d\n", e->cookie, size);
858c2ecf20Sopenharmony_ci		return;
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	printf("Pkt len: %-5d bytes. Ethernet hdr: ", e->pkt_len);
898c2ecf20Sopenharmony_ci	for (i = 0; i < 14 && i < e->pkt_len; i++)
908c2ecf20Sopenharmony_ci		printf("%02x ", e->pkt_data[i]);
918c2ecf20Sopenharmony_ci	printf("\n");
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic void sig_handler(int signo)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	do_detach(if_idx, if_name);
978c2ecf20Sopenharmony_ci	perf_buffer__free(pb);
988c2ecf20Sopenharmony_ci	exit(0);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic void usage(const char *prog)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	fprintf(stderr,
1048c2ecf20Sopenharmony_ci		"%s: %s [OPTS] <ifname|ifindex>\n\n"
1058c2ecf20Sopenharmony_ci		"OPTS:\n"
1068c2ecf20Sopenharmony_ci		"    -F    force loading prog\n",
1078c2ecf20Sopenharmony_ci		__func__, prog);
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ciint main(int argc, char **argv)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
1138c2ecf20Sopenharmony_ci	struct bpf_prog_load_attr prog_load_attr = {
1148c2ecf20Sopenharmony_ci		.prog_type	= BPF_PROG_TYPE_XDP,
1158c2ecf20Sopenharmony_ci	};
1168c2ecf20Sopenharmony_ci	struct perf_buffer_opts pb_opts = {};
1178c2ecf20Sopenharmony_ci	const char *optstr = "FS";
1188c2ecf20Sopenharmony_ci	int prog_fd, map_fd, opt;
1198c2ecf20Sopenharmony_ci	struct bpf_object *obj;
1208c2ecf20Sopenharmony_ci	struct bpf_map *map;
1218c2ecf20Sopenharmony_ci	char filename[256];
1228c2ecf20Sopenharmony_ci	int ret, err;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	while ((opt = getopt(argc, argv, optstr)) != -1) {
1258c2ecf20Sopenharmony_ci		switch (opt) {
1268c2ecf20Sopenharmony_ci		case 'F':
1278c2ecf20Sopenharmony_ci			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
1288c2ecf20Sopenharmony_ci			break;
1298c2ecf20Sopenharmony_ci		case 'S':
1308c2ecf20Sopenharmony_ci			xdp_flags |= XDP_FLAGS_SKB_MODE;
1318c2ecf20Sopenharmony_ci			break;
1328c2ecf20Sopenharmony_ci		default:
1338c2ecf20Sopenharmony_ci			usage(basename(argv[0]));
1348c2ecf20Sopenharmony_ci			return 1;
1358c2ecf20Sopenharmony_ci		}
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
1398c2ecf20Sopenharmony_ci		xdp_flags |= XDP_FLAGS_DRV_MODE;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (optind == argc) {
1428c2ecf20Sopenharmony_ci		usage(basename(argv[0]));
1438c2ecf20Sopenharmony_ci		return 1;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (setrlimit(RLIMIT_MEMLOCK, &r)) {
1478c2ecf20Sopenharmony_ci		perror("setrlimit(RLIMIT_MEMLOCK)");
1488c2ecf20Sopenharmony_ci		return 1;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
1528c2ecf20Sopenharmony_ci	prog_load_attr.file = filename;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
1558c2ecf20Sopenharmony_ci		return 1;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (!prog_fd) {
1588c2ecf20Sopenharmony_ci		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
1598c2ecf20Sopenharmony_ci		return 1;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	map = bpf_map__next(NULL, obj);
1638c2ecf20Sopenharmony_ci	if (!map) {
1648c2ecf20Sopenharmony_ci		printf("finding a map in obj file failed\n");
1658c2ecf20Sopenharmony_ci		return 1;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci	map_fd = bpf_map__fd(map);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if_idx = if_nametoindex(argv[optind]);
1708c2ecf20Sopenharmony_ci	if (!if_idx)
1718c2ecf20Sopenharmony_ci		if_idx = strtoul(argv[optind], NULL, 0);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (!if_idx) {
1748c2ecf20Sopenharmony_ci		fprintf(stderr, "Invalid ifname\n");
1758c2ecf20Sopenharmony_ci		return 1;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci	if_name = argv[optind];
1788c2ecf20Sopenharmony_ci	err = do_attach(if_idx, prog_fd, if_name);
1798c2ecf20Sopenharmony_ci	if (err)
1808c2ecf20Sopenharmony_ci		return err;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (signal(SIGINT, sig_handler) ||
1838c2ecf20Sopenharmony_ci	    signal(SIGHUP, sig_handler) ||
1848c2ecf20Sopenharmony_ci	    signal(SIGTERM, sig_handler)) {
1858c2ecf20Sopenharmony_ci		perror("signal");
1868c2ecf20Sopenharmony_ci		return 1;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	pb_opts.sample_cb = print_bpf_output;
1908c2ecf20Sopenharmony_ci	pb = perf_buffer__new(map_fd, 8, &pb_opts);
1918c2ecf20Sopenharmony_ci	err = libbpf_get_error(pb);
1928c2ecf20Sopenharmony_ci	if (err) {
1938c2ecf20Sopenharmony_ci		perror("perf_buffer setup failed");
1948c2ecf20Sopenharmony_ci		return 1;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	while ((ret = perf_buffer__poll(pb, 1000)) >= 0) {
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	kill(0, SIGINT);
2018c2ecf20Sopenharmony_ci	return ret;
2028c2ecf20Sopenharmony_ci}
203