From: Roy Marples Date: Tue, 9 Sep 2008 17:07:48 +0000 (+0000) Subject: Sort interfaces according to preference and pass this to dhcpcd-run-hooks so we can... X-Git-Tag: v5.0.0~272 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2b60ecb71e76e4a6ac765afac3ce7da87ef4464b;p=thirdparty%2Fdhcpcd.git Sort interfaces according to preference and pass this to dhcpcd-run-hooks so we can prefer configs. --- diff --git a/Makefile b/Makefile index d637330e..7949aa9d 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ PROG= dhcpcd SRCS= arp.c bind.c common.c control.c dhcp.c dhcpcd.c duid.c eloop.c -SRCS+= if-options.c ipv4ll.c net.c signals.c +SRCS+= if-options.c if-pref.c ipv4ll.c net.c signals.c SRCS+= configure.c SRCS+= ${SRC_IF} ${SRC_PF} diff --git a/configure.c b/configure.c index a4c206b7..c736ac2c 100644 --- a/configure.c +++ b/configure.c @@ -43,6 +43,7 @@ #include "configure.h" #include "dhcpf.h" #include "if-options.h" +#include "if-pref.h" #include "net.h" #include "signals.h" @@ -83,17 +84,18 @@ run_script(const struct interface *iface, const char *reason) { char *const argv[2] = { UNCONST(iface->state->options->script), NULL }; char **env = NULL, **ep; - char *path; - ssize_t e, elen; + char *path, *p; + ssize_t e, elen, l; pid_t pid; int status = 0; const struct if_options *ifo = iface->state->options; + const struct interface *ifp; syslog(LOG_DEBUG, "%s: executing `%s', reason %s", iface->name, argv[0], reason); /* Make our env */ - elen = 5; + elen = 6; env = xmalloc(sizeof(char *) * (elen + 1)); path = getenv("PATH"); if (path) { @@ -113,6 +115,21 @@ run_script(const struct interface *iface, const char *reason) snprintf(env[3], e, "pid=%d", getpid()); env[4] = xmalloc(e); snprintf(env[4], e, "metric=%d", iface->metric); + l = e = strlen("interface_order="); + for (ifp = ifaces; ifp; ifp = ifp->next) + e += strlen(ifp->name) + 1; + p = env[5] = xmalloc(e); + strlcpy(p, "interface_order=", e); + e -= l; + p += l; + for (ifp = ifaces; ifp; ifp = ifp->next) { + l = strlcpy(p, ifp->name, e); + p += l; + e -= l; + *p++ = ' '; + e--; + } + *--p = '\0'; if (iface->state->old) { e = configure_env(NULL, NULL, iface->state->old, ifo); if (e > 0) { @@ -326,6 +343,10 @@ configure(struct interface *iface, const char *reason) struct in_addr gate; #endif + /* As we are now adjusting an interface, we need to ensure + * we have them in the right order for routing and configuration. */ + sort_interfaces(); + /* Grab our IP config */ if (dhcp) { addr.s_addr = dhcp->yiaddr; diff --git a/dhcpcd-run-hooks.in b/dhcpcd-run-hooks.in index 1e5d5b37..c18bd272 100644 --- a/dhcpcd-run-hooks.in +++ b/dhcpcd-run-hooks.in @@ -25,15 +25,26 @@ uniqify() } # List interface config files in a dir -# We may wish to control the order at some point rather than just lexical +# If dhcpcd is running as a single instance then it will have a list of +# interfaces in the preferred order. +# Otherwise we just use what we have. list_interfaces() { - local x= interfaces= + local i= x= ifaces= + for i in ${interface_order}; do + [ -e "$1"/${i} ] && ifaces="${ifaces}${ifaces:+ }${i}" + done for x in "$1"/*; do [ -e "${x}" ] || continue - interfaces="${interfaces}${interfaces:+ }${x##*/}" + for i in ${interface_order}; do + if [ ${i} = "${x##*/}" ]; then + unset x + break + fi + done + [ -n "${x}" ] && ifaces="${ifaces}${ifaces:+ }${x##*/}" done - echo "${interfaces}" + echo "${ifaces}" } # We normally use sed to extract values using a key from a list of files diff --git a/dhcpcd.c b/dhcpcd.c index 95370339..9cfa1009 100644 --- a/dhcpcd.c +++ b/dhcpcd.c @@ -57,6 +57,7 @@ const char copyright[] = "Copyright (c) 2006-2008 Roy Marples"; #include "duid.h" #include "eloop.h" #include "if-options.h" +#include "if-pref.h" #include "ipv4ll.h" #include "net.h" #include "signals.h" @@ -66,12 +67,13 @@ const char copyright[] = "Copyright (c) 2006-2008 Roy Marples"; int options = 0; int pidfd = -1; +struct interface *ifaces = NULL; + static char **ifv = NULL; static int ifc = 0; static int linkfd = -1; static char *cffile = NULL; static char *pidfile; -static struct interface *ifaces = NULL; struct dhcp_op { uint8_t value; @@ -329,7 +331,7 @@ start_expire(void *arg) delete_timeout(NULL, iface); drop_config(iface, "EXPIRE"); iface->state->interval = 0; - if (iface->state->carrier != LINK_DOWN) { + if (iface->carrier != LINK_DOWN) { if (ll) start_interface(iface); else @@ -599,16 +601,16 @@ handle_carrier(const char *ifname) syslog(LOG_ERR, "carrier_status: %m"); break; case 0: - if (iface->state->carrier != LINK_DOWN) { - iface->state->carrier = LINK_DOWN; + if (iface->carrier != LINK_DOWN) { + iface->carrier = LINK_DOWN; syslog(LOG_INFO, "%s: carrier lost", iface->name); close_sockets(iface); delete_timeouts(iface, start_expire, NULL); } break; default: - if (iface->state->carrier != LINK_UP) { - iface->state->carrier = LINK_UP; + if (iface->carrier != LINK_UP) { + iface->carrier = LINK_UP; syslog(LOG_INFO, "%s: carrier acquired", iface->name); start_interface(iface); } @@ -657,7 +659,7 @@ start_reboot(struct interface *iface) { struct if_options *ifo = iface->state->options; - if (ifo->options & DHCPCD_LINK && iface->state->carrier == LINK_DOWN) { + if (ifo->options & DHCPCD_LINK && iface->carrier == LINK_DOWN) { syslog(LOG_INFO, "%s: waiting for carrier", iface->name); return; } @@ -690,6 +692,11 @@ start_interface(void *arg) { struct interface *iface = arg; + if (iface->carrier == LINK_DOWN) { + syslog(LOG_INFO, "%s: waiting for carrier", iface->name); + return; + } + iface->start_uptime = uptime(); if (!iface->state->lease.addr.s_addr) start_discover(iface); @@ -763,26 +770,19 @@ init_state(struct interface *iface, int argc, char **argv) ifs->nakoff = 1; configure_interface(iface, argc, argv); - if (!(options & DHCPCD_TEST)) - run_script(iface, "PREINIT"); - if (ifs->options->options & DHCPCD_LINK) { switch (carrier_status(iface->name)) { case 0: - ifs->carrier = LINK_DOWN; + iface->carrier = LINK_DOWN; break; case 1: - ifs->carrier = LINK_UP; + iface->carrier = LINK_UP; break; default: - ifs->carrier = LINK_UNKNOWN; + iface->carrier = LINK_UNKNOWN; } - } - - if (ifs->carrier == LINK_DOWN) - syslog(LOG_INFO, "%s: waiting for carrier", iface->name); - else - start_interface(iface); + } else + iface->carrier = LINK_UNKNOWN; } static void @@ -812,6 +812,8 @@ handle_new_interface(const char *ifname) if (ifn) continue; init_state(ifp, 2, UNCONST(argv)); + run_script(ifp, "PREINIT"); + start_interface(ifp); if (ifl) ifl->next = ifp; else @@ -859,7 +861,6 @@ handle_signal(_unused void *arg) case SIGALRM: syslog(LOG_INFO, "received SIGALRM, rebinding lease"); do_reboot = 1; - break; case SIGHUP: syslog(LOG_INFO, "received SIGHUP, releasing lease"); do_release = 1; @@ -871,9 +872,13 @@ handle_signal(_unused void *arg) return; } - for (iface = ifaces; iface; iface = iface->next) { - if (!iface->state) - continue; + /* As drop_config could re-arrange the order, we do it like this. */ + for (;;) { + for (iface = ifaces; iface; iface = iface->next) + if (iface->state && iface->state->new) + break; + if (!iface) + break; if (do_reboot) start_reboot(iface); else { @@ -940,6 +945,7 @@ handle_args(int argc, char **argv) start_reboot(ifp); } } + sort_interfaces(); return 0; } @@ -954,12 +960,15 @@ handle_args(int argc, char **argv) } if (!ifn) { init_state(ifp, argc, argv); + run_script(ifp, "PREINIT"); + start_interface(ifp); if (ifl) ifl->next = ifp; else ifaces = ifp; } } + sort_interfaces(); } return 0; } @@ -1165,14 +1174,21 @@ main(int argc, char **argv) ifc = argc - optind; ifv = argv + optind; ifaces = discover_interfaces(ifc, ifv); - for (iface = ifaces; iface; iface = iface->next) - init_state(iface, argc, argv); if (!ifaces && ifc == 1) { syslog(LOG_ERR, "interface `%s' does not exist", ifv[0]); exit(EXIT_FAILURE); } if (options & DHCPCD_BACKGROUND) daemonise(); + for (iface = ifaces; iface; iface = iface->next) + init_state(iface, argc, argv); + sort_interfaces(); + if (!(options & DHCPCD_TEST)) { + for (iface = ifaces; iface; iface = iface->next) { + run_script(iface, "PREINIT"); + start_interface(iface); + } + } start_eloop(); /* NOTREACHED */ } diff --git a/dhcpcd.h b/dhcpcd.h index 08605074..2251febd 100644 --- a/dhcpcd.h +++ b/dhcpcd.h @@ -67,7 +67,6 @@ struct if_state { time_t nakoff; uint32_t xid; int socket; - int carrier; int probes; int claims; int conflicts; @@ -84,6 +83,7 @@ struct interface unsigned char hwaddr[HWADDR_LEN]; size_t hwlen; int metric; + int carrier; int arpable; int raw_fd; @@ -106,6 +106,7 @@ struct interface extern int pidfd; extern int options; +extern struct interface *ifaces; int handle_args(int, char **); void handle_exit_timeout(void *); diff --git a/if-bsd.c b/if-bsd.c index 6dce779a..97197022 100644 --- a/if-bsd.c +++ b/if-bsd.c @@ -248,8 +248,8 @@ manage_link(int fd, struct interface * discover_interfaces(int argc, char * const *argv) { - struct interface *ifaces = NULL; + struct interface *ifs = NULL; - do_interface(NULL, &ifaces, argc, argv, NULL, NULL, 2); - return ifaces; + do_interface(NULL, &ifs, argc, argv, NULL, NULL, 2); + return ifs; } diff --git a/if-pref.c b/if-pref.c new file mode 100644 index 00000000..c6c008e7 --- /dev/null +++ b/if-pref.c @@ -0,0 +1,99 @@ +/* + * dhcpcd - DHCP client daemon + * Copyright 2006-2008 Roy Marples + * All rights reserved + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" +#include "dhcpcd.h" +#include "if-pref.h" + +/* Interface comparer for working out ordering. */ +static int +ifcmp(struct interface *si, struct interface *ti) +{ + int sill, till; + + /* If one has a lease and the other not, it takes precedence. */ + if (si->state->new && !ti->state->new) + return -1; + if (!si->state->new && ti->state->new) + return 1; + /* If we are either, they neither have a lease, or they both have. + * We need to check for IPv4LL and make it non-preferred. */ + if (si->state->new) { + sill = IN_LINKLOCAL(htonl(si->state->new->yiaddr)); + till = IN_LINKLOCAL(htonl(ti->state->new->yiaddr)); + if (!sill && till) + return -1; + if (sill && !till) + return 1; + } + /* Then carrier status. */ + if (si->carrier > ti->carrier) + return -1; + if (si->carrier < ti->carrier) + return 1; + /* Finally, metric */ + if (si->metric < ti->metric) + return -1; + if (si->metric > ti->metric) + return 1; + return 0; +} + +/* Sort the interfaces into a preferred order - best first, worst last. */ +void +sort_interfaces(void) +{ + struct interface *sorted, *ifp, *ifn, *ift; + + if (!ifaces || !ifaces->next) + return; + sorted = ifaces; + ifaces = ifaces->next; + sorted->next = NULL; + for (ifp = ifaces; ifp && (ifn = ifp->next, 1); ifp = ifn) { + /* Are we the new head? */ + if (ifcmp(ifp, sorted) == -1) { + ifp->next = sorted; + sorted = ifp; + continue; + } + /* Do we fit in the middle? */ + for (ift = sorted; ift->next; ift = ift->next) { + if (ifcmp(ifp, ift->next) == -1) { + ifp->next = ift->next; + ift->next = ifp; + break; + } + } + /* We must be at the end */ + if (!ift->next) { + ift->next = ifp; + ifp->next = NULL; + } + } + ifaces = sorted; +} diff --git a/if-pref.h b/if-pref.h new file mode 100644 index 00000000..18ad5770 --- /dev/null +++ b/if-pref.h @@ -0,0 +1,34 @@ +/* + * dhcpcd - DHCP client daemon + * Copyright 2006-2008 Roy Marples + * All rights reserved + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef IF_PREF_H +#define IF_PREF_H + +#include "dhcpcd.h" + +void sort_interfaces(void); +#endif diff --git a/net.c b/net.c index 55155d89..1f015be7 100644 --- a/net.c +++ b/net.c @@ -315,7 +315,7 @@ free_interface(struct interface *iface) int do_interface(const char *ifname, - _unused struct interface **ifaces, + _unused struct interface **ifs, _unused int argc, _unused char * const *argv, struct in_addr *addr, struct in_addr *net, int act) { @@ -385,7 +385,7 @@ do_interface(const char *ifname, #ifdef AF_LINK /* Interface discovery for BSD's */ if (act == 2 && ifr->ifr_addr.sa_family == AF_LINK) { - for (ifp = *ifaces; ifp; ifp = ifp->next) { + for (ifp = *ifs; ifp; ifp = ifp->next) { ifl = ifp; if (strcmp(ifp->name, ifr->ifr_name) == 0) break; @@ -405,7 +405,7 @@ do_interface(const char *ifname, if (ifl) ifp = ifl->next = ifn; else - ifp = *ifaces = ifn; + ifp = *ifs = ifn; sdl = xmalloc(ifr->ifr_addr.sa_len); memcpy(sdl, &ifr->ifr_addr, ifr->ifr_addr.sa_len); ifp->hwlen = sdl->sdl_alen;