if.c revision 24aa62eefd9284fb94820093f6088607d42c4249
1/*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
4 * All rights reserved
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/param.h>
29#include <sys/types.h>
30#include <sys/ioctl.h>
31#include <sys/socket.h>
32
33#include <net/if.h>
34#include <net/if_arp.h>
35#include <netinet/in.h>
36#ifdef __FreeBSD__ /* Needed so that including netinet6/in6_var.h works */
37#  include <net/if_var.h>
38#endif
39#ifdef AF_LINK
40#  include <net/if_dl.h>
41#  include <net/if_types.h>
42#  include <netinet/in_var.h>
43#endif
44#ifdef AF_PACKET
45#  include <netpacket/packet.h>
46#endif
47#ifdef SIOCGIFMEDIA
48#  include <net/if_media.h>
49#endif
50#include <net/route.h>
51
52#include <ctype.h>
53#include <errno.h>
54#include <ifaddrs.h>
55#include <fnmatch.h>
56#include <stddef.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <unistd.h>
61
62#include "config.h"
63#include "common.h"
64#include "dev.h"
65#include "dhcp.h"
66#include "dhcp6.h"
67#include "if.h"
68#include "if-options.h"
69#include "ipv4.h"
70#include "ipv6nd.h"
71
72#ifdef __QNX__
73/* QNX carries defines for, but does not actually support PF_LINK */
74#undef IFLR_ACTIVE
75#endif
76
77int
78split_interface_lease(const char *ifname, int *interface_name_len,
79			  const char **lease_identifier)
80{
81	int ret = 0;
82	int len;
83	const char *lease_ptr = ifname;
84	const char *p = strchr(ifname, '=');
85
86	if (interface_name_len)
87		len = *interface_name_len;
88	else
89		len = strlen(ifname);
90
91	if (p) {
92		lease_ptr = p + 1;
93		if (len > p - ifname)
94			len = p - ifname;
95		ret = 1;
96	}
97
98	if (interface_name_len)
99		*interface_name_len = len;
100	if (lease_identifier)
101		*lease_identifier = lease_ptr;
102	return ret;
103}
104
105void
106if_free(struct interface *ifp)
107{
108
109	if (ifp == NULL)
110		return;
111	ipv4_free(ifp);
112	dhcp_free(ifp);
113	dhcp6_free(ifp);
114	ipv6nd_free(ifp);
115	ipv6_free(ifp);
116	free_options(ifp->options);
117	free(ifp);
118}
119
120int
121if_carrier(struct interface *iface)
122{
123	int s, r;
124	struct ifreq ifr;
125#ifdef SIOCGIFMEDIA
126	struct ifmediareq ifmr;
127#endif
128
129	if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
130		return LINK_UNKNOWN;
131	memset(&ifr, 0, sizeof(ifr));
132	strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
133	if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) {
134		close(s);
135		return LINK_UNKNOWN;
136	}
137	iface->flags = (unsigned int)ifr.ifr_flags;
138
139#ifdef SIOCGIFMEDIA
140	memset(&ifmr, 0, sizeof(ifmr));
141	strlcpy(ifmr.ifm_name, iface->name, sizeof(ifmr.ifm_name));
142	if (ioctl(s, SIOCGIFMEDIA, &ifmr) != -1 &&
143	    ifmr.ifm_status & IFM_AVALID)
144		r = (ifmr.ifm_status & IFM_ACTIVE) ? LINK_UP : LINK_DOWN;
145	else
146		r = ifr.ifr_flags & IFF_RUNNING ? LINK_UP : LINK_UNKNOWN;
147#else
148	r = ifr.ifr_flags & IFF_RUNNING ? LINK_UP : LINK_DOWN;
149#endif
150	close(s);
151	return r;
152}
153
154int
155if_setflag(struct interface *ifp, short flag)
156{
157	struct ifreq ifr;
158	int s, r;
159
160	if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
161		return -1;
162	memset(&ifr, 0, sizeof(ifr));
163	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
164	r = -1;
165	if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0) {
166		if (flag == 0 || (ifr.ifr_flags & flag) == flag)
167			r = 0;
168		else {
169			ifr.ifr_flags |= flag;
170			if (ioctl(s, SIOCSIFFLAGS, &ifr) == 0)
171				r = 0;
172		}
173		ifp->flags = (unsigned int)ifr.ifr_flags;
174	}
175	close(s);
176	return r;
177}
178
179static int
180if_hasconf(struct dhcpcd_ctx *ctx, const char *ifname)
181{
182	int i;
183
184	for (i = 0; i < ctx->ifcc; i++) {
185		if (strcmp(ctx->ifcv[i], ifname) == 0)
186			return 1;
187	}
188	return 0;
189}
190
191static void if_learnaddrs1(struct dhcpcd_ctx *ctx, struct if_head *ifs,
192    struct ifaddrs *ifaddrs)
193{
194	struct ifaddrs *ifa;
195	struct interface *ifp;
196#ifdef INET
197	const struct sockaddr_in *addr, *net, *dst;
198#endif
199#ifdef INET6
200	struct sockaddr_in6 *sin6, *net6;
201#endif
202	int ifa_flags;
203
204
205	for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
206		if (ifa->ifa_addr == NULL)
207			continue;
208		if ((ifp = if_find(ifs, ifa->ifa_name)) == NULL)
209			continue;
210		switch(ifa->ifa_addr->sa_family) {
211#ifdef INET
212		case AF_INET:
213			addr = (const struct sockaddr_in *)
214			    (void *)ifa->ifa_addr;
215			net = (const struct sockaddr_in *)
216			    (void *)ifa->ifa_netmask;
217			if (ifa->ifa_flags & IFF_POINTOPOINT)
218				dst = (const struct sockaddr_in *)
219				    (void *)ifa->ifa_dstaddr;
220			else
221				dst = NULL;
222			ifa_flags = if_addrflags(&addr->sin_addr, ifp);
223			ipv4_handleifa(ctx, RTM_NEWADDR, ifs, ifa->ifa_name,
224				&addr->sin_addr,
225				&net->sin_addr,
226				dst ? &dst->sin_addr : NULL, ifa_flags);
227			break;
228#endif
229#ifdef INET6
230		case AF_INET6:
231			sin6 = (struct sockaddr_in6 *)(void *)ifa->ifa_addr;
232			net6 = (struct sockaddr_in6 *)(void *)ifa->ifa_netmask;
233#ifdef __KAME__
234			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
235				/* Remove the scope from the address */
236				sin6->sin6_addr.s6_addr[2] =
237				    sin6->sin6_addr.s6_addr[3] = '\0';
238#endif
239			ifa_flags = if_addrflags6(&sin6->sin6_addr, ifp);
240			if (ifa_flags != -1)
241				ipv6_handleifa(ctx, RTM_NEWADDR, ifs,
242				    ifa->ifa_name,
243				    &sin6->sin6_addr,
244				    ipv6_prefixlen(&net6->sin6_addr),
245				    ifa_flags);
246			break;
247#endif
248		}
249	}
250}
251
252struct if_head *
253if_discover(struct dhcpcd_ctx *ctx, int argc, char * const *argv)
254{
255	struct ifaddrs *ifaddrs, *ifa;
256	char *p;
257	int i;
258	struct if_head *ifs;
259	struct interface *ifp;
260	const char *lease_identifier;
261	char ifn[IF_NAMESIZE];
262
263#ifdef __linux__
264	char alias[IF_NAMESIZE];
265#endif
266
267#ifdef AF_LINK
268	const struct sockaddr_dl *sdl;
269#ifdef SIOCGIFPRIORITY
270	struct ifreq ifr;
271	int s_inet;
272#endif
273#ifdef IFLR_ACTIVE
274	struct if_laddrreq iflr;
275	int s_link;
276#endif
277
278#ifdef SIOCGIFPRIORITY
279	if ((s_inet = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
280		return NULL;
281#endif
282#ifdef IFLR_ACTIVE
283	if ((s_link = socket(PF_LINK, SOCK_DGRAM, 0)) == -1) {
284#ifdef SIOCGIFPRIORITY
285		close(s_inet);
286#endif
287		return NULL;
288	}
289	memset(&iflr, 0, sizeof(iflr));
290#endif
291#elif AF_PACKET
292	const struct sockaddr_ll *sll;
293#endif
294
295	if (getifaddrs(&ifaddrs) == -1)
296		return NULL;
297	ifs = malloc(sizeof(*ifs));
298	if (ifs == NULL)
299		return NULL;
300	TAILQ_INIT(ifs);
301
302	for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
303		if (ifa->ifa_addr != NULL) {
304#ifdef AF_LINK
305			if (ifa->ifa_addr->sa_family != AF_LINK)
306				continue;
307#elif AF_PACKET
308			if (ifa->ifa_addr->sa_family != AF_PACKET)
309				continue;
310#endif
311		}
312
313		/* It's possible for an interface to have >1 AF_LINK.
314		 * For our purposes, we use the first one. */
315		TAILQ_FOREACH(ifp, ifs, next) {
316			if (strcmp(ifp->name, ifa->ifa_name) == 0)
317				break;
318		}
319		if (ifp)
320			continue;
321
322		lease_identifier = NULL;
323		if (argc > 0) {
324			for (i = 0; i < argc; i++) {
325				int intf_len = strlen(argv[i]);
326				split_interface_lease(argv[i], &intf_len,
327						      &lease_identifier);
328				if (intf_len > IF_NAMESIZE)
329					continue;
330				strlcpy(ifn, argv[i], intf_len + 1);
331#ifdef __linux__
332				strlcpy(alias, argv[i], intf_len + 1);
333				/* Check the real interface name */
334				p = strchr(ifn, ':');
335				if (p)
336					*p = '\0';
337#endif
338				if (strcmp(ifn, ifa->ifa_name) == 0)
339					break;
340			}
341			if (i == argc)
342				continue;
343		} else {
344			strlcpy(ifn, ifa->ifa_name, sizeof(ifn));
345#ifdef __linux
346			strlcpy(alias, ifa->ifa_name, sizeof(alias));
347#endif
348			/* -1 means we're discovering against a specific
349			 * interface, but we still need the below rules
350			 * to apply. */
351			if (argc == -1 && strcmp(argv[0], ifa->ifa_name) != 0)
352				continue;
353		}
354		p = ifn;
355
356		for (i = 0; i < ctx->ifdc; i++)
357			if (!fnmatch(ctx->ifdv[i], p, 0))
358				break;
359		if (i < ctx->ifdc)
360			continue;
361		for (i = 0; i < ctx->ifac; i++)
362			if (!fnmatch(ctx->ifav[i], p, 0))
363				break;
364		if (ctx->ifac && i == ctx->ifac)
365			continue;
366
367		/* Ensure that the interface name has settled */
368		if (!dev_initialized(ctx, p))
369			continue;
370
371		/* Don't allow loopback or pointopoint unless explicit */
372		if (ifa->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) {
373			if ((argc == 0 || argc == -1) &&
374			    ctx->ifac == 0 && !if_hasconf(ctx, p))
375				continue;
376		}
377
378		if (if_vimaster(p) == 1) {
379			logger(ctx, argc ? LOG_ERR : LOG_DEBUG,
380			    "%s: is a Virtual Interface Master, skipping", p);
381			continue;
382		}
383
384		ifp = calloc(1, sizeof(*ifp));
385		if (ifp == NULL) {
386			logger(ctx, LOG_ERR, "%s: %m", __func__);
387			break;
388		}
389		ifp->ctx = ctx;
390#ifdef __linux__
391		strlcpy(ifp->alias, alias, sizeof(ifp->alias));
392#endif
393		strlcpy(ifp->name, p, sizeof(ifp->name));
394		if (lease_identifier) {
395			strlcpy(ifp->lease_identifier, lease_identifier,
396				sizeof(ifp->lease_identifier));
397		}
398		ifp->flags = ifa->ifa_flags;
399		ifp->carrier = if_carrier(ifp);
400
401		if (ifa->ifa_addr != NULL) {
402#ifdef AF_LINK
403			sdl = (const struct sockaddr_dl *)(void *)ifa->ifa_addr;
404
405#ifdef IFLR_ACTIVE
406			/* We need to check for active address */
407			strlcpy(iflr.iflr_name, ifp->name,
408			    sizeof(iflr.iflr_name));
409			memcpy(&iflr.addr, ifa->ifa_addr,
410			    MIN(ifa->ifa_addr->sa_len, sizeof(iflr.addr)));
411			iflr.flags = IFLR_PREFIX;
412			iflr.prefixlen = (unsigned int)sdl->sdl_alen * NBBY;
413			if (ioctl(s_link, SIOCGLIFADDR, &iflr) == -1 ||
414			    !(iflr.flags & IFLR_ACTIVE))
415			{
416				if_free(ifp);
417				continue;
418			}
419#endif
420
421			ifp->index = sdl->sdl_index;
422			switch(sdl->sdl_type) {
423#ifdef IFT_BRIDGE
424			case IFT_BRIDGE: /* FALLTHROUGH */
425#endif
426#ifdef IFT_PPP
427			case IFT_PPP: /* FALLTHROUGH */
428#endif
429#ifdef IFT_PROPVIRTUAL
430			case IFT_PROPVIRTUAL: /* FALLTHROUGH */
431#endif
432#if defined(IFT_BRIDGE) || defined(IFT_PPP) || defined(IFT_PROPVIRTUAL)
433				/* Don't allow unless explicit */
434				if ((argc == 0 || argc == -1) &&
435				    ctx->ifac == 0 &&
436				    !if_hasconf(ctx, ifp->name))
437				{
438					logger(ifp->ctx, LOG_DEBUG,
439					    "%s: ignoring due to"
440					    " interface type and"
441					    " no config",
442					    ifp->name);
443					if_free(ifp);
444					continue;
445				}
446				/* FALLTHROUGH */
447#endif
448#ifdef IFT_L2VLAN
449			case IFT_L2VLAN: /* FALLTHROUGH */
450#endif
451#ifdef IFT_L3IPVLAN
452			case IFT_L3IPVLAN: /* FALLTHROUGH */
453#endif
454			case IFT_ETHER:
455				ifp->family = ARPHRD_ETHER;
456				break;
457#ifdef IFT_IEEE1394
458			case IFT_IEEE1394:
459				ifp->family = ARPHRD_IEEE1394;
460				break;
461#endif
462#ifdef IFT_INFINIBAND
463			case IFT_INFINIBAND:
464				ifp->family = ARPHRD_INFINIBAND;
465				break;
466#endif
467			default:
468				/* Don't allow unless explicit */
469				if ((argc == 0 || argc == -1) &&
470				    ctx->ifac == 0 &&
471				    !if_hasconf(ctx, ifp->name))
472				{
473					if_free(ifp);
474					continue;
475				}
476				logger(ifp->ctx, LOG_WARNING,
477				    "%s: unsupported interface type %.2x",
478				    ifp->name, sdl->sdl_type);
479				/* Pretend it's ethernet */
480				ifp->family = ARPHRD_ETHER;
481				break;
482			}
483			ifp->hwlen = sdl->sdl_alen;
484#ifndef CLLADDR
485#  define CLLADDR(s) ((const char *)((s)->sdl_data + (s)->sdl_nlen))
486#endif
487			memcpy(ifp->hwaddr, CLLADDR(sdl), ifp->hwlen);
488#elif AF_PACKET
489			sll = (const struct sockaddr_ll *)(void *)ifa->ifa_addr;
490			ifp->index = (unsigned int)sll->sll_ifindex;
491			ifp->family = sll->sll_hatype;
492			ifp->hwlen = sll->sll_halen;
493			if (ifp->hwlen != 0)
494				memcpy(ifp->hwaddr, sll->sll_addr, ifp->hwlen);
495#endif
496		}
497#ifdef __linux__
498		/* PPP addresses on Linux don't have hardware addresses */
499		else
500			ifp->index = if_nametoindex(ifp->name);
501#endif
502
503		/* We only work on ethernet by default */
504		if (ifp->family != ARPHRD_ETHER) {
505			if ((argc == 0 || argc == -1) &&
506			    ctx->ifac == 0 && !if_hasconf(ctx, ifp->name))
507			{
508				if_free(ifp);
509				continue;
510			}
511			switch (ifp->family) {
512			case ARPHRD_IEEE1394:
513			case ARPHRD_INFINIBAND:
514#ifdef ARPHRD_LOOPBACK
515			case ARPHRD_LOOPBACK:
516#endif
517#ifdef ARPHRD_PPP
518			case ARPHRD_PPP:
519#endif
520				/* We don't warn for supported families */
521				break;
522
523/* IFT already checked */
524#ifndef AF_LINK
525			default:
526				logger(ifp->ctx, LOG_WARNING,
527				    "%s: unsupported interface family %.2x",
528				    ifp->name, ifp->family);
529				break;
530#endif
531			}
532		}
533
534		/* Handle any platform init for the interface */
535		if (if_init(ifp) == -1) {
536			logger(ifp->ctx, LOG_ERR, "%s: if_init: %m", p);
537			if_free(ifp);
538			continue;
539		}
540
541		/* Ensure that the MTU is big enough for DHCP */
542		if (if_getmtu(ifp->name) < MTU_MIN &&
543		    if_setmtu(ifp->name, MTU_MIN) == -1)
544		{
545			logger(ifp->ctx, LOG_ERR, "%s: set_mtu: %m", p);
546			if_free(ifp);
547			continue;
548		}
549
550#ifdef SIOCGIFPRIORITY
551		/* Respect the interface priority */
552		memset(&ifr, 0, sizeof(ifr));
553		strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
554		if (ioctl(s_inet, SIOCGIFPRIORITY, &ifr) == 0)
555			ifp->metric = ifr.ifr_metric;
556#else
557		/* We reserve the 100 range for virtual interfaces, if and when
558		 * we can work them out. */
559		ifp->metric = 200 + ifp->index;
560		if (if_getssid(ifp) != -1) {
561			ifp->wireless = 1;
562			ifp->metric += 100;
563		}
564#endif
565
566		TAILQ_INSERT_TAIL(ifs, ifp, next);
567	}
568
569	if (!ifs) {
570		logger(ctx, LOG_INFO, "%s: no matching interfaces", __func__);
571		for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
572			logger(ctx, LOG_INFO, "%s: considered %s",
573			       __func__, ifa->ifa_name);
574		}
575	}
576
577	if_learnaddrs1(ctx, ifs, ifaddrs);
578	freeifaddrs(ifaddrs);
579
580#ifdef SIOCGIFPRIORITY
581	close(s_inet);
582#endif
583#ifdef IFLR_ACTIVE
584	close(s_link);
585#endif
586
587	return ifs;
588}
589
590static struct interface *
591if_findindexname(struct if_head *ifaces, unsigned int idx, const char *name)
592{
593
594	if (ifaces != NULL) {
595		struct interface *ifp;
596
597		TAILQ_FOREACH(ifp, ifaces, next) {
598			if ((ifp->options == NULL ||
599			    !(ifp->options->options & DHCPCD_PFXDLGONLY)) &&
600			    ((name && strcmp(ifp->name, name) == 0) ||
601#ifdef __linux__
602			    (name && strcmp(ifp->alias, name) == 0) ||
603#endif
604			    (!name && ifp->index == idx)))
605				return ifp;
606		}
607	}
608
609	errno = ESRCH;
610	return NULL;
611}
612
613struct interface *
614if_find(struct if_head *ifaces, const char *name)
615{
616
617	return if_findindexname(ifaces, 0, name);
618}
619
620struct interface *
621if_findindex(struct if_head *ifaces, unsigned int idx)
622{
623
624	return if_findindexname(ifaces, idx, NULL);
625}
626
627int
628if_domtu(const char *ifname, short int mtu)
629{
630	int s, r;
631	struct ifreq ifr;
632
633	if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
634		return -1;
635	memset(&ifr, 0, sizeof(ifr));
636	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
637	ifr.ifr_mtu = mtu;
638	r = ioctl(s, mtu ? SIOCSIFMTU : SIOCGIFMTU, &ifr);
639	close(s);
640	if (r == -1)
641		return -1;
642	return ifr.ifr_mtu;
643}
644
645/* Interface comparer for working out ordering. */
646static int
647if_cmp(const struct interface *si, const struct interface *ti)
648{
649#ifdef INET
650	int r;
651#endif
652
653	/* Always prefer master interfaces */
654	if (!(si->options->options & DHCPCD_PFXDLGONLY) &&
655	    ti->options->options & DHCPCD_PFXDLGONLY)
656		return -1;
657	if (si->options->options & DHCPCD_PFXDLGONLY &&
658	    !(ti->options->options & DHCPCD_PFXDLGONLY))
659		return 1;
660
661	/* Check carrier status first */
662	if (si->carrier > ti->carrier)
663		return -1;
664	if (si->carrier < ti->carrier)
665		return 1;
666
667	if (D_STATE_RUNNING(si) && !D_STATE_RUNNING(ti))
668		return -1;
669	if (!D_STATE_RUNNING(si) && D_STATE_RUNNING(ti))
670		return 1;
671	if (RS_STATE_RUNNING(si) && !RS_STATE_RUNNING(ti))
672		return -1;
673	if (!RS_STATE_RUNNING(si) && RS_STATE_RUNNING(ti))
674		return 1;
675	if (D6_STATE_RUNNING(si) && !D6_STATE_RUNNING(ti))
676		return -1;
677	if (!D6_STATE_RUNNING(si) && D6_STATE_RUNNING(ti))
678		return 1;
679
680#ifdef INET
681	/* Special attention needed here due to states and IPv4LL. */
682	if ((r = ipv4_ifcmp(si, ti)) != 0)
683		return r;
684#endif
685
686	/* Finally, metric */
687	if (si->metric < ti->metric)
688		return -1;
689	if (si->metric > ti->metric)
690		return 1;
691	return 0;
692}
693
694/* Sort the interfaces into a preferred order - best first, worst last. */
695void
696if_sortinterfaces(struct dhcpcd_ctx *ctx)
697{
698	struct if_head sorted;
699	struct interface *ifp, *ift;
700
701	if (ctx->ifaces == NULL ||
702	    (ifp = TAILQ_FIRST(ctx->ifaces)) == NULL ||
703	    TAILQ_NEXT(ifp, next) == NULL)
704		return;
705
706	TAILQ_INIT(&sorted);
707	TAILQ_REMOVE(ctx->ifaces, ifp, next);
708	TAILQ_INSERT_HEAD(&sorted, ifp, next);
709	while ((ifp = TAILQ_FIRST(ctx->ifaces))) {
710		TAILQ_REMOVE(ctx->ifaces, ifp, next);
711		TAILQ_FOREACH(ift, &sorted, next) {
712			if (if_cmp(ifp, ift) == -1) {
713				TAILQ_INSERT_BEFORE(ift, ifp, next);
714				break;
715			}
716		}
717		if (ift == NULL)
718			TAILQ_INSERT_TAIL(&sorted, ifp, next);
719	}
720	TAILQ_CONCAT(ctx->ifaces, &sorted, next);
721}
722