diff options
Diffstat (limited to 'configure.c')
-rw-r--r-- | configure.c | 796 |
1 files changed, 579 insertions, 217 deletions
diff --git a/configure.c b/configure.c index 1e6daeb..fb28669 100644 --- a/configure.c +++ b/configure.c @@ -1,6 +1,6 @@ /* * dhcpcd - DHCP client daemon - * Copyright 2006-2008 Roy Marples <roy@marples.name> + * Copyright (c) 2006-2010 Roy Marples <roy@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -26,6 +26,7 @@ */ #include <sys/stat.h> +#include <sys/uio.h> #include <sys/wait.h> #include <netinet/in.h> @@ -35,19 +36,33 @@ #include <errno.h> #include <signal.h> #include <stdlib.h> +#include <string.h> +#include <syslog.h> #include <unistd.h> #include "config.h" #include "common.h" #include "configure.h" #include "dhcp.h" -#include "dhcpcd.h" -#include "logger.h" +#include "if-options.h" +#include "if-pref.h" #include "net.h" #include "signals.h" #define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin" +/* Some systems have route metrics */ +#ifndef HAVE_ROUTE_METRIC +# ifdef __linux__ +# define HAVE_ROUTE_METRIC 1 +# endif +# ifndef HAVE_ROUTE_METRIC +# define HAVE_ROUTE_METRIC 0 +# endif +#endif + +static struct rt *routes; + static int exec_script(char *const *argv, char *const *env) @@ -63,12 +78,12 @@ exec_script(char *const *argv, char *const *env) switch (pid = vfork()) { case -1: - logger(LOG_ERR, "vfork: %s", strerror(errno)); + syslog(LOG_ERR, "vfork: %m"); break; case 0: sigprocmask(SIG_SETMASK, &old, NULL); execve(argv[0], argv, env); - logger(LOG_ERR, "%s: %s", argv[0], strerror(errno)); + syslog(LOG_ERR, "%s: %m", argv[0]); _exit(127); /* NOTREACHED */ } @@ -79,70 +94,231 @@ exec_script(char *const *argv, char *const *env) return pid; } -int -run_script(const struct options *options, const char *iface, - const char *reason, - const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo) +static char * +make_var(const char *prefix, const char *var) { - char *const argv[2] = { UNCONST(options->script), NULL }; - char **env = NULL, **ep; - char *path; - ssize_t e, elen; - pid_t pid; - int status = 0; + size_t len; + char *v; + + len = strlen(prefix) + strlen(var) + 2; + v = xmalloc(len); + snprintf(v, len, "%s_%s", prefix, var); + return v; +} + - logger(LOG_DEBUG, "executing `%s', reason %s", options->script, reason); +static void +append_config(char ***env, ssize_t *len, + const char *prefix, const char *const *config) +{ + ssize_t i, j, e1; + char **ne, *eq; + + if (config == NULL) + return; + + ne = *env; + for (i = 0; config[i] != NULL; i++) { + eq = strchr(config[i], '='); + e1 = eq - config[i] + 1; + for (j = 0; j < *len; j++) { + if (strncmp(ne[j] + strlen(prefix) + 1, + config[i], e1) == 0) + { + free(ne[j]); + ne[j] = make_var(prefix, config[i]); + break; + } + } + if (j == *len) { + j++; + ne = xrealloc(ne, sizeof(char *) * (j + 1)); + ne[j - 1] = make_var(prefix, config[i]); + *len = j; + } + } + *env = ne; +} + +static size_t +arraytostr(const char *const *argv, char **s) +{ + const char *const *ap; + char *p; + size_t len, l; + + len = 0; + ap = argv; + while (*ap) + len += strlen(*ap++) + 1; + *s = p = xmalloc(len); + ap = argv; + while (*ap) { + l = strlen(*ap) + 1; + memcpy(p, *ap, l); + p += l; + ap++; + } + return len; +} + +static ssize_t +make_env(const struct interface *iface, char ***argv) +{ + char **env, *p; + ssize_t e, elen, l; + const struct if_options *ifo = iface->state->options; + const struct interface *ifp; /* Make our env */ - elen = 5; + elen = 8; env = xmalloc(sizeof(char *) * (elen + 1)); - path = getenv("PATH"); - if (path) { - e = strlen("PATH") + strlen(path) + 2; - env[0] = xmalloc(e); - snprintf(env[0], e, "PATH=%s", path); - } else - env[0] = xstrdup(DEFAULT_PATH); - e = strlen("interface") + strlen(iface) + 2; + e = strlen("interface") + strlen(iface->name) + 2; + env[0] = xmalloc(e); + snprintf(env[0], e, "interface=%s", iface->name); + e = strlen("reason") + strlen(iface->state->reason) + 2; env[1] = xmalloc(e); - snprintf(env[1], e, "interface=%s", iface); - e = strlen("reason") + strlen(reason) + 2; - env[2] = xmalloc(e); - snprintf(env[2], e, "reason=%s", reason); + snprintf(env[1], e, "reason=%s", iface->state->reason); e = 20; + env[2] = xmalloc(e); + snprintf(env[2], e, "pid=%d", getpid()); env[3] = xmalloc(e); - snprintf(env[3], e, "pid=%d", getpid()); + snprintf(env[3], e, "ifmetric=%d", iface->metric); env[4] = xmalloc(e); - snprintf(env[4], e, "metric=%d", options->metric); - if (dhcpo) { - e = configure_env(NULL, NULL, dhcpo, options); + snprintf(env[4], e, "ifwireless=%d", iface->wireless); + env[5] = xmalloc(e); + snprintf(env[5], e, "ifflags=%u", iface->flags); + env[6] = xmalloc(e); + snprintf(env[6], e, "ifmtu=%d", get_mtu(iface->name)); + l = e = strlen("interface_order="); + for (ifp = ifaces; ifp; ifp = ifp->next) + e += strlen(ifp->name) + 1; + p = env[7] = 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->profile) { + e = strlen("profile=") + strlen(iface->state->profile) + 2; + env[elen] = xmalloc(e); + snprintf(env[elen++], e, "profile=%s", iface->state->profile); + } + if (iface->wireless) { + e = strlen("new_ssid=") + strlen(iface->ssid) + 2; + if (iface->state->new != NULL || + strcmp(iface->state->reason, "CARRIER") == 0) + { + env = xrealloc(env, sizeof(char *) * (elen + 2)); + env[elen] = xmalloc(e); + snprintf(env[elen++], e, "new_ssid=%s", iface->ssid); + } + if (iface->state->old != NULL || + strcmp(iface->state->reason, "NOCARRIER") == 0) + { + env = xrealloc(env, sizeof(char *) * (elen + 2)); + env[elen] = xmalloc(e); + snprintf(env[elen++], e, "old_ssid=%s", iface->ssid); + } + } + if (iface->state->old) { + e = configure_env(NULL, NULL, iface->state->old, ifo); if (e > 0) { env = xrealloc(env, sizeof(char *) * (elen + e + 1)); - elen += configure_env(env + elen, "old", dhcpo, options); + elen += configure_env(env + elen, "old", + iface->state->old, ifo); } + append_config(&env, &elen, "old", + (const char *const *)ifo->config); } - if (dhcpn) { - e = configure_env(NULL, NULL, dhcpn, options); + if (iface->state->new) { + e = configure_env(NULL, NULL, iface->state->new, ifo); if (e > 0) { env = xrealloc(env, sizeof(char *) * (elen + e + 1)); - elen += configure_env(env + elen, "new", dhcpn, options); + elen += configure_env(env + elen, "new", + iface->state->new, ifo); } + append_config(&env, &elen, "new", + (const char *const *)ifo->config); } + /* Add our base environment */ - if (options->environ) { + if (ifo->environ) { e = 0; - while (options->environ[e++]) + while (ifo->environ[e++]) ; env = xrealloc(env, sizeof(char *) * (elen + e + 1)); e = 0; - while (options->environ[e]) { - env[elen + e] = xstrdup(options->environ[e]); + while (ifo->environ[e]) { + env[elen + e] = xstrdup(ifo->environ[e]); e++; } elen += e; } env[elen] = '\0'; + *argv = env; + return elen; +} + +int +send_interface(int fd, const struct interface *iface) +{ + char **env, **ep, *s; + ssize_t elen; + struct iovec iov[2]; + int retval; + + retval = 0; + make_env(iface, &env); + elen = arraytostr((const char *const *)env, &s); + iov[0].iov_base = &elen; + iov[0].iov_len = sizeof(ssize_t); + iov[1].iov_base = s; + iov[1].iov_len = elen; + retval = writev(fd, iov, 2); + ep = env; + while (*ep) + free(*ep++); + free(env); + free(s); + return retval; +} + +int +run_script(const struct interface *iface) +{ + char *const argv[2] = { UNCONST(iface->state->options->script), NULL }; + char **env = NULL, **ep; + char *path, *bigenv; + ssize_t e, elen = 0; + pid_t pid; + int status = 0; + const struct fd_list *fd; + struct iovec iov[2]; + + syslog(LOG_DEBUG, "%s: executing `%s', reason %s", + iface->name, argv[0], iface->state->reason); + + /* Make our env */ + elen = make_env(iface, &env); + env = xrealloc(env, sizeof(char *) * (elen + 2)); + /* Add path to it */ + path = getenv("PATH"); + if (path) { + e = strlen("PATH") + strlen(path) + 2; + env[elen] = xmalloc(e); + snprintf(env[elen], e, "PATH=%s", path); + } else + env[elen] = xstrdup(DEFAULT_PATH); + env[++elen] = '\0'; + pid = exec_script(argv, env); if (pid == -1) status = -1; @@ -150,13 +326,31 @@ run_script(const struct options *options, const char *iface, /* Wait for the script to finish */ while (waitpid(pid, &status, 0) == -1) { if (errno != EINTR) { - logger(LOG_ERR, "waitpid: %s", strerror(errno)); + syslog(LOG_ERR, "waitpid: %m"); status = -1; break; } } } + /* Send to our listeners */ + bigenv = NULL; + for (fd = fds; fd != NULL; fd = fd->next) { + if (fd->listener) { + if (bigenv == NULL) { + elen = arraytostr((const char *const *)env, + &bigenv); + iov[0].iov_base = &elen; + iov[0].iov_len = sizeof(ssize_t); + iov[1].iov_base = bigenv; + iov[1].iov_len = elen; + } + if (writev(fd->fd, iov, 2) == -1) + syslog(LOG_ERR, "writev: %m"); + } + } + free(bigenv); + /* Cleanup */ ep = env; while (*ep) @@ -166,251 +360,419 @@ run_script(const struct options *options, const char *iface, } static struct rt * -reverse_routes(struct rt *routes) +find_route(struct rt *rts, const struct rt *r, struct rt **lrt, + const struct rt *srt) { struct rt *rt; - struct rt *rtn = NULL; - - while (routes) { - rt = routes->next; - routes->next = rtn; - rtn = routes; - routes = rt; + + if (lrt) + *lrt = NULL; + for (rt = rts; rt; rt = rt->next) { + if (rt->dest.s_addr == r->dest.s_addr && +#if HAVE_ROUTE_METRIC + (srt || (!rt->iface || + rt->iface->metric == r->iface->metric)) && +#endif + (!srt || srt != rt) && + rt->net.s_addr == r->net.s_addr) + return rt; + if (lrt) + *lrt = rt; + } + return NULL; +} + +static void +desc_route(const char *cmd, const struct rt *rt, const char *ifname) +{ + char addr[sizeof("000.000.000.000") + 1]; + + strlcpy(addr, inet_ntoa(rt->dest), sizeof(addr)); + if (rt->gate.s_addr == INADDR_ANY) + syslog(LOG_DEBUG, "%s: %s route to %s/%d", ifname, cmd, + addr, inet_ntocidr(rt->net)); + else if (rt->gate.s_addr == rt->dest.s_addr && + rt->net.s_addr == INADDR_BROADCAST) + syslog(LOG_DEBUG, "%s: %s host route to %s", ifname, cmd, + addr); + else if (rt->dest.s_addr == INADDR_ANY && rt->net.s_addr == INADDR_ANY) + syslog(LOG_DEBUG, "%s: %s default route via %s", ifname, cmd, + inet_ntoa(rt->gate)); + else + syslog(LOG_DEBUG, "%s: %s route to %s/%d via %s", ifname, cmd, + addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate)); +} + +/* If something other than dhcpcd removes a route, + * we need to remove it from our internal table. */ +int +route_deleted(const struct rt *rt) +{ + struct rt *f, *l; + + f = find_route(routes, rt, &l, NULL); + if (f == NULL) + return 0; + desc_route("removing", f, f->iface->name); + if (l) + l->next = f->next; + else + routes = f->next; + free(f); + return 1; +} + +static int +n_route(struct rt *rt, const struct interface *iface) +{ + /* Don't set default routes if not asked to */ + if (rt->dest.s_addr == 0 && + rt->net.s_addr == 0 && + !(iface->state->options->options & DHCPCD_GATEWAY)) + return -1; + + desc_route("adding", rt, iface->name); + if (!add_route(iface, &rt->dest, &rt->net, &rt->gate, iface->metric)) + return 0; + if (errno == EEXIST) { + /* Pretend we added the subnet route */ + if (rt->dest.s_addr == (iface->addr.s_addr & iface->net.s_addr) && + rt->net.s_addr == iface->net.s_addr && + rt->gate.s_addr == 0) + return 0; + else + return -1; } - return rtn; + syslog(LOG_ERR, "%s: add_route: %m", iface->name); + return -1; } static int -delete_route(const struct interface *iface, struct rt *rt, int metric) +c_route(struct rt *ort, struct rt *nrt, const struct interface *iface) +{ + /* Don't set default routes if not asked to */ + if (nrt->dest.s_addr == 0 && + nrt->net.s_addr == 0 && + !(iface->state->options->options & DHCPCD_GATEWAY)) + return -1; + + desc_route("changing", nrt, iface->name); + /* We delete and add the route so that we can change metric. + * This also has the nice side effect of flushing ARP entries so + * we don't have to do that manually. */ + del_route(ort->iface, &ort->dest, &ort->net, &ort->gate, + ort->iface->metric); + if (!add_route(iface, &nrt->dest, &nrt->net, &nrt->gate, + iface->metric)) + return 0; + syslog(LOG_ERR, "%s: add_route: %m", iface->name); + return -1; +} + +static int +d_route(struct rt *rt, const struct interface *iface, int metric) { - char *addr; int retval; - addr = xstrdup(inet_ntoa(rt->dest)); - logger(LOG_DEBUG, "deleting route %s/%d via %s", - addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate)); - free(addr); + desc_route("deleting", rt, iface->name); retval = del_route(iface, &rt->dest, &rt->net, &rt->gate, metric); if (retval != 0 && errno != ENOENT && errno != ESRCH) - logger(LOG_ERR," del_route: %s", strerror(errno)); + syslog(LOG_ERR,"%s: del_route: %m", iface->name); return retval; } -static int -delete_routes(struct interface *iface, int metric) +static struct rt * +get_subnet_route(struct dhcp_message *dhcp) { + in_addr_t addr; + struct in_addr net; struct rt *rt; - struct rt *rtn; - int retval = 0; - rt = reverse_routes(iface->routes); - while (rt) { - rtn = rt->next; - retval += delete_route(iface, rt, metric); - free(rt); - rt = rtn; - } - iface->routes = NULL; + addr = dhcp->yiaddr; + if (addr == 0) + addr = dhcp->ciaddr; + /* Ensure we have all the needed values */ + if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1) + net.s_addr = get_netmask(addr); + if (net.s_addr == INADDR_BROADCAST || net.s_addr == INADDR_ANY) + return NULL; + rt = malloc(sizeof(*rt)); + rt->dest.s_addr = addr & net.s_addr; + rt->net.s_addr = net.s_addr; + rt->gate.s_addr = 0; + return rt; +} - return retval; +static struct rt * +add_subnet_route(struct rt *rt, const struct interface *iface) +{ + struct rt *r; + + if (iface->net.s_addr == INADDR_BROADCAST || + iface->net.s_addr == INADDR_ANY || + (iface->state->options->options & + (DHCPCD_INFORM | DHCPCD_STATIC) && + iface->state->options->req_addr.s_addr == INADDR_ANY)) + return rt; + + r = xmalloc(sizeof(*r)); + r->dest.s_addr = iface->addr.s_addr & iface->net.s_addr; + r->net.s_addr = iface->net.s_addr; + r->gate.s_addr = 0; + r->next = rt; + return r; } -static int -in_routes(const struct rt *routes, const struct rt *rt) +static struct rt * +get_routes(const struct interface *iface) { - while (routes) { - if (routes->dest.s_addr == rt->dest.s_addr && - routes->net.s_addr == rt->net.s_addr && - routes->gate.s_addr == rt->gate.s_addr) - return 0; - routes = routes->next; + struct rt *rt, *nrt = NULL, *r = NULL; + + if (iface->state->options->routes != NULL) { + for (rt = iface->state->options->routes; + rt != NULL; + rt = rt->next) + { + if (rt->gate.s_addr == 0) + break; + if (r == NULL) + r = nrt = xmalloc(sizeof(*r)); + else { + r->next = xmalloc(sizeof(*r)); + r = r->next; + } + memcpy(r, rt, sizeof(*r)); + r->next = NULL; + } + return nrt; } - return -1; + + return get_option_routes(iface->state->new, + iface->name, &iface->state->options->options); } -static int -configure_routes(struct interface *iface, const struct dhcp_message *dhcp, - const struct options *options) +/* Some DHCP servers add set host routes by setting the gateway + * to the assinged IP address. This differs from our notion of a host route + * where the gateway is the destination address, so we fix it. */ +static struct rt * +massage_host_routes(struct rt *rt, const struct interface *iface) { - struct rt *rt, *ort; - struct rt *rtn = NULL, *nr = NULL; - int remember; - int retval = 0; - char *addr; + struct rt *r; - ort = get_option_routes(dhcp); + for (r = rt; r; r = r->next) + if (r->gate.s_addr == iface->addr.s_addr && + r->net.s_addr == INADDR_BROADCAST) + r->gate.s_addr = r->dest.s_addr; + return rt; +} -#ifdef IPV4LL_ALWAYSROUTE - if (options->options & DHCPCD_IPV4LL && - IN_PRIVATE(ntohl(dhcp->yiaddr))) - { - for (rt = ort; rt; rt = rt->next) { - /* Check if we have already got a link locale route - * dished out by the DHCP server */ - if (rt->dest.s_addr == htonl(LINKLOCAL_ADDR) && - rt->net.s_addr == htonl(LINKLOCAL_MASK)) +static struct rt * +add_destination_route(struct rt *rt, const struct interface *iface) +{ + struct rt *r; + + if (!(iface->flags & IFF_POINTOPOINT) || + !has_option_mask(iface->state->options->dstmask, DHO_ROUTER)) + return rt; + r = xmalloc(sizeof(*r)); + r->dest.s_addr = INADDR_ANY; + r->net.s_addr = INADDR_ANY; + r->gate.s_addr = iface->dst.s_addr; + r->next = rt; + return r; +} + +/* We should check to ensure the routers are on the same subnet + * OR supply a host route. If not, warn and add a host route. */ +static struct rt * +add_router_host_route(struct rt *rt, const struct interface *ifp) +{ + struct rt *rtp, *rtl, *rtn; + const char *cp, *cp2, *cp3, *cplim; + + for (rtp = rt, rtl = NULL; rtp; rtl = rtp, rtp = rtp->next) { + if (rtp->dest.s_addr != INADDR_ANY) + continue; + /* Scan for a route to match */ + for (rtn = rt; rtn != rtp; rtn = rtn->next) { + /* match host */ + if (rtn->dest.s_addr == rtp->gate.s_addr) + break; + /* match subnet */ + cp = (const char *)&rtp->gate.s_addr; + cp2 = (const char *)&rtn->dest.s_addr; + cp3 = (const char *)&rtn->net.s_addr; + cplim = cp3 + sizeof(rtn->net.s_addr); + while (cp3 < cplim) { + if ((*cp++ ^ *cp2++) & *cp3++) + break; + } + if (cp3 == cplim) break; - rtn = rt; } - - if (!rt) { - rt = xmalloc(sizeof(*rt)); - rt->dest.s_addr = htonl(LINKLOCAL_ADDR); - rt->net.s_addr = htonl(LINKLOCAL_MASK); - rt->gate.s_addr = 0; - rt->next = NULL; - if (rtn) - rtn->next = rt; - else - ort = rt; + if (rtn != rtp) + continue; + if (ifp->flags & IFF_NOARP) { + syslog(LOG_WARNING, + "%s: forcing router %s through interface", + ifp->name, inet_ntoa(rtp->gate)); + rtp->gate.s_addr = 0; + continue; } + syslog(LOG_WARNING, "%s: router %s requires a host route", + ifp->name, inet_ntoa(rtp->gate)); + rtn = xmalloc(sizeof(*rtn)); + rtn->dest.s_addr = rtp->gate.s_addr; + rtn->net.s_addr = INADDR_BROADCAST; + rtn->gate.s_addr = rtp->gate.s_addr; + rtn->next = rtp; + if (rtl == NULL) + rt = rtn; + else + rtl->next = rtn; } -#endif - - /* Now remove old routes we no longer use. - * We should do this in reverse order. */ - iface->routes = reverse_routes(iface->routes); - for (rt = iface->routes; rt; rt = rt->next) - if (in_routes(ort, rt) != 0) - delete_route(iface, rt, options->metric); - - for (rt = ort; rt; rt = rt->next) { - /* Don't set default routes if not asked to */ - if (rt->dest.s_addr == 0 && - rt->net.s_addr == 0 && - !(options->options & DHCPCD_GATEWAY)) - continue; + return rt; +} - addr = xstrdup(inet_ntoa(rt->dest)); - logger(LOG_DEBUG, "adding route to %s/%d via %s", - addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate)); - free(addr); - remember = add_route(iface, &rt->dest, - &rt->net, &rt->gate, - options->metric); - retval += remember; - - /* If we failed to add the route, we may have already added it - ourselves. If so, remember it again. */ - if (remember < 0) { - if (errno != EEXIST) - logger(LOG_ERR, "add_route: %s", - strerror(errno)); - if (in_routes(iface->routes, rt) == 0) - remember = 1; - } +void +build_routes(void) +{ + struct rt *nrs = NULL, *dnr, *or, *rt, *rtn, *rtl, *lrt = NULL; + const struct interface *ifp; - /* This login is split from above due to the #ifdef below */ - if (remember >= 0) { - if (nr) { - rtn->next = xmalloc(sizeof(*rtn)); - rtn = rtn->next; + for (ifp = ifaces; ifp; ifp = ifp->next) { + if (ifp->state->new == NULL) + continue; + dnr = get_routes(ifp); + dnr = massage_host_routes(dnr, ifp); + dnr = add_subnet_route(dnr, ifp); + dnr = add_router_host_route(dnr, ifp); + dnr = add_destination_route(dnr, ifp); + for (rt = dnr; rt && (rtn = rt->next, 1); lrt = rt, rt = rtn) { + rt->iface = ifp; + /* Is this route already in our table? */ + if ((find_route(nrs, rt, NULL, NULL)) != NULL) + continue; + /* Do we already manage it? */ + if ((or = find_route(routes, rt, &rtl, NULL))) { + if (or->iface != ifp || + rt->gate.s_addr != or->gate.s_addr) + { + if (c_route(or, rt, ifp) != 0) + continue; + } + if (rtl != NULL) + rtl->next = or->next; + else + routes = or->next; + free(or); } else { - nr = rtn = xmalloc(sizeof(*rtn)); + if (n_route(rt, ifp) != 0) + continue; } - rtn->dest.s_addr = rt->dest.s_addr; - rtn->net.s_addr = rt->net.s_addr; - rtn->gate.s_addr = rt->gate.s_addr; - rtn->next = NULL; + if (dnr == rt) + dnr = rtn; + else if (lrt) + lrt->next = rtn; + rt->next = nrs; + nrs = rt; } + free_routes(dnr); } - free_routes(ort); - free_routes(iface->routes); - iface->routes = nr; - return retval; + + /* Remove old routes we used to manage */ + for (rt = routes; rt; rt = rt->next) { + if (find_route(nrs, rt, NULL, NULL) == NULL) + d_route(rt, rt->iface, rt->iface->metric); + } + + free_routes(routes); + routes = nrs; } static int delete_address(struct interface *iface) { int retval; - logger(LOG_DEBUG, "deleting IP address %s/%d", - inet_ntoa(iface->addr), - inet_ntocidr(iface->net)); - retval = del_address(iface->name, &iface->addr, &iface->net); + struct if_options *ifo; + + ifo = iface->state->options; + if (ifo->options & DHCPCD_INFORM || + (ifo->options & DHCPCD_STATIC && ifo->req_addr.s_addr == 0)) + return 0; + syslog(LOG_DEBUG, "%s: deleting IP address %s/%d", + iface->name, + inet_ntoa(iface->addr), + inet_ntocidr(iface->net)); + retval = del_address(iface, &iface->addr, &iface->net); if (retval == -1 && errno != EADDRNOTAVAIL) - logger(LOG_ERR, "del_address: %s", strerror(errno)); + syslog(LOG_ERR, "del_address: %m"); iface->addr.s_addr = 0; iface->net.s_addr = 0; return retval; } int -configure(struct interface *iface, const char *reason, - const struct dhcp_message *dhcp, const struct dhcp_message *old, - const struct dhcp_lease *lease, const struct options *options, - int up) +configure(struct interface *iface) { - struct in_addr addr; - struct in_addr net; - struct in_addr brd; -#ifdef __linux__ - struct in_addr dest; - struct in_addr gate; -#endif + struct dhcp_message *dhcp = iface->state->new; + struct dhcp_lease *lease = &iface->state->lease; + struct if_options *ifo = iface->state->options; + struct rt *rt; - /* Grab our IP config */ - if (dhcp == NULL) - up = 0; - else { - addr.s_addr = dhcp->yiaddr; - if (addr.s_addr == 0) - addr.s_addr = lease->addr.s_addr; - /* Ensure we have all the needed values */ - if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1) - net.s_addr = get_netmask(addr.s_addr); - if (get_option_addr(&brd, dhcp, DHO_BROADCAST) == -1) - brd.s_addr = addr.s_addr | ~net.s_addr; - } + /* As we are now adjusting an interface, we need to ensure + * we have them in the right order for routing and configuration. */ + sort_interfaces(); - /* If we aren't up, then reset the interface as much as we can */ - if (!up) { - /* Only reset things if we had set them before */ - if (iface->addr.s_addr != 0) { - delete_routes(iface, options->metric); - delete_address(iface); + if (dhcp == NULL) { + if (!(ifo->options & DHCPCD_PERSISTENT)) { + build_routes(); + if (iface->addr.s_addr != 0) + delete_address(iface); + run_script(iface); } - - run_script(options, iface->name, reason, NULL, old); return 0; } /* This also changes netmask */ - if (!(options->options & DHCPCD_INFORM) || - !has_address(iface->name, &addr, &net)) { - logger(LOG_DEBUG, "adding IP address %s/%d", - inet_ntoa(addr), inet_ntocidr(net)); - if (add_address(iface->name, &addr, &net, &brd) == -1 && + if (!(ifo->options & DHCPCD_INFORM) || + !has_address(iface->name, &lease->addr, &lease->net)) + { + syslog(LOG_DEBUG, "%s: adding IP address %s/%d", + iface->name, inet_ntoa(lease->addr), + inet_ntocidr(lease->net)); + if (add_address(iface, + &lease->addr, &lease->net, &lease->brd) == -1 && errno != EEXIST) { - logger(LOG_ERR, "add_address: %s", strerror(errno)); + syslog(LOG_ERR, "add_address: %m"); return -1; } } /* Now delete the old address if different */ - if (iface->addr.s_addr != addr.s_addr && + if (iface->addr.s_addr != lease->addr.s_addr && iface->addr.s_addr != 0) delete_address(iface); -#ifdef __linux__ - /* On linux, we need to change the subnet route to have our metric. */ - if (iface->addr.s_addr != lease->addr.s_addr && - options->metric > 0 && net.s_addr != INADDR_BROADCAST) - { - dest.s_addr = addr.s_addr & net.s_addr; - gate.s_addr = 0; - add_route(iface, &dest, &net, &gate, options->metric); - del_route(iface, &dest, &net, &gate, 0); + iface->addr.s_addr = lease->addr.s_addr; + iface->net.s_addr = lease->net.s_addr; + + /* We need to delete the subnet route to have our metric or + * prefer the interface. */ + rt = get_subnet_route(dhcp); + if (rt != NULL) { + rt->iface = iface; + if (!find_route(routes, rt, NULL, NULL)) + del_route(iface, &rt->dest, &rt->net, &rt->gate, 0); + free(rt); } -#endif - iface->addr.s_addr = addr.s_addr; - iface->net.s_addr = net.s_addr; - configure_routes(iface, dhcp, options); - if (!lease->frominfo) + build_routes(); + if (!iface->state->lease.frominfo && + !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))) if (write_lease(iface, dhcp) == -1) - logger(LOG_ERR, "write_lease: %s", strerror(errno)); - run_script(options, iface->name, reason, dhcp, old); + syslog(LOG_ERR, "write_lease: %m"); + run_script(iface); return 0; } |