1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3 * Solaris interface driver for dhcpcd
4 * Copyright (c) 2016-2021 Roy Marples <roy@marples.name>
5 * All rights reserved
6
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <assert.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <ifaddrs.h>
33 #include <libdlpi.h>
34 #include <kstat.h>
35 #include <stddef.h>
36 #include <stdlib.h>
37 #include <stropts.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #include <inet/ip.h>
42
43 #include <net/if_dl.h>
44 #include <net/if_types.h>
45
46 #include <netinet/if_ether.h>
47 #include <netinet/udp.h>
48
49 #include <sys/ioctl.h>
50 #include <sys/mac.h>
51 #include <sys/pfmod.h>
52 #include <sys/tihdr.h>
53 #include <sys/utsname.h>
54
55 /* Private libsocket interface we can hook into to get
56 * a better getifaddrs(3).
57 * From libsocket_priv.h, which is not always distributed so is here. */
58 extern int getallifaddrs(sa_family_t, struct ifaddrs **, int64_t);
59
60 #include "config.h"
61 #include "bpf.h"
62 #include "common.h"
63 #include "dhcp.h"
64 #include "if.h"
65 #include "if-options.h"
66 #include "ipv4.h"
67 #include "ipv6.h"
68 #include "ipv6nd.h"
69 #include "logerr.h"
70 #include "route.h"
71 #include "sa.h"
72
73 #ifndef ARP_MOD_NAME
74 # define ARP_MOD_NAME "arp"
75 #endif
76
77 #ifndef RT_ROUNDUP
78 #define RT_ROUNDUP(a) \
79 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(int32_t) - 1))) : sizeof(int32_t))
80 #define RT_ADVANCE(x, n) ((x) += RT_ROUNDUP(sa_len((n))))
81 #endif
82
83 #define COPYOUT(sin, sa) do { \
84 if ((sa) && ((sa)->sa_family == AF_INET)) \
85 (sin) = ((const struct sockaddr_in *)(const void *) \
86 (sa))->sin_addr; \
87 } while (0)
88
89 #define COPYOUT6(sin, sa) do { \
90 if ((sa) && ((sa)->sa_family == AF_INET6)) \
91 (sin) = ((const struct sockaddr_in6 *)(const void *) \
92 (sa))->sin6_addr; \
93 } while (0)
94
95 #define COPYSA(dst, src) memcpy((dst), (src), sa_len((src)))
96
97 struct priv {
98 #ifdef INET6
99 int pf_inet6_fd;
100 #endif
101 };
102
103 struct rtm
104 {
105 struct rt_msghdr hdr;
106 char buffer[sizeof(struct sockaddr_storage) * RTAX_MAX];
107 };
108
109 static int if_plumb(int, const struct dhcpcd_ctx *, int, const char *);
110
111 int
os_init(void)112 os_init(void)
113 {
114 return 0;
115 }
116
117 int
if_init(struct interface * ifp)118 if_init(struct interface *ifp)
119 {
120
121 #ifdef INET
122 if (if_plumb(RTM_NEWADDR, ifp->ctx, AF_INET, ifp->name) == -1 &&
123 errno != EEXIST)
124 return -1;
125 #endif
126
127 #ifdef INET6
128 if (if_plumb(RTM_NEWADDR, ifp->ctx, AF_INET6, ifp->name) == -1 &&
129 errno != EEXIST)
130 return -1;
131 #endif
132
133 if (ifp->index == 0)
134 ifp->index = if_nametoindex(ifp->name);
135
136 return 0;
137 }
138
139 int
if_conf(__unused struct interface * ifp)140 if_conf(__unused struct interface *ifp)
141 {
142
143 return 0;
144 }
145
146 int
if_opensockets_os(struct dhcpcd_ctx * ctx)147 if_opensockets_os(struct dhcpcd_ctx *ctx)
148 {
149 struct priv *priv;
150 int n;
151
152 if ((priv = malloc(sizeof(*priv))) == NULL)
153 return -1;
154 ctx->priv = priv;
155
156 #ifdef INET6
157 priv->pf_inet6_fd = xsocket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
158 /* Don't return an error so we at least work on kernels witout INET6
159 * even though we expect INET6 support.
160 * We will fail noisily elsewhere anyway. */
161 #endif
162
163 ctx->link_fd = socket(PF_ROUTE,
164 SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
165
166 if (ctx->link_fd == -1) {
167 free(ctx->priv);
168 return -1;
169 }
170
171 /* Ignore our own route(4) messages.
172 * Sadly there is no way of doing this for route(4) messages
173 * generated from addresses we add/delete. */
174 n = 0;
175 if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_USELOOPBACK,
176 &n, sizeof(n)) == -1)
177 logerr("%s: SO_USELOOPBACK", __func__);
178
179 return 0;
180 }
181
182 void
if_closesockets_os(struct dhcpcd_ctx * ctx)183 if_closesockets_os(struct dhcpcd_ctx *ctx)
184 {
185 #ifdef INET6
186 struct priv *priv;
187
188 priv = (struct priv *)ctx->priv;
189 if (priv->pf_inet6_fd != -1)
190 close(priv->pf_inet6_fd);
191 #endif
192
193 /* each interface should have closed itself */
194 free(ctx->priv);
195 }
196
197 int
if_setmac(struct interface * ifp,void * mac,uint8_t maclen)198 if_setmac(struct interface *ifp, void *mac, uint8_t maclen)
199 {
200
201 errno = ENOTSUP;
202 return -1;
203 }
204
205 int
if_carrier(struct interface * ifp,__unused const void * ifadata)206 if_carrier(struct interface *ifp, __unused const void *ifadata)
207 {
208 kstat_ctl_t *kcp;
209 kstat_t *ksp;
210 kstat_named_t *knp;
211 link_state_t linkstate;
212
213 if (if_getflags(ifp) == -1)
214 return LINK_UNKNOWN;
215
216 kcp = kstat_open();
217 if (kcp == NULL)
218 goto err;
219 ksp = kstat_lookup(kcp, UNCONST("link"), 0, ifp->name);
220 if (ksp == NULL)
221 goto err;
222 if (kstat_read(kcp, ksp, NULL) == -1)
223 goto err;
224 knp = kstat_data_lookup(ksp, UNCONST("link_state"));
225 if (knp == NULL)
226 goto err;
227 if (knp->data_type != KSTAT_DATA_UINT32)
228 goto err;
229 linkstate = (link_state_t)knp->value.ui32;
230 kstat_close(kcp);
231
232 switch (linkstate) {
233 case LINK_STATE_UP:
234 ifp->flags |= IFF_UP;
235 return LINK_UP;
236 case LINK_STATE_DOWN:
237 return LINK_DOWN;
238 default:
239 return LINK_UNKNOWN;
240 }
241
242 err:
243 if (kcp != NULL)
244 kstat_close(kcp);
245 return LINK_UNKNOWN;
246 }
247
248 bool
if_roaming(__unused struct interface * ifp)249 if_roaming(__unused struct interface *ifp)
250 {
251
252 return false;
253 }
254
255 int
if_mtu_os(const struct interface * ifp)256 if_mtu_os(const struct interface *ifp)
257 {
258 dlpi_handle_t dh;
259 dlpi_info_t dlinfo;
260 int mtu;
261
262 if (dlpi_open(ifp->name, &dh, 0) != DLPI_SUCCESS)
263 return -1;
264 if (dlpi_info(dh, &dlinfo, 0) == DLPI_SUCCESS)
265 mtu = dlinfo.di_max_sdu;
266 else
267 mtu = -1;
268 dlpi_close(dh);
269 return mtu;
270 }
271
272 int
if_getssid(__unused struct interface * ifp)273 if_getssid(__unused struct interface *ifp)
274 {
275
276 errno = ENOTSUP;
277 return -1;
278 }
279
280 /* XXX work out TAP interfaces? */
281 bool
if_ignore(__unused struct dhcpcd_ctx * ctx,__unused const char * ifname)282 if_ignore(__unused struct dhcpcd_ctx *ctx, __unused const char *ifname)
283 {
284
285 return false;
286 }
287
288 unsigned short
if_vlanid(__unused const struct interface * ifp)289 if_vlanid(__unused const struct interface *ifp)
290 {
291
292 return 0;
293 }
294
295 int
if_vimaster(__unused struct dhcpcd_ctx * ctx,__unused const char * ifname)296 if_vimaster(__unused struct dhcpcd_ctx *ctx, __unused const char *ifname)
297 {
298
299 return 0;
300 }
301
302 int
if_machinearch(__unused char * str,__unused size_t len)303 if_machinearch(__unused char *str, __unused size_t len)
304 {
305
306 /* There is no extra data really.
307 * isainfo -v does return amd64, but also i386. */
308 return 0;
309 }
310
311 struct linkwalk {
312 struct ifaddrs *lw_ifa;
313 int lw_error;
314 };
315
316 static boolean_t
if_newaddr(const char * ifname,void * arg)317 if_newaddr(const char *ifname, void *arg)
318 {
319 struct linkwalk *lw = arg;
320 int error;
321 struct ifaddrs *ifa;
322 dlpi_handle_t dh;
323 dlpi_info_t dlinfo;
324 uint8_t pa[DLPI_PHYSADDR_MAX];
325 size_t pa_len;
326 struct sockaddr_dl *sdl;
327
328 ifa = NULL;
329 error = dlpi_open(ifname, &dh, 0);
330 if (error == DLPI_ENOLINK) /* Just vanished or in global zone */
331 return B_FALSE;
332 if (error != DLPI_SUCCESS)
333 goto failed1;
334 if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS)
335 goto failed;
336
337 /* For some reason, dlpi_info won't return the
338 * physical address, it's all zero's.
339 * So cal dlpi_get_physaddr. */
340 pa_len = DLPI_PHYSADDR_MAX;
341 if (dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR,
342 pa, &pa_len) != DLPI_SUCCESS)
343 goto failed;
344
345 if ((ifa = calloc(1, sizeof(*ifa))) == NULL)
346 goto failed;
347 if ((ifa->ifa_name = strdup(ifname)) == NULL)
348 goto failed;
349 if ((sdl = calloc(1, sizeof(*sdl))) == NULL)
350 goto failed;
351
352 ifa->ifa_addr = (struct sockaddr *)sdl;
353 sdl->sdl_index = if_nametoindex(ifname);
354 sdl->sdl_family = AF_LINK;
355 switch (dlinfo.di_mactype) {
356 case DL_ETHER:
357 sdl->sdl_type = IFT_ETHER;
358 break;
359 case DL_IB:
360 sdl->sdl_type = IFT_IB;
361 break;
362 default:
363 sdl->sdl_type = IFT_OTHER;
364 break;
365 }
366
367 sdl->sdl_alen = pa_len;
368 memcpy(sdl->sdl_data, pa, pa_len);
369
370 ifa->ifa_next = lw->lw_ifa;
371 lw->lw_ifa = ifa;
372 dlpi_close(dh);
373 return B_FALSE;
374
375 failed:
376 dlpi_close(dh);
377 if (ifa != NULL) {
378 free(ifa->ifa_name);
379 free(ifa->ifa_addr);
380 free(ifa);
381 }
382 failed1:
383 lw->lw_error = errno;
384 return B_TRUE;
385 }
386
387 /* Creates an empty sockaddr_dl for lo0. */
388 static struct ifaddrs *
if_ifa_lo0(void)389 if_ifa_lo0(void)
390 {
391 struct ifaddrs *ifa;
392 struct sockaddr_dl *sdl;
393
394 if ((ifa = calloc(1, sizeof(*ifa))) == NULL)
395 return NULL;
396 if ((sdl = calloc(1, sizeof(*sdl))) == NULL) {
397 free(ifa);
398 return NULL;
399 }
400 if ((ifa->ifa_name = strdup("lo0")) == NULL) {
401 free(ifa);
402 free(sdl);
403 return NULL;
404 }
405
406 ifa->ifa_addr = (struct sockaddr *)sdl;
407 ifa->ifa_flags = IFF_LOOPBACK;
408 sdl->sdl_family = AF_LINK;
409 sdl->sdl_index = if_nametoindex("lo0");
410
411 return ifa;
412 }
413
414 /* getifaddrs(3) does not support AF_LINK, strips aliases and won't
415 * report addresses that are not UP.
416 * As such it's just totally useless, so we need to roll our own. */
417 int
if_getifaddrs(struct ifaddrs ** ifap)418 if_getifaddrs(struct ifaddrs **ifap)
419 {
420 struct linkwalk lw;
421 struct ifaddrs *ifa;
422
423 /* Private libc function which we should not have to call
424 * to get non UP addresses. */
425 if (getallifaddrs(AF_UNSPEC, &lw.lw_ifa, 0) == -1)
426 return -1;
427
428 /* Start with some AF_LINK addresses. */
429 lw.lw_error = 0;
430 dlpi_walk(if_newaddr, &lw, 0);
431 if (lw.lw_error != 0) {
432 freeifaddrs(lw.lw_ifa);
433 errno = lw.lw_error;
434 return -1;
435 }
436
437 /* lo0 doesn't appear in dlpi_walk, so fudge it. */
438 if ((ifa = if_ifa_lo0()) == NULL) {
439 freeifaddrs(lw.lw_ifa);
440 return -1;
441 }
442 ifa->ifa_next = lw.lw_ifa;
443
444 *ifap = ifa;
445 return 0;
446 }
447
448 static void
if_linkaddr(struct sockaddr_dl * sdl,const struct interface * ifp)449 if_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp)
450 {
451
452 memset(sdl, 0, sizeof(*sdl));
453 sdl->sdl_family = AF_LINK;
454 sdl->sdl_nlen = sdl->sdl_alen = sdl->sdl_slen = 0;
455 sdl->sdl_index = (unsigned short)ifp->index;
456 }
457
458 static int
get_addrs(int type,const void * data,size_t data_len,const struct sockaddr ** sa)459 get_addrs(int type, const void *data, size_t data_len,
460 const struct sockaddr **sa)
461 {
462 const char *cp, *ep;
463 int i;
464
465 cp = data;
466 ep = cp + data_len;
467 for (i = 0; i < RTAX_MAX; i++) {
468 if (type & (1 << i)) {
469 if (cp >= ep) {
470 errno = EINVAL;
471 return -1;
472 }
473 sa[i] = (const struct sockaddr *)cp;
474 RT_ADVANCE(cp, sa[i]);
475 } else
476 sa[i] = NULL;
477 }
478
479 return 0;
480 }
481
482 static struct interface *
if_findsdl(struct dhcpcd_ctx * ctx,const struct sockaddr_dl * sdl)483 if_findsdl(struct dhcpcd_ctx *ctx, const struct sockaddr_dl *sdl)
484 {
485
486 if (sdl->sdl_index)
487 return if_findindex(ctx->ifaces, sdl->sdl_index);
488
489 if (sdl->sdl_nlen) {
490 char ifname[IF_NAMESIZE];
491
492 memcpy(ifname, sdl->sdl_data, sdl->sdl_nlen);
493 ifname[sdl->sdl_nlen] = '\0';
494 return if_find(ctx->ifaces, ifname);
495 }
496 if (sdl->sdl_alen) {
497 struct interface *ifp;
498
499 TAILQ_FOREACH(ifp, ctx->ifaces, next) {
500 if (ifp->hwlen == sdl->sdl_alen &&
501 memcmp(ifp->hwaddr,
502 sdl->sdl_data, sdl->sdl_alen) == 0)
503 return ifp;
504 }
505 }
506
507 errno = ENOENT;
508 return NULL;
509 }
510
511 static struct interface *
if_findsa(struct dhcpcd_ctx * ctx,const struct sockaddr * sa)512 if_findsa(struct dhcpcd_ctx *ctx, const struct sockaddr *sa)
513 {
514 if (sa == NULL) {
515 errno = EINVAL;
516 return NULL;
517 }
518
519 switch (sa->sa_family) {
520 case AF_LINK:
521 {
522 const struct sockaddr_dl *sdl;
523
524 sdl = (const void *)sa;
525 return if_findsdl(ctx, sdl);
526 }
527 #ifdef INET
528 case AF_INET:
529 {
530 const struct sockaddr_in *sin;
531 struct ipv4_addr *ia;
532
533 sin = (const void *)sa;
534 if ((ia = ipv4_findmaskaddr(ctx, &sin->sin_addr)))
535 return ia->iface;
536 if ((ia = ipv4_findmaskbrd(ctx, &sin->sin_addr)))
537 return ia->iface;
538 break;
539 }
540 #endif
541 #ifdef INET6
542 case AF_INET6:
543 {
544 const struct sockaddr_in6 *sin;
545 struct ipv6_addr *ia;
546
547 sin = (const void *)sa;
548 if ((ia = ipv6_findmaskaddr(ctx, &sin->sin6_addr)))
549 return ia->iface;
550 break;
551 }
552 #endif
553 default:
554 errno = EAFNOSUPPORT;
555 return NULL;
556 }
557
558 errno = ENOENT;
559 return NULL;
560 }
561
562 static void
if_route0(struct dhcpcd_ctx * ctx,struct rtm * rtmsg,unsigned char cmd,const struct rt * rt)563 if_route0(struct dhcpcd_ctx *ctx, struct rtm *rtmsg,
564 unsigned char cmd, const struct rt *rt)
565 {
566 struct rt_msghdr *rtm;
567 char *bp = rtmsg->buffer;
568 socklen_t sl;
569 bool gateway_unspec;
570
571 /* WARNING: Solaris will not allow you to delete RTF_KERNEL routes.
572 * This includes subnet/prefix routes. */
573
574 #define ADDSA(sa) do { \
575 sl = sa_len((sa)); \
576 memcpy(bp, (sa), sl); \
577 bp += RT_ROUNDUP(sl); \
578 } while (/* CONSTCOND */ 0)
579
580 memset(rtmsg, 0, sizeof(*rtmsg));
581 rtm = &rtmsg->hdr;
582 rtm->rtm_version = RTM_VERSION;
583 rtm->rtm_type = cmd;
584 rtm->rtm_seq = ++ctx->seq;
585 rtm->rtm_flags = rt->rt_flags;
586 rtm->rtm_addrs = RTA_DST | RTA_GATEWAY;
587
588 gateway_unspec = sa_is_unspecified(&rt->rt_gateway);
589
590 if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
591 bool netmask_bcast = sa_is_allones(&rt->rt_netmask);
592
593 rtm->rtm_flags |= RTF_UP;
594 if (!(rtm->rtm_flags & RTF_REJECT) &&
595 !sa_is_loopback(&rt->rt_gateway))
596 {
597 rtm->rtm_addrs |= RTA_IFP;
598 /* RTA_IFA is currently ignored by the kernel.
599 * RTA_SRC and RTF_SETSRC look like what we want,
600 * but they don't work with RTF_GATEWAY.
601 * We set RTA_IFA just in the hope that the
602 * kernel will one day support this. */
603 if (!sa_is_unspecified(&rt->rt_ifa))
604 rtm->rtm_addrs |= RTA_IFA;
605 }
606
607 if (netmask_bcast)
608 rtm->rtm_flags |= RTF_HOST;
609 else if (!gateway_unspec)
610 rtm->rtm_flags |= RTF_GATEWAY;
611
612 if (rt->rt_dflags & RTDF_STATIC)
613 rtm->rtm_flags |= RTF_STATIC;
614
615 if (rt->rt_mtu != 0) {
616 rtm->rtm_inits |= RTV_MTU;
617 rtm->rtm_rmx.rmx_mtu = rt->rt_mtu;
618 }
619 }
620
621 if (!(rtm->rtm_flags & RTF_HOST))
622 rtm->rtm_addrs |= RTA_NETMASK;
623
624 ADDSA(&rt->rt_dest);
625
626 if (gateway_unspec)
627 ADDSA(&rt->rt_ifa);
628 else
629 ADDSA(&rt->rt_gateway);
630
631 if (rtm->rtm_addrs & RTA_NETMASK)
632 ADDSA(&rt->rt_netmask);
633
634 if (rtm->rtm_addrs & RTA_IFP) {
635 struct sockaddr_dl sdl;
636
637 if_linkaddr(&sdl, rt->rt_ifp);
638 ADDSA((struct sockaddr *)&sdl);
639 }
640
641 if (rtm->rtm_addrs & RTA_IFA)
642 ADDSA(&rt->rt_ifa);
643
644 #if 0
645 if (rtm->rtm_addrs & RTA_SRC)
646 ADDSA(&rt->rt_ifa);
647 #endif
648
649 rtm->rtm_msglen = (unsigned short)(bp - (char *)rtm);
650 }
651
652 int
if_route(unsigned char cmd,const struct rt * rt)653 if_route(unsigned char cmd, const struct rt *rt)
654 {
655 struct rtm rtm;
656 struct dhcpcd_ctx *ctx = rt->rt_ifp->ctx;
657
658 if_route0(ctx, &rtm, cmd, rt);
659
660 if (write(ctx->link_fd, &rtm, rtm.hdr.rtm_msglen) == -1)
661 return -1;
662 return 0;
663 }
664
665 static int
if_copyrt(struct dhcpcd_ctx * ctx,struct rt * rt,const struct rt_msghdr * rtm)666 if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)
667 {
668 const struct sockaddr *rti_info[RTAX_MAX];
669
670 if (~rtm->rtm_addrs & RTA_DST) {
671 errno = EINVAL;
672 return -1;
673 }
674
675 if (get_addrs(rtm->rtm_addrs, (const char *)rtm + sizeof(*rtm),
676 rtm->rtm_msglen - sizeof(*rtm), rti_info) == -1)
677 return -1;
678
679 memset(rt, 0, sizeof(*rt));
680
681 rt->rt_flags = (unsigned int)rtm->rtm_flags;
682 COPYSA(&rt->rt_dest, rti_info[RTAX_DST]);
683 if (rtm->rtm_addrs & RTA_NETMASK)
684 COPYSA(&rt->rt_netmask, rti_info[RTAX_NETMASK]);
685
686 /* dhcpcd likes an unspecified gateway to indicate via the link.
687 * However we need to know if gateway was a link with an address. */
688 if (rtm->rtm_addrs & RTA_GATEWAY) {
689 if (rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) {
690 const struct sockaddr_dl *sdl;
691
692 sdl = (const struct sockaddr_dl*)
693 (const void *)rti_info[RTAX_GATEWAY];
694 if (sdl->sdl_alen != 0)
695 rt->rt_dflags |= RTDF_GATELINK;
696 } else if (rtm->rtm_flags & RTF_GATEWAY)
697 COPYSA(&rt->rt_gateway, rti_info[RTAX_GATEWAY]);
698 }
699
700 if (rtm->rtm_addrs & RTA_SRC)
701 COPYSA(&rt->rt_ifa, rti_info[RTAX_SRC]);
702 rt->rt_mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;
703
704 if (rtm->rtm_index)
705 rt->rt_ifp = if_findindex(ctx->ifaces, rtm->rtm_index);
706 else if (rtm->rtm_addrs & RTA_IFP)
707 rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_IFP]);
708 else if (rtm->rtm_addrs & RTA_GATEWAY)
709 rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_GATEWAY]);
710 else
711 rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_DST]);
712
713 if (rt->rt_ifp == NULL && rtm->rtm_type == RTM_MISS)
714 rt->rt_ifp = if_loopback(ctx);
715
716 if (rt->rt_ifp == NULL) {
717 errno = ESRCH;
718 return -1;
719 }
720
721 return 0;
722 }
723
724 static struct rt *
if_route_get(struct dhcpcd_ctx * ctx,struct rt * rt)725 if_route_get(struct dhcpcd_ctx *ctx, struct rt *rt)
726 {
727 struct rtm rtm;
728 int s;
729 struct iovec iov = { .iov_base = &rtm, .iov_len = sizeof(rtm) };
730 struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };
731 ssize_t len;
732 struct rt *rtw = rt;
733
734 if_route0(ctx, &rtm, RTM_GET, rt);
735 rt = NULL;
736 s = socket(PF_ROUTE, SOCK_RAW | SOCK_CLOEXEC, 0);
737 if (s == -1)
738 return NULL;
739 if (write(s, &rtm, rtm.hdr.rtm_msglen) == -1)
740 goto out;
741 if ((len = recvmsg(s, &msg, 0)) == -1)
742 goto out;
743 if ((size_t)len < sizeof(rtm.hdr) || len < rtm.hdr.rtm_msglen) {
744 errno = EINVAL;
745 goto out;
746 }
747 if (if_copyrt(ctx, rtw, &rtm.hdr) == -1)
748 goto out;
749 rt = rtw;
750
751 out:
752 close(s);
753 return rt;
754 }
755
756 static int
if_finishrt(struct dhcpcd_ctx * ctx,struct rt * rt)757 if_finishrt(struct dhcpcd_ctx *ctx, struct rt *rt)
758 {
759 int mtu;
760
761 /* Solaris has a subnet route with the gateway
762 * of the owning address.
763 * dhcpcd has a blank gateway here to indicate a
764 * subnet route. */
765 if (!sa_is_unspecified(&rt->rt_dest) &&
766 !sa_is_unspecified(&rt->rt_gateway))
767 {
768 switch(rt->rt_gateway.sa_family) {
769 #ifdef INET
770 case AF_INET:
771 {
772 struct in_addr *in;
773
774 in = &satosin(&rt->rt_gateway)->sin_addr;
775 if (ipv4_findaddr(ctx, in))
776 in->s_addr = INADDR_ANY;
777 break;
778 }
779 #endif
780 #ifdef INET6
781 case AF_INET6:
782 {
783 struct in6_addr *in6;
784
785 in6 = &satosin6(&rt->rt_gateway)->sin6_addr;
786 if (ipv6_findaddr(ctx, in6, 0))
787 *in6 = in6addr_any;
788 break;
789 }
790 #endif
791 }
792 }
793
794 /* Solaris doesn't set interfaces for some routes.
795 * This sucks, so we need to call RTM_GET to
796 * work out the interface. */
797 if (rt->rt_ifp == NULL) {
798 if (if_route_get(ctx, rt) == NULL) {
799 rt->rt_ifp = if_loopback(ctx);
800 if (rt->rt_ifp == NULL)
801 return - 1;
802 }
803 }
804
805 /* Solaris likes to set route MTU to match
806 * interface MTU when adding routes.
807 * This confuses dhcpcd as it expects MTU to be 0
808 * when no explicit MTU has been set. */
809 mtu = if_getmtu(rt->rt_ifp);
810 if (mtu == -1)
811 return -1;
812 if (rt->rt_mtu == (unsigned int)mtu)
813 rt->rt_mtu = 0;
814
815 return 0;
816 }
817
818 static int
if_addrflags0(int fd,int af,const char * ifname)819 if_addrflags0(int fd, int af, const char *ifname)
820 {
821 struct lifreq lifr;
822 int flags;
823
824 memset(&lifr, 0, sizeof(lifr));
825 strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
826 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
827 return -1;
828
829 flags = 0;
830 if (lifr.lifr_flags & IFF_DUPLICATE)
831 flags |= af == AF_INET6 ? IN6_IFF_DUPLICATED:IN_IFF_DUPLICATED;
832 else if (!(lifr.lifr_flags & IFF_UP))
833 flags |= af == AF_INET6 ? IN6_IFF_TENTATIVE:IN_IFF_TENTATIVE;
834 return flags;
835 }
836
837 static int
if_rtm(struct dhcpcd_ctx * ctx,const struct rt_msghdr * rtm)838 if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
839 {
840 const struct sockaddr *sa;
841 struct rt rt;
842
843 if (rtm->rtm_msglen < sizeof(*rtm) + sizeof(*sa)) {
844 errno = EINVAL;
845 return -1;
846 }
847
848 if (if_copyrt(ctx, &rt, rtm) == -1 && errno != ESRCH)
849 return -1;
850
851 #ifdef INET6
852 /*
853 * BSD announces host routes.
854 * As such, we should be notified of reachability by its
855 * existance with a hardware address.
856 * Ensure we don't call this for a newly incomplete state.
857 */
858 if (rt.rt_dest.sa_family == AF_INET6 &&
859 (rt.rt_flags & RTF_HOST || rtm->rtm_type == RTM_MISS) &&
860 !(rtm->rtm_type == RTM_ADD && !(rt.rt_dflags & RTDF_GATELINK)))
861 {
862 bool reachable;
863
864 reachable = (rtm->rtm_type == RTM_ADD ||
865 rtm->rtm_type == RTM_CHANGE) &&
866 rt.rt_dflags & RTDF_GATELINK;
867 ipv6nd_neighbour(ctx, &rt.rt_ss_dest.sin6.sin6_addr, reachable);
868 }
869 #endif
870
871 if (if_finishrt(ctx, &rt) == -1)
872 return -1;
873 rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid);
874 return 0;
875 }
876
877 static bool
if_getalias(struct interface * ifp,const struct sockaddr * sa,char * alias)878 if_getalias(struct interface *ifp, const struct sockaddr *sa, char *alias)
879 {
880 struct ifaddrs *ifaddrs, *ifa;
881 struct interface *ifpx;
882 bool found;
883
884 ifaddrs = NULL;
885 if (getallifaddrs(sa->sa_family, &ifaddrs, 0) == -1)
886 return false;
887 found = false;
888 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
889 if (ifa->ifa_addr == NULL)
890 continue;
891 if (sa_cmp(sa, ifa->ifa_addr) != 0)
892 continue;
893 /* Check it's for the right interace. */
894 ifpx = if_find(ifp->ctx->ifaces, ifa->ifa_name);
895 if (ifp == ifpx) {
896 strlcpy(alias, ifa->ifa_name, IF_NAMESIZE);
897 found = true;
898 break;
899 }
900 }
901 freeifaddrs(ifaddrs);
902 return found;
903 }
904
905 static int
if_getbrdaddr(struct dhcpcd_ctx * ctx,const char * ifname,struct in_addr * brd)906 if_getbrdaddr(struct dhcpcd_ctx *ctx, const char *ifname, struct in_addr *brd)
907 {
908 struct lifreq lifr = { 0 };
909 int r;
910
911 memset(&lifr, 0, sizeof(lifr));
912 strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
913 errno = 0;
914 r = ioctl(ctx->pf_inet_fd, SIOCGLIFBRDADDR, &lifr, sizeof(lifr));
915 if (r != -1)
916 COPYOUT(*brd, (struct sockaddr *)&lifr.lifr_broadaddr);
917 return r;
918 }
919
920 static int
if_ifa(struct dhcpcd_ctx * ctx,const struct ifa_msghdr * ifam)921 if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)
922 {
923 struct interface *ifp;
924 const struct sockaddr *sa, *rti_info[RTAX_MAX];
925 int flags;
926 char ifalias[IF_NAMESIZE];
927
928 if (ifam->ifam_msglen < sizeof(*ifam)) {
929 errno = EINVAL;
930 return -1;
931 }
932 if (~ifam->ifam_addrs & RTA_IFA)
933 return 0;
934
935 if (get_addrs(ifam->ifam_addrs, (const char *)ifam + sizeof(*ifam),
936 ifam->ifam_msglen - sizeof(*ifam), rti_info) == -1)
937 return -1;
938 sa = rti_info[RTAX_IFA];
939
940 /* XXX We have no way of knowing who generated these
941 * messages wich truely sucks because we want to
942 * avoid listening to our own delete messages. */
943 if ((ifp = if_findindex(ctx->ifaces, ifam->ifam_index)) == NULL)
944 return 0;
945
946 /*
947 * ifa_msghdr does not supply the alias, just the interface index.
948 * This is very bad, because it means we have to call getifaddrs
949 * and trawl the list of addresses to find the added address.
950 * To make life worse, you can have the same address on the same
951 * interface with different aliases.
952 * So this hack is not entirely accurate.
953 *
954 * IF anyone is going to fix Solaris, plesse consider adding the
955 * following fields to extend ifa_msghdr:
956 * ifam_alias
957 * ifam_pid
958 */
959 if (ifam->ifam_type != RTM_DELADDR && !if_getalias(ifp, sa, ifalias))
960 return 0;
961
962 switch (sa->sa_family) {
963 case AF_LINK:
964 {
965 struct sockaddr_dl sdl;
966
967 if (ifam->ifam_type != RTM_CHGADDR &&
968 ifam->ifam_type != RTM_NEWADDR)
969 break;
970 memcpy(&sdl, rti_info[RTAX_IFA], sizeof(sdl));
971 dhcpcd_handlehwaddr(ifp, ifp->hwtype,
972 CLLADDR(&sdl), sdl.sdl_alen);
973 break;
974 }
975 #ifdef INET
976 case AF_INET:
977 {
978 struct in_addr addr, mask, bcast;
979
980 COPYOUT(addr, rti_info[RTAX_IFA]);
981 COPYOUT(mask, rti_info[RTAX_NETMASK]);
982 COPYOUT(bcast, rti_info[RTAX_BRD]);
983
984 if (ifam->ifam_type == RTM_DELADDR) {
985 struct ipv4_addr *ia;
986
987 ia = ipv4_iffindaddr(ifp, &addr, &mask);
988 if (ia == NULL)
989 return 0;
990 strlcpy(ifalias, ia->alias, sizeof(ifalias));
991 } else if (bcast.s_addr == INADDR_ANY) {
992 /* Work around a bug where broadcast
993 * address is not correctly reported. */
994 if (if_getbrdaddr(ctx, ifalias, &bcast) == -1)
995 return 0;
996 }
997 flags = if_addrflags(ifp, NULL, ifalias);
998 if (ifam->ifam_type == RTM_DELADDR) {
999 if (flags != -1)
1000 return 0;
1001 } else if (flags == -1)
1002 return 0;
1003
1004 ipv4_handleifa(ctx,
1005 ifam->ifam_type == RTM_CHGADDR ?
1006 RTM_NEWADDR : ifam->ifam_type,
1007 NULL, ifalias, &addr, &mask, &bcast, flags, 0);
1008 break;
1009 }
1010 #endif
1011 #ifdef INET6
1012 case AF_INET6:
1013 {
1014 struct in6_addr addr6, mask6;
1015 const struct sockaddr_in6 *sin6;
1016
1017 sin6 = (const void *)rti_info[RTAX_IFA];
1018 addr6 = sin6->sin6_addr;
1019 sin6 = (const void *)rti_info[RTAX_NETMASK];
1020 mask6 = sin6->sin6_addr;
1021
1022 if (ifam->ifam_type == RTM_DELADDR) {
1023 struct ipv6_addr *ia;
1024
1025 ia = ipv6_iffindaddr(ifp, &addr6, 0);
1026 if (ia == NULL)
1027 return 0;
1028 strlcpy(ifalias, ia->alias, sizeof(ifalias));
1029 }
1030 flags = if_addrflags6(ifp, NULL, ifalias);
1031 if (ifam->ifam_type == RTM_DELADDR) {
1032 if (flags != -1)
1033 return 0;
1034 } else if (flags == -1)
1035 return 0;
1036
1037 ipv6_handleifa(ctx,
1038 ifam->ifam_type == RTM_CHGADDR ?
1039 RTM_NEWADDR : ifam->ifam_type,
1040 NULL, ifalias, &addr6, ipv6_prefixlen(&mask6), flags, 0);
1041 break;
1042 }
1043 #endif
1044 }
1045
1046 return 0;
1047 }
1048
1049 static int
if_ifinfo(struct dhcpcd_ctx * ctx,const struct if_msghdr * ifm)1050 if_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm)
1051 {
1052 struct interface *ifp;
1053 int state;
1054 unsigned int flags;
1055
1056 if (ifm->ifm_msglen < sizeof(*ifm)) {
1057 errno = EINVAL;
1058 return -1;
1059 }
1060
1061 if ((ifp = if_findindex(ctx->ifaces, ifm->ifm_index)) == NULL)
1062 return 0;
1063 flags = (unsigned int)ifm->ifm_flags;
1064 if (ifm->ifm_flags & IFF_OFFLINE)
1065 state = LINK_DOWN;
1066 else {
1067 state = LINK_UP;
1068 flags |= IFF_UP;
1069 }
1070 dhcpcd_handlecarrier(ifp, state, flags);
1071 return 0;
1072 }
1073
1074 static int
if_dispatch(struct dhcpcd_ctx * ctx,const struct rt_msghdr * rtm)1075 if_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
1076 {
1077
1078 if (rtm->rtm_version != RTM_VERSION)
1079 return 0;
1080
1081 switch(rtm->rtm_type) {
1082 case RTM_IFINFO:
1083 return if_ifinfo(ctx, (const void *)rtm);
1084 case RTM_ADD: /* FALLTHROUGH */
1085 case RTM_CHANGE: /* FALLTHROUGH */
1086 case RTM_DELETE: /* FALLTHROUGH */
1087 case RTM_MISS:
1088 return if_rtm(ctx, (const void *)rtm);
1089 case RTM_CHGADDR: /* FALLTHROUGH */
1090 case RTM_DELADDR: /* FALLTHROUGH */
1091 case RTM_NEWADDR:
1092 return if_ifa(ctx, (const void *)rtm);
1093 }
1094
1095 return 0;
1096 }
1097
1098 int
if_handlelink(struct dhcpcd_ctx * ctx)1099 if_handlelink(struct dhcpcd_ctx *ctx)
1100 {
1101 struct rtm rtm;
1102 ssize_t len;
1103
1104 len = read(ctx->link_fd, &rtm, sizeof(rtm));
1105 if (len == -1)
1106 return -1;
1107 if (len == 0)
1108 return 0;
1109 if ((size_t)len < sizeof(rtm.hdr.rtm_msglen) ||
1110 len != rtm.hdr.rtm_msglen)
1111 {
1112 errno = EINVAL;
1113 return -1;
1114 }
1115 /*
1116 * Coverity thinks that the data could be tainted from here.
1117 * I have no idea how because the length of the data we read
1118 * is guarded by len and checked to match rtm_msglen.
1119 * The issue seems to be related to extracting the addresses
1120 * at the end of the header, but seems to have no issues with the
1121 * equivalent call in if_initrt.
1122 */
1123 /* coverity[tainted_data] */
1124 return if_dispatch(ctx, &rtm.hdr);
1125 }
1126
1127 static void
if_octetstr(char * buf,const Octet_t * o,ssize_t len)1128 if_octetstr(char *buf, const Octet_t *o, ssize_t len)
1129 {
1130 int i;
1131 char *p;
1132
1133 p = buf;
1134 for (i = 0; i < o->o_length; i++) {
1135 if ((p + 1) - buf < len)
1136 *p++ = o->o_bytes[i];
1137 else
1138 break;
1139 }
1140 *p = '\0';
1141 }
1142
1143 static int
if_setflags(int fd,const char * ifname,uint64_t flags)1144 if_setflags(int fd, const char *ifname, uint64_t flags)
1145 {
1146 struct lifreq lifr = { .lifr_addrlen = 0 };
1147
1148 strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
1149 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
1150 return -1;
1151 if ((lifr.lifr_flags & flags) != flags) {
1152 lifr.lifr_flags |= flags;
1153 if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1)
1154 return -1;
1155 }
1156 return 0;
1157 }
1158
1159 static int
if_addaddr(int fd,const char * ifname,struct sockaddr_storage * addr,struct sockaddr_storage * mask,struct sockaddr_storage * brd,uint8_t plen)1160 if_addaddr(int fd, const char *ifname,
1161 struct sockaddr_storage *addr, struct sockaddr_storage *mask,
1162 struct sockaddr_storage *brd, uint8_t plen)
1163 {
1164 struct lifreq lifr = { .lifr_addrlen = plen };
1165
1166 strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
1167
1168 /* First assign the netmask. */
1169 lifr.lifr_addr = *mask;
1170 if (addr == NULL) {
1171 lifr.lifr_addrlen = plen;
1172 if (ioctl(fd, SIOCSLIFSUBNET, &lifr) == -1)
1173 return -1;
1174 goto up;
1175 } else {
1176 if (ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1)
1177 return -1;
1178 }
1179
1180 /* Then assign the address. */
1181 lifr.lifr_addr = *addr;
1182 if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1)
1183 return -1;
1184
1185 /* Then assign the broadcast address. */
1186 if (brd != NULL) {
1187 lifr.lifr_broadaddr = *brd;
1188 if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1)
1189 return -1;
1190 }
1191
1192 up:
1193 return if_setflags(fd, ifname, IFF_UP);
1194 }
1195
1196 static int
if_getaf_fd(const struct dhcpcd_ctx * ctx,int af)1197 if_getaf_fd(const struct dhcpcd_ctx *ctx, int af)
1198 {
1199
1200 if (af == AF_INET)
1201 return ctx->pf_inet_fd;
1202 if (af == AF_INET6) {
1203 struct priv *priv;
1204
1205 priv = (struct priv *)ctx->priv;
1206 return priv->pf_inet6_fd;
1207 }
1208
1209 errno = EAFNOSUPPORT;
1210 return -1;
1211 }
1212
1213 int
if_getsubnet(struct dhcpcd_ctx * ctx,const char * ifname,int af,void * subnet,size_t subnet_len)1214 if_getsubnet(struct dhcpcd_ctx *ctx, const char *ifname, int af,
1215 void *subnet, size_t subnet_len)
1216 {
1217 struct lifreq lifr = { .lifr_addrlen = 0 };
1218 int fd;
1219
1220 fd = if_getaf_fd(ctx, af);
1221 strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
1222 if (ioctl(fd, SIOCGLIFSUBNET, &lifr) == -1)
1223 return -1;
1224 memcpy(subnet, &lifr.lifr_addr, MIN(subnet_len,sizeof(lifr.lifr_addr)));
1225 return 0;
1226 }
1227
1228 static int
if_plumblif(int cmd,const struct dhcpcd_ctx * ctx,int af,const char * ifname)1229 if_plumblif(int cmd, const struct dhcpcd_ctx *ctx, int af, const char *ifname)
1230 {
1231 struct lifreq lifr;
1232 int s;
1233
1234 memset(&lifr, 0, sizeof(lifr));
1235 strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
1236 lifr.lifr_addr.ss_family = af;
1237 s = if_getaf_fd(ctx, af);
1238 return ioctl(s,
1239 cmd == RTM_NEWADDR ? SIOCLIFADDIF : SIOCLIFREMOVEIF,
1240 &lifr) == -1 && errno != EEXIST ? -1 : 0;
1241 }
1242
1243 static int
if_plumbif(const struct dhcpcd_ctx * ctx,int af,const char * ifname)1244 if_plumbif(const struct dhcpcd_ctx *ctx, int af, const char *ifname)
1245 {
1246 dlpi_handle_t dh, dh_arp = NULL;
1247 int fd, af_fd, mux_fd, arp_fd = -1, mux_id, retval;
1248 uint64_t flags;
1249 struct lifreq lifr;
1250 const char *udp_dev;
1251 struct strioctl ioc;
1252 struct if_spec spec;
1253
1254 if (if_nametospec(ifname, &spec) == -1)
1255 return -1;
1256
1257 af_fd = if_getaf_fd(ctx, af);
1258
1259 switch (af) {
1260 case AF_INET:
1261 flags = IFF_IPV4;
1262 udp_dev = UDP_DEV_NAME;
1263 break;
1264 case AF_INET6:
1265 /* We will take care of setting the link local address. */
1266 flags = IFF_IPV6 | IFF_NOLINKLOCAL;
1267 udp_dev = UDP6_DEV_NAME;
1268 break;
1269 default:
1270 errno = EPROTONOSUPPORT;
1271 return -1;
1272 }
1273
1274 if (dlpi_open(ifname, &dh, DLPI_NOATTACH) != DLPI_SUCCESS) {
1275 errno = EINVAL;
1276 return -1;
1277 }
1278
1279 fd = dlpi_fd(dh);
1280 retval = -1;
1281 mux_fd = -1;
1282 if (ioctl(fd, I_PUSH, IP_MOD_NAME) == -1)
1283 goto out;
1284 memset(&lifr, 0, sizeof(lifr));
1285 strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
1286 lifr.lifr_ppa = spec.ppa;
1287 lifr.lifr_flags = flags;
1288 if (ioctl(fd, SIOCSLIFNAME, &lifr) == -1)
1289 goto out;
1290
1291 /* Get full flags. */
1292 if (ioctl(af_fd, SIOCGLIFFLAGS, &lifr) == -1)
1293 goto out;
1294 flags = lifr.lifr_flags;
1295
1296 /* Open UDP as a multiplexor to PLINK the interface stream.
1297 * UDP is used because STREAMS will not let you PLINK a driver
1298 * under itself and IP is generally at the bottom of the stream. */
1299 if ((mux_fd = open(udp_dev, O_RDWR)) == -1)
1300 goto out;
1301 /* POP off all undesired modules. */
1302 while (ioctl(mux_fd, I_POP, 0) != -1)
1303 ;
1304 if (errno != EINVAL)
1305 goto out;
1306 if(ioctl(mux_fd, I_PUSH, ARP_MOD_NAME) == -1)
1307 goto out;
1308
1309 if (flags & (IFF_NOARP | IFF_IPV6)) {
1310 /* PLINK the interface stream so it persists. */
1311 if (ioctl(mux_fd, I_PLINK, fd) == -1)
1312 goto out;
1313 goto done;
1314 }
1315
1316 if (dlpi_open(ifname, &dh_arp, DLPI_NOATTACH) != DLPI_SUCCESS)
1317 goto out;
1318 arp_fd = dlpi_fd(dh_arp);
1319 if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1)
1320 goto out;
1321
1322 memset(&lifr, 0, sizeof(lifr));
1323 strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
1324 lifr.lifr_ppa = spec.ppa;
1325 lifr.lifr_flags = flags;
1326 memset(&ioc, 0, sizeof(ioc));
1327 ioc.ic_cmd = SIOCSLIFNAME;
1328 ioc.ic_dp = (char *)&lifr;
1329 ioc.ic_len = sizeof(lifr);
1330 if (ioctl(arp_fd, I_STR, &ioc) == -1)
1331 goto out;
1332
1333 /* PLINK the interface stream so it persists. */
1334 mux_id = ioctl(mux_fd, I_PLINK, fd);
1335 if (mux_id == -1)
1336 goto out;
1337 if (ioctl(mux_fd, I_PLINK, arp_fd) == -1) {
1338 ioctl(mux_fd, I_PUNLINK, mux_id);
1339 goto out;
1340 }
1341
1342 done:
1343 retval = 0;
1344
1345 out:
1346 dlpi_close(dh);
1347 if (dh_arp != NULL)
1348 dlpi_close(dh_arp);
1349 if (mux_fd != -1)
1350 close(mux_fd);
1351 return retval;
1352 }
1353
1354 static int
if_unplumbif(const struct dhcpcd_ctx * ctx,int af,const char * ifname)1355 if_unplumbif(const struct dhcpcd_ctx *ctx, int af, const char *ifname)
1356 {
1357 struct sockaddr_storage addr = { .ss_family = af };
1358 int fd;
1359
1360 /* For the time being, don't unplumb the interface, just
1361 * set the address to zero. */
1362 fd = if_getaf_fd(ctx, af);
1363 return if_addaddr(fd, ifname, &addr, &addr,
1364 af == AF_INET ? &addr : NULL, 0);
1365 }
1366
1367 static int
if_plumb(int cmd,const struct dhcpcd_ctx * ctx,int af,const char * ifname)1368 if_plumb(int cmd, const struct dhcpcd_ctx *ctx, int af, const char *ifname)
1369 {
1370 struct if_spec spec;
1371
1372 if (if_nametospec(ifname, &spec) == -1)
1373 return -1;
1374 if (spec.lun != -1)
1375 return if_plumblif(cmd, ctx, af, ifname);
1376 if (cmd == RTM_NEWADDR)
1377 return if_plumbif(ctx, af, ifname);
1378 else
1379 return if_unplumbif(ctx, af, ifname);
1380 }
1381
1382 #ifdef INET
1383 static int
if_walkrt(struct dhcpcd_ctx * ctx,rb_tree_t * routes,char * data,size_t len)1384 if_walkrt(struct dhcpcd_ctx *ctx, rb_tree_t *routes, char *data, size_t len)
1385 {
1386 mib2_ipRouteEntry_t *re, *e;
1387 struct rt rt, *rtn;
1388 char ifname[IF_NAMESIZE];
1389 struct in_addr in;
1390
1391 if (len % sizeof(*re) != 0) {
1392 errno = EINVAL;
1393 return -1;
1394 }
1395
1396 re = (mib2_ipRouteEntry_t *)data;
1397 e = (mib2_ipRouteEntry_t *)(data + len);
1398 do {
1399 /* Skip route types we don't want. */
1400 switch (re->ipRouteInfo.re_ire_type) {
1401 case IRE_IF_CLONE:
1402 case IRE_BROADCAST:
1403 case IRE_MULTICAST:
1404 case IRE_NOROUTE:
1405 case IRE_LOCAL:
1406 continue;
1407 default:
1408 break;
1409 }
1410
1411 memset(&rt, 0, sizeof(rt));
1412 in.s_addr = re->ipRouteDest;
1413 sa_in_init(&rt.rt_dest, &in);
1414 in.s_addr = re->ipRouteMask;
1415 sa_in_init(&rt.rt_netmask, &in);
1416 in.s_addr = re->ipRouteNextHop;
1417 sa_in_init(&rt.rt_gateway, &in);
1418 rt.rt_flags = re->ipRouteInfo.re_flags;
1419 in.s_addr = re->ipRouteInfo.re_src_addr;
1420 sa_in_init(&rt.rt_ifa, &in);
1421 rt.rt_mtu = re->ipRouteInfo.re_max_frag;
1422 if_octetstr(ifname, &re->ipRouteIfIndex, sizeof(ifname));
1423 rt.rt_ifp = if_find(ctx->ifaces, ifname);
1424 if (if_finishrt(ctx, &rt) == -1) {
1425 logerr(__func__);
1426 continue;
1427 }
1428 if ((rtn = rt_new(rt.rt_ifp)) == NULL) {
1429 logerr(__func__);
1430 break;
1431 }
1432 memcpy(rtn, &rt, sizeof(*rtn));
1433 if (rb_tree_insert_node(routes, rtn) != rtn)
1434 rt_free(rtn);
1435 } while (++re < e);
1436 return 0;
1437 }
1438 #endif
1439
1440 #ifdef INET6
1441 static int
if_walkrt6(struct dhcpcd_ctx * ctx,rb_tree_t * routes,char * data,size_t len)1442 if_walkrt6(struct dhcpcd_ctx *ctx, rb_tree_t *routes, char *data, size_t len)
1443 {
1444 mib2_ipv6RouteEntry_t *re, *e;
1445 struct rt rt, *rtn;
1446 char ifname[IF_NAMESIZE];
1447 struct in6_addr in6;
1448
1449 if (len % sizeof(*re) != 0) {
1450 errno = EINVAL;
1451 return -1;
1452 }
1453
1454 re = (mib2_ipv6RouteEntry_t *)data;
1455 e = (mib2_ipv6RouteEntry_t *)(data + len);
1456
1457 do {
1458 /* Skip route types we don't want. */
1459 switch (re->ipv6RouteInfo.re_ire_type) {
1460 case IRE_IF_CLONE:
1461 case IRE_BROADCAST:
1462 case IRE_MULTICAST:
1463 case IRE_NOROUTE:
1464 case IRE_LOCAL:
1465 continue;
1466 default:
1467 break;
1468 }
1469
1470 memset(&rt, 0, sizeof(rt));
1471 sa_in6_init(&rt.rt_dest, &re->ipv6RouteDest);
1472 ipv6_mask(&in6, re->ipv6RoutePfxLength);
1473 sa_in6_init(&rt.rt_netmask, &in6);
1474 sa_in6_init(&rt.rt_gateway, &re->ipv6RouteNextHop);
1475 sa_in6_init(&rt.rt_ifa, &re->ipv6RouteInfo.re_src_addr);
1476 rt.rt_mtu = re->ipv6RouteInfo.re_max_frag;
1477 if_octetstr(ifname, &re->ipv6RouteIfIndex, sizeof(ifname));
1478 rt.rt_ifp = if_find(ctx->ifaces, ifname);
1479 if (if_finishrt(ctx, &rt) == -1) {
1480 logerr(__func__);
1481 continue;
1482 }
1483 if ((rtn = rt_new(rt.rt_ifp)) == NULL) {
1484 logerr(__func__);
1485 break;
1486 }
1487 memcpy(rtn, &rt, sizeof(*rtn));
1488 if (rb_tree_insert_node(routes, rtn) != rtn)
1489 rt_free(rtn);
1490 } while (++re < e);
1491 return 0;
1492 }
1493 #endif
1494
1495 static int
if_parsert(struct dhcpcd_ctx * ctx,rb_tree_t * routes,unsigned int level,unsigned int name,int (* walkrt)(struct dhcpcd_ctx *,rb_tree_t *,char *,size_t))1496 if_parsert(struct dhcpcd_ctx *ctx, rb_tree_t *routes,
1497 unsigned int level, unsigned int name,
1498 int (*walkrt)(struct dhcpcd_ctx *, rb_tree_t *, char *, size_t))
1499 {
1500 int s, retval, code, flags;
1501 uintptr_t buf[512 / sizeof(uintptr_t)];
1502 struct strbuf ctlbuf, databuf;
1503 struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf;
1504 struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf;
1505 struct T_error_ack *tea = (struct T_error_ack *)buf;
1506 struct opthdr *req;
1507
1508 if ((s = open("/dev/arp", O_RDWR)) == -1)
1509 return -1;
1510
1511 /* Assume we are erroring. */
1512 retval = -1;
1513
1514 tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
1515 tor->OPT_offset = sizeof (struct T_optmgmt_req);
1516 tor->OPT_length = sizeof (struct opthdr);
1517 tor->MGMT_flags = T_CURRENT;
1518
1519 req = (struct opthdr *)&tor[1];
1520 req->level = EXPER_IP_AND_ALL_IRES;
1521 req->name = 0;
1522 req->len = 1;
1523
1524 ctlbuf.buf = (char *)buf;
1525 ctlbuf.len = tor->OPT_length + tor->OPT_offset;
1526 if (putmsg(s, &ctlbuf, NULL, 0) == 1)
1527 goto out;
1528
1529 req = (struct opthdr *)&toa[1];
1530 ctlbuf.maxlen = sizeof(buf);
1531
1532 /* Create a reasonable buffer to start with */
1533 databuf.maxlen = BUFSIZ * 2;
1534 if ((databuf.buf = malloc(databuf.maxlen)) == NULL)
1535 goto out;
1536
1537 for (;;) {
1538 flags = 0;
1539 if ((code = getmsg(s, &ctlbuf, 0, &flags)) == -1)
1540 break;
1541 if (code == 0 &&
1542 toa->PRIM_type == T_OPTMGMT_ACK &&
1543 toa->MGMT_flags == T_SUCCESS &&
1544 (size_t)ctlbuf.len >= sizeof(struct T_optmgmt_ack))
1545 {
1546 /* End of messages, so return success! */
1547 retval = 0;
1548 break;
1549 }
1550 if (tea->PRIM_type == T_ERROR_ACK) {
1551 errno = tea->TLI_error == TSYSERR ?
1552 tea->UNIX_error : EPROTO;
1553 break;
1554 }
1555 if (code != MOREDATA ||
1556 toa->PRIM_type != T_OPTMGMT_ACK ||
1557 toa->MGMT_flags != T_SUCCESS)
1558 {
1559 errno = ENOMSG;
1560 break;
1561 }
1562
1563 /* Try to ensure out buffer is big enough
1564 * for future messages as well. */
1565 if ((size_t)databuf.maxlen < req->len) {
1566 size_t newlen;
1567
1568 free(databuf.buf);
1569 newlen = roundup(req->len, BUFSIZ);
1570 if ((databuf.buf = malloc(newlen)) == NULL)
1571 break;
1572 databuf.maxlen = newlen;
1573 }
1574
1575 flags = 0;
1576 if (getmsg(s, NULL, &databuf, &flags) == -1)
1577 break;
1578
1579 /* We always have to get the data before moving onto
1580 * the next item, so don't move this test higher up
1581 * to avoid the buffer allocation and getmsg calls. */
1582 if (req->level == level && req->name == name) {
1583 if (walkrt(ctx, routes, databuf.buf, req->len) == -1)
1584 break;
1585 }
1586 }
1587
1588 free(databuf.buf);
1589 out:
1590 close(s);
1591 return retval;
1592 }
1593
1594
1595 int
if_initrt(struct dhcpcd_ctx * ctx,rb_tree_t * routes,int af)1596 if_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *routes, int af)
1597 {
1598
1599 #ifdef INET
1600 if ((af == AF_UNSPEC || af == AF_INET) &&
1601 if_parsert(ctx, routes, MIB2_IP,MIB2_IP_ROUTE, if_walkrt) == -1)
1602 return -1;
1603 #endif
1604 #ifdef INET6
1605 if ((af == AF_UNSPEC || af == AF_INET6) &&
1606 if_parsert(ctx, routes, MIB2_IP6, MIB2_IP6_ROUTE, if_walkrt6) == -1)
1607 return -1;
1608 #endif
1609 return 0;
1610 }
1611
1612
1613 #ifdef INET
1614 /* XXX We should fix this to write via the BPF interface. */
1615 ssize_t
bpf_send(const struct bpf * bpf,uint16_t protocol,const void * data,size_t len)1616 bpf_send(const struct bpf *bpf, uint16_t protocol, const void *data, size_t len)
1617 {
1618 const struct interface *ifp = bpf->bpf_ifp;
1619 dlpi_handle_t dh;
1620 dlpi_info_t di;
1621 int r;
1622
1623 if (dlpi_open(ifp->name, &dh, 0) != DLPI_SUCCESS)
1624 return -1;
1625 if ((r = dlpi_info(dh, &di, 0)) == DLPI_SUCCESS &&
1626 (r = dlpi_bind(dh, protocol, NULL)) == DLPI_SUCCESS)
1627 r = dlpi_send(dh, di.di_bcastaddr, ifp->hwlen, data, len, NULL);
1628 dlpi_close(dh);
1629 return r == DLPI_SUCCESS ? (ssize_t)len : -1;
1630 }
1631
1632 int
if_address(unsigned char cmd,const struct ipv4_addr * ia)1633 if_address(unsigned char cmd, const struct ipv4_addr *ia)
1634 {
1635 union {
1636 struct sockaddr sa;
1637 struct sockaddr_storage ss;
1638 } addr, mask, brd;
1639 int fd = ia->iface->ctx->pf_inet_fd;
1640
1641 /* Either remove the alias or ensure it exists. */
1642 if (if_plumb(cmd, ia->iface->ctx, AF_INET, ia->alias) == -1 &&
1643 errno != EEXIST)
1644 return -1;
1645
1646 if (cmd == RTM_DELADDR)
1647 return 0;
1648
1649 if (cmd != RTM_NEWADDR) {
1650 errno = EINVAL;
1651 return -1;
1652 }
1653
1654 /* We need to update the index now */
1655 ia->iface->index = if_nametoindex(ia->alias);
1656
1657 sa_in_init(&addr.sa, &ia->addr);
1658 sa_in_init(&mask.sa, &ia->mask);
1659 sa_in_init(&brd.sa, &ia->brd);
1660 return if_addaddr(fd, ia->alias, &addr.ss, &mask.ss, &brd.ss, 0);
1661 }
1662
1663 int
if_addrflags(const struct interface * ifp,__unused const struct in_addr * ia,const char * alias)1664 if_addrflags(const struct interface *ifp, __unused const struct in_addr * ia,
1665 const char *alias)
1666 {
1667
1668 return if_addrflags0(ifp->ctx->pf_inet_fd, AF_INET, alias);
1669 }
1670
1671 #endif
1672
1673 #ifdef INET6
1674 int
if_address6(unsigned char cmd,const struct ipv6_addr * ia)1675 if_address6(unsigned char cmd, const struct ipv6_addr *ia)
1676 {
1677 union {
1678 struct sockaddr sa;
1679 struct sockaddr_in6 sin6;
1680 struct sockaddr_storage ss;
1681 } addr, mask;
1682 int fd, r;
1683
1684 /* Either remove the alias or ensure it exists. */
1685 if (if_plumb(cmd, ia->iface->ctx, AF_INET6, ia->alias) == -1 &&
1686 errno != EEXIST)
1687 return -1;
1688
1689 if (cmd == RTM_DELADDR)
1690 return 0;
1691
1692 if (cmd != RTM_NEWADDR) {
1693 errno = EINVAL;
1694 return -1;
1695 }
1696
1697 fd = if_getaf_fd(ia->iface->ctx, AF_INET6);
1698
1699 if (!(ia->flags & IPV6_AF_AUTOCONF) && ia->flags & IPV6_AF_RAPFX) {
1700 if (if_setflags(fd, ia->alias, IFF_NOLOCAL) ==-1)
1701 return -1;
1702 sa_in6_init(&mask.sa, &ia->prefix);
1703 r = if_addaddr(fd, ia->alias,
1704 NULL, &mask.ss, NULL, ia->prefix_len);
1705 } else {
1706 sa_in6_init(&addr.sa, &ia->addr);
1707 mask.sin6.sin6_family = AF_INET6;
1708 ipv6_mask(&mask.sin6.sin6_addr, ia->prefix_len);
1709 r = if_addaddr(fd, ia->alias,
1710 &addr.ss, &mask.ss, NULL, ia->prefix_len);
1711 }
1712 if (r == -1 && errno == EEXIST)
1713 return 0;
1714 return r;
1715 }
1716
1717 int
if_addrflags6(const struct interface * ifp,__unused const struct in6_addr * ia,const char * alias)1718 if_addrflags6(const struct interface *ifp, __unused const struct in6_addr *ia,
1719 const char *alias)
1720 {
1721 int fd;
1722
1723 fd = if_getaf_fd(ifp->ctx, AF_INET6);
1724 return if_addrflags0(fd, AF_INET6, alias);
1725 }
1726
1727 int
if_getlifetime6(struct ipv6_addr * addr)1728 if_getlifetime6(struct ipv6_addr *addr)
1729 {
1730
1731 UNUSED(addr);
1732 errno = ENOTSUP;
1733 return -1;
1734 }
1735
1736 int
if_applyra(const struct ra * rap)1737 if_applyra(const struct ra *rap)
1738 {
1739 struct lifreq lifr = {
1740 .lifr_ifinfo.lir_maxhops = rap->hoplimit,
1741 .lifr_ifinfo.lir_reachtime = rap->reachable,
1742 .lifr_ifinfo.lir_reachretrans = rap->retrans,
1743 };
1744
1745 strlcpy(lifr.lifr_name, rap->iface->name, sizeof(lifr.lifr_name));
1746 return ioctl(rap->iface->ctx->pf_inet_fd, SIOCSLIFLNKINFO, &lifr);
1747 }
1748
1749 void
if_setup_inet6(__unused const struct interface * ifp)1750 if_setup_inet6(__unused const struct interface *ifp)
1751 {
1752
1753 }
1754
1755 int
ip6_forwarding(__unused const char * ifname)1756 ip6_forwarding(__unused const char *ifname)
1757 {
1758
1759 return 1;
1760 }
1761 #endif
1762