blob: fd3caffaab8a8cd39e6d09d660b22f98e6c34d4b [file] [log] [blame]
Harald Weltec5efb5b2018-04-25 17:38:51 +02001/*
2 * TUN interface functions.
3 * Copyright (C) 2002, 2003, 2004 Mondru AB.
4 * Copyright (C) 2017-2018 by Harald Welte <laforge@gnumonks.org>
5 *
6 * The contents of this file may be used under the terms of the GNU
7 * General Public License Version 2, provided that the above copyright
8 * notice and this permission notice is included in all copies or
9 * substantial portions of the software.
10 *
11 */
12
13/*
14 * netdev.c: Contains generic network device related functionality.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <sys/types.h>
20#include <sys/socket.h>
21#include <netinet/in.h>
22#include <arpa/inet.h>
23#include <sys/stat.h>
24#include <sys/time.h>
25#include <unistd.h>
26#include <string.h>
27#include <errno.h>
28#include <fcntl.h>
29
30#include <stdio.h>
31#include <fcntl.h>
32#include <unistd.h>
33#include <sys/time.h>
34#include <sys/ioctl.h>
35#include <sys/socket.h>
36#include <errno.h>
37#include <net/route.h>
38#include <net/if.h>
39
40#if defined(__linux__)
41#include <linux/netlink.h>
42#include <linux/rtnetlink.h>
43
44#elif defined (__FreeBSD__)
45#include <net/if_var.h>
46#include <netinet/in_var.h>
47
48#elif defined (__APPLE__)
49#include <net/if.h>
50
51#else
52#error "Unknown platform!"
53#endif
54
55#include "netdev.h"
56#include "syserr.h"
57
58#if defined(__linux__)
59
60#include <linux/ipv6.h>
61
62static int netdev_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
63{
64 int len = RTA_LENGTH(dlen);
65 int alen = NLMSG_ALIGN(n->nlmsg_len);
66 struct rtattr *rta = (struct rtattr *)(((void *)n) + alen);
67 if (alen + len > nsize)
68 return -1;
69 rta->rta_len = len;
70 rta->rta_type = type;
71 memcpy(RTA_DATA(rta), d, dlen);
72 n->nlmsg_len = alen + len;
73 return 0;
74}
75#endif
76
77static int netdev_sifflags(const char *devname, int flags)
78{
79 struct ifreq ifr;
80 int fd;
81
82 memset(&ifr, '\0', sizeof(ifr));
83 ifr.ifr_flags = flags;
84 strncpy(ifr.ifr_name, devname, IFNAMSIZ);
85 ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
86 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
87 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
88 return -1;
89 }
90 if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
91 SYS_ERR(DTUN, LOGL_ERROR, errno,
92 "ioctl(SIOCSIFFLAGS) failed");
93 close(fd);
94 return -1;
95 }
96 close(fd);
97 return 0;
98}
99
100int netdev_setaddr4(const char *devname, struct in_addr *addr,
101 struct in_addr *dstaddr, struct in_addr *netmask)
102{
103 struct ifreq ifr;
104 int fd;
105
106 memset(&ifr, '\0', sizeof(ifr));
107 ifr.ifr_addr.sa_family = AF_INET;
108 ifr.ifr_dstaddr.sa_family = AF_INET;
109
110#if defined(__linux__)
111 ifr.ifr_netmask.sa_family = AF_INET;
Harald Weltec5efb5b2018-04-25 17:38:51 +0200112#elif defined(__FreeBSD__) || defined (__APPLE__)
113 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_len =
114 sizeof(struct sockaddr_in);
115 ((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_len =
116 sizeof(struct sockaddr_in);
117#endif
118
119 strncpy(ifr.ifr_name, devname, IFNAMSIZ);
120 ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
121
122 /* Create a channel to the NET kernel. */
123 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
124 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
125 return -1;
126 }
127
128 if (addr) { /* Set the interface address */
129 memcpy(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, addr,
130 sizeof(*addr));
131 if (ioctl(fd, SIOCSIFADDR, (void *)&ifr) < 0) {
132 if (errno != EEXIST) {
133 SYS_ERR(DTUN, LOGL_ERROR, errno,
134 "ioctl(SIOCSIFADDR) failed");
135 } else {
136 SYS_ERR(DTUN, LOGL_NOTICE, errno,
137 "ioctl(SIOCSIFADDR): Address already exists");
138 }
139 close(fd);
140 return -1;
141 }
142 }
143
144 if (dstaddr) { /* Set the destination address */
145 memcpy(&((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr,
146 dstaddr, sizeof(*dstaddr));
147 if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) & ifr) < 0) {
148 SYS_ERR(DTUN, LOGL_ERROR, errno,
149 "ioctl(SIOCSIFDSTADDR) failed");
150 close(fd);
151 return -1;
152 }
153 }
154
155 if (netmask) { /* Set the netmask */
156#if defined(__linux__)
157 memcpy(&((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr,
158 netmask, sizeof(*netmask));
Harald Weltec5efb5b2018-04-25 17:38:51 +0200159#elif defined(__FreeBSD__) || defined (__APPLE__)
160 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
161 netmask->s_addr;
162#endif
163
164 if (ioctl(fd, SIOCSIFNETMASK, (void *)&ifr) < 0) {
165 SYS_ERR(DTUN, LOGL_ERROR, errno,
166 "ioctl(SIOCSIFNETMASK) failed");
167 close(fd);
168 return -1;
169 }
170 }
171
172 close(fd);
173
Harald Weltec5efb5b2018-04-25 17:38:51 +0200174 netdev_sifflags(devname, IFF_UP | IFF_RUNNING);
175
Harald Welte47adad02018-04-25 17:49:28 +0200176 /* On linux the route to the interface is set automatically
177 on FreeBSD we have to do this manually */
Harald Weltec5efb5b2018-04-25 17:38:51 +0200178#if defined(__FreeBSD__) || defined (__APPLE__)
Pau Espin Pedrol2a1cedd2020-04-15 15:21:58 +0200179 netdev_addroute4(dstaddr, addr, &this->netmask);
Harald Weltec5efb5b2018-04-25 17:38:51 +0200180#endif
181
182 return 0;
183}
184
185int netdev_setaddr6(const char *devname, struct in6_addr *addr, struct in6_addr *dstaddr,
186 size_t prefixlen)
187{
188 struct in6_ifreq ifr;
189 int fd;
190
191 memset(&ifr, 0, sizeof(ifr));
192
193#if defined(__linux__)
194 ifr.ifr6_prefixlen = prefixlen;
195 ifr.ifr6_ifindex = if_nametoindex(devname);
196 if (ifr.ifr6_ifindex == 0) {
197 SYS_ERR(DTUN, LOGL_ERROR, 0, "Error getting ifindex for %s\n", devname);
198 return -1;
199 }
200#elif defined(__FreeBSD__) || defined (__APPLE__)
201 strncpy(ifr.ifr_name, devname, IFNAMSIZ);
202#endif
203
204 /* Create a channel to the NET kernel */
205 if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
206 SYS_ERR(DTUN, LOGL_ERROR, 0, "socket() failed");
207 return -1;
208 }
209
210#if defined(__linux__)
211 if (addr) {
212 memcpy(&ifr.ifr6_addr, addr, sizeof(*addr));
213 if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
214 if (errno != EEXIST) {
215 SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR) failed");
216 } else {
217 SYS_ERR(DTUN, LOGL_NOTICE, 0, "ioctl(SIOCSIFADDR): Address already exists");
218 }
219 close(fd);
220 return -1;
221 }
222 }
223
224#if 0
225 /* FIXME: looks like this is not possible/necessary for IPv6? */
226 if (dstaddr) {
227 memcpy(&ifr.ifr6_addr, dstaddr, sizeof(*dstaddr));
228 if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t *) &ifr) < 0) {
229 SYS_ERR(DTUN, LOGL_ERROR, "ioctl(SIOCSIFDSTADDR) failed");
230 close(fd);
231 return -1;
232 }
233 }
234#endif
235
236#elif defined(__FreeBSD__) || defined (__APPLE__)
237 if (addr)
238 memcpy(&ifr.ifr_ifru.ifru_addr, addr, sizeof(ifr.ifr_ifru.ifru_addr));
239 if (dstaddr)
240 memcpy(&ifr.ifr_ifru.ifru_dstaddr, dstaddr, sizeof(ifr.ifr_ifru.ifru_dstaddr));
241
242 if (ioctl(fd, SIOCSIFADDR_IN6, (struct ifreq *)&ifr) < 0) {
243 SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR_IN6) failed");
244 close(fd);
245 return -1;
246 }
247#endif
248
249 close(fd);
250
Harald Weltec5efb5b2018-04-25 17:38:51 +0200251 netdev_sifflags(devname, IFF_UP | IFF_RUNNING);
252
Harald Welte47adad02018-04-25 17:49:28 +0200253 /* On linux the route to the interface is set automatically
254 on FreeBSD we have to do this manually */
Harald Weltec5efb5b2018-04-25 17:38:51 +0200255#if 0 /* FIXME */
256//#if defined(__FreeBSD__) || defined (__APPLE__)
257 netdev_addroute6(dstaddr, addr, prefixlen);
258#endif
259
260 return 0;
261}
262
263int netdev_addaddr4(const char *devname, struct in_addr *addr,
264 struct in_addr *dstaddr, struct in_addr *netmask)
265{
Harald Welte47adad02018-04-25 17:49:28 +0200266 int fd;
Harald Weltec5efb5b2018-04-25 17:38:51 +0200267#if defined(__linux__)
268 struct {
269 struct nlmsghdr n;
270 struct ifaddrmsg i;
271 char buf[TUN_NLBUFSIZE];
272 } req;
273
274 struct sockaddr_nl local;
275 socklen_t addr_len;
Harald Weltec5efb5b2018-04-25 17:38:51 +0200276 int status;
277
278 struct sockaddr_nl nladdr;
279 struct iovec iov;
280 struct msghdr msg;
281
282 memset(&req, 0, sizeof(req));
283 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
284 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
285 req.n.nlmsg_type = RTM_NEWADDR;
286 req.i.ifa_family = AF_INET;
287 req.i.ifa_prefixlen = 32; /* 32 FOR IPv4 */
288 req.i.ifa_flags = 0;
289 req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
290 req.i.ifa_index = if_nametoindex(devname);
291 if (!req.i.ifa_index) {
292 SYS_ERR(DTUN, LOGL_ERROR, errno, "Unable to get ifindex for %s", devname);
293 return -1;
294 }
295
296 netdev_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(*addr));
297 if (dstaddr)
298 netdev_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(*dstaddr));
299
300 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
301 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
302 return -1;
303 }
304
305 memset(&local, 0, sizeof(local));
306 local.nl_family = AF_NETLINK;
307 local.nl_groups = 0;
308
309 if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
310 SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
311 close(fd);
312 return -1;
313 }
314
315 addr_len = sizeof(local);
316 if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
317 SYS_ERR(DTUN, LOGL_ERROR, errno,
318 "getsockname() failed");
319 close(fd);
320 return -1;
321 }
322
323 if (addr_len != sizeof(local)) {
324 SYS_ERR(DTUN, LOGL_ERROR, 0,
325 "Wrong address length %d", addr_len);
326 close(fd);
327 return -1;
328 }
329
330 if (local.nl_family != AF_NETLINK) {
331 SYS_ERR(DTUN, LOGL_ERROR, 0,
332 "Wrong address family %d", local.nl_family);
333 close(fd);
334 return -1;
335 }
336
337 iov.iov_base = (void *)&req.n;
338 iov.iov_len = req.n.nlmsg_len;
339
340 msg.msg_name = (void *)&nladdr;
341 msg.msg_namelen = sizeof(nladdr);
342 msg.msg_iov = &iov;
343 msg.msg_iovlen = 1;
344 msg.msg_control = NULL;
345 msg.msg_controllen = 0;
346 msg.msg_flags = 0;
347
348 memset(&nladdr, 0, sizeof(nladdr));
349 nladdr.nl_family = AF_NETLINK;
350 nladdr.nl_pid = 0;
351 nladdr.nl_groups = 0;
352
353 req.n.nlmsg_seq = 0;
354 req.n.nlmsg_flags |= NLM_F_ACK;
355
356 status = sendmsg(fd, &msg, 0);
357 if (status != req.n.nlmsg_len) {
358 SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status);
359 close(fd);
360 return -1;
361 }
362
363 status = netdev_sifflags(devname, IFF_UP | IFF_RUNNING);
364 if (status == -1) {
365 close(fd);
366 return -1;
367 }
Harald Weltec5efb5b2018-04-25 17:38:51 +0200368#elif defined (__FreeBSD__) || defined (__APPLE__)
Harald Weltec5efb5b2018-04-25 17:38:51 +0200369 struct ifaliasreq areq;
370
371 memset(&areq, 0, sizeof(areq));
372
373 /* Set up interface name */
374 strncpy(areq.ifra_name, devname, IFNAMSIZ);
375 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
376
377 ((struct sockaddr_in *)&areq.ifra_addr)->sin_family = AF_INET;
378 ((struct sockaddr_in *)&areq.ifra_addr)->sin_len =
379 sizeof(areq.ifra_addr);
380 ((struct sockaddr_in *)&areq.ifra_addr)->sin_addr.s_addr = addr->s_addr;
381
382 ((struct sockaddr_in *)&areq.ifra_mask)->sin_family = AF_INET;
383 ((struct sockaddr_in *)&areq.ifra_mask)->sin_len =
384 sizeof(areq.ifra_mask);
385 ((struct sockaddr_in *)&areq.ifra_mask)->sin_addr.s_addr =
386 netmask->s_addr;
387
388 /* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
389 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_family = AF_INET;
390 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_len =
391 sizeof(areq.ifra_broadaddr);
392 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_addr.s_addr =
393 dstaddr->s_addr;
394
395 /* Create a channel to the NET kernel. */
396 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
397 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
398 return -1;
399 }
400
401 if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
402 SYS_ERR(DTUN, LOGL_ERROR, errno,
403 "ioctl(SIOCAIFADDR) failed");
404 close(fd);
405 return -1;
406 }
Harald Welte47adad02018-04-25 17:49:28 +0200407#endif
Harald Weltec5efb5b2018-04-25 17:38:51 +0200408 close(fd);
409 return 0;
Harald Weltec5efb5b2018-04-25 17:38:51 +0200410}
411
412int netdev_addaddr6(const char *devname, struct in6_addr *addr,
413 struct in6_addr *dstaddr, int prefixlen)
414{
Harald Welte47adad02018-04-25 17:49:28 +0200415 int fd;
Harald Weltec5efb5b2018-04-25 17:38:51 +0200416#if defined(__linux__)
417 struct {
418 struct nlmsghdr n;
419 struct ifaddrmsg i;
420 char buf[TUN_NLBUFSIZE];
421 } req;
422
423 struct sockaddr_nl local;
424 socklen_t addr_len;
Harald Weltec5efb5b2018-04-25 17:38:51 +0200425 int status;
426
427 struct sockaddr_nl nladdr;
428 struct iovec iov;
429 struct msghdr msg;
430
431 memset(&req, 0, sizeof(req));
432 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
433 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
434 req.n.nlmsg_type = RTM_NEWADDR;
435 req.i.ifa_family = AF_INET6;
Pau Espin Pedrol55528722020-04-14 15:09:18 +0200436 req.i.ifa_prefixlen = prefixlen; /* 64 FOR IPv6 */
Harald Weltec5efb5b2018-04-25 17:38:51 +0200437 req.i.ifa_flags = 0;
438 req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
439 req.i.ifa_index = if_nametoindex(devname);
440 if (!req.i.ifa_index) {
441 SYS_ERR(DTUN, LOGL_ERROR, errno, "Unable to get ifindex for %s", devname);
442 return -1;
443 }
444
445 netdev_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(*addr));
446 if (dstaddr)
447 netdev_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(*dstaddr));
448
449 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
450 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
451 return -1;
452 }
453
454 memset(&local, 0, sizeof(local));
455 local.nl_family = AF_NETLINK;
456 local.nl_groups = 0;
457
458 if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
459 SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
460 close(fd);
461 return -1;
462 }
463
464 addr_len = sizeof(local);
465 if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
466 SYS_ERR(DTUN, LOGL_ERROR, errno,
467 "getsockname() failed");
468 close(fd);
469 return -1;
470 }
471
472 if (addr_len != sizeof(local)) {
473 SYS_ERR(DTUN, LOGL_ERROR, 0,
474 "Wrong address length %d", addr_len);
475 close(fd);
476 return -1;
477 }
478
479 if (local.nl_family != AF_NETLINK) {
480 SYS_ERR(DTUN, LOGL_ERROR, 0,
481 "Wrong address family %d", local.nl_family);
482 close(fd);
483 return -1;
484 }
485
486 iov.iov_base = (void *)&req.n;
487 iov.iov_len = req.n.nlmsg_len;
488
489 msg.msg_name = (void *)&nladdr;
490 msg.msg_namelen = sizeof(nladdr);
491 msg.msg_iov = &iov;
492 msg.msg_iovlen = 1;
493 msg.msg_control = NULL;
494 msg.msg_controllen = 0;
495 msg.msg_flags = 0;
496
497 memset(&nladdr, 0, sizeof(nladdr));
498 nladdr.nl_family = AF_NETLINK;
499 nladdr.nl_pid = 0;
500 nladdr.nl_groups = 0;
501
502 req.n.nlmsg_seq = 0;
503 req.n.nlmsg_flags |= NLM_F_ACK;
504
505 status = sendmsg(fd, &msg, 0);
506 if (status != req.n.nlmsg_len) {
507 SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status);
508 close(fd);
509 return -1;
510 }
511
512 status = netdev_sifflags(devname, IFF_UP | IFF_RUNNING);
513 if (status == -1) {
514 close(fd);
515 return -1;
516 }
Harald Weltec5efb5b2018-04-25 17:38:51 +0200517#elif defined (__FreeBSD__) || defined (__APPLE__)
Harald Weltec5efb5b2018-04-25 17:38:51 +0200518 struct ifaliasreq areq;
519
520 memset(&areq, 0, sizeof(areq));
521
522 /* Set up interface name */
523 strncpy(areq.ifra_name, devname, IFNAMSIZ);
524 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
525
526 ((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_family = AF_INET6;
527 ((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_len = sizeof(areq.ifra_addr);
528 ((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_addr.s6_addr = addr->s6_addr;
529
530 ((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_family = AF_INET6;
531 ((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_len = sizeof(areq.ifra_mask);
532 ((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_addr.s6_addr = netmask->s6_addr;
533
534 /* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
535 ((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_family = AF_INET6;
536 ((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_len = sizeof(areq.ifra_broadaddr);
537 ((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_addr.s6_addr = dstaddr->s6_addr;
538
539 /* Create a channel to the NET kernel. */
540 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
541 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
542 return -1;
543 }
544
545 if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
546 SYS_ERR(DTUN, LOGL_ERROR, errno,
547 "ioctl(SIOCAIFADDR) failed");
548 close(fd);
549 return -1;
550 }
Harald Welte47adad02018-04-25 17:49:28 +0200551#endif
Harald Weltec5efb5b2018-04-25 17:38:51 +0200552 close(fd);
553 return 0;
Harald Weltec5efb5b2018-04-25 17:38:51 +0200554}
555
Pau Espin Pedrol2a1cedd2020-04-15 15:21:58 +0200556static int netdev_route4(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask, int delete)
Harald Weltec5efb5b2018-04-25 17:38:51 +0200557{
Harald Weltec5efb5b2018-04-25 17:38:51 +0200558 int fd;
Harald Welte47adad02018-04-25 17:49:28 +0200559#if defined(__linux__)
560 struct rtentry r;
Harald Weltec5efb5b2018-04-25 17:38:51 +0200561
562 memset(&r, '\0', sizeof(r));
563 r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
564
565 /* Create a channel to the NET kernel. */
566 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
567 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
568 return -1;
569 }
570
571 r.rt_dst.sa_family = AF_INET;
572 r.rt_gateway.sa_family = AF_INET;
573 r.rt_genmask.sa_family = AF_INET;
574 memcpy(&((struct sockaddr_in *)&r.rt_dst)->sin_addr, dst, sizeof(*dst));
575 memcpy(&((struct sockaddr_in *)&r.rt_gateway)->sin_addr, gateway,
576 sizeof(*gateway));
577 memcpy(&((struct sockaddr_in *)&r.rt_genmask)->sin_addr, mask,
578 sizeof(*mask));
579
580 if (delete) {
581 if (ioctl(fd, SIOCDELRT, (void *)&r) < 0) {
582 SYS_ERR(DTUN, LOGL_ERROR, errno,
583 "ioctl(SIOCDELRT) failed");
584 close(fd);
585 return -1;
586 }
587 } else {
588 if (ioctl(fd, SIOCADDRT, (void *)&r) < 0) {
589 SYS_ERR(DTUN, LOGL_ERROR, errno,
590 "ioctl(SIOCADDRT) failed");
591 close(fd);
592 return -1;
593 }
594 }
Harald Weltec5efb5b2018-04-25 17:38:51 +0200595#elif defined(__FreeBSD__) || defined (__APPLE__)
Harald Weltec5efb5b2018-04-25 17:38:51 +0200596 struct {
597 struct rt_msghdr rt;
598 struct sockaddr_in dst;
599 struct sockaddr_in gate;
600 struct sockaddr_in mask;
601 } req;
Harald Weltec5efb5b2018-04-25 17:38:51 +0200602 struct rt_msghdr *rtm;
603
604 if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
605 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
606 return -1;
607 }
608
609 memset(&req, 0x00, sizeof(req));
610
611 rtm = &req.rt;
612
613 rtm->rtm_msglen = sizeof(req);
614 rtm->rtm_version = RTM_VERSION;
615 if (delete) {
616 rtm->rtm_type = RTM_DELETE;
617 } else {
618 rtm->rtm_type = RTM_ADD;
619 }
620 rtm->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; /* TODO */
621 rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
622 rtm->rtm_pid = getpid();
623 rtm->rtm_seq = 0044; /* TODO */
624
625 req.dst.sin_family = AF_INET;
626 req.dst.sin_len = sizeof(req.dst);
627 req.mask.sin_family = AF_INET;
628 req.mask.sin_len = sizeof(req.mask);
629 req.gate.sin_family = AF_INET;
630 req.gate.sin_len = sizeof(req.gate);
631
632 req.dst.sin_addr.s_addr = dst->s_addr;
633 req.mask.sin_addr.s_addr = mask->s_addr;
634 req.gate.sin_addr.s_addr = gateway->s_addr;
635
636 if (write(fd, rtm, rtm->rtm_msglen) < 0) {
637 SYS_ERR(DTUN, LOGL_ERROR, errno, "write() failed");
638 close(fd);
639 return -1;
640 }
Harald Welte47adad02018-04-25 17:49:28 +0200641#endif
Harald Weltec5efb5b2018-04-25 17:38:51 +0200642 close(fd);
643 return 0;
Harald Weltec5efb5b2018-04-25 17:38:51 +0200644}
645
Pau Espin Pedrole2b09612020-04-15 15:09:30 +0200646static int netdev_route6(struct in6_addr *dst, struct in6_addr *gateway, int prefixlen, const char *gw_iface, int delete)
647{
648 int fd;
649#if defined(__linux__)
650 struct in6_rtmsg r;
651 struct ifreq ifr;
652
653 memset(&r, 0, sizeof(r));
654 r.rtmsg_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
655 r.rtmsg_metric = 1;
656
657 /* Create a channel to the NET kernel. */
658 if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
659 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
660 return -1;
661 }
662
663 if (gw_iface) {
664 strncpy(ifr.ifr_name, gw_iface, IFNAMSIZ);
665 ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
666 if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
667 SYS_ERR(DTUN, LOGL_ERROR, errno,
668 "ioctl(SIOCGIFINDEX) failed");
669 close(fd);
670 return -1;
671 }
672 r.rtmsg_ifindex = ifr.ifr_ifindex;
673 }
674
675 memcpy(&r.rtmsg_dst, dst->s6_addr, sizeof(struct in6_addr));
676 memcpy(&r.rtmsg_gateway, gateway->s6_addr, sizeof(struct in6_addr));
677 r.rtmsg_dst_len = prefixlen;
678
679 if (delete) {
680 if (ioctl(fd, SIOCDELRT, (void *)&r) < 0) {
681 SYS_ERR(DTUN, LOGL_ERROR, errno,
682 "ioctl(SIOCDELRT) failed");
683 close(fd);
684 return -1;
685 }
686 } else {
687 if (ioctl(fd, SIOCADDRT, (void *)&r) < 0) {
688 SYS_ERR(DTUN, LOGL_ERROR, errno,
689 "ioctl(SIOCADDRT) failed");
690 close(fd);
691 return -1;
692 }
693 }
694 close(fd);
695#endif
696 return 0;
697}
698
Pau Espin Pedrol2a1cedd2020-04-15 15:21:58 +0200699int netdev_addroute4(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask)
Harald Weltec5efb5b2018-04-25 17:38:51 +0200700{
Pau Espin Pedrol2a1cedd2020-04-15 15:21:58 +0200701 return netdev_route4(dst, gateway, mask, 0);
Harald Weltec5efb5b2018-04-25 17:38:51 +0200702}
703
Pau Espin Pedrol2a1cedd2020-04-15 15:21:58 +0200704int netdev_delroute4(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask)
Harald Weltec5efb5b2018-04-25 17:38:51 +0200705{
Pau Espin Pedrol2a1cedd2020-04-15 15:21:58 +0200706 return netdev_route4(dst, gateway, mask, 1);
Harald Weltec5efb5b2018-04-25 17:38:51 +0200707}
708
Pau Espin Pedrole2b09612020-04-15 15:09:30 +0200709int netdev_addroute6(struct in6_addr *dst, struct in6_addr *gateway, int prefixlen, const char *gw_iface)
710{
711 return netdev_route6(dst, gateway, prefixlen, gw_iface, 0);
712}
713
714int netdev_delroute6(struct in6_addr *dst, struct in6_addr *gateway, int prefixlen, const char *gw_iface)
715{
716 return netdev_route6(dst, gateway, prefixlen, gw_iface, 1);
717}
718
719
720
Harald Weltec5efb5b2018-04-25 17:38:51 +0200721#include <ifaddrs.h>
722
723/*! Obtain the local address of a network device
724 * \param[in] devname Target device owning the IP
725 * \param[out] prefix_list List of prefix structures to fill with each IPv4/6 and prefix length found.
726 * \param[in] prefix_size Amount of elements allowed to be fill in the prefix_list array.
727 * \param[in] flags Specify which kind of IP to look for: IP_TYPE_IPv4, IP_TYPE_IPv6_LINK, IP_TYPE_IPv6_NONLINK
728 * \returns The number of ips found following the criteria specified by flags, -1 on error.
729 *
730 * This function will fill prefix_list with up to prefix_size IPs following the
731 * criteria specified by flags parameter. It returns the number of IPs matching
732 * the criteria. As a result, the number returned can be bigger than
733 * prefix_size. It can be used with prefix_size=0 to get an estimate of the size
734 * needed for prefix_list.
735 */
736int netdev_ip_local_get(const char *devname, struct in46_prefix *prefix_list, size_t prefix_size, int flags)
737{
738 static const uint8_t ll_prefix[] = { 0xfe,0x80, 0,0, 0,0, 0,0 };
739 struct ifaddrs *ifaddr, *ifa;
740 struct in46_addr netmask;
741 size_t count = 0;
742 bool is_ipv6_ll;
743
744 if (getifaddrs(&ifaddr) == -1) {
745 return -1;
746 }
747
748 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
749 if (ifa->ifa_addr == NULL)
750 continue;
751
752 if (strcmp(ifa->ifa_name, devname))
753 continue;
754
755 if (ifa->ifa_addr->sa_family == AF_INET && (flags & IP_TYPE_IPv4)) {
756 struct sockaddr_in *sin4 = (struct sockaddr_in *) ifa->ifa_addr;
757 struct sockaddr_in *netmask4 = (struct sockaddr_in *) ifa->ifa_netmask;
758
759 if (count < prefix_size) {
760 netmask.len = sizeof(netmask4->sin_addr);
761 netmask.v4 = netmask4->sin_addr;
762 prefix_list[count].addr.len = sizeof(sin4->sin_addr);
763 prefix_list[count].addr.v4 = sin4->sin_addr;
764 prefix_list[count].prefixlen = in46a_netmasklen(&netmask);
765 }
766 count++;
767 }
768
769 if (ifa->ifa_addr->sa_family == AF_INET6 && (flags & IP_TYPE_IPv6)) {
770 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) ifa->ifa_addr;
771 struct sockaddr_in6 *netmask6 = (struct sockaddr_in6 *) ifa->ifa_netmask;
772
773 is_ipv6_ll = !memcmp(sin6->sin6_addr.s6_addr, ll_prefix, sizeof(ll_prefix));
774 if ((flags & IP_TYPE_IPv6_NONLINK) && is_ipv6_ll)
775 continue;
776 if ((flags & IP_TYPE_IPv6_LINK) && !is_ipv6_ll)
777 continue;
778
779 if (count < prefix_size) {
780 netmask.len = sizeof(netmask6->sin6_addr);
781 netmask.v6 = netmask6->sin6_addr;
782 prefix_list[count].addr.len = sizeof(sin6->sin6_addr);
783 prefix_list[count].addr.v6 = sin6->sin6_addr;
784 prefix_list[count].prefixlen = in46a_netmasklen(&netmask);
785 }
786 count++;
787 }
788 }
789
790 freeifaddrs(ifaddr);
791 return count;
792}