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