OpenVPN
networking_sitnl.c
Go to the documentation of this file.
1/*
2 * Simplified Interface To NetLink
3 *
4 * Copyright (C) 2016-2026 Antonio Quartulli <a@unstable.cc>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program (see the file COPYING included with this
17 * distribution); if not, see <https://www.gnu.org/licenses/>.
18 */
19
20#ifdef HAVE_CONFIG_H
21#include "config.h"
22#endif
23
24#ifdef TARGET_LINUX
25
26#include "syshead.h"
27
28#include "dco.h"
29#include "errlevel.h"
30#include "fdmisc.h"
31#include "buffer.h"
32#include "misc.h"
33#include "networking.h"
34#include "proto.h"
35#include "route.h"
36
37#include <errno.h>
38#include <string.h>
39#include <unistd.h>
40#include <sys/types.h>
41#include <sys/socket.h>
42#include <linux/netlink.h>
43#include <linux/rtnetlink.h>
44
45#define SNDBUF_SIZE (1024 * 2)
46#define RCVBUF_SIZE (1024 * 4)
47
48#define SITNL_ADDATTR(_msg, _max_size, _attr, _data, _size) \
49 { \
50 if (sitnl_addattr(_msg, _max_size, _attr, _data, _size) < 0) \
51 { \
52 goto err; \
53 } \
54 }
55
56#define SITNL_NEST(_msg, _max_size, _attr) \
57 ({ \
58 struct rtattr *_nest = sitnl_nlmsg_tail(_msg); \
59 SITNL_ADDATTR(_msg, _max_size, _attr, NULL, 0); \
60 _nest; \
61 })
62
63#define SITNL_NEST_END(_msg, _nest) \
64 { \
65 _nest->rta_len = (unsigned short)((void *)sitnl_nlmsg_tail(_msg) - (void *)_nest); \
66 }
67
68/* This function was originally implemented as a macro, but compiling with
69 * gcc and -O3 was getting confused about the math and thus raising
70 * security warnings on subsequent memcpy() calls.
71 *
72 * Converting the macro to a function was not enough, because gcc was still
73 * inlining it and falling in the same math trap.
74 *
75 * The only way out to avoid any warning/error is to force the function to
76 * not be inline'd.
77 */
78static __attribute__((noinline)) void *
79sitnl_nlmsg_tail(const struct nlmsghdr *nlh)
80{
81 return (unsigned char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len);
82}
83
88typedef union
89{
90 in_addr_t ipv4;
91 struct in6_addr ipv6;
93
97struct sitnl_link_req
98{
99 struct nlmsghdr n;
100 struct ifinfomsg i;
101 char buf[256];
102};
103
107struct sitnl_addr_req
108{
109 struct nlmsghdr n;
110 struct ifaddrmsg i;
111 char buf[256];
112};
113
117struct sitnl_route_req
118{
119 struct nlmsghdr n;
120 struct rtmsg r;
121 char buf[256];
122};
123
124typedef int (*sitnl_parse_reply_cb)(struct nlmsghdr *msg, void *arg);
125
129static int
130sitnl_addattr(struct nlmsghdr *n, size_t maxlen, unsigned short type, const void *data, size_t alen)
131{
132 size_t len = RTA_LENGTH(alen);
133
134 if ((NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen)
135 {
136 msg(M_WARN, "%s: rtnl: message exceeded bound of %zu", __func__, maxlen);
137 return -EMSGSIZE;
138 }
139
140 struct rtattr *rta = sitnl_nlmsg_tail(n);
141 rta->rta_type = type;
142 ASSERT(len <= USHRT_MAX);
143 rta->rta_len = (unsigned short)len;
144
145 if (!data)
146 {
147 memset(RTA_DATA(rta), 0, alen);
148 }
149 else
150 {
151 memcpy(RTA_DATA(rta), data, alen);
152 }
153
154 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
155
156 return 0;
157}
158
162static int
163sitnl_socket(void)
164{
165 int sndbuf = SNDBUF_SIZE;
166 int rcvbuf = RCVBUF_SIZE;
167 int fd;
168
169 fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
170 if (fd < 0)
171 {
172 msg(M_WARN, "%s: cannot open netlink socket", __func__);
173 return fd;
174 }
175
176 /* set close on exec to avoid child processes access the socket */
177 set_cloexec(fd);
178
179 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0)
180 {
181 msg(M_WARN | M_ERRNO, "%s: SO_SNDBUF", __func__);
182 close(fd);
183 return -1;
184 }
185
186 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) < 0)
187 {
188 msg(M_WARN | M_ERRNO, "%s: SO_RCVBUF", __func__);
189 close(fd);
190 return -1;
191 }
192
193 return fd;
194}
195
199static int
200sitnl_bind(int fd, uint32_t groups, uint32_t *local_pid)
201{
202 socklen_t addr_len;
203 struct sockaddr_nl local;
204
205 CLEAR(local);
206
207 local.nl_family = AF_NETLINK;
208 local.nl_groups = groups;
209
210 if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0)
211 {
212 msg(M_WARN | M_ERRNO, "%s: cannot bind netlink socket", __func__);
213 return -errno;
214 }
215
216 addr_len = sizeof(local);
217 if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0)
218 {
219 msg(M_WARN | M_ERRNO, "%s: cannot getsockname", __func__);
220 return -errno;
221 }
222
223 if (addr_len != sizeof(local))
224 {
225 msg(M_WARN, "%s: wrong address length %d", __func__, addr_len);
226 return -EINVAL;
227 }
228
229 if (local.nl_family != AF_NETLINK)
230 {
231 msg(M_WARN, "%s: wrong address family %d", __func__, local.nl_family);
232 return -EINVAL;
233 }
234
235 /* We bound with nl_pid=0, so the kernel assigned this socket a unique port
236 * id (it is not the process pid - a process may own several netlink
237 * sockets). getsockname() above is the only way to learn it: hand it back
238 * to the caller, which uses it to check that replies are addressed to this
239 * socket.
240 */
241 *local_pid = local.nl_pid;
242
243 return 0;
244}
245
249static int
250sitnl_send(struct nlmsghdr *payload, pid_t peer, unsigned int groups, sitnl_parse_reply_cb cb,
251 void *arg_cb)
252{
253 int fd, ret;
254 uint32_t local_pid = 0;
255 struct sockaddr_nl nladdr;
256 struct nlmsgerr *err;
257 struct nlmsghdr *h;
258 char buf[1024 * 16];
259 struct iovec iov = {
260 .iov_base = payload,
261 .iov_len = payload->nlmsg_len,
262 };
263 struct msghdr nlmsg = {
264 .msg_name = &nladdr,
265 .msg_namelen = sizeof(nladdr),
266 .msg_iov = &iov,
267 .msg_iovlen = 1,
268 };
269
270 CLEAR(nladdr);
271
272 nladdr.nl_family = AF_NETLINK;
273 nladdr.nl_pid = peer;
274 nladdr.nl_groups = groups;
275
276 /* Match replies to requests with a monotonically increasing sequence
277 * number, seeded once from wall-clock time so it differs between runs.
278 * The kernel echoes this seq (and our port id) in its replies, letting the
279 * receive loop below discard any unrelated or spoofed message.
280 */
281 static uint32_t sitnl_seq;
282 if (!sitnl_seq)
283 {
284 sitnl_seq = (uint32_t)time(NULL);
285 }
286 payload->nlmsg_seq = ++sitnl_seq;
287
288 /* no need to send reply */
289 if (!cb)
290 {
291 payload->nlmsg_flags |= NLM_F_ACK;
292 }
293
294 fd = sitnl_socket();
295 if (fd < 0)
296 {
297 msg(M_WARN | M_ERRNO, "%s: can't open rtnl socket", __func__);
298 return -errno;
299 }
300
301 if (sitnl_bind(fd, 0, &local_pid) < 0)
302 {
303 msg(M_WARN | M_ERRNO, "%s: can't bind rtnl socket", __func__);
304 ret = -errno;
305 goto out;
306 }
307
308 if (sendmsg(fd, &nlmsg, 0) < 0)
309 {
310 msg(M_WARN | M_ERRNO, "%s: rtnl: error on sendmsg()", __func__);
311 ret = -errno;
312 goto out;
313 }
314
315 /* prepare buffer to store RTNL replies */
316 memset(buf, 0, sizeof(buf));
317 iov.iov_base = buf;
318
319 while (1)
320 {
321 /*
322 * iov_len is modified by recvmsg(), therefore has to be initialized before
323 * using it again
324 */
325 msg(D_RTNL, "%s: checking for received messages", __func__);
326 iov.iov_len = sizeof(buf);
327 ssize_t rcv_len = recvmsg(fd, &nlmsg, 0);
328 msg(D_RTNL, "%s: rtnl: received %zd bytes", __func__, rcv_len);
329 if (rcv_len < 0)
330 {
331 if ((errno == EINTR) || (errno == EAGAIN))
332 {
333 msg(D_RTNL, "%s: interrupted call", __func__);
334 continue;
335 }
336 msg(M_WARN | M_ERRNO, "%s: rtnl: error on recvmsg()", __func__);
337 ret = -errno;
338 goto out;
339 }
340
341 if (rcv_len == 0)
342 {
343 msg(M_WARN, "%s: rtnl: socket reached unexpected EOF", __func__);
344 ret = -EIO;
345 goto out;
346 }
347
348 if (nlmsg.msg_namelen != sizeof(nladdr))
349 {
350 msg(M_WARN, "%s: sender address length: %u (expected %zu)", __func__, nlmsg.msg_namelen,
351 sizeof(nladdr));
352 ret = -EIO;
353 goto out;
354 }
355
356 h = (struct nlmsghdr *)buf;
357 while (rcv_len >= (int)sizeof(*h))
358 {
359 uint32_t len = h->nlmsg_len;
360 ssize_t rem_len = len - sizeof(*h);
361
362 if ((rem_len < 0) || (len > rcv_len))
363 {
364 if (nlmsg.msg_flags & MSG_TRUNC)
365 {
366 msg(M_WARN, "%s: truncated message", __func__);
367 ret = -EIO;
368 goto out;
369 }
370 msg(M_WARN, "%s: malformed message: len=%u", __func__, len);
371 ret = -EIO;
372 goto out;
373 }
374
375 /* Discard any message that did not come from the kernel or does
376 * not match the request we sent: only the kernel (nl_pid 0) can
377 * legitimately reply, and a valid reply echoes our port id and
378 * sequence number. This prevents a local process from injecting a
379 * spoofed reply that the callback would otherwise act on.
380 */
381 if ((nladdr.nl_pid != 0) || (h->nlmsg_pid != local_pid)
382 || (h->nlmsg_seq != payload->nlmsg_seq))
383 {
384 msg(D_RTNL,
385 "%s: skipping unrelated message. nl_pid:%u nlmsg_pid:%u (local:%u) nlmsg_seq:%u (seq:%u)",
386 __func__, nladdr.nl_pid, h->nlmsg_pid, local_pid, h->nlmsg_seq,
387 payload->nlmsg_seq);
388 rcv_len -= NLMSG_ALIGN(len);
389 h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
390 continue;
391 }
392
393 if (h->nlmsg_type == NLMSG_DONE)
394 {
395 ret = 0;
396 goto out;
397 }
398
399 if (h->nlmsg_type == NLMSG_ERROR)
400 {
401 err = (struct nlmsgerr *)NLMSG_DATA(h);
402 if (rem_len < (int)sizeof(struct nlmsgerr))
403 {
404 msg(M_WARN, "%s: ERROR truncated", __func__);
405 ret = -EIO;
406 }
407 else
408 {
409 if (!err->error)
410 {
411 ret = 0;
412 if (cb)
413 {
414 int r = cb(h, arg_cb);
415 if (r <= 0)
416 {
417 ret = r;
418 }
419 }
420 }
421 else
422 {
423 msg(M_WARN, "%s: rtnl: generic error (%d): %s", __func__, err->error,
424 strerror(-err->error));
425 ret = err->error;
426 }
427 }
428 goto out;
429 }
430
431 if (cb)
432 {
433 int r = cb(h, arg_cb);
434 if (r <= 0)
435 {
436 ret = r;
437 goto out;
438 }
439 }
440 else
441 {
442 msg(M_WARN, "%s: RTNL: unexpected reply", __func__);
443 }
444
445 rcv_len -= NLMSG_ALIGN(len);
446 h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
447 }
448
449 if (nlmsg.msg_flags & MSG_TRUNC)
450 {
451 msg(M_WARN, "%s: message truncated", __func__);
452 continue;
453 }
454
455 if (rcv_len)
456 {
457 msg(M_WARN, "%s: rtnl: %zd not parsed bytes", __func__, rcv_len);
458 ret = -1;
459 goto out;
460 }
461 }
462out:
463 close(fd);
464
465 return ret;
466}
467
468typedef struct
469{
470 size_t addr_size;
472 char iface[IFNAMSIZ];
473 bool default_only;
474 unsigned int table;
475} route_res_t;
476
477static int
478sitnl_route_save(struct nlmsghdr *n, void *arg)
479{
480 route_res_t *res = arg;
481 struct rtmsg *r = NLMSG_DATA(n);
482 struct rtattr *rta = RTM_RTA(r);
483 size_t len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
484 unsigned int table, ifindex = 0;
485 const void *gw = NULL;
486
487 /* filter-out non-zero dst prefixes */
488 if (res->default_only && r->rtm_dst_len != 0)
489 {
490 return 1;
491 }
492
493 /* route table, ignored with RTA_TABLE */
494 table = r->rtm_table;
495
496 while (RTA_OK(rta, len))
497 {
498 switch (rta->rta_type)
499 {
500 /* route interface */
501 case RTA_OIF:
502 ifindex = *(unsigned int *)RTA_DATA(rta);
503 break;
504
505 /* route prefix */
506 case RTA_DST:
507 break;
508
509 /* GW for the route */
510 case RTA_GATEWAY:
511 gw = RTA_DATA(rta);
512 break;
513
514 /* route table */
515 case RTA_TABLE:
516 table = *(unsigned int *)RTA_DATA(rta);
517 break;
518 }
519
520 rta = RTA_NEXT(rta, len);
521 }
522
523 /* filter out any route not coming from the selected table */
524 if (res->table && res->table != table)
525 {
526 return 1;
527 }
528
529 if (!if_indextoname(ifindex, res->iface))
530 {
531 msg(M_WARN | M_ERRNO, "%s: rtnl: can't get ifname for index %d", __func__, ifindex);
532 return -1;
533 }
534
535 if (gw)
536 {
537 memcpy(&res->gw, gw, res->addr_size);
538 }
539
540 return 0;
541}
542
543static int
544sitnl_route_best_gw(sa_family_t af_family, const inet_address_t *dst, void *best_gw,
545 char *best_iface)
546{
547 struct sitnl_route_req req;
548 route_res_t res;
549 int ret = -EINVAL;
550
551 ASSERT(best_gw);
552 ASSERT(best_iface);
553
554 CLEAR(req);
555 CLEAR(res);
556
557 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r));
558 req.n.nlmsg_type = RTM_GETROUTE;
559 req.n.nlmsg_flags = NLM_F_REQUEST;
560
561 ASSERT(af_family <= UCHAR_MAX);
562 req.r.rtm_family = (unsigned char)af_family;
563
564 switch (af_family)
565 {
566 case AF_INET:
567 res.addr_size = sizeof(in_addr_t);
568 /*
569 * kernel can't return 0.0.0.0/8 host route, dump all
570 * the routes and filter for 0.0.0.0/0 in cb()
571 */
572 if (!dst || !dst->ipv4)
573 {
574 req.n.nlmsg_flags |= NLM_F_DUMP;
575 res.default_only = true;
576 res.table = RT_TABLE_MAIN;
577 }
578 else
579 {
580 req.r.rtm_dst_len = 32;
581 }
582 break;
583
584 case AF_INET6:
585 res.addr_size = sizeof(struct in6_addr);
586 /* kernel can return ::/128 host route */
587 req.r.rtm_dst_len = 128;
588 break;
589
590 default:
591 /* unsupported */
592 return -EINVAL;
593 }
594
595 SITNL_ADDATTR(&req.n, sizeof(req), RTA_DST, dst, res.addr_size);
596
597 ret = sitnl_send(&req.n, 0, 0, sitnl_route_save, &res);
598 if (ret < 0)
599 {
600 goto err;
601 }
602
603 /* save result in output variables */
604 memcpy(best_gw, &res.gw, res.addr_size);
605 strncpy(best_iface, res.iface, IFNAMSIZ);
606err:
607 return ret;
608}
609
610/* used by iproute2 implementation too */
611int
612net_route_v6_best_gw(openvpn_net_ctx_t *ctx, const struct in6_addr *dst, struct in6_addr *best_gw,
613 char *best_iface)
614{
615 inet_address_t dst_v6 = { 0 };
616 char buf[INET6_ADDRSTRLEN];
617 int ret;
618
619 if (dst)
620 {
621 dst_v6.ipv6 = *dst;
622 }
623
624 msg(D_ROUTE, "%s query: dst %s", __func__, inet_ntop(AF_INET6, &dst_v6.ipv6, buf, sizeof(buf)));
625
626 ret = sitnl_route_best_gw(AF_INET6, &dst_v6, best_gw, best_iface);
627 if (ret < 0)
628 {
629 return ret;
630 }
631
632 msg(D_ROUTE, "%s result: via %s dev %s", __func__,
633 inet_ntop(AF_INET6, best_gw, buf, sizeof(buf)), best_iface);
634
635 return ret;
636}
637
638/* used by iproute2 implementation too */
639int
640net_route_v4_best_gw(openvpn_net_ctx_t *ctx, const in_addr_t *dst, in_addr_t *best_gw,
641 char *best_iface)
642{
643 inet_address_t dst_v4 = { 0 };
644 char buf[INET_ADDRSTRLEN];
645 int ret;
646
647 if (dst)
648 {
649 dst_v4.ipv4 = htonl(*dst);
650 }
651
652 msg(D_ROUTE, "%s query: dst %s", __func__, inet_ntop(AF_INET, &dst_v4.ipv4, buf, sizeof(buf)));
653
654 ret = sitnl_route_best_gw(AF_INET, &dst_v4, best_gw, best_iface);
655 if (ret < 0)
656 {
657 return ret;
658 }
659
660 msg(D_ROUTE, "%s result: via %s dev %s", __func__,
661 inet_ntop(AF_INET, best_gw, buf, sizeof(buf)), best_iface);
662
663 /* result is expected in Host Order */
664 *best_gw = ntohl(*best_gw);
665
666 return ret;
667}
668
669#ifdef ENABLE_SITNL
670
671int
672net_iface_up(openvpn_net_ctx_t *ctx, const char *iface, bool up)
673{
674 struct sitnl_link_req req;
675 int ifindex;
676
677 CLEAR(req);
678
679 if (!iface)
680 {
681 msg(M_WARN, "%s: passed NULL interface", __func__);
682 return -EINVAL;
683 }
684
685 ifindex = if_nametoindex(iface);
686 if (ifindex == 0)
687 {
688 msg(M_WARN, "%s: rtnl: cannot get ifindex for %s: %s", __func__, iface, strerror(errno));
689 return -ENOENT;
690 }
691
692 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
693 req.n.nlmsg_flags = NLM_F_REQUEST;
694 req.n.nlmsg_type = RTM_NEWLINK;
695
696 req.i.ifi_family = AF_PACKET;
697 req.i.ifi_index = ifindex;
698 req.i.ifi_change |= IFF_UP;
699 if (up)
700 {
701 req.i.ifi_flags |= IFF_UP;
702 }
703 else
704 {
705 req.i.ifi_flags &= ~IFF_UP;
706 }
707
708 msg(M_INFO, "%s: set %s %s", __func__, iface, up ? "up" : "down");
709
710 return sitnl_send(&req.n, 0, 0, NULL, NULL);
711}
712
713int
714net_iface_mtu_set(openvpn_net_ctx_t *ctx, const char *iface, uint32_t mtu)
715{
716 struct sitnl_link_req req;
717 int ifindex, ret = -1;
718
719 CLEAR(req);
720
721 ifindex = if_nametoindex(iface);
722 if (ifindex == 0)
723 {
724 msg(M_WARN | M_ERRNO, "%s: rtnl: cannot get ifindex for %s", __func__, iface);
725 return -1;
726 }
727
728 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
729 req.n.nlmsg_flags = NLM_F_REQUEST;
730 req.n.nlmsg_type = RTM_NEWLINK;
731
732 req.i.ifi_family = AF_PACKET;
733 req.i.ifi_index = ifindex;
734
735 SITNL_ADDATTR(&req.n, sizeof(req), IFLA_MTU, &mtu, 4);
736
737 msg(M_INFO, "%s: mtu %u for %s", __func__, mtu, iface);
738
739 ret = sitnl_send(&req.n, 0, 0, NULL, NULL);
740err:
741 return ret;
742}
743
744int
745net_addr_ll_set(openvpn_net_ctx_t *ctx, const openvpn_net_iface_t *iface, uint8_t *addr)
746{
747 struct sitnl_link_req req;
748 int ifindex, ret = -1;
749
750 CLEAR(req);
751
752 ifindex = if_nametoindex(iface);
753 if (ifindex == 0)
754 {
755 msg(M_WARN | M_ERRNO, "%s: rtnl: cannot get ifindex for %s", __func__, iface);
756 return -1;
757 }
758
759 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
760 req.n.nlmsg_flags = NLM_F_REQUEST;
761 req.n.nlmsg_type = RTM_NEWLINK;
762
763 req.i.ifi_family = AF_PACKET;
764 req.i.ifi_index = ifindex;
765
766 SITNL_ADDATTR(&req.n, sizeof(req), IFLA_ADDRESS, addr, OPENVPN_ETH_ALEN);
767
768 msg(M_INFO, "%s: lladdr " MAC_FMT " for %s", __func__, MAC_PRINT_ARG(addr), iface);
769
770 ret = sitnl_send(&req.n, 0, 0, NULL, NULL);
771err:
772 return ret;
773}
774
775static int
776sitnl_addr_set(uint16_t cmd, uint16_t flags, int ifindex, sa_family_t af_family,
777 const inet_address_t *local, const inet_address_t *remote, int prefixlen)
778{
779 struct sitnl_addr_req req;
780 uint32_t size;
781 int ret = -EINVAL;
782
783 CLEAR(req);
784
785 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
786 req.n.nlmsg_type = cmd;
787 req.n.nlmsg_flags = NLM_F_REQUEST | flags;
788
789 req.i.ifa_index = ifindex;
790 ASSERT(af_family <= UINT8_MAX);
791 req.i.ifa_family = (uint8_t)af_family;
792
793 switch (af_family)
794 {
795 case AF_INET:
796 size = sizeof(struct in_addr);
797 break;
798
799 case AF_INET6:
800 size = sizeof(struct in6_addr);
801 break;
802
803 default:
804 msg(M_WARN, "%s: rtnl: unknown address family %d", __func__, af_family);
805 return -EINVAL;
806 }
807
808 /* if no prefixlen has been specified, assume host address */
809 if (prefixlen == 0)
810 {
811 prefixlen = size * 8;
812 }
813 ASSERT(prefixlen <= UINT8_MAX);
814 req.i.ifa_prefixlen = (uint8_t)prefixlen;
815
816 if (remote)
817 {
818 SITNL_ADDATTR(&req.n, sizeof(req), IFA_ADDRESS, remote, size);
819 }
820
821 if (local)
822 {
823 SITNL_ADDATTR(&req.n, sizeof(req), IFA_LOCAL, local, size);
824 }
825
826 if (af_family == AF_INET && local && !remote && prefixlen <= 30)
827 {
828 inet_address_t broadcast = *local;
829 broadcast.ipv4 |= htonl(~netbits_to_netmask(prefixlen));
830 SITNL_ADDATTR(&req.n, sizeof(req), IFA_BROADCAST, &broadcast, size);
831 }
832
833 ret = sitnl_send(&req.n, 0, 0, NULL, NULL);
834 if (ret == -EEXIST)
835 {
836 ret = 0;
837 }
838err:
839 return ret;
840}
841
842static int
843sitnl_addr_ptp_add(sa_family_t af_family, const char *iface, const inet_address_t *local,
844 const inet_address_t *remote)
845{
846 int ifindex;
847
848 switch (af_family)
849 {
850 case AF_INET:
851 case AF_INET6:
852 break;
853
854 default:
855 return -EINVAL;
856 }
857
858 if (!iface)
859 {
860 msg(M_WARN, "%s: passed NULL interface", __func__);
861 return -EINVAL;
862 }
863
864 ifindex = if_nametoindex(iface);
865 if (ifindex == 0)
866 {
867 msg(M_WARN, "%s: cannot get ifindex for %s: %s", __func__, np(iface), strerror(errno));
868 return -ENOENT;
869 }
870
871 return sitnl_addr_set(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, ifindex, af_family, local,
872 remote, 0);
873}
874
875static int
876sitnl_addr_ptp_del(sa_family_t af_family, const char *iface, const inet_address_t *local)
877{
878 int ifindex;
879
880 switch (af_family)
881 {
882 case AF_INET:
883 case AF_INET6:
884 break;
885
886 default:
887 return -EINVAL;
888 }
889
890 if (!iface)
891 {
892 msg(M_WARN, "%s: passed NULL interface", __func__);
893 return -EINVAL;
894 }
895
896 ifindex = if_nametoindex(iface);
897 if (ifindex == 0)
898 {
899 msg(M_WARN | M_ERRNO, "%s: cannot get ifindex for %s", __func__, iface);
900 return -ENOENT;
901 }
902
903 return sitnl_addr_set(RTM_DELADDR, 0, ifindex, af_family, local, NULL, 0);
904}
905
906static int
907sitnl_route_set(uint16_t cmd, uint16_t flags, int ifindex, sa_family_t af_family, const void *dst,
908 int prefixlen, const void *gw, enum rt_class_t table, int metric,
909 enum rt_scope_t scope, unsigned char protocol, unsigned char type)
910{
911 struct sitnl_route_req req;
912 int ret = -1, size;
913
914 CLEAR(req);
915
916 switch (af_family)
917 {
918 case AF_INET:
919 size = sizeof(in_addr_t);
920 break;
921
922 case AF_INET6:
923 size = sizeof(struct in6_addr);
924 break;
925
926 default:
927 return -EINVAL;
928 }
929
930 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r));
931 req.n.nlmsg_type = cmd;
932 req.n.nlmsg_flags = NLM_F_REQUEST | flags;
933
934 ASSERT(af_family <= UCHAR_MAX);
935 req.r.rtm_family = (unsigned char)af_family;
936 req.r.rtm_scope = (unsigned char)scope;
937 req.r.rtm_protocol = protocol;
938 req.r.rtm_type = type;
939 ASSERT(prefixlen >= 0 && prefixlen <= UCHAR_MAX);
940 req.r.rtm_dst_len = (unsigned char)prefixlen;
941
942 if (table <= UCHAR_MAX)
943 {
944 req.r.rtm_table = (unsigned char)table;
945 }
946 else
947 {
948 req.r.rtm_table = RT_TABLE_UNSPEC;
949 SITNL_ADDATTR(&req.n, sizeof(req), RTA_TABLE, &table, 4);
950 }
951
952 if (dst)
953 {
954 SITNL_ADDATTR(&req.n, sizeof(req), RTA_DST, dst, size);
955 }
956
957 if (gw)
958 {
959 SITNL_ADDATTR(&req.n, sizeof(req), RTA_GATEWAY, gw, size);
960 }
961
962 if (ifindex > 0)
963 {
964 SITNL_ADDATTR(&req.n, sizeof(req), RTA_OIF, &ifindex, 4);
965 }
966
967 if (metric > 0)
968 {
969 SITNL_ADDATTR(&req.n, sizeof(req), RTA_PRIORITY, &metric, 4);
970 }
971
972 ret = sitnl_send(&req.n, 0, 0, NULL, NULL);
973err:
974 return ret;
975}
976
977static int
978sitnl_addr_add(sa_family_t af_family, const char *iface, const inet_address_t *addr, int prefixlen)
979{
980 int ifindex;
981
982 switch (af_family)
983 {
984 case AF_INET:
985 case AF_INET6:
986 break;
987
988 default:
989 return -EINVAL;
990 }
991
992 if (!iface)
993 {
994 msg(M_WARN, "%s: passed NULL interface", __func__);
995 return -EINVAL;
996 }
997
998 ifindex = if_nametoindex(iface);
999 if (ifindex == 0)
1000 {
1001 msg(M_WARN | M_ERRNO, "%s: rtnl: cannot get ifindex for %s", __func__, iface);
1002 return -ENOENT;
1003 }
1004
1005 return sitnl_addr_set(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, ifindex, af_family, addr, NULL,
1006 prefixlen);
1007}
1008
1009static int
1010sitnl_addr_del(sa_family_t af_family, const char *iface, inet_address_t *addr, int prefixlen)
1011{
1012 int ifindex;
1013
1014 switch (af_family)
1015 {
1016 case AF_INET:
1017 case AF_INET6:
1018 break;
1019
1020 default:
1021 return -EINVAL;
1022 }
1023
1024 if (!iface)
1025 {
1026 msg(M_WARN, "%s: passed NULL interface", __func__);
1027 return -EINVAL;
1028 }
1029
1030 ifindex = if_nametoindex(iface);
1031 if (ifindex == 0)
1032 {
1033 msg(M_WARN | M_ERRNO, "%s: rtnl: cannot get ifindex for %s", __func__, iface);
1034 return -ENOENT;
1035 }
1036
1037 return sitnl_addr_set(RTM_DELADDR, 0, ifindex, af_family, addr, NULL, prefixlen);
1038}
1039
1040int
1041net_addr_v4_add(openvpn_net_ctx_t *ctx, const char *iface, const in_addr_t *addr, int prefixlen)
1042{
1043 inet_address_t addr_v4 = { 0 };
1044 char buf[INET_ADDRSTRLEN];
1045
1046 if (!addr)
1047 {
1048 return -EINVAL;
1049 }
1050
1051 addr_v4.ipv4 = htonl(*addr);
1052
1053 msg(M_INFO, "%s: %s/%d dev %s", __func__, inet_ntop(AF_INET, &addr_v4.ipv4, buf, sizeof(buf)),
1054 prefixlen, iface);
1055
1056 return sitnl_addr_add(AF_INET, iface, &addr_v4, prefixlen);
1057}
1058
1059int
1060net_addr_v6_add(openvpn_net_ctx_t *ctx, const char *iface, const struct in6_addr *addr,
1061 int prefixlen)
1062{
1063 inet_address_t addr_v6 = { 0 };
1064 char buf[INET6_ADDRSTRLEN];
1065
1066 if (!addr)
1067 {
1068 return -EINVAL;
1069 }
1070
1071 addr_v6.ipv6 = *addr;
1072
1073 msg(M_INFO, "%s: %s/%d dev %s", __func__, inet_ntop(AF_INET6, &addr_v6.ipv6, buf, sizeof(buf)),
1074 prefixlen, iface);
1075
1076 return sitnl_addr_add(AF_INET6, iface, &addr_v6, prefixlen);
1077}
1078
1079int
1080net_addr_v4_del(openvpn_net_ctx_t *ctx, const char *iface, const in_addr_t *addr, int prefixlen)
1081{
1082 inet_address_t addr_v4 = { 0 };
1083 char buf[INET_ADDRSTRLEN];
1084
1085 if (!addr)
1086 {
1087 return -EINVAL;
1088 }
1089
1090 addr_v4.ipv4 = htonl(*addr);
1091
1092 msg(M_INFO, "%s: %s dev %s", __func__, inet_ntop(AF_INET, &addr_v4.ipv4, buf, sizeof(buf)),
1093 iface);
1094
1095 return sitnl_addr_del(AF_INET, iface, &addr_v4, prefixlen);
1096}
1097
1098int
1099net_addr_v6_del(openvpn_net_ctx_t *ctx, const char *iface, const struct in6_addr *addr,
1100 int prefixlen)
1101{
1102 inet_address_t addr_v6 = { 0 };
1103 char buf[INET6_ADDRSTRLEN];
1104
1105 if (!addr)
1106 {
1107 return -EINVAL;
1108 }
1109
1110 addr_v6.ipv6 = *addr;
1111
1112 msg(M_INFO, "%s: %s/%d dev %s", __func__, inet_ntop(AF_INET6, &addr_v6.ipv6, buf, sizeof(buf)),
1113 prefixlen, iface);
1114
1115 return sitnl_addr_del(AF_INET6, iface, &addr_v6, prefixlen);
1116}
1117
1118int
1119net_addr_ptp_v4_add(openvpn_net_ctx_t *ctx, const char *iface, const in_addr_t *local,
1120 const in_addr_t *remote)
1121{
1122 inet_address_t local_v4 = { 0 };
1123 inet_address_t remote_v4 = { 0 };
1124 char buf1[INET_ADDRSTRLEN];
1125 char buf2[INET_ADDRSTRLEN];
1126
1127 if (!local)
1128 {
1129 return -EINVAL;
1130 }
1131
1132 local_v4.ipv4 = htonl(*local);
1133
1134 if (remote)
1135 {
1136 remote_v4.ipv4 = htonl(*remote);
1137 }
1138
1139 msg(M_INFO, "%s: %s peer %s dev %s", __func__,
1140 inet_ntop(AF_INET, &local_v4.ipv4, buf1, sizeof(buf1)),
1141 inet_ntop(AF_INET, &remote_v4.ipv4, buf2, sizeof(buf2)), iface);
1142
1143 return sitnl_addr_ptp_add(AF_INET, iface, &local_v4, &remote_v4);
1144}
1145
1146int
1147net_addr_ptp_v4_del(openvpn_net_ctx_t *ctx, const char *iface, const in_addr_t *local,
1148 const in_addr_t *remote)
1149{
1150 inet_address_t local_v4 = { 0 };
1151 char buf[INET6_ADDRSTRLEN];
1152
1153
1154 if (!local)
1155 {
1156 return -EINVAL;
1157 }
1158
1159 local_v4.ipv4 = htonl(*local);
1160
1161 msg(M_INFO, "%s: %s dev %s", __func__, inet_ntop(AF_INET, &local_v4.ipv4, buf, sizeof(buf)),
1162 iface);
1163
1164 return sitnl_addr_ptp_del(AF_INET, iface, &local_v4);
1165}
1166
1167static int
1168sitnl_route_add(const char *iface, sa_family_t af_family, const void *dst, int prefixlen,
1169 const void *gw, uint32_t table, int metric)
1170{
1171 enum rt_scope_t scope = RT_SCOPE_UNIVERSE;
1172 int ifindex = 0;
1173
1174 if (iface)
1175 {
1176 ifindex = if_nametoindex(iface);
1177 if (ifindex == 0)
1178 {
1179 msg(M_WARN | M_ERRNO, "%s: rtnl: can't get ifindex for %s", __func__, iface);
1180 return -ENOENT;
1181 }
1182 }
1183
1184 if (table == 0)
1185 {
1186 table = RT_TABLE_MAIN;
1187 }
1188
1189 if (!gw && iface)
1190 {
1191 scope = RT_SCOPE_LINK;
1192 }
1193
1194 return sitnl_route_set(RTM_NEWROUTE, NLM_F_CREATE, ifindex, af_family, dst, prefixlen, gw,
1195 table, metric, scope, RTPROT_BOOT, RTN_UNICAST);
1196}
1197
1198int
1199net_route_v4_add(openvpn_net_ctx_t *ctx, const in_addr_t *dst, int prefixlen, const in_addr_t *gw,
1200 const char *iface, uint32_t table, int metric)
1201{
1202 const in_addr_t *dst_ptr = NULL, *gw_ptr = NULL;
1203 in_addr_t dst_be = 0, gw_be = 0;
1204 char dst_str[INET_ADDRSTRLEN];
1205 char gw_str[INET_ADDRSTRLEN];
1206
1207 if (dst)
1208 {
1209 dst_be = htonl(*dst);
1210 dst_ptr = &dst_be;
1211 }
1212
1213 if (gw)
1214 {
1215 gw_be = htonl(*gw);
1216 gw_ptr = &gw_be;
1217 }
1218
1219 msg(D_ROUTE, "%s: %s/%d via %s dev %s table %d metric %d", __func__,
1220 inet_ntop(AF_INET, &dst_be, dst_str, sizeof(dst_str)), prefixlen,
1221 inet_ntop(AF_INET, &gw_be, gw_str, sizeof(gw_str)), np(iface), table, metric);
1222
1223 return sitnl_route_add(iface, AF_INET, dst_ptr, prefixlen, gw_ptr, table, metric);
1224}
1225
1226int
1227net_route_v6_add(openvpn_net_ctx_t *ctx, const struct in6_addr *dst, int prefixlen,
1228 const struct in6_addr *gw, const char *iface, uint32_t table, int metric)
1229{
1230 inet_address_t dst_v6 = { 0 };
1231 inet_address_t gw_v6 = { 0 };
1232 char dst_str[INET6_ADDRSTRLEN];
1233 char gw_str[INET6_ADDRSTRLEN];
1234
1235 if (dst)
1236 {
1237 dst_v6.ipv6 = *dst;
1238 }
1239
1240 if (gw)
1241 {
1242 gw_v6.ipv6 = *gw;
1243 }
1244
1245 msg(D_ROUTE, "%s: %s/%d via %s dev %s table %d metric %d", __func__,
1246 inet_ntop(AF_INET6, &dst_v6.ipv6, dst_str, sizeof(dst_str)), prefixlen,
1247 inet_ntop(AF_INET6, &gw_v6.ipv6, gw_str, sizeof(gw_str)), np(iface), table, metric);
1248
1249 return sitnl_route_add(iface, AF_INET6, dst, prefixlen, gw, table, metric);
1250}
1251
1252static int
1253sitnl_route_del(const char *iface, sa_family_t af_family, inet_address_t *dst, int prefixlen,
1254 inet_address_t *gw, uint32_t table, int metric)
1255{
1256 int ifindex = 0;
1257
1258 if (iface)
1259 {
1260 ifindex = if_nametoindex(iface);
1261 if (ifindex == 0)
1262 {
1263 msg(M_WARN | M_ERRNO, "%s: rtnl: can't get ifindex for %s", __func__, iface);
1264 return -ENOENT;
1265 }
1266 }
1267
1268 if (table == 0)
1269 {
1270 table = RT_TABLE_MAIN;
1271 }
1272
1273 return sitnl_route_set(RTM_DELROUTE, 0, ifindex, af_family, dst, prefixlen, gw, table, metric,
1274 RT_SCOPE_NOWHERE, 0, 0);
1275}
1276
1277int
1278net_route_v4_del(openvpn_net_ctx_t *ctx, const in_addr_t *dst, int prefixlen, const in_addr_t *gw,
1279 const char *iface, uint32_t table, int metric)
1280{
1281 inet_address_t dst_v4 = { 0 };
1282 inet_address_t gw_v4 = { 0 };
1283 char dst_str[INET_ADDRSTRLEN];
1284 char gw_str[INET_ADDRSTRLEN];
1285
1286 if (dst)
1287 {
1288 dst_v4.ipv4 = htonl(*dst);
1289 }
1290
1291 if (gw)
1292 {
1293 gw_v4.ipv4 = htonl(*gw);
1294 }
1295
1296 msg(D_ROUTE, "%s: %s/%d via %s dev %s table %d metric %d", __func__,
1297 inet_ntop(AF_INET, &dst_v4.ipv4, dst_str, sizeof(dst_str)), prefixlen,
1298 inet_ntop(AF_INET, &gw_v4.ipv4, gw_str, sizeof(gw_str)), np(iface), table, metric);
1299
1300 return sitnl_route_del(iface, AF_INET, &dst_v4, prefixlen, &gw_v4, table, metric);
1301}
1302
1303int
1304net_route_v6_del(openvpn_net_ctx_t *ctx, const struct in6_addr *dst, int prefixlen,
1305 const struct in6_addr *gw, const char *iface, uint32_t table, int metric)
1306{
1307 inet_address_t dst_v6 = { 0 };
1308 inet_address_t gw_v6 = { 0 };
1309 char dst_str[INET6_ADDRSTRLEN];
1310 char gw_str[INET6_ADDRSTRLEN];
1311
1312 if (dst)
1313 {
1314 dst_v6.ipv6 = *dst;
1315 }
1316
1317 if (gw)
1318 {
1319 gw_v6.ipv6 = *gw;
1320 }
1321
1322 msg(D_ROUTE, "%s: %s/%d via %s dev %s table %d metric %d", __func__,
1323 inet_ntop(AF_INET6, &dst_v6.ipv6, dst_str, sizeof(dst_str)), prefixlen,
1324 inet_ntop(AF_INET6, &gw_v6.ipv6, gw_str, sizeof(gw_str)), np(iface), table, metric);
1325
1326 return sitnl_route_del(iface, AF_INET6, &dst_v6, prefixlen, &gw_v6, table, metric);
1327}
1328
1329
1330int
1331net_iface_new(openvpn_net_ctx_t *ctx, const char *iface, const char *type, void *arg)
1332{
1333 struct sitnl_link_req req = {};
1334 int ret = -1;
1335
1336 ASSERT(iface);
1337
1338 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
1339 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
1340 req.n.nlmsg_type = RTM_NEWLINK;
1341
1342 SITNL_ADDATTR(&req.n, sizeof(req), IFLA_IFNAME, iface, strlen(iface) + 1);
1343
1344 struct rtattr *linkinfo = SITNL_NEST(&req.n, sizeof(req), IFLA_LINKINFO);
1345 SITNL_ADDATTR(&req.n, sizeof(req), IFLA_INFO_KIND, type, strlen(type) + 1);
1346#if defined(ENABLE_DCO)
1347 if (arg && (strcmp(type, OVPN_FAMILY_NAME) == 0))
1348 {
1349 const dco_context_t *dco = arg;
1350 struct rtattr *data = SITNL_NEST(&req.n, sizeof(req), IFLA_INFO_DATA);
1351
1352 /* the netlink format is uint8_t for this and using something
1353 * other than uint8_t here (enum underlying type is undefined but
1354 * commonly int) causes the values to be 0 when passed
1355 * on big endian arch as we only take the (biggest endian) byte
1356 * directly at the address
1357 */
1358 uint8_t ifmode = (uint8_t)dco->ifmode;
1359 SITNL_ADDATTR(&req.n, sizeof(req), IFLA_OVPN_MODE, &ifmode, sizeof(uint8_t));
1360 SITNL_NEST_END(&req.n, data);
1361 }
1362#endif
1363 SITNL_NEST_END(&req.n, linkinfo);
1364
1365 req.i.ifi_family = AF_PACKET;
1366
1367 msg(D_ROUTE, "%s: add %s type %s", __func__, iface, type);
1368
1369 ret = sitnl_send(&req.n, 0, 0, NULL, NULL);
1370err:
1371 return ret;
1372}
1373
1374static void
1375sitnl_parse_rtattr_flags(struct rtattr *tb[], size_t max, struct rtattr *rta, size_t len,
1376 unsigned short flags)
1377{
1378 unsigned short type;
1379
1380 memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
1381
1382 while (RTA_OK(rta, len))
1383 {
1384 type = rta->rta_type & ~flags;
1385
1386 if ((type <= max) && (!tb[type]))
1387 {
1388 tb[type] = rta;
1389 }
1390
1391 rta = RTA_NEXT(rta, len);
1392 }
1393
1394 if (len)
1395 {
1396 msg(D_ROUTE, "%s: %zu bytes not parsed! (rta_len=%u)", __func__, len, rta->rta_len);
1397 }
1398}
1399
1400static void
1401sitnl_parse_rtattr(struct rtattr *tb[], size_t max, struct rtattr *rta, size_t len)
1402{
1403 sitnl_parse_rtattr_flags(tb, max, rta, len, 0);
1404}
1405
1406#define sitnl_parse_rtattr_nested(tb, max, rta) \
1407 (sitnl_parse_rtattr_flags(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta), NLA_F_NESTED))
1408
1409static int
1410sitnl_type_save(struct nlmsghdr *n, void *arg)
1411{
1412 char *type = arg;
1413 struct ifinfomsg *ifi = NLMSG_DATA(n);
1414 struct rtattr *tb[IFLA_MAX + 1];
1415
1416 sitnl_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
1417
1418 if (tb[IFLA_LINKINFO])
1419 {
1420 struct rtattr *tb_link[IFLA_INFO_MAX + 1];
1421
1422 sitnl_parse_rtattr_nested(tb_link, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
1423
1424 if (!tb_link[IFLA_INFO_KIND])
1425 {
1426 return -ENOENT;
1427 }
1428
1429 strncpynt(type, RTA_DATA(tb_link[IFLA_INFO_KIND]), IFACE_TYPE_LEN_MAX);
1430 }
1431
1432 return 0;
1433}
1434
1435int
1436net_iface_type(openvpn_net_ctx_t *ctx, const char *iface, char type[IFACE_TYPE_LEN_MAX])
1437{
1438 struct sitnl_link_req req = {};
1439 int ifindex = if_nametoindex(iface);
1440
1441 if (!ifindex)
1442 {
1443 return -errno;
1444 }
1445
1446 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
1447 req.n.nlmsg_flags = NLM_F_REQUEST;
1448 req.n.nlmsg_type = RTM_GETLINK;
1449
1450 req.i.ifi_family = AF_PACKET;
1451 req.i.ifi_index = ifindex;
1452
1453 memset(type, 0, IFACE_TYPE_LEN_MAX);
1454
1455 int ret = sitnl_send(&req.n, 0, 0, sitnl_type_save, type);
1456 if (ret < 0)
1457 {
1458 msg(D_ROUTE, "%s: cannot retrieve iface %s: %s (%d)", __func__, iface, strerror(-ret), ret);
1459 return ret;
1460 }
1461
1462 msg(D_ROUTE, "%s: type of %s: %s", __func__, iface, type);
1463
1464 return 0;
1465}
1466
1467int
1468net_iface_del(openvpn_net_ctx_t *ctx, const char *iface)
1469{
1470 struct sitnl_link_req req = {};
1471 int ifindex = if_nametoindex(iface);
1472
1473 if (!ifindex)
1474 {
1475 return -errno;
1476 }
1477
1478 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
1479 req.n.nlmsg_flags = NLM_F_REQUEST;
1480 req.n.nlmsg_type = RTM_DELLINK;
1481
1482 req.i.ifi_family = AF_PACKET;
1483 req.i.ifi_index = ifindex;
1484
1485 msg(D_ROUTE, "%s: delete %s", __func__, iface);
1486
1487 return sitnl_send(&req.n, 0, 0, NULL, NULL);
1488}
1489
1490#endif /* !ENABLE_SITNL */
1491
1492#endif /* TARGET_LINUX */
Buffer management functions and garbage collection.
static void strncpynt(char *dest, const char *src, size_t maxlen)
Like strncpy() but always null-terminates the destination.
Definition buffer.h:646
void * dco_context_t
Definition dco.h:259
#define D_RTNL
Definition errlevel.h:111
#define M_INFO
Definition errlevel.h:54
#define D_ROUTE
Definition errlevel.h:79
void set_cloexec(socket_descriptor_t fd)
Definition fdmisc.c:78
#define MAC_PRINT_ARG(_mac)
Definition misc.h:227
#define MAC_FMT
Definition misc.h:225
static const char * np(const char *str)
Definition multi-auth.c:146
void * openvpn_net_iface_t
Definition networking.h:39
#define IFACE_TYPE_LEN_MAX
Definition networking.h:25
void * openvpn_net_ctx_t
Definition networking.h:38
#define CLEAR(x)
Definition basic.h:32
#define msg(flags,...)
Definition error.h:152
#define ASSERT(x)
Definition error.h:219
#define M_WARN
Definition error.h:92
#define M_ERRNO
Definition error.h:95
#define OVPN_FAMILY_NAME
#define OPENVPN_ETH_ALEN
Definition proto.h:52
static in_addr_t netbits_to_netmask(const int netbits)
Definition route.h:399
unsigned short sa_family_t
Definition syshead.h:409
uint32_t in_addr_t
Definition syshead.h:52
__attribute__((unused))
Definition test.c:42
static char * iface
struct in6_addr ipv6
Definition openvpn-msg.h:64
struct in_addr ipv4
Definition openvpn-msg.h:63