xref: /dragonfly/contrib/dhcpcd/src/if.c (revision 49837aef)
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * dhcpcd - DHCP client daemon
4  * Copyright (c) 2006-2019 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 <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/ioctl.h>
32 #include <sys/socket.h>
33 
34 #include "config.h"
35 
36 #include <net/if.h>
37 #include <net/if_arp.h>
38 #include <netinet/in.h>
39 #ifdef AF_LINK
40 #  include <net/if_dl.h>
41 #  include <net/if_types.h>
42 #  include <netinet/in_var.h>
43 #  undef AF_PACKET	/* Newer Illumos defines this */
44 #endif
45 #ifdef AF_PACKET
46 #  include <netpacket/packet.h>
47 #endif
48 #ifdef SIOCGIFMEDIA
49 #  include <net/if_media.h>
50 #endif
51 #include <net/route.h>
52 
53 #include <ctype.h>
54 #include <errno.h>
55 #include <ifaddrs.h>
56 #include <inttypes.h>
57 #include <fnmatch.h>
58 #include <stddef.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 
64 #include "common.h"
65 #include "dev.h"
66 #include "dhcp.h"
67 #include "dhcp6.h"
68 #include "if.h"
69 #include "if-options.h"
70 #include "ipv4.h"
71 #include "ipv4ll.h"
72 #include "ipv6nd.h"
73 #include "logerr.h"
74 
75 #ifdef __sun
76 /* It has the ioctl, but the member is missing from the struct?
77  * No matter, our getifaddrs foo in if-sun.c will DTRT. */
78 #undef SIOCGIFHWADDR
79 #endif
80 
81 void
82 if_free(struct interface *ifp)
83 {
84 
85 	if (ifp == NULL)
86 		return;
87 #ifdef IPV4LL
88 	ipv4ll_free(ifp);
89 #endif
90 #ifdef INET
91 	dhcp_free(ifp);
92 	ipv4_free(ifp);
93 #endif
94 #ifdef DHCP6
95 	dhcp6_free(ifp);
96 #endif
97 #ifdef INET6
98 	ipv6nd_free(ifp);
99 	ipv6_free(ifp);
100 #endif
101 	rt_freeif(ifp);
102 	free_options(ifp->ctx, ifp->options);
103 	free(ifp);
104 }
105 
106 int
107 if_opensockets(struct dhcpcd_ctx *ctx)
108 {
109 
110 	if (if_opensockets_os(ctx) == -1)
111 		return -1;
112 
113 	/* We use this socket for some operations without INET. */
114 	ctx->pf_inet_fd = xsocket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
115 	if (ctx->pf_inet_fd == -1)
116 		return -1;
117 
118 	return 0;
119 }
120 
121 void
122 if_closesockets(struct dhcpcd_ctx *ctx)
123 {
124 
125 	if (ctx->pf_inet_fd != -1)
126 		close(ctx->pf_inet_fd);
127 
128 	if (ctx->priv) {
129 		if_closesockets_os(ctx);
130 		free(ctx->priv);
131 	}
132 }
133 
134 int
135 if_getflags(struct interface *ifp)
136 {
137 	struct ifreq ifr = { .ifr_flags = 0 };
138 
139 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
140 	if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr) == -1)
141 		return -1;
142 	ifp->flags = (unsigned int)ifr.ifr_flags;
143 	return 0;
144 }
145 
146 int
147 if_setflag(struct interface *ifp, short flag)
148 {
149 	struct ifreq ifr = { .ifr_flags = 0 };
150 	short f;
151 
152 	if (if_getflags(ifp) == -1)
153 		return -1;
154 
155 	f = (short)ifp->flags;
156 	if ((f & flag) == flag)
157 		return 0;
158 
159 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
160 	ifr.ifr_flags = f | flag;
161 	if (ioctl(ifp->ctx->pf_inet_fd, SIOCSIFFLAGS, &ifr) == -1)
162 		return -1;
163 
164 	ifp->flags = (unsigned int)ifr.ifr_flags;
165 	return 0;
166 }
167 
168 static int
169 if_hasconf(struct dhcpcd_ctx *ctx, const char *ifname)
170 {
171 	int i;
172 
173 	for (i = 0; i < ctx->ifcc; i++) {
174 		if (strcmp(ctx->ifcv[i], ifname) == 0)
175 			return 1;
176 	}
177 	return 0;
178 }
179 
180 void
181 if_markaddrsstale(struct if_head *ifs)
182 {
183 	struct interface *ifp;
184 
185 	TAILQ_FOREACH(ifp, ifs, next) {
186 #ifdef INET
187 		ipv4_markaddrsstale(ifp);
188 #endif
189 #ifdef INET6
190 		ipv6_markaddrsstale(ifp, 0);
191 #endif
192 	}
193 }
194 
195 void
196 if_learnaddrs(struct dhcpcd_ctx *ctx, struct if_head *ifs,
197     struct ifaddrs **ifaddrs)
198 {
199 	struct ifaddrs *ifa;
200 	struct interface *ifp;
201 #ifdef INET
202 	const struct sockaddr_in *addr, *net, *brd;
203 #endif
204 #ifdef INET6
205 	struct sockaddr_in6 *sin6, *net6;
206 #endif
207 	int addrflags;
208 
209 	for (ifa = *ifaddrs; ifa; ifa = ifa->ifa_next) {
210 		if (ifa->ifa_addr == NULL)
211 			continue;
212 		if ((ifp = if_find(ifs, ifa->ifa_name)) == NULL)
213 			continue;
214 #ifdef HAVE_IFADDRS_ADDRFLAGS
215 		addrflags = (int)ifa->ifa_addrflags;
216 #endif
217 		switch(ifa->ifa_addr->sa_family) {
218 #ifdef INET
219 		case AF_INET:
220 			addr = (void *)ifa->ifa_addr;
221 			net = (void *)ifa->ifa_netmask;
222 			if (ifa->ifa_flags & IFF_POINTOPOINT)
223 				brd = (void *)ifa->ifa_dstaddr;
224 			else
225 				brd = (void *)ifa->ifa_broadaddr;
226 #ifndef HAVE_IFADDRS_ADDRFLAGS
227 			addrflags = if_addrflags(ifp, &addr->sin_addr,
228 			    ifa->ifa_name);
229 			if (addrflags == -1) {
230 				if (errno != EEXIST && errno != EADDRNOTAVAIL)
231 					logerr("%s: if_addrflags", __func__);
232 				continue;
233 			}
234 #endif
235 			ipv4_handleifa(ctx, RTM_NEWADDR, ifs, ifa->ifa_name,
236 				&addr->sin_addr, &net->sin_addr,
237 				brd ? &brd->sin_addr : NULL, addrflags, 0);
238 			break;
239 #endif
240 #ifdef INET6
241 		case AF_INET6:
242 			sin6 = (void *)ifa->ifa_addr;
243 			net6 = (void *)ifa->ifa_netmask;
244 
245 #ifdef __KAME__
246 			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
247 				/* Remove the scope from the address */
248 				sin6->sin6_addr.s6_addr[2] =
249 				    sin6->sin6_addr.s6_addr[3] = '\0';
250 #endif
251 #ifndef HAVE_IFADDRS_ADDRFLAGS
252 			addrflags = if_addrflags6(ifp, &sin6->sin6_addr,
253 			    ifa->ifa_name);
254 			if (addrflags == -1) {
255 				if (errno != EEXIST && errno != EADDRNOTAVAIL)
256 					logerr("%s: if_addrflags6", __func__);
257 				continue;
258 			}
259 #endif
260 			ipv6_handleifa(ctx, RTM_NEWADDR, ifs,
261 			    ifa->ifa_name, &sin6->sin6_addr,
262 			    ipv6_prefixlen(&net6->sin6_addr), addrflags, 0);
263 			break;
264 #endif
265 		}
266 	}
267 
268 	freeifaddrs(*ifaddrs);
269 	*ifaddrs = NULL;
270 }
271 
272 void
273 if_deletestaleaddrs(struct if_head *ifs)
274 {
275 	struct interface *ifp;
276 
277 	TAILQ_FOREACH(ifp, ifs, next) {
278 #ifdef INET
279 		ipv4_deletestaleaddrs(ifp);
280 #endif
281 #ifdef INET6
282 		ipv6_deletestaleaddrs(ifp);
283 #endif
284 	}
285 }
286 
287 bool
288 if_valid_hwaddr(const uint8_t *hwaddr, size_t hwlen)
289 {
290 	size_t i;
291 	bool all_zeros, all_ones;
292 
293 	all_zeros = all_ones = true;
294 	for (i = 0; i < hwlen; i++) {
295 		if (hwaddr[i] != 0x00)
296 			all_zeros = false;
297 		if (hwaddr[i] != 0xff)
298 			all_ones = false;
299 		if (!all_zeros && !all_ones)
300 			return true;
301 	}
302 	return false;
303 }
304 
305 struct if_head *
306 if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs,
307     int argc, char * const *argv)
308 {
309 	struct ifaddrs *ifa;
310 	int i;
311 	unsigned int active;
312 	struct if_head *ifs;
313 	struct interface *ifp;
314 	struct if_spec spec;
315 	bool if_noconf;
316 #ifdef AF_LINK
317 	const struct sockaddr_dl *sdl;
318 #ifdef IFLR_ACTIVE
319 	struct if_laddrreq iflr = { .flags = IFLR_PREFIX };
320 	int link_fd;
321 #endif
322 #elif AF_PACKET
323 	const struct sockaddr_ll *sll;
324 #endif
325 #if defined(SIOCGIFPRIORITY) || defined(SIOCGIFHWADDR)
326 	struct ifreq ifr;
327 #endif
328 
329 	if ((ifs = malloc(sizeof(*ifs))) == NULL) {
330 		logerr(__func__);
331 		return NULL;
332 	}
333 	if (getifaddrs(ifaddrs) == -1) {
334 		logerr(__func__);
335 		free(ifs);
336 		return NULL;
337 	}
338 	TAILQ_INIT(ifs);
339 
340 #ifdef IFLR_ACTIVE
341 	link_fd = xsocket(PF_LINK, SOCK_DGRAM | SOCK_CLOEXEC, 0);
342 	if (link_fd == -1) {
343 		logerr(__func__);
344 		free(ifs);
345 		return NULL;
346 	}
347 #endif
348 
349 	for (ifa = *ifaddrs; ifa; ifa = ifa->ifa_next) {
350 		if (ifa->ifa_addr != NULL) {
351 #ifdef AF_LINK
352 			if (ifa->ifa_addr->sa_family != AF_LINK)
353 				continue;
354 #elif AF_PACKET
355 			if (ifa->ifa_addr->sa_family != AF_PACKET)
356 				continue;
357 #endif
358 		}
359 		if (if_nametospec(ifa->ifa_name, &spec) != 0)
360 			continue;
361 
362 		/* It's possible for an interface to have >1 AF_LINK.
363 		 * For our purposes, we use the first one. */
364 		TAILQ_FOREACH(ifp, ifs, next) {
365 			if (strcmp(ifp->name, spec.devname) == 0)
366 				break;
367 		}
368 		if (ifp)
369 			continue;
370 
371 		if (argc > 0) {
372 			for (i = 0; i < argc; i++) {
373 				if (strcmp(argv[i], spec.devname) == 0)
374 					break;
375 			}
376 			active = (i == argc) ? IF_INACTIVE : IF_ACTIVE_USER;
377 		} else {
378 			/* -1 means we're discovering against a specific
379 			 * interface, but we still need the below rules
380 			 * to apply. */
381 			if (argc == -1 && strcmp(argv[0], spec.devname) != 0)
382 				continue;
383 			active = ctx->options & DHCPCD_INACTIVE ?
384 			    IF_INACTIVE: IF_ACTIVE_USER;
385 		}
386 
387 		for (i = 0; i < ctx->ifdc; i++)
388 			if (fnmatch(ctx->ifdv[i], spec.devname, 0) == 0)
389 				break;
390 		if (i < ctx->ifdc)
391 			active = IF_INACTIVE;
392 		for (i = 0; i < ctx->ifc; i++)
393 			if (fnmatch(ctx->ifv[i], spec.devname, 0) == 0)
394 				break;
395 		if (ctx->ifc && i == ctx->ifc)
396 			active = IF_INACTIVE;
397 		for (i = 0; i < ctx->ifac; i++)
398 			if (fnmatch(ctx->ifav[i], spec.devname, 0) == 0)
399 				break;
400 		if (ctx->ifac && i == ctx->ifac)
401 			active = IF_INACTIVE;
402 
403 #ifdef PLUGIN_DEV
404 		/* Ensure that the interface name has settled */
405 		if (!dev_initialized(ctx, spec.devname))
406 			continue;
407 #endif
408 
409 		if (if_vimaster(ctx, spec.devname) == 1) {
410 			logfunc_t *logfunc = argc != 0 ? logerrx : logdebugx;
411 			logfunc("%s: is a Virtual Interface Master, skipping",
412 			    spec.devname);
413 			continue;
414 		}
415 
416 		if_noconf = ((argc == 0 || argc == -1) && ctx->ifac == 0 &&
417 		    !if_hasconf(ctx, spec.devname));
418 
419 		/* Don't allow loopback or pointopoint unless explicit.
420 		 * Don't allow some reserved interface names unless explicit. */
421 		if (if_noconf) {
422 			if (ifa->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT) ||
423 			    if_ignore(ctx, spec.devname))
424 				active = IF_INACTIVE;
425 		}
426 
427 		ifp = calloc(1, sizeof(*ifp));
428 		if (ifp == NULL) {
429 			logerr(__func__);
430 			break;
431 		}
432 		ifp->ctx = ctx;
433 		strlcpy(ifp->name, spec.devname, sizeof(ifp->name));
434 		ifp->flags = ifa->ifa_flags;
435 
436 		if (ifa->ifa_addr != NULL) {
437 #ifdef AF_LINK
438 			sdl = (const void *)ifa->ifa_addr;
439 
440 #ifdef IFLR_ACTIVE
441 			/* We need to check for active address */
442 			strlcpy(iflr.iflr_name, ifp->name,
443 			    sizeof(iflr.iflr_name));
444 			memcpy(&iflr.addr, ifa->ifa_addr,
445 			    MIN(ifa->ifa_addr->sa_len, sizeof(iflr.addr)));
446 			iflr.flags = IFLR_PREFIX;
447 			iflr.prefixlen = (unsigned int)sdl->sdl_alen * NBBY;
448 			if (ioctl(link_fd, SIOCGLIFADDR, &iflr) == -1 ||
449 			    !(iflr.flags & IFLR_ACTIVE))
450 			{
451 				if_free(ifp);
452 				continue;
453 			}
454 #endif
455 
456 			ifp->index = sdl->sdl_index;
457 			switch(sdl->sdl_type) {
458 #ifdef IFT_BRIDGE
459 			case IFT_BRIDGE: /* FALLTHROUGH */
460 #endif
461 #ifdef IFT_PROPVIRTUAL
462 			case IFT_PROPVIRTUAL: /* FALLTHROUGH */
463 #endif
464 #ifdef IFT_TUNNEL
465 			case IFT_TUNNEL: /* FALLTHROUGH */
466 #endif
467 			case IFT_PPP:
468 				/* Don't allow unless explicit */
469 				if (if_noconf) {
470 					logdebugx("%s: ignoring due to"
471 					    " interface type and"
472 					    " no config",
473 					    ifp->name);
474 					active = IF_INACTIVE;
475 				}
476 				__fallthrough; /* appease gcc */
477 				/* FALLTHROUGH */
478 #ifdef IFT_L2VLAN
479 			case IFT_L2VLAN: /* FALLTHROUGH */
480 #endif
481 #ifdef IFT_L3IPVLAN
482 			case IFT_L3IPVLAN: /* FALLTHROUGH */
483 #endif
484 			case IFT_ETHER:
485 				ifp->family = ARPHRD_ETHER;
486 				break;
487 #ifdef IFT_IEEE1394
488 			case IFT_IEEE1394:
489 				ifp->family = ARPHRD_IEEE1394;
490 				break;
491 #endif
492 #ifdef IFT_INFINIBAND
493 			case IFT_INFINIBAND:
494 				ifp->family = ARPHRD_INFINIBAND;
495 				break;
496 #endif
497 			default:
498 				/* Don't allow unless explicit */
499 				if (if_noconf)
500 					active = IF_INACTIVE;
501 				if (active)
502 					logwarnx("%s: unsupported"
503 					    " interface type 0x%.2x",
504 					    ifp->name, sdl->sdl_type);
505 				/* Pretend it's ethernet */
506 				ifp->family = ARPHRD_ETHER;
507 				break;
508 			}
509 			ifp->hwlen = sdl->sdl_alen;
510 			memcpy(ifp->hwaddr, CLLADDR(sdl), ifp->hwlen);
511 #elif AF_PACKET
512 			sll = (const void *)ifa->ifa_addr;
513 			ifp->index = (unsigned int)sll->sll_ifindex;
514 			ifp->family = sll->sll_hatype;
515 			ifp->hwlen = sll->sll_halen;
516 			if (ifp->hwlen != 0)
517 				memcpy(ifp->hwaddr, sll->sll_addr, ifp->hwlen);
518 #endif
519 		}
520 #ifdef SIOCGIFHWADDR
521 		else {
522 			/* This is a huge bug in getifaddrs(3) as there
523 			 * is no reason why this can't be returned in
524 			 * ifa_addr. */
525 			memset(&ifr, 0, sizeof(ifr));
526 			strlcpy(ifr.ifr_name, ifa->ifa_name,
527 			    sizeof(ifr.ifr_name));
528 			if (ioctl(ctx->pf_inet_fd, SIOCGIFHWADDR, &ifr) == -1)
529 				logerr("%s: SIOCGIFHWADDR", ifa->ifa_name);
530 			ifp->family = ifr.ifr_hwaddr.sa_family;
531 			if (ioctl(ctx->pf_inet_fd, SIOCGIFINDEX, &ifr) == -1)
532 				logerr("%s: SIOCGIFINDEX", ifa->ifa_name);
533 			ifp->index = (unsigned int)ifr.ifr_ifindex;
534 		}
535 #endif
536 
537 		/* Ensure hardware address is valid. */
538 		if (!if_valid_hwaddr(ifp->hwaddr, ifp->hwlen))
539 			ifp->hwlen = 0;
540 
541 		/* We only work on ethernet by default */
542 		if (ifp->family != ARPHRD_ETHER) {
543 			if ((argc == 0 || argc == -1) &&
544 			    ctx->ifac == 0 && !if_hasconf(ctx, ifp->name))
545 				active = IF_INACTIVE;
546 			switch (ifp->family) {
547 			case ARPHRD_IEEE1394:
548 			case ARPHRD_INFINIBAND:
549 #ifdef ARPHRD_LOOPBACK
550 			case ARPHRD_LOOPBACK:
551 #endif
552 #ifdef ARPHRD_PPP
553 			case ARPHRD_PPP:
554 #endif
555 #ifdef ARPHRD_NONE
556 			case ARPHRD_NONE:
557 #endif
558 				/* We don't warn for supported families */
559 				break;
560 
561 /* IFT already checked */
562 #ifndef AF_LINK
563 			default:
564 				if (active)
565 					logwarnx("%s: unsupported"
566 					    " interface family 0x%.2x",
567 					    ifp->name, ifp->family);
568 				break;
569 #endif
570 			}
571 		}
572 
573 		if (!(ctx->options & (DHCPCD_DUMPLEASE | DHCPCD_TEST))) {
574 			/* Handle any platform init for the interface */
575 			if (active != IF_INACTIVE && if_init(ifp) == -1) {
576 				logerr("%s: if_init", ifp->name);
577 				if_free(ifp);
578 				continue;
579 			}
580 		}
581 
582 		ifp->vlanid = if_vlanid(ifp);
583 
584 #ifdef SIOCGIFPRIORITY
585 		/* Respect the interface priority */
586 		memset(&ifr, 0, sizeof(ifr));
587 		strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
588 		if (ioctl(ctx->pf_inet_fd, SIOCGIFPRIORITY, &ifr) == 0)
589 			ifp->metric = (unsigned int)ifr.ifr_metric;
590 		if_getssid(ifp);
591 #else
592 		/* We reserve the 100 range for virtual interfaces, if and when
593 		 * we can work them out. */
594 		ifp->metric = 200 + ifp->index;
595 		if (if_getssid(ifp) != -1) {
596 			ifp->wireless = true;
597 			ifp->metric += 100;
598 		}
599 #endif
600 
601 		ifp->active = active;
602 		if (ifp->active)
603 			ifp->carrier = if_carrier(ifp);
604 		else
605 			ifp->carrier = LINK_UNKNOWN;
606 		TAILQ_INSERT_TAIL(ifs, ifp, next);
607 	}
608 
609 #ifdef IFLR_ACTIVE
610 	close(link_fd);
611 #endif
612 	return ifs;
613 }
614 
615 /* Decode bge0:1 as dev = bge, ppa = 0 and lun = 1 */
616 int
617 if_nametospec(const char *ifname, struct if_spec *spec)
618 {
619 	char *ep;
620 	int e;
621 
622 	if (ifname == NULL || *ifname == '\0' ||
623 	    strlcpy(spec->ifname, ifname, sizeof(spec->ifname)) >=
624 	    sizeof(spec->ifname) ||
625 	    strlcpy(spec->drvname, ifname, sizeof(spec->drvname)) >=
626 	    sizeof(spec->drvname))
627 	{
628 		errno = EINVAL;
629 		return -1;
630 	}
631 	ep = strchr(spec->drvname, ':');
632 	if (ep) {
633 		spec->lun = (int)strtoi(ep + 1, NULL, 10, 0, INT_MAX, &e);
634 		if (e != 0) {
635 			errno = e;
636 			return -1;
637 		}
638 		*ep-- = '\0';
639 	} else {
640 		spec->lun = -1;
641 		ep = spec->drvname + strlen(spec->drvname) - 1;
642 	}
643 	strlcpy(spec->devname, spec->drvname, sizeof(spec->devname));
644 	while (ep > spec->drvname && isdigit((int)*ep))
645 		ep--;
646 	if (*ep++ == ':') {
647 		errno = EINVAL;
648 		return -1;
649 	}
650 	spec->ppa = (int)strtoi(ep, NULL, 10, 0, INT_MAX, &e);
651 	if (e != 0)
652 		spec->ppa = -1;
653 	*ep = '\0';
654 
655 	return 0;
656 }
657 
658 static struct interface *
659 if_findindexname(struct if_head *ifaces, unsigned int idx, const char *name)
660 {
661 
662 	if (ifaces != NULL) {
663 		struct if_spec spec;
664 		struct interface *ifp;
665 
666 		if (name && if_nametospec(name, &spec) == -1)
667 			return NULL;
668 
669 		TAILQ_FOREACH(ifp, ifaces, next) {
670 			if ((name && strcmp(ifp->name, spec.devname) == 0) ||
671 			    (!name && ifp->index == idx))
672 				return ifp;
673 		}
674 	}
675 
676 	errno = ENXIO;
677 	return NULL;
678 }
679 
680 struct interface *
681 if_find(struct if_head *ifaces, const char *name)
682 {
683 
684 	return if_findindexname(ifaces, 0, name);
685 }
686 
687 struct interface *
688 if_findindex(struct if_head *ifaces, unsigned int idx)
689 {
690 
691 	return if_findindexname(ifaces, idx, NULL);
692 }
693 
694 struct interface *
695 if_loopback(struct dhcpcd_ctx *ctx)
696 {
697 	struct interface *ifp;
698 
699 	TAILQ_FOREACH(ifp, ctx->ifaces, next) {
700 		if (ifp->flags & IFF_LOOPBACK)
701 			return ifp;
702 	}
703 	return NULL;
704 }
705 
706 int
707 if_domtu(const struct interface *ifp, short int mtu)
708 {
709 	int r;
710 	struct ifreq ifr;
711 
712 #ifdef __sun
713 	if (mtu == 0)
714 		return if_mtu_os(ifp);
715 #endif
716 
717 	memset(&ifr, 0, sizeof(ifr));
718 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
719 	ifr.ifr_mtu = mtu;
720 	r = ioctl(ifp->ctx->pf_inet_fd, mtu ? SIOCSIFMTU : SIOCGIFMTU, &ifr);
721 	if (r == -1)
722 		return -1;
723 	return ifr.ifr_mtu;
724 }
725 
726 #ifdef ALIAS_ADDR
727 int
728 if_makealias(char *alias, size_t alias_len, const char *ifname, int lun)
729 {
730 
731 	if (lun == 0)
732 		return strlcpy(alias, ifname, alias_len);
733 	return snprintf(alias, alias_len, "%s:%u", ifname, lun);
734 }
735 #endif
736 
737 struct interface *
738 if_findifpfromcmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg, int *hoplimit)
739 {
740 	struct cmsghdr *cm;
741 	unsigned int ifindex = 0;
742 	struct interface *ifp;
743 #ifdef INET
744 #ifdef IP_RECVIF
745 	struct sockaddr_dl sdl;
746 #else
747 	struct in_pktinfo ipi;
748 #endif
749 #endif
750 #ifdef INET6
751 	struct in6_pktinfo ipi6;
752 #else
753 	UNUSED(hoplimit);
754 #endif
755 
756 	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(msg);
757 	     cm;
758 	     cm = (struct cmsghdr *)CMSG_NXTHDR(msg, cm))
759 	{
760 #ifdef INET
761 		if (cm->cmsg_level == IPPROTO_IP) {
762 			switch(cm->cmsg_type) {
763 #ifdef IP_RECVIF
764 			case IP_RECVIF:
765 				if (cm->cmsg_len <
766 				    offsetof(struct sockaddr_dl, sdl_index) +
767 				    sizeof(sdl.sdl_index))
768 					continue;
769 				memcpy(&sdl, CMSG_DATA(cm),
770 				    MIN(sizeof(sdl), cm->cmsg_len));
771 				ifindex = sdl.sdl_index;
772 				break;
773 #else
774 			case IP_PKTINFO:
775 				if (cm->cmsg_len != CMSG_LEN(sizeof(ipi)))
776 					continue;
777 				memcpy(&ipi, CMSG_DATA(cm), sizeof(ipi));
778 				ifindex = (unsigned int)ipi.ipi_ifindex;
779 				break;
780 #endif
781 			}
782 		}
783 #endif
784 #ifdef INET6
785 		if (cm->cmsg_level == IPPROTO_IPV6) {
786 			switch(cm->cmsg_type) {
787 			case IPV6_PKTINFO:
788 				if (cm->cmsg_len != CMSG_LEN(sizeof(ipi6)))
789 					continue;
790 				memcpy(&ipi6, CMSG_DATA(cm), sizeof(ipi6));
791 				ifindex = (unsigned int)ipi6.ipi6_ifindex;
792 				break;
793 			case IPV6_HOPLIMIT:
794 				if (cm->cmsg_len != CMSG_LEN(sizeof(int)))
795 					continue;
796 				if (hoplimit == NULL)
797 					break;
798 				memcpy(hoplimit, CMSG_DATA(cm), sizeof(int));
799 				break;
800 			}
801 		}
802 #endif
803 	}
804 
805 	/* Find the receiving interface */
806 	TAILQ_FOREACH(ifp, ctx->ifaces, next) {
807 		if (ifp->index == ifindex)
808 			break;
809 	}
810 	if (ifp == NULL)
811 		errno = ESRCH;
812 	return ifp;
813 }
814 
815 int
816 xsocket(int domain, int type, int protocol)
817 {
818 	int s;
819 #if !defined(HAVE_SOCK_CLOEXEC) || !defined(HAVE_SOCK_NONBLOCK)
820 	int xflags, xtype = type;
821 #endif
822 #ifdef SO_RERROR
823 	int on;
824 #endif
825 
826 #ifndef HAVE_SOCK_CLOEXEC
827 	if (xtype & SOCK_CLOEXEC)
828 		type &= ~SOCK_CLOEXEC;
829 #endif
830 #ifndef HAVE_SOCK_NONBLOCK
831 	if (xtype & SOCK_NONBLOCK)
832 		type &= ~SOCK_NONBLOCK;
833 #endif
834 
835 	if ((s = socket(domain, type, protocol)) == -1)
836 		return -1;
837 
838 #ifndef HAVE_SOCK_CLOEXEC
839 	if ((xtype & SOCK_CLOEXEC) && ((xflags = fcntl(s, F_GETFD)) == -1 ||
840 	    fcntl(s, F_SETFD, xflags | FD_CLOEXEC) == -1))
841 		goto out;
842 #endif
843 #ifndef HAVE_SOCK_NONBLOCK
844 	if ((xtype & SOCK_NONBLOCK) && ((xflags = fcntl(s, F_GETFL)) == -1 ||
845 	    fcntl(s, F_SETFL, xflags | O_NONBLOCK) == -1))
846 		goto out;
847 #endif
848 
849 #ifdef SO_RERROR
850 	/* Tell recvmsg(2) to return ENOBUFS if the receiving socket overflows. */
851 	on = 1;
852 	if (setsockopt(s, SOL_SOCKET, SO_RERROR, &on, sizeof(on)) == -1)
853 		logerr("%s: SO_RERROR", __func__);
854 #endif
855 
856 	return s;
857 
858 #if !defined(HAVE_SOCK_CLOEXEC) || !defined(HAVE_SOCK_NONBLOCK)
859 out:
860 	close(s);
861 	return -1;
862 #endif
863 }
864