changeset 961:1cd8de49352c draft

Sort interfaces according to preference and pass this to dhcpcd-run-hooks so we can prefer configs.
author Roy Marples <roy@marples.name>
date Tue, 09 Sep 2008 17:07:48 +0000
parents 2bf1fef43ace
children 8cb8068f0275
files Makefile configure.c dhcpcd-run-hooks.in dhcpcd.c dhcpcd.h if-bsd.c if-pref.c if-pref.h net.c
diffstat 9 files changed, 222 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Tue Sep 09 15:36:50 2008 +0000
+++ b/Makefile	Tue Sep 09 17:07:48 2008 +0000
@@ -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}
 
--- a/configure.c	Tue Sep 09 15:36:50 2008 +0000
+++ b/configure.c	Tue Sep 09 17:07:48 2008 +0000
@@ -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 @@
 {
 	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 @@
 	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 @@
 	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;
--- a/dhcpcd-run-hooks.in	Tue Sep 09 15:36:50 2008 +0000
+++ b/dhcpcd-run-hooks.in	Tue Sep 09 17:07:48 2008 +0000
@@ -25,15 +25,26 @@
 }
 
 # 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
--- a/dhcpcd.c	Tue Sep 09 15:36:50 2008 +0000
+++ b/dhcpcd.c	Tue Sep 09 17:07:48 2008 +0000
@@ -57,6 +57,7 @@
 #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 @@
 
 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 @@
 	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 @@
 		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 @@
 {
 	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 @@
 {
 	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 @@
 	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 @@
 			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 @@
 	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 @@
 		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 @@
 				start_reboot(ifp);
 			}
 		}
+		sort_interfaces();
 		return 0;
 	}
 
@@ -954,12 +960,15 @@
 			}
 			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 @@
 	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 */
 }
--- a/dhcpcd.h	Tue Sep 09 15:36:50 2008 +0000
+++ b/dhcpcd.h	Tue Sep 09 17:07:48 2008 +0000
@@ -67,7 +67,6 @@
 	time_t nakoff;
 	uint32_t xid;
 	int socket;
-	int carrier;
 	int probes;
 	int claims;
 	int conflicts;
@@ -84,6 +83,7 @@
 	unsigned char hwaddr[HWADDR_LEN];
 	size_t hwlen;
 	int metric;
+	int carrier;
 	int arpable;
 
 	int raw_fd;
@@ -106,6 +106,7 @@
 
 extern int pidfd;
 extern int options;
+extern struct interface *ifaces;
 
 int handle_args(int, char **);
 void handle_exit_timeout(void *);
--- a/if-bsd.c	Tue Sep 09 15:36:50 2008 +0000
+++ b/if-bsd.c	Tue Sep 09 17:07:48 2008 +0000
@@ -248,8 +248,8 @@
 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;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/if-pref.c	Tue Sep 09 17:07:48 2008 +0000
@@ -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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/if-pref.h	Tue Sep 09 17:07:48 2008 +0000
@@ -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
--- a/net.c	Tue Sep 09 15:36:50 2008 +0000
+++ b/net.c	Tue Sep 09 17:07:48 2008 +0000
@@ -315,7 +315,7 @@
 
 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 @@
 #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 @@
 			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;