162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci * Copyright (c) 2018 Facebook
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or
562306a36Sopenharmony_ci * modify it under the terms of version 2 of the GNU General Public
662306a36Sopenharmony_ci * License as published by the Free Software Foundation.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/bpf.h>
962306a36Sopenharmony_ci#include <linux/if_link.h>
1062306a36Sopenharmony_ci#include <assert.h>
1162306a36Sopenharmony_ci#include <errno.h>
1262306a36Sopenharmony_ci#include <signal.h>
1362306a36Sopenharmony_ci#include <stdio.h>
1462306a36Sopenharmony_ci#include <stdlib.h>
1562306a36Sopenharmony_ci#include <string.h>
1662306a36Sopenharmony_ci#include <net/if.h>
1762306a36Sopenharmony_ci#include <arpa/inet.h>
1862306a36Sopenharmony_ci#include <netinet/ether.h>
1962306a36Sopenharmony_ci#include <unistd.h>
2062306a36Sopenharmony_ci#include <time.h>
2162306a36Sopenharmony_ci#include <bpf/bpf.h>
2262306a36Sopenharmony_ci#include <bpf/libbpf.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define STATS_INTERVAL_S 2U
2562306a36Sopenharmony_ci#define MAX_PCKT_SIZE 600
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int ifindex = -1;
2862306a36Sopenharmony_cistatic __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
2962306a36Sopenharmony_cistatic __u32 prog_id;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void int_exit(int sig)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	__u32 curr_prog_id = 0;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	if (ifindex > -1) {
3662306a36Sopenharmony_ci		if (bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id)) {
3762306a36Sopenharmony_ci			printf("bpf_xdp_query_id failed\n");
3862306a36Sopenharmony_ci			exit(1);
3962306a36Sopenharmony_ci		}
4062306a36Sopenharmony_ci		if (prog_id == curr_prog_id)
4162306a36Sopenharmony_ci			bpf_xdp_detach(ifindex, xdp_flags, NULL);
4262306a36Sopenharmony_ci		else if (!curr_prog_id)
4362306a36Sopenharmony_ci			printf("couldn't find a prog id on a given iface\n");
4462306a36Sopenharmony_ci		else
4562306a36Sopenharmony_ci			printf("program on interface changed, not removing\n");
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci	exit(0);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* simple "icmp packet too big sent" counter
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_cistatic void poll_stats(unsigned int map_fd, unsigned int kill_after_s)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	time_t started_at = time(NULL);
5562306a36Sopenharmony_ci	__u64 value = 0;
5662306a36Sopenharmony_ci	int key = 0;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	while (!kill_after_s || time(NULL) - started_at <= kill_after_s) {
6062306a36Sopenharmony_ci		sleep(STATS_INTERVAL_S);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci		assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci		printf("icmp \"packet too big\" sent: %10llu pkts\n", value);
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic void usage(const char *cmd)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	printf("Start a XDP prog which send ICMP \"packet too big\" \n"
7162306a36Sopenharmony_ci		"messages if ingress packet is bigger then MAX_SIZE bytes\n");
7262306a36Sopenharmony_ci	printf("Usage: %s [...]\n", cmd);
7362306a36Sopenharmony_ci	printf("    -i <ifname|ifindex> Interface\n");
7462306a36Sopenharmony_ci	printf("    -T <stop-after-X-seconds> Default: 0 (forever)\n");
7562306a36Sopenharmony_ci	printf("    -P <MAX_PCKT_SIZE> Default: %u\n", MAX_PCKT_SIZE);
7662306a36Sopenharmony_ci	printf("    -S use skb-mode\n");
7762306a36Sopenharmony_ci	printf("    -N enforce native mode\n");
7862306a36Sopenharmony_ci	printf("    -F force loading prog\n");
7962306a36Sopenharmony_ci	printf("    -h Display this help\n");
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ciint main(int argc, char **argv)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	unsigned char opt_flags[256] = {};
8562306a36Sopenharmony_ci	const char *optstr = "i:T:P:SNFh";
8662306a36Sopenharmony_ci	struct bpf_prog_info info = {};
8762306a36Sopenharmony_ci	__u32 info_len = sizeof(info);
8862306a36Sopenharmony_ci	unsigned int kill_after_s = 0;
8962306a36Sopenharmony_ci	int i, prog_fd, map_fd, opt;
9062306a36Sopenharmony_ci	struct bpf_program *prog;
9162306a36Sopenharmony_ci	struct bpf_object *obj;
9262306a36Sopenharmony_ci	__u32 max_pckt_size = 0;
9362306a36Sopenharmony_ci	__u32 key = 0;
9462306a36Sopenharmony_ci	char filename[256];
9562306a36Sopenharmony_ci	int err;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	for (i = 0; i < strlen(optstr); i++)
9862306a36Sopenharmony_ci		if (optstr[i] != 'h' && 'a' <= optstr[i] && optstr[i] <= 'z')
9962306a36Sopenharmony_ci			opt_flags[(unsigned char)optstr[i]] = 1;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	while ((opt = getopt(argc, argv, optstr)) != -1) {
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		switch (opt) {
10462306a36Sopenharmony_ci		case 'i':
10562306a36Sopenharmony_ci			ifindex = if_nametoindex(optarg);
10662306a36Sopenharmony_ci			if (!ifindex)
10762306a36Sopenharmony_ci				ifindex = atoi(optarg);
10862306a36Sopenharmony_ci			break;
10962306a36Sopenharmony_ci		case 'T':
11062306a36Sopenharmony_ci			kill_after_s = atoi(optarg);
11162306a36Sopenharmony_ci			break;
11262306a36Sopenharmony_ci		case 'P':
11362306a36Sopenharmony_ci			max_pckt_size = atoi(optarg);
11462306a36Sopenharmony_ci			break;
11562306a36Sopenharmony_ci		case 'S':
11662306a36Sopenharmony_ci			xdp_flags |= XDP_FLAGS_SKB_MODE;
11762306a36Sopenharmony_ci			break;
11862306a36Sopenharmony_ci		case 'N':
11962306a36Sopenharmony_ci			/* default, set below */
12062306a36Sopenharmony_ci			break;
12162306a36Sopenharmony_ci		case 'F':
12262306a36Sopenharmony_ci			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
12362306a36Sopenharmony_ci			break;
12462306a36Sopenharmony_ci		default:
12562306a36Sopenharmony_ci			usage(argv[0]);
12662306a36Sopenharmony_ci			return 1;
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci		opt_flags[opt] = 0;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
13262306a36Sopenharmony_ci		xdp_flags |= XDP_FLAGS_DRV_MODE;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	for (i = 0; i < strlen(optstr); i++) {
13562306a36Sopenharmony_ci		if (opt_flags[(unsigned int)optstr[i]]) {
13662306a36Sopenharmony_ci			fprintf(stderr, "Missing argument -%c\n", optstr[i]);
13762306a36Sopenharmony_ci			usage(argv[0]);
13862306a36Sopenharmony_ci			return 1;
13962306a36Sopenharmony_ci		}
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (!ifindex) {
14362306a36Sopenharmony_ci		fprintf(stderr, "Invalid ifname\n");
14462306a36Sopenharmony_ci		return 1;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	obj = bpf_object__open_file(filename, NULL);
15062306a36Sopenharmony_ci	if (libbpf_get_error(obj))
15162306a36Sopenharmony_ci		return 1;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	prog = bpf_object__next_program(obj, NULL);
15462306a36Sopenharmony_ci	bpf_program__set_type(prog, BPF_PROG_TYPE_XDP);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	err = bpf_object__load(obj);
15762306a36Sopenharmony_ci	if (err)
15862306a36Sopenharmony_ci		return 1;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	prog_fd = bpf_program__fd(prog);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* static global var 'max_pcktsz' is accessible from .data section */
16362306a36Sopenharmony_ci	if (max_pckt_size) {
16462306a36Sopenharmony_ci		map_fd = bpf_object__find_map_fd_by_name(obj, "xdp_adju.data");
16562306a36Sopenharmony_ci		if (map_fd < 0) {
16662306a36Sopenharmony_ci			printf("finding a max_pcktsz map in obj file failed\n");
16762306a36Sopenharmony_ci			return 1;
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci		bpf_map_update_elem(map_fd, &key, &max_pckt_size, BPF_ANY);
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* fetch icmpcnt map */
17362306a36Sopenharmony_ci	map_fd = bpf_object__find_map_fd_by_name(obj, "icmpcnt");
17462306a36Sopenharmony_ci	if (map_fd < 0) {
17562306a36Sopenharmony_ci		printf("finding a icmpcnt map in obj file failed\n");
17662306a36Sopenharmony_ci		return 1;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	signal(SIGINT, int_exit);
18062306a36Sopenharmony_ci	signal(SIGTERM, int_exit);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) {
18362306a36Sopenharmony_ci		printf("link set xdp fd failed\n");
18462306a36Sopenharmony_ci		return 1;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	err = bpf_prog_get_info_by_fd(prog_fd, &info, &info_len);
18862306a36Sopenharmony_ci	if (err) {
18962306a36Sopenharmony_ci		printf("can't get prog info - %s\n", strerror(errno));
19062306a36Sopenharmony_ci		return 1;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci	prog_id = info.id;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	poll_stats(map_fd, kill_after_s);
19562306a36Sopenharmony_ci	int_exit(0);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci}
199