]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Sort interfaces according to preference and pass this to dhcpcd-run-hooks so we can...
authorRoy Marples <roy@marples.name>
Tue, 9 Sep 2008 17:07:48 +0000 (17:07 +0000)
committerRoy Marples <roy@marples.name>
Tue, 9 Sep 2008 17:07:48 +0000 (17:07 +0000)
Makefile
configure.c
dhcpcd-run-hooks.in
dhcpcd.c
dhcpcd.h
if-bsd.c
if-pref.c [new file with mode: 0644]
if-pref.h [new file with mode: 0644]
net.c

index d637330e4d310d1fee0dcf623c341908583b45d0..7949aa9d897a3006d1f9e434d70f876373b4f4e1 100644 (file)
--- 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}
 
index a4c206b784e77aff0674019ec4a2a53ce0213308..c736ac2cb44aaaef2f1d6548f764a84267da8301 100644 (file)
@@ -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;
index 1e5d5b374be6fe3694a037f44efb4e23b4b585ad..c18bd27267dd46bd4605827da1e56681e00140fd 100644 (file)
@@ -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
index 953703393f9ac0365f6b944804b1985e18c14e2c..9cfa1009961dcd81c4fa1c127a3306ed4fbe5cb4 100644 (file)
--- 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 */
 }
index 0860507498f547ed27427eaf6379bcd0c309802e..2251febd8faac3c19e8004d004a83eeb9f13f45d 100644 (file)
--- 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 *);
index 6dce779a7aba9ab18ba238ae67b7d0456ea55275..97197022d0310b8f1766d10849bcbd581066b0af 100644 (file)
--- 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 (file)
index 0000000..c6c008e
--- /dev/null
+++ b/if-pref.c
@@ -0,0 +1,99 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * 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 (file)
index 0000000..18ad577
--- /dev/null
+++ b/if-pref.h
@@ -0,0 +1,34 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * 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 55155d894178c3aaedf41f3eb5e8bf60551b93d0..1f015be7bfe91852afdacc8efbf1220c139c36a2 100644 (file)
--- 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;