Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* Copyright (C) 2017 Cavium, Inc.
0003  */
0004 #include <linux/bpf.h>
0005 #include <linux/netlink.h>
0006 #include <linux/rtnetlink.h>
0007 #include <assert.h>
0008 #include <errno.h>
0009 #include <signal.h>
0010 #include <stdio.h>
0011 #include <stdlib.h>
0012 #include <string.h>
0013 #include <sys/socket.h>
0014 #include <unistd.h>
0015 #include <bpf/bpf.h>
0016 #include <arpa/inet.h>
0017 #include <fcntl.h>
0018 #include <poll.h>
0019 #include <net/if.h>
0020 #include <netdb.h>
0021 #include <sys/ioctl.h>
0022 #include <sys/syscall.h>
0023 #include "bpf_util.h"
0024 #include <bpf/libbpf.h>
0025 #include <libgen.h>
0026 #include <getopt.h>
0027 #include <pthread.h>
0028 #include "xdp_sample_user.h"
0029 #include "xdp_router_ipv4.skel.h"
0030 
0031 static const char *__doc__ =
0032 "XDP IPv4 router implementation\n"
0033 "Usage: xdp_router_ipv4 <IFNAME-0> ... <IFNAME-N>\n";
0034 
0035 static char buf[8192];
0036 static int lpm_map_fd;
0037 static int arp_table_map_fd;
0038 static int exact_match_map_fd;
0039 static int tx_port_map_fd;
0040 
0041 static bool routes_thread_exit;
0042 static int interval = 5;
0043 
0044 static int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_MAP_CNT |
0045           SAMPLE_DEVMAP_XMIT_CNT_MULTI | SAMPLE_EXCEPTION_CNT;
0046 
0047 DEFINE_SAMPLE_INIT(xdp_router_ipv4);
0048 
0049 static const struct option long_options[] = {
0050     { "help", no_argument, NULL, 'h' },
0051     { "skb-mode", no_argument, NULL, 'S' },
0052     { "force", no_argument, NULL, 'F' },
0053     { "interval", required_argument, NULL, 'i' },
0054     { "verbose", no_argument, NULL, 'v' },
0055     { "stats", no_argument, NULL, 's' },
0056     {}
0057 };
0058 
0059 static int get_route_table(int rtm_family);
0060 
0061 static int recv_msg(struct sockaddr_nl sock_addr, int sock)
0062 {
0063     struct nlmsghdr *nh;
0064     int len, nll = 0;
0065     char *buf_ptr;
0066 
0067     buf_ptr = buf;
0068     while (1) {
0069         len = recv(sock, buf_ptr, sizeof(buf) - nll, 0);
0070         if (len < 0)
0071             return len;
0072 
0073         nh = (struct nlmsghdr *)buf_ptr;
0074 
0075         if (nh->nlmsg_type == NLMSG_DONE)
0076             break;
0077         buf_ptr += len;
0078         nll += len;
0079         if ((sock_addr.nl_groups & RTMGRP_NEIGH) == RTMGRP_NEIGH)
0080             break;
0081 
0082         if ((sock_addr.nl_groups & RTMGRP_IPV4_ROUTE) == RTMGRP_IPV4_ROUTE)
0083             break;
0084     }
0085     return nll;
0086 }
0087 
0088 /* Function to parse the route entry returned by netlink
0089  * Updates the route entry related map entries
0090  */
0091 static void read_route(struct nlmsghdr *nh, int nll)
0092 {
0093     char dsts[24], gws[24], ifs[16], dsts_len[24], metrics[24];
0094     struct bpf_lpm_trie_key *prefix_key;
0095     struct rtattr *rt_attr;
0096     struct rtmsg *rt_msg;
0097     int rtm_family;
0098     int rtl;
0099     int i;
0100     struct route_table {
0101         int  dst_len, iface, metric;
0102         __be32 dst, gw;
0103         __be64 mac;
0104     } route;
0105     struct arp_table {
0106         __be64 mac;
0107         __be32 dst;
0108     };
0109 
0110     struct direct_map {
0111         struct arp_table arp;
0112         int ifindex;
0113         __be64 mac;
0114     } direct_entry;
0115 
0116     memset(&route, 0, sizeof(route));
0117     for (; NLMSG_OK(nh, nll); nh = NLMSG_NEXT(nh, nll)) {
0118         rt_msg = (struct rtmsg *)NLMSG_DATA(nh);
0119         rtm_family = rt_msg->rtm_family;
0120         if (rtm_family == AF_INET)
0121             if (rt_msg->rtm_table != RT_TABLE_MAIN)
0122                 continue;
0123         rt_attr = (struct rtattr *)RTM_RTA(rt_msg);
0124         rtl = RTM_PAYLOAD(nh);
0125 
0126         for (; RTA_OK(rt_attr, rtl); rt_attr = RTA_NEXT(rt_attr, rtl)) {
0127             switch (rt_attr->rta_type) {
0128             case NDA_DST:
0129                 sprintf(dsts, "%u",
0130                     (*((__be32 *)RTA_DATA(rt_attr))));
0131                 break;
0132             case RTA_GATEWAY:
0133                 sprintf(gws, "%u",
0134                     *((__be32 *)RTA_DATA(rt_attr)));
0135                 break;
0136             case RTA_OIF:
0137                 sprintf(ifs, "%u",
0138                     *((int *)RTA_DATA(rt_attr)));
0139                 break;
0140             case RTA_METRICS:
0141                 sprintf(metrics, "%u",
0142                     *((int *)RTA_DATA(rt_attr)));
0143             default:
0144                 break;
0145             }
0146         }
0147         sprintf(dsts_len, "%d", rt_msg->rtm_dst_len);
0148         route.dst = atoi(dsts);
0149         route.dst_len = atoi(dsts_len);
0150         route.gw = atoi(gws);
0151         route.iface = atoi(ifs);
0152         route.metric = atoi(metrics);
0153         assert(get_mac_addr(route.iface, &route.mac) == 0);
0154         assert(bpf_map_update_elem(tx_port_map_fd,
0155                        &route.iface, &route.iface, 0) == 0);
0156         if (rtm_family == AF_INET) {
0157             struct trie_value {
0158                 __u8 prefix[4];
0159                 __be64 value;
0160                 int ifindex;
0161                 int metric;
0162                 __be32 gw;
0163             } *prefix_value;
0164 
0165             prefix_key = alloca(sizeof(*prefix_key) + 3);
0166             prefix_value = alloca(sizeof(*prefix_value));
0167 
0168             prefix_key->prefixlen = 32;
0169             prefix_key->prefixlen = route.dst_len;
0170             direct_entry.mac = route.mac & 0xffffffffffff;
0171             direct_entry.ifindex = route.iface;
0172             direct_entry.arp.mac = 0;
0173             direct_entry.arp.dst = 0;
0174             if (route.dst_len == 32) {
0175                 if (nh->nlmsg_type == RTM_DELROUTE) {
0176                     assert(bpf_map_delete_elem(exact_match_map_fd,
0177                                    &route.dst) == 0);
0178                 } else {
0179                     if (bpf_map_lookup_elem(arp_table_map_fd,
0180                                 &route.dst,
0181                                 &direct_entry.arp.mac) == 0)
0182                         direct_entry.arp.dst = route.dst;
0183                     assert(bpf_map_update_elem(exact_match_map_fd,
0184                                    &route.dst,
0185                                    &direct_entry, 0) == 0);
0186                 }
0187             }
0188             for (i = 0; i < 4; i++)
0189                 prefix_key->data[i] = (route.dst >> i * 8) & 0xff;
0190 
0191             if (bpf_map_lookup_elem(lpm_map_fd, prefix_key,
0192                         prefix_value) < 0) {
0193                 for (i = 0; i < 4; i++)
0194                     prefix_value->prefix[i] = prefix_key->data[i];
0195                 prefix_value->value = route.mac & 0xffffffffffff;
0196                 prefix_value->ifindex = route.iface;
0197                 prefix_value->gw = route.gw;
0198                 prefix_value->metric = route.metric;
0199 
0200                 assert(bpf_map_update_elem(lpm_map_fd,
0201                                prefix_key,
0202                                prefix_value, 0
0203                                ) == 0);
0204             } else {
0205                 if (nh->nlmsg_type == RTM_DELROUTE) {
0206                     assert(bpf_map_delete_elem(lpm_map_fd,
0207                                    prefix_key
0208                                    ) == 0);
0209                     /* Rereading the route table to check if
0210                      * there is an entry with the same
0211                      * prefix but a different metric as the
0212                      * deleted enty.
0213                      */
0214                     get_route_table(AF_INET);
0215                 } else if (prefix_key->data[0] ==
0216                        prefix_value->prefix[0] &&
0217                        prefix_key->data[1] ==
0218                        prefix_value->prefix[1] &&
0219                        prefix_key->data[2] ==
0220                        prefix_value->prefix[2] &&
0221                        prefix_key->data[3] ==
0222                        prefix_value->prefix[3] &&
0223                        route.metric >= prefix_value->metric) {
0224                     continue;
0225                 } else {
0226                     for (i = 0; i < 4; i++)
0227                         prefix_value->prefix[i] =
0228                             prefix_key->data[i];
0229                     prefix_value->value =
0230                         route.mac & 0xffffffffffff;
0231                     prefix_value->ifindex = route.iface;
0232                     prefix_value->gw = route.gw;
0233                     prefix_value->metric = route.metric;
0234                     assert(bpf_map_update_elem(lpm_map_fd,
0235                                    prefix_key,
0236                                    prefix_value,
0237                                    0) == 0);
0238                 }
0239             }
0240         }
0241         memset(&route, 0, sizeof(route));
0242         memset(dsts, 0, sizeof(dsts));
0243         memset(dsts_len, 0, sizeof(dsts_len));
0244         memset(gws, 0, sizeof(gws));
0245         memset(ifs, 0, sizeof(ifs));
0246         memset(&route, 0, sizeof(route));
0247     }
0248 }
0249 
0250 /* Function to read the existing route table  when the process is launched*/
0251 static int get_route_table(int rtm_family)
0252 {
0253     struct sockaddr_nl sa;
0254     struct nlmsghdr *nh;
0255     int sock, seq = 0;
0256     struct msghdr msg;
0257     struct iovec iov;
0258     int ret = 0;
0259     int nll;
0260 
0261     struct {
0262         struct nlmsghdr nl;
0263         struct rtmsg rt;
0264         char buf[8192];
0265     } req;
0266 
0267     sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
0268     if (sock < 0) {
0269         fprintf(stderr, "open netlink socket: %s\n", strerror(errno));
0270         return -errno;
0271     }
0272     memset(&sa, 0, sizeof(sa));
0273     sa.nl_family = AF_NETLINK;
0274     if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
0275         fprintf(stderr, "bind netlink socket: %s\n", strerror(errno));
0276         ret = -errno;
0277         goto cleanup;
0278     }
0279     memset(&req, 0, sizeof(req));
0280     req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
0281     req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
0282     req.nl.nlmsg_type = RTM_GETROUTE;
0283 
0284     req.rt.rtm_family = rtm_family;
0285     req.rt.rtm_table = RT_TABLE_MAIN;
0286     req.nl.nlmsg_pid = 0;
0287     req.nl.nlmsg_seq = ++seq;
0288     memset(&msg, 0, sizeof(msg));
0289     iov.iov_base = (void *)&req.nl;
0290     iov.iov_len = req.nl.nlmsg_len;
0291     msg.msg_iov = &iov;
0292     msg.msg_iovlen = 1;
0293     ret = sendmsg(sock, &msg, 0);
0294     if (ret < 0) {
0295         fprintf(stderr, "send to netlink: %s\n", strerror(errno));
0296         ret = -errno;
0297         goto cleanup;
0298     }
0299     memset(buf, 0, sizeof(buf));
0300     nll = recv_msg(sa, sock);
0301     if (nll < 0) {
0302         fprintf(stderr, "recv from netlink: %s\n", strerror(nll));
0303         ret = nll;
0304         goto cleanup;
0305     }
0306     nh = (struct nlmsghdr *)buf;
0307     read_route(nh, nll);
0308 cleanup:
0309     close(sock);
0310     return ret;
0311 }
0312 
0313 /* Function to parse the arp entry returned by netlink
0314  * Updates the arp entry related map entries
0315  */
0316 static void read_arp(struct nlmsghdr *nh, int nll)
0317 {
0318     struct rtattr *rt_attr;
0319     char dsts[24], mac[24];
0320     struct ndmsg *rt_msg;
0321     int rtl, ndm_family;
0322 
0323     struct arp_table {
0324         __be64 mac;
0325         __be32 dst;
0326     } arp_entry;
0327     struct direct_map {
0328         struct arp_table arp;
0329         int ifindex;
0330         __be64 mac;
0331     } direct_entry;
0332 
0333     for (; NLMSG_OK(nh, nll); nh = NLMSG_NEXT(nh, nll)) {
0334         rt_msg = (struct ndmsg *)NLMSG_DATA(nh);
0335         rt_attr = (struct rtattr *)RTM_RTA(rt_msg);
0336         ndm_family = rt_msg->ndm_family;
0337         rtl = RTM_PAYLOAD(nh);
0338         for (; RTA_OK(rt_attr, rtl); rt_attr = RTA_NEXT(rt_attr, rtl)) {
0339             switch (rt_attr->rta_type) {
0340             case NDA_DST:
0341                 sprintf(dsts, "%u",
0342                     *((__be32 *)RTA_DATA(rt_attr)));
0343                 break;
0344             case NDA_LLADDR:
0345                 sprintf(mac, "%lld",
0346                     *((__be64 *)RTA_DATA(rt_attr)));
0347                 break;
0348             default:
0349                 break;
0350             }
0351         }
0352         arp_entry.dst = atoi(dsts);
0353         arp_entry.mac = atol(mac);
0354 
0355         if (ndm_family == AF_INET) {
0356             if (bpf_map_lookup_elem(exact_match_map_fd,
0357                         &arp_entry.dst,
0358                         &direct_entry) == 0) {
0359                 if (nh->nlmsg_type == RTM_DELNEIGH) {
0360                     direct_entry.arp.dst = 0;
0361                     direct_entry.arp.mac = 0;
0362                 } else if (nh->nlmsg_type == RTM_NEWNEIGH) {
0363                     direct_entry.arp.dst = arp_entry.dst;
0364                     direct_entry.arp.mac = arp_entry.mac;
0365                 }
0366                 assert(bpf_map_update_elem(exact_match_map_fd,
0367                                &arp_entry.dst,
0368                                &direct_entry, 0
0369                                ) == 0);
0370                 memset(&direct_entry, 0, sizeof(direct_entry));
0371             }
0372             if (nh->nlmsg_type == RTM_DELNEIGH) {
0373                 assert(bpf_map_delete_elem(arp_table_map_fd,
0374                                &arp_entry.dst) == 0);
0375             } else if (nh->nlmsg_type == RTM_NEWNEIGH) {
0376                 assert(bpf_map_update_elem(arp_table_map_fd,
0377                                &arp_entry.dst,
0378                                &arp_entry.mac, 0
0379                                ) == 0);
0380             }
0381         }
0382         memset(&arp_entry, 0, sizeof(arp_entry));
0383         memset(dsts, 0, sizeof(dsts));
0384     }
0385 }
0386 
0387 /* Function to read the existing arp table  when the process is launched*/
0388 static int get_arp_table(int rtm_family)
0389 {
0390     struct sockaddr_nl sa;
0391     struct nlmsghdr *nh;
0392     int sock, seq = 0;
0393     struct msghdr msg;
0394     struct iovec iov;
0395     int ret = 0;
0396     int nll;
0397     struct {
0398         struct nlmsghdr nl;
0399         struct ndmsg rt;
0400         char buf[8192];
0401     } req;
0402 
0403     sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
0404     if (sock < 0) {
0405         fprintf(stderr, "open netlink socket: %s\n", strerror(errno));
0406         return -errno;
0407     }
0408     memset(&sa, 0, sizeof(sa));
0409     sa.nl_family = AF_NETLINK;
0410     if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
0411         fprintf(stderr, "bind netlink socket: %s\n", strerror(errno));
0412         ret = -errno;
0413         goto cleanup;
0414     }
0415     memset(&req, 0, sizeof(req));
0416     req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
0417     req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
0418     req.nl.nlmsg_type = RTM_GETNEIGH;
0419     req.rt.ndm_state = NUD_REACHABLE;
0420     req.rt.ndm_family = rtm_family;
0421     req.nl.nlmsg_pid = 0;
0422     req.nl.nlmsg_seq = ++seq;
0423     memset(&msg, 0, sizeof(msg));
0424     iov.iov_base = (void *)&req.nl;
0425     iov.iov_len = req.nl.nlmsg_len;
0426     msg.msg_iov = &iov;
0427     msg.msg_iovlen = 1;
0428     ret = sendmsg(sock, &msg, 0);
0429     if (ret < 0) {
0430         fprintf(stderr, "send to netlink: %s\n", strerror(errno));
0431         ret = -errno;
0432         goto cleanup;
0433     }
0434     memset(buf, 0, sizeof(buf));
0435     nll = recv_msg(sa, sock);
0436     if (nll < 0) {
0437         fprintf(stderr, "recv from netlink: %s\n", strerror(nll));
0438         ret = nll;
0439         goto cleanup;
0440     }
0441     nh = (struct nlmsghdr *)buf;
0442     read_arp(nh, nll);
0443 cleanup:
0444     close(sock);
0445     return ret;
0446 }
0447 
0448 /* Function to keep track and update changes in route and arp table
0449  * Give regular statistics of packets forwarded
0450  */
0451 static void *monitor_routes_thread(void *arg)
0452 {
0453     struct pollfd fds_route, fds_arp;
0454     struct sockaddr_nl la, lr;
0455     int sock, sock_arp, nll;
0456     struct nlmsghdr *nh;
0457 
0458     sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
0459     if (sock < 0) {
0460         fprintf(stderr, "open netlink socket: %s\n", strerror(errno));
0461         return NULL;
0462     }
0463 
0464     fcntl(sock, F_SETFL, O_NONBLOCK);
0465     memset(&lr, 0, sizeof(lr));
0466     lr.nl_family = AF_NETLINK;
0467     lr.nl_groups = RTMGRP_IPV6_ROUTE | RTMGRP_IPV4_ROUTE | RTMGRP_NOTIFY;
0468     if (bind(sock, (struct sockaddr *)&lr, sizeof(lr)) < 0) {
0469         fprintf(stderr, "bind netlink socket: %s\n", strerror(errno));
0470         close(sock);
0471         return NULL;
0472     }
0473 
0474     fds_route.fd = sock;
0475     fds_route.events = POLL_IN;
0476 
0477     sock_arp = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
0478     if (sock_arp < 0) {
0479         fprintf(stderr, "open netlink socket: %s\n", strerror(errno));
0480         close(sock);
0481         return NULL;
0482     }
0483 
0484     fcntl(sock_arp, F_SETFL, O_NONBLOCK);
0485     memset(&la, 0, sizeof(la));
0486     la.nl_family = AF_NETLINK;
0487     la.nl_groups = RTMGRP_NEIGH | RTMGRP_NOTIFY;
0488     if (bind(sock_arp, (struct sockaddr *)&la, sizeof(la)) < 0) {
0489         fprintf(stderr, "bind netlink socket: %s\n", strerror(errno));
0490         goto cleanup;
0491     }
0492 
0493     fds_arp.fd = sock_arp;
0494     fds_arp.events = POLL_IN;
0495 
0496     /* dump route and arp tables */
0497     if (get_arp_table(AF_INET) < 0) {
0498         fprintf(stderr, "Failed reading arp table\n");
0499         goto cleanup;
0500     }
0501 
0502     if (get_route_table(AF_INET) < 0) {
0503         fprintf(stderr, "Failed reading route table\n");
0504         goto cleanup;
0505     }
0506 
0507     while (!routes_thread_exit) {
0508         memset(buf, 0, sizeof(buf));
0509         if (poll(&fds_route, 1, 3) == POLL_IN) {
0510             nll = recv_msg(lr, sock);
0511             if (nll < 0) {
0512                 fprintf(stderr, "recv from netlink: %s\n",
0513                     strerror(nll));
0514                 goto cleanup;
0515             }
0516 
0517             nh = (struct nlmsghdr *)buf;
0518             read_route(nh, nll);
0519         }
0520 
0521         memset(buf, 0, sizeof(buf));
0522         if (poll(&fds_arp, 1, 3) == POLL_IN) {
0523             nll = recv_msg(la, sock_arp);
0524             if (nll < 0) {
0525                 fprintf(stderr, "recv from netlink: %s\n",
0526                     strerror(nll));
0527                 goto cleanup;
0528             }
0529 
0530             nh = (struct nlmsghdr *)buf;
0531             read_arp(nh, nll);
0532         }
0533 
0534         sleep(interval);
0535     }
0536 
0537 cleanup:
0538     close(sock_arp);
0539     close(sock);
0540     return NULL;
0541 }
0542 
0543 static void usage(char *argv[], const struct option *long_options,
0544           const char *doc, int mask, bool error,
0545           struct bpf_object *obj)
0546 {
0547     sample_usage(argv, long_options, doc, mask, error);
0548 }
0549 
0550 int main(int argc, char **argv)
0551 {
0552     bool error = true, generic = false, force = false;
0553     int opt, ret = EXIT_FAIL_BPF;
0554     struct xdp_router_ipv4 *skel;
0555     int i, total_ifindex = argc - 1;
0556     char **ifname_list = argv + 1;
0557     pthread_t routes_thread;
0558     int longindex = 0;
0559 
0560     if (libbpf_set_strict_mode(LIBBPF_STRICT_ALL) < 0) {
0561         fprintf(stderr, "Failed to set libbpf strict mode: %s\n",
0562             strerror(errno));
0563         goto end;
0564     }
0565 
0566     skel = xdp_router_ipv4__open();
0567     if (!skel) {
0568         fprintf(stderr, "Failed to xdp_router_ipv4__open: %s\n",
0569             strerror(errno));
0570         goto end;
0571     }
0572 
0573     ret = sample_init_pre_load(skel);
0574     if (ret < 0) {
0575         fprintf(stderr, "Failed to sample_init_pre_load: %s\n",
0576             strerror(-ret));
0577         ret = EXIT_FAIL_BPF;
0578         goto end_destroy;
0579     }
0580 
0581     ret = xdp_router_ipv4__load(skel);
0582     if (ret < 0) {
0583         fprintf(stderr, "Failed to xdp_router_ipv4__load: %s\n",
0584             strerror(errno));
0585         goto end_destroy;
0586     }
0587 
0588     ret = sample_init(skel, mask);
0589     if (ret < 0) {
0590         fprintf(stderr, "Failed to initialize sample: %s\n", strerror(-ret));
0591         ret = EXIT_FAIL;
0592         goto end_destroy;
0593     }
0594 
0595     while ((opt = getopt_long(argc, argv, "si:SFvh",
0596                   long_options, &longindex)) != -1) {
0597         switch (opt) {
0598         case 's':
0599             mask |= SAMPLE_REDIRECT_MAP_CNT;
0600             total_ifindex--;
0601             ifname_list++;
0602             break;
0603         case 'i':
0604             interval = strtoul(optarg, NULL, 0);
0605             total_ifindex -= 2;
0606             ifname_list += 2;
0607             break;
0608         case 'S':
0609             generic = true;
0610             total_ifindex--;
0611             ifname_list++;
0612             break;
0613         case 'F':
0614             force = true;
0615             total_ifindex--;
0616             ifname_list++;
0617             break;
0618         case 'v':
0619             sample_switch_mode();
0620             total_ifindex--;
0621             ifname_list++;
0622             break;
0623         case 'h':
0624             error = false;
0625         default:
0626             usage(argv, long_options, __doc__, mask, error, skel->obj);
0627             goto end_destroy;
0628         }
0629     }
0630 
0631     ret = EXIT_FAIL_OPTION;
0632     if (optind == argc) {
0633         usage(argv, long_options, __doc__, mask, true, skel->obj);
0634         goto end_destroy;
0635     }
0636 
0637     lpm_map_fd = bpf_map__fd(skel->maps.lpm_map);
0638     if (lpm_map_fd < 0) {
0639         fprintf(stderr, "Failed loading lpm_map %s\n",
0640             strerror(-lpm_map_fd));
0641         goto end_destroy;
0642     }
0643     arp_table_map_fd = bpf_map__fd(skel->maps.arp_table);
0644     if (arp_table_map_fd < 0) {
0645         fprintf(stderr, "Failed loading arp_table_map_fd %s\n",
0646             strerror(-arp_table_map_fd));
0647         goto end_destroy;
0648     }
0649     exact_match_map_fd = bpf_map__fd(skel->maps.exact_match);
0650     if (exact_match_map_fd < 0) {
0651         fprintf(stderr, "Failed loading exact_match_map_fd %s\n",
0652             strerror(-exact_match_map_fd));
0653         goto end_destroy;
0654     }
0655     tx_port_map_fd = bpf_map__fd(skel->maps.tx_port);
0656     if (tx_port_map_fd < 0) {
0657         fprintf(stderr, "Failed loading tx_port_map_fd %s\n",
0658             strerror(-tx_port_map_fd));
0659         goto end_destroy;
0660     }
0661 
0662     ret = EXIT_FAIL_XDP;
0663     for (i = 0; i < total_ifindex; i++) {
0664         int index = if_nametoindex(ifname_list[i]);
0665 
0666         if (!index) {
0667             fprintf(stderr, "Interface %s not found %s\n",
0668                 ifname_list[i], strerror(-tx_port_map_fd));
0669             goto end_destroy;
0670         }
0671         if (sample_install_xdp(skel->progs.xdp_router_ipv4_prog,
0672                        index, generic, force) < 0)
0673             goto end_destroy;
0674     }
0675 
0676     ret = pthread_create(&routes_thread, NULL, monitor_routes_thread, NULL);
0677     if (ret) {
0678         fprintf(stderr, "Failed creating routes_thread: %s\n", strerror(-ret));
0679         ret = EXIT_FAIL;
0680         goto end_destroy;
0681     }
0682 
0683     ret = sample_run(interval, NULL, NULL);
0684     routes_thread_exit = true;
0685 
0686     if (ret < 0) {
0687         fprintf(stderr, "Failed during sample run: %s\n", strerror(-ret));
0688         ret = EXIT_FAIL;
0689         goto end_thread_wait;
0690     }
0691     ret = EXIT_OK;
0692 
0693 end_thread_wait:
0694     pthread_join(routes_thread, NULL);
0695 end_destroy:
0696     xdp_router_ipv4__destroy(skel);
0697 end:
0698     sample_exit(ret);
0699 }