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