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}
#include "configure.h"
#include "dhcpf.h"
#include "if-options.h"
+#include "if-pref.h"
#include "net.h"
#include "signals.h"
{
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) {
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) {
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;
}
# 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
#include "duid.h"
#include "eloop.h"
#include "if-options.h"
+#include "if-pref.h"
#include "ipv4ll.h"
#include "net.h"
#include "signals.h"
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;
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
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);
}
{
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;
}
{
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);
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
if (ifn)
continue;
init_state(ifp, 2, UNCONST(argv));
+ run_script(ifp, "PREINIT");
+ start_interface(ifp);
if (ifl)
ifl->next = ifp;
else
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;
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 {
start_reboot(ifp);
}
}
+ sort_interfaces();
return 0;
}
}
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;
}
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 */
}
time_t nakoff;
uint32_t xid;
int socket;
- int carrier;
int probes;
int claims;
int conflicts;
unsigned char hwaddr[HWADDR_LEN];
size_t hwlen;
int metric;
+ int carrier;
int arpable;
int raw_fd;
extern int pidfd;
extern int options;
+extern struct interface *ifaces;
int handle_args(int, char **);
void handle_exit_timeout(void *);
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
+/*
+ * 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
+/*
+ * 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
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)
{
#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;
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;