162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright (C) 2017 Cavium, Inc.
362306a36Sopenharmony_ci */
462306a36Sopenharmony_ci#include <linux/bpf.h>
562306a36Sopenharmony_ci#include <linux/netlink.h>
662306a36Sopenharmony_ci#include <linux/rtnetlink.h>
762306a36Sopenharmony_ci#include <assert.h>
862306a36Sopenharmony_ci#include <errno.h>
962306a36Sopenharmony_ci#include <signal.h>
1062306a36Sopenharmony_ci#include <stdio.h>
1162306a36Sopenharmony_ci#include <stdlib.h>
1262306a36Sopenharmony_ci#include <string.h>
1362306a36Sopenharmony_ci#include <sys/socket.h>
1462306a36Sopenharmony_ci#include <unistd.h>
1562306a36Sopenharmony_ci#include <bpf/bpf.h>
1662306a36Sopenharmony_ci#include <arpa/inet.h>
1762306a36Sopenharmony_ci#include <fcntl.h>
1862306a36Sopenharmony_ci#include <poll.h>
1962306a36Sopenharmony_ci#include <net/if.h>
2062306a36Sopenharmony_ci#include <netdb.h>
2162306a36Sopenharmony_ci#include <sys/ioctl.h>
2262306a36Sopenharmony_ci#include <sys/syscall.h>
2362306a36Sopenharmony_ci#include "bpf_util.h"
2462306a36Sopenharmony_ci#include <bpf/libbpf.h>
2562306a36Sopenharmony_ci#include <libgen.h>
2662306a36Sopenharmony_ci#include <getopt.h>
2762306a36Sopenharmony_ci#include <pthread.h>
2862306a36Sopenharmony_ci#include "xdp_sample_user.h"
2962306a36Sopenharmony_ci#include "xdp_router_ipv4.skel.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic const char *__doc__ =
3262306a36Sopenharmony_ci"XDP IPv4 router implementation\n"
3362306a36Sopenharmony_ci"Usage: xdp_router_ipv4 <IFNAME-0> ... <IFNAME-N>\n";
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic char buf[8192];
3662306a36Sopenharmony_cistatic int lpm_map_fd;
3762306a36Sopenharmony_cistatic int arp_table_map_fd;
3862306a36Sopenharmony_cistatic int exact_match_map_fd;
3962306a36Sopenharmony_cistatic int tx_port_map_fd;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic bool routes_thread_exit;
4262306a36Sopenharmony_cistatic int interval = 5;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_MAP_CNT |
4562306a36Sopenharmony_ci		  SAMPLE_DEVMAP_XMIT_CNT_MULTI | SAMPLE_EXCEPTION_CNT;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ciDEFINE_SAMPLE_INIT(xdp_router_ipv4);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic const struct option long_options[] = {
5062306a36Sopenharmony_ci	{ "help", no_argument, NULL, 'h' },
5162306a36Sopenharmony_ci	{ "skb-mode", no_argument, NULL, 'S' },
5262306a36Sopenharmony_ci	{ "force", no_argument, NULL, 'F' },
5362306a36Sopenharmony_ci	{ "interval", required_argument, NULL, 'i' },
5462306a36Sopenharmony_ci	{ "verbose", no_argument, NULL, 'v' },
5562306a36Sopenharmony_ci	{ "stats", no_argument, NULL, 's' },
5662306a36Sopenharmony_ci	{}
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic int get_route_table(int rtm_family);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int recv_msg(struct sockaddr_nl sock_addr, int sock)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct nlmsghdr *nh;
6462306a36Sopenharmony_ci	int len, nll = 0;
6562306a36Sopenharmony_ci	char *buf_ptr;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	buf_ptr = buf;
6862306a36Sopenharmony_ci	while (1) {
6962306a36Sopenharmony_ci		len = recv(sock, buf_ptr, sizeof(buf) - nll, 0);
7062306a36Sopenharmony_ci		if (len < 0)
7162306a36Sopenharmony_ci			return len;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		nh = (struct nlmsghdr *)buf_ptr;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci		if (nh->nlmsg_type == NLMSG_DONE)
7662306a36Sopenharmony_ci			break;
7762306a36Sopenharmony_ci		buf_ptr += len;
7862306a36Sopenharmony_ci		nll += len;
7962306a36Sopenharmony_ci		if ((sock_addr.nl_groups & RTMGRP_NEIGH) == RTMGRP_NEIGH)
8062306a36Sopenharmony_ci			break;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		if ((sock_addr.nl_groups & RTMGRP_IPV4_ROUTE) == RTMGRP_IPV4_ROUTE)
8362306a36Sopenharmony_ci			break;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci	return nll;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* Function to parse the route entry returned by netlink
8962306a36Sopenharmony_ci * Updates the route entry related map entries
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_cistatic void read_route(struct nlmsghdr *nh, int nll)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	char dsts[24], gws[24], ifs[16], dsts_len[24], metrics[24];
9462306a36Sopenharmony_ci	struct bpf_lpm_trie_key *prefix_key;
9562306a36Sopenharmony_ci	struct rtattr *rt_attr;
9662306a36Sopenharmony_ci	struct rtmsg *rt_msg;
9762306a36Sopenharmony_ci	int rtm_family;
9862306a36Sopenharmony_ci	int rtl;
9962306a36Sopenharmony_ci	int i;
10062306a36Sopenharmony_ci	struct route_table {
10162306a36Sopenharmony_ci		int  dst_len, iface, metric;
10262306a36Sopenharmony_ci		__be32 dst, gw;
10362306a36Sopenharmony_ci		__be64 mac;
10462306a36Sopenharmony_ci	} route;
10562306a36Sopenharmony_ci	struct arp_table {
10662306a36Sopenharmony_ci		__be64 mac;
10762306a36Sopenharmony_ci		__be32 dst;
10862306a36Sopenharmony_ci	};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	struct direct_map {
11162306a36Sopenharmony_ci		struct arp_table arp;
11262306a36Sopenharmony_ci		int ifindex;
11362306a36Sopenharmony_ci		__be64 mac;
11462306a36Sopenharmony_ci	} direct_entry;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	memset(&route, 0, sizeof(route));
11762306a36Sopenharmony_ci	for (; NLMSG_OK(nh, nll); nh = NLMSG_NEXT(nh, nll)) {
11862306a36Sopenharmony_ci		rt_msg = (struct rtmsg *)NLMSG_DATA(nh);
11962306a36Sopenharmony_ci		rtm_family = rt_msg->rtm_family;
12062306a36Sopenharmony_ci		if (rtm_family == AF_INET)
12162306a36Sopenharmony_ci			if (rt_msg->rtm_table != RT_TABLE_MAIN)
12262306a36Sopenharmony_ci				continue;
12362306a36Sopenharmony_ci		rt_attr = (struct rtattr *)RTM_RTA(rt_msg);
12462306a36Sopenharmony_ci		rtl = RTM_PAYLOAD(nh);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		for (; RTA_OK(rt_attr, rtl); rt_attr = RTA_NEXT(rt_attr, rtl)) {
12762306a36Sopenharmony_ci			switch (rt_attr->rta_type) {
12862306a36Sopenharmony_ci			case NDA_DST:
12962306a36Sopenharmony_ci				sprintf(dsts, "%u",
13062306a36Sopenharmony_ci					(*((__be32 *)RTA_DATA(rt_attr))));
13162306a36Sopenharmony_ci				break;
13262306a36Sopenharmony_ci			case RTA_GATEWAY:
13362306a36Sopenharmony_ci				sprintf(gws, "%u",
13462306a36Sopenharmony_ci					*((__be32 *)RTA_DATA(rt_attr)));
13562306a36Sopenharmony_ci				break;
13662306a36Sopenharmony_ci			case RTA_OIF:
13762306a36Sopenharmony_ci				sprintf(ifs, "%u",
13862306a36Sopenharmony_ci					*((int *)RTA_DATA(rt_attr)));
13962306a36Sopenharmony_ci				break;
14062306a36Sopenharmony_ci			case RTA_METRICS:
14162306a36Sopenharmony_ci				sprintf(metrics, "%u",
14262306a36Sopenharmony_ci					*((int *)RTA_DATA(rt_attr)));
14362306a36Sopenharmony_ci			default:
14462306a36Sopenharmony_ci				break;
14562306a36Sopenharmony_ci			}
14662306a36Sopenharmony_ci		}
14762306a36Sopenharmony_ci		sprintf(dsts_len, "%d", rt_msg->rtm_dst_len);
14862306a36Sopenharmony_ci		route.dst = atoi(dsts);
14962306a36Sopenharmony_ci		route.dst_len = atoi(dsts_len);
15062306a36Sopenharmony_ci		route.gw = atoi(gws);
15162306a36Sopenharmony_ci		route.iface = atoi(ifs);
15262306a36Sopenharmony_ci		route.metric = atoi(metrics);
15362306a36Sopenharmony_ci		assert(get_mac_addr(route.iface, &route.mac) == 0);
15462306a36Sopenharmony_ci		assert(bpf_map_update_elem(tx_port_map_fd,
15562306a36Sopenharmony_ci					   &route.iface, &route.iface, 0) == 0);
15662306a36Sopenharmony_ci		if (rtm_family == AF_INET) {
15762306a36Sopenharmony_ci			struct trie_value {
15862306a36Sopenharmony_ci				__u8 prefix[4];
15962306a36Sopenharmony_ci				__be64 value;
16062306a36Sopenharmony_ci				int ifindex;
16162306a36Sopenharmony_ci				int metric;
16262306a36Sopenharmony_ci				__be32 gw;
16362306a36Sopenharmony_ci			} *prefix_value;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci			prefix_key = alloca(sizeof(*prefix_key) + 4);
16662306a36Sopenharmony_ci			prefix_value = alloca(sizeof(*prefix_value));
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci			prefix_key->prefixlen = 32;
16962306a36Sopenharmony_ci			prefix_key->prefixlen = route.dst_len;
17062306a36Sopenharmony_ci			direct_entry.mac = route.mac & 0xffffffffffff;
17162306a36Sopenharmony_ci			direct_entry.ifindex = route.iface;
17262306a36Sopenharmony_ci			direct_entry.arp.mac = 0;
17362306a36Sopenharmony_ci			direct_entry.arp.dst = 0;
17462306a36Sopenharmony_ci			if (route.dst_len == 32) {
17562306a36Sopenharmony_ci				if (nh->nlmsg_type == RTM_DELROUTE) {
17662306a36Sopenharmony_ci					assert(bpf_map_delete_elem(exact_match_map_fd,
17762306a36Sopenharmony_ci								   &route.dst) == 0);
17862306a36Sopenharmony_ci				} else {
17962306a36Sopenharmony_ci					if (bpf_map_lookup_elem(arp_table_map_fd,
18062306a36Sopenharmony_ci								&route.dst,
18162306a36Sopenharmony_ci								&direct_entry.arp.mac) == 0)
18262306a36Sopenharmony_ci						direct_entry.arp.dst = route.dst;
18362306a36Sopenharmony_ci					assert(bpf_map_update_elem(exact_match_map_fd,
18462306a36Sopenharmony_ci								   &route.dst,
18562306a36Sopenharmony_ci								   &direct_entry, 0) == 0);
18662306a36Sopenharmony_ci				}
18762306a36Sopenharmony_ci			}
18862306a36Sopenharmony_ci			for (i = 0; i < 4; i++)
18962306a36Sopenharmony_ci				prefix_key->data[i] = (route.dst >> i * 8) & 0xff;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci			if (bpf_map_lookup_elem(lpm_map_fd, prefix_key,
19262306a36Sopenharmony_ci						prefix_value) < 0) {
19362306a36Sopenharmony_ci				for (i = 0; i < 4; i++)
19462306a36Sopenharmony_ci					prefix_value->prefix[i] = prefix_key->data[i];
19562306a36Sopenharmony_ci				prefix_value->value = route.mac & 0xffffffffffff;
19662306a36Sopenharmony_ci				prefix_value->ifindex = route.iface;
19762306a36Sopenharmony_ci				prefix_value->gw = route.gw;
19862306a36Sopenharmony_ci				prefix_value->metric = route.metric;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci				assert(bpf_map_update_elem(lpm_map_fd,
20162306a36Sopenharmony_ci							   prefix_key,
20262306a36Sopenharmony_ci							   prefix_value, 0
20362306a36Sopenharmony_ci							   ) == 0);
20462306a36Sopenharmony_ci			} else {
20562306a36Sopenharmony_ci				if (nh->nlmsg_type == RTM_DELROUTE) {
20662306a36Sopenharmony_ci					assert(bpf_map_delete_elem(lpm_map_fd,
20762306a36Sopenharmony_ci								   prefix_key
20862306a36Sopenharmony_ci								   ) == 0);
20962306a36Sopenharmony_ci					/* Rereading the route table to check if
21062306a36Sopenharmony_ci					 * there is an entry with the same
21162306a36Sopenharmony_ci					 * prefix but a different metric as the
21262306a36Sopenharmony_ci					 * deleted entry.
21362306a36Sopenharmony_ci					 */
21462306a36Sopenharmony_ci					get_route_table(AF_INET);
21562306a36Sopenharmony_ci				} else if (prefix_key->data[0] ==
21662306a36Sopenharmony_ci					   prefix_value->prefix[0] &&
21762306a36Sopenharmony_ci					   prefix_key->data[1] ==
21862306a36Sopenharmony_ci					   prefix_value->prefix[1] &&
21962306a36Sopenharmony_ci					   prefix_key->data[2] ==
22062306a36Sopenharmony_ci					   prefix_value->prefix[2] &&
22162306a36Sopenharmony_ci					   prefix_key->data[3] ==
22262306a36Sopenharmony_ci					   prefix_value->prefix[3] &&
22362306a36Sopenharmony_ci					   route.metric >= prefix_value->metric) {
22462306a36Sopenharmony_ci					continue;
22562306a36Sopenharmony_ci				} else {
22662306a36Sopenharmony_ci					for (i = 0; i < 4; i++)
22762306a36Sopenharmony_ci						prefix_value->prefix[i] =
22862306a36Sopenharmony_ci							prefix_key->data[i];
22962306a36Sopenharmony_ci					prefix_value->value =
23062306a36Sopenharmony_ci						route.mac & 0xffffffffffff;
23162306a36Sopenharmony_ci					prefix_value->ifindex = route.iface;
23262306a36Sopenharmony_ci					prefix_value->gw = route.gw;
23362306a36Sopenharmony_ci					prefix_value->metric = route.metric;
23462306a36Sopenharmony_ci					assert(bpf_map_update_elem(lpm_map_fd,
23562306a36Sopenharmony_ci								   prefix_key,
23662306a36Sopenharmony_ci								   prefix_value,
23762306a36Sopenharmony_ci								   0) == 0);
23862306a36Sopenharmony_ci				}
23962306a36Sopenharmony_ci			}
24062306a36Sopenharmony_ci		}
24162306a36Sopenharmony_ci		memset(&route, 0, sizeof(route));
24262306a36Sopenharmony_ci		memset(dsts, 0, sizeof(dsts));
24362306a36Sopenharmony_ci		memset(dsts_len, 0, sizeof(dsts_len));
24462306a36Sopenharmony_ci		memset(gws, 0, sizeof(gws));
24562306a36Sopenharmony_ci		memset(ifs, 0, sizeof(ifs));
24662306a36Sopenharmony_ci		memset(&route, 0, sizeof(route));
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/* Function to read the existing route table  when the process is launched*/
25162306a36Sopenharmony_cistatic int get_route_table(int rtm_family)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct sockaddr_nl sa;
25462306a36Sopenharmony_ci	struct nlmsghdr *nh;
25562306a36Sopenharmony_ci	int sock, seq = 0;
25662306a36Sopenharmony_ci	struct msghdr msg;
25762306a36Sopenharmony_ci	struct iovec iov;
25862306a36Sopenharmony_ci	int ret = 0;
25962306a36Sopenharmony_ci	int nll;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	struct {
26262306a36Sopenharmony_ci		struct nlmsghdr nl;
26362306a36Sopenharmony_ci		struct rtmsg rt;
26462306a36Sopenharmony_ci		char buf[8192];
26562306a36Sopenharmony_ci	} req;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
26862306a36Sopenharmony_ci	if (sock < 0) {
26962306a36Sopenharmony_ci		fprintf(stderr, "open netlink socket: %s\n", strerror(errno));
27062306a36Sopenharmony_ci		return -errno;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci	memset(&sa, 0, sizeof(sa));
27362306a36Sopenharmony_ci	sa.nl_family = AF_NETLINK;
27462306a36Sopenharmony_ci	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
27562306a36Sopenharmony_ci		fprintf(stderr, "bind netlink socket: %s\n", strerror(errno));
27662306a36Sopenharmony_ci		ret = -errno;
27762306a36Sopenharmony_ci		goto cleanup;
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci	memset(&req, 0, sizeof(req));
28062306a36Sopenharmony_ci	req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
28162306a36Sopenharmony_ci	req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
28262306a36Sopenharmony_ci	req.nl.nlmsg_type = RTM_GETROUTE;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	req.rt.rtm_family = rtm_family;
28562306a36Sopenharmony_ci	req.rt.rtm_table = RT_TABLE_MAIN;
28662306a36Sopenharmony_ci	req.nl.nlmsg_pid = 0;
28762306a36Sopenharmony_ci	req.nl.nlmsg_seq = ++seq;
28862306a36Sopenharmony_ci	memset(&msg, 0, sizeof(msg));
28962306a36Sopenharmony_ci	iov.iov_base = (void *)&req.nl;
29062306a36Sopenharmony_ci	iov.iov_len = req.nl.nlmsg_len;
29162306a36Sopenharmony_ci	msg.msg_iov = &iov;
29262306a36Sopenharmony_ci	msg.msg_iovlen = 1;
29362306a36Sopenharmony_ci	ret = sendmsg(sock, &msg, 0);
29462306a36Sopenharmony_ci	if (ret < 0) {
29562306a36Sopenharmony_ci		fprintf(stderr, "send to netlink: %s\n", strerror(errno));
29662306a36Sopenharmony_ci		ret = -errno;
29762306a36Sopenharmony_ci		goto cleanup;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci	memset(buf, 0, sizeof(buf));
30062306a36Sopenharmony_ci	nll = recv_msg(sa, sock);
30162306a36Sopenharmony_ci	if (nll < 0) {
30262306a36Sopenharmony_ci		fprintf(stderr, "recv from netlink: %s\n", strerror(nll));
30362306a36Sopenharmony_ci		ret = nll;
30462306a36Sopenharmony_ci		goto cleanup;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci	nh = (struct nlmsghdr *)buf;
30762306a36Sopenharmony_ci	read_route(nh, nll);
30862306a36Sopenharmony_cicleanup:
30962306a36Sopenharmony_ci	close(sock);
31062306a36Sopenharmony_ci	return ret;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/* Function to parse the arp entry returned by netlink
31462306a36Sopenharmony_ci * Updates the arp entry related map entries
31562306a36Sopenharmony_ci */
31662306a36Sopenharmony_cistatic void read_arp(struct nlmsghdr *nh, int nll)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct rtattr *rt_attr;
31962306a36Sopenharmony_ci	char dsts[24], mac[24];
32062306a36Sopenharmony_ci	struct ndmsg *rt_msg;
32162306a36Sopenharmony_ci	int rtl, ndm_family;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	struct arp_table {
32462306a36Sopenharmony_ci		__be64 mac;
32562306a36Sopenharmony_ci		__be32 dst;
32662306a36Sopenharmony_ci	} arp_entry;
32762306a36Sopenharmony_ci	struct direct_map {
32862306a36Sopenharmony_ci		struct arp_table arp;
32962306a36Sopenharmony_ci		int ifindex;
33062306a36Sopenharmony_ci		__be64 mac;
33162306a36Sopenharmony_ci	} direct_entry;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	for (; NLMSG_OK(nh, nll); nh = NLMSG_NEXT(nh, nll)) {
33462306a36Sopenharmony_ci		rt_msg = (struct ndmsg *)NLMSG_DATA(nh);
33562306a36Sopenharmony_ci		rt_attr = (struct rtattr *)RTM_RTA(rt_msg);
33662306a36Sopenharmony_ci		ndm_family = rt_msg->ndm_family;
33762306a36Sopenharmony_ci		rtl = RTM_PAYLOAD(nh);
33862306a36Sopenharmony_ci		for (; RTA_OK(rt_attr, rtl); rt_attr = RTA_NEXT(rt_attr, rtl)) {
33962306a36Sopenharmony_ci			switch (rt_attr->rta_type) {
34062306a36Sopenharmony_ci			case NDA_DST:
34162306a36Sopenharmony_ci				sprintf(dsts, "%u",
34262306a36Sopenharmony_ci					*((__be32 *)RTA_DATA(rt_attr)));
34362306a36Sopenharmony_ci				break;
34462306a36Sopenharmony_ci			case NDA_LLADDR:
34562306a36Sopenharmony_ci				sprintf(mac, "%lld",
34662306a36Sopenharmony_ci					*((__be64 *)RTA_DATA(rt_attr)));
34762306a36Sopenharmony_ci				break;
34862306a36Sopenharmony_ci			default:
34962306a36Sopenharmony_ci				break;
35062306a36Sopenharmony_ci			}
35162306a36Sopenharmony_ci		}
35262306a36Sopenharmony_ci		arp_entry.dst = atoi(dsts);
35362306a36Sopenharmony_ci		arp_entry.mac = atol(mac);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		if (ndm_family == AF_INET) {
35662306a36Sopenharmony_ci			if (bpf_map_lookup_elem(exact_match_map_fd,
35762306a36Sopenharmony_ci						&arp_entry.dst,
35862306a36Sopenharmony_ci						&direct_entry) == 0) {
35962306a36Sopenharmony_ci				if (nh->nlmsg_type == RTM_DELNEIGH) {
36062306a36Sopenharmony_ci					direct_entry.arp.dst = 0;
36162306a36Sopenharmony_ci					direct_entry.arp.mac = 0;
36262306a36Sopenharmony_ci				} else if (nh->nlmsg_type == RTM_NEWNEIGH) {
36362306a36Sopenharmony_ci					direct_entry.arp.dst = arp_entry.dst;
36462306a36Sopenharmony_ci					direct_entry.arp.mac = arp_entry.mac;
36562306a36Sopenharmony_ci				}
36662306a36Sopenharmony_ci				assert(bpf_map_update_elem(exact_match_map_fd,
36762306a36Sopenharmony_ci							   &arp_entry.dst,
36862306a36Sopenharmony_ci							   &direct_entry, 0
36962306a36Sopenharmony_ci							   ) == 0);
37062306a36Sopenharmony_ci				memset(&direct_entry, 0, sizeof(direct_entry));
37162306a36Sopenharmony_ci			}
37262306a36Sopenharmony_ci			if (nh->nlmsg_type == RTM_DELNEIGH) {
37362306a36Sopenharmony_ci				assert(bpf_map_delete_elem(arp_table_map_fd,
37462306a36Sopenharmony_ci							   &arp_entry.dst) == 0);
37562306a36Sopenharmony_ci			} else if (nh->nlmsg_type == RTM_NEWNEIGH) {
37662306a36Sopenharmony_ci				assert(bpf_map_update_elem(arp_table_map_fd,
37762306a36Sopenharmony_ci							   &arp_entry.dst,
37862306a36Sopenharmony_ci							   &arp_entry.mac, 0
37962306a36Sopenharmony_ci							   ) == 0);
38062306a36Sopenharmony_ci			}
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci		memset(&arp_entry, 0, sizeof(arp_entry));
38362306a36Sopenharmony_ci		memset(dsts, 0, sizeof(dsts));
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci/* Function to read the existing arp table  when the process is launched*/
38862306a36Sopenharmony_cistatic int get_arp_table(int rtm_family)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct sockaddr_nl sa;
39162306a36Sopenharmony_ci	struct nlmsghdr *nh;
39262306a36Sopenharmony_ci	int sock, seq = 0;
39362306a36Sopenharmony_ci	struct msghdr msg;
39462306a36Sopenharmony_ci	struct iovec iov;
39562306a36Sopenharmony_ci	int ret = 0;
39662306a36Sopenharmony_ci	int nll;
39762306a36Sopenharmony_ci	struct {
39862306a36Sopenharmony_ci		struct nlmsghdr nl;
39962306a36Sopenharmony_ci		struct ndmsg rt;
40062306a36Sopenharmony_ci		char buf[8192];
40162306a36Sopenharmony_ci	} req;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
40462306a36Sopenharmony_ci	if (sock < 0) {
40562306a36Sopenharmony_ci		fprintf(stderr, "open netlink socket: %s\n", strerror(errno));
40662306a36Sopenharmony_ci		return -errno;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci	memset(&sa, 0, sizeof(sa));
40962306a36Sopenharmony_ci	sa.nl_family = AF_NETLINK;
41062306a36Sopenharmony_ci	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
41162306a36Sopenharmony_ci		fprintf(stderr, "bind netlink socket: %s\n", strerror(errno));
41262306a36Sopenharmony_ci		ret = -errno;
41362306a36Sopenharmony_ci		goto cleanup;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci	memset(&req, 0, sizeof(req));
41662306a36Sopenharmony_ci	req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
41762306a36Sopenharmony_ci	req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
41862306a36Sopenharmony_ci	req.nl.nlmsg_type = RTM_GETNEIGH;
41962306a36Sopenharmony_ci	req.rt.ndm_state = NUD_REACHABLE;
42062306a36Sopenharmony_ci	req.rt.ndm_family = rtm_family;
42162306a36Sopenharmony_ci	req.nl.nlmsg_pid = 0;
42262306a36Sopenharmony_ci	req.nl.nlmsg_seq = ++seq;
42362306a36Sopenharmony_ci	memset(&msg, 0, sizeof(msg));
42462306a36Sopenharmony_ci	iov.iov_base = (void *)&req.nl;
42562306a36Sopenharmony_ci	iov.iov_len = req.nl.nlmsg_len;
42662306a36Sopenharmony_ci	msg.msg_iov = &iov;
42762306a36Sopenharmony_ci	msg.msg_iovlen = 1;
42862306a36Sopenharmony_ci	ret = sendmsg(sock, &msg, 0);
42962306a36Sopenharmony_ci	if (ret < 0) {
43062306a36Sopenharmony_ci		fprintf(stderr, "send to netlink: %s\n", strerror(errno));
43162306a36Sopenharmony_ci		ret = -errno;
43262306a36Sopenharmony_ci		goto cleanup;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci	memset(buf, 0, sizeof(buf));
43562306a36Sopenharmony_ci	nll = recv_msg(sa, sock);
43662306a36Sopenharmony_ci	if (nll < 0) {
43762306a36Sopenharmony_ci		fprintf(stderr, "recv from netlink: %s\n", strerror(nll));
43862306a36Sopenharmony_ci		ret = nll;
43962306a36Sopenharmony_ci		goto cleanup;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci	nh = (struct nlmsghdr *)buf;
44262306a36Sopenharmony_ci	read_arp(nh, nll);
44362306a36Sopenharmony_cicleanup:
44462306a36Sopenharmony_ci	close(sock);
44562306a36Sopenharmony_ci	return ret;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci/* Function to keep track and update changes in route and arp table
44962306a36Sopenharmony_ci * Give regular statistics of packets forwarded
45062306a36Sopenharmony_ci */
45162306a36Sopenharmony_cistatic void *monitor_routes_thread(void *arg)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct pollfd fds_route, fds_arp;
45462306a36Sopenharmony_ci	struct sockaddr_nl la, lr;
45562306a36Sopenharmony_ci	int sock, sock_arp, nll;
45662306a36Sopenharmony_ci	struct nlmsghdr *nh;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
45962306a36Sopenharmony_ci	if (sock < 0) {
46062306a36Sopenharmony_ci		fprintf(stderr, "open netlink socket: %s\n", strerror(errno));
46162306a36Sopenharmony_ci		return NULL;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	fcntl(sock, F_SETFL, O_NONBLOCK);
46562306a36Sopenharmony_ci	memset(&lr, 0, sizeof(lr));
46662306a36Sopenharmony_ci	lr.nl_family = AF_NETLINK;
46762306a36Sopenharmony_ci	lr.nl_groups = RTMGRP_IPV6_ROUTE | RTMGRP_IPV4_ROUTE | RTMGRP_NOTIFY;
46862306a36Sopenharmony_ci	if (bind(sock, (struct sockaddr *)&lr, sizeof(lr)) < 0) {
46962306a36Sopenharmony_ci		fprintf(stderr, "bind netlink socket: %s\n", strerror(errno));
47062306a36Sopenharmony_ci		close(sock);
47162306a36Sopenharmony_ci		return NULL;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	fds_route.fd = sock;
47562306a36Sopenharmony_ci	fds_route.events = POLL_IN;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	sock_arp = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
47862306a36Sopenharmony_ci	if (sock_arp < 0) {
47962306a36Sopenharmony_ci		fprintf(stderr, "open netlink socket: %s\n", strerror(errno));
48062306a36Sopenharmony_ci		close(sock);
48162306a36Sopenharmony_ci		return NULL;
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	fcntl(sock_arp, F_SETFL, O_NONBLOCK);
48562306a36Sopenharmony_ci	memset(&la, 0, sizeof(la));
48662306a36Sopenharmony_ci	la.nl_family = AF_NETLINK;
48762306a36Sopenharmony_ci	la.nl_groups = RTMGRP_NEIGH | RTMGRP_NOTIFY;
48862306a36Sopenharmony_ci	if (bind(sock_arp, (struct sockaddr *)&la, sizeof(la)) < 0) {
48962306a36Sopenharmony_ci		fprintf(stderr, "bind netlink socket: %s\n", strerror(errno));
49062306a36Sopenharmony_ci		goto cleanup;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	fds_arp.fd = sock_arp;
49462306a36Sopenharmony_ci	fds_arp.events = POLL_IN;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* dump route and arp tables */
49762306a36Sopenharmony_ci	if (get_arp_table(AF_INET) < 0) {
49862306a36Sopenharmony_ci		fprintf(stderr, "Failed reading arp table\n");
49962306a36Sopenharmony_ci		goto cleanup;
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (get_route_table(AF_INET) < 0) {
50362306a36Sopenharmony_ci		fprintf(stderr, "Failed reading route table\n");
50462306a36Sopenharmony_ci		goto cleanup;
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	while (!routes_thread_exit) {
50862306a36Sopenharmony_ci		memset(buf, 0, sizeof(buf));
50962306a36Sopenharmony_ci		if (poll(&fds_route, 1, 3) == POLL_IN) {
51062306a36Sopenharmony_ci			nll = recv_msg(lr, sock);
51162306a36Sopenharmony_ci			if (nll < 0) {
51262306a36Sopenharmony_ci				fprintf(stderr, "recv from netlink: %s\n",
51362306a36Sopenharmony_ci					strerror(nll));
51462306a36Sopenharmony_ci				goto cleanup;
51562306a36Sopenharmony_ci			}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci			nh = (struct nlmsghdr *)buf;
51862306a36Sopenharmony_ci			read_route(nh, nll);
51962306a36Sopenharmony_ci		}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		memset(buf, 0, sizeof(buf));
52262306a36Sopenharmony_ci		if (poll(&fds_arp, 1, 3) == POLL_IN) {
52362306a36Sopenharmony_ci			nll = recv_msg(la, sock_arp);
52462306a36Sopenharmony_ci			if (nll < 0) {
52562306a36Sopenharmony_ci				fprintf(stderr, "recv from netlink: %s\n",
52662306a36Sopenharmony_ci					strerror(nll));
52762306a36Sopenharmony_ci				goto cleanup;
52862306a36Sopenharmony_ci			}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci			nh = (struct nlmsghdr *)buf;
53162306a36Sopenharmony_ci			read_arp(nh, nll);
53262306a36Sopenharmony_ci		}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci		sleep(interval);
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cicleanup:
53862306a36Sopenharmony_ci	close(sock_arp);
53962306a36Sopenharmony_ci	close(sock);
54062306a36Sopenharmony_ci	return NULL;
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic void usage(char *argv[], const struct option *long_options,
54462306a36Sopenharmony_ci		  const char *doc, int mask, bool error,
54562306a36Sopenharmony_ci		  struct bpf_object *obj)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	sample_usage(argv, long_options, doc, mask, error);
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ciint main(int argc, char **argv)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	bool error = true, generic = false, force = false;
55362306a36Sopenharmony_ci	int opt, ret = EXIT_FAIL_BPF;
55462306a36Sopenharmony_ci	struct xdp_router_ipv4 *skel;
55562306a36Sopenharmony_ci	int i, total_ifindex = argc - 1;
55662306a36Sopenharmony_ci	char **ifname_list = argv + 1;
55762306a36Sopenharmony_ci	pthread_t routes_thread;
55862306a36Sopenharmony_ci	int longindex = 0;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (libbpf_set_strict_mode(LIBBPF_STRICT_ALL) < 0) {
56162306a36Sopenharmony_ci		fprintf(stderr, "Failed to set libbpf strict mode: %s\n",
56262306a36Sopenharmony_ci			strerror(errno));
56362306a36Sopenharmony_ci		goto end;
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	skel = xdp_router_ipv4__open();
56762306a36Sopenharmony_ci	if (!skel) {
56862306a36Sopenharmony_ci		fprintf(stderr, "Failed to xdp_router_ipv4__open: %s\n",
56962306a36Sopenharmony_ci			strerror(errno));
57062306a36Sopenharmony_ci		goto end;
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	ret = sample_init_pre_load(skel);
57462306a36Sopenharmony_ci	if (ret < 0) {
57562306a36Sopenharmony_ci		fprintf(stderr, "Failed to sample_init_pre_load: %s\n",
57662306a36Sopenharmony_ci			strerror(-ret));
57762306a36Sopenharmony_ci		ret = EXIT_FAIL_BPF;
57862306a36Sopenharmony_ci		goto end_destroy;
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	ret = xdp_router_ipv4__load(skel);
58262306a36Sopenharmony_ci	if (ret < 0) {
58362306a36Sopenharmony_ci		fprintf(stderr, "Failed to xdp_router_ipv4__load: %s\n",
58462306a36Sopenharmony_ci			strerror(errno));
58562306a36Sopenharmony_ci		goto end_destroy;
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	ret = sample_init(skel, mask);
58962306a36Sopenharmony_ci	if (ret < 0) {
59062306a36Sopenharmony_ci		fprintf(stderr, "Failed to initialize sample: %s\n", strerror(-ret));
59162306a36Sopenharmony_ci		ret = EXIT_FAIL;
59262306a36Sopenharmony_ci		goto end_destroy;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	while ((opt = getopt_long(argc, argv, "si:SFvh",
59662306a36Sopenharmony_ci				  long_options, &longindex)) != -1) {
59762306a36Sopenharmony_ci		switch (opt) {
59862306a36Sopenharmony_ci		case 's':
59962306a36Sopenharmony_ci			mask |= SAMPLE_REDIRECT_MAP_CNT;
60062306a36Sopenharmony_ci			total_ifindex--;
60162306a36Sopenharmony_ci			ifname_list++;
60262306a36Sopenharmony_ci			break;
60362306a36Sopenharmony_ci		case 'i':
60462306a36Sopenharmony_ci			interval = strtoul(optarg, NULL, 0);
60562306a36Sopenharmony_ci			total_ifindex -= 2;
60662306a36Sopenharmony_ci			ifname_list += 2;
60762306a36Sopenharmony_ci			break;
60862306a36Sopenharmony_ci		case 'S':
60962306a36Sopenharmony_ci			generic = true;
61062306a36Sopenharmony_ci			total_ifindex--;
61162306a36Sopenharmony_ci			ifname_list++;
61262306a36Sopenharmony_ci			break;
61362306a36Sopenharmony_ci		case 'F':
61462306a36Sopenharmony_ci			force = true;
61562306a36Sopenharmony_ci			total_ifindex--;
61662306a36Sopenharmony_ci			ifname_list++;
61762306a36Sopenharmony_ci			break;
61862306a36Sopenharmony_ci		case 'v':
61962306a36Sopenharmony_ci			sample_switch_mode();
62062306a36Sopenharmony_ci			total_ifindex--;
62162306a36Sopenharmony_ci			ifname_list++;
62262306a36Sopenharmony_ci			break;
62362306a36Sopenharmony_ci		case 'h':
62462306a36Sopenharmony_ci			error = false;
62562306a36Sopenharmony_ci		default:
62662306a36Sopenharmony_ci			usage(argv, long_options, __doc__, mask, error, skel->obj);
62762306a36Sopenharmony_ci			goto end_destroy;
62862306a36Sopenharmony_ci		}
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	ret = EXIT_FAIL_OPTION;
63262306a36Sopenharmony_ci	if (optind == argc) {
63362306a36Sopenharmony_ci		usage(argv, long_options, __doc__, mask, true, skel->obj);
63462306a36Sopenharmony_ci		goto end_destroy;
63562306a36Sopenharmony_ci	}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	lpm_map_fd = bpf_map__fd(skel->maps.lpm_map);
63862306a36Sopenharmony_ci	if (lpm_map_fd < 0) {
63962306a36Sopenharmony_ci		fprintf(stderr, "Failed loading lpm_map %s\n",
64062306a36Sopenharmony_ci			strerror(-lpm_map_fd));
64162306a36Sopenharmony_ci		goto end_destroy;
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci	arp_table_map_fd = bpf_map__fd(skel->maps.arp_table);
64462306a36Sopenharmony_ci	if (arp_table_map_fd < 0) {
64562306a36Sopenharmony_ci		fprintf(stderr, "Failed loading arp_table_map_fd %s\n",
64662306a36Sopenharmony_ci			strerror(-arp_table_map_fd));
64762306a36Sopenharmony_ci		goto end_destroy;
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci	exact_match_map_fd = bpf_map__fd(skel->maps.exact_match);
65062306a36Sopenharmony_ci	if (exact_match_map_fd < 0) {
65162306a36Sopenharmony_ci		fprintf(stderr, "Failed loading exact_match_map_fd %s\n",
65262306a36Sopenharmony_ci			strerror(-exact_match_map_fd));
65362306a36Sopenharmony_ci		goto end_destroy;
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci	tx_port_map_fd = bpf_map__fd(skel->maps.tx_port);
65662306a36Sopenharmony_ci	if (tx_port_map_fd < 0) {
65762306a36Sopenharmony_ci		fprintf(stderr, "Failed loading tx_port_map_fd %s\n",
65862306a36Sopenharmony_ci			strerror(-tx_port_map_fd));
65962306a36Sopenharmony_ci		goto end_destroy;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	ret = EXIT_FAIL_XDP;
66362306a36Sopenharmony_ci	for (i = 0; i < total_ifindex; i++) {
66462306a36Sopenharmony_ci		int index = if_nametoindex(ifname_list[i]);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci		if (!index) {
66762306a36Sopenharmony_ci			fprintf(stderr, "Interface %s not found %s\n",
66862306a36Sopenharmony_ci				ifname_list[i], strerror(-tx_port_map_fd));
66962306a36Sopenharmony_ci			goto end_destroy;
67062306a36Sopenharmony_ci		}
67162306a36Sopenharmony_ci		if (sample_install_xdp(skel->progs.xdp_router_ipv4_prog,
67262306a36Sopenharmony_ci				       index, generic, force) < 0)
67362306a36Sopenharmony_ci			goto end_destroy;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	ret = pthread_create(&routes_thread, NULL, monitor_routes_thread, NULL);
67762306a36Sopenharmony_ci	if (ret) {
67862306a36Sopenharmony_ci		fprintf(stderr, "Failed creating routes_thread: %s\n", strerror(-ret));
67962306a36Sopenharmony_ci		ret = EXIT_FAIL;
68062306a36Sopenharmony_ci		goto end_destroy;
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	ret = sample_run(interval, NULL, NULL);
68462306a36Sopenharmony_ci	routes_thread_exit = true;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	if (ret < 0) {
68762306a36Sopenharmony_ci		fprintf(stderr, "Failed during sample run: %s\n", strerror(-ret));
68862306a36Sopenharmony_ci		ret = EXIT_FAIL;
68962306a36Sopenharmony_ci		goto end_thread_wait;
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci	ret = EXIT_OK;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ciend_thread_wait:
69462306a36Sopenharmony_ci	pthread_join(routes_thread, NULL);
69562306a36Sopenharmony_ciend_destroy:
69662306a36Sopenharmony_ci	xdp_router_ipv4__destroy(skel);
69762306a36Sopenharmony_ciend:
69862306a36Sopenharmony_ci	sample_exit(ret);
69962306a36Sopenharmony_ci}
700