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