# dhcpcd Makefile
PROG= dhcpcd
-SRCS= arp.c bind.c common.c control.c dhcp.c dhcpcd.c duid.c eloop.c
-SRCS+= configure.c if-options.c if-pref.c ipv4ll.c net.c signals.c
+SRCS= arp.c common.c control.c dhcpcd.c duid.c eloop.c
+SRCS+= if-options.c if-pref.c net.c script.c signals.c
+SRCS+= dhcp-common.c
+SRCS+= ipv4.c dhcp.c ipv4ll.c
SRCS+= ipv6.c ipv6rs.c ipv6ns.c dhcp6.c
CFLAGS?= -O2
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
#include <unistd.h>
#include "arp.h"
-#include "bind.h"
+#include "ipv4.h"
#include "common.h"
+#include "dhcp.h"
#include "dhcpcd.h"
#include "eloop.h"
#include "if-options.h"
(sizeof(struct arphdr) + (2 * sizeof(uint32_t)) + (2 * HWADDR_LEN))
static int
-send_arp(const struct interface *ifp, int op, in_addr_t sip, in_addr_t tip)
+arp_send(const struct interface *ifp, int op, in_addr_t sip, in_addr_t tip)
{
uint8_t arp_buffer[ARP_LEN];
struct arphdr ar;
}
static void
-handle_arp_failure(struct interface *ifp)
+arp_failure(struct interface *ifp)
{
/* If we failed without a magic cookie then we need to try
unlink(ifp->leasefile);
if (!ifp->state->lease.frominfo)
- send_decline(ifp);
- close_sockets(ifp);
+ dhcp_decline(ifp);
+ dhcp_close(ifp);
eloop_timeout_delete(NULL, ifp);
if (ifp->state->lease.frominfo)
start_interface(ifp);
}
static void
-handle_arp_packet(void *arg)
+arp_packet(void *arg)
{
struct interface *ifp = arg;
uint8_t arp_buffer[ARP_LEN];
if (select_profile(ifp, hwaddr) == -1 &&
errno == ENOENT)
select_profile(ifp, inet_ntoa(ina));
- close_sockets(ifp);
+ dhcp_close(ifp);
eloop_timeout_delete(NULL, ifp);
start_interface(ifp);
return;
(size_t)ar.ar_hln),
inet_ntoa(state->fail));
errno = EEXIST;
- handle_arp_failure(ifp);
+ arp_failure(ifp);
return;
}
}
}
void
-send_arp_announce(void *arg)
+arp_announce(void *arg)
{
struct interface *ifp = arg;
struct if_state *state = ifp->state;
return;
if (ifp->arp_fd == -1) {
open_socket(ifp, ETHERTYPE_ARP);
- eloop_event_add(ifp->arp_fd, handle_arp_packet, ifp);
+ eloop_event_add(ifp->arp_fd, arp_packet, ifp);
}
if (++state->claims < ANNOUNCE_NUM)
syslog(LOG_DEBUG,
syslog(LOG_DEBUG,
"%s: sending ARP announce (%d of %d)",
ifp->name, state->claims, ANNOUNCE_NUM);
- if (send_arp(ifp, ARPOP_REQUEST,
+ if (arp_send(ifp, ARPOP_REQUEST,
state->new->yiaddr, state->new->yiaddr) == -1)
syslog(LOG_ERR, "send_arp: %m");
if (state->claims < ANNOUNCE_NUM) {
- eloop_timeout_add_sec(ANNOUNCE_WAIT, send_arp_announce, ifp);
+ eloop_timeout_add_sec(ANNOUNCE_WAIT, arp_announce, ifp);
return;
}
if (state->new->cookie != htonl(MAGIC_COOKIE)) {
tv.tv_sec = state->interval - DHCP_RAND_MIN;
tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
timernorm(&tv);
- eloop_timeout_add_tv(&tv, start_discover, ifp);
+ eloop_timeout_add_tv(&tv, dhcp_discover, ifp);
} else {
eloop_event_delete(ifp->arp_fd);
close(ifp->arp_fd);
}
void
-send_arp_probe(void *arg)
+arp_probe(void *arg)
{
struct interface *ifp = arg;
struct if_state *state = ifp->state;
if (ifp->arp_fd == -1) {
open_socket(ifp, ETHERTYPE_ARP);
- eloop_event_add(ifp->arp_fd, handle_arp_packet, ifp);
+ eloop_event_add(ifp->arp_fd, arp_packet, ifp);
}
if (state->probes == 0) {
if (arping)
tv.tv_sec = PROBE_MIN;
tv.tv_usec = arc4random() % (PROBE_MAX_U - PROBE_MIN_U);
timernorm(&tv);
- eloop_timeout_add_tv(&tv, send_arp_probe, ifp);
+ eloop_timeout_add_tv(&tv, arp_probe, ifp);
} else {
tv.tv_sec = ANNOUNCE_WAIT;
tv.tv_usec = 0;
if (arping) {
state->probes = 0;
if (++state->arping_index < state->options->arping_len)
- eloop_timeout_add_tv(&tv, send_arp_probe, ifp);
+ eloop_timeout_add_tv(&tv, arp_probe, ifp);
else
eloop_timeout_add_tv(&tv, start_interface, ifp);
} else
- eloop_timeout_add_tv(&tv, bind_interface, ifp);
+ eloop_timeout_add_tv(&tv, dhcp_bind, ifp);
}
syslog(LOG_DEBUG,
"%s: sending ARP probe (%d of %d), next in %0.2f seconds",
ifp->name, state->probes ? state->probes : PROBE_NUM, PROBE_NUM,
timeval_to_double(&tv));
- if (send_arp(ifp, ARPOP_REQUEST, 0, addr.s_addr) == -1)
+ if (arp_send(ifp, ARPOP_REQUEST, 0, addr.s_addr) == -1)
syslog(LOG_ERR, "send_arp: %m");
}
void
-start_arping(struct interface *ifp)
+arp_start(struct interface *ifp)
{
ifp->state->probes = 0;
ifp->state->arping_index = 0;
- send_arp_probe(ifp);
+ arp_probe(ifp);
}
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
#include "dhcpcd.h"
-void send_arp_announce(void *);
-void send_arp_probe(void *);
-void start_arping(struct interface *);
+void arp_announce(void *);
+void arp_probe(void *);
+void arp_start(struct interface *);
#endif
+++ /dev/null
-/*
- * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 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 <sys/param.h>
-#include <sys/time.h>
-
-#include <fcntl.h>
-#ifdef BSD
-# include <paths.h>
-#endif
-#include <signal.h>
-#include <stdlib.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include "arp.h"
-#include "bind.h"
-#include "common.h"
-#include "configure.h"
-#include "dhcpcd.h"
-#include "eloop.h"
-#include "if-options.h"
-#include "net.h"
-#include "signals.h"
-
-#ifndef _PATH_DEVNULL
-# define _PATH_DEVNULL "/dev/null"
-#endif
-
-/* We do things after aquiring the lease, so ensure we have enough time for them */
-#define DHCP_MIN_LEASE 20
-
-#ifndef THERE_IS_NO_FORK
-pid_t
-daemonise(void)
-{
- pid_t pid;
- char buf = '\0';
- int sidpipe[2], fd;
-
- eloop_timeout_delete(handle_exit_timeout, NULL);
- if (options & DHCPCD_DAEMONISED || !(options & DHCPCD_DAEMONISE))
- return 0;
- /* Setup a signal pipe so parent knows when to exit. */
- if (pipe(sidpipe) == -1) {
- syslog(LOG_ERR, "pipe: %m");
- return -1;
- }
- syslog(LOG_DEBUG, "forking to background");
- switch (pid = fork()) {
- case -1:
- syslog(LOG_ERR, "fork: %m");
- exit(EXIT_FAILURE);
- /* NOTREACHED */
- case 0:
- setsid();
- /* Notify parent it's safe to exit as we've detached. */
- close(sidpipe[0]);
- if (write(sidpipe[1], &buf, 1) == -1)
- syslog(LOG_ERR, "failed to notify parent: %m");
- close(sidpipe[1]);
- if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- close(fd);
- }
- break;
- default:
- /* Wait for child to detach */
- close(sidpipe[1]);
- if (read(sidpipe[0], &buf, 1) == -1)
- syslog(LOG_ERR, "failed to read child: %m");
- close(sidpipe[0]);
- break;
- }
- /* Done with the fd now */
- if (pid != 0) {
- syslog(LOG_INFO, "forked to background, child pid %d",pid);
- writepid(pidfd, pid);
- close(pidfd);
- pidfd = -1;
- options |= DHCPCD_FORKED;
- exit(EXIT_SUCCESS);
- }
- options |= DHCPCD_DAEMONISED;
- return pid;
-}
-#endif
-
-void
-bind_interface(void *arg)
-{
- struct interface *iface = arg;
- struct if_state *state = iface->state;
- struct if_options *ifo = state->options;
- struct dhcp_lease *lease = &state->lease;
- struct timeval tv;
-
- /* We're binding an address now - ensure that sockets are closed */
- close_sockets(iface);
- state->reason = NULL;
- if (clock_monotonic)
- get_monotonic(&lease->boundtime);
- state->xid = 0;
- free(state->old);
- state->old = state->new;
- state->new = state->offer;
- state->offer = NULL;
- get_lease(lease, state->new);
- if (ifo->options & DHCPCD_STATIC) {
- syslog(LOG_INFO, "%s: using static address %s",
- iface->name, inet_ntoa(lease->addr));
- lease->leasetime = ~0U;
- lease->net.s_addr = ifo->req_mask.s_addr;
- state->reason = "STATIC";
- } else if (state->new->cookie != htonl(MAGIC_COOKIE)) {
- syslog(LOG_INFO, "%s: using IPv4LL address %s",
- iface->name, inet_ntoa(lease->addr));
- lease->leasetime = ~0U;
- state->reason = "IPV4LL";
- } else if (ifo->options & DHCPCD_INFORM) {
- if (ifo->req_addr.s_addr != 0)
- lease->addr.s_addr = ifo->req_addr.s_addr;
- else
- lease->addr.s_addr = iface->addr.s_addr;
- syslog(LOG_INFO, "%s: received approval for %s", iface->name,
- inet_ntoa(lease->addr));
- lease->leasetime = ~0U;
- state->reason = "INFORM";
- } else {
- if (gettimeofday(&tv, NULL) == 0)
- lease->leasedfrom = tv.tv_sec;
- else if (lease->frominfo)
- state->reason = "TIMEOUT";
- if (lease->leasetime == ~0U) {
- lease->renewaltime =
- lease->rebindtime =
- lease->leasetime;
- syslog(LOG_INFO, "%s: leased %s for infinity",
- iface->name, inet_ntoa(lease->addr));
- } else {
- if (lease->leasetime < DHCP_MIN_LEASE) {
- syslog(LOG_WARNING,
- "%s: minimum lease is %d seconds",
- iface->name, DHCP_MIN_LEASE);
- lease->leasetime = DHCP_MIN_LEASE;
- }
- if (lease->rebindtime == 0)
- lease->rebindtime = lease->leasetime * T2;
- else if (lease->rebindtime >= lease->leasetime) {
- lease->rebindtime = lease->leasetime * T2;
- syslog(LOG_ERR,
- "%s: rebind time greater than lease "
- "time, forcing to %u seconds",
- iface->name, lease->rebindtime);
- }
- if (lease->renewaltime == 0)
- lease->renewaltime = lease->leasetime * T1;
- else if (lease->renewaltime > lease->rebindtime) {
- lease->renewaltime = lease->leasetime * T1;
- syslog(LOG_ERR,
- "%s: renewal time greater than rebind "
- "time, forcing to %u seconds",
- iface->name, lease->renewaltime);
- }
- syslog(LOG_INFO,
- "%s: leased %s for %u seconds", iface->name,
- inet_ntoa(lease->addr), lease->leasetime);
- }
- }
- if (options & DHCPCD_TEST) {
- state->reason = "TEST";
- run_script(iface);
- exit(EXIT_SUCCESS);
- }
- if (state->reason == NULL) {
- if (state->old) {
- if (state->old->yiaddr == state->new->yiaddr &&
- lease->server.s_addr)
- state->reason = "RENEW";
- else
- state->reason = "REBIND";
- } else if (state->state == DHS_REBOOT)
- state->reason = "REBOOT";
- else
- state->reason = "BOUND";
- }
- if (lease->leasetime == ~0U)
- lease->renewaltime = lease->rebindtime = lease->leasetime;
- else {
- eloop_timeout_add_sec(lease->renewaltime, start_renew, iface);
- eloop_timeout_add_sec(lease->rebindtime, start_rebind, iface);
- eloop_timeout_add_sec(lease->leasetime, start_expire, iface);
- syslog(LOG_DEBUG,
- "%s: renew in %u seconds, rebind in %u seconds",
- iface->name, lease->renewaltime, lease->rebindtime);
- }
- ifo->options &= ~ DHCPCD_CSR_WARNED;
- configure(iface);
- daemonise();
- state->state = DHS_BOUND;
- if (ifo->options & DHCPCD_ARP) {
- state->claims = 0;
- send_arp_announce(iface);
- }
-}
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp-common.h"
+#include "dhcp.h"
+
+int make_option_mask(const struct dhcp_opt *dopts,
+ uint8_t *mask, const char *opts, int add)
+{
+ char *token, *o, *p, *t;
+ const struct dhcp_opt *opt;
+ int match, n;
+
+ o = p = xstrdup(opts);
+ while ((token = strsep(&p, ", "))) {
+ if (*token == '\0')
+ continue;
+ for (opt = dopts; opt->option; opt++) {
+ if (!opt->var)
+ continue;
+ match = 0;
+ if (strcmp(opt->var, token) == 0)
+ match = 1;
+ else {
+ errno = 0;
+ n = strtol(token, &t, 0);
+ if (errno == 0 && !*t)
+ if (opt->option == n)
+ match = 1;
+ }
+ if (match) {
+ if (add == 2 && !(opt->type & ADDRIPV4)) {
+ free(o);
+ errno = EINVAL;
+ return -1;
+ }
+ if (add == 1 || add == 2)
+ add_option_mask(mask,
+ opt->option);
+ else
+ del_option_mask(mask,
+ opt->option);
+ break;
+ }
+ }
+ if (!opt->option) {
+ free(o);
+ errno = ENOENT;
+ return -1;
+ }
+ }
+ free(o);
+ return 0;
+}
+
+/* Decode an RFC3397 DNS search order option into a space
+ * separated string. Returns length of string (including
+ * terminating zero) or zero on error. out may be NULL
+ * to just determine output length. */
+ssize_t
+decode_rfc3397(char *out, ssize_t len, int pl, const uint8_t *p)
+{
+ const char *start;
+ ssize_t start_len;
+ const uint8_t *r, *q = p;
+ int count = 0, l, hops;
+ uint8_t ltype;
+
+ start = out;
+ start_len = len;
+ while (q - p < pl) {
+ r = NULL;
+ hops = 0;
+ /* We check we are inside our length again incase
+ * the data is NOT terminated correctly. */
+ while ((l = *q++) && q - p < pl) {
+ ltype = l & 0xc0;
+ if (ltype == 0x80 || ltype == 0x40)
+ return 0;
+ else if (ltype == 0xc0) { /* pointer */
+ l = (l & 0x3f) << 8;
+ l |= *q++;
+ /* save source of first jump. */
+ if (!r)
+ r = q;
+ hops++;
+ if (hops > 255)
+ return 0;
+ q = p + l;
+ if (q - p >= pl)
+ return 0;
+ } else {
+ /* straightforward name segment, add with '.' */
+ count += l + 1;
+ if (out) {
+ if ((ssize_t)l + 1 > len) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ memcpy(out, q, l);
+ out += l;
+ *out++ = '.';
+ len -= l;
+ len--;
+ }
+ q += l;
+ }
+ }
+ /* change last dot to space */
+ if (out && out != start)
+ *(out - 1) = ' ';
+ if (r)
+ q = r;
+ }
+
+ /* change last space to zero terminator */
+ if (out) {
+ if (out != start)
+ *(out - 1) = '\0';
+ else if (start_len > 0)
+ *out = '\0';
+ }
+
+ return count;
+}
+
+ssize_t
+print_string(char *s, ssize_t len, int dl, const uint8_t *data)
+{
+ uint8_t c;
+ const uint8_t *e, *p;
+ ssize_t bytes = 0;
+ ssize_t r;
+
+ e = data + dl;
+ while (data < e) {
+ c = *data++;
+ if (c == '\0') {
+ /* If rest is all NULL, skip it. */
+ for (p = data; p < e; p++)
+ if (*p != '\0')
+ break;
+ if (p == e)
+ break;
+ }
+ if (!isascii(c) || !isprint(c)) {
+ if (s) {
+ if (len < 5) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ r = snprintf(s, len, "\\%03o", c);
+ len -= r;
+ bytes += r;
+ s += r;
+ } else
+ bytes += 4;
+ continue;
+ }
+ switch (c) {
+ case '"': /* FALLTHROUGH */
+ case '\'': /* FALLTHROUGH */
+ case '$': /* FALLTHROUGH */
+ case '`': /* FALLTHROUGH */
+ case '\\': /* FALLTHROUGH */
+ case '|': /* FALLTHROUGH */
+ case '&':
+ if (s) {
+ if (len < 3) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ *s++ = '\\';
+ len--;
+ }
+ bytes++;
+ break;
+ }
+ if (s) {
+ *s++ = c;
+ len--;
+ }
+ bytes++;
+ }
+
+ /* NULL */
+ if (s)
+ *s = '\0';
+ bytes++;
+ return bytes;
+}
+
+ssize_t
+print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data,
+ const char *ifname)
+{
+ const uint8_t *e, *t;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ int32_t s32;
+ struct in_addr addr;
+ ssize_t bytes = 0;
+ ssize_t l;
+ char *tmp;
+
+ if (type & RFC3397) {
+ l = decode_rfc3397(NULL, 0, dl, data);
+ if (l < 1)
+ return l;
+ tmp = xmalloc(l);
+ decode_rfc3397(tmp, l, dl, data);
+ l = print_string(s, len, l - 1, (uint8_t *)tmp);
+ free(tmp);
+ return l;
+ }
+
+ if (type & RFC3361) {
+ if ((tmp = decode_rfc3361(dl, data)) == NULL)
+ return -1;
+ l = strlen(tmp);
+ l = print_string(s, len, l - 1, (uint8_t *)tmp);
+ free(tmp);
+ return l;
+ }
+
+ if (type & RFC3442)
+ return decode_rfc3442(s, len, dl, data);
+
+ if (type & RFC5969)
+ return decode_rfc5969(s, len, dl, data);
+
+ if (type & STRING) {
+ /* Some DHCP servers return NULL strings */
+ if (*data == '\0')
+ return 0;
+ return print_string(s, len, dl, data);
+ }
+
+ /* DHCPv6 status code */
+ if (type & SCODE && dl >= (int)sizeof(u16)) {
+ if (s) {
+ memcpy(&u16, data, sizeof(u16));
+ u16 = ntohs(u16);
+ l = snprintf(s, len, "%d ", u16);
+ len -= l;
+ } else
+ l = 7;
+ data += sizeof(u16);
+ dl -= sizeof(u16);
+ if (dl)
+ l += print_option(s, len, STRING, dl, data, ifname);
+ return l;
+ }
+
+ if (!s) {
+ if (type & UINT8)
+ l = 3;
+ else if (type & UINT16) {
+ l = 5;
+ dl /= 2;
+ } else if (type & SINT16) {
+ l = 6;
+ dl /= 2;
+ } else if (type & UINT32) {
+ l = 10;
+ dl /= 4;
+ } else if (type & SINT32) {
+ l = 11;
+ dl /= 4;
+ } else if (type & ADDRIPV4) {
+ l = 16;
+ dl /= 4;
+ } else if (type & ADDRIPV6) {
+ e = data + dl;
+ l = 0;
+ while (data < e) {
+ if (l)
+ l++; /* space */
+ dl = ipv6_printaddr(NULL, 0, data, ifname);
+ if (dl != -1)
+ l += dl;
+ data += 16;
+ }
+ return l + 1;
+ } else if (type & BINHEX) {
+ l = 2;
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+ return (l + 1) * dl;
+ }
+
+ t = data;
+ e = data + dl;
+ while (data < e) {
+ if (data != t && type != BINHEX) {
+ *s++ = ' ';
+ bytes++;
+ len--;
+ }
+ if (type & UINT8) {
+ l = snprintf(s, len, "%d", *data);
+ data++;
+ } else if (type & UINT16) {
+ memcpy(&u16, data, sizeof(u16));
+ u16 = ntohs(u16);
+ l = snprintf(s, len, "%d", u16);
+ data += sizeof(u16);
+ } else if (type & SINT16) {
+ memcpy(&s16, data, sizeof(s16));
+ s16 = ntohs(s16);
+ l = snprintf(s, len, "%d", s16);
+ data += sizeof(s16);
+ } else if (type & UINT32) {
+ memcpy(&u32, data, sizeof(u32));
+ u32 = ntohl(u32);
+ l = snprintf(s, len, "%d", u32);
+ data += sizeof(u32);
+ } else if (type & SINT32) {
+ memcpy(&s32, data, sizeof(s32));
+ s32 = ntohl(s32);
+ l = snprintf(s, len, "%d", s32);
+ data += sizeof(s32);
+ } else if (type & ADDRIPV4) {
+ memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
+ l = snprintf(s, len, "%s", inet_ntoa(addr));
+ data += sizeof(addr.s_addr);
+ } else if (type & ADDRIPV6) {
+ dl = ipv6_printaddr(s, len, data, ifname);
+ if (dl != -1)
+ l = dl;
+ else
+ l = 0;
+ data += 16;
+ } else if (type & BINHEX) {
+ l = snprintf(s, len, "%.2x", data[0]);
+ data++;
+ } else
+ l = 0;
+ len -= l;
+ bytes += l;
+ s += l;
+ }
+
+ return bytes;
+}
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 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 DHCPCOMMON_H
+#define DHCPCOMMON_H
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <stdint.h>
+
+#include "common.h"
+
+/* Max MTU - defines dhcp option length */
+#define MTU_MAX 1500
+#define MTU_MIN 576
+
+#define REQUEST (1 << 0)
+#define UINT8 (1 << 1)
+#define UINT16 (1 << 2)
+#define SINT16 (1 << 3)
+#define UINT32 (1 << 4)
+#define SINT32 (1 << 5)
+#define ADDRIPV4 (1 << 6)
+#define STRING (1 << 7)
+#define PAIR (1 << 8)
+#define ARRAY (1 << 9)
+#define RFC3361 (1 << 10)
+#define RFC3397 (1 << 11)
+#define RFC3442 (1 << 12)
+#define RFC5969 (1 << 13)
+#define ADDRIPV6 (1 << 14)
+#define BINHEX (1 << 15)
+#define SCODE (1 << 16)
+
+struct dhcp_opt {
+ uint16_t option;
+ int type;
+ const char *var;
+};
+
+#define add_option_mask(var, val) (var[val >> 3] |= 1 << (val & 7))
+#define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
+#define has_option_mask(var, val) (var[val >> 3] & (1 << (val & 7)))
+int make_option_mask(const struct dhcp_opt *,uint8_t *, const char *, int);
+ssize_t decode_rfc3397(char *, ssize_t, int, const uint8_t *);
+ssize_t print_string(char *, ssize_t, int, const uint8_t *);
+ssize_t print_option(char *, ssize_t, int, int, const uint8_t *, const char *);
+
+#endif
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* SUCH DAMAGE.
*/
+#include <arpa/inet.h>
+#include <net/route.h>
+#include <sys/stat.h>
+
+#ifdef __linux__
+# include <asm/types.h> /* for systems with broken headers */
+# include <linux/rtnetlink.h>
+#endif
+
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <syslog.h>
#include <unistd.h>
+#include "arp.h"
#include "config.h"
#include "common.h"
#include "dhcp.h"
+#include "dhcp-common.h"
+#include "eloop.h"
+#include "ipv4.h"
+#include "ipv4ll.h"
+#include "script.h"
+
+#define DAD "Duplicate address detected"
+#define DHCP_MIN_LEASE 20
-#define DAD "Duplicate address detected"
+static uint8_t *packet;
/* Our aggregate option buffer.
* We ONLY use this when options are split, which for most purposes is
* practically never. See RFC3396 for details. */
static uint8_t *opt_buffer;
+#define IPV4A ADDRIPV4 | ARRAY
+#define IPV4R ADDRIPV4 | REQUEST
+
+/* We should define a maximum for the NAK exponential backoff */
+#define NAKOFF_MAX 60
+
+/* Wait N nanoseconds between sending a RELEASE and dropping the address.
+ * This gives the kernel enough time to actually send it. */
+#define RELEASE_DELAY_S 0
+#define RELEASE_DELAY_NS 10000000
+
+struct dhcp_op {
+ uint8_t value;
+ const char *name;
+};
+
+static const struct dhcp_op dhcp_ops[] = {
+ { DHCP_DISCOVER, "DISCOVER" },
+ { DHCP_OFFER, "OFFER" },
+ { DHCP_REQUEST, "REQUEST" },
+ { DHCP_DECLINE, "DECLINE" },
+ { DHCP_ACK, "ACK" },
+ { DHCP_NAK, "NAK" },
+ { DHCP_RELEASE, "RELEASE" },
+ { DHCP_INFORM, "INFORM" },
+ { 0, NULL }
+};
+
const struct dhcp_opt const dhcp_opts[] = {
- { 1, IPV4 | REQUEST, "subnet_mask" },
+ { 1, ADDRIPV4 | REQUEST, "subnet_mask" },
/* RFC 3442 states that the CSR has to come before all other
* routes. For completeness, we also specify static routes,
* then routers. */
{ 121, RFC3442, "classless_static_routes" },
{ 249, RFC3442, "ms_classless_static_routes" },
- { 33, IPV4 | ARRAY | REQUEST, "static_routes" },
- { 3, IPV4 | ARRAY | REQUEST, "routers" },
+ { 33, IPV4A | REQUEST, "static_routes" },
+ { 3, IPV4A | REQUEST, "routers" },
{ 2, UINT32, "time_offset" },
- { 4, IPV4 | ARRAY, "time_servers" },
- { 5, IPV4 | ARRAY, "ien116_name_servers" },
- { 6, IPV4 | ARRAY, "domain_name_servers" },
- { 7, IPV4 | ARRAY, "log_servers" },
- { 8, IPV4 | ARRAY, "cookie_servers" },
- { 9, IPV4 | ARRAY, "lpr_servers" },
- { 10, IPV4 | ARRAY, "impress_servers" },
- { 11, IPV4 | ARRAY, "resource_location_servers" },
+ { 4, IPV4A, "time_servers" },
+ { 5, IPV4A, "ien116_name_servers" },
+ { 6, IPV4A, "domain_name_servers" },
+ { 7, IPV4A, "log_servers" },
+ { 8, IPV4A, "cookie_servers" },
+ { 9, IPV4A, "lpr_servers" },
+ { 10, IPV4A, "impress_servers" },
+ { 11, IPV4A, "resource_location_servers" },
{ 12, STRING, "host_name" },
{ 13, UINT16, "boot_size" },
{ 14, STRING, "merit_dump" },
{ 15, STRING, "domain_name" },
- { 16, IPV4, "swap_server" },
+ { 16, ADDRIPV4, "swap_server" },
{ 17, STRING, "root_path" },
{ 18, STRING, "extensions_path" },
{ 19, UINT8, "ip_forwarding" },
{ 20, UINT8, "non_local_source_routing" },
- { 21, IPV4 | ARRAY, "policy_filter" },
+ { 21, IPV4A, "policy_filter" },
{ 22, SINT16, "max_dgram_reassembly" },
{ 23, UINT16, "default_ip_ttl" },
{ 24, UINT32, "path_mtu_aging_timeout" },
{ 25, UINT16 | ARRAY, "path_mtu_plateau_table" },
{ 26, UINT16, "interface_mtu" },
{ 27, UINT8, "all_subnets_local" },
- { 28, IPV4 | REQUEST, "broadcast_address" },
+ { 28, ADDRIPV4 | REQUEST, "broadcast_address" },
{ 29, UINT8, "perform_mask_discovery" },
{ 30, UINT8, "mask_supplier" },
{ 31, UINT8, "router_discovery" },
- { 32, IPV4, "router_solicitation_address" },
+ { 32, ADDRIPV4, "router_solicitation_address" },
{ 34, UINT8, "trailer_encapsulation" },
{ 35, UINT32, "arp_cache_timeout" },
{ 36, UINT16, "ieee802_3_encapsulation" },
{ 38, UINT32, "tcp_keepalive_interval" },
{ 39, UINT8, "tcp_keepalive_garbage" },
{ 40, STRING, "nis_domain" },
- { 41, IPV4 | ARRAY, "nis_servers" },
- { 42, IPV4 | ARRAY, "ntp_servers" },
+ { 41, IPV4A, "nis_servers" },
+ { 42, IPV4A, "ntp_servers" },
{ 43, STRING, "vendor_encapsulated_options" },
- { 44, IPV4 | ARRAY, "netbios_name_servers" },
- { 45, IPV4, "netbios_dd_server" },
+ { 44, IPV4A, "netbios_name_servers" },
+ { 45, ADDRIPV4, "netbios_dd_server" },
{ 46, UINT8, "netbios_node_type" },
{ 47, STRING, "netbios_scope" },
- { 48, IPV4 | ARRAY, "font_servers" },
- { 49, IPV4 | ARRAY, "x_display_manager" },
- { 50, IPV4, "dhcp_requested_address" },
+ { 48, IPV4A, "font_servers" },
+ { 49, IPV4A, "x_display_manager" },
+ { 50, ADDRIPV4, "dhcp_requested_address" },
{ 51, UINT32 | REQUEST, "dhcp_lease_time" },
{ 52, UINT8, "dhcp_option_overload" },
{ 53, UINT8, "dhcp_message_type" },
- { 54, IPV4, "dhcp_server_identifier" },
+ { 54, ADDRIPV4, "dhcp_server_identifier" },
{ 55, UINT8 | ARRAY, "dhcp_parameter_request_list" },
{ 56, STRING, "dhcp_message" },
{ 57, UINT16, "dhcp_max_message_size" },
{ 58, UINT32 | REQUEST, "dhcp_renewal_time" },
{ 59, UINT32 | REQUEST, "dhcp_rebinding_time" },
{ 64, STRING, "nisplus_domain" },
- { 65, IPV4 | ARRAY, "nisplus_servers" },
+ { 65, IPV4A, "nisplus_servers" },
{ 66, STRING, "tftp_server_name" },
{ 67, STRING, "bootfile_name" },
- { 68, IPV4 | ARRAY, "mobile_ip_home_agent" },
- { 69, IPV4 | ARRAY, "smtp_server" },
- { 70, IPV4 | ARRAY, "pop_server" },
- { 71, IPV4 | ARRAY, "nntp_server" },
- { 72, IPV4 | ARRAY, "www_server" },
- { 73, IPV4 | ARRAY, "finger_server" },
- { 74, IPV4 | ARRAY, "irc_server" },
- { 75, IPV4 | ARRAY, "streettalk_server" },
- { 76, IPV4 | ARRAY, "streettalk_directory_assistance_server" },
+ { 68, IPV4A, "mobile_ip_home_agent" },
+ { 69, IPV4A, "smtp_server" },
+ { 70, IPV4A, "pop_server" },
+ { 71, IPV4A, "nntp_server" },
+ { 72, IPV4A, "www_server" },
+ { 73, IPV4A, "finger_server" },
+ { 74, IPV4A, "irc_server" },
+ { 75, IPV4A, "streettalk_server" },
+ { 76, IPV4A, "streettalk_directory_assistance_server" },
{ 77, STRING, "user_class" },
{ 81, STRING | RFC3397, "fqdn_name" },
- { 85, IPV4 | ARRAY, "nds_servers" },
+ { 85, IPV4A, "nds_servers" },
{ 86, STRING, "nds_tree_name" },
{ 87, STRING, "nds_context" },
{ 88, STRING | RFC3397, "bcms_controller_names" },
- { 89, IPV4 | ARRAY, "bcms_controller_address" },
+ { 89, IPV4A, "bcms_controller_address" },
{ 91, UINT32, "client_last_transaction_time" },
- { 92, IPV4 | ARRAY, "associated_ip" },
+ { 92, IPV4A, "associated_ip" },
{ 98, STRING, "uap_servers" },
- { 112, IPV4 | ARRAY, "netinfo_server_address" },
+ { 112, IPV4A, "netinfo_server_address" },
{ 113, STRING, "netinfo_server_tag" },
{ 114, STRING, "default_url" },
- { 118, IPV4, "subnet_selection" },
+ { 118, ADDRIPV4, "subnet_selection" },
{ 119, STRING | RFC3397, "domain_search" },
{ 120, STRING | RFC3361, "sip_server" },
{ 212, RFC5969, "sixrd" },
NULL
};
+static int dhcp_open(struct interface *);
+
void
print_options(void)
{
printf("%03d %s\n", opt->option, opt->var);
}
-int make_option_mask(const struct dhcp_opt *dopts,
- uint8_t *mask, const char *opts, int add)
-{
- char *token, *o, *p, *t;
- const struct dhcp_opt *opt;
- int match, n;
-
- o = p = xstrdup(opts);
- while ((token = strsep(&p, ", "))) {
- if (*token == '\0')
- continue;
- for (opt = dopts; opt->option; opt++) {
- if (!opt->var)
- continue;
- match = 0;
- if (strcmp(opt->var, token) == 0)
- match = 1;
- else {
- errno = 0;
- n = strtol(token, &t, 0);
- if (errno == 0 && !*t)
- if (opt->option == n)
- match = 1;
- }
- if (match) {
- if (add == 2 && !(opt->type & IPV4)) {
- free(o);
- errno = EINVAL;
- return -1;
- }
- if (add == 1 || add == 2)
- add_option_mask(mask,
- opt->option);
- else
- del_option_mask(mask,
- opt->option);
- break;
- }
- }
- if (!opt->option) {
- free(o);
- errno = ENOENT;
- return -1;
- }
- }
- free(o);
- return 0;
-}
-
static int
validate_length(uint8_t option, int dl, int *type)
{
opt->type & (STRING | RFC3442 | RFC5969))
return dl;
- if (opt->type & IPV4 && opt->type & ARRAY) {
+ if (opt->type & ADDRIPV4 && opt->type & ARRAY) {
if (dl < (int)sizeof(uint32_t))
return -1;
return dl - (dl % sizeof(uint32_t));
}
sz = 0;
- if (opt->type & (UINT32 | IPV4))
+ if (opt->type & (UINT32 | ADDRIPV4))
sz = sizeof(uint32_t);
if (opt->type & UINT16)
sz = sizeof(uint16_t);
return 0;
}
-/* Decode an RFC3397 DNS search order option into a space
- * separated string. Returns length of string (including
- * terminating zero) or zero on error. out may be NULL
- * to just determine output length. */
ssize_t
-decode_rfc3397(char *out, ssize_t len, int pl, const uint8_t *p)
-{
- const char *start;
- ssize_t start_len;
- const uint8_t *r, *q = p;
- int count = 0, l, hops;
- uint8_t ltype;
-
- start = out;
- start_len = len;
- while (q - p < pl) {
- r = NULL;
- hops = 0;
- /* We check we are inside our length again incase
- * the data is NOT terminated correctly. */
- while ((l = *q++) && q - p < pl) {
- ltype = l & 0xc0;
- if (ltype == 0x80 || ltype == 0x40)
- return 0;
- else if (ltype == 0xc0) { /* pointer */
- l = (l & 0x3f) << 8;
- l |= *q++;
- /* save source of first jump. */
- if (!r)
- r = q;
- hops++;
- if (hops > 255)
- return 0;
- q = p + l;
- if (q - p >= pl)
- return 0;
- } else {
- /* straightforward name segment, add with '.' */
- count += l + 1;
- if (out) {
- if ((ssize_t)l + 1 > len) {
- errno = ENOBUFS;
- return -1;
- }
- memcpy(out, q, l);
- out += l;
- *out++ = '.';
- len -= l;
- len--;
- }
- q += l;
- }
- }
- /* change last dot to space */
- if (out && out != start)
- *(out - 1) = ' ';
- if (r)
- q = r;
- }
-
- /* change last space to zero terminator */
- if (out) {
- if (out != start)
- *(out - 1) = '\0';
- else if (start_len > 0)
- *out = '\0';
- }
-
- return count;
-}
-
-static ssize_t
decode_rfc3442(char *out, ssize_t len, int pl, const uint8_t *p)
{
const uint8_t *e;
return routes;
}
-static char *
+char *
decode_rfc3361(int dl, const uint8_t *data)
{
uint8_t enc;
/* Decode an RFC5969 6rd order option into a space
* separated string. Returns length of string (including
* terminating zero) or zero on error. */
-static ssize_t
+ssize_t
decode_rfc5969(char *out, ssize_t len, int pl, const uint8_t *p)
{
uint8_t ipv4masklen, ipv6prefixlen;
return dhcp;
}
-ssize_t
-print_string(char *s, ssize_t len, int dl, const uint8_t *data)
-{
- uint8_t c;
- const uint8_t *e, *p;
- ssize_t bytes = 0;
- ssize_t r;
-
- e = data + dl;
- while (data < e) {
- c = *data++;
- if (c == '\0') {
- /* If rest is all NULL, skip it. */
- for (p = data; p < e; p++)
- if (*p != '\0')
- break;
- if (p == e)
- break;
- }
- if (!isascii(c) || !isprint(c)) {
- if (s) {
- if (len < 5) {
- errno = ENOBUFS;
- return -1;
- }
- r = snprintf(s, len, "\\%03o", c);
- len -= r;
- bytes += r;
- s += r;
- } else
- bytes += 4;
- continue;
- }
- switch (c) {
- case '"': /* FALLTHROUGH */
- case '\'': /* FALLTHROUGH */
- case '$': /* FALLTHROUGH */
- case '`': /* FALLTHROUGH */
- case '\\': /* FALLTHROUGH */
- case '|': /* FALLTHROUGH */
- case '&':
- if (s) {
- if (len < 3) {
- errno = ENOBUFS;
- return -1;
- }
- *s++ = '\\';
- len--;
- }
- bytes++;
- break;
- }
- if (s) {
- *s++ = c;
- len--;
- }
- bytes++;
- }
-
- /* NULL */
- if (s)
- *s = '\0';
- bytes++;
- return bytes;
-}
-
-ssize_t
-print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data,
- const char *ifname)
-{
- const uint8_t *e, *t;
- uint16_t u16;
- int16_t s16;
- uint32_t u32;
- int32_t s32;
- struct in_addr addr;
- ssize_t bytes = 0;
- ssize_t l;
- char *tmp;
-
- if (type & RFC3397) {
- l = decode_rfc3397(NULL, 0, dl, data);
- if (l < 1)
- return l;
- tmp = xmalloc(l);
- decode_rfc3397(tmp, l, dl, data);
- l = print_string(s, len, l - 1, (uint8_t *)tmp);
- free(tmp);
- return l;
- }
-
- if (type & RFC3361) {
- if ((tmp = decode_rfc3361(dl, data)) == NULL)
- return -1;
- l = strlen(tmp);
- l = print_string(s, len, l - 1, (uint8_t *)tmp);
- free(tmp);
- return l;
- }
-
- if (type & RFC3442)
- return decode_rfc3442(s, len, dl, data);
-
- if (type & RFC5969)
- return decode_rfc5969(s, len, dl, data);
-
- if (type & STRING) {
- /* Some DHCP servers return NULL strings */
- if (*data == '\0')
- return 0;
- return print_string(s, len, dl, data);
- }
-
- /* DHCPv6 status code */
- if (type & SCODE && dl >= (int)sizeof(u16)) {
- if (s) {
- memcpy(&u16, data, sizeof(u16));
- u16 = ntohs(u16);
- l = snprintf(s, len, "%d ", u16);
- len -= l;
- } else
- l = 7;
- data += sizeof(u16);
- dl -= sizeof(u16);
- if (dl)
- l += print_option(s, len, STRING, dl, data, ifname);
- return l;
- }
-
- if (type & IPV6) {
- }
-
- if (!s) {
- if (type & UINT8)
- l = 3;
- else if (type & UINT16) {
- l = 5;
- dl /= 2;
- } else if (type & SINT16) {
- l = 6;
- dl /= 2;
- } else if (type & UINT32) {
- l = 10;
- dl /= 4;
- } else if (type & SINT32) {
- l = 11;
- dl /= 4;
- } else if (type & IPV4) {
- l = 16;
- dl /= 4;
- } else if (type & IPV6) {
- e = data + dl;
- l = 0;
- while (data < e) {
- if (l)
- l++; /* space */
- dl = ipv6_printaddr(NULL, 0, data, ifname);
- if (dl != -1)
- l += dl;
- data += 16;
- }
- return l + 1;
- } else if (type & BINHEX) {
- l = 2;
- } else {
- errno = EINVAL;
- return -1;
- }
- return (l + 1) * dl;
- }
-
- t = data;
- e = data + dl;
- while (data < e) {
- if (data != t && type != BINHEX) {
- *s++ = ' ';
- bytes++;
- len--;
- }
- if (type & UINT8) {
- l = snprintf(s, len, "%d", *data);
- data++;
- } else if (type & UINT16) {
- memcpy(&u16, data, sizeof(u16));
- u16 = ntohs(u16);
- l = snprintf(s, len, "%d", u16);
- data += sizeof(u16);
- } else if (type & SINT16) {
- memcpy(&s16, data, sizeof(s16));
- s16 = ntohs(s16);
- l = snprintf(s, len, "%d", s16);
- data += sizeof(s16);
- } else if (type & UINT32) {
- memcpy(&u32, data, sizeof(u32));
- u32 = ntohl(u32);
- l = snprintf(s, len, "%d", u32);
- data += sizeof(u32);
- } else if (type & SINT32) {
- memcpy(&s32, data, sizeof(s32));
- s32 = ntohl(s32);
- l = snprintf(s, len, "%d", s32);
- data += sizeof(s32);
- } else if (type & IPV4) {
- memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
- l = snprintf(s, len, "%s", inet_ntoa(addr));
- data += sizeof(addr.s_addr);
- } else if (type & IPV6) {
- dl = ipv6_printaddr(s, len, data, ifname);
- if (dl != -1)
- l = dl;
- else
- l = 0;
- data += 16;
- } else if (type & BINHEX) {
- l = snprintf(s, len, "%.2x", data[0]);
- data++;
- } else
- l = 0;
- len -= l;
- bytes += l;
- s += l;
- }
-
- return bytes;
-}
-
ssize_t
configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
const struct interface *ifp)
if (get_option_addr(&lease->server, dhcp, DHO_SERVERID) != 0)
lease->server.s_addr = INADDR_ANY;
}
+
+static const char *
+get_dhcp_op(uint8_t type)
+{
+ const struct dhcp_op *d;
+
+ for (d = dhcp_ops; d->name; d++)
+ if (d->value == type)
+ return d->name;
+ return NULL;
+}
+
+static void
+dhcp_fallback(void *arg)
+{
+ struct interface *iface;
+
+ iface = (struct interface *)arg;
+ select_profile(iface, iface->state->options->fallback);
+ start_interface(iface);
+}
+
+uint32_t
+dhcp_xid(const struct interface *ifp)
+{
+ uint32_t xid;
+
+ if (ifp->state->options->options & DHCPCD_XID_HWADDR &&
+ ifp->hwlen >= sizeof(xid))
+ /* The lower bits are probably more unique on the network */
+ memcpy(&xid, (ifp->hwaddr + ifp->hwlen) - sizeof(xid),
+ sizeof(xid));
+ else
+ xid = arc4random();
+
+ return xid;
+}
+
+void
+dhcp_close(struct interface *iface)
+{
+
+ if (iface->arp_fd != -1) {
+ eloop_event_delete(iface->arp_fd);
+ close(iface->arp_fd);
+ iface->arp_fd = -1;
+ }
+ if (iface->raw_fd != -1) {
+ eloop_event_delete(iface->raw_fd);
+ close(iface->raw_fd);
+ iface->raw_fd = -1;
+ }
+ if (iface->udp_fd != -1) {
+ /* we don't listen to events on the udp */
+ close(iface->udp_fd);
+ iface->udp_fd = -1;
+ }
+}
+
+static void
+send_message(struct interface *iface, int type,
+ void (*callback)(void *))
+{
+ struct if_state *state = iface->state;
+ struct if_options *ifo = state->options;
+ struct dhcp_message *dhcp;
+ uint8_t *udp;
+ ssize_t len, r;
+ struct in_addr from, to;
+ in_addr_t a = 0;
+ struct timeval tv;
+
+ if (!callback)
+ syslog(LOG_DEBUG, "%s: sending %s with xid 0x%x",
+ iface->name, get_dhcp_op(type), state->xid);
+ else {
+ if (state->interval == 0)
+ state->interval = 4;
+ else {
+ state->interval *= 2;
+ if (state->interval > 64)
+ state->interval = 64;
+ }
+ tv.tv_sec = state->interval + DHCP_RAND_MIN;
+ tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
+ timernorm(&tv);
+ syslog(LOG_DEBUG,
+ "%s: sending %s (xid 0x%x), next in %0.2f seconds",
+ iface->name, get_dhcp_op(type), state->xid,
+ timeval_to_double(&tv));
+ }
+
+ /* Ensure sockets are open. */
+ if (dhcp_open(iface) == -1) {
+ if (!(options & DHCPCD_TEST))
+ dhcp_drop(iface, "FAIL");
+ return;
+ }
+
+ /* If we couldn't open a UDP port for our IP address
+ * then we cannot renew.
+ * This could happen if our IP was pulled out from underneath us.
+ * Also, we should not unicast from a BOOTP lease. */
+ if (iface->udp_fd == -1 ||
+ (!(ifo->options & DHCPCD_INFORM) && is_bootp(iface->state->new)))
+ {
+ a = iface->addr.s_addr;
+ iface->addr.s_addr = 0;
+ }
+ len = make_message(&dhcp, iface, type);
+ if (a)
+ iface->addr.s_addr = a;
+ from.s_addr = dhcp->ciaddr;
+ if (from.s_addr)
+ to.s_addr = state->lease.server.s_addr;
+ else
+ to.s_addr = 0;
+ if (to.s_addr && to.s_addr != INADDR_BROADCAST) {
+ r = send_packet(iface, to, (uint8_t *)dhcp, len);
+ if (r == -1) {
+ syslog(LOG_ERR, "%s: send_packet: %m", iface->name);
+ dhcp_close(iface);
+ }
+ } else {
+ len = make_udp_packet(&udp, (uint8_t *)dhcp, len, from, to);
+ r = send_raw_packet(iface, ETHERTYPE_IP, udp, len);
+ free(udp);
+ /* If we failed to send a raw packet this normally means
+ * we don't have the ability to work beneath the IP layer
+ * for this interface.
+ * As such we remove it from consideration without actually
+ * stopping the interface. */
+ if (r == -1) {
+ syslog(LOG_ERR, "%s: send_raw_packet: %m", iface->name);
+ if (!(options & DHCPCD_TEST))
+ dhcp_drop(iface, "FAIL");
+ dhcp_close(iface);
+ eloop_timeout_delete(NULL, iface);
+ callback = NULL;
+ }
+ }
+ free(dhcp);
+
+ /* Even if we fail to send a packet we should continue as we are
+ * as our failure timeouts will change out codepath when needed. */
+ if (callback)
+ eloop_timeout_add_tv(&tv, callback, iface);
+}
+
+static void
+send_inform(void *arg)
+{
+
+ send_message((struct interface *)arg, DHCP_INFORM, send_inform);
+}
+
+static void
+send_discover(void *arg)
+{
+
+ send_message((struct interface *)arg, DHCP_DISCOVER, send_discover);
+}
+
+static void
+send_request(void *arg)
+{
+
+ send_message((struct interface *)arg, DHCP_REQUEST, send_request);
+}
+
+static void
+send_renew(void *arg)
+{
+
+ send_message((struct interface *)arg, DHCP_REQUEST, send_renew);
+}
+
+static void
+send_rebind(void *arg)
+{
+
+ send_message((struct interface *)arg, DHCP_REQUEST, send_rebind);
+}
+
+void
+dhcp_discover(void *arg)
+{
+ struct interface *iface = arg;
+ struct if_options *ifo = iface->state->options;
+ int timeout = ifo->timeout;
+
+ /* If we're rebooting and we're not daemonised then we need
+ * to shorten the normal timeout to ensure we try correctly
+ * for a fallback or IPv4LL address. */
+ if (iface->state->state == DHS_REBOOT &&
+ !(options & DHCPCD_DAEMONISED))
+ {
+ timeout -= ifo->reboot;
+ if (timeout <= 0)
+ timeout = 2;
+ }
+
+ iface->state->state = DHS_DISCOVER;
+ iface->state->xid = dhcp_xid(iface);
+ eloop_timeout_delete(NULL, iface);
+ if (ifo->fallback)
+ eloop_timeout_add_sec(timeout, dhcp_fallback, iface);
+ else if (ifo->options & DHCPCD_IPV4LL &&
+ !IN_LINKLOCAL(htonl(iface->addr.s_addr)))
+ {
+ if (IN_LINKLOCAL(htonl(iface->state->fail.s_addr)))
+ eloop_timeout_add_sec(RATE_LIMIT_INTERVAL,
+ ipv4ll_start, iface);
+ else
+ eloop_timeout_add_sec(timeout, ipv4ll_start, iface);
+ }
+ if (ifo->options & DHCPCD_REQUEST)
+ syslog(LOG_INFO, "%s: broadcasting for a lease (requesting %s)",
+ iface->name, inet_ntoa(ifo->req_addr));
+ else
+ syslog(LOG_INFO, "%s: broadcasting for a lease", iface->name);
+ send_discover(iface);
+}
+
+static void
+dhcp_request(void *arg)
+{
+ struct interface *iface = arg;
+
+ iface->state->state = DHS_REQUEST;
+ send_request(iface);
+}
+
+static void
+dhcp_expire(void *arg)
+{
+ struct interface *iface = arg;
+
+ iface->state->interval = 0;
+ if (iface->addr.s_addr == 0) {
+ /* We failed to reboot, so enter discovery. */
+ iface->state->lease.addr.s_addr = 0;
+ dhcp_discover(iface);
+ return;
+ }
+
+ syslog(LOG_ERR, "%s: lease expired", iface->name);
+ eloop_timeout_delete(NULL, iface);
+ dhcp_drop(iface, "EXPIRE");
+ unlink(iface->leasefile);
+ if (iface->carrier != LINK_DOWN)
+ start_interface(iface);
+}
+
+void
+dhcp_release(struct interface *iface)
+{
+ struct timespec ts;
+
+ if (iface->state->new != NULL &&
+ iface->state->new->cookie == htonl(MAGIC_COOKIE))
+ {
+ syslog(LOG_INFO, "%s: releasing lease of %s",
+ iface->name, inet_ntoa(iface->state->lease.addr));
+ iface->state->xid = dhcp_xid(iface);
+ send_message(iface, DHCP_RELEASE, NULL);
+ /* Give the packet a chance to go before dropping the ip */
+ ts.tv_sec = RELEASE_DELAY_S;
+ ts.tv_nsec = RELEASE_DELAY_NS;
+ nanosleep(&ts, NULL);
+ dhcp_drop(iface, "RELEASE");
+ }
+ unlink(iface->leasefile);
+}
+
+void
+dhcp_decline(struct interface *ifp)
+{
+
+ send_message(ifp, DHCP_DECLINE, NULL);
+}
+
+static void
+dhcp_renew(void *arg)
+{
+ struct interface *iface = arg;
+ struct dhcp_lease *lease = &iface->state->lease;
+
+ syslog(LOG_INFO, "%s: renewing lease of %s",
+ iface->name, inet_ntoa(lease->addr));
+ syslog(LOG_DEBUG, "%s: rebind in %u seconds, expire in %u seconds",
+ iface->name, lease->rebindtime - lease->renewaltime,
+ lease->leasetime - lease->renewaltime);
+ iface->state->state = DHS_RENEW;
+ iface->state->xid = dhcp_xid(iface);
+ send_renew(iface);
+}
+
+static void
+dhcp_rebind(void *arg)
+{
+ struct interface *iface = arg;
+ struct dhcp_lease *lease = &iface->state->lease;
+
+ syslog(LOG_ERR, "%s: failed to renew, attempting to rebind",
+ iface->name);
+ syslog(LOG_DEBUG, "%s: expire in %u seconds",
+ iface->name, lease->leasetime - lease->rebindtime);
+ iface->state->state = DHS_REBIND;
+ eloop_timeout_delete(send_renew, iface);
+ iface->state->lease.server.s_addr = 0;
+ send_rebind(iface);
+}
+
+void
+dhcp_bind(void *arg)
+{
+ struct interface *iface = arg;
+ struct if_state *state = iface->state;
+ struct if_options *ifo = state->options;
+ struct dhcp_lease *lease = &state->lease;
+ struct timeval tv;
+
+ /* We're binding an address now - ensure that sockets are closed */
+ dhcp_close(iface);
+ state->reason = NULL;
+ if (clock_monotonic)
+ get_monotonic(&lease->boundtime);
+ state->xid = 0;
+ free(state->old);
+ state->old = state->new;
+ state->new = state->offer;
+ state->offer = NULL;
+ get_lease(lease, state->new);
+ if (ifo->options & DHCPCD_STATIC) {
+ syslog(LOG_INFO, "%s: using static address %s",
+ iface->name, inet_ntoa(lease->addr));
+ lease->leasetime = ~0U;
+ lease->net.s_addr = ifo->req_mask.s_addr;
+ state->reason = "STATIC";
+ } else if (state->new->cookie != htonl(MAGIC_COOKIE)) {
+ syslog(LOG_INFO, "%s: using IPv4LL address %s",
+ iface->name, inet_ntoa(lease->addr));
+ lease->leasetime = ~0U;
+ state->reason = "IPV4LL";
+ } else if (ifo->options & DHCPCD_INFORM) {
+ if (ifo->req_addr.s_addr != 0)
+ lease->addr.s_addr = ifo->req_addr.s_addr;
+ else
+ lease->addr.s_addr = iface->addr.s_addr;
+ syslog(LOG_INFO, "%s: received approval for %s", iface->name,
+ inet_ntoa(lease->addr));
+ lease->leasetime = ~0U;
+ state->reason = "INFORM";
+ } else {
+ if (gettimeofday(&tv, NULL) == 0)
+ lease->leasedfrom = tv.tv_sec;
+ else if (lease->frominfo)
+ state->reason = "TIMEOUT";
+ if (lease->leasetime == ~0U) {
+ lease->renewaltime =
+ lease->rebindtime =
+ lease->leasetime;
+ syslog(LOG_INFO, "%s: leased %s for infinity",
+ iface->name, inet_ntoa(lease->addr));
+ } else {
+ if (lease->leasetime < DHCP_MIN_LEASE) {
+ syslog(LOG_WARNING,
+ "%s: minimum lease is %d seconds",
+ iface->name, DHCP_MIN_LEASE);
+ lease->leasetime = DHCP_MIN_LEASE;
+ }
+ if (lease->rebindtime == 0)
+ lease->rebindtime = lease->leasetime * T2;
+ else if (lease->rebindtime >= lease->leasetime) {
+ lease->rebindtime = lease->leasetime * T2;
+ syslog(LOG_ERR,
+ "%s: rebind time greater than lease "
+ "time, forcing to %u seconds",
+ iface->name, lease->rebindtime);
+ }
+ if (lease->renewaltime == 0)
+ lease->renewaltime = lease->leasetime * T1;
+ else if (lease->renewaltime > lease->rebindtime) {
+ lease->renewaltime = lease->leasetime * T1;
+ syslog(LOG_ERR,
+ "%s: renewal time greater than rebind "
+ "time, forcing to %u seconds",
+ iface->name, lease->renewaltime);
+ }
+ syslog(LOG_INFO,
+ "%s: leased %s for %u seconds", iface->name,
+ inet_ntoa(lease->addr), lease->leasetime);
+ }
+ }
+ if (options & DHCPCD_TEST) {
+ state->reason = "TEST";
+ script_run(iface);
+ exit(EXIT_SUCCESS);
+ }
+ if (state->reason == NULL) {
+ if (state->old) {
+ if (state->old->yiaddr == state->new->yiaddr &&
+ lease->server.s_addr)
+ state->reason = "RENEW";
+ else
+ state->reason = "REBIND";
+ } else if (state->state == DHS_REBOOT)
+ state->reason = "REBOOT";
+ else
+ state->reason = "BOUND";
+ }
+ if (lease->leasetime == ~0U)
+ lease->renewaltime = lease->rebindtime = lease->leasetime;
+ else {
+ eloop_timeout_add_sec(lease->renewaltime, dhcp_renew, iface);
+ eloop_timeout_add_sec(lease->rebindtime, dhcp_rebind, iface);
+ eloop_timeout_add_sec(lease->leasetime, dhcp_expire, iface);
+ syslog(LOG_DEBUG,
+ "%s: renew in %u seconds, rebind in %u seconds",
+ iface->name, lease->renewaltime, lease->rebindtime);
+ }
+ ifo->options &= ~ DHCPCD_CSR_WARNED;
+ ipv4_applyaddr(iface);
+ daemonise();
+ state->state = DHS_BOUND;
+ if (ifo->options & DHCPCD_ARP) {
+ state->claims = 0;
+ arp_announce(iface);
+ }
+}
+
+
+static void
+dhcp_timeout(void *arg)
+{
+ struct interface *iface = arg;
+
+ dhcp_bind(iface);
+ iface->state->interval = 0;
+ dhcp_discover(iface);
+}
+
+struct dhcp_message *
+dhcp_message_new(struct in_addr *addr, struct in_addr *mask)
+{
+ struct dhcp_message *dhcp;
+ uint8_t *p;
+
+ dhcp = xzalloc(sizeof(*dhcp));
+ dhcp->yiaddr = addr->s_addr;
+ p = dhcp->options;
+ if (mask && mask->s_addr != INADDR_ANY) {
+ *p++ = DHO_SUBNETMASK;
+ *p++ = sizeof(mask->s_addr);
+ memcpy(p, &mask->s_addr, sizeof(mask->s_addr));
+ p+= sizeof(mask->s_addr);
+ }
+ *p++ = DHO_END;
+ return dhcp;
+}
+
+static int
+handle_3rdparty(struct interface *iface)
+{
+ struct if_options *ifo;
+ struct in_addr addr, net, dst;
+
+ ifo = iface->state->options;
+ if (ifo->req_addr.s_addr != INADDR_ANY)
+ return 0;
+
+ if (get_address(iface->name, &addr, &net, &dst) == 1)
+ ipv4_handleifa(RTM_NEWADDR, iface->name, &addr, &net, &dst);
+ else {
+ syslog(LOG_INFO,
+ "%s: waiting for 3rd party to configure IP address",
+ iface->name);
+ iface->state->reason = "3RDPARTY";
+ script_run(iface);
+ }
+ return 1;
+}
+
+static void
+dhcp_static(struct interface *iface)
+{
+ struct if_options *ifo;
+
+ if (handle_3rdparty(iface))
+ return;
+ ifo = iface->state->options;
+ iface->state->offer =
+ dhcp_message_new(&ifo->req_addr, &ifo->req_mask);
+ eloop_timeout_delete(NULL, iface);
+ dhcp_bind(iface);
+}
+
+void
+dhcp_inform(struct interface *iface)
+{
+
+ if (handle_3rdparty(iface))
+ return;
+
+ if (options & DHCPCD_TEST) {
+ iface->addr.s_addr = iface->state->options->req_addr.s_addr;
+ iface->net.s_addr = iface->state->options->req_mask.s_addr;
+ } else {
+ iface->state->options->options |= DHCPCD_STATIC;
+ dhcp_static(iface);
+ }
+
+ iface->state->state = DHS_INFORM;
+ iface->state->xid = dhcp_xid(iface);
+ send_inform(iface);
+}
+
+static void
+dhcp_reboot(struct interface *iface)
+{
+ struct if_options *ifo = iface->state->options;
+
+ if (ifo->options & DHCPCD_LINK && iface->carrier == LINK_DOWN) {
+ syslog(LOG_INFO, "%s: waiting for carrier", iface->name);
+ return;
+ }
+ if (ifo->options & DHCPCD_STATIC) {
+ dhcp_static(iface);
+ return;
+ }
+ if (ifo->reboot == 0 || iface->state->offer == NULL) {
+ dhcp_discover(iface);
+ return;
+ }
+ if (ifo->options & DHCPCD_INFORM) {
+ syslog(LOG_INFO, "%s: informing address of %s",
+ iface->name, inet_ntoa(iface->state->lease.addr));
+ } else if (iface->state->offer->cookie == 0) {
+ if (ifo->options & DHCPCD_IPV4LL) {
+ iface->state->claims = 0;
+ arp_announce(iface);
+ } else
+ dhcp_discover(iface);
+ return;
+ } else {
+ syslog(LOG_INFO, "%s: rebinding lease of %s",
+ iface->name, inet_ntoa(iface->state->lease.addr));
+ }
+ iface->state->state = DHS_REBOOT;
+ iface->state->xid = dhcp_xid(iface);
+ iface->state->lease.server.s_addr = 0;
+ eloop_timeout_delete(NULL, iface);
+ if (ifo->fallback)
+ eloop_timeout_add_sec(ifo->reboot, dhcp_fallback, iface);
+ else if (ifo->options & DHCPCD_LASTLEASE &&
+ iface->state->lease.frominfo)
+ eloop_timeout_add_sec(ifo->reboot, dhcp_timeout, iface);
+ else if (!(ifo->options & DHCPCD_INFORM &&
+ options & (DHCPCD_MASTER | DHCPCD_DAEMONISED)))
+ eloop_timeout_add_sec(ifo->reboot, dhcp_expire, iface);
+ /* Don't bother ARP checking as the server could NAK us first. */
+ if (ifo->options & DHCPCD_INFORM)
+ dhcp_inform(iface);
+ else
+ dhcp_request(iface);
+}
+
+
+void
+dhcp_drop(struct interface *iface, const char *reason)
+{
+
+ eloop_timeouts_delete(iface, dhcp_expire, NULL);
+ free(iface->state->old);
+ iface->state->old = iface->state->new;
+ iface->state->new = NULL;
+ iface->state->reason = reason;
+ ipv4_applyaddr(iface);
+ free(iface->state->old);
+ iface->state->old = NULL;
+ iface->state->lease.addr.s_addr = 0;
+}
+
+static void
+log_dhcp(int lvl, const char *msg,
+ const struct interface *iface, const struct dhcp_message *dhcp,
+ const struct in_addr *from)
+{
+ const char *tfrom;
+ char *a;
+ struct in_addr addr;
+ int r;
+
+ if (strcmp(msg, "NAK:") == 0)
+ a = get_option_string(dhcp, DHO_MESSAGE);
+ else if (dhcp->yiaddr != 0) {
+ addr.s_addr = dhcp->yiaddr;
+ a = xstrdup(inet_ntoa(addr));
+ } else
+ a = NULL;
+
+ tfrom = "from";
+ r = get_option_addr(&addr, dhcp, DHO_SERVERID);
+ if (dhcp->servername[0] && r == 0)
+ syslog(lvl, "%s: %s %s %s %s `%s'", iface->name, msg, a,
+ tfrom, inet_ntoa(addr), dhcp->servername);
+ else {
+ if (r != 0) {
+ tfrom = "via";
+ addr = *from;
+ }
+ if (a == NULL)
+ syslog(lvl, "%s: %s %s %s",
+ iface->name, msg, tfrom, inet_ntoa(addr));
+ else
+ syslog(lvl, "%s: %s %s %s %s",
+ iface->name, msg, a, tfrom, inet_ntoa(addr));
+ }
+ free(a);
+}
+
+static int
+blacklisted_ip(const struct if_options *ifo, in_addr_t addr)
+{
+ size_t i;
+
+ for (i = 0; i < ifo->blacklist_len; i += 2)
+ if (ifo->blacklist[i] == (addr & ifo->blacklist[i + 1]))
+ return 1;
+ return 0;
+}
+
+static int
+whitelisted_ip(const struct if_options *ifo, in_addr_t addr)
+{
+ size_t i;
+
+ if (ifo->whitelist_len == 0)
+ return -1;
+ for (i = 0; i < ifo->whitelist_len; i += 2)
+ if (ifo->whitelist[i] == (addr & ifo->whitelist[i + 1]))
+ return 1;
+ return 0;
+}
+
+static void
+dhcp_handle(struct interface *iface, struct dhcp_message **dhcpp,
+ const struct in_addr *from)
+{
+ struct if_state *state = iface->state;
+ struct if_options *ifo = state->options;
+ struct dhcp_message *dhcp = *dhcpp;
+ struct dhcp_lease *lease = &state->lease;
+ uint8_t type, tmp;
+ struct in_addr addr;
+ size_t i;
+
+ /* reset the message counter */
+ state->interval = 0;
+
+ /* We may have found a BOOTP server */
+ if (get_option_uint8(&type, dhcp, DHO_MESSAGETYPE) == -1)
+ type = 0;
+
+ if (type == DHCP_NAK) {
+ /* For NAK, only check if we require the ServerID */
+ if (has_option_mask(ifo->requiremask, DHO_SERVERID) &&
+ get_option_addr(&addr, dhcp, DHO_SERVERID) == -1)
+ {
+ log_dhcp(LOG_WARNING, "reject NAK", iface, dhcp, from);
+ return;
+ }
+ /* We should restart on a NAK */
+ log_dhcp(LOG_WARNING, "NAK:", iface, dhcp, from);
+ if (!(options & DHCPCD_TEST)) {
+ dhcp_drop(iface, "NAK");
+ unlink(iface->leasefile);
+ }
+ dhcp_close(iface);
+ /* If we constantly get NAKS then we should slowly back off */
+ eloop_timeout_add_sec(state->nakoff, start_interface, iface);
+ if (state->nakoff == 0)
+ state->nakoff = 1;
+ else {
+ state->nakoff *= 2;
+ if (state->nakoff > NAKOFF_MAX)
+ state->nakoff = NAKOFF_MAX;
+ }
+ return;
+ }
+
+ /* Ensure that all required options are present */
+ for (i = 1; i < 255; i++) {
+ if (has_option_mask(ifo->requiremask, i) &&
+ get_option_uint8(&tmp, dhcp, i) != 0)
+ {
+ /* If we are bootp, then ignore the need for serverid.
+ * To ignore bootp, require dhcp_message_type instead. */
+ if (type == 0 && i == DHO_SERVERID)
+ continue;
+ log_dhcp(LOG_WARNING, "reject DHCP", iface, dhcp, from);
+ return;
+ }
+ }
+
+ /* Ensure that the address offered is valid */
+ if ((type == 0 || type == DHCP_OFFER || type == DHCP_ACK) &&
+ (dhcp->ciaddr == INADDR_ANY || dhcp->ciaddr == INADDR_BROADCAST) &&
+ (dhcp->yiaddr == INADDR_ANY || dhcp->yiaddr == INADDR_BROADCAST))
+ {
+ log_dhcp(LOG_WARNING, "reject invalid address",
+ iface, dhcp, from);
+ return;
+ }
+
+ /* No NAK, so reset the backoff */
+ state->nakoff = 0;
+
+ if ((type == 0 || type == DHCP_OFFER) &&
+ state->state == DHS_DISCOVER)
+ {
+ lease->frominfo = 0;
+ lease->addr.s_addr = dhcp->yiaddr;
+ lease->cookie = dhcp->cookie;
+ if (type == 0 ||
+ get_option_addr(&lease->server, dhcp, DHO_SERVERID) != 0)
+ lease->server.s_addr = INADDR_ANY;
+ log_dhcp(LOG_INFO, "offered", iface, dhcp, from);
+ free(state->offer);
+ state->offer = dhcp;
+ *dhcpp = NULL;
+ if (options & DHCPCD_TEST) {
+ free(state->old);
+ state->old = state->new;
+ state->new = state->offer;
+ state->offer = NULL;
+ state->reason = "TEST";
+ script_run(iface);
+ exit(EXIT_SUCCESS);
+ }
+ eloop_timeout_delete(send_discover, iface);
+ /* We don't request BOOTP addresses */
+ if (type) {
+ /* We used to ARP check here, but that seems to be in
+ * violation of RFC2131 where it only describes
+ * DECLINE after REQUEST.
+ * It also seems that some MS DHCP servers actually
+ * ignore DECLINE if no REQUEST, ie we decline a
+ * DISCOVER. */
+ dhcp_request(iface);
+ return;
+ }
+ }
+
+ if (type) {
+ if (type == DHCP_OFFER) {
+ log_dhcp(LOG_INFO, "ignoring offer of",
+ iface, dhcp, from);
+ return;
+ }
+
+ /* We should only be dealing with acks */
+ if (type != DHCP_ACK) {
+ log_dhcp(LOG_ERR, "not ACK or OFFER",
+ iface, dhcp, from);
+ return;
+ }
+
+ if (!(ifo->options & DHCPCD_INFORM))
+ log_dhcp(LOG_INFO, "acknowledged", iface, dhcp, from);
+ }
+
+ /* BOOTP could have already assigned this above, so check we still
+ * have a pointer. */
+ if (*dhcpp) {
+ free(state->offer);
+ state->offer = dhcp;
+ *dhcpp = NULL;
+ }
+
+ lease->frominfo = 0;
+ eloop_timeout_delete(NULL, iface);
+
+ /* We now have an offer, so close the DHCP sockets.
+ * This allows us to safely ARP when broken DHCP servers send an ACK
+ * follows by an invalid NAK. */
+ dhcp_close(iface);
+
+ if (ifo->options & DHCPCD_ARP &&
+ iface->addr.s_addr != state->offer->yiaddr)
+ {
+ /* If the interface already has the address configured
+ * then we can't ARP for duplicate detection. */
+ addr.s_addr = state->offer->yiaddr;
+ if (has_address(iface->name, &addr, NULL) != 1) {
+ state->claims = 0;
+ state->probes = 0;
+ state->conflicts = 0;
+ state->state = DHS_PROBE;
+ arp_probe(iface);
+ return;
+ }
+ }
+
+ dhcp_bind(iface);
+}
+
+static void
+dhcp_handlepacket(void *arg)
+{
+ struct interface *iface = arg;
+ struct dhcp_message *dhcp = NULL;
+ const uint8_t *pp;
+ ssize_t bytes;
+ struct in_addr from;
+ int i, partialcsum = 0;
+
+ /* We loop through until our buffer is empty.
+ * The benefit is that if we get >1 DHCP packet in our buffer and
+ * the first one fails for any reason, we can use the next. */
+ if (packet == NULL)
+ packet = xmalloc(udp_dhcp_len);
+ for(;;) {
+ bytes = get_raw_packet(iface, ETHERTYPE_IP,
+ packet, udp_dhcp_len, &partialcsum);
+ if (bytes == 0 || bytes == -1)
+ break;
+ if (valid_udp_packet(packet, bytes, &from, partialcsum) == -1) {
+ syslog(LOG_ERR, "%s: invalid UDP packet from %s",
+ iface->name, inet_ntoa(from));
+ continue;
+ }
+ i = whitelisted_ip(iface->state->options, from.s_addr);
+ if (i == 0) {
+ syslog(LOG_WARNING,
+ "%s: non whitelisted DHCP packet from %s",
+ iface->name, inet_ntoa(from));
+ continue;
+ } else if (i != 1 &&
+ blacklisted_ip(iface->state->options, from.s_addr) == 1)
+ {
+ syslog(LOG_WARNING,
+ "%s: blacklisted DHCP packet from %s",
+ iface->name, inet_ntoa(from));
+ continue;
+ }
+ if (iface->flags & IFF_POINTOPOINT &&
+ iface->dst.s_addr != from.s_addr)
+ {
+ syslog(LOG_WARNING,
+ "%s: server %s is not destination",
+ iface->name, inet_ntoa(from));
+ }
+ bytes = get_udp_data(&pp, packet);
+ if ((size_t)bytes > sizeof(*dhcp)) {
+ syslog(LOG_ERR,
+ "%s: packet greater than DHCP size from %s",
+ iface->name, inet_ntoa(from));
+ continue;
+ }
+ if (!dhcp)
+ dhcp = xzalloc(sizeof(*dhcp));
+ memcpy(dhcp, pp, bytes);
+ if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
+ syslog(LOG_DEBUG, "%s: bogus cookie from %s",
+ iface->name, inet_ntoa(from));
+ continue;
+ }
+ /* Ensure it's the right transaction */
+ if (iface->state->xid != ntohl(dhcp->xid)) {
+ syslog(LOG_DEBUG,
+ "%s: wrong xid 0x%x (expecting 0x%x) from %s",
+ iface->name, ntohl(dhcp->xid), iface->state->xid,
+ inet_ntoa(from));
+ continue;
+ }
+ /* Ensure packet is for us */
+ if (iface->hwlen <= sizeof(dhcp->chaddr) &&
+ memcmp(dhcp->chaddr, iface->hwaddr, iface->hwlen))
+ {
+ syslog(LOG_DEBUG, "%s: xid 0x%x is not for hwaddr %s",
+ iface->name, ntohl(dhcp->xid),
+ hwaddr_ntoa(dhcp->chaddr, sizeof(dhcp->chaddr)));
+ continue;
+ }
+ dhcp_handle(iface, &dhcp, &from);
+ if (iface->raw_fd == -1)
+ break;
+ }
+ free(packet);
+ packet = NULL;
+ free(dhcp);
+}
+
+static int
+dhcp_open(struct interface *ifp)
+{
+ int r = 0;
+
+ if (ifp->raw_fd == -1) {
+ if ((r = open_socket(ifp, ETHERTYPE_IP)) == -1)
+ syslog(LOG_ERR, "%s: %s: %m", __func__, ifp->name);
+ else
+ eloop_event_add(ifp->raw_fd, dhcp_handlepacket, ifp);
+ }
+ if (ifp->udp_fd == -1 &&
+ ifp->addr.s_addr != 0 &&
+ ifp->state->new != NULL &&
+ (ifp->state->new->cookie == htonl(MAGIC_COOKIE) ||
+ ifp->state->options->options & DHCPCD_INFORM))
+ {
+ if (open_udp_socket(ifp) == -1 && errno != EADDRINUSE) {
+ syslog(LOG_ERR, "%s: open_udp_socket: %m", ifp->name);
+ r = -1;
+ }
+ }
+ return r;
+}
+
+void
+dhcp_start(struct interface *ifp)
+{
+ struct if_options *ifo = ifp->state->options;
+ struct stat st;
+ struct timeval now;
+ uint32_t l;
+ int nolease;
+
+ if (!(ifo->options & DHCPCD_IPV4))
+ return;
+
+ if (ifo->options & DHCPCD_STATIC) {
+ dhcp_static(ifp);
+ return;
+ }
+
+ if (!dhcp_open(ifp))
+ return;
+
+ if (ifo->options & DHCPCD_INFORM) {
+ dhcp_inform(ifp);
+ return;
+ }
+ if (ifp->hwlen == 0 && ifo->clientid[0] == '\0') {
+ syslog(LOG_WARNING, "%s: needs a clientid to configure",
+ ifp->name);
+ dhcp_drop(ifp, "FAIL");
+ dhcp_close(ifp);
+ eloop_timeout_delete(NULL, ifp);
+ return;
+ }
+ /* We don't want to read the old lease if we NAK an old test */
+ nolease = ifp->state->offer && options & DHCPCD_TEST;
+ if (!nolease)
+ ifp->state->offer = read_lease(ifp);
+ if (ifp->state->offer) {
+ get_lease(&ifp->state->lease, ifp->state->offer);
+ ifp->state->lease.frominfo = 1;
+ if (ifp->state->offer->cookie == 0) {
+ if (ifp->state->offer->yiaddr ==
+ ifp->addr.s_addr)
+ {
+ free(ifp->state->offer);
+ ifp->state->offer = NULL;
+ }
+ } else if (ifp->state->lease.leasetime != ~0U &&
+ stat(ifp->leasefile, &st) == 0)
+ {
+ /* Offset lease times and check expiry */
+ gettimeofday(&now, NULL);
+ if ((time_t)ifp->state->lease.leasetime <
+ now.tv_sec - st.st_mtime)
+ {
+ syslog(LOG_DEBUG,
+ "%s: discarding expired lease",
+ ifp->name);
+ free(ifp->state->offer);
+ ifp->state->offer = NULL;
+ ifp->state->lease.addr.s_addr = 0;
+ } else {
+ l = now.tv_sec - st.st_mtime;
+ ifp->state->lease.leasetime -= l;
+ ifp->state->lease.renewaltime -= l;
+ ifp->state->lease.rebindtime -= l;
+ }
+ }
+ }
+ if (ifp->state->offer == NULL)
+ dhcp_discover(ifp);
+ else if (ifp->state->offer->cookie == 0 &&
+ ifp->state->options->options & DHCPCD_IPV4LL)
+ ipv4ll_start(ifp);
+ else
+ dhcp_reboot(ifp);
+}
+
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
#include <stdint.h>
#include "common.h"
-
-/* Max MTU - defines dhcp option length */
-#define MTU_MAX 1500
-#define MTU_MIN 576
+#include "dhcp-common.h"
/* UDP port numbers for DHCP */
#define DHCP_SERVER_PORT 67
DHO_END = 255
};
-#define REQUEST (1 << 0)
-#define UINT8 (1 << 1)
-#define UINT16 (1 << 2)
-#define SINT16 (1 << 3)
-#define UINT32 (1 << 4)
-#define SINT32 (1 << 5)
-#define IPV4 (1 << 6)
-#define STRING (1 << 7)
-#define PAIR (1 << 8)
-#define ARRAY (1 << 9)
-#define RFC3361 (1 << 10)
-#define RFC3397 (1 << 11)
-#define RFC3442 (1 << 12)
-#define RFC5969 (1 << 13)
-#define IPV6 (1 << 14)
-#define BINHEX (1 << 15)
-#define SCODE (1 << 16)
-
-#define IPV4R IPV4 | REQUEST
-
/* FQDN values - lsnybble used in flags
* hsnybble to create order
* and to allow 0x00 to mean disable
#include "if-options.h"
#include "net.h"
-struct dhcp_opt {
- uint16_t option;
- int type;
- const char *var;
-};
-
extern const struct dhcp_opt const dhcp_opts[];
-#define add_option_mask(var, val) (var[val >> 3] |= 1 << (val & 7))
-#define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
-#define has_option_mask(var, val) (var[val >> 3] & (1 << (val & 7)))
-int make_option_mask(const struct dhcp_opt *,uint8_t *, const char *, int);
+char *decode_rfc3361(int dl, const uint8_t *data);
+ssize_t decode_rfc3442(char *out, ssize_t len, int pl, const uint8_t *p);
+ssize_t decode_rfc5969(char *out, ssize_t len, int pl, const uint8_t *p);
+
void print_options(void);
char *get_option_string(const struct dhcp_message *, uint8_t);
int get_option_addr(struct in_addr *, const struct dhcp_message *, uint8_t);
!IN_LINKLOCAL(htonl((m)->yiaddr)) && \
get_option_uint8(NULL, m, DHO_MESSAGETYPE) == -1)
struct rt *get_option_routes(struct interface *, const struct dhcp_message *);
-ssize_t decode_rfc3397(char *, ssize_t, int, const uint8_t *);
-ssize_t print_string(char *, ssize_t, int, const uint8_t *);
-ssize_t print_option(char *, ssize_t, int, int, const uint8_t *, const char *);
ssize_t configure_env(char **, const char *, const struct dhcp_message *,
const struct interface *);
+uint32_t dhcp_xid(const struct interface *);
+struct dhcp_message *dhcp_message_new(struct in_addr *addr,
+ struct in_addr *mask);
int dhcp_message_add_addr(struct dhcp_message *, uint8_t, struct in_addr);
ssize_t make_message(struct dhcp_message **, const struct interface *,
uint8_t);
struct dhcp_message *read_lease(const struct interface *);
void get_lease(struct dhcp_lease *, const struct dhcp_message *);
+void dhcp_drop(struct interface *, const char *);
+void dhcp_start(struct interface *);
+void dhcp_stop(struct interface *);
+void dhcp_decline(struct interface *);
+void dhcp_discover(void *);
+void dhcp_inform(struct interface *);
+void dhcp_release(struct interface *);
+void dhcp_bind(void *);
+void dhcp_close(struct interface *);
+
#endif
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
#define ELOOP_QUEUE 2
-#include "bind.h"
#include "config.h"
#include "common.h"
-#include "configure.h"
#include "dhcp.h"
#include "dhcp6.h"
#include "duid.h"
#include "eloop.h"
#include "ipv6rs.h"
#include "platform.h"
+#include "script.h"
#ifndef __UNCONST
#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
{ 0, NULL }
};
+#define IPV6A ADDRIPV6 | ARRAY
const struct dhcp_opt const dhcp6_opts[] = {
{ D6_OPTION_CLIENTID, BINHEX, "client_id" },
{ D6_OPTION_SERVERID, BINHEX, "server_id" },
- { D6_OPTION_IA_ADDR, IPV6 | ARRAY, "ia_addr" },
+ { D6_OPTION_IA_ADDR, IPV6A, "ia_addr" },
{ D6_OPTION_PREFERENCE, UINT8, "preference" },
{ D6_OPTION_RAPID_COMMIT, 0, "rapid_commit" },
- { D6_OPTION_UNICAST, IPV6, "unicast" },
+ { D6_OPTION_UNICAST, ADDRIPV6, "unicast" },
{ D6_OPTION_STATUS_CODE, SCODE, "status_code" },
{ D6_OPTION_SIP_SERVERS_NAME, RFC3397, "sip_servers_names" },
- { D6_OPTION_SIP_SERVERS_ADDRESS,IPV6 | ARRAY, "sip_servers_addresses" },
- { D6_OPTION_DNS_SERVERS, IPV6 | ARRAY, "name_servers" },
+ { D6_OPTION_SIP_SERVERS_ADDRESS,IPV6A, "sip_servers_addresses" },
+ { D6_OPTION_DNS_SERVERS, IPV6A, "name_servers" },
{ D6_OPTION_DOMAIN_LIST, RFC3397, "domain_search" },
- { D6_OPTION_NIS_SERVERS, IPV6 | ARRAY, "nis_servers" },
- { D6_OPTION_NISP_SERVERS, IPV6 | ARRAY, "nisp_servers" },
+ { D6_OPTION_NIS_SERVERS, IPV6A, "nis_servers" },
+ { D6_OPTION_NISP_SERVERS, IPV6A, "nisp_servers" },
{ D6_OPTION_NIS_DOMAIN_NAME, RFC3397, "nis_domain_name" },
{ D6_OPTION_NISP_DOMAIN_NAME, RFC3397, "nisp_domain_name" },
- { D6_OPTION_SNTP_SERVERS, IPV6 | ARRAY, "sntp_servers" },
+ { D6_OPTION_SNTP_SERVERS, IPV6A, "sntp_servers" },
{ D6_OPTION_INFO_REFRESH_TIME, UINT32, "info_refresh_time" },
{ D6_OPTION_BCMS_SERVER_D, RFC3397, "bcms_server_d" },
- { D6_OPTION_BCMS_SERVER_A, IPV6 | ARRAY, "bcms_server_a" },
+ { D6_OPTION_BCMS_SERVER_A, IPV6A, "bcms_server_a" },
{ 0, 0, NULL }
};
syslog(LOG_ERR, "%s: DHCPv6 lease expired", ifp->name);
dhcp6_freedrop_addrs(ifp, 1);
- run_script_reason(ifp, "EXPIRE6");
+ script_runreason(ifp, "EXPIRE6");
state = D6_CSTATE(ifp);
unlink(state->leasefile);
dhcp6_startdiscover(ifp);
dhcp6_writelease(ifp);
}
- run_script_reason(ifp, options & DHCPCD_TEST ? "TEST" : reason);
+ script_runreason(ifp, options & DHCPCD_TEST ? "TEST" : reason);
if (options & DHCPCD_TEST ||
(ifp->state->options->options & DHCPCD_INFORM &&
!(options & DHCPCD_MASTER)))
if (drop && state->new) {
if (reason == NULL)
reason = "STOP6";
- run_script_reason(ifp, reason);
+ script_runreason(ifp, reason);
}
free(state->send);
free(state->recv);
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* SUCH DAMAGE.
*/
-const char copyright[] = "Copyright (c) 2006-2012 Roy Marples";
+const char copyright[] = "Copyright (c) 2006-2013 Roy Marples";
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/utsname.h>
-#include <arpa/inet.h>
-#include <net/route.h>
-
-#ifdef __linux__
-# include <asm/types.h> /* for systems with broken headers */
-# include <linux/rtnetlink.h>
-#endif
-
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <time.h>
#include "arp.h"
-#include "bind.h"
#include "config.h"
#include "common.h"
-#include "configure.h"
#include "control.h"
#include "dhcpcd.h"
#include "dhcp6.h"
#include "eloop.h"
#include "if-options.h"
#include "if-pref.h"
+#include "ipv4.h"
#include "ipv4ll.h"
#include "ipv6.h"
#include "ipv6ns.h"
#include "ipv6rs.h"
#include "net.h"
#include "platform.h"
+#include "script.h"
#include "signals.h"
/* We should define a maximum for the NAK exponential backoff */
static struct if_options *if_options;
static char *pidfile;
static int linkfd = -1, ipv6rsfd = -1, ipv6nsfd = -1;
-static uint8_t *packet;
static char **ifv;
static int ifc;
static char **margv;
static int margc;
-struct dhcp_op {
- uint8_t value;
- const char *name;
-};
-
-static const struct dhcp_op dhcp_ops[] = {
- { DHCP_DISCOVER, "DISCOVER" },
- { DHCP_OFFER, "OFFER" },
- { DHCP_REQUEST, "REQUEST" },
- { DHCP_DECLINE, "DECLINE" },
- { DHCP_ACK, "ACK" },
- { DHCP_NAK, "NAK" },
- { DHCP_RELEASE, "RELEASE" },
- { DHCP_INFORM, "INFORM" },
- { 0, NULL }
-};
-
-static void send_release(struct interface *);
-
-static const char *
-get_dhcp_op(uint8_t type)
-{
- const struct dhcp_op *d;
-
- for (d = dhcp_ops; d->name; d++)
- if (d->value == type)
- return d->name;
- return NULL;
-}
-
static pid_t
read_pid(void)
{
}
/* ARGSUSED */
-void
+static void
handle_exit_timeout(_unused void *arg)
{
int timeout;
eloop_timeout_add_sec(timeout, handle_exit_timeout, NULL);
}
-void
-drop_dhcp(struct interface *iface, const char *reason)
+pid_t
+daemonise(void)
{
- free(iface->state->old);
- iface->state->old = iface->state->new;
- iface->state->new = NULL;
- iface->state->reason = reason;
- configure(iface);
- free(iface->state->old);
- iface->state->old = NULL;
- iface->state->lease.addr.s_addr = 0;
+#ifdef THERE_IS_NO_FORK
+ return -1;
+#else
+ pid_t pid;
+ char buf = '\0';
+ int sidpipe[2], fd;
+
+ eloop_timeout_delete(handle_exit_timeout, NULL);
+ if (options & DHCPCD_DAEMONISED || !(options & DHCPCD_DAEMONISE))
+ return 0;
+ /* Setup a signal pipe so parent knows when to exit. */
+ if (pipe(sidpipe) == -1) {
+ syslog(LOG_ERR, "pipe: %m");
+ return -1;
+ }
+ syslog(LOG_DEBUG, "forking to background");
+ switch (pid = fork()) {
+ case -1:
+ syslog(LOG_ERR, "fork: %m");
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+ case 0:
+ setsid();
+ /* Notify parent it's safe to exit as we've detached. */
+ close(sidpipe[0]);
+ if (write(sidpipe[1], &buf, 1) == -1)
+ syslog(LOG_ERR, "failed to notify parent: %m");
+ close(sidpipe[1]);
+ if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ }
+ break;
+ default:
+ /* Wait for child to detach */
+ close(sidpipe[1]);
+ if (read(sidpipe[0], &buf, 1) == -1)
+ syslog(LOG_ERR, "failed to read child: %m");
+ close(sidpipe[0]);
+ break;
+ }
+ /* Done with the fd now */
+ if (pid != 0) {
+ syslog(LOG_INFO, "forked to background, child pid %d",pid);
+ writepid(pidfd, pid);
+ close(pidfd);
+ pidfd = -1;
+ options |= DHCPCD_FORKED;
+ exit(EXIT_SUCCESS);
+ }
+ options |= DHCPCD_DAEMONISED;
+ return pid;
+#endif
}
struct interface *
dhcp6_drop(iface, NULL);
ipv6rs_drop(iface);
- if (strcmp(iface->state->reason, "RELEASE") != 0)
- drop_dhcp(iface, "STOP");
- close_sockets(iface);
+// if (strcmp(iface->state->reason, "RELEASE") != 0)
+ dhcp_drop(iface, "STOP");
+ dhcp_close(iface);
eloop_timeout_delete(NULL, iface);
free_interface(ifp);
if (!(options & (DHCPCD_MASTER | DHCPCD_TEST)))
exit(EXIT_FAILURE);
}
-static uint32_t
-dhcp_xid(struct interface *iface)
-{
- uint32_t xid;
-
- if (iface->state->options->options & DHCPCD_XID_HWADDR &&
- iface->hwlen >= sizeof(xid))
- /* The lower bits are probably more unique on the network */
- memcpy(&xid, (iface->hwaddr + iface->hwlen) - sizeof(xid),
- sizeof(xid));
- else
- xid = arc4random();
-
- return xid;
-}
-
-static void
-send_message(struct interface *iface, int type,
- void (*callback)(void *))
-{
- struct if_state *state = iface->state;
- struct if_options *ifo = state->options;
- struct dhcp_message *dhcp;
- uint8_t *udp;
- ssize_t len, r;
- struct in_addr from, to;
- in_addr_t a = 0;
- struct timeval tv;
-
- if (!callback)
- syslog(LOG_DEBUG, "%s: sending %s with xid 0x%x",
- iface->name, get_dhcp_op(type), state->xid);
- else {
- if (state->interval == 0)
- state->interval = 4;
- else {
- state->interval *= 2;
- if (state->interval > 64)
- state->interval = 64;
- }
- tv.tv_sec = state->interval + DHCP_RAND_MIN;
- tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
- timernorm(&tv);
- syslog(LOG_DEBUG,
- "%s: sending %s (xid 0x%x), next in %0.2f seconds",
- iface->name, get_dhcp_op(type), state->xid,
- timeval_to_double(&tv));
- }
-
- /* Ensure sockets are open. */
- if (open_sockets(iface) == -1) {
- if (!(options & DHCPCD_TEST))
- drop_dhcp(iface, "FAIL");
- return;
- }
-
- /* If we couldn't open a UDP port for our IP address
- * then we cannot renew.
- * This could happen if our IP was pulled out from underneath us.
- * Also, we should not unicast from a BOOTP lease. */
- if (iface->udp_fd == -1 ||
- (!(ifo->options & DHCPCD_INFORM) && is_bootp(iface->state->new)))
- {
- a = iface->addr.s_addr;
- iface->addr.s_addr = 0;
- }
- len = make_message(&dhcp, iface, type);
- if (a)
- iface->addr.s_addr = a;
- from.s_addr = dhcp->ciaddr;
- if (from.s_addr)
- to.s_addr = state->lease.server.s_addr;
- else
- to.s_addr = 0;
- if (to.s_addr && to.s_addr != INADDR_BROADCAST) {
- r = send_packet(iface, to, (uint8_t *)dhcp, len);
- if (r == -1) {
- syslog(LOG_ERR, "%s: send_packet: %m", iface->name);
- close_sockets(iface);
- }
- } else {
- len = make_udp_packet(&udp, (uint8_t *)dhcp, len, from, to);
- r = send_raw_packet(iface, ETHERTYPE_IP, udp, len);
- free(udp);
- /* If we failed to send a raw packet this normally means
- * we don't have the ability to work beneath the IP layer
- * for this interface.
- * As such we remove it from consideration without actually
- * stopping the interface. */
- if (r == -1) {
- syslog(LOG_ERR, "%s: send_raw_packet: %m", iface->name);
- if (!(options & DHCPCD_TEST))
- drop_dhcp(iface, "FAIL");
- close_sockets(iface);
- eloop_timeout_delete(NULL, iface);
- callback = NULL;
- }
- }
- free(dhcp);
-
- /* Even if we fail to send a packet we should continue as we are
- * as our failure timeouts will change out codepath when needed. */
- if (callback)
- eloop_timeout_add_tv(&tv, callback, iface);
-}
-
-static void
-send_inform(void *arg)
-{
- send_message((struct interface *)arg, DHCP_INFORM, send_inform);
-}
-
-static void
-send_discover(void *arg)
-{
- send_message((struct interface *)arg, DHCP_DISCOVER, send_discover);
-}
-
-static void
-send_request(void *arg)
-{
- send_message((struct interface *)arg, DHCP_REQUEST, send_request);
-}
-
-static void
-send_renew(void *arg)
-{
- send_message((struct interface *)arg, DHCP_REQUEST, send_renew);
-}
-
-static void
-send_rebind(void *arg)
-{
- send_message((struct interface *)arg, DHCP_REQUEST, send_rebind);
-}
-
-void
-start_expire(void *arg)
-{
- struct interface *iface = arg;
-
- iface->state->interval = 0;
- if (iface->addr.s_addr == 0) {
- /* We failed to reboot, so enter discovery. */
- iface->state->lease.addr.s_addr = 0;
- start_discover(iface);
- return;
- }
-
- syslog(LOG_ERR, "%s: lease expired", iface->name);
- eloop_timeout_delete(NULL, iface);
- drop_dhcp(iface, "EXPIRE");
- unlink(iface->leasefile);
- if (iface->carrier != LINK_DOWN)
- start_interface(iface);
-}
-
-static void
-log_dhcp(int lvl, const char *msg,
- const struct interface *iface, const struct dhcp_message *dhcp,
- const struct in_addr *from)
-{
- const char *tfrom;
- char *a;
- struct in_addr addr;
- int r;
-
- if (strcmp(msg, "NAK:") == 0)
- a = get_option_string(dhcp, DHO_MESSAGE);
- else if (dhcp->yiaddr != 0) {
- addr.s_addr = dhcp->yiaddr;
- a = xstrdup(inet_ntoa(addr));
- } else
- a = NULL;
-
- tfrom = "from";
- r = get_option_addr(&addr, dhcp, DHO_SERVERID);
- if (dhcp->servername[0] && r == 0)
- syslog(lvl, "%s: %s %s %s %s `%s'", iface->name, msg, a,
- tfrom, inet_ntoa(addr), dhcp->servername);
- else {
- if (r != 0) {
- tfrom = "via";
- addr = *from;
- }
- if (a == NULL)
- syslog(lvl, "%s: %s %s %s",
- iface->name, msg, tfrom, inet_ntoa(addr));
- else
- syslog(lvl, "%s: %s %s %s %s",
- iface->name, msg, a, tfrom, inet_ntoa(addr));
- }
- free(a);
-}
-
-static int
-blacklisted_ip(const struct if_options *ifo, in_addr_t addr)
-{
- size_t i;
-
- for (i = 0; i < ifo->blacklist_len; i += 2)
- if (ifo->blacklist[i] == (addr & ifo->blacklist[i + 1]))
- return 1;
- return 0;
-}
-
-static int
-whitelisted_ip(const struct if_options *ifo, in_addr_t addr)
-{
- size_t i;
-
- if (ifo->whitelist_len == 0)
- return -1;
- for (i = 0; i < ifo->whitelist_len; i += 2)
- if (ifo->whitelist[i] == (addr & ifo->whitelist[i + 1]))
- return 1;
- return 0;
-}
-
-static void
-handle_dhcp(struct interface *iface, struct dhcp_message **dhcpp, const struct in_addr *from)
-{
- struct if_state *state = iface->state;
- struct if_options *ifo = state->options;
- struct dhcp_message *dhcp = *dhcpp;
- struct dhcp_lease *lease = &state->lease;
- uint8_t type, tmp;
- struct in_addr addr;
- size_t i;
-
- /* reset the message counter */
- state->interval = 0;
-
- /* We may have found a BOOTP server */
- if (get_option_uint8(&type, dhcp, DHO_MESSAGETYPE) == -1)
- type = 0;
-
- if (type == DHCP_NAK) {
- /* For NAK, only check if we require the ServerID */
- if (has_option_mask(ifo->requiremask, DHO_SERVERID) &&
- get_option_addr(&addr, dhcp, DHO_SERVERID) == -1)
- {
- log_dhcp(LOG_WARNING, "reject NAK", iface, dhcp, from);
- return;
- }
- /* We should restart on a NAK */
- log_dhcp(LOG_WARNING, "NAK:", iface, dhcp, from);
- if (!(options & DHCPCD_TEST)) {
- drop_dhcp(iface, "NAK");
- unlink(iface->leasefile);
- }
- close_sockets(iface);
- /* If we constantly get NAKS then we should slowly back off */
- eloop_timeout_add_sec(state->nakoff, start_interface, iface);
- if (state->nakoff == 0)
- state->nakoff = 1;
- else {
- state->nakoff *= 2;
- if (state->nakoff > NAKOFF_MAX)
- state->nakoff = NAKOFF_MAX;
- }
- return;
- }
-
- /* Ensure that all required options are present */
- for (i = 1; i < 255; i++) {
- if (has_option_mask(ifo->requiremask, i) &&
- get_option_uint8(&tmp, dhcp, i) != 0)
- {
- /* If we are bootp, then ignore the need for serverid.
- * To ignore bootp, require dhcp_message_type instead. */
- if (type == 0 && i == DHO_SERVERID)
- continue;
- log_dhcp(LOG_WARNING, "reject DHCP", iface, dhcp, from);
- return;
- }
- }
-
- /* Ensure that the address offered is valid */
- if ((type == 0 || type == DHCP_OFFER || type == DHCP_ACK) &&
- (dhcp->ciaddr == INADDR_ANY || dhcp->ciaddr == INADDR_BROADCAST) &&
- (dhcp->yiaddr == INADDR_ANY || dhcp->yiaddr == INADDR_BROADCAST))
- {
- log_dhcp(LOG_WARNING, "reject invalid address",
- iface, dhcp, from);
- return;
- }
-
- /* No NAK, so reset the backoff */
- state->nakoff = 0;
-
- if ((type == 0 || type == DHCP_OFFER) &&
- state->state == DHS_DISCOVER)
- {
- lease->frominfo = 0;
- lease->addr.s_addr = dhcp->yiaddr;
- lease->cookie = dhcp->cookie;
- if (type == 0 ||
- get_option_addr(&lease->server, dhcp, DHO_SERVERID) != 0)
- lease->server.s_addr = INADDR_ANY;
- log_dhcp(LOG_INFO, "offered", iface, dhcp, from);
- free(state->offer);
- state->offer = dhcp;
- *dhcpp = NULL;
- if (options & DHCPCD_TEST) {
- free(state->old);
- state->old = state->new;
- state->new = state->offer;
- state->offer = NULL;
- state->reason = "TEST";
- run_script(iface);
- exit(EXIT_SUCCESS);
- }
- eloop_timeout_delete(send_discover, iface);
- /* We don't request BOOTP addresses */
- if (type) {
- /* We used to ARP check here, but that seems to be in
- * violation of RFC2131 where it only describes
- * DECLINE after REQUEST.
- * It also seems that some MS DHCP servers actually
- * ignore DECLINE if no REQUEST, ie we decline a
- * DISCOVER. */
- start_request(iface);
- return;
- }
- }
-
- if (type) {
- if (type == DHCP_OFFER) {
- log_dhcp(LOG_INFO, "ignoring offer of",
- iface, dhcp, from);
- return;
- }
-
- /* We should only be dealing with acks */
- if (type != DHCP_ACK) {
- log_dhcp(LOG_ERR, "not ACK or OFFER",
- iface, dhcp, from);
- return;
- }
-
- if (!(ifo->options & DHCPCD_INFORM))
- log_dhcp(LOG_INFO, "acknowledged", iface, dhcp, from);
- }
-
- /* BOOTP could have already assigned this above, so check we still
- * have a pointer. */
- if (*dhcpp) {
- free(state->offer);
- state->offer = dhcp;
- *dhcpp = NULL;
- }
-
- lease->frominfo = 0;
- eloop_timeout_delete(NULL, iface);
-
- /* We now have an offer, so close the DHCP sockets.
- * This allows us to safely ARP when broken DHCP servers send an ACK
- * follows by an invalid NAK. */
- close_sockets(iface);
-
- if (ifo->options & DHCPCD_ARP &&
- iface->addr.s_addr != state->offer->yiaddr)
- {
- /* If the interface already has the address configured
- * then we can't ARP for duplicate detection. */
- addr.s_addr = state->offer->yiaddr;
- if (has_address(iface->name, &addr, NULL) != 1) {
- state->claims = 0;
- state->probes = 0;
- state->conflicts = 0;
- state->state = DHS_PROBE;
- send_arp_probe(iface);
- return;
- }
- }
-
- bind_interface(iface);
-}
-
-static void
-handle_dhcp_packet(void *arg)
-{
- struct interface *iface = arg;
- struct dhcp_message *dhcp = NULL;
- const uint8_t *pp;
- ssize_t bytes;
- struct in_addr from;
- int i, partialcsum = 0;
-
- /* We loop through until our buffer is empty.
- * The benefit is that if we get >1 DHCP packet in our buffer and
- * the first one fails for any reason, we can use the next. */
- if (packet == NULL)
- packet = xmalloc(udp_dhcp_len);
- for(;;) {
- bytes = get_raw_packet(iface, ETHERTYPE_IP,
- packet, udp_dhcp_len, &partialcsum);
- if (bytes == 0 || bytes == -1)
- break;
- if (valid_udp_packet(packet, bytes, &from, partialcsum) == -1) {
- syslog(LOG_ERR, "%s: invalid UDP packet from %s",
- iface->name, inet_ntoa(from));
- continue;
- }
- i = whitelisted_ip(iface->state->options, from.s_addr);
- if (i == 0) {
- syslog(LOG_WARNING,
- "%s: non whitelisted DHCP packet from %s",
- iface->name, inet_ntoa(from));
- continue;
- } else if (i != 1 &&
- blacklisted_ip(iface->state->options, from.s_addr) == 1)
- {
- syslog(LOG_WARNING,
- "%s: blacklisted DHCP packet from %s",
- iface->name, inet_ntoa(from));
- continue;
- }
- if (iface->flags & IFF_POINTOPOINT &&
- iface->dst.s_addr != from.s_addr)
- {
- syslog(LOG_WARNING,
- "%s: server %s is not destination",
- iface->name, inet_ntoa(from));
- }
- bytes = get_udp_data(&pp, packet);
- if ((size_t)bytes > sizeof(*dhcp)) {
- syslog(LOG_ERR,
- "%s: packet greater than DHCP size from %s",
- iface->name, inet_ntoa(from));
- continue;
- }
- if (!dhcp)
- dhcp = xzalloc(sizeof(*dhcp));
- memcpy(dhcp, pp, bytes);
- if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
- syslog(LOG_DEBUG, "%s: bogus cookie from %s",
- iface->name, inet_ntoa(from));
- continue;
- }
- /* Ensure it's the right transaction */
- if (iface->state->xid != ntohl(dhcp->xid)) {
- syslog(LOG_DEBUG,
- "%s: wrong xid 0x%x (expecting 0x%x) from %s",
- iface->name, ntohl(dhcp->xid), iface->state->xid,
- inet_ntoa(from));
- continue;
- }
- /* Ensure packet is for us */
- if (iface->hwlen <= sizeof(dhcp->chaddr) &&
- memcmp(dhcp->chaddr, iface->hwaddr, iface->hwlen))
- {
- syslog(LOG_DEBUG, "%s: xid 0x%x is not for hwaddr %s",
- iface->name, ntohl(dhcp->xid),
- hwaddr_ntoa(dhcp->chaddr, sizeof(dhcp->chaddr)));
- continue;
- }
- handle_dhcp(iface, &dhcp, &from);
- if (iface->raw_fd == -1)
- break;
- }
- free(packet);
- packet = NULL;
- free(dhcp);
-}
-
-static void
-send_release(struct interface *iface)
-{
- struct timespec ts;
-
- if (iface->state->new != NULL &&
- iface->state->new->cookie == htonl(MAGIC_COOKIE))
- {
- syslog(LOG_INFO, "%s: releasing lease of %s",
- iface->name, inet_ntoa(iface->state->lease.addr));
- iface->state->xid = dhcp_xid(iface);
- send_message(iface, DHCP_RELEASE, NULL);
- /* Give the packet a chance to go before dropping the ip */
- ts.tv_sec = RELEASE_DELAY_S;
- ts.tv_nsec = RELEASE_DELAY_NS;
- nanosleep(&ts, NULL);
- drop_dhcp(iface, "RELEASE");
- }
- unlink(iface->leasefile);
-}
-
-void
-send_decline(struct interface *iface)
-{
- send_message(iface, DHCP_DECLINE, NULL);
-}
-
static void
configure_interface1(struct interface *iface)
{
return ret;
}
-static void
-start_fallback(void *arg)
-{
- struct interface *iface;
-
- iface = (struct interface *)arg;
- select_profile(iface, iface->state->options->fallback);
- start_interface(iface);
-}
-
static void
configure_interface(struct interface *iface, int argc, char **argv)
{
if (iface->carrier != LINK_DOWN) {
iface->carrier = LINK_DOWN;
syslog(LOG_INFO, "%s: carrier lost", iface->name);
- close_sockets(iface);
- eloop_timeouts_delete(iface, start_expire, NULL);
+ dhcp_close(iface);
dhcp6_drop(iface, "EXPIRE6");
ipv6rs_drop(iface);
- drop_dhcp(iface, "NOCARRIER");
+ dhcp_drop(iface, "NOCARRIER");
}
} else if (carrier == 1 && !(~iface->flags & IFF_UP)) {
if (iface->carrier != LINK_UP) {
configure_interface(iface, margc, margv);
iface->state->interval = 0;
iface->state->reason = "CARRIER";
- run_script(iface);
+ script_run(iface);
start_interface(iface);
}
}
}
-void
-start_discover(void *arg)
-{
- struct interface *iface = arg;
- struct if_options *ifo = iface->state->options;
- int timeout = ifo->timeout;
-
- /* If we're rebooting and we're not daemonised then we need
- * to shorten the normal timeout to ensure we try correctly
- * for a fallback or IPv4LL address. */
- if (iface->state->state == DHS_REBOOT &&
- !(options & DHCPCD_DAEMONISED))
- {
- timeout -= ifo->reboot;
- if (timeout <= 0)
- timeout = 2;
- }
-
- iface->state->state = DHS_DISCOVER;
- iface->state->xid = dhcp_xid(iface);
- eloop_timeout_delete(NULL, iface);
- if (ifo->fallback)
- eloop_timeout_add_sec(timeout, start_fallback, iface);
- else if (ifo->options & DHCPCD_IPV4LL &&
- !IN_LINKLOCAL(htonl(iface->addr.s_addr)))
- {
- if (IN_LINKLOCAL(htonl(iface->state->fail.s_addr)))
- eloop_timeout_add_sec(RATE_LIMIT_INTERVAL,
- ipv4ll_start, iface);
- else
- eloop_timeout_add_sec(timeout, ipv4ll_start, iface);
- }
- if (ifo->options & DHCPCD_REQUEST)
- syslog(LOG_INFO, "%s: broadcasting for a lease (requesting %s)",
- iface->name, inet_ntoa(ifo->req_addr));
- else
- syslog(LOG_INFO, "%s: broadcasting for a lease", iface->name);
- send_discover(iface);
-}
-
-void
-start_request(void *arg)
-{
- struct interface *iface = arg;
-
- iface->state->state = DHS_REQUEST;
- send_request(iface);
-}
-
-void
-start_renew(void *arg)
-{
- struct interface *iface = arg;
- struct dhcp_lease *lease = &iface->state->lease;
-
- syslog(LOG_INFO, "%s: renewing lease of %s",
- iface->name, inet_ntoa(lease->addr));
- syslog(LOG_DEBUG, "%s: rebind in %u seconds, expire in %u seconds",
- iface->name, lease->rebindtime - lease->renewaltime,
- lease->leasetime - lease->renewaltime);
- iface->state->state = DHS_RENEW;
- iface->state->xid = dhcp_xid(iface);
- send_renew(iface);
-}
-
-void
-start_rebind(void *arg)
-{
- struct interface *iface = arg;
- struct dhcp_lease *lease = &iface->state->lease;
-
- syslog(LOG_ERR, "%s: failed to renew, attempting to rebind",
- iface->name);
- syslog(LOG_DEBUG, "%s: expire in %u seconds",
- iface->name, lease->leasetime - lease->rebindtime);
- iface->state->state = DHS_REBIND;
- eloop_timeout_delete(send_renew, iface);
- iface->state->lease.server.s_addr = 0;
- send_rebind(iface);
-}
-
-static void
-start_timeout(void *arg)
-{
- struct interface *iface = arg;
-
- bind_interface(iface);
- iface->state->interval = 0;
- start_discover(iface);
-}
-
-static struct dhcp_message *
-dhcp_message_new(struct in_addr *addr, struct in_addr *mask)
-{
- struct dhcp_message *dhcp;
- uint8_t *p;
-
- dhcp = xzalloc(sizeof(*dhcp));
- dhcp->yiaddr = addr->s_addr;
- p = dhcp->options;
- if (mask && mask->s_addr != INADDR_ANY) {
- *p++ = DHO_SUBNETMASK;
- *p++ = sizeof(mask->s_addr);
- memcpy(p, &mask->s_addr, sizeof(mask->s_addr));
- p+= sizeof(mask->s_addr);
- }
- *p++ = DHO_END;
- return dhcp;
-}
-
-static int
-handle_3rdparty(struct interface *iface)
-{
- struct if_options *ifo;
- struct in_addr addr, net, dst;
-
- ifo = iface->state->options;
- if (ifo->req_addr.s_addr != INADDR_ANY)
- return 0;
-
- if (get_address(iface->name, &addr, &net, &dst) == 1)
- handle_ifa(RTM_NEWADDR, iface->name, &addr, &net, &dst);
- else {
- syslog(LOG_INFO,
- "%s: waiting for 3rd party to configure IP address",
- iface->name);
- iface->state->reason = "3RDPARTY";
- run_script(iface);
- }
- return 1;
-}
-
-static void
-start_static(struct interface *iface)
-{
- struct if_options *ifo;
-
- if (handle_3rdparty(iface))
- return;
- ifo = iface->state->options;
- iface->state->offer =
- dhcp_message_new(&ifo->req_addr, &ifo->req_mask);
- eloop_timeout_delete(NULL, iface);
- bind_interface(iface);
-}
-
-static void
-start_inform(struct interface *iface)
-{
- if (handle_3rdparty(iface))
- return;
-
- if (options & DHCPCD_TEST) {
- iface->addr.s_addr = iface->state->options->req_addr.s_addr;
- iface->net.s_addr = iface->state->options->req_mask.s_addr;
- } else {
- iface->state->options->options |= DHCPCD_STATIC;
- start_static(iface);
- }
-
- iface->state->state = DHS_INFORM;
- iface->state->xid = dhcp_xid(iface);
- send_inform(iface);
-}
-
-void
-start_reboot(struct interface *iface)
-{
- struct if_options *ifo = iface->state->options;
-
- if (ifo->options & DHCPCD_LINK && iface->carrier == LINK_DOWN) {
- syslog(LOG_INFO, "%s: waiting for carrier", iface->name);
- return;
- }
- if (ifo->options & DHCPCD_STATIC) {
- start_static(iface);
- return;
- }
- if (ifo->reboot == 0 || iface->state->offer == NULL) {
- start_discover(iface);
- return;
- }
- if (ifo->options & DHCPCD_INFORM) {
- syslog(LOG_INFO, "%s: informing address of %s",
- iface->name, inet_ntoa(iface->state->lease.addr));
- } else if (iface->state->offer->cookie == 0) {
- if (ifo->options & DHCPCD_IPV4LL) {
- iface->state->claims = 0;
- send_arp_announce(iface);
- } else
- start_discover(iface);
- return;
- } else {
- syslog(LOG_INFO, "%s: rebinding lease of %s",
- iface->name, inet_ntoa(iface->state->lease.addr));
- }
- iface->state->state = DHS_REBOOT;
- iface->state->xid = dhcp_xid(iface);
- iface->state->lease.server.s_addr = 0;
- eloop_timeout_delete(NULL, iface);
- if (ifo->fallback)
- eloop_timeout_add_sec(ifo->reboot, start_fallback, iface);
- else if (ifo->options & DHCPCD_LASTLEASE &&
- iface->state->lease.frominfo)
- eloop_timeout_add_sec(ifo->reboot, start_timeout, iface);
- else if (!(ifo->options & DHCPCD_INFORM &&
- options & (DHCPCD_MASTER | DHCPCD_DAEMONISED)))
- eloop_timeout_add_sec(ifo->reboot, start_expire, iface);
- /* Don't bother ARP checking as the server could NAK us first. */
- if (ifo->options & DHCPCD_INFORM)
- send_inform(iface);
- else
- send_request(iface);
-}
-
void
start_interface(void *arg)
{
struct interface *iface = arg;
struct if_options *ifo = iface->state->options;
- struct stat st;
- struct timeval now;
- uint32_t l;
int nolease;
handle_carrier(0, 0, iface->name);
ipv6rs_start(iface);
if (iface->state->arping_index < ifo->arping_len) {
- start_arping(iface);
- return;
- }
- if (ifo->options & DHCPCD_STATIC) {
- start_static(iface);
+ arp_start(iface);
return;
}
syslog(LOG_ERR, "%s: dhcp6_start: %m", iface->name);
}
- if (!(ifo->options & DHCPCD_IPV4))
- return;
-
- if (ifo->options & DHCPCD_INFORM) {
- start_inform(iface);
- return;
- }
- if (iface->hwlen == 0 && ifo->clientid[0] == '\0') {
- syslog(LOG_WARNING, "%s: needs a clientid to configure",
- iface->name);
- drop_dhcp(iface, "FAIL");
- close_sockets(iface);
- eloop_timeout_delete(NULL, iface);
- return;
- }
- /* We don't want to read the old lease if we NAK an old test */
- nolease = iface->state->offer && options & DHCPCD_TEST;
- if (!nolease)
- iface->state->offer = read_lease(iface);
- if (iface->state->offer) {
- get_lease(&iface->state->lease, iface->state->offer);
- iface->state->lease.frominfo = 1;
- if (iface->state->offer->cookie == 0) {
- if (iface->state->offer->yiaddr ==
- iface->addr.s_addr)
- {
- free(iface->state->offer);
- iface->state->offer = NULL;
- }
- } else if (iface->state->lease.leasetime != ~0U &&
- stat(iface->leasefile, &st) == 0)
- {
- /* Offset lease times and check expiry */
- gettimeofday(&now, NULL);
- if ((time_t)iface->state->lease.leasetime <
- now.tv_sec - st.st_mtime)
- {
- syslog(LOG_DEBUG,
- "%s: discarding expired lease",
- iface->name);
- free(iface->state->offer);
- iface->state->offer = NULL;
- iface->state->lease.addr.s_addr = 0;
- } else {
- l = now.tv_sec - st.st_mtime;
- iface->state->lease.leasetime -= l;
- iface->state->lease.renewaltime -= l;
- iface->state->lease.rebindtime -= l;
- }
- }
- }
- if (iface->state->offer == NULL)
- start_discover(iface);
- else if (iface->state->offer->cookie == 0 &&
- iface->state->options->options & DHCPCD_IPV4LL)
- ipv4ll_start(iface);
- else
- start_reboot(iface);
+ if (ifo->options & DHCPCD_IPV4)
+ dhcp_start(iface);
}
static void
ifs->nakoff = 0;
configure_interface(iface, argc, argv);
if (!(options & DHCPCD_TEST))
- run_script(iface);
+ script_run(iface);
/* We need to drop the leasefile so that start_interface
* doesn't load it. */
if (ifs->options->options & DHCPCD_REQUEST)
return;
}
if (!(options & DHCPCD_TEST))
- run_script(iface);
+ script_run(iface);
} else
iface->carrier = LINK_UNKNOWN;
}
}
#endif
-void
-handle_ifa(int type, const char *ifname,
- struct in_addr *addr, struct in_addr *net, struct in_addr *dst)
-{
- struct interface *ifp;
- struct if_options *ifo;
- int i;
-
- if (addr->s_addr == INADDR_ANY)
- return;
- for (ifp = ifaces; ifp; ifp = ifp->next)
- if (strcmp(ifp->name, ifname) == 0)
- break;
- if (ifp == NULL)
- return;
-
- if (type == RTM_DELADDR) {
- if (ifp->state->new &&
- ifp->state->new->yiaddr == addr->s_addr)
- syslog(LOG_INFO, "%s: removing IP address %s/%d",
- ifp->name, inet_ntoa(ifp->state->lease.addr),
- inet_ntocidr(ifp->state->lease.net));
- return;
- }
-
- if (type != RTM_NEWADDR)
- return;
-
- ifo = ifp->state->options;
- if ((ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)) == 0 ||
- ifo->req_addr.s_addr != INADDR_ANY)
- return;
-
- free(ifp->state->old);
- ifp->state->old = ifp->state->new;
- ifp->state->new = dhcp_message_new(addr, net);
- ifp->dst.s_addr = dst ? dst->s_addr : INADDR_ANY;
- if (dst) {
- for (i = 1; i < 255; i++)
- if (i != DHO_ROUTER && has_option_mask(ifo->dstmask,i))
- dhcp_message_add_addr(ifp->state->new, i, *dst);
- }
- ifp->state->reason = "STATIC";
- build_routes();
- run_script(ifp);
- if (ifo->options & DHCPCD_INFORM) {
- ifp->state->state = DHS_INFORM;
- ifp->state->xid = dhcp_xid(ifp);
- ifp->state->lease.server.s_addr =
- dst ? dst->s_addr : INADDR_ANY;
- ifp->addr = *addr;
- ifp->net = *net;
- send_inform(ifp);
- }
-}
-
/* ARGSUSED */
static void
handle_link(_unused void *arg)
{
+
if (manage_link(linkfd) == -1)
syslog(LOG_ERR, "manage_link: %m");
}
(opt & (DHCPCD_INFORM | DHCPCD_STATIC) &&
!(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))))
{
- drop_dhcp(iface, "EXPIRE");
+ dhcp_drop(iface, "EXPIRE");
} else {
free(iface->state->offer);
iface->state->offer = NULL;
if (action)
if_reboot(ifn, argc, argv);
else if (ifn->state->new)
- configure(ifn);
+ ipv4_applyaddr(ifn);
free_interface(ifp);
} else {
ifp->next = NULL;
syslog(LOG_INFO, "received SIGUSR, reconfiguring");
for (ifp = ifaces; ifp; ifp = ifp->next)
if (ifp->state->new)
- configure(ifp);
+ ipv4_applyaddr(ifp);
return;
case SIGPIPE:
syslog(LOG_WARNING, "received SIGPIPE");
if (ifp->carrier != LINK_DOWN &&
(do_release ||
ifp->state->options->options & DHCPCD_RELEASE))
- send_release(ifp);
+ dhcp_release(ifp);
stop_interface(ifp);
}
exit(EXIT_FAILURE);
ifp->state->options->options |= DHCPCD_RELEASE;
if (ifp->state->options->options & DHCPCD_RELEASE &&
ifp->carrier != LINK_DOWN)
- send_release(ifp);
+ dhcp_release(ifp);
stop_interface(ifp);
}
return 0;
return 0;
}
-int
-open_sockets(struct interface *iface)
-{
- int r = 0;
-
- if (iface->raw_fd == -1) {
- if ((r = open_socket(iface, ETHERTYPE_IP)) == -1)
- syslog(LOG_ERR, "%s: open_socket: %m", iface->name);
- else
- eloop_event_add(iface->raw_fd, handle_dhcp_packet, iface);
- }
- if (iface->udp_fd == -1 &&
- iface->addr.s_addr != 0 &&
- iface->state->new != NULL &&
- (iface->state->new->cookie == htonl(MAGIC_COOKIE) ||
- iface->state->options->options & DHCPCD_INFORM))
- {
- if (open_udp_socket(iface) == -1 && errno != EADDRINUSE) {
- syslog(LOG_ERR, "%s: open_udp_socket: %m", iface->name);
- r = -1;
- }
- }
- return r;
-}
-
-void
-close_sockets(struct interface *iface)
-{
- if (iface->arp_fd != -1) {
- eloop_event_delete(iface->arp_fd);
- close(iface->arp_fd);
- iface->arp_fd = -1;
- }
- if (iface->raw_fd != -1) {
- eloop_event_delete(iface->raw_fd);
- close(iface->raw_fd);
- iface->raw_fd = -1;
- }
- if (iface->udp_fd != -1) {
- /* we don't listen to events on the udp */
- close(iface->udp_fd);
- iface->udp_fd = -1;
- }
-}
-
int
main(int argc, char **argv)
{
exit(EXIT_FAILURE);
}
iface->state->reason = "DUMP";
- run_script(iface);
+ script_run(iface);
exit(EXIT_SUCCESS);
}
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
extern char **ifdv;
extern struct interface *ifaces;
+pid_t daemonise(void);
struct interface *find_interface(const char *);
int handle_args(struct fd_list *, int, char **);
void handle_carrier(int, int, const char *);
void handle_interface(int, const char *);
void handle_hwaddr(const char *, unsigned char *, size_t);
-void handle_ifa(int, const char *,
- struct in_addr *, struct in_addr *, struct in_addr *);
-void handle_exit_timeout(void *);
void handle_signal(int);
-void start_interface(void *);
-void start_discover(void *);
-void start_request(void *);
-void start_renew(void *);
-void start_rebind(void *);
-void start_reboot(struct interface *);
-void start_expire(void *);
-void send_decline(struct interface *);
-int open_sockets(struct interface *);
-void close_sockets(struct interface *);
-void drop_dhcp(struct interface *, const char *);
void drop_interface(struct interface *, const char *);
int select_profile(struct interface *, const char *);
+void start_interface(void *);
+
#endif
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2011 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
#include "config.h"
#include "common.h"
-#include "configure.h"
#include "dhcp.h"
+#include "ipv4.h"
#include "ipv6.h"
#include "net.h"
if (rt.iface != NULL) {
if (metric == rt.iface->metric) {
inet_cidrtoaddr(rtm->rtm_dst_len, &rt.net);
- route_deleted(&rt);
+ ipv4_routedeleted(&rt);
}
}
return 1;
}
rta = RTA_NEXT(rta, len);
}
- handle_ifa(nlm->nlmsg_type, ifn, &addr, &net, &dest);
+ ipv4_handleifa(nlm->nlmsg_type, ifn, &addr, &net, &dest);
return 1;
}
* SUCH DAMAGE.
*/
-#include <sys/stat.h>
-#include <sys/uio.h>
-#include <sys/wait.h>
-
#include <netinet/in.h>
#include <arpa/inet.h>
+#ifdef __linux__
+# include <asm/types.h> /* for systems with broken headers */
+# include <linux/rtnetlink.h>
+#endif
+
#include <ctype.h>
#include <errno.h>
-#include <signal.h>
-/* We can't include spawn.h here because it may not exist.
- * config.h will pull it in, or our compat one. */
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include "config.h"
#include "common.h"
-#include "configure.h"
#include "dhcp.h"
-#include "dhcp6.h"
#include "if-options.h"
#include "if-pref.h"
-#include "ipv6rs.h"
+#include "ipv4.h"
#include "net.h"
-#include "signals.h"
-
-#define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
+#include "script.h"
static struct rt *routes;
-static const char *if_params[] = {
- "interface",
- "reason",
- "pid",
- "ifmetric",
- "ifwireless",
- "ifflags",
- "ssid",
- "profile",
- "interface_order",
- NULL
-};
-
-void
-if_printoptions(void)
-{
- const char **p;
-
- for (p = if_params; *p; p++)
- printf(" - %s\n", *p);
-}
-
-static int
-exec_script(char *const *argv, char *const *env)
-{
- pid_t pid;
- posix_spawnattr_t attr;
- short flags;
- sigset_t defsigs;
- int i;
-
- /* posix_spawn is a safe way of executing another image
- * and changing signals back to how they should be. */
- if (posix_spawnattr_init(&attr) == -1)
- return -1;
- flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
- posix_spawnattr_setflags(&attr, flags);
- sigemptyset(&defsigs);
- for (i = 0; i < handle_sigs[i]; i++)
- sigaddset(&defsigs, handle_sigs[i]);
- posix_spawnattr_setsigdefault(&attr, &defsigs);
- posix_spawnattr_setsigmask(&attr, &dhcpcd_sigset);
- errno = 0;
- i = posix_spawn(&pid, argv[0], NULL, &attr, argv, env);
- if (i) {
- errno = i;
- return -1;
- }
- return pid;
-}
-
-static char *
-make_var(const char *prefix, const char *var)
-{
- size_t len;
- char *v;
-
- len = strlen(prefix) + strlen(var) + 2;
- v = xmalloc(len);
- snprintf(v, len, "%s_%s", prefix, var);
- return v;
-}
-
-
-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, const char *reason, char ***argv)
-{
- char **env, *p;
- ssize_t e, elen, l;
- const struct if_options *ifo = iface->state->options;
- const struct interface *ifp;
- int dhcp, dhcp6, ra;
- const struct dhcp6_state *d6_state;
-
- dhcp = dhcp6 = ra = 0;
- d6_state = D6_STATE(iface);
- if (strcmp(reason, "TEST") == 0) {
- if (d6_state && d6_state->new)
- dhcp6 = 1;
- else if (ipv6rs_has_ra(iface))
- ra = 1;
- else
- dhcp = 1;
- } else if (reason[strlen(reason) - 1] == '6')
- dhcp6 = 1;
- else if (strcmp(reason, "ROUTERADVERT") == 0)
- ra = 1;
- else
- dhcp = 1;
-
- /* When dumping the lease, we only want to report interface and
- reason - the other interface variables are meaningless */
- if (options & DHCPCD_DUMPLEASE)
- elen = 2;
- else
- elen = 10;
-
- /* Make our env */
- env = xmalloc(sizeof(char *) * (elen + 1));
- e = strlen("interface") + strlen(iface->name) + 2;
- env[0] = xmalloc(e);
- snprintf(env[0], e, "interface=%s", iface->name);
- e = strlen("reason") + strlen(reason) + 2;
- env[1] = xmalloc(e);
- snprintf(env[1], e, "reason=%s", reason);
- if (options & DHCPCD_DUMPLEASE)
- goto dumplease;
-
- e = 20;
- env[2] = xmalloc(e);
- snprintf(env[2], e, "pid=%d", getpid());
- env[3] = xmalloc(e);
- snprintf(env[3], e, "ifmetric=%d", iface->metric);
- env[4] = xmalloc(e);
- 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 (strcmp(reason, "TEST") == 0) {
- env[8] = strdup("if_up=false");
- env[9] = strdup("if_down=false");
- } else if ((dhcp && iface->state->new) ||
- (dhcp6 && d6_state->new) ||
- (ra && ipv6rs_has_ra(iface)))
- {
- env[8] = strdup("if_up=true");
- env[9] = strdup("if_down=false");
- } else {
- env[8] = strdup("if_up=false");
- env[9] = strdup("if_down=true");
- }
- 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 (dhcp && iface->state->old) {
- e = configure_env(NULL, NULL, iface->state->old, iface);
- if (e > 0) {
- env = xrealloc(env, sizeof(char *) * (elen + e + 1));
- elen += configure_env(env + elen, "old",
- iface->state->old, iface);
- }
- append_config(&env, &elen, "old",
- (const char *const *)ifo->config);
- }
- if (dhcp6 && d6_state->old) {
- e = dhcp6_env(NULL, NULL, iface,
- d6_state->old, d6_state->old_len);
- if (e > 0) {
- env = xrealloc(env, sizeof(char *) * (elen + e + 1));
- elen += dhcp6_env(env + elen, "old", iface,
- d6_state->old, d6_state->old_len);
- }
- }
-
-dumplease:
- if (dhcp && iface->state->new) {
- e = configure_env(NULL, NULL, iface->state->new, iface);
- if (e > 0) {
- env = xrealloc(env, sizeof(char *) * (elen + e + 1));
- elen += configure_env(env + elen, "new",
- iface->state->new, iface);
- }
- append_config(&env, &elen, "new",
- (const char *const *)ifo->config);
- }
- if (dhcp6 && d6_state->new) {
- e = dhcp6_env(NULL, NULL, iface,
- d6_state->new, d6_state->new_len);
- if (e > 0) {
- env = xrealloc(env, sizeof(char *) * (elen + e + 1));
- elen += dhcp6_env(env + elen, "new", iface,
- d6_state->new, d6_state->new_len);
- }
- }
- if (ra) {
- e = ipv6rs_env(NULL, NULL, iface);
- if (e > 0) {
- env = xrealloc(env, sizeof(char *) * (elen + e + 1));
- elen += ipv6rs_env(env + elen, NULL, iface);
- }
- }
-
- /* Add our base environment */
- if (ifo->environ) {
- e = 0;
- while (ifo->environ[e++])
- ;
- env = xrealloc(env, sizeof(char *) * (elen + e + 1));
- e = 0;
- while (ifo->environ[e]) {
- env[elen + e] = xstrdup(ifo->environ[e]);
- e++;
- }
- elen += e;
- }
- env[elen] = '\0';
-
- *argv = env;
- return elen;
-}
-
-static int
-send_interface1(int fd, const struct interface *iface, const char *reason)
-{
- char **env, **ep, *s;
- ssize_t elen;
- struct iovec iov[2];
- int retval;
-
- make_env(iface, reason, &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
-send_interface(int fd, const struct interface *iface)
-{
- int retval = 0;
- if (send_interface1(fd, iface, iface->state->reason) == -1)
- retval = -1;
- if (ipv6rs_has_ra(iface)) {
- if (send_interface1(fd, iface, "ROUTERADVERT") == -1)
- retval = -1;
- }
- if (D6_STATE_RUNNING(iface)) {
- if (send_interface1(fd, iface, "INFORM6") == -1)
- retval = -1;
- }
- return retval;
-}
-
-int
-run_script_reason(const struct interface *iface, const char *reason)
-{
- 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];
-
- if (iface->state->options->script == NULL ||
- iface->state->options->script[0] == '\0' ||
- strcmp(iface->state->options->script, "/dev/null") == 0)
- return 0;
-
- if (reason == NULL)
- reason = iface->state->reason;
- syslog(LOG_DEBUG, "%s: executing `%s', reason %s",
- iface->name, argv[0], reason);
-
- /* Make our env */
- elen = make_env(iface, reason, &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)
- syslog(LOG_ERR, "exec_script: %s: %m", argv[0]);
- else if (pid != 0) {
- /* Wait for the script to finish */
- while (waitpid(pid, &status, 0) == -1) {
- if (errno != EINTR) {
- syslog(LOG_ERR, "waitpid: %m");
- status = 0;
- break;
- }
- }
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status))
- syslog(LOG_ERR,
- "exec_script: %s: WEXITSTATUS %d",
- argv[0], WEXITSTATUS(status));
- } else if (WIFSIGNALED(status))
- syslog(LOG_ERR, "exec_sript: %s: %s",
- argv[0], strsignal(WTERMSIG(status)));
- }
-
- /* Send to our listeners */
- bigenv = NULL;
- for (fd = control_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)
- free(*ep++);
- free(env);
- return WEXITSTATUS(status);
-}
-
static struct rt *
find_route(struct rt *rts, const struct rt *r, struct rt **lrt,
const struct rt *srt)
/* If something other than dhcpcd removes a route,
* we need to remove it from our internal table. */
int
-route_deleted(const struct rt *rt)
+ipv4_routedeleted(const struct rt *rt)
{
struct rt *f, *l;
}
static struct rt *
-get_routes(struct interface *iface)
+get_routes(struct interface *ifp)
{
struct rt *rt, *nrt = NULL, *r = NULL;
- if (iface->state->options->routes != NULL) {
- for (rt = iface->state->options->routes;
+ if (ifp->state->options->routes != NULL) {
+ for (rt = ifp->state->options->routes;
rt != NULL;
rt = rt->next)
{
return nrt;
}
- return get_option_routes(iface, iface->state->new);
+ return get_option_routes(ifp, ifp->state->new);
}
/* Some DHCP servers add set host routes by setting the gateway
}
void
-build_routes(void)
+ipv4_buildroutes(void)
{
struct rt *nrs = NULL, *dnr, *or, *rt, *rtn, *rtl, *lrt = NULL;
struct interface *ifp;
return retval;
}
-int
-configure(struct interface *iface)
+void
+ipv4_applyaddr(void *arg)
{
+ struct interface *iface = arg;
struct dhcp_message *dhcp = iface->state->new;
struct dhcp_lease *lease = &iface->state->lease;
struct if_options *ifo = iface->state->options;
if (dhcp == NULL) {
if (!(ifo->options & DHCPCD_PERSISTENT)) {
- build_routes();
+ ipv4_buildroutes();
if (iface->addr.s_addr != 0)
delete_address(iface);
- run_script(iface);
+ script_run(iface);
}
- return 0;
+ return;
}
/* This also changes netmask */
&lease->addr, &lease->net, &lease->brd) == -1 &&
errno != EEXIST)
{
- syslog(LOG_ERR, "add_address: %m");
- return -1;
+ syslog(LOG_ERR, "%s: add_address: %m", __func__);
+ return;
}
}
free(rt);
}
- build_routes();
+ ipv4_buildroutes();
if (!iface->state->lease.frominfo &&
!(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
if (write_lease(iface, dhcp) == -1)
- syslog(LOG_ERR, "write_lease: %m");
- run_script(iface);
- return 0;
+ syslog(LOG_ERR, "%s: write_lease: %m", __func__);
+ script_run(iface);
+}
+
+void
+ipv4_handleifa(int type, const char *ifname,
+ struct in_addr *addr, struct in_addr *net, struct in_addr *dst)
+{
+ struct interface *ifp;
+ struct if_options *ifo;
+ int i;
+
+ if (addr->s_addr == INADDR_ANY)
+ return;
+ for (ifp = ifaces; ifp; ifp = ifp->next)
+ if (strcmp(ifp->name, ifname) == 0)
+ break;
+ if (ifp == NULL)
+ return;
+
+ if (type == RTM_DELADDR) {
+ if (ifp->state->new &&
+ ifp->state->new->yiaddr == addr->s_addr)
+ syslog(LOG_INFO, "%s: removing IP address %s/%d",
+ ifp->name, inet_ntoa(ifp->state->lease.addr),
+ inet_ntocidr(ifp->state->lease.net));
+ return;
+ }
+
+ if (type != RTM_NEWADDR)
+ return;
+
+ ifo = ifp->state->options;
+ if ((ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)) == 0 ||
+ ifo->req_addr.s_addr != INADDR_ANY)
+ return;
+
+ free(ifp->state->old);
+ ifp->state->old = ifp->state->new;
+ ifp->state->new = dhcp_message_new(addr, net);
+ ifp->dst.s_addr = dst ? dst->s_addr : INADDR_ANY;
+ if (dst) {
+ for (i = 1; i < 255; i++)
+ if (i != DHO_ROUTER && has_option_mask(ifo->dstmask,i))
+ dhcp_message_add_addr(ifp->state->new, i, *dst);
+ }
+ ifp->state->reason = "STATIC";
+ ipv4_buildroutes();
+ script_run(ifp);
+ if (ifo->options & DHCPCD_INFORM) {
+ ifp->state->state = DHS_INFORM;
+ ifp->state->xid = dhcp_xid(ifp);
+ ifp->state->lease.server.s_addr =
+ dst ? dst->s_addr : INADDR_ANY;
+ ifp->addr = *addr;
+ ifp->net = *net;
+ dhcp_inform(ifp);
+ }
}
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* SUCH DAMAGE.
*/
-#ifndef BIND_H
-#define BIND_H
+#ifndef IPV4_H
+#define IPV4_H
-#include "config.h"
-#ifdef THERE_IS_NO_FORK
-# define daemonise() {}
-#else
-pid_t daemonise(void);
-#endif
+#include "net.h"
+
+void ipv4_buildroutes(void);
+void ipv4_applyaddr(void *);
+int ipv4_routedeleted(const struct rt *);
-void bind_interface(void *);
+void ipv4_handleifa(int, const char *,
+ struct in_addr *, struct in_addr *, struct in_addr *);
#endif
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
if (ifp->addr.s_addr) {
ifp->state->conflicts = 0;
if (IN_LINKLOCAL(htonl(ifp->addr.s_addr))) {
- send_arp_announce(ifp);
+ arp_announce(ifp);
return;
}
}
else
ifp->state->offer = ipv4ll_make_lease(addr);
ifp->state->lease.frominfo = 0;
- send_arp_probe(ifp);
+ arp_probe(ifp);
}
void
syslog(LOG_DEBUG,
"%s: IPv4LL %d second defence failed",
ifp->name, DEFEND_INTERVAL);
- drop_dhcp(ifp, "EXPIRE");
+ dhcp_drop(ifp, "EXPIRE");
ifp->state->conflicts = -1;
} else {
syslog(LOG_DEBUG, "%s: defended IPv4LL address",
}
}
- close_sockets(ifp);
+ dhcp_close(ifp);
free(ifp->state->offer);
ifp->state->offer = NULL;
eloop_timeout_delete(NULL, ifp);
syslog(LOG_ERR, "%s: failed to acquire an IPv4LL address",
ifp->name);
ifp->state->interval = RATE_LIMIT_INTERVAL / 2;
- start_discover(ifp);
+ dhcp_discover(ifp);
} else {
eloop_timeout_add_sec(PROBE_WAIT, ipv4ll_start, ifp);
}
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
#include <syslog.h>
#include "common.h"
-#include "configure.h"
#include "dhcpcd.h"
#include "dhcp6.h"
#include "ipv6.h"
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
#define ELOOP_QUEUE 1
#include "common.h"
-#include "configure.h"
#include "dhcpcd.h"
#include "eloop.h"
#include "ipv6.h"
#include "ipv6ns.h"
+#include "script.h"
#define MIN_RANDOM_FACTOR (500 * 1000) /* milliseconds in usecs */
#define MAX_RANDOM_FACTOR (1500 * 1000) /* milliseconds in usecs */
rap->iface->name, rap->sfrom);
rap->expired = 1;
ipv6_buildroutes();
- run_script_reason(rap->iface, "ROUTERADVERT"); /* XXX not RA */
+ script_runreason(rap->iface, "ROUTERADVERT"); /* XXX not RA */
}
void
ifp->name, sfrom);
rap->expired = 1;
ipv6_buildroutes();
- run_script_reason(ifp, "ROUTERADVERT");
+ script_runreason(ifp, "ROUTERADVERT");
return;
}
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
#include <syslog.h>
#define ELOOP_QUEUE 1
-#include "bind.h"
#include "common.h"
-#include "configure.h"
#include "dhcpcd.h"
#include "dhcp6.h"
#include "eloop.h"
#include "ipv6.h"
#include "ipv6ns.h"
#include "ipv6rs.h"
+#include "script.h"
/* Debugging Router Solicitations is a lot of spam, so disable it */
//#define DEBUG_RS
if (options & DHCPCD_IPV6RA_OWN && !(options & DHCPCD_TEST))
ipv6_addaddrs(ifp, &rap->addrs);
if (options & DHCPCD_TEST) {
- run_script_reason(ifp, "TEST");
+ script_runreason(ifp, "TEST");
goto handle_flag;
}
ipv6_buildroutes();
/* We will get run by the expire function */
if (rap->lifetime)
- run_script_reason(ifp, "ROUTERADVERT");
+ script_runreason(ifp, "ROUTERADVERT");
/* If we don't require RDNSS then set has_dns = 1 so we fork */
if (!(ifp->state->options->options & DHCPCD_IPV6RA_REQRDNSS))
eloop_timeout_add_tv(&next, ipv6rs_expire, ifp);
if (expired) {
ipv6_buildroutes();
- run_script_reason(ifp, "ROUTERADVERT");
+ script_runreason(ifp, "ROUTERADVERT");
}
}
ipv6rs_drop_ra(rap);
}
}
- run_script_reason(ifp, "ROUTERADVERT");
+ script_runreason(ifp, "ROUTERADVERT");
}
}
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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 <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+/* We can't include spawn.h here because it may not exist.
+ * config.h will pull it in, or our compat one. */
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+#include "dhcp6.h"
+#include "if-options.h"
+#include "if-pref.h"
+#include "ipv6rs.h"
+#include "net.h"
+#include "script.h"
+#include "signals.h"
+
+#define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
+
+static const char *if_params[] = {
+ "interface",
+ "reason",
+ "pid",
+ "ifmetric",
+ "ifwireless",
+ "ifflags",
+ "ssid",
+ "profile",
+ "interface_order",
+ NULL
+};
+
+void
+if_printoptions(void)
+{
+ const char **p;
+
+ for (p = if_params; *p; p++)
+ printf(" - %s\n", *p);
+}
+
+static int
+exec_script(char *const *argv, char *const *env)
+{
+ pid_t pid;
+ posix_spawnattr_t attr;
+ short flags;
+ sigset_t defsigs;
+ int i;
+
+ /* posix_spawn is a safe way of executing another image
+ * and changing signals back to how they should be. */
+ if (posix_spawnattr_init(&attr) == -1)
+ return -1;
+ flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
+ posix_spawnattr_setflags(&attr, flags);
+ sigemptyset(&defsigs);
+ for (i = 0; i < handle_sigs[i]; i++)
+ sigaddset(&defsigs, handle_sigs[i]);
+ posix_spawnattr_setsigdefault(&attr, &defsigs);
+ posix_spawnattr_setsigmask(&attr, &dhcpcd_sigset);
+ errno = 0;
+ i = posix_spawn(&pid, argv[0], NULL, &attr, argv, env);
+ if (i) {
+ errno = i;
+ return -1;
+ }
+ return pid;
+}
+
+static char *
+make_var(const char *prefix, const char *var)
+{
+ size_t len;
+ char *v;
+
+ len = strlen(prefix) + strlen(var) + 2;
+ v = xmalloc(len);
+ snprintf(v, len, "%s_%s", prefix, var);
+ return v;
+}
+
+
+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, const char *reason, char ***argv)
+{
+ char **env, *p;
+ ssize_t e, elen, l;
+ const struct if_options *ifo = iface->state->options;
+ const struct interface *ifp;
+ int dhcp, dhcp6, ra;
+ const struct dhcp6_state *d6_state;
+
+ dhcp = dhcp6 = ra = 0;
+ d6_state = D6_STATE(iface);
+ if (strcmp(reason, "TEST") == 0) {
+ if (d6_state && d6_state->new)
+ dhcp6 = 1;
+ else if (ipv6rs_has_ra(iface))
+ ra = 1;
+ else
+ dhcp = 1;
+ } else if (reason[strlen(reason) - 1] == '6')
+ dhcp6 = 1;
+ else if (strcmp(reason, "ROUTERADVERT") == 0)
+ ra = 1;
+ else
+ dhcp = 1;
+
+ /* When dumping the lease, we only want to report interface and
+ reason - the other interface variables are meaningless */
+ if (options & DHCPCD_DUMPLEASE)
+ elen = 2;
+ else
+ elen = 10;
+
+ /* Make our env */
+ env = xmalloc(sizeof(char *) * (elen + 1));
+ e = strlen("interface") + strlen(iface->name) + 2;
+ env[0] = xmalloc(e);
+ snprintf(env[0], e, "interface=%s", iface->name);
+ e = strlen("reason") + strlen(reason) + 2;
+ env[1] = xmalloc(e);
+ snprintf(env[1], e, "reason=%s", reason);
+ if (options & DHCPCD_DUMPLEASE)
+ goto dumplease;
+
+ e = 20;
+ env[2] = xmalloc(e);
+ snprintf(env[2], e, "pid=%d", getpid());
+ env[3] = xmalloc(e);
+ snprintf(env[3], e, "ifmetric=%d", iface->metric);
+ env[4] = xmalloc(e);
+ 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 (strcmp(reason, "TEST") == 0) {
+ env[8] = strdup("if_up=false");
+ env[9] = strdup("if_down=false");
+ } else if ((dhcp && iface->state->new) ||
+ (dhcp6 && d6_state->new) ||
+ (ra && ipv6rs_has_ra(iface)))
+ {
+ env[8] = strdup("if_up=true");
+ env[9] = strdup("if_down=false");
+ } else {
+ env[8] = strdup("if_up=false");
+ env[9] = strdup("if_down=true");
+ }
+ 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 (dhcp && iface->state->old) {
+ e = configure_env(NULL, NULL, iface->state->old, iface);
+ if (e > 0) {
+ env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+ elen += configure_env(env + elen, "old",
+ iface->state->old, iface);
+ }
+ append_config(&env, &elen, "old",
+ (const char *const *)ifo->config);
+ }
+ if (dhcp6 && d6_state->old) {
+ e = dhcp6_env(NULL, NULL, iface,
+ d6_state->old, d6_state->old_len);
+ if (e > 0) {
+ env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+ elen += dhcp6_env(env + elen, "old", iface,
+ d6_state->old, d6_state->old_len);
+ }
+ }
+
+dumplease:
+ if (dhcp && iface->state->new) {
+ e = configure_env(NULL, NULL, iface->state->new, iface);
+ if (e > 0) {
+ env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+ elen += configure_env(env + elen, "new",
+ iface->state->new, iface);
+ }
+ append_config(&env, &elen, "new",
+ (const char *const *)ifo->config);
+ }
+ if (dhcp6 && d6_state->new) {
+ e = dhcp6_env(NULL, NULL, iface,
+ d6_state->new, d6_state->new_len);
+ if (e > 0) {
+ env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+ elen += dhcp6_env(env + elen, "new", iface,
+ d6_state->new, d6_state->new_len);
+ }
+ }
+ if (ra) {
+ e = ipv6rs_env(NULL, NULL, iface);
+ if (e > 0) {
+ env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+ elen += ipv6rs_env(env + elen, NULL, iface);
+ }
+ }
+
+ /* Add our base environment */
+ if (ifo->environ) {
+ e = 0;
+ while (ifo->environ[e++])
+ ;
+ env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+ e = 0;
+ while (ifo->environ[e]) {
+ env[elen + e] = xstrdup(ifo->environ[e]);
+ e++;
+ }
+ elen += e;
+ }
+ env[elen] = '\0';
+
+ *argv = env;
+ return elen;
+}
+
+static int
+send_interface1(int fd, const struct interface *iface, const char *reason)
+{
+ char **env, **ep, *s;
+ ssize_t elen;
+ struct iovec iov[2];
+ int retval;
+
+ make_env(iface, reason, &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
+send_interface(int fd, const struct interface *iface)
+{
+ int retval = 0;
+ if (send_interface1(fd, iface, iface->state->reason) == -1)
+ retval = -1;
+ if (ipv6rs_has_ra(iface)) {
+ if (send_interface1(fd, iface, "ROUTERADVERT") == -1)
+ retval = -1;
+ }
+ if (D6_STATE_RUNNING(iface)) {
+ if (send_interface1(fd, iface, "INFORM6") == -1)
+ retval = -1;
+ }
+ return retval;
+}
+
+int
+script_runreason(const struct interface *iface, const char *reason)
+{
+ 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];
+
+ if (iface->state->options->script == NULL ||
+ iface->state->options->script[0] == '\0' ||
+ strcmp(iface->state->options->script, "/dev/null") == 0)
+ return 0;
+
+ if (reason == NULL)
+ reason = iface->state->reason;
+ syslog(LOG_DEBUG, "%s: executing `%s', reason %s",
+ iface->name, argv[0], reason);
+
+ /* Make our env */
+ elen = make_env(iface, reason, &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)
+ syslog(LOG_ERR, "%s: %s: %m", __func__, argv[0]);
+ else if (pid != 0) {
+ /* Wait for the script to finish */
+ while (waitpid(pid, &status, 0) == -1) {
+ if (errno != EINTR) {
+ syslog(LOG_ERR, "waitpid: %m");
+ status = 0;
+ break;
+ }
+ }
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status))
+ syslog(LOG_ERR,
+ "%s: %s: WEXITSTATUS %d",
+ __func__, argv[0], WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status))
+ syslog(LOG_ERR, "%s: %s: %s",
+ __func__, argv[0], strsignal(WTERMSIG(status)));
+ }
+
+ /* Send to our listeners */
+ bigenv = NULL;
+ for (fd = control_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, "%s: writev: %m", __func__);
+ }
+ }
+ free(bigenv);
+
+ /* Cleanup */
+ ep = env;
+ while (*ep)
+ free(*ep++);
+ free(env);
+ return WEXITSTATUS(status);
+}
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* SUCH DAMAGE.
*/
-#ifndef DHCPCONFIG_H
-#define DHCPCONFIG_H
+#ifndef SCRIPT_H
+#define SCRIPT_H
#include "net.h"
void if_printoptions(void);
int send_interface(int, const struct interface *);
-int run_script_reason(const struct interface *, const char *);
-void build_routes(void);
-int configure(struct interface *);
-int route_deleted(const struct rt *);
+int script_runreason(const struct interface *, const char *);
-#define run_script(ifp) run_script_reason(ifp, (ifp)->state->reason);
+#define script_run(ifp) script_runreason(ifp, (ifp)->state->reason);
#endif