The code has been drastically re-arranged.
Instead of populating a custom structure while parsing dhcp messages, we now pluck what we need right out of the message itself. We have custom functions and a lookup table to make this really easy.
This makes us more like dhclient and udhcpc, and will enable us to easily add (and remove!) more dhcp options without having to actually change the code (much).
We now store the real dhcp message we got in /var/db/dhcpcd-$iface.lease, the mtime of the file being used as when we got the lease. This file is read in when re-using an old lease instead of parsing the .info file.
The benefit of all of this means that we're actually ~15k smaller when compiled with the same features.
This has been tested for quite some time, and I'm pretty sure most bugs with the 3.2 branch have been fixed whilst making this. Right now, we are 99% command line compatible with the 3.2 branch.
# Copyright 2008 Roy Marples <roy@marples.name>
PROG= dhcpcd
-SRCS= arp.c client.c common.c configure.c dhcp.c dhcpcd.c duid.c \
- info.c if.c ipv4ll.c logger.c signal.c \
- ${SRC_IF} ${SRC_SOCKET}
+SRCS= common.c dhcp.c dhcpcd.c logger.c net.c signal.c
+SRCS+= configure.c client.c
+SRCS+= ${SRC_IF} ${SRC_SOCKET}
MAN= dhcpcd.8
-VERSION= 3.2.3
-CLEANFILES= version.h dhcpcd.8
+VERSION= 3.3.0-alpha1
+CLEANFILES= dhcpcd.8
BINDIR= ${PREFIX}/sbin
+.SUFFIXES: .in
+
+MK= mk
+include ${MK}/prog.mk
+
+CFLAGS+= -DVERSION=\"${VERSION}\"
+
# Work out how to restart services
_RC_SH= if test -n "${HAVE_INIT}"; then \
test "${HAVE_INIT}" = "no" || echo "-DENABLE_${HAVE_INIT}"; \
_RC!= ${_RC_SH}
CFLAGS+= ${_RC}$(shell ${_RC_SH})
-.SUFFIXES: .in
-
-MK= mk
-include ${MK}/prog.mk
-
CFLAGS+= -DINFODIR=\"${INFODIR}\"
LDADD+= ${LIBRESOLV} ${LIBRT}
-# As version.h is generated by us, hardcode the depend correctly.
-${SRCS}: version.h
-version.h:
- echo "#define VERSION \"${VERSION}\""> version.h
-
.in:
${SED} 's:@PREFIX@:${PREFIX}:g; s:@INFODIR@:${INFODIR}:g' $< > $@
-----
If you're cross compiling you may need to set the below knobs to avoid
automatic tests.
-HAVE_FORK=yes | no
OS=BSD | Linux
We try and detect how to restart ntp and ypbind, you can override this with
You can change the default dir where dhcpcd stores it's .info files with
INFODIR=/var/db
+If you're building for a NOMMU system where fork() does not work, youu should
+add -DTHERE_IS_NO_FORK to your CFLAGS.
+
We now default to using -std=c99. For 64-bit linux, this always works, but
for 32-bit linux it requires either gnu99 or a patch to asm/types.h.
Most distros patch linux headers so this should work fine.
+++ /dev/null
-/*
- * dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
- * All rights reserved
-
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-
-#include <netinet/in_systm.h>
-#ifdef __linux__
-#include <netinet/ether.h>
-#include <netpacket/packet.h>
-#endif
-#include <net/if.h>
-#include <net/if_arp.h>
-#include <arpa/inet.h>
-
-#include <errno.h>
-#include <poll.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "config.h"
-#include "common.h"
-#include "arp.h"
-#include "if.h"
-#include "logger.h"
-#include "signal.h"
-#include "socket.h"
-
-/* These are really for IPV4LL */
-#define NPROBES 3
-#define PROBE_INTERVAL 200
-#define NCLAIMS 2
-#define CLAIM_INTERVAL 200
-
-/* Linux does not seem to define these handy macros */
-#ifndef ar_sha
-#define ar_sha(ap) (((caddr_t)((ap) + 1)) + 0)
-#define ar_spa(ap) (((caddr_t)((ap) + 1)) + (ap)->ar_hln)
-#define ar_tha(ap) (((caddr_t)((ap) + 1)) + (ap)->ar_hln + (ap)->ar_pln)
-#define ar_tpa(ap) (((caddr_t)((ap) + 1)) + 2 * (ap)->ar_hln + (ap)->ar_pln)
-#endif
-
-#ifndef arphdr_len
-#define arphdr_len2(ar_hln, ar_pln) (sizeof(struct arphdr) + \
- 2 * (ar_hln) + 2 * (ar_pln))
-#define arphdr_len(ap) (arphdr_len2((ap)->ar_hln, (ap)->ar_pln))
-#endif
-
-#ifdef ENABLE_ARP
-
-static int
-send_arp(const struct interface *iface, int op, struct in_addr sip,
- const unsigned char *taddr, struct in_addr tip)
-{
- struct arphdr *arp;
- size_t arpsize = arphdr_len2(iface->hwlen, sizeof(sip));
- caddr_t tha;
- int retval;
-
- arp = xzalloc(arpsize);
- arp->ar_hrd = htons(iface->family);
- arp->ar_pro = htons(ETHERTYPE_IP);
- arp->ar_hln = iface->hwlen;
- arp->ar_pln = sizeof(sip);
- arp->ar_op = htons(op);
- memcpy(ar_sha(arp), iface->hwaddr, (size_t)arp->ar_hln);
- memcpy(ar_spa(arp), &sip, (size_t)arp->ar_pln);
- if (taddr) {
- /* NetBSD can return NULL from ar_tha, which is probably wrong
- * but we still need to deal with it */
- if (! (tha = ar_tha(arp))) {
- free(arp);
- errno = EINVAL;
- return -1;
- }
- memcpy(tha, taddr, (size_t)arp->ar_hln);
- }
- memcpy(ar_tpa(arp), &tip, (size_t)arp->ar_pln);
-
- retval = send_packet(iface, ETHERTYPE_ARP,
- (unsigned char *) arp, arphdr_len(arp));
- if (retval == -1)
- logger(LOG_ERR,"send_packet: %s", strerror(errno));
- free(arp);
- return retval;
-}
-
-int
-arp_claim(struct interface *iface, struct in_addr address)
-{
- struct arphdr *reply = NULL;
- long timeout = 0;
- unsigned char *buffer;
- int retval = -1;
- int nprobes = 0;
- int nclaims = 0;
- struct in_addr null_address;
- struct pollfd fds[] = {
- { -1, POLLIN, 0 },
- { -1, POLLIN, 0 }
- };
- ssize_t bufpos = 0;
- ssize_t buflen = iface->buffer_length;
- int bytes;
- int s = 0;
- struct timeval stopat;
- struct timeval now;
- union {
- unsigned char *c;
- struct in_addr *a;
- } rp;
- union {
- unsigned char *c;
- struct ether_addr *a;
- } rh;
-
- if (!iface->arpable) {
- logger(LOG_DEBUG, "interface `%s' is not ARPable", iface->name);
- return 0;
- }
-
- if (!IN_LINKLOCAL(ntohl(iface->previous_address.s_addr)) &&
- !IN_LINKLOCAL(ntohl(address.s_addr)))
- logger(LOG_INFO,
- "checking %s is available on attached networks",
- inet_ntoa(address));
-
- if (!open_socket(iface, ETHERTYPE_ARP)) {
- logger (LOG_ERR, "open_socket: %s", strerror(errno));
- return -1;
- }
-
- fds[0].fd = signal_fd();
- fds[1].fd = iface->fd;
- memset(&null_address, 0, sizeof(null_address));
- buffer = xmalloc(iface->buffer_length);
- reply = xmalloc(iface->buffer_length);
-
- for (;;) {
- bufpos = 0;
- buflen = iface->buffer_length;
- s = 0;
-
- /* Only poll if we have a timeout */
- if (timeout > 0) {
- s = poll(fds, 2, timeout);
- if (s == -1) {
- if (errno == EINTR) {
- if (signal_exists(NULL) == -1) {
- errno = 0;
- continue;
- } else
- break;
- }
-
- logger(LOG_ERR, "poll: `%s'", strerror(errno));
- break;
- }
- }
-
- /* Timed out */
- if (s == 0) {
- if (nprobes < NPROBES) {
- nprobes ++;
- timeout = PROBE_INTERVAL;
- logger(LOG_DEBUG, "sending ARP probe #%d",
- nprobes);
- if (send_arp(iface, ARPOP_REQUEST,
- null_address, NULL,
- address) == -1)
- break;
-
- /* IEEE1394 cannot set ARP target address
- * according to RFC2734 */
- if (nprobes >= NPROBES &&
- iface->family == ARPHRD_IEEE1394)
- nclaims = NCLAIMS;
- } else if (nclaims < NCLAIMS) {
- nclaims ++;
- timeout = CLAIM_INTERVAL;
- logger(LOG_DEBUG, "sending ARP claim #%d",
- nclaims);
- if (send_arp(iface, ARPOP_REQUEST,
- address, iface->hwaddr,
- address) == -1)
- break;
- } else {
- /* No replies, so done */
- retval = 0;
- break;
- }
-
- /* Setup our stop time */
- if (get_time(&stopat) != 0)
- break;
- stopat.tv_usec += timeout;
-
- continue;
- }
-
- /* We maybe ARP flooded, so check our time */
- if (get_time(&now) != 0)
- break;
- if (timercmp(&now, &stopat, >)) {
- timeout = 0;
- continue;
- }
-
- if (!(fds[1].revents & POLLIN))
- continue;
-
- memset(buffer, 0, buflen);
- do {
- memset(reply, 0, iface->buffer_length);
- if ((bytes = get_packet(iface, (unsigned char *) reply,
- buffer,
- &buflen, &bufpos)) == -1)
- break;
-
- /* Only these types are recognised */
- if (reply->ar_op != htons(ARPOP_REPLY))
- continue;
-
- /* Protocol must be IP. */
- if (reply->ar_pro != htons(ETHERTYPE_IP))
- continue;
- if (reply->ar_pln != sizeof(address))
- continue;
- if ((unsigned)bytes < sizeof(reply) +
- 2 * (4 +reply->ar_hln))
- continue;
-
- rp.c = (unsigned char *)ar_spa(reply);
- rh.c = (unsigned char *)ar_sha(reply);
-
- /* Ensure the ARP reply is for the our address */
- if (rp.a->s_addr != address.s_addr)
- continue;
-
- /* Some systems send a reply back from our hwaddress,
- * which is wierd */
- if (reply->ar_hln == iface->hwlen &&
- memcmp(rh.c, iface->hwaddr, iface->hwlen) == 0)
- continue;
-
- logger(LOG_ERR, "ARPOP_REPLY received from %s (%s)",
- inet_ntoa(*rp.a),
- hwaddr_ntoa(rh.c, (size_t)reply->ar_hln));
- retval = -1;
- goto eexit;
- } while (bufpos != 0);
- }
-
-eexit:
- close(iface->fd);
- iface->fd = -1;
- free(buffer);
- free(reply);
- return retval;
-}
-#endif
+++ /dev/null
-/*
- * dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
- * All rights reserved
-
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef ARP_H
-#define ARP_H
-
-#ifdef ENABLE_ARP
-#include <netinet/in.h>
-
-#include "if.h"
-
-int arp_claim(struct interface *, struct in_addr);
-#endif
-
-#endif
#include <sys/types.h>
#include <sys/ioctl.h>
-#include <sys/param.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <net/bpf.h>
#include <net/if.h>
-#include <netinet/in_systm.h>
-#include <netinet/in.h>
-#include <netinet/udp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <time.h>
#include <unistd.h>
#include "config.h"
+#include "common.h"
#include "dhcp.h"
-#include "if.h"
-#include "socket.h"
+#include "logger.h"
+#include "net.h"
#include "bpf-filter.h"
int
int flags;
struct ifreq ifr;
int buf = 0;
+ struct bpf_version pv;
struct bpf_program pf;
device = xmalloc(sizeof(char) * PATH_MAX);
if (fd == -1)
return -1;
- close_on_exec(fd);
+ if (ioctl(fd, BIOCVERSION, &pv) == -1)
+ goto eexit;
+ if (pv.bv_major != BPF_MAJOR_VERSION ||
+ pv.bv_minor < BPF_MINOR_VERSION) {
+ logger(LOG_ERR, "BPF version mismatch - recompile " PACKAGE);
+ goto eexit;
+ }
+
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
- if (ioctl(fd, BIOCSETIF, &ifr) == -1) {
- close(fd);
- return -1;
- }
+ if (ioctl(fd, BIOCSETIF, &ifr) == -1)
+ goto eexit;
/* Get the required BPF buffer length from the kernel. */
- if (ioctl(fd, BIOCGBLEN, &buf) == -1) {
- close(fd);
- return -1;
- }
+ if (ioctl(fd, BIOCGBLEN, &buf) == -1)
+ goto eexit;
iface->buffer_length = buf;
flags = 1;
- if (ioctl(fd, BIOCIMMEDIATE, &flags) == -1) {
- close(fd);
- return -1;
- }
+ if (ioctl(fd, BIOCIMMEDIATE, &flags) == -1)
+ goto eexit;
/* Install the DHCP filter */
if (protocol == ETHERTYPE_ARP) {
pf.bf_insns = dhcp_bpf_filter;
pf.bf_len = sizeof(dhcp_bpf_filter)/sizeof(dhcp_bpf_filter[0]);
}
- if (ioctl(fd, BIOCSETF, &pf) == -1) {
- close(fd);
- return -1;
- }
+ if (ioctl(fd, BIOCSETF, &pf) == -1)
+ goto eexit;
if (iface->fd > -1)
close(iface->fd);
+
+ close_on_exec(fd);
iface->fd = fd;
return fd;
+
+eexit:
+ close(fd);
+ return -1;
}
ssize_t
unsigned char *buffer;
struct ether_header *hw;
} hdr;
- union
- {
- unsigned char *buffer;
- struct udp_dhcp_packet *packet;
- } pay;
struct timespec ts;
ssize_t len;
unsigned char *payload;
- bool have_data;
+ const uint8_t *d;
bpf.buffer = buffer;
} else
bpf.buffer += *buffer_pos;
- do {
- len = 0;
- have_data = false;
-
- /* Ensure that the entire packet is in our buffer */
- if (*buffer_pos +
- bpf.packet->bh_hdrlen +
- bpf.packet->bh_caplen > (unsigned)*buffer_len)
- break;
+ for (; bpf.buffer - buffer < *buffer_len;
+ bpf.buffer += BPF_WORDALIGN(bpf.packet->bh_hdrlen +
+ bpf.packet->bh_caplen),
+ *buffer_pos = bpf.buffer - buffer)
+ {
+ /* Ensure we have the whole packet */
+ if (bpf.packet->bh_caplen != bpf.packet->bh_datalen)
+ continue;
hdr.buffer = bpf.buffer + bpf.packet->bh_hdrlen;
payload = hdr.buffer + sizeof(*hdr.hw);
if (hdr.hw->ether_type == htons (ETHERTYPE_ARP)) {
len = bpf.packet->bh_caplen - sizeof(*hdr.hw);
memcpy(data, payload, len);
- have_data = true;
+ return len;
} else {
- if (valid_dhcp_packet(payload) >= 0) {
- pay.buffer = payload;
- len = ntohs(pay.packet->ip.ip_len) -
- sizeof(pay.packet->ip) -
- sizeof(pay.packet->udp);
- memcpy(data, &pay.packet->dhcp, len);
- have_data = true;
+ if (valid_udp_packet(payload) >= 0) {
+ len = get_udp_data(&d, payload);
+ memcpy(data, d, len);
+ return len;
}
}
-
- /* Update the buffer_pos pointer */
- bpf.buffer += BPF_WORDALIGN(bpf.packet->bh_hdrlen +
- bpf.packet->bh_caplen);
- if (bpf.buffer - buffer < *buffer_len)
- *buffer_pos = bpf.buffer - buffer;
- else
- *buffer_pos = 0;
-
- if (have_data)
- return len;
- } while (*buffer_pos);
+ }
/* No valid packets left, so return */
*buffer_pos = 0;
* SUCH DAMAGE.
*/
+#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <arpa/inet.h>
# include <netinet/ether.h>
#endif
-#include <ctype.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>
-#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "config.h"
#include "common.h"
-#ifdef ENABLE_ARP
-# include "arp.h"
-#endif
#include "client.h"
#include "configure.h"
#include "dhcp.h"
#include "dhcpcd.h"
-#include "info.h"
-#include "if.h"
-#ifdef ENABLE_IPV4LL
-# include "ipv4ll.h"
-#endif
+#include "net.h"
#include "logger.h"
#include "signal.h"
-#include "socket.h"
-
-#ifdef ENABLE_DUID
-# include "duid.h"
-#endif
-
-#ifdef ENABLE_INFO
-# include "info.h"
-#endif
#ifdef THERE_IS_NO_FORK
# ifndef ENABLE_INFO
# endif
#endif
+#ifdef ENABLE_IPV4LL
+# ifndef ENABLE_ARP
+ # error "IPv4LL requires ENABLE_ARP to work"
+# endif
+#define IPV4LL_LEASETIME 20
+#endif
+
/* Some platforms don't define INFTIM */
#ifndef INFTIM
# define INFTIM -1
#define POLLFD_IFACE 1
struct if_state {
- int *pidfd;
- bool forked;
- int state;
- uint32_t xid;
- struct dhcp *dhcp;
- int socket;
+ int options;
struct interface *interface;
+ struct dhcp_message *dhcp;
+ struct dhcp_lease lease;
time_t start;
time_t last_sent;
time_t last_type;
+ int state;
long timeout;
time_t nakoff;
- bool daemonised;
- bool persistent;
+ uint32_t xid;
+ int socket;
unsigned char *buffer;
ssize_t buffer_len;
ssize_t buffer_pos;
+ int *pidfd;
};
+struct dhcp_op {
+ uint8_t value;
+ const char *name;
+};
+
+static struct dhcp_op dhcp_ops[] = {
+ { DHCP_DISCOVER, "DHCP_DISCOVER" },
+ { DHCP_OFFER, "DHCP_OFFER" },
+ { DHCP_REQUEST, "DHCP_REQUEST" },
+ { DHCP_DECLINE, "DHCP_DECLINE" },
+ { DHCP_ACK, "DHCP_ACK" },
+ { DHCP_NAK, "DHCP_NAK" },
+ { DHCP_RELEASE, "DHCP_RELEASE" },
+ { DHCP_INFORM, "DHCP_INFORM" },
+ { 0, NULL }
+};
+
+static const char *
+get_dhcp_op(uint8_t type)
+{
+ struct dhcp_op *d;
+
+ for (d = dhcp_ops; d->name; d++)
+ if (d->value == type)
+ return d->name;
+
+ return NULL;
+}
+
static pid_t
daemonise(int *pidfd)
{
case -1:
logger(LOG_ERR, "fork: %s", strerror(errno));
exit(EXIT_FAILURE);
- /* NOT REACHED */
+ /* NOTREACHED */
case 0:
setsid();
close_fds();
writepid(*pidfd, pid);
close(*pidfd);
*pidfd = -1;
-
}
sigprocmask(SIG_SETMASK, &old, NULL);
return pid;
}
+
+#ifdef ENABLE_DUID
+#define THIRTY_YEARS_IN_SECONDS 946707779
+static size_t
+get_duid(unsigned char *duid, const struct interface *iface)
+{
+ FILE *f;
+ uint16_t type = 0;
+ uint16_t hw = 0;
+ uint32_t ul;
+ time_t t;
+ int x = 0;
+ unsigned char *p = duid;
+ size_t len = 0;
+ char *line = NULL;
+
+ /* If we already have a DUID then use it as it's never supposed
+ * to change once we have one even if the interfaces do */
+ if ((f = fopen(DUIDFILE, "r"))) {
+ get_line(&line, &len, f);
+ if (line) {
+ len = hwaddr_aton(NULL, line);
+ if (len && len <= DUID_LEN)
+ hwaddr_aton(duid, line);
+ free(line);
+ } else
+ len = 0;
+ fclose(f);
+ if (len)
+ return len;
+ } else {
+ if (errno != ENOENT)
+ return 0;
+ }
+
+ /* No file? OK, lets make one based on our interface */
+ if (!(f = fopen(DUIDFILE, "w")))
+ return 0;
+
+ type = htons(1); /* DUI-D-LLT */
+ memcpy(p, &type, 2);
+ p += 2;
+
+ hw = htons(iface->family);
+ memcpy(p, &hw, 2);
+ p += 2;
+
+ /* time returns seconds from jan 1 1970, but DUID-LLT is
+ * seconds from jan 1 2000 modulo 2^32 */
+ t = time(NULL) - THIRTY_YEARS_IN_SECONDS;
+ ul = htonl(t & 0xffffffff);
+ memcpy(p, &ul, 4);
+ p += 4;
+
+ /* Finally, add the MAC address of the interface */
+ memcpy(p, iface->hwaddr, iface->hwlen);
+ p += iface->hwlen;
+
+ len = p - duid;
+
+ x = fprintf(f, "%s\n", hwaddr_ntoa(duid, len));
+ fclose(f);
+
+ /* Failed to write the duid? scrub it, we cannot use it */
+ if (x < 1) {
+ len = 0;
+ unlink(DUIDFILE);
+ }
+
+ return len;
+}
+#endif
+
+#ifdef ENABLE_IPV4LL
+static int
+ipv4ll_get_address(struct interface *iface, struct dhcp_lease *lease) {
+ struct in_addr addr;
+
+ for (;;) {
+ addr.s_addr = htonl(LINKLOCAL_ADDR |
+ (((uint32_t)abs((int)random())
+ % 0xFD00) + 0x0100));
+ errno = 0;
+ if (!arp_claim(iface, addr))
+ break;
+ /* Our ARP may have been interrupted */
+ if (errno)
+ return -1;
+ }
+
+ lease->addr.s_addr = addr.s_addr;
+
+ /* Finally configure some DHCP like lease times */
+ lease->leasetime = IPV4LL_LEASETIME;
+ lease->renewaltime = lease->leasetime * 0.5;
+ lease->rebindtime = lease->leasetime * 0.875;
+
+ return 0;
+}
+#endif
+
+static void
+get_lease(struct dhcp_lease *lease, const struct dhcp_message *dhcp)
+{
+ lease->addr.s_addr = dhcp->yiaddr;
+ if (get_option_addr(&lease->net.s_addr, dhcp, DHCP_NETMASK) == -1)
+ lease->net.s_addr = get_netmask(dhcp->yiaddr);
+ if (get_option_uint32(&lease->leasetime, dhcp, DHCP_LEASETIME) != 0)
+ lease->leasetime = DEFAULT_LEASETIME;
+ if (get_option_uint32(&lease->renewaltime, dhcp, DHCP_RENEWALTIME) != 0)
+ lease->renewaltime = 0;
+ if (get_option_uint32(&lease->rebindtime, dhcp, DHCP_REBINDTIME) != 0)
+ lease->rebindtime = 0;
+ lease->frominfo = 0;
+}
+
#ifdef ENABLE_INFO
-static bool
+static int
get_old_lease(struct if_state *state, const struct options *options)
{
struct interface *iface = state->interface;
- struct dhcp *dhcp = state->dhcp;
+ struct dhcp_lease *lease = &state->lease;
+ struct dhcp_message *dhcp;
struct timeval tv;
unsigned int offset = 0;
+ struct stat sb;
- if (!IN_LINKLOCAL(ntohl(iface->previous_address.s_addr)))
+ if (!IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
logger(LOG_INFO, "trying to use old lease in `%s'",
- iface->infofile);
- if (!read_info(iface, dhcp))
- return false;
+ iface->leasefile);
+ if ((dhcp = read_lease(iface)) == NULL) {
+ if (errno != ENOENT)
+ logger(LOG_INFO, "read_lease: %s", strerror(errno));
+ return -1;
+ }
+ if (stat(iface->leasefile, &sb) == -1) {
+ logger(LOG_ERR, "stat: %s", strerror(errno));
+ return -1;
+ }
+ get_lease(&state->lease, dhcp);
+ lease->frominfo = 1;
+ lease->leasedfrom = sb.st_mtime;
/* Vitaly important we remove the server information here */
- memset(&dhcp->serveraddress, 0, sizeof(dhcp->serveraddress));
- memset(dhcp->servername, 0, sizeof(dhcp->servername));
+ state->lease.server.s_addr = 0;
+ dhcp->servername[0] = '\0';
#ifdef ENABLE_ARP
/* Check that no-one is using the address */
- if ((options->options & DHCPCD_LASTLEASE ||
- (IN_LINKLOCAL(ntohl (dhcp->address.s_addr)) &&
- (!(options->options & DHCPCD_IPV4LL) ||
- arp_claim(iface, dhcp->address)))))
+ if (options->options & DHCPCD_ARP &&
+ (options->options & DHCPCD_LASTLEASE ||
+ (options->options & DHCPCD_IPV4LL &&
+ IN_LINKLOCAL(ntohl(lease->addr.s_addr)))) &&
+ arp_claim(iface, lease->addr))
{
- memset(&dhcp->address, 0, sizeof(dhcp->address));
- memset(&dhcp->netmask, 0, sizeof(dhcp->netmask));
- memset(&dhcp->broadcast, 0, sizeof(dhcp->broadcast));
- return false;
+ lease->addr.s_addr = 0;
+ free(dhcp);
+ return -1;
}
/* Ok, lets use this */
- if (IN_LINKLOCAL(dhcp->address.s_addr))
- return true;
+ if (IN_LINKLOCAL(dhcp->yiaddr)) {
+ free(state->dhcp);
+ state->dhcp = dhcp;
+ return 0;
+ }
#endif
/* Ensure that we can still use the lease */
if (gettimeofday(&tv, NULL) == -1) {
logger(LOG_ERR, "gettimeofday: %s", strerror(errno));
- return false;
+ free(dhcp);
+ return -1;
}
- offset = tv.tv_sec - dhcp->leasedfrom;
- if (dhcp->leasedfrom &&
- tv.tv_sec - dhcp->leasedfrom > dhcp->leasetime)
+ offset = tv.tv_sec - lease->leasedfrom;
+ if (lease->leasedfrom &&
+ tv.tv_sec - lease->leasedfrom > lease->leasetime)
{
logger(LOG_ERR, "lease expired %u seconds ago",
- offset + dhcp->leasetime);
- return false;
+ offset + lease->leasetime);
+ free(dhcp);
+ return -1;
}
- if (dhcp->leasedfrom == 0)
+ if (lease->leasedfrom == 0)
offset = 0;
- state->timeout = dhcp->renewaltime - offset;
+ state->timeout = lease->renewaltime - offset;
iface->start_uptime = uptime();
- return true;
-}
-#endif
-
-#ifdef THERE_IS_NO_FORK
-static void
-remove_skiproutes(struct dhcp *dhcp, struct interface *iface)
-{
- int i = -1;
- struct route *route;
- struct route *newroute;
- char *sk;
- char *skp;
- char *token;
-
- free_route(iface->previous_routes);
- iface->previous_routes = NULL;
-
- NSTAILQ_FOREACH(route, dhcp->routes, entries) {
- i++;
-
- /* Check that we did add this route or not */
- if (dhcpcd_skiproutes) {
- sk = skp = xstrdup (dhcpcd_skiproutes);
- found = false;
- while ((token = strsep(&skp, ","))) {
- if (isdigit(*token) && atoi(token) == i) {
- found = true;
- break;
- }
- }
- free(sk);
- if (found)
- continue;
- }
-
- if (! iface->previous_routes) {
- iface->previous_routes = xmalloc (sizeof (*iface->previous_routes));
- STAILQ_INIT (iface->previous_routes);
- }
-
- newroute = xmalloc(sizeof (*newroute));
- memcpy(newroute, route, sizeof(*newroute));
- STAILQ_INSERT_TAIL(iface->previous_routes, newroute, entries);
- }
-
- /* We no longer need this argument */
- free(dhcpcd_skiproutes);
- dhcpcd_skiproutes = NULL;
+ free(state->dhcp);
+ state->dhcp = dhcp;
+ return 0;
}
#endif
-static bool
+static int
client_setup(struct if_state *state, const struct options *options)
{
- struct dhcp *dhcp = state->dhcp;
struct interface *iface = state->interface;
+ struct dhcp_lease *lease = &state->lease;
+ struct in_addr addr;
+ struct in_addr net;
state->state = STATE_INIT;
state->last_type = DHCP_DISCOVER;
state->nakoff = 1;
- state->daemonised = (options->options & DHCPCD_DAEMONISED);
- state->persistent = (options->options & DHCPCD_PERSISTENT);
+ state->options = options->options;
if (options->request_address.s_addr == 0 &&
(options->options & DHCPCD_INFORM ||
#ifdef ENABLE_INFO
if (!get_old_lease(state, options))
#endif
- {
- free(dhcp);
- return false;
- }
+ return -1;
state->timeout = 0;
if (!(options->options & DHCPCD_DAEMONISED) &&
- IN_LINKLOCAL(ntohl(dhcp->address.s_addr)))
+ IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
{
logger(LOG_ERR, "cannot request a link local address");
- return false;
+ return -1;
}
#ifdef THERE_IS_NO_FORK
if (options->options & DHCPCD_DAEMONISED) {
state->state = STATE_BOUND;
- state->timeout = dhcp->renewaltime;
- iface->previous_address = dhcp->address;
- iface->previous_netmask = dhcp->netmask;
- remove_skiproutes(dhcp, iface);
+ state->timeout = state->lease.renewaltime;
+ iface->addr = lease->addr;
}
#endif
-
} else {
- dhcp->address = options->request_address;
- dhcp->netmask = options->request_netmask;
- if (dhcp->netmask.s_addr == 0)
- dhcp->netmask.s_addr = get_netmask(dhcp->address.s_addr);
- dhcp->broadcast.s_addr = dhcp->address.s_addr |
- ~dhcp->netmask.s_addr;
- }
-
- /* Remove all existing addresses.
- * After all, we ARE a DHCP client whose job it is to configure the
- * interface. We only do this on start, so persistent addresses
- * can be added afterwards by the user if needed. */
- if (!(options->options & DHCPCD_TEST) &&
- !(options->options & DHCPCD_DAEMONISED))
- {
- if (!(options->options & DHCPCD_INFORM)) {
- flush_addresses (iface->name);
- } else if (has_address(iface->name, dhcp->address) < 1) {
- /* The inform address HAS to be configured for it to
- * work with most DHCP servers */
- /* add_address */
- iface->previous_address = dhcp->address;
- iface->previous_netmask = dhcp->netmask;
- }
+ lease->addr.s_addr = options->request_address.s_addr;
+ lease->net.s_addr = options->request_netmask.s_addr;
}
if (*options->clientid) {
}
}
- return true;
+ /* Remove all existing addresses.
+ * After all, we ARE a DHCP client whose job it is to configure the
+ * interface. We only do this on start, so persistent addresses
+ * can be added afterwards by the user if needed. */
+ if (!(options->options & DHCPCD_TEST) &&
+ !(options->options & DHCPCD_DAEMONISED))
+ {
+ if (!(options->options & DHCPCD_INFORM)) {
+ while (get_address(iface->name, &addr, &net) == 1) {
+ logger(LOG_DEBUG, "deleting IP address %s/%d",
+ inet_ntoa(addr),
+ inet_ntocidr(lease->net));
+ if (del_address(iface->name, &addr, &net) == -1)
+ {
+ logger(LOG_ERR, "delete_address: %s",
+ strerror(errno));
+ break;
+ }
+ }
+ } else if (has_address(iface->name,
+ &lease->addr, &lease->net) < 1)
+ {
+ /* The inform address HAS to be configured for it to
+ * work with most DHCP servers */
+ /* add_address */
+ iface->addr.s_addr = lease->addr.s_addr;
+ iface->net.s_addr = lease->net.s_addr;
+ }
+ }
+
+ return 0;
}
-static bool
+static int
do_socket(struct if_state *state, int mode)
{
- if (state->interface->fd >= 0)
+ if (state->interface->fd >= 0) {
close(state->interface->fd);
-#ifdef __linux
- if (mode == SOCKET_CLOSED && state->interface->listen_fd >= 0) {
- close(state->interface->listen_fd);
- state->interface->listen_fd = -1;
+ state->interface->fd = -1;
}
-#endif
+ if (mode == SOCKET_CLOSED && state->interface->udp_fd >= 0) {
+ close(state->interface->udp_fd);
+ state->interface->udp_fd = -1;
+ }
+
+ /* We need to bind to a port, otherwise we generate ICMP messages
+ * that cannot connect the port when we have an address.
+ * We don't actually use this fd at all, instead using our packet
+ * filter socket. */
+ if (mode == SOCKET_OPEN &&
+ state->interface->udp_fd == -1 &&
+ state->lease.addr.s_addr != 0)
+ if (open_udp_socket(state->interface) == -1) {
+ logger(LOG_ERR, "open_udp_socket: %s", strerror(errno));
+ return -1;
+ }
- state->interface->fd = -1;
if (mode == SOCKET_OPEN)
if (open_socket(state->interface, ETHERTYPE_IP) == -1) {
logger(LOG_ERR, "open_socket: %s", strerror(errno));
- return false;
+ return -1;
}
state->socket = mode;
- return true;
+ return 0;
}
-static bool
-_send_message(struct if_state *state, int type, const struct options *options)
+static ssize_t
+send_message(struct if_state *state, int type, const struct options *options)
{
+ struct dhcp_message *dhcp;
+ uint8_t *udp;
+ ssize_t len;
ssize_t retval;
+ struct in_addr from;
+ struct in_addr to;
state->last_type = type;
state->last_sent = uptime();
- retval = send_message(state->interface, state->dhcp, state->xid,
- type, options);
- return retval == -1 ? false : true;
+ len = make_message(&dhcp, state->interface, &state->lease, state->xid,
+ type, options);
+ from.s_addr = dhcp->ciaddr;
+ if (from.s_addr)
+ to.s_addr = state->lease.server.s_addr;
+ else
+ to.s_addr = 0;
+ len = make_udp_packet(&udp, (uint8_t *)dhcp, len, from, to);
+ free(dhcp);
+ logger(LOG_DEBUG, "sending %s with xid 0x%x",
+ get_dhcp_op(type), state->xid);
+ retval = send_packet(state->interface, ETHERTYPE_IP, udp, len);
+ if (retval == -1)
+ logger(LOG_ERR, "send_packet: %s", strerror(errno));
+ free(udp);
+ return retval;
}
static void
drop_config(struct if_state *state, const struct options *options)
{
- if (! state->persistent)
- configure(options, state->interface, state->dhcp, false);
+ if (!(state->options & DHCPCD_PERSISTENT))
+ configure(state->interface, state->dhcp,
+ &state->lease, options, 0);
- free_dhcp(state->dhcp);
- memset(state->dhcp, 0, sizeof(*state->dhcp));
+ state->lease.addr.s_addr = 0;
}
static int
wait_for_packet(struct pollfd *fds, struct if_state *state,
const struct options *options)
{
- struct dhcp *dhcp = state->dhcp;
+ struct dhcp_lease *lease = &state->lease;
struct interface *iface = state->interface;
int timeout = 0;
int retval = 0;
fds[POLLFD_IFACE].fd = iface->fd;
if ((options->timeout == 0 && state->xid) ||
- (dhcp->leasetime == ~0U &&
+ (lease->leasetime == ~0U &&
state->state == STATE_BOUND))
{
logger(LOG_DEBUG, "waiting for infinity");
continue;
}
if (retval == 0)
- _send_message(state, state->last_type,
- options);
+ send_message(state, state->last_type,
+ options);
}
}
* As we use BPF or LPF, we shouldn't hit this as much, but it's
* still nice to have. */
if (iface->fd > -1 && uptime() - state->last_sent >= TIMEOUT_MINI)
- _send_message(state, state->last_type, options);
+ send_message(state, state->last_type, options);
logger(LOG_DEBUG, "waiting for %lu seconds", state->timeout);
/* If we're waiting for a reply, then we re-send the last
continue;
}
if (retval == 0 && iface->fd != -1 && state->timeout > 0)
- _send_message(state, state->last_type, options);
+ send_message(state, state->last_type, options);
}
return retval;
}
-static bool
+static int
handle_signal(int sig, struct if_state *state, const struct options *options)
{
+ struct dhcp_lease *lease = &state->lease;
+
switch (sig) {
case SIGINT:
logger(LOG_INFO, "received SIGINT, stopping");
- return false;
+ return -1;
case SIGTERM:
logger(LOG_INFO, "received SIGTERM, stopping");
- return false;
+ return -1;
case SIGALRM:
logger (LOG_INFO, "received SIGALRM, renewing lease");
}
state->timeout = 0;
state->xid = 0;
- return true;
+ return 0;
case SIGHUP:
if (state->state != STATE_BOUND &&
{
logger(LOG_ERR,
"received SIGHUP, but no lease to release");
- return false;
+ return -1;
}
logger (LOG_INFO, "received SIGHUP, releasing lease");
- if (!IN_LINKLOCAL(ntohl(state->dhcp->address.s_addr))) {
+ if (!IN_LINKLOCAL(ntohl(lease->addr.s_addr))) {
do_socket(state, SOCKET_OPEN);
state->xid = (uint32_t)random();
- _send_message(state, DHCP_RELEASE, options);
+ send_message(state, DHCP_RELEASE, options);
do_socket(state, SOCKET_CLOSED);
}
unlink(state->interface->infofile);
- return false;
+ return -1;
default:
logger (LOG_ERR,
sig);
}
- return false;
+ return -1;
}
static int
handle_timeout(struct if_state *state, const struct options *options)
{
- struct dhcp *dhcp = state->dhcp;
+ struct dhcp_lease *lease = &state->lease;
struct interface *iface = state->interface;
/* No NAK, so reset the backoff */
state->nakoff = 1;
if (state->state == STATE_INIT && state->xid != 0) {
- if (iface->previous_address.s_addr != 0 &&
- !IN_LINKLOCAL(ntohl(iface->previous_address.s_addr)) &&
- !(options->options & DHCPCD_INFORM))
+ if (iface->addr.s_addr != 0 &&
+ !IN_LINKLOCAL(ntohl(iface->addr.s_addr)) &&
+ !(state->options & DHCPCD_INFORM))
{
logger(LOG_ERR, "lost lease");
- if (!(options->options & DHCPCD_PERSISTENT))
+ if (!(state->options & DHCPCD_PERSISTENT))
drop_config(state, options);
- } else if (!IN_LINKLOCAL(ntohl(iface->previous_address.s_addr)))
+ } else if (!IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
logger(LOG_ERR, "timed out");
do_socket(state, SOCKET_CLOSED);
- free_dhcp(dhcp);
- memset(dhcp, 0, sizeof(*dhcp));
#ifdef ENABLE_INFO
- if (!(options->options & DHCPCD_TEST) &&
- (options->options & DHCPCD_IPV4LL ||
- options->options & DHCPCD_LASTLEASE))
+ if (!(state->options & DHCPCD_TEST) &&
+ (state->options & DHCPCD_IPV4LL ||
+ state->options & DHCPCD_LASTLEASE))
{
errno = 0;
- if (!get_old_lease(state, options))
- {
+ if (get_old_lease(state, options) != 0) {
if (errno == EINTR)
return 0;
- if (options->options & DHCPCD_LASTLEASE)
+ if (state->options & DHCPCD_LASTLEASE)
return -1;
- free_dhcp(dhcp);
- memset(dhcp, 0, sizeof(*dhcp));
} else if (errno == EINTR)
return 0;
}
#endif
#ifdef ENABLE_IPV4LL
- if (!(options->options & DHCPCD_TEST) &&
- options->options & DHCPCD_IPV4LL &&
- (!dhcp->address.s_addr ||
- (!IN_LINKLOCAL(ntohl(dhcp->address.s_addr)) &&
- !(options->options & DHCPCD_LASTLEASE))))
+ if (!(state->options & DHCPCD_TEST) &&
+ state->options & DHCPCD_IPV4LL &&
+ (!lease->addr.s_addr ||
+ (!IN_LINKLOCAL(ntohl(lease->addr.s_addr)) &&
+ !(state->options & DHCPCD_LASTLEASE))))
{
logger(LOG_INFO, "probing for an IPV4LL address");
- free_dhcp(dhcp);
- memset(dhcp, 0, sizeof(*dhcp));
- if (ipv4ll_get_address(iface, dhcp) == -1) {
- if (!state->daemonised)
+ if (ipv4ll_get_address(iface, lease) == -1) {
+ if (!(state->options & DHCPCD_DAEMONISED))
return -1;
/* start over */
state->xid = 0;
return 0;
}
- state->timeout = dhcp->renewaltime;
+ state->timeout = lease->renewaltime;
+ if (!state->dhcp)
+ state->dhcp = xmalloc(sizeof(*state->dhcp));
+ memset(state->dhcp, 0, sizeof(*state->dhcp));
+ state->dhcp->yiaddr = lease->addr.s_addr;
}
#endif
#if defined (ENABLE_INFO) || defined (ENABLE_IPV4LL)
- if (dhcp->address.s_addr) {
- if (!state->daemonised &&
- IN_LINKLOCAL(ntohl(dhcp->address.s_addr)))
+ if (lease->addr.s_addr) {
+ if (!(state->options & DHCPCD_DAEMONISED) &&
+ IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
logger(LOG_WARNING, "using IPV4LL address %s",
- inet_ntoa(dhcp->address));
- if (configure(options, iface, dhcp, true) == -1 &&
- !state->daemonised)
+ inet_ntoa(lease->addr));
+ if (configure(iface, state->dhcp, lease, options, 1) == -1 &&
+ !(state->options & DHCPCD_DAEMONISED))
return -1;
state->state = STATE_BOUND;
- if (!state->daemonised && options->options & DHCPCD_DAEMONISE) {
+ if (!(state->options & DHCPCD_DAEMONISED) &&
+ options->options & DHCPCD_DAEMONISE) {
switch (daemonise(state->pidfd)) {
case -1:
return -1;
case 0:
- state->daemonised = true;
+ state->options |= DHCPCD_DAEMONISED;
return 0;
default:
- state->persistent = true;
- state->forked = true;
+ state->options |= DHCPCD_PERSISTENT | DHCPCD_FORKED;
return -1;
}
}
- state->timeout = dhcp->renewaltime;
+ state->timeout = lease->renewaltime;
state->xid = 0;
return 0;
}
#endif
- if (!state->daemonised)
+ if (!(state->options & DHCPCD_DAEMONISED))
return -1;
}
do_socket(state, SOCKET_OPEN);
state->timeout = options->timeout;
iface->start_uptime = uptime ();
- if (dhcp->address.s_addr == 0) {
- if (!IN_LINKLOCAL(ntohl(iface->previous_address.s_addr)))
+ if (lease->addr.s_addr == 0) {
+ if (!IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
logger(LOG_INFO, "broadcasting for a lease");
- _send_message (state, DHCP_DISCOVER, options);
- } else if (options->options & DHCPCD_INFORM) {
+ send_message (state, DHCP_DISCOVER, options);
+ } else if (state->options & DHCPCD_INFORM) {
logger(LOG_INFO, "broadcasting inform for %s",
- inet_ntoa(dhcp->address));
- _send_message(state, DHCP_INFORM, options);
+ inet_ntoa(lease->addr));
+ send_message(state, DHCP_INFORM, options);
state->state = STATE_REQUESTING;
} else {
logger(LOG_INFO, "broadcasting for a lease of %s",
- inet_ntoa (dhcp->address));
- _send_message(state, DHCP_REQUEST, options);
+ inet_ntoa(lease->addr));
+ send_message(state, DHCP_REQUEST, options);
state->state = STATE_REQUESTING;
}
break;
case STATE_BOUND:
case STATE_RENEW_REQUESTED:
- if (IN_LINKLOCAL(ntohl (dhcp->address.s_addr))) {
- memset(&dhcp->address, 0, sizeof(dhcp->address));
+ if (IN_LINKLOCAL(ntohl(lease->addr.s_addr))) {
+ lease->addr.s_addr = 0;
state->state = STATE_INIT;
state->xid = 0;
break;
/* FALLTHROUGH */
case STATE_RENEWING:
iface->start_uptime = uptime();
- logger(LOG_INFO, "renewing lease of %s",
- inet_ntoa(dhcp->address));
+ logger(LOG_INFO, "renewing lease of %s",inet_ntoa(lease->addr));
do_socket(state, SOCKET_OPEN);
- _send_message(state, DHCP_REQUEST, options);
- state->timeout = dhcp->rebindtime - dhcp->renewaltime;
+ send_message(state, DHCP_REQUEST, options);
+ state->timeout = lease->rebindtime - lease->renewaltime;
state->state = STATE_REBINDING;
break;
case STATE_REBINDING:
logger(LOG_ERR, "lost lease, attemping to rebind");
- memset(&dhcp->address, 0, sizeof(dhcp->address));
+ lease->addr.s_addr = 0;
do_socket(state, SOCKET_OPEN);
if (state->xid == 0)
state->xid = (uint32_t)random();
- dhcp->serveraddress.s_addr = 0;
- _send_message(state, DHCP_REQUEST, options);
- state->timeout = dhcp->leasetime - dhcp->rebindtime;
+ lease->server.s_addr = 0;
+ send_message(state, DHCP_REQUEST, options);
+ state->timeout = lease->leasetime - lease->rebindtime;
state->state = STATE_REQUESTING;
break;
case STATE_REQUESTING:
state->timeout = 0;
break;
case STATE_RELEASED:
- dhcp->leasetime = 0;
+ lease->leasetime = 0;
break;
}
return 0;
}
-
static int
-handle_dhcp(struct if_state *state, int type, const struct options *options)
+handle_dhcp(struct if_state *state, struct dhcp_message *dhcp, const struct options *options)
{
struct timespec ts;
struct interface *iface = state->interface;
- struct dhcp *dhcp = state->dhcp;
+ struct dhcp_lease *lease = &state->lease;
char *addr;
+ struct in_addr saddr;
+ uint8_t type;
+ struct timeval tv;
+ int r;
+
+ if (get_option_uint8(&type, dhcp, DHCP_MESSAGETYPE) == -1) {
+ logger(LOG_ERR, "no DHCP type in message");
+ return -1;
+ }
/* We should restart on a NAK */
if (type == DHCP_NAK) {
- logger(LOG_INFO, "received NAK: %s", dhcp->message);
+ addr = get_option_string(dhcp, DHCP_MESSAGE);
+ logger(LOG_INFO, "received NAK: %s", addr);
+ free(addr);
+ free(dhcp);
state->state = STATE_INIT;
state->timeout = 0;
state->xid = 0;
- free_dhcp(dhcp);
- memset(dhcp, 0, sizeof(*dhcp));
+ lease->addr.s_addr = 0;
/* If we constantly get NAKS then we should slowly back off */
if (state->nakoff > 0) {
state->nakoff = 1;
if (type == DHCP_OFFER && state->state == STATE_INIT) {
- addr = strdup(inet_ntoa(dhcp->address));
- if (dhcp->servername[0])
+ lease->addr.s_addr = dhcp->yiaddr;
+ addr = xstrdup(inet_ntoa(lease->addr));
+ r = get_option_addr(&lease->server.s_addr, dhcp, DHCP_SERVERID);
+ if (dhcp->servername[0] && r == 0)
logger(LOG_INFO, "offered %s from %s `%s'",
- addr, inet_ntoa(dhcp->serveraddress),
+ addr, inet_ntoa(lease->server),
dhcp->servername);
- else
+ else if (r == 0)
logger(LOG_INFO, "offered %s from %s",
- addr, inet_ntoa(dhcp->serveraddress));
+ addr, inet_ntoa(lease->server));
+ else
+ logger(LOG_INFO, "offered %s", addr);
free(addr);
#ifdef ENABLE_INFO
if (options->options & DHCPCD_TEST) {
- write_info(iface, dhcp, options, false);
+ write_info(iface, dhcp, lease, options, 0);
errno = 0;
return -1;
}
#endif
- _send_message(state, DHCP_REQUEST, options);
+ free(dhcp);
+ send_message(state, DHCP_REQUEST, options);
state->state = STATE_REQUESTING;
return 0;
}
if (type == DHCP_OFFER) {
+ saddr.s_addr = dhcp->yiaddr;
logger(LOG_INFO, "got subsequent offer of %s, ignoring ",
- inet_ntoa(dhcp->address));
+ inet_ntoa(saddr));
+ free(dhcp);
return 0;
}
/* We should only be dealing with acks */
if (type != DHCP_ACK) {
logger(LOG_ERR, "%d not an ACK or OFFER", type);
+ free(dhcp);
return 0;
}
#ifdef ENABLE_ARP
if (options->options & DHCPCD_ARP &&
- iface->previous_address.s_addr != dhcp->address.s_addr)
+ iface->addr.s_addr != lease->addr.s_addr)
{
errno = 0;
- if (arp_claim(iface, dhcp->address)) {
+ if (arp_claim(iface, lease->addr) && errno != EINTR) {
+ free(dhcp);
do_socket(state, SOCKET_OPEN);
- _send_message(state, DHCP_DECLINE, options);
+ send_message(state, DHCP_DECLINE, options);
do_socket(state, SOCKET_CLOSED);
- free_dhcp(dhcp);
- memset(dhcp, 0, sizeof(*dhcp));
state->xid = 0;
state->timeout = 0;
state->state = STATE_INIT;
+ state->lease.addr.s_addr = 0;
/* RFC 2131 says that we should wait for 10 seconds
* before doing anything else */
ts.tv_nsec = 0;
nanosleep(&ts, NULL);
return 0;
- } else if (errno == EINTR)
- return 0;
+ } else if (errno == EINTR) {
+ free(dhcp);
+ return 0;
+ }
}
#endif
+ if (state->dhcp)
+ free(state->dhcp);
+ state->dhcp = dhcp;
+
if (options->options & DHCPCD_INFORM) {
if (options->request_address.s_addr != 0)
- dhcp->address = options->request_address;
+ lease->addr.s_addr = options->request_address.s_addr;
else
- dhcp->address = iface->previous_address;
+ lease->addr.s_addr = iface->addr.s_addr;
logger(LOG_INFO, "received approval for %s",
- inet_ntoa(dhcp->address));
- if (iface->previous_netmask.s_addr != dhcp->netmask.s_addr) {
- /* add_address */
- iface->previous_netmask.s_addr = dhcp->netmask.s_addr;
- }
+ inet_ntoa(lease->addr));
state->timeout = options->leasetime;
if (state->timeout == 0)
state->timeout = DEFAULT_LEASETIME;
state->state = STATE_INIT;
- } else if (dhcp->leasetime == ~0U) {
- dhcp->renewaltime = dhcp->rebindtime = dhcp->leasetime;
- state->timeout = 1; /* So we wait for infinity */
- logger(LOG_INFO, "leased %s for infinity",
- inet_ntoa(dhcp->address));
- state->state = STATE_BOUND;
} else {
- if (!dhcp->leasetime) {
- dhcp->leasetime = DEFAULT_LEASETIME;
- logger(LOG_INFO,
- "no lease time supplied, assuming %d seconds",
- dhcp->leasetime);
- }
- logger(LOG_INFO, "leased %s for %u seconds",
- inet_ntoa(dhcp->address), dhcp->leasetime);
+ if (gettimeofday(&tv, NULL) == 0)
+ lease->leasedfrom = tv.tv_sec;
- if (dhcp->rebindtime >= dhcp->leasetime) {
- dhcp->rebindtime = (dhcp->leasetime * 0.875);
- logger(LOG_ERR,
- "rebind time greater than lease "
- "time, forcing to %u seconds",
- dhcp->rebindtime);
- }
+ get_lease(lease, dhcp);
+
+ if (lease->leasetime == ~0U) {
+ lease->renewaltime = lease->rebindtime = lease->leasetime;
+ state->timeout = 1; /* So we wait for infinity */
+ logger(LOG_INFO, "leased %s for infinity",
+ inet_ntoa(lease->addr));
+ state->state = STATE_BOUND;
+ } else {
+ logger(LOG_INFO, "leased %s for %u seconds",
+ inet_ntoa(lease->addr), lease->leasetime);
+
+ if (lease->rebindtime >= lease->leasetime) {
+ lease->rebindtime = (lease->leasetime * 0.875);
+ logger(LOG_ERR,
+ "rebind time greater than lease "
+ "time, forcing to %u seconds",
+ lease->rebindtime);
+ }
- if (dhcp->renewaltime > dhcp->rebindtime) {
- dhcp->renewaltime = (dhcp->leasetime * 0.5);
- logger(LOG_ERR,
- "renewal time greater than rebind time, "
- "forcing to %u seconds",
- dhcp->renewaltime);
- }
+ if (lease->renewaltime > lease->rebindtime) {
+ lease->renewaltime = (lease->leasetime * 0.5);
+ logger(LOG_ERR,
+ "renewal time greater than rebind time, "
+ "forcing to %u seconds",
+ lease->renewaltime);
+ }
- if (!dhcp->renewaltime) {
- dhcp->renewaltime = (dhcp->leasetime * 0.5);
- logger(LOG_INFO,
- "no renewal time supplied, assuming %d seconds",
- dhcp->renewaltime);
- } else
- logger(LOG_DEBUG, "renew in %u seconds",
- dhcp->renewaltime);
-
- if (!dhcp->rebindtime) {
- dhcp->rebindtime = (dhcp->leasetime * 0.875);
- logger(LOG_INFO,
- "no rebind time supplied, assuming %d seconds",
- dhcp->rebindtime);
- } else
- logger(LOG_DEBUG, "rebind in %u seconds",
- dhcp->rebindtime);
+ if (!lease->renewaltime) {
+ lease->renewaltime = (lease->leasetime * 0.5);
+ logger(LOG_INFO,
+ "no renewal time supplied, assuming %d seconds",
+ lease->renewaltime);
+ } else
+ logger(LOG_DEBUG, "renew in %u seconds",
+ lease->renewaltime);
+
+ if (!lease->rebindtime) {
+ lease->rebindtime = (lease->leasetime * 0.875);
+ logger(LOG_INFO,
+ "no rebind time supplied, assuming %d seconds",
+ lease->rebindtime);
+ } else
+ logger(LOG_DEBUG, "rebind in %u seconds",
+ lease->rebindtime);
+
+ }
- state->timeout = dhcp->renewaltime;
+ state->timeout = lease->renewaltime;
state->state = STATE_BOUND;
}
state->xid = 0;
- if (configure(options, iface, dhcp, true) == -1 &&
- !state->daemonised)
+ if (configure(iface, dhcp, &state->lease, options, 1) == -1 &&
+ !(state->options & DHCPCD_DAEMONISED))
return -1;
- if (!state->daemonised && options->options & DHCPCD_DAEMONISE) {
+ if (!(state->options & DHCPCD_DAEMONISED)
+ && state->options & DHCPCD_DAEMONISE) {
switch (daemonise(state->pidfd)) {
case 0:
- state->daemonised = true;
+ state->options |= DHCPCD_DAEMONISED;
return 0;
case -1:
return -1;
default:
- state->persistent = true;
- state->forked = true;
+ state->options |= DHCPCD_PERSISTENT | DHCPCD_FORKED;
return -1;
}
}
handle_packet(struct if_state *state, const struct options *options)
{
struct interface *iface = state->interface;
- bool valid = false;
- int type;
- struct dhcp *new_dhcp;
- struct dhcp_message message;
+ struct dhcp_message *dhcp;
/* Allocate our buffer space for BPF.
* We cannot do this until we have opened our socket as we don't
* 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. */
- memset(&message, 0, sizeof(message));
- new_dhcp = xmalloc(sizeof(*new_dhcp));
+ dhcp = xmalloc(sizeof(*dhcp));
do {
- if (get_packet(iface, (unsigned char *)&message,
+ if (get_packet(iface, (uint8_t *)dhcp,
state->buffer,
&state->buffer_len, &state->buffer_pos) == -1)
break;
- if (state->xid != message.xid) {
+ if (state->xid != dhcp->xid) {
logger(LOG_DEBUG,
"ignoring packet with xid 0x%x as"
" it's not ours (0x%x)",
- message.xid, state->xid);
+ dhcp->xid, state->xid);
continue;
}
-
- logger(LOG_DEBUG, "got a packet with xid 0x%x", message.xid);
- memset(new_dhcp, 0, sizeof(*new_dhcp));
- type = parse_dhcpmessage(new_dhcp, &message);
- if (type == -1) {
- logger(LOG_ERR, "failed to parse packet");
- free_dhcp(new_dhcp);
- /* We don't abort on this, so return zero */
+ if (handle_dhcp(state, dhcp, options) == 0)
return 0;
- }
-
- /* If we got here then the DHCP packet is valid and appears to
- * be for us, so let's clear the buffer as we don't care about
- * any more DHCP packets at this point. */
- valid = true;
- break;
} while (state->buffer_pos != 0);
- /* No packets for us, so wait until we get one */
- if (!valid) {
- free(new_dhcp);
- return 0;
- }
-
- /* new_dhcp is now our master DHCP message */
- free_dhcp(state->dhcp);
- free(state->dhcp);
- state->dhcp = new_dhcp;
- new_dhcp = NULL;
+ if (state->options & DHCPCD_FORKED)
+ return -1;
- return handle_dhcp(state, type, options);
+ free(dhcp);
+ return 0;
}
int
hwaddr_ntoa(iface->hwaddr, iface->hwlen));
state = xzalloc(sizeof(*state));
- state->dhcp = xzalloc(sizeof(*state->dhcp));
state->pidfd = pidfd;
state->interface = iface;
- if (!client_setup(state, options))
+ if (client_setup(state, options) == -1)
goto eexit;
-
if (signal_init() == -1)
goto eexit;
if (signal_setup() == -1)
/* We should always handle our signals first */
if ((sig = (signal_read(&fds[POLLFD_SIGNAL]))) != -1) {
- if (handle_signal(sig, state, options))
- retval = 0;
- else
- retval = -1;
+ retval = handle_signal(sig, state, options);
} else if (retval == 0)
retval = handle_timeout(state, options);
else if (retval > 0 &&
if (iface) {
do_socket(state, SOCKET_CLOSED);
drop_config(state, options);
- free_route(iface->previous_routes);
+ free_routes(iface->routes);
free(iface->clientid);
free(iface);
}
if (state) {
- if (state->forked)
+ if (state->options & DHCPCD_FORKED)
retval = 0;
-
- if (state->daemonised)
+ if (state->options & DHCPCD_DAEMONISED)
unlink(options->pidfile);
-
- free_dhcp(state->dhcp);
- free(state->dhcp);
free(state->buffer);
+ free(state->dhcp);
free(state);
}
unsigned long seed;
fd = open("/dev/urandom", 0);
- if (fd == -1 || read(fd, &seed, sizeof(seed)) == -1) {
- logger(LOG_WARNING, "Could not read from /dev/urandom: %s",
- strerror(errno));
+ if (fd == -1 || read(fd, &seed, sizeof(seed)) == -1)
seed = time(0);
- }
if (fd >= 0)
close(fd);
{
int fd;
- if ((fd = open("/dev/null", O_RDWR)) == -1) {
- logger(LOG_ERR, "open `/dev/null': %s", strerror(errno));
+ if ((fd = open("/dev/null", O_RDWR)) == -1)
return -1;
- }
dup2(fd, fileno(stdin));
dup2(fd, fileno(stdout));
posix_clock_set = 1;
}
- if (clock_gettime(posix_clock, &ts) == -1) {
- logger(LOG_ERR, "clock_gettime: %s", strerror(errno));
+ if (clock_gettime(posix_clock, &ts) == -1)
return -1;
- }
tp->tv_sec = ts.tv_sec;
tp->tv_usec = ts.tv_nsec / 1000;
return 0;
#else
- if (gettimeofday(tp, NULL) == -1) {
- logger(LOG_ERR, "gettimeofday: %s", strerror(errno));
- return -1;
- }
- return 0;
+ return gettimeofday(tp, NULL);
#endif
}
if (get_time(&tp) == -1)
return -1;
-
return tp.tv_sec;
}
-void
+int
writepid(int fd, pid_t pid)
{
char spid[16];
ssize_t len;
- if (ftruncate(fd, (off_t)0) == -1) {
- logger(LOG_ERR, "ftruncate: %s", strerror(errno));
- } else {
- snprintf(spid, sizeof(spid), "%u", pid);
- len = pwrite(fd, spid, strlen(spid), (off_t)0);
- if (len != (ssize_t)strlen(spid))
- logger(LOG_ERR, "pwrite: %s", strerror(errno));
- }
+ if (ftruncate(fd, (off_t)0) == -1)
+ return -1;
+ snprintf(spid, sizeof(spid), "%u", pid);
+ len = pwrite(fd, spid, strlen(spid), (off_t)0);
+ if (len != (ssize_t)strlen(spid))
+ return -1;
+ return 0;
}
void *
if (value)
return value;
- logger (LOG_ERR, "memory exhausted");
+ logger(LOG_ERR, "memory exhausted");
exit (EXIT_FAILURE);
/* NOTREACHED */
}
#define COMMON_H
/* string.h pulls in features.h so the below define checks work */
+#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <string.h>
ssize_t get_line(char **, size_t *, FILE *);
int get_time(struct timeval *);
time_t uptime(void);
-void writepid(int, pid_t);
+int writepid(int, pid_t);
void *xrealloc(void *, size_t);
void *xmalloc(size_t);
void *xzalloc(size_t);
#define ENABLE_NTP
#define ENABLE_NIS
#define ENABLE_INFO
-/* Define this to enable some compatability with 1.x and 2.x info files */
-/* #define ENABLE_INFO_COMPAT */
/* IPV4LL, aka ZeroConf, aka APIPA, aka RFC 3927.
* Needs ARP. */
/* resolvconf is framework for multiple interfaces to manage resolv.conf */
#define ENABLE_RESOLVCONF
-/* Some systems do not have a working fork.
- * The Makefile will attempt to work it out, but if it fails to feel free to
- * define it here. */
+/* Some systems do not have a working fork. */
/* #define THERE_IS_NO_FORK */
/* Packname name and pathname definitions. */
#ifndef INFODIR
# define INFODIR "/var/db"
#endif
+#define LEASEFILE INFODIR "/" PACKAGE "-%s.lease"
#define INFOFILE INFODIR "/" PACKAGE "-%s.info"
#define DUIDFILE INFODIR "/" PACKAGE ".duid"
* SUCH DAMAGE.
*/
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/param.h>
-#include <sys/socket.h>
#include <sys/stat.h>
+#include <netinet/in.h>
#include <arpa/inet.h>
-#include <netinet/in.h>
-#ifdef __linux__
-# include <netinet/ether.h>
-#endif
-#include <string.h>
+#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <resolv.h>
#include "common.h"
#include "configure.h"
#include "dhcp.h"
-#ifdef ENABLE_INFO
-# include "info.h"
-#endif
-#include "if.h"
#include "dhcpcd.h"
#include "logger.h"
+#include "net.h"
#include "signal.h"
#include "socket.h"
-static void
-log_route(const struct in_addr *destination,
- const struct in_addr *netmask,
- const struct in_addr *gateway, _unused int metric, int del)
-{
- char *dstd = xstrdup(inet_ntoa(*destination));
-
-#ifdef __linux__
-#define METRIC " metric %d"
-#else
-#define METRIC ""
-#endif
-
- if (gateway->s_addr == destination->s_addr ||
- gateway->s_addr == INADDR_ANY)
- logger(LOG_INFO, "%s route to %s/%d" METRIC,
- del ? "removing" : "adding",
- dstd, inet_ntocidr(*netmask)
-#ifdef __linux__
- , metric
-#endif
- );
- else if (destination->s_addr == INADDR_ANY)
- logger(LOG_INFO, "%s default route via %s" METRIC,
- del ? "removing" : "adding",
- inet_ntoa(*gateway)
-
-#ifdef __linux__
- , metric
-#endif
- );
- else
- logger(LOG_INFO, "%s route to %s/%d via %s" METRIC,
- del ? "removing" : "adding",
- dstd, inet_ntocidr(*netmask), inet_ntoa(*gateway)
-#ifdef __linux__
- , metric
-#endif
- );
-
- free(dstd);
-}
-
-static int
-a_address(const char *ifname, const struct in_addr *address,
- const struct in_addr *netmask, const struct in_addr *broadcast)
-{
- int retval;
-
- logger(LOG_INFO, "adding IP address %s/%d",
- inet_ntoa(*address), inet_ntocidr(*netmask));
- retval = add_address(ifname, address, netmask, broadcast);
- if (retval == -1)
- logger(LOG_ERR, "if_address: %s", strerror(errno));
- return retval;
-}
-
-static int
-d_address(const char *ifname,
- const struct in_addr *address, const struct in_addr *netmask)
-{
- int retval;
-
- logger(LOG_INFO, "removing IP address %s/%d",
- inet_ntoa(*address), inet_ntocidr(*netmask));
- retval = del_address(ifname, address, netmask);
- if (retval == -1)
- logger(LOG_ERR, "del_address: %s", strerror(errno));
- return retval;
-}
-
-
-static int
-a_route(const char *ifname, const struct in_addr *destination,
- const struct in_addr *netmask, const struct in_addr *gateway,
- int metric)
-{
- int retval;
-
- log_route(destination, netmask, gateway, metric, 0);
- retval = add_route(ifname, destination, netmask, gateway, metric);
- if (retval == -1)
- logger(LOG_ERR, "add_route: %s", strerror(errno));
- return retval;
-}
-
-static int
-d_route(const char *ifname, const struct in_addr *destination,
- const struct in_addr *netmask, const struct in_addr *gateway,
- int metric)
-{
- int retval;
-
- log_route(destination, netmask, gateway, metric, 1);
- retval = del_route(ifname, destination, netmask, gateway, metric);
- if (retval == -1)
- logger(LOG_ERR, "del_route: %s", strerror(errno));
- return retval;
-}
-
-#ifdef ENABLE_RESOLVCONF
-static int
-file_in_path(const char *file)
-{
- char *p = getenv("PATH");
- char *path;
- char *token;
- struct stat s;
- char mypath[PATH_MAX];
- int retval = -1;
-
- if (!p) {
- errno = ENOENT;
- return -1;
- }
-
- path = strdup(p);
- p = path;
- while ((token = strsep(&p, ":"))) {
- snprintf(mypath, PATH_MAX, "%s/%s", token, file);
- if (stat(mypath, &s) == 0) {
- retval = 0;
- break;
- }
- }
- free(path);
- return(retval);
-}
-#endif
-
-/* IMPORTANT: Ensure that the last parameter is NULL when calling */
-static int
+int
exec_cmd(const char *cmd, const char *args, ...)
{
va_list va;
return ret;
}
+/* IMPORTANT: Ensure that the last parameter is NULL when calling */
static void
exec_script(const char *script, _unused const char *infofile, const char *arg)
{
#endif
}
-static int
-make_resolv(const char *ifname, const struct dhcp *dhcp)
+static char *
+lookuphostname(in_addr_t addr)
{
- FILE *f = NULL;
- struct address *address;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ } su;
+ socklen_t salen;
+ char *name;
+ struct addrinfo hints;
+ struct addrinfo *res = NULL;
+ int r;
-#ifdef ENABLE_RESOLVCONF
- char *resolvconf = NULL;
- size_t len;
+ name = xmalloc(sizeof(char) * NI_MAXHOST);
+ salen = sizeof(su.sa);
+ memset(&su.sa, 0, salen);
+ su.sin.sin_family = AF_INET;
+ su.sin.sin_addr.s_addr = addr;
- if (file_in_path("resolvconf") == 0) {
- len = strlen("resolvconf -a ") + strlen(ifname) + 1;
- resolvconf = xmalloc(sizeof(char) * len);
- snprintf(resolvconf, len, "resolvconf -a %s", ifname);
- if ((f = popen(resolvconf , "w")))
- logger(LOG_DEBUG,
- "sending DNS information to resolvconf");
- else if (errno == EEXIST)
- logger(LOG_ERR, "popen: %s", strerror(errno));
-
- if (ferror(f))
- logger(LOG_ERR, "ferror");
- free(resolvconf);
- }
+ r = getnameinfo(&su.sa, salen, name, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
+ if (r != 0) {
+ free(name);
+ switch (r) {
+#ifdef EAI_NODATA
+ case EAI_NODATA: /* FALLTHROUGH */
#endif
- if (!f) {
- logger(LOG_DEBUG, "writing "RESOLVFILE);
- if (!(f = fopen(RESOLVFILE, "w")))
- logger(LOG_ERR, "fopen `%s': %s",
- RESOLVFILE, strerror(errno));
+ case EAI_NONAME:
+ errno = ENOENT;
+ break;
+ case EAI_SYSTEM:
+ break;
+ default:
+ errno = EIO;
+ break;
+ }
+ return NULL;
+ }
+
+ /* Check for a malicious PTR record */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_NUMERICHOST;
+ r = getaddrinfo(name, "0", &hints, &res);
+ if (res)
+ freeaddrinfo(res);
+ if (r == 0 || !*name) {
+ free(name);
+ errno = ENOENT;
+ return NULL;
}
- if (!f)
- return -1;
+ return name;
+}
- fprintf(f, "# Generated by dhcpcd for interface %s\n", ifname);
- if (dhcp->dnssearch)
- fprintf(f, "search %s\n", dhcp->dnssearch);
- else if (dhcp->dnsdomain) {
- fprintf(f, "search %s\n", dhcp->dnsdomain);
- }
+static int
+configure_hostname(const struct dhcp_message *dhcp, in_addr_t addr, int h)
+{
+ char *newhostname;
+ char *curhostname;
- STAILQ_FOREACH(address, dhcp->dnsservers, entries)
- fprintf(f, "nameserver %s\n", inet_ntoa(address->address));
+ curhostname = xmalloc(sizeof(char) * MAXHOSTNAMELEN);
+ *curhostname = '\0';
-#ifdef ENABLE_RESOLVCONF
- if (resolvconf)
- pclose(f);
- else
-#endif
- fclose(f);
+ gethostname(curhostname, MAXHOSTNAMELEN);
+ if (h ||
+ strlen(curhostname) == 0 ||
+ strcmp(curhostname, "(none)") == 0 ||
+ strcmp(curhostname, "localhost") == 0)
+ {
+ newhostname = get_option_string(dhcp, DHCP_HOSTNAME);
+ if (!newhostname || h)
+ newhostname = lookuphostname(addr);
- /* Refresh the local resolver */
- res_init();
+ if (newhostname) {
+ logger(LOG_INFO, "setting hostname to `%s'", newhostname);
+ sethostname(newhostname, (int)strlen(newhostname));
+ free(newhostname);
+ }
+ }
+
+ free(curhostname);
return 0;
}
-#ifdef ENABLE_RESOLVCONF
-static void
-restore_resolv(const char *ifname)
+#ifdef ENABLE_NIS
+#define PREFIXSIZE 300
+static int
+configure_nis(const char *ifname, const struct dhcp_message *dhcp)
{
- if (file_in_path("resolvconf") == 0) {
- logger(LOG_DEBUG, "removing information from resolvconf");
- exec_cmd("resolvconf", "-d", ifname, (char *)NULL);
+ const uint8_t *servers;
+ char *domain;
+ FILE *f;
+ char *prefix;
+ const uint8_t *e;
+ uint8_t l;
+ struct in_addr addr;
+
+ servers = get_option(dhcp, DHCP_NISSERVER);
+ domain = get_option_string(dhcp, DHCP_NISDOMAIN);
+
+ if (!servers && !domain) {
+ if (errno == ENOENT)
+ return 0;
+ return -1;
}
-}
-#endif
-static bool
-in_routes(const struct route_head *routes, struct rt *route)
-{
- const struct rt *r;
+ if (!(f = fopen(NISFILE, "w")))
+ return -1;
- if (! routes)
- return false;
-
- STAILQ_FOREACH (r, routes, entries)
- if (r->destination.s_addr == route->destination.s_addr &&
- r->netmask.s_addr == route->netmask.s_addr &&
- r->gateway.s_addr == route->gateway.s_addr)
- return true;
+ prefix = xmalloc(sizeof(char) * PREFIXSIZE);
+ *prefix = '\0';
+ fprintf(f, "# Generated by dhcpcd for interface %s\n", ifname);
+
+ if (domain) {
+ setdomainname(domain, (int)strlen(domain));
+ if (servers)
+ snprintf(prefix, PREFIXSIZE, "domain %s server",
+ domain);
+ else
+ fprintf(f, "domain %s broadcast\n", domain);
+ free(domain);
+ }
+ else
+ strlcpy(prefix, "ypserver", PREFIXSIZE);
+
+ if (servers) {
+ l = *servers++;
+ e = servers + l;
+ for(; servers < e; servers += sizeof(uint32_t)) {
+ memcpy(&addr.s_addr, servers, sizeof(uint32_t));
+ fprintf(f, "%s %s\n", prefix, inet_ntoa(addr));
+ }
+ }
+
+ free(prefix);
+ fclose(f);
- return false;
+ return exec_cmd(NISSERVICE, NISRESTARTARGS, (char *)NULL);
}
+#endif
#ifdef ENABLE_NTP
-static bool
-in_addresses(const struct address_head *addresses, struct in_addr address)
+static int
+in_addresses(const uint8_t *addresses, uint32_t addr)
{
- const struct address *addr;
-
- STAILQ_FOREACH (addr, addresses, entries)
- if (addr->address.s_addr == address.s_addr)
- return true;
+ uint8_t l = *addresses++;
+ const uint8_t *e = addresses + l;
+ uint32_t a;
- return false;
+ for (; addresses < e; addresses += sizeof(a)) {
+ memcpy(&a, addresses, sizeof(a));
+ if (a == addr)
+ return 0;
+ }
+ return -1;
}
static int
-_make_ntp(const char *file, const char *ifname, const struct dhcp *dhcp)
+_make_ntp(const char *file, const char *ifname, const uint8_t *ntp)
{
FILE *f;
- struct address *address;
char *a;
char *line = NULL;
size_t len = 0;
- int tomatch = 0;
char *token;
- bool ntp = false;
struct in_addr addr;
-
- STAILQ_FOREACH(address, dhcp->ntpservers, entries)
- tomatch++;
+ uint8_t tomatch = *ntp;
+ const uint8_t *e;
+#ifdef NTPFILE
+ int ntpfile;
+#endif
/* Check that we really need to update the servers.
* We do this because ntp has to be restarted to
* work with a changed config. */
if (!(f = fopen(file, "r"))) {
- if (errno != ENOENT) {
- logger(LOG_ERR, "fopen `%s': %s", file, strerror(errno));
+ if (errno != ENOENT)
return -1;
- }
} else {
while (tomatch != 0 && (get_line(&line, &len, f))) {
a = line;
token = strsep(&a, " ");
- if (! token || strcmp(token, "server") != 0)
+ if (!token || strcmp(token, "server") != 0)
continue;
if ((token = strsep(&a, " \n")) == NULL)
continue;
if (inet_aton(token, &addr) == 1 &&
- in_addresses(dhcp->ntpservers, addr))
+ in_addresses(ntp, addr.s_addr) == 0)
tomatch--;
}
fclose(f);
/* File has the same name servers that we do,
* so no need to restart ntp */
- if (tomatch == 0) {
- logger(LOG_DEBUG, "%s already configured, skipping",
- file);
+ if (tomatch == 0)
return 0;
- }
}
- logger(LOG_DEBUG, "writing %s", file);
- if (!(f = fopen(file, "w"))) {
- logger(LOG_ERR, "fopen `%s': %s", file, strerror(errno));
+ if (!(f = fopen(file, "w")))
return -1;
- }
fprintf(f, "# Generated by dhcpcd for interface %s\n", ifname);
#ifdef NTPFILE
- if (strcmp(file, NTPFILE) == 0) {
- ntp = true;
+ if ((ntpfile = strcmp(file, NTPFILE)) == 0) {
fprintf(f, "restrict default noquery notrust nomodify\n");
fprintf(f, "restrict 127.0.0.1\n");
}
#endif
- STAILQ_FOREACH(address, dhcp->ntpservers, entries) {
- a = inet_ntoa(address->address);
- if (ntp)
+ tomatch = *ntp++;
+ e = ntp + tomatch;
+ for (; ntp < e; ntp += sizeof(uint32_t)) {
+ memcpy(&addr.s_addr, ntp, sizeof(uint32_t));
+ a = inet_ntoa(addr);
+#ifdef NTPFILE
+ if (ntpfile == 0)
fprintf(f, "restrict %s nomodify notrap noquery\n", a);
+#endif
fprintf(f, "server %s\n", a);
}
fclose(f);
return 1;
}
+#endif
static int
-make_ntp(const char *ifname, const struct dhcp *dhcp)
+configure_ntp(const char *ifname, const struct dhcp_message *dhcp)
{
- /* On some systems we have only have one ntp service, but we don't
- * know which configuration file we're using. So we need to write
- * to both and restart accordingly. */
+ const uint8_t *ntp = get_option(dhcp, DHCP_NTPSERVER);
+ int restart = 0;
+ int r;
- bool restart_ntp = false;
- bool restart_openntp = false;
- int retval = 0;
+ if (!ntp) {
+ if (errno == ENOENT)
+ return 0;
+ return -1;
+ }
#ifdef NTPFILE
- if (_make_ntp(NTPFILE, ifname, dhcp) > 0)
- restart_ntp = true;
+ r = _make_ntp(NTPFILE, ifname, ntp);
+ if (r == -1)
+ return -1;
+ if (r > 0)
+ restart |= 1;
#endif
#ifdef OPENNTPFILE
- if (_make_ntp(OPENNTPFILE, ifname, dhcp) > 0)
- restart_openntp = true;
-#endif
-
-#ifdef NTPSERVICE
- if (restart_ntp) {
-#ifdef NTPCHECK
- if (system(NTPCHECK) == 0)
-#endif
- retval += exec_cmd(NTPSERVICE, NTPRESTARTARGS,
- (char *)NULL);
- }
-#endif
-
-#if defined (NTPSERVICE) && defined (OPENNTPSERVICE)
- if (restart_openntp &&
- (strcmp(NTPSERVICE, OPENNTPSERVICE) != 0 || !restart_ntp))
- {
-#ifdef OPENNTPCHECK
- if (system(OPENNTPCHECK) == 0)
-#endif
- retval += exec_cmd (OPENNTPSERVICE,
- OPENNTPRESTARTARGS, (char *)NULL);
- }
-#elif defined (OPENNTPSERVICE) && ! defined (NTPSERVICE)
- if (restart_openntp) {
-#ifdef OPENNTPCHECK
- if (system(OPENNTPCHECK) == 0)
-#endif
- retval += exec_cmd(OPENNTPSERVICE,
- OPENNTPRESTARTARGS, (char *) NULL);
- }
+ r = _make_ntp(OPENNTPFILE, ifname, ntp);
+ if (r == -1)
+ return -1;
+ if (r > 0)
+ restart |= 2;
#endif
- return retval;
+ if (restart)
+ return exec_cmd(NTPSERVICE, NTPRESTARTARGS, (char *)NULL);
+ return 0;
}
-#endif
-#ifdef ENABLE_NIS
-#define PREFIXSIZE 256
+#ifdef ENABLE_RESOLVCONF
static int
-make_nis(const char *ifname, const struct dhcp *dhcp)
+file_in_path(const char *file)
{
- FILE *f;
- struct address *address;
- char *prefix;
+ char *p = getenv("PATH");
+ char *path;
+ char *token;
+ struct stat s;
+ char mypath[PATH_MAX];
+ int retval = -1;
- logger(LOG_DEBUG, "writing "NISFILE);
- if (!(f = fopen(NISFILE, "w"))) {
- logger(LOG_ERR, "fopen `%s': %s", NISFILE, strerror(errno));
+ if (!p) {
+ errno = ENOENT;
return -1;
}
- prefix = xmalloc(sizeof(char) * PREFIXSIZE);
- *prefix = '\0';
- fprintf(f, "# Generated by dhcpcd for interface %s\n", ifname);
-
- if (dhcp->nisdomain) {
- setdomainname(dhcp->nisdomain, (int)strlen(dhcp->nisdomain));
-
- if (dhcp->nisservers)
- snprintf(prefix, PREFIXSIZE, "domain %s server",
- dhcp->nisdomain);
- else
- fprintf(f, "domain %s broadcast\n", dhcp->nisdomain);
+ path = strdup(p);
+ p = path;
+ while ((token = strsep(&p, ":"))) {
+ snprintf(mypath, PATH_MAX, "%s/%s", token, file);
+ if (stat(mypath, &s) == 0) {
+ retval = 0;
+ break;
+ }
}
- else
- snprintf(prefix, PREFIXSIZE, "%s", "ypserver");
-
- NSTAILQ_FOREACH(address, dhcp->nisservers, entries)
- fprintf(f, "%s %s\n", prefix, inet_ntoa(address->address));
-
- free(prefix);
- fclose(f);
-
-#ifdef NISCHECK
- if (system(NISCHECK) == 0)
-#endif
- exec_cmd(NISSERVICE, NISRESTARTARGS, (char *)NULL);
- return 0;
+ free(path);
+ return(retval);
}
#endif
-static char *
-lookuphostname(char *hostname, const struct dhcp *dhcp,
- const struct options *options)
+static int
+configure_resolv(const char *ifname, const struct dhcp_message *dhcp)
{
- union {
- struct sockaddr sa;
- struct sockaddr_in sin;
- } su;
- socklen_t salen;
- char *addr;
- struct addrinfo hints;
- struct addrinfo *res = NULL;
- int result;
- char *p, *s, *sp, *t;
+ FILE *f = NULL;
+ const uint8_t *servers;
+ const uint8_t *e;
+ uint8_t l;
+ struct in_addr addr;
+ char *p;
- logger(LOG_DEBUG, "Looking up hostname via DNS");
- addr = xmalloc(sizeof(char) * NI_MAXHOST);
- salen = sizeof(su.sa);
- memset(&su.sa, 0, salen);
- su.sin.sin_family = AF_INET;
- memcpy(&su.sin.sin_addr, &dhcp->address, sizeof(su.sin.sin_addr));
+#ifdef ENABLE_RESOLVCONF
+ char *resolvconf = NULL;
+ size_t len;
+#endif
- if ((result = getnameinfo(&su.sa, salen, addr, NI_MAXHOST,
- NULL, 0, NI_NAMEREQD)) != 0)
- {
- logger(LOG_ERR,
- "Failed to lookup hostname via DNS: %s",
- gai_strerror (result));
- free(addr);
- return NULL;
+ servers = get_option(dhcp, DHCP_DNSSERVER);
+ if (!servers) {
+ if (errno == ENOENT)
+ return 0;
+ return -1;
}
-
- /* Check for a malicious PTR record */
- memset(&hints, 0, sizeof(hints));
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_flags = AI_NUMERICHOST;
- result = getaddrinfo(addr, "0", &hints, &res);
- if (res)
- freeaddrinfo(res);
- if (result == 0)
- logger (LOG_ERR, "malicious PTR record detected");
- if (result == 0 || !*addr) {
- free(addr);
- return NULL;
+
+#ifdef ENABLE_RESOLVCONF
+ if (file_in_path("resolvconf") == 0) {
+ len = strlen("resolvconf -a ") + strlen(ifname) + 1;
+ resolvconf = xmalloc(sizeof(char) * len);
+ snprintf(resolvconf, len, "resolvconf -a %s", ifname);
+ f = popen(resolvconf , "w");
+ free(resolvconf);
}
+#endif
+ if (!f && !(f = fopen(RESOLVFILE, "w")))
+ return -1;
- p = strchr(addr, '.');
+ fprintf(f, "# Generated by dhcpcd for interface %s\n", ifname);
+ p = get_option_string(dhcp, DHCP_DNSSEARCH);
+ if (!p)
+ p = get_option_string(dhcp, DHCP_DNSDOMAIN);
if (p) {
- switch (options->dohostname) {
- case 1: /* -H */
- case 4: /* -HHHH */
- break;
- case 2: /* -HH */
- case 5: /* -HHHHH */
- /* Strip out the domain if it matches */
- p++;
- if (*p && dhcp->dnssearch) {
- s = sp = xstrdup(dhcp->dnssearch);
- while ((t = strsep(&sp, " ")))
- if (strcmp(t, p) == 0) {
- *--p = '\0';
- break;
- }
- free (s);
- } else if (dhcp->dnsdomain) {
- if (strcmp(dhcp->dnsdomain, p) == 0)
- *--p = '\0';
- }
- break;
- case 3: /* -HHH */
- case 6: /* -HHHHHH */
- /* Just strip the domain */
- *p = '\0';
- break;
- default: /* Too many H! */
- break;
- }
+ fprintf(f, "search %s\n", p);
+ free(p);
}
- strlcpy(hostname, addr, MAXHOSTNAMELEN);
- free(addr);
- return hostname;
+ l = *servers++;
+ e = servers + l;
+ for (; servers < e; servers += sizeof(uint32_t)) {
+ memcpy(&addr.s_addr, servers, sizeof(uint32_t));
+ fprintf(f, "nameserver %s\n", inet_ntoa(addr));
+ }
+
+#ifdef ENABLE_RESOLVCONF
+ if (resolvconf)
+ pclose(f);
+ else
+#endif
+ fclose(f);
+
+ /* Refresh the local resolver */
+ res_init();
+ return 0;
}
-int
-configure(const struct options *options, struct interface *iface,
- const struct dhcp *dhcp, bool up)
+#ifdef ENABLE_RESOLVCONF
+static int
+restore_resolv(const char *ifname)
{
- struct rt *route = NULL;
- struct route_head *new_routes = NULL;
- struct rt *new_route = NULL;
- char *newhostname = NULL;
- char *curhostname = NULL;
- int remember;
- unsigned short mtu;
-#if defined(ENABLE_IPV4LL) || defined(__linux__)
- struct in_addr dest;
- struct in_addr mask;
- struct in_addr gate;
-#endif
+ if (file_in_path("resolvconf") != 0)
+ return 0;
-#ifdef ENABLE_IPV4LL
- bool haslinklocal = false;
-#endif
-#ifdef THERE_IS_NO_FORK
- int skip = 0;
- size_t skiplen;
- char *skipp;
+ return exec_cmd("resolvconf", "-d", ifname, (char *)NULL);
+}
#endif
- if (dhcp->address.s_addr == 0)
- up = 0;
- /* Remove old routes.
- * Always do this as the interface may have >1 address not added by us
- * so the routes we added may still exist. */
- NSTAILQ_FOREACH(route, iface->previous_routes, entries)
- if ((route->destination.s_addr ||
- options->options & DHCPCD_GATEWAY) &&
- (!up || !in_routes(dhcp->routes, route)))
- d_route(iface->name, &route->destination,
- &route->netmask, &route->gateway,
- options->metric);
- /* If we aren't up, then reset the interface as much as we can */
- if (!up) {
- if (iface->previous_routes) {
- free_route(iface->previous_routes);
- iface->previous_routes = NULL;
- }
+static struct rt *
+reverse_routes(struct rt *routes)
+{
+ struct rt *rt;
+ struct rt *rtn = NULL;
+
+ while (routes) {
+ rt = routes->next;
+ routes->next = rtn;
+ rtn = routes;
+ routes = rt;
+ }
+ return rtn;
+}
- /* Restore the original MTU value */
- if (iface->mtu && iface->previous_mtu != iface->mtu) {
- set_mtu(iface->name, iface->mtu);
- iface->previous_mtu = iface->mtu;
- }
+static int
+delete_route(const char *iface, struct rt *rt, int metric)
+{
+ char *addr;
+ int retval;
-#ifdef ENABLE_INFO
- /* If we haven't created an info file, do so now */
- if (!dhcp->frominfo)
- write_info(iface, dhcp, options, false);
-#endif
+ addr = xstrdup(inet_ntoa(rt->dest));
+ logger(LOG_DEBUG, "removing route %s/%d via %s",
+ addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
+ free(addr);
+ retval = del_route(iface, &rt->dest, &rt->net, &rt->gate, metric);
+ if (retval != 0)
+ logger(LOG_ERR," del_route: %s", strerror(errno));
+ return retval;
- /* Only reset things if we had set them before */
- if (iface->previous_address.s_addr != 0) {
- if (!(options->options & DHCPCD_KEEPADDRESS)) {
- d_address(iface->name,
- &iface->previous_address,
- &iface->previous_netmask);
- memset(&iface->previous_address,
- 0, sizeof (iface->previous_address));
- memset(&iface->previous_netmask,
- 0, sizeof (iface->previous_netmask));
- }
- }
+}
-#ifdef ENABLE_RESOLVCONF
- restore_resolv(iface->name);
-#endif
- exec_script(options->script, iface->infofile, "down");
+static int
+delete_routes(struct interface *iface, int metric)
+{
+ struct rt *rt;
+ struct rt *rtn;
+ int retval = 0;
- return 0;
+ rt = reverse_routes(iface->routes);
+ while (rt) {
+ rtn = rt->next;
+ retval += delete_route(iface->name, rt, metric);
+ free(rt);
+ rt = rtn;
}
+ iface->routes = NULL;
- /* Set the MTU requested.
- * If the DHCP server no longer sends one OR it's invalid then
- * we restore the original MTU */
- if (options->options & DHCPCD_MTU) {
- mtu = iface->mtu;
- if (dhcp->mtu)
- mtu = dhcp->mtu;
+ return retval;
+}
- if (mtu != iface->previous_mtu) {
- if (set_mtu(iface->name, mtu) == 0)
- iface->previous_mtu = mtu;
- }
+static int
+in_routes(const struct rt *routes, const struct rt *rt)
+{
+ while (routes) {
+ if (routes->dest.s_addr == rt->dest.s_addr &&
+ routes->net.s_addr == rt->net.s_addr &&
+ routes->gate.s_addr == rt->gate.s_addr)
+ return 0;
+ routes = routes->next;
}
+ return -1;
+}
- /* This also changes netmask */
- if (!(options->options & DHCPCD_INFORM) ||
- !has_address (iface->name, dhcp->address))
- if (a_address(iface->name, &dhcp->address, &dhcp->netmask,
- &dhcp->broadcast) == -1 &&
- errno != EEXIST)
- return false;
-
- /* Now delete the old address if different */
- if (iface->previous_address.s_addr != dhcp->address.s_addr &&
- iface->previous_address.s_addr != 0 &&
- !(options->options & DHCPCD_KEEPADDRESS))
- d_address(iface->name,
- &iface->previous_address, &iface->previous_netmask);
-
-#ifdef __linux__
- /* On linux, we need to change the subnet route to have our metric. */
- if (iface->previous_address.s_addr != dhcp->address.s_addr &&
- options->metric > 0 &&
- dhcp->netmask.s_addr != INADDR_BROADCAST)
- {
- dest.s_addr = dhcp->address.s_addr & dhcp->netmask.s_addr;
- gate.s_addr = 0;
- a_route(iface->name, &dest, &dhcp->netmask, &gate, options->metric);
- d_route(iface->name, &dest, &dhcp->netmask, &gate, 0);
- }
-#endif
+static int
+configure_routes(struct interface *iface, const struct dhcp_message *dhcp,
+ const struct options *options)
+{
+ struct rt *rt, *ort;
+ struct rt *rtn = NULL, *nr = NULL;
+ int remember;
+ int retval = 0;
+ char *addr;
#ifdef THERE_IS_NO_FORK
+ char *skipp;
+ size_t skiplen;
+ int skip = 0;
+
free(dhcpcd_skiproutes);
/* We can never have more than 255 routes. So we need space
* for 255 3 digit numbers and commas */
*skipp = '\0';
#endif
- /* Remember added routes */
- NSTAILQ_FOREACH(route, dhcp->routes, entries) {
+ ort = get_option_routes(dhcp);
+
#ifdef ENABLE_IPV4LL
- /* Check if we have already got a link locale route dished
- * out by the DHCP server */
- if (route->destination.s_addr == htonl(LINKLOCAL_ADDR) &&
- route->netmask.s_addr == htonl(LINKLOCAL_MASK))
- haslinklocal = true;
+ if (options->options & DHCPCD_IPV4LL &&
+ IN_PRIVATE(ntohl(dhcp->yiaddr)))
+ {
+ for (rt = ort; rt; rt = rt->next) {
+ /* Check if we have already got a link locale route dished
+ * out by the DHCP server */
+ if (rt->dest.s_addr == htonl(LINKLOCAL_ADDR) &&
+ rt->net.s_addr == htonl(LINKLOCAL_MASK))
+ break;
+ rtn = rt;
+ }
+
+ if (!rt) {
+ rt = xmalloc(sizeof(*rt));
+ rt->dest.s_addr = htonl(LINKLOCAL_ADDR);
+ rt->net.s_addr = htonl(LINKLOCAL_MASK);
+ rt->gate.s_addr = 0;
+ rt->next = NULL;
+ if (rtn)
+ rtn->next = rt;
+ else
+ ort = rt;
+ }
+ }
#endif
+
+#ifdef THERE_IS_NO_FORK
+ if (dhcpcd_skiproutes) {
+ int i = -1;
+ char *sk, *skp, *token;
+ free_routes(iface->routes);
+ for (rt = ort; rt; rt = rt->next) {
+ i++;
+ /* Check that we did add this route or not */
+ sk = skp = xstrdup(dhcpcd_skiproutes);
+ while ((token = strsep(&skp, ","))) {
+ if (isdigit(*token) && atoi(token) == i)
+ break;
+ }
+ free(sk);
+ if (token)
+ continue;
+ if (nr) {
+ rtn->next = xmalloc(sizeof(*rtn));
+ rtn = rtn->next;
+ } else {
+ nr = rtn = xmalloc(sizeof(*rtn));
+ }
+ rtn->dest.s_addr = rt->dest.s_addr;
+ rtn->net.s_addr = rt->net.s_addr;
+ rtn->gate.s_addr = rt->gate.s_addr;
+ rtn->next = NULL;
+ }
+ iface->routes = nr;
+ nr = NULL;
+
+ /* We no longer need this */
+ free(dhcpcd_skiproutes);
+ dhcpcd_skiproutes = NULL;
+ }
+#endif
+
+ /* Now remove old routes we no longer use.
+ * We should do this in reverse order. */
+ iface->routes = reverse_routes(iface->routes);
+ for (rt = iface->routes; rt; rt = rt->next)
+ if (in_routes(ort, rt) != 0)
+ delete_route(iface->name, rt, options->metric);
+
+ for (rt = ort; rt; rt = rt->next) {
/* Don't set default routes if not asked to */
- if (route->destination.s_addr == 0 &&
- route->netmask.s_addr == 0 &&
+ if (rt->dest.s_addr == 0 &&
+ rt->net.s_addr == 0 &&
!(options->options & DHCPCD_GATEWAY))
continue;
- remember = a_route(iface->name, &route->destination,
- &route->netmask, &route->gateway,
- options->metric);
+ addr = xstrdup(inet_ntoa(rt->dest));
+ logger(LOG_DEBUG, "adding route to %s/%d via %s",
+ addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
+ free(addr);
+ remember = add_route(iface->name, &rt->dest,
+ &rt->net, &rt->gate,
+ options->metric);
+ retval += remember;
+
/* If we failed to add the route, we may have already added it
ourselves. If so, remember it again. */
- if (remember < 0 && in_routes(iface->previous_routes, route))
- remember = 1;
+ if (remember < 0) {
+ if (errno != EEXIST)
+ logger(LOG_ERR, "add_route: %s",
+ strerror(errno));
+ if (in_routes(iface->routes, rt) == 0)
+ remember = 1;
+ }
+ /* This login is split from above due to the #ifdef below */
if (remember >= 0) {
- if (!new_routes) {
- new_routes = xmalloc(sizeof(*new_routes));
- STAILQ_INIT(new_routes);
+ if (nr) {
+ rtn->next = xmalloc(sizeof(*rtn));
+ rtn = rtn->next;
+ } else {
+ nr = rtn = xmalloc(sizeof(*rtn));
}
- new_route = xmalloc(sizeof(*new_route));
- memcpy(new_route, route, sizeof(*new_route));
- STAILQ_INSERT_TAIL(new_routes, new_route, entries);
+ rtn->dest.s_addr = rt->dest.s_addr;
+ rtn->net.s_addr = rt->net.s_addr;
+ rtn->gate.s_addr = rt->gate.s_addr;
+ rtn->next = NULL;
}
#ifdef THERE_IS_NO_FORK
/* If we have daemonised yet we need to record which routes
* we failed to add so we can skip them */
- else if (!(options->options & DAEMONISED)) {
+ else if (!(options->options & DHCPCD_DAEMONISED)) {
/* We can never have more than 255 / 4 routes,
* so 3 chars is plently */
if (*skipp)
skip++;
#endif
}
+ free_routes(ort);
+ free_routes(iface->routes);
+ iface->routes = nr;
#ifdef THERE_IS_NO_FORK
if (*dhcpcd_skiproutes)
}
#endif
-#ifdef ENABLE_IPV4LL
- /* Ensure we always add the link local route if we got a private
- * address and isn't link local itself */
- if (options->options & DHCPCD_IPV4LL &&
- !haslinklocal &&
- IN_PRIVATE(ntohl(dhcp->address.s_addr)))
- {
- dest.s_addr = htonl(LINKLOCAL_ADDR);
- mask.s_addr = htonl(LINKLOCAL_MASK);
- gate.s_addr = 0;
- remember = a_route(iface->name, &dest, &mask, &gate,
- options->metric);
- if (remember >= 0) {
- if (!new_routes) {
- new_routes = xmalloc(sizeof(*new_routes));
- STAILQ_INIT(new_routes);
+ return retval;
+}
+
+#ifdef ENABLE_INFO
+static void
+print_clean(FILE *f, const char *name, const char *value)
+{
+ char *clean;
+
+ if (! value)
+ return;
+
+ clean = clean_metas(value);
+ fprintf(f, "%s='%s'\n", name, clean);
+ free(clean);
+}
+
+int
+write_info(const struct interface *iface, const struct dhcp_message *dhcp,
+ const struct dhcp_lease *lease, const struct options *options,
+ int overwrite)
+{
+ FILE *f;
+ struct rt *rt, *ort;
+ struct stat sb;
+ struct in_addr addr;
+ int doneone;
+
+ if (options->options & DHCPCD_TEST)
+ f = stdout;
+ else {
+ if (!overwrite && stat(iface->infofile, &sb) == 0)
+ return 0;
+
+ if ((f = fopen(iface->infofile, "w")) == NULL)
+ return -1;
+ }
+
+ if (dhcp->yiaddr) {
+ fprintf(f, "IPADDR=%s\n", inet_ntoa(iface->addr));
+ fprintf(f, "NETMASK=%s\n", inet_ntoa(iface->net));
+ addr.s_addr = dhcp->yiaddr & iface->net.s_addr;
+ fprintf(f, "NETWORK=%s\n", inet_ntoa(addr));
+ if (get_option_addr(&addr.s_addr, dhcp, DHCP_BROADCAST) == -1)
+ addr.s_addr = dhcp->yiaddr | ~iface->net.s_addr;
+ fprintf(f, "BROADCAST=%s\n", inet_ntoa(addr));
+
+ ort = get_option_routes(dhcp);
+ doneone = 0;
+ fprintf(f, "ROUTES=");
+ for (rt = ort; rt; rt = rt->next) {
+ if (rt->dest.s_addr == 0)
+ continue;
+ if (doneone)
+ fputc(' ', f);
+ else {
+ fputc('\'', f);
+ doneone = 1;
}
- new_route = xmalloc(sizeof(*new_route));
- new_route->destination.s_addr = dest.s_addr;
- new_route->netmask.s_addr = mask.s_addr;
- new_route->gateway.s_addr = gate.s_addr;
- STAILQ_INSERT_TAIL(new_routes, new_route, entries);
+ fprintf(f, "%s", inet_ntoa(rt->dest));
+ fprintf(f, ",%s", inet_ntoa(rt->net));
+ fprintf(f, ",%s", inet_ntoa(rt->gate));
}
+ if (doneone)
+ fputc('\'', f);
+ fputc('\n', f);
+
+ doneone = 0;
+ fprintf(f, "GATEWAYS=");
+ for (rt = ort; rt; rt = rt->next) {
+ if (rt->dest.s_addr != 0)
+ continue;
+ if (doneone)
+ fputc(' ', f);
+ else {
+ fputc('\'', f);
+ doneone = 1;
+ }
+ fprintf(f, "%s", inet_ntoa(rt->gate));
+ }
+ if (doneone)
+ fputc('\'', f);
+ fputc('\n', f);
+ free_routes(ort);
}
-#endif
- if (iface->previous_routes)
- free_route(iface->previous_routes);
- iface->previous_routes = new_routes;
+ write_options(f, dhcp);
- if (options->options & DHCPCD_DNS && dhcp->dnsservers)
- make_resolv(iface->name, dhcp);
- else
- logger(LOG_DEBUG, "no dns information to write");
+/* FIXME
+ if (dhcp->fqdn) {
+ fprintf(f, "FQDNFLAGS='%u'\n", dhcp->fqdn->flags);
+ fprintf(f, "FQDNRCODE1='%u'\n", dhcp->fqdn->r1);
+ fprintf(f, "FQDNRCODE2='%u'\n", dhcp->fqdn->r2);
+ print_clean(f, "FQDNHOSTNAME", dhcp->fqdn->name);
+ }
+*/
+ if (dhcp->siaddr) {
+ addr.s_addr = dhcp->siaddr;
+ fprintf(f, "DHCPSID='%s'\n", inet_ntoa(addr));
+ }
+ if (dhcp->servername[0])
+ print_clean(f, "DHCPSNAME", (const char *)dhcp->servername);
+
+ if (!(options->options & DHCPCD_INFORM) && dhcp->yiaddr) {
+ if (!(options->options & DHCPCD_TEST))
+ fprintf(f, "LEASEDFROM=%u\n", lease->leasedfrom);
+ fprintf(f, "LEASETIME=%u\n", lease->leasetime);
+ fprintf(f, "RENEWALTIME=%u\n", lease->renewaltime);
+ fprintf(f, "REBINDTIME=%u\n", lease->rebindtime);
+ }
+ print_clean(f, "INTERFACE", iface->name);
+ print_clean(f, "CLASSID", options->classid);
+ if (iface->clientid_len > 0) {
+ fprintf(f, "CLIENTID=%s\n",
+ hwaddr_ntoa(iface->clientid, iface->clientid_len));
+ }
+ fprintf(f, "DHCPCHADDR=%s\n",
+ hwaddr_ntoa(iface->hwaddr, iface->hwlen));
-#ifdef ENABLE_NTP
- if (options->options & DHCPCD_NTP && dhcp->ntpservers)
- make_ntp(iface->name, dhcp);
+ if (!(options->options & DHCPCD_TEST))
+ fclose(f);
+ return 0;
+}
#endif
-#ifdef ENABLE_NIS
- if (options->options & DHCPCD_NIS &&
- (dhcp->nisservers || dhcp->nisdomain))
- make_nis(iface->name, dhcp);
+int
+configure(struct interface *iface, const struct dhcp_message *dhcp,
+ const struct dhcp_lease *lease, const struct options *options,
+ int up)
+{
+ unsigned short mtu;
+ struct in_addr addr;
+ struct in_addr net;
+ struct in_addr brd;
+#ifdef __linux__
+ struct in_addr dest;
+ struct in_addr gate;
#endif
- curhostname = xmalloc(sizeof(char) * MAXHOSTNAMELEN);
- *curhostname = '\0';
+ /* Grab our IP config */
+ if (dhcp == NULL || dhcp->yiaddr == 0)
+ up = 0;
+ else {
+ addr.s_addr = dhcp->yiaddr;
+ /* Ensure we have all the needed values */
+ if (get_option_addr(&net.s_addr, dhcp, DHCP_NETMASK) == -1)
+ net.s_addr = get_netmask(addr.s_addr);
+ if (get_option_addr(&brd.s_addr, dhcp, DHCP_BROADCAST) == -1)
+ brd.s_addr = addr.s_addr | ~net.s_addr;
+ }
- gethostname(curhostname, MAXHOSTNAMELEN);
- if (options->dohostname ||
- strlen(curhostname) == 0 ||
- strcmp(curhostname, "(none)") == 0 ||
- strcmp(curhostname, "localhost") == 0)
- {
- newhostname = xmalloc(sizeof(char) * MAXHOSTNAMELEN);
+ /* If we aren't up, then reset the interface as much as we can */
+ if (!up) {
+ /* Restore the original MTU value */
+ if (iface->initial_mtu != iface->mtu) {
+ set_mtu(iface->name, iface->initial_mtu);
+ iface->mtu = iface->initial_mtu;
+ }
- if (dhcp->hostname)
- strlcpy(newhostname, dhcp->hostname, MAXHOSTNAMELEN);
- else
- *newhostname = '\0';
+#ifdef ENABLE_INFO
+ /* If we haven't created an info file, do so now */
+ if (!lease->frominfo) {
+ if (write_info(iface, dhcp, lease, options, 0) == -1)
+ logger(LOG_ERR, "write_info: %s",
+ strerror(errno));
+ }
+#endif
- /* Now we have made a resolv.conf we can obtain a hostname
- * if we need it */
- if (! *newhostname || options->dohostname > 3)
- lookuphostname(newhostname, dhcp, options);
+ /* Only reset things if we had set them before */
+ if (iface->addr.s_addr != 0) {
+ if (!(options->options & DHCPCD_KEEPADDRESS)) {
+ delete_routes(iface, options->metric);
+ logger(LOG_DEBUG, "deleting IP address %s/%d",
+ inet_ntoa(iface->addr),
+ inet_ntocidr(iface->net));
+ if (del_address(iface->name, &iface->addr,
+ &iface->net) == -1 &&
+ errno != ENOENT)
+ logger(LOG_ERR, "del_address: %s",
+ strerror(errno));
+ iface->addr.s_addr = 0;
+ iface->net.s_addr = 0;
+ }
+#ifdef ENABLE_RESOLVCONF
+ if (options->options & DHCPCD_DNS)
+ restore_resolv(iface->name);
+#endif
+ }
- if (*newhostname) {
- logger(LOG_INFO, "setting hostname to `%s'",
- newhostname);
- sethostname(newhostname, (int)strlen(newhostname));
+ exec_script(options->script, iface->infofile, "down");
+
+ return 0;
+ }
+
+ if (options->options & DHCPCD_MTU)
+ if (get_option_uint16(&mtu, dhcp, DHCP_MTU) == 0)
+ if (mtu != iface->mtu && mtu >= MTU_MIN) {
+ if (set_mtu(iface->name, mtu) == 0)
+ iface->mtu = mtu;
+ else
+ logger(LOG_ERR, "set_mtu: %s",
+ strerror(errno));
+ }
+
+ /* This also changes netmask */
+ if (!(options->options & DHCPCD_INFORM) ||
+ !has_address(iface->name, &addr, &net)) {
+ logger(LOG_DEBUG, "adding IP address %s/%d",
+ inet_ntoa(addr), inet_ntocidr(net));
+ if (add_address(iface->name, &addr, &net, &brd) == -1 &&
+ errno != EEXIST)
+ {
+ logger(LOG_ERR, "add_address: %s", strerror(errno));
+ return -1;
}
+ }
+
+ /* Now delete the old address if different */
+ if (iface->addr.s_addr != addr.s_addr &&
+ iface->addr.s_addr != 0 &&
+ !(options->options & DHCPCD_KEEPADDRESS))
+ del_address(iface->name, &iface->addr, &iface->net);
- free(newhostname);
+#ifdef __linux__
+ /* On linux, we need to change the subnet route to have our metric. */
+ if (iface->addr.s_addr != lease->addr.s_addr &&
+ options->metric > 0 && net.s_addr != INADDR_BROADCAST)
+ {
+ dest.s_addr = addr.s_addr & net.s_addr;
+ gate.s_addr = 0;
+ add_route(iface->name, &dest, &net, &gate, options->metric);
+ del_route(iface->name, &dest, &net, &gate, 0);
}
+#endif
- free(curhostname);
+ configure_routes(iface, dhcp, options);
+ if (options->options & DHCPCD_DNS)
+ configure_resolv(iface->name, dhcp);
+#ifdef ENABLE_NTP
+ if (options->options & DHCPCD_NTP)
+ configure_ntp(iface->name, dhcp);
+#endif
+#ifdef ENABLE_NIS
+ if (options->options & DHCPCD_NIS)
+ configure_nis(iface->name, dhcp);
+#endif
+ configure_hostname(dhcp, addr.s_addr,
+ options->options & DHCPCD_HOSTNAME);
+
+ up = (iface->addr.s_addr != addr.s_addr ||
+ iface->net.s_addr != net.s_addr);
+
+ iface->addr.s_addr = addr.s_addr;
+ iface->net.s_addr = net.s_addr;
#ifdef ENABLE_INFO
- if (!dhcp->frominfo)
- write_info(iface, dhcp, options, true);
+ //if (!lease->frominfo)
+ write_info(iface, dhcp, lease, options, 1);
+ if (write_lease(iface, dhcp) == -1)
+ logger(LOG_ERR, "write_lease: %s", strerror(errno));
#endif
-
- if (iface->previous_address.s_addr != dhcp->address.s_addr ||
- iface->previous_netmask.s_addr != dhcp->netmask.s_addr)
- {
- memcpy(&iface->previous_address,
- &dhcp->address, sizeof(iface->previous_address));
- memcpy(&iface->previous_netmask,
- &dhcp->netmask, sizeof(iface->previous_netmask));
- exec_script(options->script, iface->infofile, "new");
- } else
- exec_script(options->script, iface->infofile, "up");
+
+ exec_script(options->script, iface->infofile, up ? "new" : "up");
return 0;
}
#define DHCPCONFIG_H
#include "dhcpcd.h"
-#include "if.h"
#include "dhcp.h"
+#include "net.h"
-int configure(const struct options *, struct interface *,
- const struct dhcp *, bool up);
+int exec_cmd(const char *, const char *, ...);
+int write_info(const struct interface *, const struct dhcp_message *,
+ const struct dhcp_lease *, const struct options *, int);
+int configure(struct interface *, const struct dhcp_message *,
+ const struct dhcp_lease *, const struct options *, int);
#endif
* SUCH DAMAGE.
*/
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-
-#include <netinet/in.h>
-#define __FAVOR_BSD /* Nasty hack so we can use BSD semantics for UDP */
-#include <netinet/udp.h>
-#undef __FAVOR_BSD
-#include <net/if_arp.h>
-#include <arpa/inet.h>
-
#include <errno.h>
-#include <limits.h>
-#include <math.h>
-#include <stdio.h>
-#include <stdint.h>
+#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include "config.h"
-
#include "common.h"
-#include "dhcpcd.h"
#include "dhcp.h"
-#include "logger.h"
-#include "socket.h"
-
-#ifndef STAILQ_CONCAT
-#define STAILQ_CONCAT(head1, head2) do { \
- if (!STAILQ_EMPTY((head2))) { \
- *(head1)->stqh_last = (head2)->stqh_first; \
- (head1)->stqh_last = (head2)->stqh_last; \
- STAILQ_INIT((head2)); \
- } \
-} while (0)
-#endif
-struct message {
- int value;
- const char *name;
+#define OPT_REQUEST (1 << 0)
+#define OPT_UINT8 (1 << 3)
+#define OPT_UINT16 (1 << 4)
+#define OPT_UINT32 (1 << 5)
+#define OPT_IPV4 (1 << 6)
+#define OPT_STRING (1 << 7)
+#define OPT_RFC3361 (1 << 10)
+#define OPT_RFC3397 (1 << 11)
+
+#define OPT_IPV4R OPT_IPV4 | OPT_REQUEST
+#define OPT_UINT32R OPT_UINT32 | OPT_REQUEST
+#define OPT_UINT16R OPT_UINT16 | OPT_REQUEST
+#define OPT_STRINGR OPT_STRING | OPT_REQUEST
+
+struct dhcp_option {
+ uint8_t option;
+ int type;
+ const char *var;
};
-static struct message dhcp_messages[] = {
- { DHCP_DISCOVER, "DHCP_DISCOVER" },
- { DHCP_OFFER, "DHCP_OFFER" },
- { DHCP_REQUEST, "DHCP_REQUEST" },
- { DHCP_DECLINE, "DHCP_DECLINE" },
- { DHCP_ACK, "DHCP_ACK" },
- { DHCP_NAK, "DHCP_NAK" },
- { DHCP_RELEASE, "DHCP_RELEASE" },
- { DHCP_INFORM, "DHCP_INFORM" },
- { -1, NULL }
+const struct dhcp_option dhcp_options[] = {
+ { DHCP_SERVERID, OPT_IPV4, "SERVERID" },
+ { DHCP_NETMASK, OPT_IPV4R, NULL },
+ { DHCP_BROADCAST, OPT_IPV4R, NULL },
+ { DHCP_LEASETIME, OPT_UINT32, NULL },
+ { DHCP_RENEWALTIME, OPT_UINT32R, "RENEWALTIME" },
+ { DHCP_REBINDTIME, OPT_UINT32R, "REBINDTIME" },
+ { DHCP_MTU, OPT_UINT16R, "MTU" },
+ { DHCP_STATICROUTE, OPT_IPV4R, NULL },
+ { DHCP_ROUTER, OPT_IPV4R, NULL },
+ { DHCP_HOSTNAME, OPT_STRINGR, "HOSTNAME" },
+ { DHCP_DNSSERVER, OPT_IPV4R, "DNSSERVER" },
+ { DHCP_DNSDOMAIN, OPT_STRINGR, "DNSDOMAIN" },
+ { DHCP_DNSSEARCH, OPT_STRINGR | OPT_RFC3397, "DNSSEARCH" },
+#ifdef ENABLE_NTP
+ { DHCP_NTPSERVER, OPT_IPV4R, "NTPSERVER" },
+#endif
+#ifdef ENABLE_NIS
+ { DHCP_NISSERVER, OPT_IPV4R, "NISSERVER" },
+ { DHCP_NISDOMAIN, OPT_IPV4R, "NISDOMAIN" },
+#endif
+#ifdef ENABLE_INFO
+ { DHCP_ROOTPATH, OPT_STRINGR, "ROOTPATH" },
+ { DHCP_SIPSERVER, OPT_STRINGR | OPT_RFC3361, "SIPSERVER" },
+#endif
+ { DHCP_MESSAGE, OPT_STRING, NULL},
+ { 0, 0, NULL }
};
-static const char *
-dhcp_message(int type)
+static int
+valid_length(uint8_t option, const uint8_t *data, int *type)
{
- struct message *d;
+ uint8_t l = *data;
+ uint8_t i;
- for (d = dhcp_messages; d->name; d++)
- if (d->value == type)
- return d->name;
+ if (l == 0)
+ return -1;
- return NULL;
-}
+ for (i = 0; i < sizeof(dhcp_options) / sizeof(dhcp_options[0]); i++) {
+ if (dhcp_options[i].option != option)
+ continue;
+
+ if (type)
+ *type = dhcp_options[i].type;
+
+ if (dhcp_options[i].type & OPT_STRING)
+ return 0;
+
+ if (dhcp_options[i].type & OPT_UINT32) {
+ if (l == sizeof(uint32_t))
+ return 0;
+ return -1;
+ }
-static uint16_t
-checksum(unsigned char *addr, uint16_t len)
-{
- uint32_t sum = 0;
- union
- {
- unsigned char *addr;
- uint16_t *i;
- } p;
- uint16_t nleft = len;
- uint8_t a = 0;
-
- p.addr = addr;
- while (nleft > 1) {
- sum += *p.i++;
- nleft -= 2;
+ if (dhcp_options[i].type & OPT_IPV4)
+ return l % sizeof(uint32_t);
}
- if (nleft == 1) {
- memcpy(&a, p.i, 1);
- sum += ntohs(a) << 8;
- }
+ /* unknown option, so let it pass */
+ return 0;
+}
- sum = (sum >> 16) + (sum & 0xffff);
- sum += (sum >> 16);
+static const uint8_t *
+_get_option(const struct dhcp_message *dhcp, uint8_t opt, int *type)
+{
+ const uint8_t *p = dhcp->options;
+ const uint8_t *e = p + sizeof(dhcp->options);
+ uint8_t l;
+ uint8_t o = 0;
+
+ while (p < e) {
+ o = *p++;
+ if (o == opt) {
+ if (valid_length(o, p, type) != -1)
+ return p;
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (o) {
+ case DHCP_PAD:
+ continue;
+ case DHCP_END:
+ if (o) {
+ if (o & 1) {
+ /* bit 1 set means parse boot file */
+ o &= ~1;
+ p = dhcp->bootfile;
+ e = p + sizeof(dhcp->bootfile);
+ } else if (o & 2) {
+ /* bit 2 set means parse server name */
+ o &= ~2;
+ p = dhcp->servername;
+ e = p + sizeof(dhcp->servername);
+ }
+ }
+ break;
+ case DHCP_OPTIONSOVERLOADED:
+ /* Ensure we only get this option once */
+ if (!(o & 4)) {
+ o = p[1];
+ o |= 4;
+ }
+ break;
+ }
+
+ l = *p++;
+ p += l;
+ }
- return ~sum;
+ errno = ENOENT;
+ return NULL;
}
-static void
-make_dhcp_packet(struct udp_dhcp_packet *packet,
- const unsigned char *data, size_t length,
- struct in_addr source, struct in_addr dest)
+const uint8_t *
+get_option(const struct dhcp_message *dhcp, uint8_t opt)
{
- struct ip *ip = &packet->ip;
- struct udphdr *udp = &packet->udp;
-
- /* OK, this is important :)
- * We copy the data to our packet and then create a small part of the
- * ip structure and an invalid ip_len (basically udp length).
- * We then fill the udp structure and put the checksum
- * of the whole packet into the udp checksum.
- * Finally we complete the ip structure and ip checksum.
- * If we don't do the ordering like so then the udp checksum will be
- * broken, so find another way of doing it! */
-
- memcpy(&packet->dhcp, data, length);
-
- ip->ip_p = IPPROTO_UDP;
- ip->ip_src.s_addr = source.s_addr;
- if (dest.s_addr == 0)
- ip->ip_dst.s_addr = INADDR_BROADCAST;
- else
- ip->ip_dst.s_addr = dest.s_addr;
-
- udp->uh_sport = htons(DHCP_CLIENT_PORT);
- udp->uh_dport = htons(DHCP_SERVER_PORT);
- udp->uh_ulen = htons(sizeof(*udp) + length);
- ip->ip_len = udp->uh_ulen;
- udp->uh_sum = checksum((unsigned char *)packet, sizeof(*packet));
-
- ip->ip_v = IPVERSION;
- ip->ip_hl = 5;
- ip->ip_id = 0;
- ip->ip_tos = IPTOS_LOWDELAY;
- ip->ip_len = htons (sizeof(*ip) + sizeof(*udp) + length);
- ip->ip_id = 0;
- ip->ip_off = htons(IP_DF); /* Don't fragment */
- ip->ip_ttl = IPDEFTTL;
-
- ip->ip_sum = checksum((unsigned char *)ip, sizeof(*ip));
+ return _get_option(dhcp, opt, NULL);
}
int
-valid_dhcp_packet(unsigned char *data)
+get_option_addr(uint32_t *a, const struct dhcp_message *dhcp, uint8_t option)
{
- union
- {
- unsigned char *data;
- struct udp_dhcp_packet *packet;
- } d;
- uint16_t bytes;
- uint16_t ipsum;
- uint16_t iplen;
- uint16_t udpsum;
- struct in_addr source;
- struct in_addr dest;
- int retval = 0;
-
- d.data = data;
- bytes = ntohs(d.packet->ip.ip_len);
- ipsum = d.packet->ip.ip_sum;
- iplen = d.packet->ip.ip_len;
- udpsum = d.packet->udp.uh_sum;
-
- d.data = data;
- d.packet->ip.ip_sum = 0;
- if (ipsum != checksum((unsigned char *)&d.packet->ip,
- sizeof(d.packet->ip)))
- {
- logger(LOG_DEBUG, "bad IP header checksum, ignoring");
- retval = -1;
- goto eexit;
- }
-
- memcpy(&source, &d.packet->ip.ip_src, sizeof(d.packet->ip.ip_src));
- memcpy(&dest, &d.packet->ip.ip_dst, sizeof(d.packet->ip.ip_dst));
- memset(&d.packet->ip, 0, sizeof(d.packet->ip));
- d.packet->udp.uh_sum = 0;
-
- d.packet->ip.ip_p = IPPROTO_UDP;
- memcpy(&d.packet->ip.ip_src, &source, sizeof(d.packet->ip.ip_src));
- memcpy(&d.packet->ip.ip_dst, &dest, sizeof(d.packet->ip.ip_dst));
- d.packet->ip.ip_len = d.packet->udp.uh_ulen;
- if (udpsum && udpsum != checksum(d.data, bytes)) {
- logger(LOG_ERR, "bad UDP checksum, ignoring");
- retval = -1;
- }
+ const uint8_t *p = get_option(dhcp, option);
-eexit:
- d.packet->ip.ip_sum = ipsum;
- d.packet->ip.ip_len = iplen;
- d.packet->udp.uh_sum = udpsum;
-
- return retval;
+ if (!p)
+ return -1;
+ memcpy(a, p + 1, sizeof(*a));
+ return 0;
}
-ssize_t
-send_message(const struct interface *iface, const struct dhcp *dhcp,
- uint32_t xid, char type, const struct options *options)
+int
+get_option_uint32(uint32_t *i, const struct dhcp_message *dhcp, uint8_t option)
{
- struct udp_dhcp_packet *packet;
- struct dhcp_message *message;
- unsigned char *m;
- unsigned char *p;
- unsigned char *n_params = NULL;
- size_t l;
- struct in_addr from;
- struct in_addr to;
- time_t up = uptime() - iface->start_uptime;
- uint32_t ul;
- uint16_t sz;
- size_t message_length;
- ssize_t retval;
-
- memset (&from, 0, sizeof(from));
- memset (&to, 0, sizeof(to));
-
- if (type == DHCP_RELEASE)
- to.s_addr = dhcp->serveraddress.s_addr;
-
- message = xzalloc(sizeof (*message));
- m = (unsigned char *)message;
- p = (unsigned char *)&message->options;
-
- if ((type == DHCP_INFORM ||
- type == DHCP_RELEASE ||
- type == DHCP_REQUEST) &&
- !IN_LINKLOCAL(ntohl(iface->previous_address.s_addr)))
- {
- message->ciaddr = iface->previous_address.s_addr;
- from.s_addr = iface->previous_address.s_addr;
-
- /* Just incase we haven't actually configured the address yet */
- if (type == DHCP_INFORM && iface->previous_address.s_addr == 0)
- message->ciaddr = dhcp->address.s_addr;
-
- /* Zero the address if we're currently on a different subnet */
- if (type == DHCP_REQUEST &&
- iface->previous_netmask.s_addr != dhcp->netmask.s_addr)
- message->ciaddr = from.s_addr = 0;
-
- if (from.s_addr != 0)
- to.s_addr = dhcp->serveraddress.s_addr;
- }
-
- message->op = DHCP_BOOTREQUEST;
- message->hwtype = iface->family;
- switch (iface->family) {
- case ARPHRD_ETHER:
- case ARPHRD_IEEE802:
- message->hwlen = ETHER_ADDR_LEN;
- memcpy(&message->chaddr, &iface->hwaddr,
- ETHER_ADDR_LEN);
- break;
- case ARPHRD_IEEE1394:
- case ARPHRD_INFINIBAND:
- message->hwlen = 0;
- if (message->ciaddr == 0)
- message->flags = htons(BROADCAST_FLAG);
- break;
- default:
- logger (LOG_ERR, "dhcp: unknown hardware type %d",
- iface->family);
- }
-
- if (up < 0 || up > (time_t)UINT16_MAX)
- message->secs = htons((uint16_t)UINT16_MAX);
- else
- message->secs = htons(up);
- message->xid = xid;
- message->cookie = htonl(MAGIC_COOKIE);
+ uint32_t a;
- *p++ = DHCP_MESSAGETYPE;
- *p++ = 1;
- *p++ = type;
-
- if (type == DHCP_REQUEST) {
- *p++ = DHCP_MAXMESSAGESIZE;
- *p++ = 2;
- sz = get_mtu(iface->name);
- if (sz < MTU_MIN) {
- if (set_mtu(iface->name, MTU_MIN) == 0)
- sz = MTU_MIN;
- }
- sz = htons(sz);
- memcpy(p, &sz, 2);
- p += 2;
- }
-
- *p++ = DHCP_CLIENTID;
- *p++ = iface->clientid_len;
- memcpy(p, iface->clientid, iface->clientid_len);
- p+= iface->clientid_len;
-
- if (type != DHCP_DECLINE && type != DHCP_RELEASE) {
- if (options->userclass_len > 0) {
- *p++ = DHCP_USERCLASS;
- *p++ = options->userclass_len;
- memcpy(p, &options->userclass, options->userclass_len);
- p += options->userclass_len;
- }
-
- if (*options->classid > 0) {
- *p++ = DHCP_CLASSID;
- *p++ = l = strlen(options->classid);
- memcpy(p, options->classid, l);
- p += l;
- }
- }
+ if (get_option_addr(&a, dhcp, option) == -1)
+ return -1;
- if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
-#define PUTADDR(_type, _val) { \
- *p++ = _type; \
- *p++ = 4; \
- memcpy(p, &_val.s_addr, 4); \
- p += 4; \
+ *i = ntohl(a);
+ return 0;
}
- if (IN_LINKLOCAL(ntohl (dhcp->address.s_addr)))
- logger(LOG_ERR, "cannot request a link local address");
- else {
- if (dhcp->address.s_addr &&
- dhcp->address.s_addr !=
- iface->previous_address.s_addr)
- {
- PUTADDR(DHCP_ADDRESS, dhcp->address);
- if (dhcp->serveraddress.s_addr)
- PUTADDR(DHCP_SERVERIDENTIFIER,
- dhcp->serveraddress);
- }
- }
-#undef PUTADDR
-
- if (options->leasetime != 0) {
- *p++ = DHCP_LEASETIME;
- *p++ = 4;
- ul = htonl(options->leasetime);
- memcpy(p, &ul, 4);
- p += 4;
- }
- }
- if (type == DHCP_DISCOVER ||
- type == DHCP_INFORM ||
- type == DHCP_REQUEST)
- {
- if (options->hostname[0]) {
- if (options->fqdn == FQDN_DISABLE) {
- *p++ = DHCP_HOSTNAME;
- *p++ = l = strlen(options->hostname);
- memcpy(p, options->hostname, l);
- p += l;
- } else {
- /* Draft IETF DHC-FQDN option (81) */
- *p++ = DHCP_FQDN;
- *p++ = (l = strlen(options->hostname)) + 3;
- /* Flags: 0000NEOS
- * S: 1 => Client requests Server to update
- * a RR in DNS as well as PTR
- * O: 1 => Server indicates to client that
- * DNS has been updated
- * E: 1 => Name data is DNS format
- * N: 1 => Client requests Server to not
- * update DNS
- */
- *p++ = options->fqdn & 0x9;
- *p++ = 0; /* from server for PTR RR */
- *p++ = 0; /* from server for A RR if S=1 */
- memcpy(p, options->hostname, l);
- p += l;
- }
- }
-
- *p++ = DHCP_PARAMETERREQUESTLIST;
- n_params = p;
- *p++ = 0;
- if (type != DHCP_INFORM) {
- *p++ = DHCP_RENEWALTIME;
- *p++ = DHCP_REBINDTIME;
- }
- *p++ = DHCP_NETMASK;
- *p++ = DHCP_BROADCAST;
-
- /* -S means request CSR and MSCSR
- * -SS means only request MSCSR incase DHCP message
- * is too big */
- if (options->domscsr < 2)
- *p++ = DHCP_CSR;
- if (options->domscsr > 0)
- *p++ = DHCP_MSCSR;
- /* RFC 3442 states classless static routes should be
- * before routers and static routes as classless static
- * routes override them both */
- *p++ = DHCP_STATICROUTE;
- *p++ = DHCP_ROUTERS;
- *p++ = DHCP_HOSTNAME;
- *p++ = DHCP_DNSSEARCH;
- *p++ = DHCP_DNSDOMAIN;
- *p++ = DHCP_DNSSERVER;
-#ifdef ENABLE_NIS
- *p++ = DHCP_NISDOMAIN;
- *p++ = DHCP_NISSERVER;
-#endif
-#ifdef ENABLE_NTP
- *p++ = DHCP_NTPSERVER;
-#endif
- *p++ = DHCP_MTU;
-#ifdef ENABLE_INFO
- *p++ = DHCP_ROOTPATH;
- *p++ = DHCP_SIPSERVER;
-#endif
- *n_params = p - n_params - 1;
- }
- *p++ = DHCP_END;
-
-#ifdef BOOTP_MESSAGE_LENTH_MIN
- /* Some crappy DHCP servers think they have to obey the BOOTP minimum
- * message length.
- * They are wrong, but we should still cater for them. */
- while (p - m < BOOTP_MESSAGE_LENTH_MIN)
- *p++ = DHCP_PAD;
-#endif
+int
+get_option_uint16(uint16_t *i, const struct dhcp_message *dhcp, uint8_t option)
+{
+ const uint8_t *p = get_option(dhcp, option);
+ uint16_t d;
- message_length = p - m;
+ if (!p)
+ return -1;
+ memcpy(&d, p + 1, sizeof(d));
+ *i = ntohs(d);
+ return 0;
+}
- packet = xzalloc(sizeof(*packet));
- make_dhcp_packet(packet, (unsigned char *)message, message_length,
- from, to);
- free(message);
+int
+get_option_uint8(uint8_t *i, const struct dhcp_message *dhcp, uint8_t option)
+{
+ const uint8_t *p = get_option(dhcp, option);
- logger(LOG_DEBUG, "sending %s with xid 0x%x",dhcp_message(type), xid);
- retval = send_packet(iface, ETHERTYPE_IP, (unsigned char *)packet,
- message_length +
- sizeof(packet->ip) + sizeof(packet->udp));
- if (retval == -1)
- logger(LOG_ERR, "send_packet: %s", strerror(errno));
- free(packet);
- return retval;
+ if (!p)
+ return -1;
+ *i = *(p + 1);
+ return 0;
}
/* Decode an RFC3397 DNS search order option into a space
* terminating zero) or zero on error. out may be NULL
* to just determine output length. */
static unsigned int
-decode_search(const unsigned char *p, int len, char *out)
+decode_rfc3397(const uint8_t *p, char *out)
{
- const unsigned char *r, *q = p;
+ uint8_t len = *p++;
+ const uint8_t *r, *q = p;
unsigned int count = 0, l, hops;
- unsigned int ltype;
+ uint8_t ltype;
while (q - p < len) {
r = NULL;
return count;
}
-/* Add our classless static routes to the routes variable
- * and return the last route set */
-static struct route_head *
-decode_CSR(const unsigned char *p, int len)
+struct rt *
+decode_rfc3442(const uint8_t *data)
{
- const unsigned char *q = p;
- unsigned int cidr;
- unsigned int ocets;
- struct route_head *routes = NULL;
- struct rt *route;
-
+ const uint8_t *p = data;
+ const uint8_t *e;
+ uint8_t l;
+ uint8_t cidr;
+ uint8_t ocets;
+ struct rt *routes = NULL;
+ struct rt *rt = NULL;
+
+ l = *p++;
/* Minimum is 5 -first is CIDR and a router length of 4 */
- if (len < 5)
+ if (l < 5)
return NULL;
- while (q - p < len) {
- if (! routes) {
- routes = xmalloc(sizeof (*routes));
- STAILQ_INIT(routes);
- }
-
- route = xzalloc(sizeof(*route));
-
- cidr = *q++;
+ e = p + l;
+ while (p < e) {
+ cidr = *p++;
+ printf ("cd %d\n", cidr);
if (cidr > 32) {
- logger(LOG_ERR,
- "invalid CIDR of %d in classless static route",
- cidr);
- free_route(routes);
+ free_routes(routes);
+ errno = EINVAL;
return NULL;
}
- ocets = (cidr + 7) / 8;
- if (ocets > 0) {
- memcpy(&route->destination.s_addr, q, (size_t)ocets);
- q += ocets;
+ if (rt) {
+ rt->next = xmalloc(sizeof(*rt));
+ rt = rt->next;
+ } else {
+ routes = rt = xmalloc(sizeof(*routes));
}
+ rt->next = NULL;
- /* Now enter the netmask */
+ ocets = (cidr + 7) / 8;
+ /* If we have ocets then we have a destination and netmask */
if (ocets > 0) {
- memset(&route->netmask.s_addr, 255, (size_t)ocets - 1);
- memset((unsigned char *)&route->netmask.s_addr +
+ memcpy(&rt->dest.s_addr, p, (size_t)ocets);
+ memset(&rt->net.s_addr, 255, (size_t)ocets - 1);
+ memset((uint8_t *)&rt->net.s_addr +
(ocets - 1),
(256 - (1 << (32 - cidr) % 8)), 1);
+ p += ocets;
+ } else {
+ rt->dest.s_addr = 0;
+ rt->net.s_addr = 0;
}
/* Finally, snag the router */
- memcpy(&route->gateway.s_addr, q, 4);
- q += 4;
-
- STAILQ_INSERT_TAIL(routes, route, entries);
+ memcpy(&rt->gate.s_addr, p, 4);
+ p += 4;
}
-
return routes;
}
-void
-free_address(struct address_head *addresses)
-{
- struct address *p;
- struct address *n;
-
- if (!addresses)
- return;
- p = STAILQ_FIRST(addresses);
- while (p) {
- n = STAILQ_NEXT(p, entries);
- free(p);
- p = n;
- }
- free(addresses);
-}
-
-void
-free_dhcp(struct dhcp *dhcp)
-{
- if (! dhcp)
- return;
-
- free_route(dhcp->routes);
- free(dhcp->hostname);
- free_address(dhcp->dnsservers);
- free(dhcp->dnsdomain);
- free(dhcp->dnssearch);
- free_address(dhcp->ntpservers);
- free(dhcp->nisdomain);
- free_address(dhcp->nisservers);
- free(dhcp->rootpath);
- free(dhcp->sipservers);
- if (dhcp->fqdn) {
- free(dhcp->fqdn->name);
- free(dhcp->fqdn);
- }
-}
-
-void
-free_route(struct route_head *routes)
-{
- struct rt *p;
- struct rt *n;
-
- if (!routes)
- return;
- p = STAILQ_FIRST(routes);
- while (p) {
- n = STAILQ_NEXT(p, entries);
- free(p);
- p = n;
- }
- free(routes);
-}
-
#ifdef ENABLE_INFO
static char *
-decode_sipservers(const unsigned char *data, int length)
+decode_rfc3361(const uint8_t *data)
{
+ uint8_t len = *data++;
+ uint8_t enc;
+ unsigned int l;
char *sip = NULL;
- char *p;
- const char encoding = *data++;
struct in_addr addr;
- size_t len;
+ char *p;
+
+ if (len < 2) {
+ errno = EINVAL;
+ return 0;
+ }
- length--;
- switch (encoding) {
+ enc = *data++;
+ len--;
+ switch (enc) {
case 0:
- if ((len = decode_search(data, length, NULL)) > 0) {
+ if ((l = decode_rfc3397(data, NULL)) > 0) {
sip = xmalloc(len);
- decode_search(data, length, sip);
+ decode_rfc3397(data, sip);
}
break;
-
case 1:
- if (length == 0 || length % 4 != 0) {
- logger (LOG_ERR,
- "invalid length %d for option 120",
- length + 1);
+ if (len == 0 || len % 4 != 0) {
+ errno = EINVAL;
break;
}
- len = ((length / 4) * (4 * 4)) + 1;
- sip = p = xmalloc(len);
- while (length != 0) {
- memcpy(&addr.s_addr, data, 4);
- data += 4;
- p += snprintf (p, len - (p - sip),
- "%s ", inet_ntoa (addr));
- length -= 4;
+ addr.s_addr = INADDR_BROADCAST;
+ l = ((len / sizeof(addr.s_addr)) * ((4 * 4) + 1)) + 1;
+ sip = p = xmalloc(l);
+ while (l != 0) {
+ memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
+ data += sizeof(addr.s_addr);
+ p += snprintf(p, l - (p - sip), "%s ", inet_ntoa(addr));
+ l -= sizeof(addr.s_addr);
}
*--p = '\0';
break;
-
default:
- logger (LOG_ERR, "unknown sip encoding %d", encoding);
- break;
+ errno = EINVAL;
+ return 0;
}
return sip;
}
#endif
+char *
+get_option_string(const struct dhcp_message *dhcp, uint8_t option)
+{
+ int type;
+ const uint8_t *p;
+ uint8_t l;
+ char *s;
+
+ p = _get_option(dhcp, option, &type);
+ if (!p)
+ return NULL;
+
+ if (type & OPT_RFC3397) {
+ type = decode_rfc3397(p, NULL);
+ if (!type) {
+ errno = EINVAL;
+ return NULL;
+ }
+ s = xmalloc(sizeof(char) * type);
+ decode_rfc3397(p, s);
+ return s;
+ }
+
+ if (type & OPT_RFC3361)
+ return decode_rfc3361(p);
+
+
+ l = *p++;
+ s = xmalloc(sizeof(char) * (l + 1));
+ memcpy(s, p, l);
+ s[l] = '\0';
+ return s;
+}
+
/* This calculates the netmask that we should use for static routes.
* This IS different from the calculation used to calculate the netmask
* for an interface address. */
return (htonl(~t));
}
-static struct route_head *
-decode_routes(const unsigned char *data, int length)
+/* We need to obey routing options.
+ * If we have a CSR then we only use that.
+ * Otherwise we add static routes and then routers. */
+struct rt *
+get_option_routes(const struct dhcp_message *dhcp)
{
- int i;
- struct route_head *head = NULL;
- struct rt *route;
-
- for (i = 0; i < length; i += 8) {
- if (! head) {
- head = xmalloc(sizeof(*head));
- STAILQ_INIT(head);
+ const uint8_t *p;
+ const uint8_t *e;
+ struct rt *routes = NULL;
+ struct rt *route = NULL;
+ uint8_t l;
+
+ /* If we have CSR's then we MUST use these only */
+ p = get_option(dhcp, DHCP_CSR);
+ /* Check for crappy MS option */
+ if (!p)
+ p = get_option(dhcp, DHCP_MSCSR);
+ if (p) {
+ routes = decode_rfc3442(p);
+ if (routes)
+ return routes;
+ }
+
+ /* OK, get our static routes first. */
+ p = get_option(dhcp, DHCP_STATICROUTE);
+ if (p) {
+ l = *p++;
+ e = p + l;
+ while (p < e) {
+ if (route) {
+ route->next = xmalloc(sizeof(*route));
+ route = route->next;
+ } else
+ routes = route = xmalloc(sizeof(*routes));
+ route->next = NULL;
+ memcpy(&route->dest.s_addr, p, 4);
+ p += 4;
+ memcpy(&route->gate.s_addr, p, 4);
+ p += 4;
+ route->net.s_addr = route_netmask(route->dest.s_addr);
+ }
+ }
+
+ /* Now grab our routers */
+ p = get_option(dhcp, DHCP_ROUTER);
+ if (p) {
+ l = *p++;
+ e = p + l;
+ while (p < e) {
+ if (route) {
+ route->next = xzalloc(sizeof(*route));
+ route = route->next;
+ } else
+ routes = route = xzalloc(sizeof(*route));
+ memcpy(&route->gate.s_addr, p, 4);
+ p += 4;
}
- route = xzalloc(sizeof(*route));
- memcpy(&route->destination.s_addr, data + i, 4);
- memcpy(&route->gateway.s_addr, data + i + 4, 4);
- route->netmask.s_addr =
- route_netmask(route->destination.s_addr);
- STAILQ_INSERT_TAIL(head, route, entries);
}
- return head;
+ return routes;
}
-static struct route_head *
-decode_routers(const unsigned char *data, int length)
+ssize_t
+make_message(struct dhcp_message **message,
+ const struct interface *iface, const struct dhcp_lease *lease,
+ uint32_t xid, uint8_t type, const struct options *options)
{
- int i;
- struct route_head *head = NULL;
- struct rt *route;
-
- for (i = 0; i < length; i += 4) {
- if (! head) {
- head = xmalloc(sizeof(*head));
- STAILQ_INIT(head);
+ struct dhcp_message *dhcp;
+ uint8_t *m;
+ uint8_t *p;
+ uint8_t *n_params = NULL;
+ size_t l;
+ time_t up = uptime() - iface->start_uptime;
+ uint32_t ul;
+ uint16_t sz;
+
+ dhcp = xzalloc(sizeof (*dhcp));
+ m = (uint8_t *)dhcp;
+ p = (uint8_t *)&dhcp->options;
+
+ if ((type == DHCP_INFORM ||
+ type == DHCP_RELEASE ||
+ type == DHCP_REQUEST) &&
+ !IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
+ {
+ dhcp->ciaddr = iface->addr.s_addr;
+ /* Just incase we haven't actually configured the address yet */
+ if (type == DHCP_INFORM && iface->addr.s_addr == 0)
+ dhcp->ciaddr = lease->addr.s_addr;
+ /* Zero the address if we're currently on a different subnet */
+ if (type == DHCP_REQUEST &&
+ iface->net.s_addr != lease->net.s_addr)
+ dhcp->ciaddr = 0;
+ }
+
+ dhcp->op = DHCP_BOOTREQUEST;
+ dhcp->hwtype = iface->family;
+ switch (iface->family) {
+ case ARPHRD_ETHER:
+ case ARPHRD_IEEE802:
+ dhcp->hwlen = ETHER_ADDR_LEN;
+ memcpy(&dhcp->chaddr, &iface->hwaddr,
+ ETHER_ADDR_LEN);
+ break;
+ case ARPHRD_IEEE1394:
+ case ARPHRD_INFINIBAND:
+ dhcp->hwlen = 0;
+ if (dhcp->ciaddr == 0)
+ dhcp->flags = htons(BROADCAST_FLAG);
+ break;
+ }
+
+ if (up < 0 || up > (time_t)UINT16_MAX)
+ dhcp->secs = htons((uint16_t)UINT16_MAX);
+ else
+ dhcp->secs = htons(up);
+ dhcp->xid = xid;
+ dhcp->cookie = htonl(MAGIC_COOKIE);
+
+ *p++ = DHCP_MESSAGETYPE;
+ *p++ = 1;
+ *p++ = type;
+
+ if (type == DHCP_REQUEST) {
+ *p++ = DHCP_MAXMESSAGESIZE;
+ *p++ = 2;
+ sz = get_mtu(iface->name);
+ if (sz < MTU_MIN) {
+ if (set_mtu(iface->name, MTU_MIN) == 0)
+ sz = MTU_MIN;
}
- route = xzalloc(sizeof (*route));
- memcpy(&route->gateway.s_addr, data + i, 4);
- STAILQ_INSERT_TAIL(head, route, entries);
+ sz = htons(sz);
+ memcpy(p, &sz, 2);
+ p += 2;
}
- return head;
-}
+ *p++ = DHCP_CLIENTID;
+ *p++ = iface->clientid_len;
+ memcpy(p, iface->clientid, iface->clientid_len);
+ p+= iface->clientid_len;
-static bool
-add_addr(struct address_head **addresses,
- const unsigned char *data, size_t length, char option)
-{
- size_t i;
- struct address *address;
-
- for (i = 0; i < length; i += 4) {
- /* Sanity check */
- if (i + 4 > length) {
- logger(LOG_ERR, "invalid length %zu for option %i",
- length, option);
- return false;
+ if (type != DHCP_DECLINE && type != DHCP_RELEASE) {
+ if (options->userclass_len > 0) {
+ *p++ = DHCP_USERCLASS;
+ *p++ = options->userclass_len;
+ memcpy(p, &options->userclass, options->userclass_len);
+ p += options->userclass_len;
}
- if (*addresses == NULL) {
- *addresses = xmalloc(sizeof(**addresses));
- STAILQ_INIT(*addresses);
+ if (*options->classid > 0) {
+ *p++ = DHCP_CLASSID;
+ *p++ = l = strlen(options->classid);
+ memcpy(p, options->classid, l);
+ p += l;
}
- address = xzalloc(sizeof(*address));
- memcpy(&address->address.s_addr, data + i, 4);
- STAILQ_INSERT_TAIL(*addresses, address, entries);
}
- return true;
+ if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
+#define PUTADDR(_type, _val) \
+ { \
+ *p++ = _type; \
+ *p++ = 4; \
+ memcpy(p, &_val.s_addr, 4); \
+ p += 4; \
+ }
+ if (lease->addr.s_addr &&
+ lease->addr.s_addr != iface->addr.s_addr &&
+ !IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
+ {
+ PUTADDR(DHCP_ADDRESS, lease->addr);
+ if (lease->server.s_addr)
+ PUTADDR(DHCP_SERVERID, lease->server);
+ }
+#undef PUTADDR
+
+ if (options->leasetime != 0) {
+ *p++ = DHCP_LEASETIME;
+ *p++ = 4;
+ ul = htonl(options->leasetime);
+ memcpy(p, &ul, 4);
+ p += 4;
+ }
+ }
+
+ if (type == DHCP_DISCOVER ||
+ type == DHCP_INFORM ||
+ type == DHCP_REQUEST)
+ {
+ if (options->hostname[0]) {
+ if (options->fqdn == FQDN_DISABLE) {
+ *p++ = DHCP_HOSTNAME;
+ *p++ = l = strlen(options->hostname);
+ memcpy(p, options->hostname, l);
+ p += l;
+ } else {
+ /* Draft IETF DHC-FQDN option (81) */
+ *p++ = DHCP_FQDN;
+ *p++ = (l = strlen(options->hostname)) + 3;
+ /* Flags: 0000NEOS
+ * S: 1 => Client requests Server to update
+ * a RR in DNS as well as PTR
+ * O: 1 => Server indicates to client that
+ * DNS has been updated
+ * E: 1 => Name data is DNS format
+ * N: 1 => Client requests Server to not
+ * update DNS
+ */
+ *p++ = options->fqdn & 0x9;
+ *p++ = 0; /* from server for PTR RR */
+ *p++ = 0; /* from server for A RR if S=1 */
+ memcpy(p, options->hostname, l);
+ p += l;
+ }
+ }
+
+ *p++ = DHCP_PARAMETERREQUESTLIST;
+ n_params = p;
+ *p++ = 0;
+ for (l = 0; l < sizeof(dhcp_options) / sizeof(dhcp_options[0]); l++) {
+ if (!(dhcp_options[l].type & OPT_REQUEST))
+ continue;
+ switch (dhcp_options[l].option) {
+ case DHCP_RENEWALTIME: /* FALLTHROUGH */
+ case DHCP_REBINDTIME:
+ if (type == DHCP_INFORM)
+ continue;
+ break;
+ case DHCP_CSR:
+ if (options->domscsr > 1)
+ continue;
+ break;
+ }
+ *p++ = dhcp_options[l].option;
+ }
+ if (options->domscsr)
+ *p++ = DHCP_MSCSR;
+ *n_params = p - n_params - 1;
+ }
+ *p++ = DHCP_END;
+
+#ifdef BOOTP_MESSAGE_LENTH_MIN
+ /* Some crappy DHCP servers think they have to obey the BOOTP minimum
+ * message length.
+ * They are wrong, but we should still cater for them. */
+ while (p - m < BOOTP_MESSAGE_LENTH_MIN)
+ *p++ = DHCP_PAD;
+#endif
+
+ *message = dhcp;
+ return p - m;
}
-static bool
-get_string(char **ptr, const unsigned char *data, size_t len)
+ssize_t
+write_lease(const struct interface *iface, const struct dhcp_message *dhcp)
{
- if (*ptr)
- free(*ptr);
- *ptr = xmalloc(len + 1);
- memcpy(*ptr, data, len);
- (*ptr)[len] = '\0';
- return true;
+ int fd;
+ ssize_t bytes = sizeof(*dhcp);
+ const uint8_t *p = dhcp->options;
+ const uint8_t *e = p + sizeof(dhcp->options);
+ uint8_t l;
+ uint8_t o = 0;
+
+ fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd == -1)
+ return -1;
+
+ /* Only write as much as we need */
+ while (p < e) {
+ o = *p;
+ if (o == DHCP_END) {
+ bytes = p - (const uint8_t *)dhcp;
+ break;
+ }
+ p++;
+ if (o != DHCP_PAD) {
+ l = *p++;
+ p += l;
+ }
+ }
+ bytes = write(fd, dhcp, bytes);
+ close(fd);
+ return bytes;
}
-static bool
-get_value(void *ptr, const unsigned char *data, size_t len,
- char option, size_t lencheck)
+struct dhcp_message *
+read_lease(const struct interface *iface)
{
- if (lencheck && len != lencheck) {
- logger(LOG_ERR, "invalid length %zu for option %i",
- len, option);
- return false;
+ int fd;
+ struct dhcp_message *dhcp;
+ ssize_t bytes;
+
+ fd = open(iface->leasefile, O_RDONLY);
+ if (fd == -1)
+ return NULL;
+ dhcp = xmalloc(sizeof(*dhcp));
+ memset(dhcp, 0, sizeof(*dhcp));
+ bytes = read(fd, dhcp, sizeof(*dhcp));
+ close(fd);
+ if (bytes < 0) {
+ free(dhcp);
+ dhcp = NULL;
}
+ return dhcp;
+}
- memcpy(ptr, data, len);
- return true;
+#ifdef ENABLE_INFO
+/* Create a malloced string of cstr, changing ' to '\''
+ * so the contents work in a shell */
+char *
+clean_metas(const char *cstr)
+{
+ const char *p = cstr;
+ char *new;
+ char *n;
+ size_t len;
+ size_t pos;
+
+ if (cstr == NULL || (len = strlen(cstr)) == 0)
+ return (xstrdup(""));
+
+ n = new = xmalloc(sizeof(char) * len + 2);
+ do
+ if (*p == '\'') {
+ pos = n - new;
+ len += 4;
+ new = xrealloc(new, sizeof(char) * len + 1);
+ n = new + pos;
+ *n++ = '\'';
+ *n++ = '\\';
+ *n++ = '\'';
+ *n++ = '\'';
+ } else
+ *n++ = *p;
+ while (*p++);
+
+ /* Terminate the sucker */
+ *n = '\0';
+
+ return new;
}
-int
-parse_dhcpmessage(struct dhcp *dhcp, const struct dhcp_message *message)
+ssize_t
+write_options(FILE *f, const struct dhcp_message *dhcp)
{
- const unsigned char *p = message->options;
- const unsigned char *end = p; /* Add size later for gcc-3 issue */
- unsigned char option;
- unsigned char length;
- unsigned int len = 0;
- int retval = -1;
- struct timeval tv;
- struct route_head *routers = NULL;
- struct route_head *routes = NULL;
- struct route_head *csr = NULL;
- struct route_head *mscsr = NULL;
- bool in_overload = false;
- bool parse_sname = false;
- bool parse_file = false;
-
- end += sizeof(message->options);
-
- if (gettimeofday(&tv, NULL) == -1) {
- logger(LOG_ERR, "gettimeofday: %s", strerror(errno));
- return -1;
- }
+ uint8_t i;
+ const uint8_t *p, *e, *t;
+ char *s;
+ char *c;
+ uint32_t u32;
+ uint16_t u16;
+ uint8_t u8;
+ struct in_addr addr;
+ ssize_t retval = 0;
- dhcp->address.s_addr = message->yiaddr;
- dhcp->leasedfrom = tv.tv_sec;
- dhcp->frominfo = false;
- dhcp->address.s_addr = message->yiaddr;
- strlcpy(dhcp->servername, (char *)message->servername,
- sizeof(dhcp->servername));
-
-/* Handy macros to make the get_* functions easier to use */
-#define GET_UINT8(var) get_value(&(var), p, length, option, sizeof(uint8_t))
-#define GET_UINT16(var) get_value(&(var), p, length, option, sizeof(uint16_t))
-#define GET_UINT32(var) get_value(&(var), p, length, option, sizeof(uint32_t))
-#define GET_UINT16_H(var) if (GET_UINT16(var)) var = ntohs(var)
-#define GET_UINT32_H(var) if (GET_UINT32(var)) var = ntohl(var)
-#define GET_STR(var) get_string(&(var), p, length)
-#define GET_ADDR(var) add_addr(&var, p, length, option)
-
-#define LEN_ERR \
- { \
- logger (LOG_ERR, "invalid length %d for option %d", \
- length, option); \
- p += length; \
- continue; \
- }
-#define LENGTH(_length) if (length != _length) LEN_ERR;
-#define MIN_LENGTH(_length) if (length < _length) LEN_ERR;
-#define MULT_LENGTH(_mult) if (length % _mult != 0) LEN_ERR;
-
-parse_start:
- while (p < end) {
- option = *p++;
- if (!option)
+ for (i = 0; i < sizeof(dhcp_options) / sizeof(dhcp_options[0]); i++) {
+ if (!dhcp_options[i].var)
continue;
- if (option == DHCP_END)
- goto eexit;
- length = *p++;
- if (option != DHCP_PAD && length == 0) {
- logger(LOG_ERR, "option %d has zero length, skipping",
- option);
- continue;
+ retval += fprintf(f, "%s=", dhcp_options[i].var);
+
+ if (dhcp_options[i].type & OPT_STRING) {
+ s = get_option_string(dhcp, dhcp_options[i].type);
+ if (s) {
+ c = clean_metas(s);
+ retval += fprintf(f, "'%s'", c);
+ free(c);
+ free(s);
+ }
}
- if (p + length >= end) {
- logger(LOG_ERR, "dhcp option exceeds message length");
- retval = -1;
- goto eexit;
+
+ if (dhcp_options[i].type & OPT_UINT32) {
+ if (get_option_uint32(&u32, dhcp,
+ dhcp_options[i].option) == 0)
+ retval += fprintf(f, "%d", u32);
}
- switch (option) {
- case DHCP_MESSAGETYPE:
- retval = (int)*p;
- break;
- case DHCP_ADDRESS:
- GET_UINT32(dhcp->address.s_addr);
- break;
- case DHCP_NETMASK:
- GET_UINT32(dhcp->netmask.s_addr);
- break;
- case DHCP_BROADCAST:
- GET_UINT32(dhcp->broadcast.s_addr);
- break;
- case DHCP_SERVERIDENTIFIER:
- GET_UINT32(dhcp->serveraddress.s_addr);
- break;
- case DHCP_LEASETIME:
- GET_UINT32_H(dhcp->leasetime);
- break;
- case DHCP_RENEWALTIME:
- GET_UINT32_H(dhcp->renewaltime);
- break;
- case DHCP_REBINDTIME:
- GET_UINT32_H(dhcp->rebindtime);
- break;
- case DHCP_MTU:
- GET_UINT16_H (dhcp->mtu);
- /* Minimum legal mtu is 68 accoridng to
- * RFC 2132. In practise it's 576 which is the
- * minimum maximum message size. */
- if (dhcp->mtu < MTU_MIN) {
- logger(LOG_DEBUG,
- "MTU %d is too low, minimum is %d; ignoring",
- dhcp->mtu, MTU_MIN);
- dhcp->mtu = 0;
- }
- break;
- case DHCP_HOSTNAME:
- GET_STR(dhcp->hostname);
- break;
- case DHCP_DNSDOMAIN:
- GET_STR(dhcp->dnsdomain);
- break;
- case DHCP_MESSAGE:
- GET_STR(dhcp->message);
- break;
-#ifdef ENABLE_INFO
- case DHCP_ROOTPATH:
- GET_STR(dhcp->rootpath);
- break;
-#endif
-#ifdef ENABLE_NIS
- case DHCP_NISDOMAIN:
- GET_STR(dhcp->nisdomain);
- break;
-#endif
- case DHCP_DNSSERVER:
- GET_ADDR(dhcp->dnsservers);
- break;
-#ifdef ENABLE_NTP
- case DHCP_NTPSERVER:
- GET_ADDR(dhcp->ntpservers);
- break;
-#endif
-#ifdef ENABLE_NIS
- case DHCP_NISSERVER:
- GET_ADDR(dhcp->nisservers);
- break;
-#endif
+ if (dhcp_options[i].type & OPT_UINT16) {
+ if (get_option_uint16(&u16, dhcp,
+ dhcp_options[i].option) == 0)
+ retval += fprintf(f, "%d", u16);
+ }
- case DHCP_DNSSEARCH:
- MIN_LENGTH(1);
- free(dhcp->dnssearch);
- len = decode_search(p, length, NULL);
- if (len > 0) {
- dhcp->dnssearch = xmalloc(len);
- decode_search(p, length, dhcp->dnssearch);
- }
- break;
- case DHCP_CSR:
- MIN_LENGTH(5);
- free_route(csr);
- csr = decode_CSR(p, length);
- break;
- case DHCP_MSCSR:
- MIN_LENGTH(5);
- free_route(mscsr);
- mscsr = decode_CSR(p, length);
- break;
-#ifdef ENABLE_INFO
- case DHCP_SIPSERVER:
- free(dhcp->sipservers);
- dhcp->sipservers = decode_sipservers(p, length);
- break;
-#endif
- case DHCP_STATICROUTE:
- MULT_LENGTH(8);
- free_route(routes);
- routes = decode_routes(p, length);
- break;
- case DHCP_ROUTERS:
- MULT_LENGTH(4);
- free_route(routers);
- routers = decode_routers(p, length);
- break;
- case DHCP_OPTIONSOVERLOADED:
- LENGTH(1);
- /* The overloaded option in an overloaded option
- * should be ignored, overwise we may get an
- * infinite loop */
- if (!in_overload) {
- if (*p & 1)
- parse_file = true;
- if (*p & 2)
- parse_sname = true;
+ if (dhcp_options[i].type & OPT_IPV4) {
+ p = get_option(dhcp, dhcp_options[i].option);
+ if (p) {
+ if (fputc('\'', f))
+ retval++;
+ u8 = *p++;
+ t = p;
+ e = p + u8;
+ while (p < e) {
+ memcpy(&addr.s_addr, p,
+ sizeof(addr.s_addr));
+ if (t != p)
+ retval += fprintf(f, " ");
+ retval += fprintf(f, "%s",
+ inet_ntoa(addr));
+ p += sizeof(addr.s_addr);
+ }
+ if (fputc('\'', f))
+ retval++;
}
- break;
- case DHCP_FQDN:
- /* We ignore replies about FQDN */
- break;
- default:
- logger (LOG_DEBUG,
- "no facility to parse DHCP code %u", option);
- break;
}
- p += length;
- }
-
-eexit:
- /* We may have options overloaded, so go back and grab them */
- if (parse_file) {
- parse_file = false;
- p = message->bootfile;
- end = p + sizeof(message->bootfile);
- in_overload = true;
- goto parse_start;
- } else if (parse_sname) {
- parse_sname = false;
- p = message->servername;
- end = p + sizeof(message->servername);
- memset(dhcp->servername, 0, sizeof(dhcp->servername));
- in_overload = true;
- goto parse_start;
- }
-
- /* Fill in any missing fields */
- if (!dhcp->netmask.s_addr)
- dhcp->netmask.s_addr = get_netmask(dhcp->address.s_addr);
- if (!dhcp->broadcast.s_addr)
- dhcp->broadcast.s_addr = dhcp->address.s_addr |
- ~dhcp->netmask.s_addr;
-
- /* If we have classess static routes then we discard
- * static routes and routers according to RFC 3442 */
- if (csr) {
- dhcp->routes = csr;
- free_route(mscsr);
- free_route(routers);
- free_route(routes);
- } else if (mscsr) {
- dhcp->routes = mscsr;
- free_route(routers);
- free_route(routes);
- } else {
- /* Ensure that we apply static routes before routers */
- if (! routes)
- routes = routers;
- else if (routers)
- STAILQ_CONCAT(routes, routers);
- dhcp->routes = routes;
+
+ if (fputc('\n', f))
+ retval++;
}
-
return retval;
}
+#endif
#ifndef DHCP_H
#define DHCP_H
-#ifdef __linux__
-# include "queue.h" /* not all libc's support queue.h, so include our own */
-#else
-# include <sys/queue.h>
-#endif
+#include <arpa/inet.h>
-#include <netinet/in_systm.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#define __FAVOR_BSD /* Nasty hack so we can use BSD semantics for UDP */
-#include <netinet/udp.h>
-#undef __FAVOUR_BSD
#include <stdint.h>
+#include "config.h"
#include "dhcpcd.h"
-#include "if.h"
+#include "net.h"
/* Max MTU - defines dhcp option length */
#define MTU_MAX 1500
DHCP_PAD = 0,
DHCP_NETMASK = 1,
DHCP_TIMEROFFSET = 2,
- DHCP_ROUTERS = 3,
+ DHCP_ROUTER = 3,
DHCP_TIMESERVER = 4,
DHCP_NAMESERVER = 5,
DHCP_DNSSERVER = 6,
DHCP_LEASETIME = 51,
DHCP_OPTIONSOVERLOADED = 52,
DHCP_MESSAGETYPE = 53,
- DHCP_SERVERIDENTIFIER = 54,
+ DHCP_SERVERID = 54,
DHCP_PARAMETERREQUESTLIST = 55,
DHCP_MESSAGE = 56,
DHCP_MAXMESSAGESIZE = 57,
char *name;
};
-/* We use these structures to handle multiple routes and addresses */
-struct rt
-{
- struct in_addr destination;
- struct in_addr netmask;
- struct in_addr gateway;
- STAILQ_ENTRY (rt) entries;
-};
-STAILQ_HEAD (route_head, rt);
-
-struct address
-{
- struct in_addr address;
- STAILQ_ENTRY (address) entries;
-};
-STAILQ_HEAD (address_head, address);
-
-struct dhcp
-{
- char version[11];
-
- struct in_addr serveraddress;
- char serverhw[IF_NAMESIZE];
- char servername[64];
-
- struct in_addr address;
- struct in_addr netmask;
- struct in_addr broadcast;
- unsigned short mtu;
-
- uint32_t leasedfrom;
- uint32_t leasetime;
- uint32_t renewaltime;
- uint32_t rebindtime;
-
- struct route_head *routes;
-
- char *hostname;
- struct fqdn *fqdn;
-
- struct address_head *dnsservers;
- char *dnsdomain;
- char *dnssearch;
-
- struct address_head *ntpservers;
-
- struct address_head *nisservers;
- char *nisdomain;
-
- char *sipservers;
-
- char *message;
- char *rootpath;
-
- bool frominfo;
-};
-
/* Sizes for DHCP options */
#define DHCP_CHADDR_LEN 16
#define SERVERNAME_LEN 64
/* Some crappy DHCP servers require the BOOTP minimum length */
#define BOOTP_MESSAGE_LENTH_MIN 300
-struct dhcp_message
-{
- unsigned char op; /* message type */
- unsigned char hwtype; /* hardware address type */
- unsigned char hwlen; /* hardware address length */
- unsigned char hwopcount; /* should be zero in client message */
+struct dhcp_message {
+ uint8_t op; /* message type */
+ uint8_t hwtype; /* hardware address type */
+ uint8_t hwlen; /* hardware address length */
+ uint8_t hwopcount; /* should be zero in client message */
uint32_t xid; /* transaction id */
uint16_t secs; /* elapsed time in sec. from boot */
uint16_t flags;
uint32_t yiaddr; /* 'your' client IP address */
uint32_t siaddr; /* should be zero in client's messages */
uint32_t giaddr; /* should be zero in client's messages */
- unsigned char chaddr[DHCP_CHADDR_LEN]; /* client's hardware address */
- unsigned char servername[SERVERNAME_LEN]; /* server host name */
- unsigned char bootfile[BOOTFILE_LEN]; /* boot file name */
+ uint8_t chaddr[DHCP_CHADDR_LEN]; /* client's hardware address */
+ uint8_t servername[SERVERNAME_LEN]; /* server host name */
+ uint8_t bootfile[BOOTFILE_LEN]; /* boot file name */
uint32_t cookie;
- unsigned char options[DHCP_OPTION_LEN]; /* message options - cookie */
+ uint8_t options[DHCP_OPTION_LEN]; /* message options - cookie */
};
-struct udp_dhcp_packet
-{
- struct ip ip;
- struct udphdr udp;
- struct dhcp_message dhcp;
+struct dhcp_lease {
+ struct in_addr addr;
+ struct in_addr net;
+ uint32_t leasetime;
+ uint32_t renewaltime;
+ uint32_t rebindtime;
+ struct in_addr server;
+ uint32_t leasedfrom;
+ uint8_t frominfo;
};
-ssize_t send_message(const struct interface *, const struct dhcp *,
- uint32_t, char, const struct options *);
-void free_address(struct address_head *);
-void free_dhcp(struct dhcp *);
-void free_route(struct route_head *);
-int parse_dhcpmessage (struct dhcp *, const struct dhcp_message *);
+const uint8_t *get_option(const struct dhcp_message *, uint8_t);
+char *get_option_string(const struct dhcp_message *, uint8_t);
+int get_option_addr(uint32_t *a, const struct dhcp_message *dhcp, uint8_t option);
+int get_option_uint32(uint32_t *i, const struct dhcp_message *dhcp, uint8_t option);
+int get_option_uint16(uint16_t *i, const struct dhcp_message *dhcp, uint8_t option);
+int get_option_uint8(uint8_t *i, const struct dhcp_message *dhcp, uint8_t option);
+struct rt *get_option_routes(const struct dhcp_message *dhcp);
+struct rt *decode_rfc3442(const uint8_t *);
+ssize_t make_message(struct dhcp_message **,
+ const struct interface *, const struct dhcp_lease *,
+ uint32_t, uint8_t, const struct options *);
int valid_dhcp_packet(unsigned char *);
+ssize_t write_lease(const struct interface *, const struct dhcp_message *);
+struct dhcp_message *read_lease(const struct interface *iface);
+
+#ifdef ENABLE_INFO
+char *clean_metas(const char *cstr);
+ssize_t write_options(FILE *f, const struct dhcp_message *dhcp);
+#endif
#endif
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd Feb 20, 2008
+.Dd Apr 9, 2008
.Dt DHCPCD 8 SMM
.Sh NAME
.Nm dhcpcd
.It Fl H , --sethostname
Forces
.Nm
-to set the hostname as supplied by the DHCP server. Because some OS's and users
-prefer to have just the hostname, or the full FQDN more
-.Fl H , --sethostname
-options change the behaviour. Below is the list of possible combinations:-
-.Bl -tag -width indent
-.It Fl H
-set the hostname to the full FQDN.
-.It Fl HH
-strip the domain if it matches the dns domain.
-.It Fl HHH
-strip the domain regardless.
-.It Fl HHHH
-same as
-.Fl H
-but force hostname lookup via DNS.
-.It Fl HHHHH
-same as above, but strip the domain if it matches the dns domain.
-.It Fl HHHHHH
-same as above, but strip the domain regardless.
-.El
+to set the hostname as supplied by the DHCP server. If none supplied, lookup
+the hostname of the leased address in DNS and use that.
.It Fl I , -clientid Ar clientid
Send
.Ar clientid
Bourne shell file that holds the DHCP values used in configuring the interface.
This path is passed as the first argument to
.Pa @PREFIX@/etc/dhcpcd.sh .
+.It Pa @INFODIR@/dhcpcd- Ns Ar interface Ns .lease
+The actual DHCP message send by the server. We use this when reading the last
+lease and use the files mtime as when it was issued.
.El
.Sh SEE ALSO
.Xr ntp 1 ,
#include <getopt.h>
#include <paths.h>
#include <signal.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "client.h"
#include "dhcpcd.h"
#include "dhcp.h"
-#include "if.h"
+#include "net.h"
#include "logger.h"
#include "socket.h"
-#include "version.h"
static int doversion = 0;
static int dohelp = 0;
goto abort;
#endif
options->options &= ~DHCPCD_ARP;
+ /* IPv4LL requires ARP */
+ options->options &= ~DHCPCD_IPV4LL;
break;
case 'E':
#ifndef ENABLE_INFO
options->options &= ~DHCPCD_GATEWAY;
break;
case 'H':
- options->dohostname++;
+ options->options |= DHCPCD_HOSTNAME;
break;
case 'I':
if (optarg) {
if (options->request_address.s_addr == 0 &&
options->options & DHCPCD_INFORM)
{
- if ((options->request_address.s_addr =
- get_address(options->interface)) != 0)
+ if (get_address(options->interface,
+ &options->request_address,
+ &options->request_netmask) == 0)
options->options |= DHCPCD_KEEPADDRESS;
}
free(dhcpcd_skiproutes);
#endif
- logger(LOG_INFO, "exiting");
-
exit(retval);
/* NOTREACHED */
}
#define DHCPCD_DAEMONISE (1 << 14)
#define DHCPCD_DAEMONISED (1 << 15)
#define DHCPCD_TEST (1 << 16)
+#define DHCPCD_FORKED (1 << 17)
+#define DHCPCD_HOSTNAME (1 << 18)
struct options {
char interface[IF_NAMESIZE];
int metric;
int options;
- int dohostname;
+ int dohostname;
int domscsr;
struct in_addr request_address;
+++ /dev/null
-/*
- * dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
- * All rights reserved
-
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <arpa/inet.h>
-
-#include <errno.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <time.h>
-
-#include "config.h"
-#include "common.h"
-#include "duid.h"
-
-#ifdef ENABLE_DUID
-
-#define THIRTY_YEARS_IN_SECONDS 946707779
-
-size_t
-get_duid(unsigned char *duid, const struct interface *iface)
-{
- FILE *f;
- uint16_t type = 0;
- uint16_t hw = 0;
- uint32_t ul;
- time_t t;
- int x = 0;
- unsigned char *p = duid;
- size_t len = 0;
- char *line = NULL;
-
- /* If we already have a DUID then use it as it's never supposed
- * to change once we have one even if the interfaces do */
- if ((f = fopen(DUIDFILE, "r"))) {
- get_line(&line, &len, f);
- if (line) {
- len = hwaddr_aton(NULL, line);
- if (len && len <= DUID_LEN)
- hwaddr_aton(duid, line);
- free(line);
- } else
- len = 0;
- fclose(f);
- if (len)
- return len;
- } else {
- if (errno != ENOENT)
- return 0;
- }
-
- /* No file? OK, lets make one based on our interface */
- if (!(f = fopen(DUIDFILE, "w")))
- return 0;
-
- type = htons(1); /* DUI-D-LLT */
- memcpy(p, &type, 2);
- p += 2;
-
- hw = htons(iface->family);
- memcpy(p, &hw, 2);
- p += 2;
-
- /* time returns seconds from jan 1 1970, but DUID-LLT is
- * seconds from jan 1 2000 modulo 2^32 */
- t = time(NULL) - THIRTY_YEARS_IN_SECONDS;
- ul = htonl(t & 0xffffffff);
- memcpy(p, &ul, 4);
- p += 4;
-
- /* Finally, add the MAC address of the interface */
- memcpy(p, iface->hwaddr, iface->hwlen);
- p += iface->hwlen;
-
- len = p - duid;
-
- x = fprintf(f, "%s\n", hwaddr_ntoa(duid, len));
- fclose(f);
-
- /* Failed to write the duid? scrub it, we cannot use it */
- if (x < 1) {
- len = 0;
- unlink(DUIDFILE);
- }
-
- return len;
-}
-#endif
+++ /dev/null
-/*
- * dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
- * All rights reserved
-
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef DUID_H
-#define DUID_H
-
-#include "config.h"
-
-#ifdef ENABLE_DUID
-#ifndef DUID_LEN
-# define DUID_LEN 128 + 2
-#endif
-
-#include "if.h"
-
-size_t get_duid(unsigned char *, const struct interface *);
-#endif
-#endif
#include "config.h"
#include "common.h"
#include "dhcp.h"
-#include "if.h"
+#include "net.h"
/* Darwin doesn't define this for some very odd reason */
#ifndef SA_SIZE
else
rtm.hdr.rtm_type = RTM_DELETE;
rtm.hdr.rtm_flags = RTF_UP | RTF_STATIC;
+ if (netmask->s_addr == INADDR_BROADCAST)
+ rtm.hdr.rtm_flags |= RTF_HOST;
/* This order is important */
rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
ADDADDR(destination);
- if (netmask->s_addr == INADDR_BROADCAST ||
- gateway->s_addr == INADDR_ANY)
+ if (gateway->s_addr == INADDR_ANY)
{
/* Make us a link layer socket */
- if (netmask->s_addr == INADDR_BROADCAST)
- rtm.hdr.rtm_flags |= RTF_HOST;
-
hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
- do_interface(ifname, hwaddr, &hwlen, NULL, false, false);
+ do_interface(ifname, hwaddr, &hwlen, NULL, 0, 0);
memset(&su, 0, sizeof(su));
su.sdl.sdl_len = sizeof(su.sdl);
su.sdl.sdl_family = AF_LINK;
#include "config.h"
#include "common.h"
#include "dhcp.h"
-#include "if.h"
+#include "net.h"
/* This netlink stuff is overly compex IMO.
* The BSD implementation is much cleaner and a lot less code.
else {
nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
nlm->rt.rtm_protocol = RTPROT_BOOT;
- if (netmask->s_addr == INADDR_BROADCAST ||
- gateway->s_addr == INADDR_ANY)
+ if (gateway->s_addr == INADDR_ANY)
nlm->rt.rtm_scope = RT_SCOPE_LINK;
else
nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE;
nlm->rt.rtm_dst_len = inet_ntocidr(*netmask);
add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST,
&destination->s_addr, sizeof(destination->s_addr));
- if (netmask->s_addr != INADDR_BROADCAST &&
- destination->s_addr != gateway->s_addr)
- add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY,
- &gateway->s_addr, sizeof(gateway->s_addr));
+ add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY,
+ &gateway->s_addr, sizeof(gateway->s_addr));
add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, ifindex);
add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, metric);
+++ /dev/null
-/*
- * dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
- * All rights reserved
-
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-
-#include <arpa/inet.h>
-#ifdef AF_LINK
-# include <net/if_dl.h>
-#endif
-
-#include <ctype.h>
-#include <errno.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "config.h"
-#include "common.h"
-#include "dhcp.h"
-#include "if.h"
-
-int
-inet_ntocidr(struct in_addr address)
-{
- int cidr = 0;
- uint32_t mask = htonl(address.s_addr);
-
- while (mask) {
- cidr++;
- mask <<= 1;
- }
-
- return cidr;
-}
-
-int
-inet_cidrtoaddr (int cidr, struct in_addr *addr)
-{
- int ocets;
-
- if (cidr < 0 || cidr > 32) {
- errno = EINVAL;
- return -1;
- }
- ocets = (cidr + 7) / 8;
-
- addr->s_addr = 0;
- if (ocets > 0) {
- memset(&addr->s_addr, 255, (size_t)ocets - 1);
- memset((unsigned char *)&addr->s_addr + (ocets - 1),
- (256 - (1 << (32 - cidr) % 8)), 1);
- }
-
- return 0;
-}
-
-uint32_t
-get_netmask(uint32_t addr)
-{
- uint32_t dst;
-
- if (addr == 0)
- return 0;
-
- dst = htonl(addr);
- if (IN_CLASSA(dst))
- return ntohl(IN_CLASSA_NET);
- if (IN_CLASSB (dst))
- return ntohl(IN_CLASSB_NET);
- if (IN_CLASSC (dst))
- return ntohl(IN_CLASSC_NET);
-
- return 0;
-}
-
-char *
-hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen)
-{
- static char buffer[(HWADDR_LEN * 3) + 1];
- char *p = buffer;
- size_t i;
-
- for (i = 0; i < hwlen && i < HWADDR_LEN; i++) {
- if (i > 0)
- *p ++= ':';
- p += snprintf(p, 3, "%.2x", hwaddr[i]);
- }
-
- *p ++= '\0';
-
- return buffer;
-}
-
-size_t
-hwaddr_aton(unsigned char *buffer, const char *addr)
-{
- char c[3];
- const char *p = addr;
- unsigned char *bp = buffer;
- size_t len = 0;
-
- c[2] = '\0';
- while (*p) {
- c[0] = *p++;
- c[1] = *p++;
- /* Ensure that next data is EOL or a seperator with data */
- if (!(*p == '\0' || (*p == ':' && *(p + 1) != '\0'))) {
- errno = EINVAL;
- return 0;
- }
- /* Ensure that digits are hex */
- if (isxdigit ((int)c[0]) == 0 || isxdigit((int)c[1]) == 0) {
- errno = EINVAL;
- return 0;
- }
- p++;
- if (bp)
- *bp++ = (unsigned char)strtol(c, NULL, 16);
- else
- len++;
- }
-
- if (bp)
- return bp - buffer;
- return len;
-}
-
-int
-do_interface(const char *ifname,
- _unused unsigned char *hwaddr, _unused size_t *hwlen,
- struct in_addr *addr, bool flush, bool get)
-{
- int s;
- struct ifconf ifc;
- int retval = 0;
- int len = 10 * sizeof(struct ifreq);
- int lastlen = 0;
- char *p;
- union {
- char *buffer;
- struct ifreq *ifr;
- } ifreqs;
- struct sockaddr_in address;
- struct ifreq *ifr;
- struct sockaddr_in netmask;
-
-#ifdef AF_LINK
- struct sockaddr_dl sdl;
-#endif
-
- if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
- return -1;
-
- /* Not all implementations return the needed buffer size for
- * SIOGIFCONF so we loop like so for all until it works */
- memset(&ifc, 0, sizeof(ifc));
- for (;;) {
- ifc.ifc_len = len;
- ifc.ifc_buf = xmalloc((size_t)len);
- if (ioctl(s, SIOCGIFCONF, &ifc) == -1) {
- if (errno != EINVAL || lastlen != 0) {
- close(s);
- free(ifc.ifc_buf);
- return -1;
- }
- } else {
- if (ifc.ifc_len == lastlen)
- break;
- lastlen = ifc.ifc_len;
- }
-
- free(ifc.ifc_buf);
- ifc.ifc_buf = NULL;
- len *= 2;
- }
-
- for (p = ifc.ifc_buf; p < ifc.ifc_buf + ifc.ifc_len;) {
- /* Cast the ifc buffer to an ifreq cleanly */
- ifreqs.buffer = p;
- ifr = ifreqs.ifr;
-
-#ifdef __linux__
- p += sizeof(*ifr);
-#else
- p += offsetof(struct ifreq, ifr_ifru) + ifr->ifr_addr.sa_len;
-#endif
-
- if (strcmp(ifname, ifr->ifr_name) != 0)
- continue;
-
-#ifdef AF_LINK
- if (hwaddr && hwlen && ifr->ifr_addr.sa_family == AF_LINK) {
- memcpy(&sdl, &ifr->ifr_addr, sizeof(sdl));
- *hwlen = sdl.sdl_alen;
- memcpy(hwaddr, sdl.sdl_data + sdl.sdl_nlen,
- (size_t)sdl.sdl_alen);
- retval = 1;
- break;
- }
-#endif
-
- if (ifr->ifr_addr.sa_family == AF_INET) {
- memcpy(&address, &ifr->ifr_addr, sizeof(address));
- if (flush) {
- if (ioctl(s, SIOCGIFNETMASK, ifr) == -1)
- continue;
- memcpy(&netmask, &ifr->ifr_addr,
- sizeof(netmask));
-
- if (del_address(ifname,
- &address.sin_addr,
- &netmask.sin_addr) == -1)
- retval = -1;
- } else if (get) {
- addr->s_addr = address.sin_addr.s_addr;
- retval = 1;
- break;
- } else if (address.sin_addr.s_addr == addr->s_addr) {
- retval = 1;
- break;
- }
- }
-
- }
-
- close(s);
- free(ifc.ifc_buf);
- return retval;
-}
-
-struct interface *
-read_interface(const char *ifname, _unused int metric)
-{
- int s;
- struct ifreq ifr;
- struct interface *iface = NULL;
- unsigned char *hwaddr = NULL;
- size_t hwlen = 0;
- sa_family_t family = 0;
- unsigned short mtu;
-#ifdef __linux__
- char *p;
-#endif
-
- memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-
- if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
- return NULL;
-
-#ifdef __linux__
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
- if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1)
- goto eexit;
-
- switch (ifr.ifr_hwaddr.sa_family) {
- case ARPHRD_ETHER:
- case ARPHRD_IEEE802:
- hwlen = ETHER_ADDR_LEN;
- break;
- case ARPHRD_IEEE1394:
- hwlen = EUI64_ADDR_LEN;
- case ARPHRD_INFINIBAND:
- hwlen = INFINIBAND_ADDR_LEN;
- break;
- }
-
- hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
- memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, hwlen);
- family = ifr.ifr_hwaddr.sa_family;
-#else
- ifr.ifr_metric = metric;
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
- if (ioctl(s, SIOCSIFMETRIC, &ifr) == -1)
- goto eexit;
-
- hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
- if (do_interface(ifname, hwaddr, &hwlen, NULL, false, false) != 1)
- goto eexit;
-
- family = ARPHRD_ETHER;
-#endif
-
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
- if (ioctl(s, SIOCGIFMTU, &ifr) == -1)
- goto eexit;
-
- /* Ensure that the MTU is big enough for DHCP */
- if (ifr.ifr_mtu < MTU_MIN) {
- ifr.ifr_mtu = MTU_MIN;
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
- if (ioctl(s, SIOCSIFMTU, &ifr) == -1)
- goto eexit;
- }
- mtu = ifr.ifr_mtu;
-
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-#ifdef __linux__
- /* We can only bring the real interface up */
- if ((p = strchr(ifr.ifr_name, ':')))
- *p = '\0';
-#endif
- if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1)
- goto eexit;
-
- if (!(ifr.ifr_flags & IFF_UP) || !(ifr.ifr_flags & IFF_RUNNING)) {
- ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
- if (ioctl(s, SIOCSIFFLAGS, &ifr) != 0)
- goto eexit;
- }
-
- iface = xzalloc(sizeof(*iface));
- strlcpy(iface->name, ifname, IF_NAMESIZE);
-#ifdef ENABLE_INFO
- snprintf(iface->infofile, PATH_MAX, INFOFILE, ifname);
-#endif
- memcpy(&iface->hwaddr, hwaddr, hwlen);
- iface->hwlen = hwlen;
-
- iface->family = family;
- iface->arpable = !(ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK));
- iface->mtu = iface->previous_mtu = mtu;
-
- /* 0 is a valid fd, so init to -1 */
- iface->fd = -1;
-#ifdef __linux__
- iface->listen_fd = -1;
-#endif
-
-eexit:
- close(s);
- free(hwaddr);
- return iface;
-}
-
-int
-do_mtu(const char *ifname, short int mtu)
-{
- struct ifreq ifr;
- int r;
- int s;
-
- if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
- return -1;
-
- memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
- ifr.ifr_mtu = mtu;
- r = ioctl(s, mtu ? SIOCSIFMTU : SIOCGIFMTU, &ifr);
- close(s);
- if (r == -1)
- return -1;
- return ifr.ifr_mtu;
-}
-
-
-in_addr_t
-get_address(const char *ifname)
-{
- struct in_addr address;
- int retval;
-
- retval = do_interface(ifname, NULL, NULL, &address, false, true);
- if (retval > 0)
- return address.s_addr;
- return retval;
-}
+++ /dev/null
-/*
- * dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
- * All rights reserved
-
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/stat.h>
-
-#include <arpa/inet.h>
-
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "config.h"
-#include "common.h"
-#include "dhcp.h"
-#include "if.h"
-#include "logger.h"
-#include "info.h"
-
-#ifdef ENABLE_INFO
-
-/* Create a malloced string of cstr, changing ' to '\''
- * so the contents work in a shell */
-static char *
-cleanmetas(const char *cstr)
-{
- const char *p = cstr;
- char *new;
- char *n;
- size_t len;
- size_t pos;
-
- if (cstr == NULL || (len = strlen(cstr)) == 0)
- return (xstrdup(""));
-
- n = new = xmalloc(sizeof(char) * len + 2);
- do
- if (*p == '\'') {
- pos = n - new;
- len += 4;
- new = xrealloc(new, sizeof(char) * len + 1);
- n = new + pos;
- *n++ = '\'';
- *n++ = '\\';
- *n++ = '\'';
- *n++ = '\'';
- } else
- *n++ = *p;
- while (*p++);
-
- /* Terminate the sucker */
- *n = '\0';
-
- return new;
-}
-
-
-static void
-print_addresses(FILE *f, const struct address_head *addresses)
-{
- const struct address *addr;
-
- STAILQ_FOREACH(addr, addresses, entries) {
- fprintf(f, "%s", inet_ntoa(addr->address));
- if (STAILQ_NEXT(addr, entries))
- fprintf(f, " ");
- }
-}
-
-static void
-print_clean(FILE *f, const char *name, const char *value)
-{
- char *clean;
-
- if (! value)
- return;
-
- clean = cleanmetas(value);
- fprintf(f, "%s='%s'\n", name, clean);
- free(clean);
-}
-
-bool
-write_info(const struct interface *iface, const struct dhcp *dhcp,
- const struct options *options, bool overwrite)
-{
- FILE *f;
- struct rt *route;
- struct stat sb;
- struct in_addr addr;
- bool doneone;
-
- if (options->options & DHCPCD_TEST)
- f = stdout;
- else {
- if (!overwrite && stat(iface->infofile, &sb) == 0)
- return true;
-
- logger(LOG_DEBUG, "writing %s", iface->infofile);
- if ((f = fopen(iface->infofile, "w")) == NULL) {
- logger(LOG_ERR, "fopen `%s': %s",
- iface->infofile, strerror(errno));
- return false;
- }
- }
-
- if (dhcp->address.s_addr) {
- addr.s_addr = dhcp->address.s_addr & dhcp->netmask.s_addr;
- fprintf(f, "IPADDR='%s'\n", inet_ntoa(dhcp->address));
- fprintf(f, "NETMASK='%s'\n", inet_ntoa(dhcp->netmask));
- fprintf(f, "NETWORK='%s'\n", inet_ntoa(addr));
- fprintf(f, "BROADCAST='%s'\n", inet_ntoa(dhcp->broadcast));
- }
- if (dhcp->mtu > 0)
- fprintf(f, "MTU='%d'\n", dhcp->mtu);
-
- if (dhcp->routes) {
- doneone = false;
- fprintf(f, "ROUTES='");
- STAILQ_FOREACH(route, dhcp->routes, entries) {
- if (route->destination.s_addr != 0) {
- if (doneone)
- fprintf(f, " ");
- fprintf(f, "%s", inet_ntoa(route->destination));
- fprintf(f, ",%s", inet_ntoa(route->netmask));
- fprintf(f, ",%s", inet_ntoa(route->gateway));
- doneone = true;
- }
- }
- fprintf(f, "'\n");
-
- doneone = false;
- fprintf(f, "GATEWAYS='");
- STAILQ_FOREACH(route, dhcp->routes, entries) {
- if (route->destination.s_addr == 0) {
- if (doneone)
- fprintf(f, " ");
- fprintf(f, "%s", inet_ntoa(route->gateway));
- doneone = true;
- }
- }
- fprintf(f, "'\n");
- }
-
- print_clean(f, "HOSTNAME", dhcp->hostname);
- print_clean(f, "DNSDOMAIN", dhcp->dnsdomain);
- print_clean(f, "DNSSEARCH", dhcp->dnssearch);
-
- if (dhcp->dnsservers) {
- fprintf(f, "DNSSERVERS='");
- print_addresses(f, dhcp->dnsservers);
- fprintf(f, "'\n");
- }
-
- if (dhcp->fqdn) {
- fprintf(f, "FQDNFLAGS='%u'\n", dhcp->fqdn->flags);
- fprintf(f, "FQDNRCODE1='%u'\n", dhcp->fqdn->r1);
- fprintf(f, "FQDNRCODE2='%u'\n", dhcp->fqdn->r2);
- print_clean(f, "FQDNHOSTNAME", dhcp->fqdn->name);
- }
-
- if (dhcp->ntpservers) {
- fprintf(f, "NTPSERVERS='");
- print_addresses(f, dhcp->ntpservers);
- fprintf(f, "'\n");
- }
-
- print_clean(f, "NISDOMAIN", dhcp->nisdomain);
- if (dhcp->nisservers) {
- fprintf(f, "NISSERVERS='");
- print_addresses(f, dhcp->nisservers);
- fprintf(f, "'\n");
- }
-
- print_clean(f, "ROOTPATH", dhcp->rootpath);
- print_clean(f, "SIPSERVERS", dhcp->sipservers);
-
- if (dhcp->serveraddress.s_addr)
- fprintf(f, "DHCPSID='%s'\n", inet_ntoa(dhcp->serveraddress));
- if (dhcp->servername[0])
- print_clean(f, "DHCPSNAME", dhcp->servername);
-
- if (!(options->options & DHCPCD_INFORM) && dhcp->address.s_addr) {
- if (!(options->options & DHCPCD_TEST))
- fprintf(f, "LEASEDFROM='%u'\n", dhcp->leasedfrom);
- fprintf(f, "LEASETIME='%u'\n", dhcp->leasetime);
- fprintf(f, "RENEWALTIME='%u'\n", dhcp->renewaltime);
- fprintf(f, "REBINDTIME='%u'\n", dhcp->rebindtime);
- }
- print_clean(f, "INTERFACE", iface->name);
- print_clean(f, "CLASSID", options->classid);
- if (iface->clientid_len > 0) {
- fprintf(f, "CLIENTID='%s'\n",
- hwaddr_ntoa(iface->clientid, iface->clientid_len));
- }
- fprintf(f, "DHCPCHADDR='%s'\n",
- hwaddr_ntoa(iface->hwaddr, iface->hwlen));
-
-#ifdef ENABLE_INFO_COMPAT
- /* Support the old .info settings if we need to */
- fprintf(f, "\n# dhcpcd-1.x and 2.x compatible variables\n");
- if (dhcp->dnsservers) {
- struct address *a;
-
- fprintf(f, "DNS='");
- STAILQ_FOREACH(a, dhcp->dnsservers, entries) {
- fprintf(f, "%s", inet_ntoa(a->address));
- if (STAILQ_NEXT(a, entries))
- fprintf(f, ",");
- }
- fprintf(f, "'\n");
- }
-
- if (dhcp->routes) {
- doneone = false;
- fprintf(f, "GATEWAY='");
- STAILQ_FOREACH(route, dhcp->routes, entries) {
- if (route->destination.s_addr == 0) {
- if (doneone)
- fprintf(f, ",");
- fprintf(f, "%s", inet_ntoa(route->gateway));
- doneone = true;
- }
- }
- fprintf(f, "'\n");
- }
-#endif
-
- if (!(options->options & DHCPCD_TEST))
- fclose(f);
- return true;
-}
-
-static bool
-parse_address(struct in_addr *addr, const char *value, const char *var)
-{
- if (inet_aton(value, addr) == 0) {
- logger(LOG_ERR, "%s `%s': %s", var, value, strerror(errno));
- return false;
- }
- return true;
-}
-
-static bool
-parse_uint(unsigned int *i, const char *value, const char *var)
-{
- if (sscanf(value, "%u", i) != 1) {
- logger(LOG_ERR, "%s `%s': not a valid number", var, value);
- return false;
- }
- return true;
-}
-
-static bool
-parse_ushort(unsigned short *s, const char *value, const char *var)
-{
- if (sscanf(value, "%hu", s) != 1) {
- logger(LOG_ERR, "%s `%s': not a valid number", var, value);
- return false;
- }
- return true;
-}
-
-static struct address_head *
-parse_addresses(char *value, const char *var)
-{
- char *token;
- char *p = value;
- struct address_head *head = NULL;
- struct address *a;
-
- while ((token = strsep (&p, " "))) {
- a = xzalloc (sizeof(*a));
- if (inet_aton(token, &a->address) == 0) {
- logger(LOG_ERR, "%s: invalid address `%s'", var, token);
- free_address(head);
- free(a);
- return NULL;
- }
-
- if (!head) {
- head = xmalloc(sizeof(*head));
- STAILQ_INIT(head);
- }
- STAILQ_INSERT_TAIL(head, a, entries);
- }
-
- return head;
-}
-
-bool
-read_info(const struct interface *iface, struct dhcp *dhcp)
-{
- FILE *fp;
- char *line = NULL;
- size_t len = 0;
- char *var;
- char *value;
- char *p;
- struct stat sb;
- char *pp, *dest, *net, *gate;
- struct rt *route;
-
- if (stat(iface->infofile, &sb) != 0) {
- logger(LOG_ERR, "lease information file `%s' does not exist",
- iface->infofile);
- return false;
- }
-
- if (!(fp = fopen(iface->infofile, "r"))) {
- logger(LOG_ERR, "fopen `%s': %s",
- iface->infofile, strerror(errno));
- return false;
- }
-
- dhcp->frominfo = true;
-
- while ((get_line(&line, &len, fp))) {
- var = line;
-
- /* Strip leading spaces/tabs */
- while ((*var == ' ') || (*var == '\t'))
- var++;
-
- /* Trim trailing \n */
- p = var + strlen(var) - 1;
- if (*p == '\n')
- *p = 0;
-
- /* Skip comments */
- if (*var == '#')
- continue;
-
- /* If we don't have an equals sign then skip it */
- if (!(p = strchr(var, '=')))
- continue;
-
- /* Terminate the = so we have two strings */
- *p = 0;
-
- value = p + 1;
- /* Strip leading and trailing quotes if present */
- if (*value == '\'' || *value == '"')
- value++;
- p = value + strlen(value) - 1;
- if (*p == '\'' || *p == '"')
- *p = 0;
-
- /* Don't process null vars or values */
- if (!*var || !*value)
- continue;
-
- if (strcmp(var, "IPADDR") == 0)
- parse_address(&dhcp->address, value, "IPADDR");
- else if (strcmp(var, "NETMASK") == 0)
- parse_address (&dhcp->netmask, value, "NETMASK");
- else if (strcmp(var, "BROADCAST") == 0)
- parse_address (&dhcp->broadcast, value, "BROADCAST");
- else if (strcmp(var, "MTU") == 0)
- parse_ushort (&dhcp->mtu, value, "MTU");
- else if (strcmp(var, "ROUTES") == 0) {
- p = value;
- while ((value = strsep (&p, " "))) {
- pp = value;
- dest = strsep (&pp, ",");
- net = strsep (&pp, ",");
- gate = strsep (&pp, ",");
-
- if (!dest || !net || !gate) {
- logger(LOG_ERR,
- "read_info ROUTES `%s,%s,%s': "
- "invalid route",
- dest, net, gate);
- continue;
- }
-
- /* See if we can create a route */
- route = xzalloc(sizeof(*route));
- if (inet_aton(dest, &route->destination) == 0) {
- logger(LOG_ERR,
- "read_info ROUTES `%s': "
- "not a valid destination address",
- dest);
- free(route);
- continue;
- }
- if (inet_aton(dest, &route->netmask) == 0) {
- logger(LOG_ERR,
- "read_info ROUTES `%s': "
- "not a valid netmask address",
- net);
- free(route);
- continue;
- }
- if (inet_aton(dest, &route->gateway) == 0) {
- logger(LOG_ERR,
- "read_info ROUTES `%s': "
- "not a valid gateway address",
- gate);
- free(route);
- continue;
- }
-
- /* OK, now add our route */
- if (!dhcp->routes) {
- dhcp->routes = xmalloc(sizeof(*dhcp->routes));
- STAILQ_INIT(dhcp->routes);
- }
- STAILQ_INSERT_TAIL(dhcp->routes, route, entries);
- }
- } else if (strcmp(var, "GATEWAYS") == 0) {
- p = value;
- while ((value = strsep(&p, " "))) {
- route = xzalloc(sizeof(*route));
- if (parse_address(&route->gateway, value,
- "GATEWAYS"))
- {
- if (!dhcp->routes) {
- dhcp->routes = xmalloc(sizeof(*dhcp->routes));
- STAILQ_INIT(dhcp->routes);
- }
- STAILQ_INSERT_TAIL(dhcp->routes, route, entries);
- } else
- free(route);
- }
- } else if (strcmp(var, "HOSTNAME") == 0)
- dhcp->hostname = xstrdup(value);
- else if (strcmp (var, "DNSDOMAIN") == 0)
- dhcp->dnsdomain = xstrdup(value);
- else if (strcmp(var, "DNSSEARCH") == 0)
- dhcp->dnssearch = xstrdup(value);
- else if (strcmp(var, "DNSSERVERS") == 0)
- dhcp->dnsservers = parse_addresses(value, "DNSSERVERS");
- else if (strcmp(var, "NTPSERVERS") == 0)
- dhcp->ntpservers = parse_addresses(value, "NTPSERVERS");
- else if (strcmp(var, "NISDOMAIN") == 0)
- dhcp->nisdomain = xstrdup (value);
- else if (strcmp(var, "NISSERVERS") == 0)
- dhcp->nisservers = parse_addresses(value, "NISSERVERS");
- else if (strcmp(var, "ROOTPATH") == 0)
- dhcp->rootpath = xstrdup(value);
- else if (strcmp(var, "DHCPSID") == 0)
- parse_address(&dhcp->serveraddress, value, "DHCPSID");
- else if (strcmp(var, "DHCPSNAME") == 0)
- strlcpy(dhcp->servername, value,
- sizeof(dhcp->servername));
- else if (strcmp(var, "LEASEDFROM") == 0)
- parse_uint(&dhcp->leasedfrom, value, "LEASEDFROM");
- else if (strcmp(var, "LEASETIME") == 0)
- parse_uint(&dhcp->leasetime, value, "LEASETIME");
- else if (strcmp(var, "RENEWALTIME") == 0)
- parse_uint(&dhcp->renewaltime, value, "RENEWALTIME");
- else if (strcmp(var, "REBINDTIME") == 0)
- parse_uint(&dhcp->rebindtime, value, "REBINDTIME");
- }
-
- fclose (fp);
- free(line);
- return true;
-}
-
-#endif
-
+++ /dev/null
-/*
- * dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
- * All rights reserved
-
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef INFO_H
-#define INFO_H
-
-#include "dhcpcd.h"
-#include "if.h"
-#include "dhcp.h"
-
-#ifdef ENABLE_INFO
-bool write_info(const struct interface *, const struct dhcp *,
- const struct options *, bool);
-
-bool read_info(const struct interface *, struct dhcp *);
-#endif
-
-#endif
+++ /dev/null
-/*
- * dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
- * All rights reserved
-
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <errno.h>
-#include <stdlib.h>
-
-#include "config.h"
-#include "arp.h"
-#include "ipv4ll.h"
-
-#ifdef ENABLE_IPV4LL
-
-#ifndef ENABLE_ARP
- # error IPV4LL requires ARP
-#endif
-
-#define IPV4LL_LEASETIME 20
-
-int
-ipv4ll_get_address(struct interface *iface, struct dhcp *dhcp) {
- struct in_addr addr;
-
- for (;;) {
- addr.s_addr = htonl(LINKLOCAL_ADDR |
- (((uint32_t)abs((int)random())
- % 0xFD00) + 0x0100));
- errno = 0;
- if (!arp_claim(iface, addr))
- break;
- /* Our ARP may have been interrupted */
- if (errno)
- return -1;
- }
-
- dhcp->address.s_addr = addr.s_addr;
- dhcp->netmask.s_addr = htonl(LINKLOCAL_MASK);
- dhcp->broadcast.s_addr = htonl(LINKLOCAL_BRDC);
-
- /* Finally configure some DHCP like lease times */
- dhcp->leasetime = IPV4LL_LEASETIME;
- dhcp->renewaltime = dhcp->leasetime * 0.5;
- dhcp->rebindtime = dhcp->leasetime * 0.875;
-
- return 0;
-}
-
-#endif
+++ /dev/null
-/*
- * dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
- * All rights reserved
-
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef IPV4LL_H
-#define IPV4LL_H
-
-#ifdef ENABLE_IPV4LL
-
-#include "dhcp.h"
-#include "if.h"
-
-int ipv4ll_get_address(struct interface *, struct dhcp *);
-
-#endif
-#endif
fmt2len = strlen(fmt) + len + 1;
fmt2 = pf = malloc(sizeof(char) * fmt2len);
if (fmt2) {
- strlcpy(pf, logprefix, len);
+ strlcpy(pf, logprefix, fmt2len);
pf += len;
strlcpy(pf, fmt, fmt2len - len);
vsyslog(level, fmt2, p2);
# Copyright 2008 Roy Marples <roy@marples.name>
# Setup some good default CFLAGS
-CFLAGS?= -O2
+CFLAGS?= -Os
# Default to using the C99 standard
CSTD?= c99
_CSTD!= ${_CSTD_SH}
CFLAGS+= ${_CSTD}$(shell ${_CSTD_SH})
-# Try and use some good cc flags
-_CC_FLAGS= -pedantic -Wall -Wunused -Wimplicit -Wshadow -Wformat=2 \
+# Try and use some good cc flags if we're building from git
+_CCFLAGS= -pedantic -Wall -Wunused -Wimplicit -Wshadow -Wformat=2 \
-Wmissing-declarations -Wno-missing-prototypes -Wwrite-strings \
-Wbad-function-cast -Wnested-externs -Wcomment -Winline \
-Wchar-subscripts -Wcast-align -Wno-format-nonliteral \
-Wdeclaration-after-statement -Wsequence-point -Wextra
-_CC_FLAGS_SH= for f in ${_CC_FLAGS}; do \
+_CC_FLAGS_SH= if ! test -d .git; then echo ""; else for f in ${_CCFLAGS}; do \
if ${CC} $$f -S -o /dev/null -xc /dev/null >/dev/null 2>&1; \
then printf "%s" "$$f "; fi \
- done
+ done; fi
_CC_FLAGS!= ${_CC_FLAGS_SH}
-CFLAGS+= ${_CC_FLAGS}$(shell ${CC_FLAGS_SH})
+CFLAGS+= ${_CC_FLAGS}$(shell ${_CC_FLAGS_SH})
SRC_SOCKET= bpf.c
SRC_IF= if-bsd.c
-INFODIR?= /var/db
+INFODIR= /var/db
${PROG}: ${SCRIPTS} ${OBJS}
${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD}
-# We could save about 500 bytes by building it like this
+# We could save about 600 bytes by building it like this
# instead of the more traditional method above
-#${PROG}: ${SRCS}
-# echo "" > _${PROG}.c
-# for src in ${SRCS}; do echo "#include \"$$src\"" >> _${PROG}.c; done
-# ${CC} ${CFLAGS} -DSYSLOG_NAMES -c _${PROG}.c -o _${PROG}.o
-# ${CC} ${LDFLAGS} -o $@ _${PROG}.o ${LDADD}
+small: ${SRCS}
+ echo "" > _${PROG}.c
+ for src in ${SRCS}; do echo "#include \"$$src\"" >> _${PROG}.c; done
+ ${CC} ${CFLAGS} -DSYSLOG_NAMES -c _${PROG}.c -o _${PROG}.o
+ ${CC} ${LDFLAGS} -o ${PROG} _${PROG}.o ${LDADD}
_proginstall: ${PROG}
${INSTALL} -d ${DESTDIR}${BINDIR}
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <arpa/inet.h>
+#include <netinet/in_systm.h>
+#ifdef __linux__
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */
+#include <netinet/udp.h>
+#undef __FAVOR_BSD
+#include <arpa/inet.h>
+#ifdef AF_LINK
+# include <net/if_dl.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <poll.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+#include "logger.h"
+#include "net.h"
+#include "signal.h"
+
+int
+inet_ntocidr(struct in_addr address)
+{
+ int cidr = 0;
+ uint32_t mask = htonl(address.s_addr);
+
+ while (mask) {
+ cidr++;
+ mask <<= 1;
+ }
+
+ return cidr;
+}
+
+int
+inet_cidrtoaddr (int cidr, struct in_addr *addr)
+{
+ int ocets;
+
+ if (cidr < 0 || cidr > 32) {
+ errno = EINVAL;
+ return -1;
+ }
+ ocets = (cidr + 7) / 8;
+
+ addr->s_addr = 0;
+ if (ocets > 0) {
+ memset(&addr->s_addr, 255, (size_t)ocets - 1);
+ memset((unsigned char *)&addr->s_addr + (ocets - 1),
+ (256 - (1 << (32 - cidr) % 8)), 1);
+ }
+
+ return 0;
+}
+
+uint32_t
+get_netmask(uint32_t addr)
+{
+ uint32_t dst;
+
+ if (addr == 0)
+ return 0;
+
+ dst = htonl(addr);
+ if (IN_CLASSA(dst))
+ return ntohl(IN_CLASSA_NET);
+ if (IN_CLASSB (dst))
+ return ntohl(IN_CLASSB_NET);
+ if (IN_CLASSC (dst))
+ return ntohl(IN_CLASSC_NET);
+
+ return 0;
+}
+
+char *
+hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen)
+{
+ static char buffer[(HWADDR_LEN * 3) + 1];
+ char *p = buffer;
+ size_t i;
+
+ for (i = 0; i < hwlen && i < HWADDR_LEN; i++) {
+ if (i > 0)
+ *p ++= ':';
+ p += snprintf(p, 3, "%.2x", hwaddr[i]);
+ }
+
+ *p ++= '\0';
+
+ return buffer;
+}
+
+size_t
+hwaddr_aton(unsigned char *buffer, const char *addr)
+{
+ char c[3];
+ const char *p = addr;
+ unsigned char *bp = buffer;
+ size_t len = 0;
+
+ c[2] = '\0';
+ while (*p) {
+ c[0] = *p++;
+ c[1] = *p++;
+ /* Ensure that next data is EOL or a seperator with data */
+ if (!(*p == '\0' || (*p == ':' && *(p + 1) != '\0'))) {
+ errno = EINVAL;
+ return 0;
+ }
+ /* Ensure that digits are hex */
+ if (isxdigit ((int)c[0]) == 0 || isxdigit((int)c[1]) == 0) {
+ errno = EINVAL;
+ return 0;
+ }
+ p++;
+ if (bp)
+ *bp++ = (unsigned char)strtol(c, NULL, 16);
+ else
+ len++;
+ }
+
+ if (bp)
+ return bp - buffer;
+ return len;
+}
+
+int
+do_interface(const char *ifname,
+ _unused unsigned char *hwaddr, _unused size_t *hwlen,
+ struct in_addr *addr, struct in_addr *net, int get)
+{
+ int s;
+ struct ifconf ifc;
+ int retval = 0;
+ int len = 10 * sizeof(struct ifreq);
+ int lastlen = 0;
+ char *p;
+ union {
+ char *buffer;
+ struct ifreq *ifr;
+ } ifreqs;
+ struct sockaddr_in address;
+ struct ifreq *ifr;
+ struct sockaddr_in netmask;
+
+#ifdef AF_LINK
+ struct sockaddr_dl sdl;
+#endif
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return -1;
+
+ /* Not all implementations return the needed buffer size for
+ * SIOGIFCONF so we loop like so for all until it works */
+ memset(&ifc, 0, sizeof(ifc));
+ for (;;) {
+ ifc.ifc_len = len;
+ ifc.ifc_buf = xmalloc((size_t)len);
+ if (ioctl(s, SIOCGIFCONF, &ifc) == -1) {
+ if (errno != EINVAL || lastlen != 0) {
+ close(s);
+ free(ifc.ifc_buf);
+ return -1;
+ }
+ } else {
+ if (ifc.ifc_len == lastlen)
+ break;
+ lastlen = ifc.ifc_len;
+ }
+
+ free(ifc.ifc_buf);
+ ifc.ifc_buf = NULL;
+ len *= 2;
+ }
+
+ for (p = ifc.ifc_buf; p < ifc.ifc_buf + ifc.ifc_len;) {
+ /* Cast the ifc buffer to an ifreq cleanly */
+ ifreqs.buffer = p;
+ ifr = ifreqs.ifr;
+
+#ifndef __linux__
+ if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
+ p += offsetof(struct ifreq, ifr_ifru) +
+ ifr->ifr_addr.sa_len;
+ else
+#endif
+ p += sizeof(*ifr);
+
+ if (strcmp(ifname, ifr->ifr_name) != 0)
+ continue;
+
+#ifdef AF_LINK
+ if (hwaddr && hwlen && ifr->ifr_addr.sa_family == AF_LINK) {
+ memcpy(&sdl, &ifr->ifr_addr, sizeof(sdl));
+ *hwlen = sdl.sdl_alen;
+ memcpy(hwaddr, sdl.sdl_data + sdl.sdl_nlen,
+ (size_t)sdl.sdl_alen);
+ retval = 1;
+ break;
+ }
+#endif
+
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ memcpy(&address, &ifr->ifr_addr, sizeof(address));
+ if (ioctl(s, SIOCGIFNETMASK, ifr) == -1)
+ continue;
+ memcpy(&netmask, &ifr->ifr_addr, sizeof(netmask));
+ if (get) {
+ addr->s_addr = address.sin_addr.s_addr;
+ net->s_addr = netmask.sin_addr.s_addr;
+ retval = 1;
+ break;
+ } else {
+ if (address.sin_addr.s_addr == addr->s_addr &&
+ (!net ||
+ netmask.sin_addr.s_addr == net->s_addr))
+ {
+ retval = 1;
+ break;
+ }
+ }
+ }
+
+ }
+
+ close(s);
+ free(ifc.ifc_buf);
+ return retval;
+}
+
+struct interface *
+read_interface(const char *ifname, _unused int metric)
+{
+ int s;
+ struct ifreq ifr;
+ struct interface *iface = NULL;
+ unsigned char *hwaddr = NULL;
+ size_t hwlen = 0;
+ sa_family_t family = 0;
+ unsigned short mtu;
+#ifdef __linux__
+ char *p;
+#endif
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return NULL;
+
+#ifdef __linux__
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1)
+ goto eexit;
+
+ switch (ifr.ifr_hwaddr.sa_family) {
+ case ARPHRD_ETHER:
+ case ARPHRD_IEEE802:
+ hwlen = ETHER_ADDR_LEN;
+ break;
+ case ARPHRD_IEEE1394:
+ hwlen = EUI64_ADDR_LEN;
+ case ARPHRD_INFINIBAND:
+ hwlen = INFINIBAND_ADDR_LEN;
+ break;
+ }
+
+ hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
+ memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, hwlen);
+ family = ifr.ifr_hwaddr.sa_family;
+#else
+ ifr.ifr_metric = metric;
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCSIFMETRIC, &ifr) == -1)
+ goto eexit;
+
+ hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
+ if (do_interface(ifname, hwaddr, &hwlen, NULL, NULL, 0) != 1)
+ goto eexit;
+
+ family = ARPHRD_ETHER;
+#endif
+
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFMTU, &ifr) == -1)
+ goto eexit;
+
+ /* Ensure that the MTU is big enough for DHCP */
+ if (ifr.ifr_mtu < MTU_MIN) {
+ ifr.ifr_mtu = MTU_MIN;
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCSIFMTU, &ifr) == -1)
+ goto eexit;
+ }
+ mtu = ifr.ifr_mtu;
+
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+#ifdef __linux__
+ /* We can only bring the real interface up */
+ if ((p = strchr(ifr.ifr_name, ':')))
+ *p = '\0';
+#endif
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1)
+ goto eexit;
+
+ if (!(ifr.ifr_flags & IFF_UP) || !(ifr.ifr_flags & IFF_RUNNING)) {
+ ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+ if (ioctl(s, SIOCSIFFLAGS, &ifr) != 0)
+ goto eexit;
+ }
+
+ iface = xzalloc(sizeof(*iface));
+ strlcpy(iface->name, ifname, IF_NAMESIZE);
+ snprintf(iface->leasefile, PATH_MAX, LEASEFILE, ifname);
+#ifdef ENABLE_INFO
+ snprintf(iface->infofile, PATH_MAX, INFOFILE, ifname);
+#endif
+ memcpy(&iface->hwaddr, hwaddr, hwlen);
+ iface->hwlen = hwlen;
+
+ iface->family = family;
+ iface->arpable = !(ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK));
+ iface->mtu = iface->initial_mtu = mtu;
+
+ /* 0 is a valid fd, so init to -1 */
+ iface->fd = -1;
+ iface->udp_fd = -1;
+
+eexit:
+ close(s);
+ free(hwaddr);
+ return iface;
+}
+
+int
+do_mtu(const char *ifname, short int mtu)
+{
+ struct ifreq ifr;
+ int r;
+ int s;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_mtu = mtu;
+ r = ioctl(s, mtu ? SIOCSIFMTU : SIOCGIFMTU, &ifr);
+ close(s);
+ if (r == -1)
+ return -1;
+ return ifr.ifr_mtu;
+}
+
+void
+free_routes(struct rt *routes)
+{
+ struct rt *r;
+
+ while (routes) {
+ r = routes->next;
+ free(routes);
+ routes = r;
+ }
+}
+
+int
+open_udp_socket(struct interface *iface)
+{
+ int s;
+ union sockunion {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ } su;
+ int n = 1;
+
+ if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+ return -1;
+
+ memset(&su, 0, sizeof(su));
+ su.sin.sin_family = AF_INET;
+ su.sin.sin_port = htons(DHCP_CLIENT_PORT);
+ su.sin.sin_addr.s_addr = iface->addr.s_addr;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1)
+ goto eexit;
+ /* As we don't actually use this socket for anything, set
+ * the receiver buffer to 1 */
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1)
+ goto eexit;
+ if (bind(s, &su.sa, sizeof(su)) == -1)
+ goto eexit;
+
+ iface->udp_fd = s;
+ close_on_exec(s);
+ return 0;
+
+eexit:
+ close(s);
+ return -1;
+}
+
+struct udp_dhcp_packet
+{
+ struct ip ip;
+ struct udphdr udp;
+ struct dhcp_message dhcp;
+};
+
+static uint16_t
+checksum(uint8_t *addr, uint16_t len)
+{
+ uint32_t sum = 0;
+ union
+ {
+ uint8_t *addr;
+ uint16_t *i;
+ } p;
+ uint16_t nleft = len;
+ uint8_t a = 0;
+
+ p.addr = addr;
+ while (nleft > 1) {
+ sum += *p.i++;
+ nleft -= 2;
+ }
+
+ if (nleft == 1) {
+ memcpy(&a, p.i, 1);
+ sum += ntohs(a) << 8;
+ }
+
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += (sum >> 16);
+
+ return ~sum;
+}
+
+ssize_t
+make_udp_packet(uint8_t **packet, const uint8_t *data, size_t length,
+ struct in_addr source, struct in_addr dest)
+{
+ struct udp_dhcp_packet *udpp;
+ struct ip *ip;
+ struct udphdr *udp;
+
+ udpp = xzalloc(sizeof(*udpp));
+ ip = &udpp->ip;
+ udp = &udpp->udp;
+
+ /* OK, this is important :)
+ * We copy the data to our packet and then create a small part of the
+ * ip structure and an invalid ip_len (basically udp length).
+ * We then fill the udp structure and put the checksum
+ * of the whole packet into the udp checksum.
+ * Finally we complete the ip structure and ip checksum.
+ * If we don't do the ordering like so then the udp checksum will be
+ * broken, so find another way of doing it! */
+
+ memcpy(&udpp->dhcp, data, length);
+
+ ip->ip_p = IPPROTO_UDP;
+ ip->ip_src.s_addr = source.s_addr;
+ if (dest.s_addr == 0)
+ ip->ip_dst.s_addr = INADDR_BROADCAST;
+ else
+ ip->ip_dst.s_addr = dest.s_addr;
+
+ udp->uh_sport = htons(DHCP_CLIENT_PORT);
+ udp->uh_dport = htons(DHCP_SERVER_PORT);
+ udp->uh_ulen = htons(sizeof(*udp) + length);
+ ip->ip_len = udp->uh_ulen;
+ udp->uh_sum = checksum((uint8_t *)udpp, sizeof(*udpp));
+
+ ip->ip_v = IPVERSION;
+ ip->ip_hl = 5;
+ ip->ip_id = 0;
+ ip->ip_tos = IPTOS_LOWDELAY;
+ ip->ip_len = htons (sizeof(*ip) + sizeof(*udp) + length);
+ ip->ip_id = 0;
+ ip->ip_off = htons(IP_DF); /* Don't fragment */
+ ip->ip_ttl = IPDEFTTL;
+
+ ip->ip_sum = checksum((uint8_t *)ip, sizeof(*ip));
+
+ *packet = (uint8_t *)udpp;
+ return sizeof(*ip) + sizeof(*udp) + length;
+}
+
+ssize_t
+get_udp_data(const uint8_t **data, const uint8_t *udp)
+{
+ union
+ {
+ const uint8_t *data;
+ const struct udp_dhcp_packet *packet;
+ } d;
+
+ d.data = udp;
+ *data = (const uint8_t *)&d.packet->dhcp;
+ return ntohs(d.packet->ip.ip_len) -
+ sizeof(d.packet->ip) -
+ sizeof(d.packet->udp);
+}
+
+int
+valid_udp_packet(uint8_t *data)
+{
+ union
+ {
+ uint8_t *data;
+ struct udp_dhcp_packet *packet;
+ } d;
+ uint16_t bytes;
+ uint16_t ipsum;
+ uint16_t iplen;
+ uint16_t udpsum;
+ struct in_addr source;
+ struct in_addr dest;
+ int retval = 0;
+
+ d.data = data;
+ bytes = ntohs(d.packet->ip.ip_len);
+ ipsum = d.packet->ip.ip_sum;
+ iplen = d.packet->ip.ip_len;
+ udpsum = d.packet->udp.uh_sum;
+
+ d.data = data;
+ d.packet->ip.ip_sum = 0;
+ if (ipsum != checksum((uint8_t *)&d.packet->ip, sizeof(d.packet->ip))) {
+ errno = EINVAL;
+ retval = -1;
+ goto eexit;
+ }
+
+ memcpy(&source, &d.packet->ip.ip_src, sizeof(d.packet->ip.ip_src));
+ memcpy(&dest, &d.packet->ip.ip_dst, sizeof(d.packet->ip.ip_dst));
+ memset(&d.packet->ip, 0, sizeof(d.packet->ip));
+ d.packet->udp.uh_sum = 0;
+
+ d.packet->ip.ip_p = IPPROTO_UDP;
+ memcpy(&d.packet->ip.ip_src, &source, sizeof(d.packet->ip.ip_src));
+ memcpy(&d.packet->ip.ip_dst, &dest, sizeof(d.packet->ip.ip_dst));
+ d.packet->ip.ip_len = d.packet->udp.uh_ulen;
+ if (udpsum && udpsum != checksum(d.data, bytes)) {
+ errno = EINVAL;
+ retval = -1;
+ }
+
+eexit:
+ d.packet->ip.ip_sum = ipsum;
+ d.packet->ip.ip_len = iplen;
+ d.packet->udp.uh_sum = udpsum;
+
+ return retval;
+}
+
+#ifdef ENABLE_ARP
+/* These are really for IPV4LL */
+#define NPROBES 3
+#define PROBE_INTERVAL 200
+#define NCLAIMS 2
+#define CLAIM_INTERVAL 200
+
+/* Linux does not seem to define these handy macros */
+#ifndef ar_sha
+#define ar_sha(ap) (((caddr_t)((ap) + 1)) + 0)
+#define ar_spa(ap) (((caddr_t)((ap) + 1)) + (ap)->ar_hln)
+#define ar_tha(ap) (((caddr_t)((ap) + 1)) + (ap)->ar_hln + (ap)->ar_pln)
+#define ar_tpa(ap) (((caddr_t)((ap) + 1)) + 2 * (ap)->ar_hln + (ap)->ar_pln)
+#endif
+
+#ifndef arphdr_len
+#define arphdr_len2(ar_hln, ar_pln) (sizeof(struct arphdr) + \
+ 2 * (ar_hln) + 2 * (ar_pln))
+#define arphdr_len(ap) (arphdr_len2((ap)->ar_hln, (ap)->ar_pln))
+#endif
+
+static int
+send_arp(const struct interface *iface, int op, struct in_addr sip,
+ const unsigned char *taddr, struct in_addr tip)
+{
+ struct arphdr *arp;
+ size_t arpsize = arphdr_len2(iface->hwlen, sizeof(sip));
+ caddr_t tha;
+ int retval;
+
+ arp = xzalloc(arpsize);
+ arp->ar_hrd = htons(iface->family);
+ arp->ar_pro = htons(ETHERTYPE_IP);
+ arp->ar_hln = iface->hwlen;
+ arp->ar_pln = sizeof(sip);
+ arp->ar_op = htons(op);
+ memcpy(ar_sha(arp), iface->hwaddr, (size_t)arp->ar_hln);
+ memcpy(ar_spa(arp), &sip, (size_t)arp->ar_pln);
+ if (taddr) {
+ /* NetBSD can return NULL from ar_tha, which is probably wrong
+ * but we still need to deal with it */
+ if (! (tha = ar_tha(arp))) {
+ free(arp);
+ errno = EINVAL;
+ return -1;
+ }
+ memcpy(tha, taddr, (size_t)arp->ar_hln);
+ }
+ memcpy(ar_tpa(arp), &tip, (size_t)arp->ar_pln);
+
+ retval = send_packet(iface, ETHERTYPE_ARP,
+ (unsigned char *) arp, arphdr_len(arp));
+ if (retval == -1)
+ logger(LOG_ERR,"send_packet: %s", strerror(errno));
+ free(arp);
+ return retval;
+}
+
+int
+arp_claim(struct interface *iface, struct in_addr address)
+{
+ struct arphdr *reply = NULL;
+ long timeout = 0;
+ unsigned char *buffer;
+ int retval = -1;
+ int nprobes = 0;
+ int nclaims = 0;
+ struct in_addr null_address;
+ struct pollfd fds[] = {
+ { -1, POLLIN, 0 },
+ { -1, POLLIN, 0 }
+ };
+ ssize_t bufpos = 0;
+ ssize_t buflen = iface->buffer_length;
+ int bytes;
+ int s = 0;
+ struct timeval stopat;
+ struct timeval now;
+ union {
+ unsigned char *c;
+ struct in_addr *a;
+ } rp;
+ union {
+ unsigned char *c;
+ struct ether_addr *a;
+ } rh;
+
+ if (!iface->arpable) {
+ logger(LOG_DEBUG, "interface `%s' is not ARPable", iface->name);
+ return 0;
+ }
+
+ if (!IN_LINKLOCAL(ntohl(iface->addr.s_addr)) &&
+ !IN_LINKLOCAL(ntohl(address.s_addr)))
+ logger(LOG_INFO,
+ "checking %s is available on attached networks",
+ inet_ntoa(address));
+
+ if (!open_socket(iface, ETHERTYPE_ARP)) {
+ logger (LOG_ERR, "open_socket: %s", strerror(errno));
+ return -1;
+ }
+
+ fds[0].fd = signal_fd();
+ fds[1].fd = iface->fd;
+ memset(&null_address, 0, sizeof(null_address));
+ buffer = xmalloc(iface->buffer_length);
+ reply = xmalloc(iface->buffer_length);
+
+ for (;;) {
+ bufpos = 0;
+ buflen = iface->buffer_length;
+ s = 0;
+
+ /* Only poll if we have a timeout */
+ if (timeout > 0) {
+ s = poll(fds, 2, timeout);
+ if (s == -1) {
+ if (errno == EINTR) {
+ if (signal_exists(NULL) == -1) {
+ errno = 0;
+ continue;
+ } else
+ break;
+ }
+
+ logger(LOG_ERR, "poll: `%s'", strerror(errno));
+ break;
+ }
+ }
+
+ /* Timed out */
+ if (s == 0) {
+ if (nprobes < NPROBES) {
+ nprobes ++;
+ timeout = PROBE_INTERVAL;
+ logger(LOG_DEBUG, "sending ARP probe #%d",
+ nprobes);
+ if (send_arp(iface, ARPOP_REQUEST,
+ null_address, NULL,
+ address) == -1)
+ break;
+
+ /* IEEE1394 cannot set ARP target address
+ * according to RFC2734 */
+ if (nprobes >= NPROBES &&
+ iface->family == ARPHRD_IEEE1394)
+ nclaims = NCLAIMS;
+ } else if (nclaims < NCLAIMS) {
+ nclaims ++;
+ timeout = CLAIM_INTERVAL;
+ logger(LOG_DEBUG, "sending ARP claim #%d",
+ nclaims);
+ if (send_arp(iface, ARPOP_REQUEST,
+ address, iface->hwaddr,
+ address) == -1)
+ break;
+ } else {
+ /* No replies, so done */
+ retval = 0;
+ break;
+ }
+
+ /* Setup our stop time */
+ if (get_time(&stopat) != 0)
+ break;
+ stopat.tv_usec += timeout;
+
+ continue;
+ }
+
+ /* We maybe ARP flooded, so check our time */
+ if (get_time(&now) != 0)
+ break;
+ if (timercmp(&now, &stopat, >)) {
+ timeout = 0;
+ continue;
+ }
+
+ if (!(fds[1].revents & POLLIN))
+ continue;
+
+ memset(buffer, 0, buflen);
+ do {
+ memset(reply, 0, iface->buffer_length);
+ if ((bytes = get_packet(iface, (unsigned char *) reply,
+ buffer,
+ &buflen, &bufpos)) == -1)
+ break;
+
+ /* Only these types are recognised */
+ if (reply->ar_op != htons(ARPOP_REPLY))
+ continue;
+
+ /* Protocol must be IP. */
+ if (reply->ar_pro != htons(ETHERTYPE_IP))
+ continue;
+ if (reply->ar_pln != sizeof(address))
+ continue;
+ if ((unsigned)bytes < sizeof(reply) +
+ 2 * (4 +reply->ar_hln))
+ continue;
+
+ rp.c = (unsigned char *)ar_spa(reply);
+ rh.c = (unsigned char *)ar_sha(reply);
+
+ /* Ensure the ARP reply is for the our address */
+ if (rp.a->s_addr != address.s_addr)
+ continue;
+
+ /* Some systems send a reply back from our hwaddress,
+ * which is wierd */
+ if (reply->ar_hln == iface->hwlen &&
+ memcmp(rh.c, iface->hwaddr, iface->hwlen) == 0)
+ continue;
+
+ logger(LOG_ERR, "ARPOP_REPLY received from %s (%s)",
+ inet_ntoa(*rp.a),
+ hwaddr_ntoa(rh.c, (size_t)reply->ar_hln));
+ retval = -1;
+ goto eexit;
+ } while (bufpos != 0);
+ }
+
+eexit:
+ close(iface->fd);
+ iface->fd = -1;
+ free(buffer);
+ free(reply);
+ return retval;
+}
+#endif
#include <netinet/if_ether.h>
#include <limits.h>
-#include <stdbool.h>
#include "config.h"
-#ifdef __linux__
-# include <asm/types.h> /* needed for 2.4 kernels for the below header */
-# include <linux/netlink.h>
-# include "queue.h" /* not all libc's support queue.h, so include our own */
-#else
-# include <sys/queue.h>
-#endif
-
#ifdef ENABLE_DUID
#ifndef DUID_LEN
# define DUID_LEN 128 + 2
# define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == LINKLOCAL_ADDR)
#endif
-#define NSTAILQ_FOREACH(var, head, field) \
- if (head) STAILQ_FOREACH (var, head, field)
+/* There is an argument that this should be converted to an STAIL using
+ * queue(3). However, that isn't readily available on all libc's that
+ * dhcpcd works on. The only benefit of STAILQ over this is the ability to
+ * quickly loop backwards through the list - currently we reverse the list
+ * and then move through it forwards. This isn't that much of a big deal
+ * though as the norm is to just have one default route, and an IPV4LL route.
+ * You can (and do) get more routes in the DHCP message, but not enough to
+ * really warrant a change to STAIL queue for performance reasons. */
+struct rt {
+ struct in_addr dest;
+ struct in_addr net;
+ struct in_addr gate;
+ struct rt *next;
+};
struct interface
{
sa_family_t family;
unsigned char hwaddr[HWADDR_LEN];
size_t hwlen;
- bool arpable;
- unsigned short mtu;
+ int arpable;
int fd;
+ int udp_fd;
size_t buffer_length;
#ifdef __linux__
- int listen_fd;
int socket_protocol;
#endif
+ char leasefile[PATH_MAX];
char infofile[PATH_MAX];
- unsigned short previous_mtu;
- struct in_addr previous_address;
- struct in_addr previous_netmask;
- struct route_head *previous_routes;
+ unsigned short initial_mtu;
+ unsigned short mtu;
+ struct in_addr addr;
+ struct in_addr net;
+ struct rt *routes;
time_t start_uptime;
#define get_mtu(iface) do_mtu(iface, 0)
#define set_mtu(iface, mtu) do_mtu(iface, mtu)
-#define add_address(ifname, addr, mask, brd) \
- if_address(ifname, addr, mask, brd, 1)
-#define del_address(ifname, addr, mask) \
- if_address(ifname, addr, mask, NULL, -1)
-#define flush_addresses(ifname) \
- do_interface(ifname, NULL, NULL, NULL, true, false)
-in_addr_t get_address(const char *);
-#define has_address(ifname, addr) \
- do_interface(ifname, NULL, NULL, (struct in_addr *)&(addr), false, false)
-
-#define add_route(ifname, dest, mask, gate, metric) \
- if_route(ifname, dest, mask, gate, metric, 1)
-#define del_route(ifname, dest, mask, gate, metric) \
- if_route(ifname, dest, mask, gate, metric, -1)
-
int inet_ntocidr(struct in_addr);
int inet_cidrtoaddr(int, struct in_addr *);
-int do_interface(const char *, unsigned char *, size_t *, struct in_addr *,
- bool, bool);
+int do_interface(const char *, unsigned char *, size_t *,
+ struct in_addr *, struct in_addr *, int);
int if_address(const char *, const struct in_addr *, const struct in_addr *,
const struct in_addr *, int);
+#define add_address(ifname, addr, net, brd) \
+ if_address(ifname, addr, net, brd, 1)
+#define del_address(ifname, addr, net) \
+ if_address(ifname, addr, net, NULL, -1)
+#define has_address(ifname, addr, net) \
+ do_interface(ifname, NULL, NULL, addr, net, 0)
+#define get_address(ifname, addr, net) \
+ do_interface(ifname, NULL, NULL, addr, net, 1)
+
int if_route(const char *, const struct in_addr *, const struct in_addr *,
const struct in_addr *, int, int);
+#define add_route(ifname, dest, mask, gate, metric) \
+ if_route(ifname, dest, mask, gate, metric, 1)
+#define del_route(ifname, dest, mask, gate, metric) \
+ if_route(ifname, dest, mask, gate, metric, -1)
+void free_routes(struct rt *);
+
+int open_udp_socket(struct interface *);
+ssize_t make_udp_packet(uint8_t **, const uint8_t *, size_t,
+ struct in_addr, struct in_addr);
+ssize_t get_udp_data(const uint8_t **, const uint8_t *);
+int valid_udp_packet(uint8_t *);
+
+#ifdef __linux__
+void setup_packet_filters(void);
+#endif
+int open_socket(struct interface *, int);
+ssize_t send_packet(const struct interface *, int,
+ const uint8_t *, ssize_t);
+ssize_t get_packet(const struct interface *, uint8_t *,
+ uint8_t *, ssize_t *, ssize_t *);
+
+#ifdef ENABLE_ARP
+int arp_claim(struct interface *, struct in_addr);
+#endif
#endif
+++ /dev/null
-/*-
- * Copyright (c) 1991, 1993
- * The Regents of the University of California. 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.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
- *
- * @(#)queue.h 8.5 (Berkeley) 8/20/94
- * $FreeBSD: src/sys/sys/queue.h,v 1.68 2006/10/24 11:20:29 ru Exp $
- */
-
-#ifndef _QUEUE_H_
-#define _QUEUE_H_
-
-/*
- * Singly-linked Tail queue declarations.
- */
-#define STAILQ_HEAD(name, type) \
-struct name { \
- struct type *stqh_first;/* first element */ \
- struct type **stqh_last;/* addr of last next element */ \
-}
-
-#define STAILQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).stqh_first }
-
-#define STAILQ_ENTRY(type) \
-struct { \
- struct type *stqe_next; /* next element */ \
-}
-
-/*
- * Singly-linked Tail queue functions.
- */
-#define STAILQ_CONCAT(head1, head2) do { \
- if (!STAILQ_EMPTY((head2))) { \
- *(head1)->stqh_last = (head2)->stqh_first; \
- (head1)->stqh_last = (head2)->stqh_last; \
- STAILQ_INIT((head2)); \
- } \
-} while (0)
-
-#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
-
-#define STAILQ_FIRST(head) ((head)->stqh_first)
-
-#define STAILQ_FOREACH(var, head, field) \
- for((var) = STAILQ_FIRST((head)); \
- (var); \
- (var) = STAILQ_NEXT((var), field))
-
-
-#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = STAILQ_FIRST((head)); \
- (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define STAILQ_INIT(head) do { \
- STAILQ_FIRST((head)) = NULL; \
- (head)->stqh_last = &STAILQ_FIRST((head)); \
-} while (0)
-
-#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
- if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
- STAILQ_NEXT((tqelm), field) = (elm); \
-} while (0)
-
-#define STAILQ_INSERT_HEAD(head, elm, field) do { \
- if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
- STAILQ_FIRST((head)) = (elm); \
-} while (0)
-
-#define STAILQ_INSERT_TAIL(head, elm, field) do { \
- STAILQ_NEXT((elm), field) = NULL; \
- *(head)->stqh_last = (elm); \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
-} while (0)
-
-#define STAILQ_LAST(head, type, field) \
- (STAILQ_EMPTY((head)) ? \
- NULL : \
- ((struct type *)(void *) \
- ((char *)((head)->stqh_last) - __offsetof(struct type, field))))
-
-#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
-
-#define STAILQ_REMOVE(head, elm, type, field) do { \
- if (STAILQ_FIRST((head)) == (elm)) { \
- STAILQ_REMOVE_HEAD((head), field); \
- } \
- else { \
- struct type *curelm = STAILQ_FIRST((head)); \
- while (STAILQ_NEXT(curelm, field) != (elm)) \
- curelm = STAILQ_NEXT(curelm, field); \
- if ((STAILQ_NEXT(curelm, field) = \
- STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
- (head)->stqh_last = &STAILQ_NEXT((curelm), field);\
- } \
- TRASHIT((elm)->field.stqe_next); \
-} while (0)
-
-#define STAILQ_REMOVE_HEAD(head, field) do { \
- if ((STAILQ_FIRST((head)) = \
- STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
- (head)->stqh_last = &STAILQ_FIRST((head)); \
-} while (0)
-
-#endif /* !_QUEUE_H_ */
#include <sys/types.h>
#include <sys/ioctl.h>
-#include <sys/param.h>
#include <sys/socket.h>
-#include <sys/uio.h>
#include <net/if.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#ifdef __linux__
+# include <asm/types.h> /* needed for 2.4 kernels for the below header */
+# include <linux/filter.h>
+# include <netpacket/packet.h>
+# define bpf_insn sock_filter
+#endif
+
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
-#ifdef __linux__
-# include <asm/types.h> /* needed for 2.4 kernels for the below header */
-# include <linux/filter.h>
-# include <netpacket/packet.h>
-# define bpf_insn sock_filter
-#endif
-
#include "config.h"
+#include "common.h"
#include "dhcp.h"
-#include "if.h"
-#include "socket.h"
+#include "net.h"
#include "bpf-filter.h"
/* A suitably large buffer for all transactions.
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
};
-
void
setup_packet_filters(void)
{
+#ifdef __linux__
/* We need to massage the filters for Linux cooked packets */
dhcp_bpf_filter[1].jf = 0; /* skip the IP packet type check */
dhcp_bpf_filter[2].k -= ETH_HLEN;
arp_bpf_filter[1].jf = 0; /* skip the IP packet type check */
arp_bpf_filter[2].k -= ETH_HLEN;
-}
-
-static int open_listen_socket(struct interface *iface)
-{
- int fd;
- union sockunion {
- struct sockaddr sa;
- struct sockaddr_in sin;
- } su;
- struct ifreq ifr;
- int n = 1;
-
- if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
- return -1;
-
- memset(&su, 0, sizeof(su));
- su.sin.sin_family = AF_INET;
- su.sin.sin_port = htons(DHCP_CLIENT_PORT);
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1)
- goto eexit;
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1)
- goto eexit;
- memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
- if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
- goto eexit;
- if (bind(fd, &su.sa, sizeof(su)) == -1)
- goto eexit;
-
- iface->listen_fd = fd;
- close_on_exec(fd);
- return 0;
-eexit:
- close(fd);
- return -1;
+ /* Some buggy Linux kernels do not work with ~0U.
+ * 65536 should be enough for anyone ;) */
+ dhcp_bpf_filter[9].k = 65536;
+ arp_bpf_filter[5].k = 65536;
+#endif
}
int
open_socket(struct interface *iface, int protocol)
{
- int fd;
+ int s;
union sockunion {
struct sockaddr sa;
struct sockaddr_in sin;
} su;
struct sock_fprog pf;
- /* We need to bind to a port, otherwise Linux generate ICMP messages
- * that cannot contect the port when we have an address.
- * We don't actually use this fd at all, instead using our packet
- * filter socket. */
- if (iface->listen_fd == -1 && protocol == ETHERTYPE_IP)
- if (open_listen_socket(iface) == -1)
- return -1;
-
- if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(protocol))) == -1)
+ if ((s = socket(PF_PACKET, SOCK_DGRAM, htons(protocol))) == -1)
return -1;
- close_on_exec(fd);
+ close_on_exec(s);
memset(&su, 0, sizeof(su));
su.sll.sll_family = PF_PACKET;
su.sll.sll_protocol = htons(protocol);
pf.filter = dhcp_bpf_filter;
pf.len = sizeof(dhcp_bpf_filter) / sizeof(dhcp_bpf_filter[0]);
}
- if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf)) != 0)
+ if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf)) != 0)
goto eexit;
- if (bind(fd, &su.sa, sizeof(su)) == -1)
+ if (bind(s, &su.sa, sizeof(su)) == -1)
goto eexit;
if (iface->fd > -1)
close(iface->fd);
- iface->fd = fd;
+ iface->fd = s;
iface->socket_protocol = protocol;
iface->buffer_length = BUFFER_LENGTH;
- return fd;
+ return s;
eexit:
- close(fd);
+ close(s);
return -1;
}
ssize_t
send_packet(const struct interface *iface, int type,
- const unsigned char *data, ssize_t len)
+ const uint8_t *data, ssize_t len)
{
union sockunion {
struct sockaddr sa;
/* Linux has no need for the buffer as we can read as much as we want.
* We only have the buffer listed to keep the same API. */
ssize_t
-get_packet(const struct interface *iface, unsigned char *data,
- unsigned char *buffer, ssize_t *buffer_len, ssize_t *buffer_pos)
+get_packet(const struct interface *iface, uint8_t *data,
+ uint8_t *buffer, ssize_t *buffer_len, ssize_t *buffer_pos)
{
ssize_t bytes;
- union
- {
- unsigned char *buffer;
- struct udp_dhcp_packet *packet;
- } pay;
struct timespec ts;
+ const uint8_t *p;
/* We don't use the given buffer, but we need to rewind the position */
*buffer_pos = 0;
return bytes;
}
- if ((unsigned)bytes < (sizeof(pay.packet->ip) +sizeof(pay.packet->udp)))
- {
- errno = EBADMSG;
- return -1;
- }
-
- pay.buffer = buffer;
- if (bytes < ntohs(pay.packet->ip.ip_len)) {
- errno = EBADMSG;
- return -1;
- }
-
- if (valid_dhcp_packet(buffer) == -1)
+ if (valid_udp_packet(buffer) != 0)
return -1;
- bytes = ntohs(pay.packet->ip.ip_len) -
- (sizeof(pay.packet->ip) + sizeof(pay.packet->udp));
- memcpy(data, &pay.packet->dhcp, bytes);
+ bytes = get_udp_data(&p, buffer);
+ memcpy(data, p, bytes);
return bytes;
}
#ifndef SOCKET_H
#define SOCKET_H
-#include <stdbool.h>
-
#include "dhcp.h"
-#include "if.h"
-
-#ifdef __linux__
-void setup_packet_filters(void);
-#endif
+#include "net.h"
-int open_socket(struct interface *, int);
-ssize_t send_packet(const struct interface *, int,
- const unsigned char *, ssize_t);
-ssize_t get_packet(const struct interface *, unsigned char *,
- unsigned char *, ssize_t *, ssize_t *);
#endif