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