xref: /openbmc/linux/samples/bpf/xdp_tx_iptunnel_user.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
125763b3cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
212d8bb64SMartin KaFai Lau /* Copyright (c) 2016 Facebook
312d8bb64SMartin KaFai Lau  */
412d8bb64SMartin KaFai Lau #include <linux/bpf.h>
53993f2cbSDavid Ahern #include <linux/if_link.h>
612d8bb64SMartin KaFai Lau #include <assert.h>
712d8bb64SMartin KaFai Lau #include <errno.h>
812d8bb64SMartin KaFai Lau #include <signal.h>
912d8bb64SMartin KaFai Lau #include <stdio.h>
1012d8bb64SMartin KaFai Lau #include <stdlib.h>
1112d8bb64SMartin KaFai Lau #include <string.h>
129e859e8fSDaniel T. Lee #include <net/if.h>
1312d8bb64SMartin KaFai Lau #include <arpa/inet.h>
1412d8bb64SMartin KaFai Lau #include <netinet/ether.h>
1512d8bb64SMartin KaFai Lau #include <unistd.h>
1612d8bb64SMartin KaFai Lau #include <time.h>
177cf245a3SToke Høiland-Jørgensen #include <bpf/libbpf.h>
182bf3e2efSJakub Kicinski #include <bpf/bpf.h>
1912d8bb64SMartin KaFai Lau #include "bpf_util.h"
2012d8bb64SMartin KaFai Lau #include "xdp_tx_iptunnel_common.h"
2112d8bb64SMartin KaFai Lau 
2212d8bb64SMartin KaFai Lau #define STATS_INTERVAL_S 2U
2312d8bb64SMartin KaFai Lau 
2412d8bb64SMartin KaFai Lau static int ifindex = -1;
25743e568cSMaciej Fijalkowski static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
26bbaf6029SMaciej Fijalkowski static int rxcnt_map_fd;
273b7a8ec2SMaciej Fijalkowski static __u32 prog_id;
2812d8bb64SMartin KaFai Lau 
int_exit(int sig)2912d8bb64SMartin KaFai Lau static void int_exit(int sig)
3012d8bb64SMartin KaFai Lau {
313b7a8ec2SMaciej Fijalkowski 	__u32 curr_prog_id = 0;
323b7a8ec2SMaciej Fijalkowski 
333b7a8ec2SMaciej Fijalkowski 	if (ifindex > -1) {
34d4e34bfcSAndrii Nakryiko 		if (bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id)) {
35d4e34bfcSAndrii Nakryiko 			printf("bpf_xdp_query_id failed\n");
363b7a8ec2SMaciej Fijalkowski 			exit(1);
373b7a8ec2SMaciej Fijalkowski 		}
383b7a8ec2SMaciej Fijalkowski 		if (prog_id == curr_prog_id)
39d4e34bfcSAndrii Nakryiko 			bpf_xdp_detach(ifindex, xdp_flags, NULL);
403b7a8ec2SMaciej Fijalkowski 		else if (!curr_prog_id)
413b7a8ec2SMaciej Fijalkowski 			printf("couldn't find a prog id on a given iface\n");
423b7a8ec2SMaciej Fijalkowski 		else
433b7a8ec2SMaciej Fijalkowski 			printf("program on interface changed, not removing\n");
443b7a8ec2SMaciej Fijalkowski 	}
4512d8bb64SMartin KaFai Lau 	exit(0);
4612d8bb64SMartin KaFai Lau }
4712d8bb64SMartin KaFai Lau 
4812d8bb64SMartin KaFai Lau /* simple per-protocol drop counter
4912d8bb64SMartin KaFai Lau  */
poll_stats(unsigned int kill_after_s)5012d8bb64SMartin KaFai Lau static void poll_stats(unsigned int kill_after_s)
5112d8bb64SMartin KaFai Lau {
5212d8bb64SMartin KaFai Lau 	const unsigned int nr_protos = 256;
5312d8bb64SMartin KaFai Lau 	unsigned int nr_cpus = bpf_num_possible_cpus();
5412d8bb64SMartin KaFai Lau 	time_t started_at = time(NULL);
5512d8bb64SMartin KaFai Lau 	__u64 values[nr_cpus], prev[nr_protos][nr_cpus];
5612d8bb64SMartin KaFai Lau 	__u32 proto;
5712d8bb64SMartin KaFai Lau 	int i;
5812d8bb64SMartin KaFai Lau 
5912d8bb64SMartin KaFai Lau 	memset(prev, 0, sizeof(prev));
6012d8bb64SMartin KaFai Lau 
6112d8bb64SMartin KaFai Lau 	while (!kill_after_s || time(NULL) - started_at <= kill_after_s) {
6212d8bb64SMartin KaFai Lau 		sleep(STATS_INTERVAL_S);
6312d8bb64SMartin KaFai Lau 
6412d8bb64SMartin KaFai Lau 		for (proto = 0; proto < nr_protos; proto++) {
6512d8bb64SMartin KaFai Lau 			__u64 sum = 0;
6612d8bb64SMartin KaFai Lau 
67bbaf6029SMaciej Fijalkowski 			assert(bpf_map_lookup_elem(rxcnt_map_fd, &proto,
68bbaf6029SMaciej Fijalkowski 						   values) == 0);
6912d8bb64SMartin KaFai Lau 			for (i = 0; i < nr_cpus; i++)
7012d8bb64SMartin KaFai Lau 				sum += (values[i] - prev[proto][i]);
7112d8bb64SMartin KaFai Lau 
7212d8bb64SMartin KaFai Lau 			if (sum)
7312d8bb64SMartin KaFai Lau 				printf("proto %u: sum:%10llu pkts, rate:%10llu pkts/s\n",
7412d8bb64SMartin KaFai Lau 				       proto, sum, sum / STATS_INTERVAL_S);
7512d8bb64SMartin KaFai Lau 			memcpy(prev[proto], values, sizeof(values));
7612d8bb64SMartin KaFai Lau 		}
7712d8bb64SMartin KaFai Lau 	}
7812d8bb64SMartin KaFai Lau }
7912d8bb64SMartin KaFai Lau 
usage(const char * cmd)8012d8bb64SMartin KaFai Lau static void usage(const char *cmd)
8112d8bb64SMartin KaFai Lau {
8212d8bb64SMartin KaFai Lau 	printf("Start a XDP prog which encapsulates incoming packets\n"
8312d8bb64SMartin KaFai Lau 	       "in an IPv4/v6 header and XDP_TX it out.  The dst <VIP:PORT>\n"
8412d8bb64SMartin KaFai Lau 	       "is used to select packets to encapsulate\n\n");
8512d8bb64SMartin KaFai Lau 	printf("Usage: %s [...]\n", cmd);
869e859e8fSDaniel T. Lee 	printf("    -i <ifname|ifindex> Interface\n");
8712d8bb64SMartin KaFai Lau 	printf("    -a <vip-service-address> IPv4 or IPv6\n");
8812d8bb64SMartin KaFai Lau 	printf("    -p <vip-service-port> A port range (e.g. 433-444) is also allowed\n");
8912d8bb64SMartin KaFai Lau 	printf("    -s <source-ip> Used in the IPTunnel header\n");
9012d8bb64SMartin KaFai Lau 	printf("    -d <dest-ip> Used in the IPTunnel header\n");
9112d8bb64SMartin KaFai Lau 	printf("    -m <dest-MAC> Used in sending the IP Tunneled pkt\n");
9212d8bb64SMartin KaFai Lau 	printf("    -T <stop-after-X-seconds> Default: 0 (forever)\n");
9312d8bb64SMartin KaFai Lau 	printf("    -P <IP-Protocol> Default is TCP\n");
940489df9aSDaniel Borkmann 	printf("    -S use skb-mode\n");
950489df9aSDaniel Borkmann 	printf("    -N enforce native mode\n");
96743e568cSMaciej Fijalkowski 	printf("    -F Force loading the XDP prog\n");
9712d8bb64SMartin KaFai Lau 	printf("    -h Display this help\n");
9812d8bb64SMartin KaFai Lau }
9912d8bb64SMartin KaFai Lau 
parse_ipstr(const char * ipstr,unsigned int * addr)10012d8bb64SMartin KaFai Lau static int parse_ipstr(const char *ipstr, unsigned int *addr)
10112d8bb64SMartin KaFai Lau {
10212d8bb64SMartin KaFai Lau 	if (inet_pton(AF_INET6, ipstr, addr) == 1) {
10312d8bb64SMartin KaFai Lau 		return AF_INET6;
10412d8bb64SMartin KaFai Lau 	} else if (inet_pton(AF_INET, ipstr, addr) == 1) {
10512d8bb64SMartin KaFai Lau 		addr[1] = addr[2] = addr[3] = 0;
10612d8bb64SMartin KaFai Lau 		return AF_INET;
10712d8bb64SMartin KaFai Lau 	}
10812d8bb64SMartin KaFai Lau 
10912d8bb64SMartin KaFai Lau 	fprintf(stderr, "%s is an invalid IP\n", ipstr);
11012d8bb64SMartin KaFai Lau 	return AF_UNSPEC;
11112d8bb64SMartin KaFai Lau }
11212d8bb64SMartin KaFai Lau 
parse_ports(const char * port_str,int * min_port,int * max_port)11312d8bb64SMartin KaFai Lau static int parse_ports(const char *port_str, int *min_port, int *max_port)
11412d8bb64SMartin KaFai Lau {
11512d8bb64SMartin KaFai Lau 	char *end;
11612d8bb64SMartin KaFai Lau 	long tmp_min_port;
11712d8bb64SMartin KaFai Lau 	long tmp_max_port;
11812d8bb64SMartin KaFai Lau 
11912d8bb64SMartin KaFai Lau 	tmp_min_port = strtol(optarg, &end, 10);
12012d8bb64SMartin KaFai Lau 	if (tmp_min_port < 1 || tmp_min_port > 65535) {
12112d8bb64SMartin KaFai Lau 		fprintf(stderr, "Invalid port(s):%s\n", optarg);
12212d8bb64SMartin KaFai Lau 		return 1;
12312d8bb64SMartin KaFai Lau 	}
12412d8bb64SMartin KaFai Lau 
12512d8bb64SMartin KaFai Lau 	if (*end == '-') {
12612d8bb64SMartin KaFai Lau 		end++;
12712d8bb64SMartin KaFai Lau 		tmp_max_port = strtol(end, NULL, 10);
12812d8bb64SMartin KaFai Lau 		if (tmp_max_port < 1 || tmp_max_port > 65535) {
12912d8bb64SMartin KaFai Lau 			fprintf(stderr, "Invalid port(s):%s\n", optarg);
13012d8bb64SMartin KaFai Lau 			return 1;
13112d8bb64SMartin KaFai Lau 		}
13212d8bb64SMartin KaFai Lau 	} else {
13312d8bb64SMartin KaFai Lau 		tmp_max_port = tmp_min_port;
13412d8bb64SMartin KaFai Lau 	}
13512d8bb64SMartin KaFai Lau 
13612d8bb64SMartin KaFai Lau 	if (tmp_min_port > tmp_max_port) {
13712d8bb64SMartin KaFai Lau 		fprintf(stderr, "Invalid port(s):%s\n", optarg);
13812d8bb64SMartin KaFai Lau 		return 1;
13912d8bb64SMartin KaFai Lau 	}
14012d8bb64SMartin KaFai Lau 
14112d8bb64SMartin KaFai Lau 	if (tmp_max_port - tmp_min_port + 1 > MAX_IPTNL_ENTRIES) {
14212d8bb64SMartin KaFai Lau 		fprintf(stderr, "Port range (%s) is larger than %u\n",
14312d8bb64SMartin KaFai Lau 			port_str, MAX_IPTNL_ENTRIES);
14412d8bb64SMartin KaFai Lau 		return 1;
14512d8bb64SMartin KaFai Lau 	}
14612d8bb64SMartin KaFai Lau 	*min_port = tmp_min_port;
14712d8bb64SMartin KaFai Lau 	*max_port = tmp_max_port;
14812d8bb64SMartin KaFai Lau 
14912d8bb64SMartin KaFai Lau 	return 0;
15012d8bb64SMartin KaFai Lau }
15112d8bb64SMartin KaFai Lau 
main(int argc,char ** argv)15212d8bb64SMartin KaFai Lau int main(int argc, char **argv)
15312d8bb64SMartin KaFai Lau {
154bbaf6029SMaciej Fijalkowski 	int min_port = 0, max_port = 0, vip2tnl_map_fd;
155743e568cSMaciej Fijalkowski 	const char *optstr = "i:a:p:s:d:m:T:P:FSNh";
15612d8bb64SMartin KaFai Lau 	unsigned char opt_flags[256] = {};
1573b7a8ec2SMaciej Fijalkowski 	struct bpf_prog_info info = {};
1583b7a8ec2SMaciej Fijalkowski 	__u32 info_len = sizeof(info);
15912d8bb64SMartin KaFai Lau 	unsigned int kill_after_s = 0;
16012d8bb64SMartin KaFai Lau 	struct iptnl_info tnl = {};
1611e4edb6dSAndrii Nakryiko 	struct bpf_program *prog;
162bbaf6029SMaciej Fijalkowski 	struct bpf_object *obj;
16312d8bb64SMartin KaFai Lau 	struct vip vip = {};
16412d8bb64SMartin KaFai Lau 	char filename[256];
165bbaf6029SMaciej Fijalkowski 	int opt, prog_fd;
1663b7a8ec2SMaciej Fijalkowski 	int i, err;
16712d8bb64SMartin KaFai Lau 
16812d8bb64SMartin KaFai Lau 	tnl.family = AF_UNSPEC;
16912d8bb64SMartin KaFai Lau 	vip.protocol = IPPROTO_TCP;
17012d8bb64SMartin KaFai Lau 
17112d8bb64SMartin KaFai Lau 	for (i = 0; i < strlen(optstr); i++)
17212d8bb64SMartin KaFai Lau 		if (optstr[i] != 'h' && 'a' <= optstr[i] && optstr[i] <= 'z')
17312d8bb64SMartin KaFai Lau 			opt_flags[(unsigned char)optstr[i]] = 1;
17412d8bb64SMartin KaFai Lau 
17512d8bb64SMartin KaFai Lau 	while ((opt = getopt(argc, argv, optstr)) != -1) {
17612d8bb64SMartin KaFai Lau 		unsigned short family;
17712d8bb64SMartin KaFai Lau 		unsigned int *v6;
17812d8bb64SMartin KaFai Lau 
17912d8bb64SMartin KaFai Lau 		switch (opt) {
18012d8bb64SMartin KaFai Lau 		case 'i':
1819e859e8fSDaniel T. Lee 			ifindex = if_nametoindex(optarg);
1829e859e8fSDaniel T. Lee 			if (!ifindex)
18312d8bb64SMartin KaFai Lau 				ifindex = atoi(optarg);
18412d8bb64SMartin KaFai Lau 			break;
18512d8bb64SMartin KaFai Lau 		case 'a':
18612d8bb64SMartin KaFai Lau 			vip.family = parse_ipstr(optarg, vip.daddr.v6);
18712d8bb64SMartin KaFai Lau 			if (vip.family == AF_UNSPEC)
18812d8bb64SMartin KaFai Lau 				return 1;
18912d8bb64SMartin KaFai Lau 			break;
19012d8bb64SMartin KaFai Lau 		case 'p':
19112d8bb64SMartin KaFai Lau 			if (parse_ports(optarg, &min_port, &max_port))
19212d8bb64SMartin KaFai Lau 				return 1;
19312d8bb64SMartin KaFai Lau 			break;
19412d8bb64SMartin KaFai Lau 		case 'P':
19512d8bb64SMartin KaFai Lau 			vip.protocol = atoi(optarg);
19612d8bb64SMartin KaFai Lau 			break;
19712d8bb64SMartin KaFai Lau 		case 's':
19812d8bb64SMartin KaFai Lau 		case 'd':
19912d8bb64SMartin KaFai Lau 			if (opt == 's')
20012d8bb64SMartin KaFai Lau 				v6 = tnl.saddr.v6;
20112d8bb64SMartin KaFai Lau 			else
20212d8bb64SMartin KaFai Lau 				v6 = tnl.daddr.v6;
20312d8bb64SMartin KaFai Lau 
20412d8bb64SMartin KaFai Lau 			family = parse_ipstr(optarg, v6);
20512d8bb64SMartin KaFai Lau 			if (family == AF_UNSPEC)
20612d8bb64SMartin KaFai Lau 				return 1;
20712d8bb64SMartin KaFai Lau 			if (tnl.family == AF_UNSPEC) {
20812d8bb64SMartin KaFai Lau 				tnl.family = family;
20912d8bb64SMartin KaFai Lau 			} else if (tnl.family != family) {
21012d8bb64SMartin KaFai Lau 				fprintf(stderr,
21112d8bb64SMartin KaFai Lau 					"The IP version of the src and dst addresses used in the IP encapsulation does not match\n");
21212d8bb64SMartin KaFai Lau 				return 1;
21312d8bb64SMartin KaFai Lau 			}
21412d8bb64SMartin KaFai Lau 			break;
21512d8bb64SMartin KaFai Lau 		case 'm':
21612d8bb64SMartin KaFai Lau 			if (!ether_aton_r(optarg,
21712d8bb64SMartin KaFai Lau 					  (struct ether_addr *)tnl.dmac)) {
21812d8bb64SMartin KaFai Lau 				fprintf(stderr, "Invalid mac address:%s\n",
21912d8bb64SMartin KaFai Lau 					optarg);
22012d8bb64SMartin KaFai Lau 				return 1;
22112d8bb64SMartin KaFai Lau 			}
22212d8bb64SMartin KaFai Lau 			break;
22312d8bb64SMartin KaFai Lau 		case 'T':
22412d8bb64SMartin KaFai Lau 			kill_after_s = atoi(optarg);
22512d8bb64SMartin KaFai Lau 			break;
2263993f2cbSDavid Ahern 		case 'S':
2276387d011SJesper Dangaard Brouer 			xdp_flags |= XDP_FLAGS_SKB_MODE;
2283993f2cbSDavid Ahern 			break;
2290489df9aSDaniel Borkmann 		case 'N':
230d50ecc46SToke Høiland-Jørgensen 			/* default, set below */
2310489df9aSDaniel Borkmann 			break;
232743e568cSMaciej Fijalkowski 		case 'F':
233743e568cSMaciej Fijalkowski 			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
234743e568cSMaciej Fijalkowski 			break;
23512d8bb64SMartin KaFai Lau 		default:
23612d8bb64SMartin KaFai Lau 			usage(argv[0]);
23712d8bb64SMartin KaFai Lau 			return 1;
23812d8bb64SMartin KaFai Lau 		}
23912d8bb64SMartin KaFai Lau 		opt_flags[opt] = 0;
24012d8bb64SMartin KaFai Lau 	}
24112d8bb64SMartin KaFai Lau 
242d50ecc46SToke Høiland-Jørgensen 	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
243d50ecc46SToke Høiland-Jørgensen 		xdp_flags |= XDP_FLAGS_DRV_MODE;
244d50ecc46SToke Høiland-Jørgensen 
24512d8bb64SMartin KaFai Lau 	for (i = 0; i < strlen(optstr); i++) {
24612d8bb64SMartin KaFai Lau 		if (opt_flags[(unsigned int)optstr[i]]) {
24712d8bb64SMartin KaFai Lau 			fprintf(stderr, "Missing argument -%c\n", optstr[i]);
24812d8bb64SMartin KaFai Lau 			usage(argv[0]);
24912d8bb64SMartin KaFai Lau 			return 1;
25012d8bb64SMartin KaFai Lau 		}
25112d8bb64SMartin KaFai Lau 	}
25212d8bb64SMartin KaFai Lau 
2539e859e8fSDaniel T. Lee 	if (!ifindex) {
2549e859e8fSDaniel T. Lee 		fprintf(stderr, "Invalid ifname\n");
2559e859e8fSDaniel T. Lee 		return 1;
2569e859e8fSDaniel T. Lee 	}
2579e859e8fSDaniel T. Lee 
25812d8bb64SMartin KaFai Lau 	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
25912d8bb64SMartin KaFai Lau 
2601e4edb6dSAndrii Nakryiko 	obj = bpf_object__open_file(filename, NULL);
2611e4edb6dSAndrii Nakryiko 	if (libbpf_get_error(obj))
262bbaf6029SMaciej Fijalkowski 		return 1;
263bbaf6029SMaciej Fijalkowski 
2641e4edb6dSAndrii Nakryiko 	prog = bpf_object__next_program(obj, NULL);
2651e4edb6dSAndrii Nakryiko 	bpf_program__set_type(prog, BPF_PROG_TYPE_XDP);
2661e4edb6dSAndrii Nakryiko 
2671e4edb6dSAndrii Nakryiko 	err = bpf_object__load(obj);
2681e4edb6dSAndrii Nakryiko 	if (err) {
2691e4edb6dSAndrii Nakryiko 		printf("bpf_object__load(): %s\n", strerror(errno));
27012d8bb64SMartin KaFai Lau 		return 1;
27112d8bb64SMartin KaFai Lau 	}
2721e4edb6dSAndrii Nakryiko 	prog_fd = bpf_program__fd(prog);
27312d8bb64SMartin KaFai Lau 
274bbaf6029SMaciej Fijalkowski 	rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
275bbaf6029SMaciej Fijalkowski 	vip2tnl_map_fd = bpf_object__find_map_fd_by_name(obj, "vip2tnl");
276bbaf6029SMaciej Fijalkowski 	if (vip2tnl_map_fd < 0 || rxcnt_map_fd < 0) {
277bbaf6029SMaciej Fijalkowski 		printf("bpf_object__find_map_fd_by_name failed\n");
27812d8bb64SMartin KaFai Lau 		return 1;
27912d8bb64SMartin KaFai Lau 	}
28012d8bb64SMartin KaFai Lau 
28112d8bb64SMartin KaFai Lau 	signal(SIGINT, int_exit);
282ad990dbeSAndy Gospodarek 	signal(SIGTERM, int_exit);
28312d8bb64SMartin KaFai Lau 
28412d8bb64SMartin KaFai Lau 	while (min_port <= max_port) {
28512d8bb64SMartin KaFai Lau 		vip.dport = htons(min_port++);
286bbaf6029SMaciej Fijalkowski 		if (bpf_map_update_elem(vip2tnl_map_fd, &vip, &tnl,
287bbaf6029SMaciej Fijalkowski 					BPF_NOEXIST)) {
288d40fc181SJoe Stringer 			perror("bpf_map_update_elem(&vip2tnl)");
28912d8bb64SMartin KaFai Lau 			return 1;
29012d8bb64SMartin KaFai Lau 		}
29112d8bb64SMartin KaFai Lau 	}
29212d8bb64SMartin KaFai Lau 
293d4e34bfcSAndrii Nakryiko 	if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) {
29412d8bb64SMartin KaFai Lau 		printf("link set xdp fd failed\n");
29512d8bb64SMartin KaFai Lau 		return 1;
29612d8bb64SMartin KaFai Lau 	}
29712d8bb64SMartin KaFai Lau 
298*c0ca277bSIlya Leoshkevich 	err = bpf_prog_get_info_by_fd(prog_fd, &info, &info_len);
2993b7a8ec2SMaciej Fijalkowski 	if (err) {
3003b7a8ec2SMaciej Fijalkowski 		printf("can't get prog info - %s\n", strerror(errno));
3013b7a8ec2SMaciej Fijalkowski 		return err;
3023b7a8ec2SMaciej Fijalkowski 	}
3033b7a8ec2SMaciej Fijalkowski 	prog_id = info.id;
3043b7a8ec2SMaciej Fijalkowski 
30512d8bb64SMartin KaFai Lau 	poll_stats(kill_after_s);
30612d8bb64SMartin KaFai Lau 
307d4e34bfcSAndrii Nakryiko 	bpf_xdp_detach(ifindex, xdp_flags, NULL);
30812d8bb64SMartin KaFai Lau 
30912d8bb64SMartin KaFai Lau 	return 0;
31012d8bb64SMartin KaFai Lau }
311