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