xref: /linux/samples/bpf/xdp_router_ipv4_user.c (revision 81ee0eb6c0fe34490ed92667538197d9295e899e)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (C) 2017 Cavium, Inc.
3  */
4 #include <linux/bpf.h>
5 #include <linux/netlink.h>
6 #include <linux/rtnetlink.h>
7 #include <assert.h>
8 #include <errno.h>
9 #include <signal.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/socket.h>
14 #include <unistd.h>
15 #include <bpf/bpf.h>
16 #include <arpa/inet.h>
17 #include <fcntl.h>
18 #include <poll.h>
19 #include <net/if.h>
20 #include <netdb.h>
21 #include <sys/ioctl.h>
22 #include <sys/syscall.h>
23 #include "bpf_util.h"
24 #include <bpf/libbpf.h>
25 #include <sys/resource.h>
26 #include <libgen.h>
27 #include <getopt.h>
28 #include <pthread.h>
29 #include "xdp_sample_user.h"
30 #include "xdp_router_ipv4.skel.h"
31 
32 static const char *__doc__ =
33 "XDP IPv4 router implementation\n"
34 "Usage: xdp_router_ipv4 <IFNAME-0> ... <IFNAME-N>\n";
35 
36 static char buf[8192];
37 static int lpm_map_fd;
38 static int arp_table_map_fd;
39 static int exact_match_map_fd;
40 static int tx_port_map_fd;
41 
42 static bool routes_thread_exit;
43 static int interval = 5;
44 
45 static int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_MAP_CNT |
46 		  SAMPLE_DEVMAP_XMIT_CNT_MULTI | SAMPLE_EXCEPTION_CNT;
47 
48 DEFINE_SAMPLE_INIT(xdp_router_ipv4);
49 
50 static const struct option long_options[] = {
51 	{ "help", no_argument, NULL, 'h' },
52 	{ "skb-mode", no_argument, NULL, 'S' },
53 	{ "force", no_argument, NULL, 'F' },
54 	{ "interval", required_argument, NULL, 'i' },
55 	{ "verbose", no_argument, NULL, 'v' },
56 	{ "stats", no_argument, NULL, 's' },
57 	{}
58 };
59 
60 static int get_route_table(int rtm_family);
61 
62 static int recv_msg(struct sockaddr_nl sock_addr, int sock)
63 {
64 	struct nlmsghdr *nh;
65 	int len, nll = 0;
66 	char *buf_ptr;
67 
68 	buf_ptr = buf;
69 	while (1) {
70 		len = recv(sock, buf_ptr, sizeof(buf) - nll, 0);
71 		if (len < 0)
72 			return len;
73 
74 		nh = (struct nlmsghdr *)buf_ptr;
75 
76 		if (nh->nlmsg_type == NLMSG_DONE)
77 			break;
78 		buf_ptr += len;
79 		nll += len;
80 		if ((sock_addr.nl_groups & RTMGRP_NEIGH) == RTMGRP_NEIGH)
81 			break;
82 
83 		if ((sock_addr.nl_groups & RTMGRP_IPV4_ROUTE) == RTMGRP_IPV4_ROUTE)
84 			break;
85 	}
86 	return nll;
87 }
88 
89 /* Function to parse the route entry returned by netlink
90  * Updates the route entry related map entries
91  */
92 static void read_route(struct nlmsghdr *nh, int nll)
93 {
94 	char dsts[24], gws[24], ifs[16], dsts_len[24], metrics[24];
95 	struct bpf_lpm_trie_key *prefix_key;
96 	struct rtattr *rt_attr;
97 	struct rtmsg *rt_msg;
98 	int rtm_family;
99 	int rtl;
100 	int i;
101 	struct route_table {
102 		int  dst_len, iface, metric;
103 		__be32 dst, gw;
104 		__be64 mac;
105 	} route;
106 	struct arp_table {
107 		__be64 mac;
108 		__be32 dst;
109 	};
110 
111 	struct direct_map {
112 		struct arp_table arp;
113 		int ifindex;
114 		__be64 mac;
115 	} direct_entry;
116 
117 	memset(&route, 0, sizeof(route));
118 	for (; NLMSG_OK(nh, nll); nh = NLMSG_NEXT(nh, nll)) {
119 		rt_msg = (struct rtmsg *)NLMSG_DATA(nh);
120 		rtm_family = rt_msg->rtm_family;
121 		if (rtm_family == AF_INET)
122 			if (rt_msg->rtm_table != RT_TABLE_MAIN)
123 				continue;
124 		rt_attr = (struct rtattr *)RTM_RTA(rt_msg);
125 		rtl = RTM_PAYLOAD(nh);
126 
127 		for (; RTA_OK(rt_attr, rtl); rt_attr = RTA_NEXT(rt_attr, rtl)) {
128 			switch (rt_attr->rta_type) {
129 			case NDA_DST:
130 				sprintf(dsts, "%u",
131 					(*((__be32 *)RTA_DATA(rt_attr))));
132 				break;
133 			case RTA_GATEWAY:
134 				sprintf(gws, "%u",
135 					*((__be32 *)RTA_DATA(rt_attr)));
136 				break;
137 			case RTA_OIF:
138 				sprintf(ifs, "%u",
139 					*((int *)RTA_DATA(rt_attr)));
140 				break;
141 			case RTA_METRICS:
142 				sprintf(metrics, "%u",
143 					*((int *)RTA_DATA(rt_attr)));
144 			default:
145 				break;
146 			}
147 		}
148 		sprintf(dsts_len, "%d", rt_msg->rtm_dst_len);
149 		route.dst = atoi(dsts);
150 		route.dst_len = atoi(dsts_len);
151 		route.gw = atoi(gws);
152 		route.iface = atoi(ifs);
153 		route.metric = atoi(metrics);
154 		assert(get_mac_addr(route.iface, &route.mac) == 0);
155 		assert(bpf_map_update_elem(tx_port_map_fd,
156 					   &route.iface, &route.iface, 0) == 0);
157 		if (rtm_family == AF_INET) {
158 			struct trie_value {
159 				__u8 prefix[4];
160 				__be64 value;
161 				int ifindex;
162 				int metric;
163 				__be32 gw;
164 			} *prefix_value;
165 
166 			prefix_key = alloca(sizeof(*prefix_key) + 3);
167 			prefix_value = alloca(sizeof(*prefix_value));
168 
169 			prefix_key->prefixlen = 32;
170 			prefix_key->prefixlen = route.dst_len;
171 			direct_entry.mac = route.mac & 0xffffffffffff;
172 			direct_entry.ifindex = route.iface;
173 			direct_entry.arp.mac = 0;
174 			direct_entry.arp.dst = 0;
175 			if (route.dst_len == 32) {
176 				if (nh->nlmsg_type == RTM_DELROUTE) {
177 					assert(bpf_map_delete_elem(exact_match_map_fd,
178 								   &route.dst) == 0);
179 				} else {
180 					if (bpf_map_lookup_elem(arp_table_map_fd,
181 								&route.dst,
182 								&direct_entry.arp.mac) == 0)
183 						direct_entry.arp.dst = route.dst;
184 					assert(bpf_map_update_elem(exact_match_map_fd,
185 								   &route.dst,
186 								   &direct_entry, 0) == 0);
187 				}
188 			}
189 			for (i = 0; i < 4; i++)
190 				prefix_key->data[i] = (route.dst >> i * 8) & 0xff;
191 
192 			if (bpf_map_lookup_elem(lpm_map_fd, prefix_key,
193 						prefix_value) < 0) {
194 				for (i = 0; i < 4; i++)
195 					prefix_value->prefix[i] = prefix_key->data[i];
196 				prefix_value->value = route.mac & 0xffffffffffff;
197 				prefix_value->ifindex = route.iface;
198 				prefix_value->gw = route.gw;
199 				prefix_value->metric = route.metric;
200 
201 				assert(bpf_map_update_elem(lpm_map_fd,
202 							   prefix_key,
203 							   prefix_value, 0
204 							   ) == 0);
205 			} else {
206 				if (nh->nlmsg_type == RTM_DELROUTE) {
207 					assert(bpf_map_delete_elem(lpm_map_fd,
208 								   prefix_key
209 								   ) == 0);
210 					/* Rereading the route table to check if
211 					 * there is an entry with the same
212 					 * prefix but a different metric as the
213 					 * deleted enty.
214 					 */
215 					get_route_table(AF_INET);
216 				} else if (prefix_key->data[0] ==
217 					   prefix_value->prefix[0] &&
218 					   prefix_key->data[1] ==
219 					   prefix_value->prefix[1] &&
220 					   prefix_key->data[2] ==
221 					   prefix_value->prefix[2] &&
222 					   prefix_key->data[3] ==
223 					   prefix_value->prefix[3] &&
224 					   route.metric >= prefix_value->metric) {
225 					continue;
226 				} else {
227 					for (i = 0; i < 4; i++)
228 						prefix_value->prefix[i] =
229 							prefix_key->data[i];
230 					prefix_value->value =
231 						route.mac & 0xffffffffffff;
232 					prefix_value->ifindex = route.iface;
233 					prefix_value->gw = route.gw;
234 					prefix_value->metric = route.metric;
235 					assert(bpf_map_update_elem(lpm_map_fd,
236 								   prefix_key,
237 								   prefix_value,
238 								   0) == 0);
239 				}
240 			}
241 		}
242 		memset(&route, 0, sizeof(route));
243 		memset(dsts, 0, sizeof(dsts));
244 		memset(dsts_len, 0, sizeof(dsts_len));
245 		memset(gws, 0, sizeof(gws));
246 		memset(ifs, 0, sizeof(ifs));
247 		memset(&route, 0, sizeof(route));
248 	}
249 }
250 
251 /* Function to read the existing route table  when the process is launched*/
252 static int get_route_table(int rtm_family)
253 {
254 	struct sockaddr_nl sa;
255 	struct nlmsghdr *nh;
256 	int sock, seq = 0;
257 	struct msghdr msg;
258 	struct iovec iov;
259 	int ret = 0;
260 	int nll;
261 
262 	struct {
263 		struct nlmsghdr nl;
264 		struct rtmsg rt;
265 		char buf[8192];
266 	} req;
267 
268 	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
269 	if (sock < 0) {
270 		fprintf(stderr, "open netlink socket: %s\n", strerror(errno));
271 		return -errno;
272 	}
273 	memset(&sa, 0, sizeof(sa));
274 	sa.nl_family = AF_NETLINK;
275 	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
276 		fprintf(stderr, "bind netlink socket: %s\n", strerror(errno));
277 		ret = -errno;
278 		goto cleanup;
279 	}
280 	memset(&req, 0, sizeof(req));
281 	req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
282 	req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
283 	req.nl.nlmsg_type = RTM_GETROUTE;
284 
285 	req.rt.rtm_family = rtm_family;
286 	req.rt.rtm_table = RT_TABLE_MAIN;
287 	req.nl.nlmsg_pid = 0;
288 	req.nl.nlmsg_seq = ++seq;
289 	memset(&msg, 0, sizeof(msg));
290 	iov.iov_base = (void *)&req.nl;
291 	iov.iov_len = req.nl.nlmsg_len;
292 	msg.msg_iov = &iov;
293 	msg.msg_iovlen = 1;
294 	ret = sendmsg(sock, &msg, 0);
295 	if (ret < 0) {
296 		fprintf(stderr, "send to netlink: %s\n", strerror(errno));
297 		ret = -errno;
298 		goto cleanup;
299 	}
300 	memset(buf, 0, sizeof(buf));
301 	nll = recv_msg(sa, sock);
302 	if (nll < 0) {
303 		fprintf(stderr, "recv from netlink: %s\n", strerror(nll));
304 		ret = nll;
305 		goto cleanup;
306 	}
307 	nh = (struct nlmsghdr *)buf;
308 	read_route(nh, nll);
309 cleanup:
310 	close(sock);
311 	return ret;
312 }
313 
314 /* Function to parse the arp entry returned by netlink
315  * Updates the arp entry related map entries
316  */
317 static void read_arp(struct nlmsghdr *nh, int nll)
318 {
319 	struct rtattr *rt_attr;
320 	char dsts[24], mac[24];
321 	struct ndmsg *rt_msg;
322 	int rtl, ndm_family;
323 
324 	struct arp_table {
325 		__be64 mac;
326 		__be32 dst;
327 	} arp_entry;
328 	struct direct_map {
329 		struct arp_table arp;
330 		int ifindex;
331 		__be64 mac;
332 	} direct_entry;
333 
334 	for (; NLMSG_OK(nh, nll); nh = NLMSG_NEXT(nh, nll)) {
335 		rt_msg = (struct ndmsg *)NLMSG_DATA(nh);
336 		rt_attr = (struct rtattr *)RTM_RTA(rt_msg);
337 		ndm_family = rt_msg->ndm_family;
338 		rtl = RTM_PAYLOAD(nh);
339 		for (; RTA_OK(rt_attr, rtl); rt_attr = RTA_NEXT(rt_attr, rtl)) {
340 			switch (rt_attr->rta_type) {
341 			case NDA_DST:
342 				sprintf(dsts, "%u",
343 					*((__be32 *)RTA_DATA(rt_attr)));
344 				break;
345 			case NDA_LLADDR:
346 				sprintf(mac, "%lld",
347 					*((__be64 *)RTA_DATA(rt_attr)));
348 				break;
349 			default:
350 				break;
351 			}
352 		}
353 		arp_entry.dst = atoi(dsts);
354 		arp_entry.mac = atol(mac);
355 
356 		if (ndm_family == AF_INET) {
357 			if (bpf_map_lookup_elem(exact_match_map_fd,
358 						&arp_entry.dst,
359 						&direct_entry) == 0) {
360 				if (nh->nlmsg_type == RTM_DELNEIGH) {
361 					direct_entry.arp.dst = 0;
362 					direct_entry.arp.mac = 0;
363 				} else if (nh->nlmsg_type == RTM_NEWNEIGH) {
364 					direct_entry.arp.dst = arp_entry.dst;
365 					direct_entry.arp.mac = arp_entry.mac;
366 				}
367 				assert(bpf_map_update_elem(exact_match_map_fd,
368 							   &arp_entry.dst,
369 							   &direct_entry, 0
370 							   ) == 0);
371 				memset(&direct_entry, 0, sizeof(direct_entry));
372 			}
373 			if (nh->nlmsg_type == RTM_DELNEIGH) {
374 				assert(bpf_map_delete_elem(arp_table_map_fd,
375 							   &arp_entry.dst) == 0);
376 			} else if (nh->nlmsg_type == RTM_NEWNEIGH) {
377 				assert(bpf_map_update_elem(arp_table_map_fd,
378 							   &arp_entry.dst,
379 							   &arp_entry.mac, 0
380 							   ) == 0);
381 			}
382 		}
383 		memset(&arp_entry, 0, sizeof(arp_entry));
384 		memset(dsts, 0, sizeof(dsts));
385 	}
386 }
387 
388 /* Function to read the existing arp table  when the process is launched*/
389 static int get_arp_table(int rtm_family)
390 {
391 	struct sockaddr_nl sa;
392 	struct nlmsghdr *nh;
393 	int sock, seq = 0;
394 	struct msghdr msg;
395 	struct iovec iov;
396 	int ret = 0;
397 	int nll;
398 	struct {
399 		struct nlmsghdr nl;
400 		struct ndmsg rt;
401 		char buf[8192];
402 	} req;
403 
404 	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
405 	if (sock < 0) {
406 		fprintf(stderr, "open netlink socket: %s\n", strerror(errno));
407 		return -errno;
408 	}
409 	memset(&sa, 0, sizeof(sa));
410 	sa.nl_family = AF_NETLINK;
411 	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
412 		fprintf(stderr, "bind netlink socket: %s\n", strerror(errno));
413 		ret = -errno;
414 		goto cleanup;
415 	}
416 	memset(&req, 0, sizeof(req));
417 	req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
418 	req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
419 	req.nl.nlmsg_type = RTM_GETNEIGH;
420 	req.rt.ndm_state = NUD_REACHABLE;
421 	req.rt.ndm_family = rtm_family;
422 	req.nl.nlmsg_pid = 0;
423 	req.nl.nlmsg_seq = ++seq;
424 	memset(&msg, 0, sizeof(msg));
425 	iov.iov_base = (void *)&req.nl;
426 	iov.iov_len = req.nl.nlmsg_len;
427 	msg.msg_iov = &iov;
428 	msg.msg_iovlen = 1;
429 	ret = sendmsg(sock, &msg, 0);
430 	if (ret < 0) {
431 		fprintf(stderr, "send to netlink: %s\n", strerror(errno));
432 		ret = -errno;
433 		goto cleanup;
434 	}
435 	memset(buf, 0, sizeof(buf));
436 	nll = recv_msg(sa, sock);
437 	if (nll < 0) {
438 		fprintf(stderr, "recv from netlink: %s\n", strerror(nll));
439 		ret = nll;
440 		goto cleanup;
441 	}
442 	nh = (struct nlmsghdr *)buf;
443 	read_arp(nh, nll);
444 cleanup:
445 	close(sock);
446 	return ret;
447 }
448 
449 /* Function to keep track and update changes in route and arp table
450  * Give regular statistics of packets forwarded
451  */
452 static void *monitor_routes_thread(void *arg)
453 {
454 	struct pollfd fds_route, fds_arp;
455 	struct sockaddr_nl la, lr;
456 	int sock, sock_arp, nll;
457 	struct nlmsghdr *nh;
458 
459 	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
460 	if (sock < 0) {
461 		fprintf(stderr, "open netlink socket: %s\n", strerror(errno));
462 		return NULL;
463 	}
464 
465 	fcntl(sock, F_SETFL, O_NONBLOCK);
466 	memset(&lr, 0, sizeof(lr));
467 	lr.nl_family = AF_NETLINK;
468 	lr.nl_groups = RTMGRP_IPV6_ROUTE | RTMGRP_IPV4_ROUTE | RTMGRP_NOTIFY;
469 	if (bind(sock, (struct sockaddr *)&lr, sizeof(lr)) < 0) {
470 		fprintf(stderr, "bind netlink socket: %s\n", strerror(errno));
471 		close(sock);
472 		return NULL;
473 	}
474 
475 	fds_route.fd = sock;
476 	fds_route.events = POLL_IN;
477 
478 	sock_arp = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
479 	if (sock_arp < 0) {
480 		fprintf(stderr, "open netlink socket: %s\n", strerror(errno));
481 		close(sock);
482 		return NULL;
483 	}
484 
485 	fcntl(sock_arp, F_SETFL, O_NONBLOCK);
486 	memset(&la, 0, sizeof(la));
487 	la.nl_family = AF_NETLINK;
488 	la.nl_groups = RTMGRP_NEIGH | RTMGRP_NOTIFY;
489 	if (bind(sock_arp, (struct sockaddr *)&la, sizeof(la)) < 0) {
490 		fprintf(stderr, "bind netlink socket: %s\n", strerror(errno));
491 		goto cleanup;
492 	}
493 
494 	fds_arp.fd = sock_arp;
495 	fds_arp.events = POLL_IN;
496 
497 	/* dump route and arp tables */
498 	if (get_arp_table(AF_INET) < 0) {
499 		fprintf(stderr, "Failed reading arp table\n");
500 		goto cleanup;
501 	}
502 
503 	if (get_route_table(AF_INET) < 0) {
504 		fprintf(stderr, "Failed reading route table\n");
505 		goto cleanup;
506 	}
507 
508 	while (!routes_thread_exit) {
509 		memset(buf, 0, sizeof(buf));
510 		if (poll(&fds_route, 1, 3) == POLL_IN) {
511 			nll = recv_msg(lr, sock);
512 			if (nll < 0) {
513 				fprintf(stderr, "recv from netlink: %s\n",
514 					strerror(nll));
515 				goto cleanup;
516 			}
517 
518 			nh = (struct nlmsghdr *)buf;
519 			read_route(nh, nll);
520 		}
521 
522 		memset(buf, 0, sizeof(buf));
523 		if (poll(&fds_arp, 1, 3) == POLL_IN) {
524 			nll = recv_msg(la, sock_arp);
525 			if (nll < 0) {
526 				fprintf(stderr, "recv from netlink: %s\n",
527 					strerror(nll));
528 				goto cleanup;
529 			}
530 
531 			nh = (struct nlmsghdr *)buf;
532 			read_arp(nh, nll);
533 		}
534 
535 		sleep(interval);
536 	}
537 
538 cleanup:
539 	close(sock_arp);
540 	close(sock);
541 	return NULL;
542 }
543 
544 static void usage(char *argv[], const struct option *long_options,
545 		  const char *doc, int mask, bool error,
546 		  struct bpf_object *obj)
547 {
548 	sample_usage(argv, long_options, doc, mask, error);
549 }
550 
551 int main(int argc, char **argv)
552 {
553 	bool error = true, generic = false, force = false;
554 	int opt, ret = EXIT_FAIL_BPF;
555 	struct xdp_router_ipv4 *skel;
556 	int i, total_ifindex = argc - 1;
557 	char **ifname_list = argv + 1;
558 	pthread_t routes_thread;
559 	int longindex = 0;
560 
561 	if (libbpf_set_strict_mode(LIBBPF_STRICT_ALL) < 0) {
562 		fprintf(stderr, "Failed to set libbpf strict mode: %s\n",
563 			strerror(errno));
564 		goto end;
565 	}
566 
567 	skel = xdp_router_ipv4__open();
568 	if (!skel) {
569 		fprintf(stderr, "Failed to xdp_router_ipv4__open: %s\n",
570 			strerror(errno));
571 		goto end;
572 	}
573 
574 	ret = sample_init_pre_load(skel);
575 	if (ret < 0) {
576 		fprintf(stderr, "Failed to sample_init_pre_load: %s\n",
577 			strerror(-ret));
578 		ret = EXIT_FAIL_BPF;
579 		goto end_destroy;
580 	}
581 
582 	ret = xdp_router_ipv4__load(skel);
583 	if (ret < 0) {
584 		fprintf(stderr, "Failed to xdp_router_ipv4__load: %s\n",
585 			strerror(errno));
586 		goto end_destroy;
587 	}
588 
589 	ret = sample_init(skel, mask);
590 	if (ret < 0) {
591 		fprintf(stderr, "Failed to initialize sample: %s\n", strerror(-ret));
592 		ret = EXIT_FAIL;
593 		goto end_destroy;
594 	}
595 
596 	while ((opt = getopt_long(argc, argv, "si:SFvh",
597 				  long_options, &longindex)) != -1) {
598 		switch (opt) {
599 		case 's':
600 			mask |= SAMPLE_REDIRECT_MAP_CNT;
601 			total_ifindex--;
602 			ifname_list++;
603 			break;
604 		case 'i':
605 			interval = strtoul(optarg, NULL, 0);
606 			total_ifindex -= 2;
607 			ifname_list += 2;
608 			break;
609 		case 'S':
610 			generic = true;
611 			total_ifindex--;
612 			ifname_list++;
613 			break;
614 		case 'F':
615 			force = true;
616 			total_ifindex--;
617 			ifname_list++;
618 			break;
619 		case 'v':
620 			sample_switch_mode();
621 			total_ifindex--;
622 			ifname_list++;
623 			break;
624 		case 'h':
625 			error = false;
626 		default:
627 			usage(argv, long_options, __doc__, mask, error, skel->obj);
628 			goto end_destroy;
629 		}
630 	}
631 
632 	ret = EXIT_FAIL_OPTION;
633 	if (optind == argc) {
634 		usage(argv, long_options, __doc__, mask, true, skel->obj);
635 		goto end_destroy;
636 	}
637 
638 	lpm_map_fd = bpf_map__fd(skel->maps.lpm_map);
639 	if (lpm_map_fd < 0) {
640 		fprintf(stderr, "Failed loading lpm_map %s\n",
641 			strerror(-lpm_map_fd));
642 		goto end_destroy;
643 	}
644 	arp_table_map_fd = bpf_map__fd(skel->maps.arp_table);
645 	if (arp_table_map_fd < 0) {
646 		fprintf(stderr, "Failed loading arp_table_map_fd %s\n",
647 			strerror(-arp_table_map_fd));
648 		goto end_destroy;
649 	}
650 	exact_match_map_fd = bpf_map__fd(skel->maps.exact_match);
651 	if (exact_match_map_fd < 0) {
652 		fprintf(stderr, "Failed loading exact_match_map_fd %s\n",
653 			strerror(-exact_match_map_fd));
654 		goto end_destroy;
655 	}
656 	tx_port_map_fd = bpf_map__fd(skel->maps.tx_port);
657 	if (tx_port_map_fd < 0) {
658 		fprintf(stderr, "Failed loading tx_port_map_fd %s\n",
659 			strerror(-tx_port_map_fd));
660 		goto end_destroy;
661 	}
662 
663 	ret = EXIT_FAIL_XDP;
664 	for (i = 0; i < total_ifindex; i++) {
665 		int index = if_nametoindex(ifname_list[i]);
666 
667 		if (!index) {
668 			fprintf(stderr, "Interface %s not found %s\n",
669 				ifname_list[i], strerror(-tx_port_map_fd));
670 			goto end_destroy;
671 		}
672 		if (sample_install_xdp(skel->progs.xdp_router_ipv4_prog,
673 				       index, generic, force) < 0)
674 			goto end_destroy;
675 	}
676 
677 	ret = pthread_create(&routes_thread, NULL, monitor_routes_thread, NULL);
678 	if (ret) {
679 		fprintf(stderr, "Failed creating routes_thread: %s\n", strerror(-ret));
680 		ret = EXIT_FAIL;
681 		goto end_destroy;
682 	}
683 
684 	ret = sample_run(interval, NULL, NULL);
685 	routes_thread_exit = true;
686 
687 	if (ret < 0) {
688 		fprintf(stderr, "Failed during sample run: %s\n", strerror(-ret));
689 		ret = EXIT_FAIL;
690 		goto end_thread_wait;
691 	}
692 	ret = EXIT_OK;
693 
694 end_thread_wait:
695 	pthread_join(routes_thread, NULL);
696 end_destroy:
697 	xdp_router_ipv4__destroy(skel);
698 end:
699 	sample_exit(ret);
700 }
701