18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* Copyright (c) 2016 PLUMgrid
38c2ecf20Sopenharmony_ci */
48c2ecf20Sopenharmony_ci#include <linux/bpf.h>
58c2ecf20Sopenharmony_ci#include <linux/if_link.h>
68c2ecf20Sopenharmony_ci#include <assert.h>
78c2ecf20Sopenharmony_ci#include <errno.h>
88c2ecf20Sopenharmony_ci#include <signal.h>
98c2ecf20Sopenharmony_ci#include <stdio.h>
108c2ecf20Sopenharmony_ci#include <stdlib.h>
118c2ecf20Sopenharmony_ci#include <string.h>
128c2ecf20Sopenharmony_ci#include <unistd.h>
138c2ecf20Sopenharmony_ci#include <libgen.h>
148c2ecf20Sopenharmony_ci#include <sys/resource.h>
158c2ecf20Sopenharmony_ci#include <net/if.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "bpf_util.h"
188c2ecf20Sopenharmony_ci#include <bpf/bpf.h>
198c2ecf20Sopenharmony_ci#include <bpf/libbpf.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic int ifindex;
228c2ecf20Sopenharmony_cistatic __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
238c2ecf20Sopenharmony_cistatic __u32 prog_id;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic void int_exit(int sig)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	__u32 curr_prog_id = 0;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) {
308c2ecf20Sopenharmony_ci		printf("bpf_get_link_xdp_id failed\n");
318c2ecf20Sopenharmony_ci		exit(1);
328c2ecf20Sopenharmony_ci	}
338c2ecf20Sopenharmony_ci	if (prog_id == curr_prog_id)
348c2ecf20Sopenharmony_ci		bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
358c2ecf20Sopenharmony_ci	else if (!curr_prog_id)
368c2ecf20Sopenharmony_ci		printf("couldn't find a prog id on a given interface\n");
378c2ecf20Sopenharmony_ci	else
388c2ecf20Sopenharmony_ci		printf("program on interface changed, not removing\n");
398c2ecf20Sopenharmony_ci	exit(0);
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* simple per-protocol drop counter
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_cistatic void poll_stats(int map_fd, int interval)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	unsigned int nr_cpus = bpf_num_possible_cpus();
478c2ecf20Sopenharmony_ci	__u64 values[nr_cpus], prev[UINT8_MAX] = { 0 };
488c2ecf20Sopenharmony_ci	int i;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	while (1) {
518c2ecf20Sopenharmony_ci		__u32 key = UINT32_MAX;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci		sleep(interval);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci		while (bpf_map_get_next_key(map_fd, &key, &key) != -1) {
568c2ecf20Sopenharmony_ci			__u64 sum = 0;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci			assert(bpf_map_lookup_elem(map_fd, &key, values) == 0);
598c2ecf20Sopenharmony_ci			for (i = 0; i < nr_cpus; i++)
608c2ecf20Sopenharmony_ci				sum += values[i];
618c2ecf20Sopenharmony_ci			if (sum > prev[key])
628c2ecf20Sopenharmony_ci				printf("proto %u: %10llu pkt/s\n",
638c2ecf20Sopenharmony_ci				       key, (sum - prev[key]) / interval);
648c2ecf20Sopenharmony_ci			prev[key] = sum;
658c2ecf20Sopenharmony_ci		}
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic void usage(const char *prog)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	fprintf(stderr,
728c2ecf20Sopenharmony_ci		"usage: %s [OPTS] IFACE\n\n"
738c2ecf20Sopenharmony_ci		"OPTS:\n"
748c2ecf20Sopenharmony_ci		"    -S    use skb-mode\n"
758c2ecf20Sopenharmony_ci		"    -N    enforce native mode\n"
768c2ecf20Sopenharmony_ci		"    -F    force loading prog\n",
778c2ecf20Sopenharmony_ci		prog);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ciint main(int argc, char **argv)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
838c2ecf20Sopenharmony_ci	struct bpf_prog_load_attr prog_load_attr = {
848c2ecf20Sopenharmony_ci		.prog_type	= BPF_PROG_TYPE_XDP,
858c2ecf20Sopenharmony_ci	};
868c2ecf20Sopenharmony_ci	struct bpf_prog_info info = {};
878c2ecf20Sopenharmony_ci	__u32 info_len = sizeof(info);
888c2ecf20Sopenharmony_ci	const char *optstr = "FSN";
898c2ecf20Sopenharmony_ci	int prog_fd, map_fd, opt;
908c2ecf20Sopenharmony_ci	struct bpf_object *obj;
918c2ecf20Sopenharmony_ci	struct bpf_map *map;
928c2ecf20Sopenharmony_ci	char filename[256];
938c2ecf20Sopenharmony_ci	int err;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	while ((opt = getopt(argc, argv, optstr)) != -1) {
968c2ecf20Sopenharmony_ci		switch (opt) {
978c2ecf20Sopenharmony_ci		case 'S':
988c2ecf20Sopenharmony_ci			xdp_flags |= XDP_FLAGS_SKB_MODE;
998c2ecf20Sopenharmony_ci			break;
1008c2ecf20Sopenharmony_ci		case 'N':
1018c2ecf20Sopenharmony_ci			/* default, set below */
1028c2ecf20Sopenharmony_ci			break;
1038c2ecf20Sopenharmony_ci		case 'F':
1048c2ecf20Sopenharmony_ci			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
1058c2ecf20Sopenharmony_ci			break;
1068c2ecf20Sopenharmony_ci		default:
1078c2ecf20Sopenharmony_ci			usage(basename(argv[0]));
1088c2ecf20Sopenharmony_ci			return 1;
1098c2ecf20Sopenharmony_ci		}
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
1138c2ecf20Sopenharmony_ci		xdp_flags |= XDP_FLAGS_DRV_MODE;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (optind == argc) {
1168c2ecf20Sopenharmony_ci		usage(basename(argv[0]));
1178c2ecf20Sopenharmony_ci		return 1;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (setrlimit(RLIMIT_MEMLOCK, &r)) {
1218c2ecf20Sopenharmony_ci		perror("setrlimit(RLIMIT_MEMLOCK)");
1228c2ecf20Sopenharmony_ci		return 1;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	ifindex = if_nametoindex(argv[optind]);
1268c2ecf20Sopenharmony_ci	if (!ifindex) {
1278c2ecf20Sopenharmony_ci		perror("if_nametoindex");
1288c2ecf20Sopenharmony_ci		return 1;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
1328c2ecf20Sopenharmony_ci	prog_load_attr.file = filename;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
1358c2ecf20Sopenharmony_ci		return 1;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	map = bpf_map__next(NULL, obj);
1388c2ecf20Sopenharmony_ci	if (!map) {
1398c2ecf20Sopenharmony_ci		printf("finding a map in obj file failed\n");
1408c2ecf20Sopenharmony_ci		return 1;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci	map_fd = bpf_map__fd(map);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (!prog_fd) {
1458c2ecf20Sopenharmony_ci		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
1468c2ecf20Sopenharmony_ci		return 1;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	signal(SIGINT, int_exit);
1508c2ecf20Sopenharmony_ci	signal(SIGTERM, int_exit);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) {
1538c2ecf20Sopenharmony_ci		printf("link set xdp fd failed\n");
1548c2ecf20Sopenharmony_ci		return 1;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
1588c2ecf20Sopenharmony_ci	if (err) {
1598c2ecf20Sopenharmony_ci		printf("can't get prog info - %s\n", strerror(errno));
1608c2ecf20Sopenharmony_ci		return err;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci	prog_id = info.id;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	poll_stats(map_fd, 2);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return 0;
1678c2ecf20Sopenharmony_ci}
168