blob: ddd03ffbe80e97c1b0f3074f8f1546017ed5004b [file] [log] [blame]
Samuel Tand7ed8512015-08-13 16:11:35 -07001/*
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
Samuel Tana3595822015-08-13 16:41:21 -070077int
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
Samuel Tand7ed8512015-08-13 16:11:35 -0700105void
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;
Samuel Tana3595822015-08-13 16:41:21 -0700260 const char *lease_identifier;
Samuel Tand7ed8512015-08-13 16:11:35 -0700261 char ifn[IF_NAMESIZE];
Samuel Tana3595822015-08-13 16:41:21 -0700262
263#ifdef __linux__
264 char alias[IF_NAMESIZE];
Samuel Tand7ed8512015-08-13 16:11:35 -0700265#endif
Samuel Tana3595822015-08-13 16:41:21 -0700266
Samuel Tand7ed8512015-08-13 16:11:35 -0700267#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
Samuel Tana3595822015-08-13 16:41:21 -0700322 lease_identifier = NULL;
Samuel Tand7ed8512015-08-13 16:11:35 -0700323 if (argc > 0) {
324 for (i = 0; i < argc; i++) {
Samuel Tana3595822015-08-13 16:41:21 -0700325 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);
Samuel Tand7ed8512015-08-13 16:11:35 -0700331#ifdef __linux__
Samuel Tana3595822015-08-13 16:41:21 -0700332 strlcpy(alias, argv[i], intf_len + 1);
Samuel Tand7ed8512015-08-13 16:11:35 -0700333 /* Check the real interface name */
Samuel Tand7ed8512015-08-13 16:11:35 -0700334 p = strchr(ifn, ':');
335 if (p)
336 *p = '\0';
Samuel Tana3595822015-08-13 16:41:21 -0700337#endif
Samuel Tand7ed8512015-08-13 16:11:35 -0700338 if (strcmp(ifn, ifa->ifa_name) == 0)
339 break;
Samuel Tand7ed8512015-08-13 16:11:35 -0700340 }
341 if (i == argc)
342 continue;
Samuel Tand7ed8512015-08-13 16:11:35 -0700343 } else {
Samuel Tand7ed8512015-08-13 16:11:35 -0700344 strlcpy(ifn, ifa->ifa_name, sizeof(ifn));
Samuel Tana3595822015-08-13 16:41:21 -0700345#ifdef __linux
346 strlcpy(alias, ifa->ifa_name, sizeof(alias));
Samuel Tand7ed8512015-08-13 16:11:35 -0700347#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 }
Samuel Tana3595822015-08-13 16:41:21 -0700354 p = ifn;
355
Samuel Tand7ed8512015-08-13 16:11:35 -0700356 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__
Samuel Tana3595822015-08-13 16:41:21 -0700391 strlcpy(ifp->alias, alias, sizeof(ifp->alias));
Samuel Tand7ed8512015-08-13 16:11:35 -0700392#endif
Samuel Tana3595822015-08-13 16:41:21 -0700393 strlcpy(ifp->name, p, sizeof(ifp->name));
394 if (lease_identifier) {
395 strlcpy(ifp->lease_identifier, lease_identifier,
396 sizeof(ifp->lease_identifier));
397 }
Samuel Tand7ed8512015-08-13 16:11:35 -0700398 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
Samuel Tan24aa62e2015-08-13 17:12:30 -0700569 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
Samuel Tand7ed8512015-08-13 16:11:35 -0700577 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}