]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Split interface and socket out into OS bpf, if-bsd and if-linux.
authorRoy Marples <roy@marples.name>
Mon, 24 Mar 2008 01:29:33 +0000 (01:29 +0000)
committerRoy Marples <roy@marples.name>
Mon, 24 Mar 2008 01:29:33 +0000 (01:29 +0000)
27 files changed:
Makefile
arp.c
arp.h
bpf-filter.h [new file with mode: 0644]
bpf.c [new file with mode: 0644]
client.c
config.h
configure.c
configure.h
dhcp.c
dhcp.h
dhcpcd.c
duid.h
if-bsd.c [new file with mode: 0644]
if-linux.c [new file with mode: 0644]
if.c [new file with mode: 0644]
if.h [moved from interface.h with 88% similarity]
info.c
info.h
interface.c [deleted file]
ipv4ll.h
mk/os-BSD.mk [new file with mode: 0644]
mk/os-Linux.mk [new file with mode: 0644]
mk/os.mk
mk/prog.mk
socket.c
socket.h

index 1db5df5872f21baa4f1b75fca6e962b9f09ae47d..d6ac36c592279d814bfc3498b7de8a7d170f4094 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,8 @@
 
 PROG=          dhcpcd
 SRCS=          arp.c client.c common.c configure.c dhcp.c dhcpcd.c duid.c \
-               info.c interface.c ipv4ll.c logger.c signal.c socket.c
+               info.c if.c ipv4ll.c logger.c signal.c \
+               ${SRC_IF} ${SRC_SOCKET}
 MAN=           dhcpcd.8
 
 VERSION=       3.2.3
@@ -12,18 +13,24 @@ CLEANFILES= version.h dhcpcd.8
 
 BINDIR=                ${PREFIX}/sbin
 
+# Work out how to restart services 
+_RC_SH=                if test -n "${HAVE_INIT}"; then \
+               test "${HAVE_INIT}" = "no" || echo "-DENABLE_${HAVE_INIT}"; \
+               elif test -x /sbin/runscript; then echo "-DENABLE_OPENRC"; \
+               elif test -x /sbin/service; then echo "-DENABLE_SERVICE"; \
+               elif test -x /etc/rc.d/rc.S -a -x /etc/rc.d/rc.M; then echo "-DENABLE_SLACKRC"; \
+               elif test -d /etc/rc.d; then echo "-DENABLE_BSDRC"; \
+               elif test -d /etc/init.d; then echo "-DENABLE_SYSV"; \
+               fi
+_RC!=          ${_RC_SH}
+CFLAGS+=       ${_RC}$(shell ${_RC_SH})
+
 .SUFFIXES:     .in
 
 MK=            mk
-include ${MK}/os.mk
-include ${MK}/cc.mk
 include ${MK}/prog.mk
 
-# os.mk should define this, but heh
-INFOD?=                /var/db
-
 LDADD+=                ${LIBRESOLV} ${LIBRT}
-CFLAGS+=       -DINFODIR=\"${INFOD}\" ${FORK} ${RC}
 
 # As version.h is generated by us, hardcode the depend correctly.
 ${SRCS}:       version.h
diff --git a/arp.c b/arp.c
index b390fa3db1319fa7ce44529f2f3ec769a9bd9f05..399af0c71db4e4fbb5845d0488e6ae1318da1311 100644 (file)
--- a/arp.c
+++ b/arp.c
@@ -48,7 +48,7 @@
 #include "config.h"
 #include "common.h"
 #include "arp.h"
-#include "interface.h"
+#include "if.h"
 #include "logger.h"
 #include "signal.h"
 #include "socket.h"
diff --git a/arp.h b/arp.h
index e8928ce7893b2174211dcdbb3f2d4b2a1fe74a48..2bc3ecf8b8a8e9f59479fcb16a9bc73fc55aef6a 100644 (file)
--- a/arp.h
+++ b/arp.h
@@ -31,7 +31,7 @@
 #ifdef ENABLE_ARP
 #include <netinet/in.h>
 
-#include "interface.h"
+#include "if.h"
 
 int arp_claim(struct interface *, struct in_addr);
 #endif
diff --git a/bpf-filter.h b/bpf-filter.h
new file mode 100644 (file)
index 0000000..17ba845
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ *
+ * 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.
+ */
+
+/* Credit where credit is due :)
+ * The below BPF filter is taken from ISC DHCP */
+static struct bpf_insn dhcp_bpf_filter [] = {
+       /* Make sure this is an IP packet... */
+       BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12),
+       BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
+
+       /* Make sure it's a UDP packet... */
+       BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 23),
+       BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
+
+       /* Make sure this isn't a fragment... */
+       BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 20),
+       BPF_JUMP (BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
+
+       /* Get the IP header length... */
+       BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14),
+
+       /* Make sure it's to the right port... */
+       BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16),
+       BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, DHCP_CLIENT_PORT, 0, 1),
+
+       /* If we passed all the tests, ask for the whole packet. */
+       BPF_STMT (BPF_RET + BPF_K, ~0U),
+
+       /* Otherwise, drop it. */
+       BPF_STMT (BPF_RET + BPF_K, 0),
+};
+
+/* This, however, is mine */
+static struct bpf_insn arp_bpf_filter [] = {
+       /* Make sure this is an ARP packet... */
+       BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12),
+       BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_ARP, 0, 3),
+
+       /* Make sure this is an ARP REPLY... */
+       BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 20),
+       BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 0, 1),
+
+       /* If we passed all the tests, ask for the whole packet. */
+       BPF_STMT (BPF_RET + BPF_K, ~0U),
+
+       /* Otherwise, drop it. */
+       BPF_STMT (BPF_RET + BPF_K, 0),
+};
diff --git a/bpf.c b/bpf.c
new file mode 100644 (file)
index 0000000..b6af76d
--- /dev/null
+++ b/bpf.c
@@ -0,0 +1,248 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ *
+ * 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/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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "dhcp.h"
+#include "interface.h"
+#include "logger.h"
+#include "socket.h"
+
+void
+setup_packet_filters(void)
+{
+       /* Empty function */
+}
+
+int
+open_socket(struct interface *iface, int protocol)
+{
+       int n = 0;
+       int fd = -1;
+       char *device;
+       int flags;
+       struct ifreq ifr;
+       int buf = 0;
+       struct bpf_program pf;
+
+       device = xmalloc(sizeof(char) * PATH_MAX);
+       do {
+               snprintf(device, PATH_MAX, "/dev/bpf%d",  n++);
+               fd = open(device, O_RDWR);
+       } while (fd == -1 && errno == EBUSY);
+       free(device);
+
+       if (fd == -1) {
+               logger(LOG_ERR, "unable to open a BPF device");
+               return -1;
+       }
+
+       close_on_exec(fd);
+
+       memset(&ifr, 0, sizeof(ifr));
+       strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
+       if (ioctl(fd, BIOCSETIF, &ifr) == -1) {
+               logger(LOG_ERR,
+                      "cannot attach interface `%s' to bpf device `%s': %s",
+                      iface->name, device, strerror(errno));
+               close(fd);
+               return -1;
+       }
+
+       /* Get the required BPF buffer length from the kernel. */
+       if (ioctl(fd, BIOCGBLEN, &buf) == -1) {
+               logger (LOG_ERR, "ioctl BIOCGBLEN: %s", strerror(errno));
+               close(fd);
+               return -1;
+       }
+       iface->buffer_length = buf;
+
+       flags = 1;
+       if (ioctl(fd, BIOCIMMEDIATE, &flags) == -1) {
+               logger(LOG_ERR, "ioctl BIOCIMMEDIATE: %s", strerror(errno));
+               close(fd);
+               return -1;
+       }
+
+       /* Install the DHCP filter */
+       if (protocol == ETHERTYPE_ARP) {
+               pf.bf_insns = arp_bpf_filter;
+               pf.bf_len = sizeof(arp_bpf_filter) / sizeof(arp_bpf_filter[0]);
+       } else {
+               pf.bf_insns = dhcp_bpf_filter;
+               pf.bf_len = sizeof(dhcp_bpf_filter)/sizeof(dhcp_bpf_filter[0]);
+       }
+       if (ioctl(fd, BIOCSETF, &pf) == -1) {
+               logger(LOG_ERR, "ioctl BIOCSETF: %s", strerror(errno));
+               close(fd);
+               return -1;
+       }
+
+       if (iface->fd > -1)
+               close(iface->fd);
+       iface->fd = fd;
+
+       return fd;
+}
+
+ssize_t
+send_packet(const struct interface *iface, int type,
+           const unsigned char *data, size_t len)
+{
+       ssize_t retval = -1;
+       struct iovec iov[2];
+       struct ether_header hw;
+
+       if (iface->family == ARPHRD_ETHER) {
+               memset(&hw, 0, sizeof(hw));
+               memset(&hw.ether_dhost, 0xff, ETHER_ADDR_LEN);
+               hw.ether_type = htons(type);
+
+               iov[0].iov_base = &hw;
+               iov[0].iov_len = sizeof(hw);
+       } else {
+               logger(LOG_ERR, "unsupported interace type %d", iface->family);
+               return -1;
+       }
+       iov[1].iov_base = (unsigned char *)data;
+       iov[1].iov_len = len;
+
+       if ((retval = writev(iface->fd, iov, 2)) == -1)
+               logger(LOG_ERR, "writev: %s", strerror(errno));
+
+       return retval;
+}
+
+/* BPF requires that we read the entire buffer.
+ * So we pass the buffer in the API so we can loop on >1 dhcp packet. */
+ssize_t
+get_packet(const struct interface *iface, unsigned char *data,
+          unsigned char *buffer, size_t *buffer_len, size_t *buffer_pos)
+{
+       union
+       {
+               unsigned char *buffer;
+               struct bpf_hdr *packet;
+       } bpf;
+       union
+       {
+               unsigned char *buffer;
+               struct ether_header *hw;
+       } hdr;
+       union
+       {
+               unsigned char *buffer;
+               struct udp_dhcp_packet *packet;
+       } pay;
+       struct timespec ts;
+       size_t len;
+       unsigned char *payload;
+       bool have_data;
+
+       bpf.buffer = buffer;
+
+       if (*buffer_pos < 1) {
+               memset(bpf.buffer, 0, iface->buffer_length);
+               *buffer_len = read(iface->fd, bpf.buffer, iface->buffer_length);
+               *buffer_pos = 0;
+               if (*buffer_len < 1) {
+                       logger(LOG_ERR, "read: %s", strerror(errno));
+                       ts.tv_sec = 3;
+                       ts.tv_nsec = 0;
+                       nanosleep(&ts, NULL);
+                       return -1;
+               }
+       } else
+               bpf.buffer += *buffer_pos;
+
+       while (bpf.packet) {
+               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;
+
+               hdr.buffer = bpf.buffer + bpf.packet->bh_hdrlen;
+               payload = hdr.buffer + sizeof(*hdr.hw);
+
+               /* If it's an ARP reply, then just send it back */
+               if (hdr.hw->ether_type == htons (ETHERTYPE_ARP)) {
+                       len = bpf.packet->bh_caplen - sizeof(*hdr.hw);
+                       memcpy(data, payload, len);
+                       have_data = true;
+               } 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;
+                       }
+               }
+
+               /* Update the buffer_pos pointer */
+               bpf.buffer += BPF_WORDALIGN(bpf.packet->bh_hdrlen +
+                                           bpf.packet->bh_caplen);
+               if ((unsigned)(bpf.buffer - buffer) < *buffer_len)
+                       *buffer_pos = bpf.buffer - buffer;
+               else
+                       *buffer_pos = 0;
+
+               if (have_data)
+                       return len;
+
+               if (*buffer_pos == 0)
+                       break;
+       }
+
+       /* No valid packets left, so return */
+       *buffer_pos = 0;
+       return -1;
+}
index 23dd97015f8fd1cdf5575e24b39de5ab1a727533..edc207197caa68049b6f33fcb2cd1b22ac13922f 100644 (file)
--- a/client.c
+++ b/client.c
@@ -54,7 +54,7 @@
 #include "dhcp.h"
 #include "dhcpcd.h"
 #include "info.h"
-#include "interface.h"
+#include "if.h"
 #ifdef ENABLE_IPV4LL
 # include "ipv4ll.h"
 #endif
index 2c0991bf7b850cd7f65fba5a2599209b8698869c..4c0cd719be4ddf640f55d4582910f4ffd50c83e2 100644 (file)
--- a/config.h
+++ b/config.h
@@ -77,7 +77,7 @@
 #define PIDFILE             STATEDIR "/run/" PACKAGE "-%s.pid"
 
 #ifndef INFODIR
-# define INFODIR            "/var/lib/dhcpcd"
+# define INFODIR            "/var/db"
 #endif
 #define INFOFILE            INFODIR "/" PACKAGE "-%s.info"
 #define DUIDFILE            INFODIR "/" PACKAGE ".duid"
index 6cc14bca56cac89b63500b3ba57e8ed56fa08859..9f16c7fc732ad0bbfefd5a81e63363c8bb7e69a5 100644 (file)
@@ -53,7 +53,7 @@
 #ifdef ENABLE_INFO
 # include "info.h"
 #endif
-#include "interface.h"
+#include "if.h"
 #include "dhcpcd.h"
 #include "logger.h"
 #include "signal.h"
index 8dfbf1b26801d351eff474c730270a96062c3d90..dc282a88ce70667017f95fe06e3087c3ee7f2eab 100644 (file)
@@ -29,7 +29,7 @@
 #define DHCPCONFIG_H
 
 #include "dhcpcd.h"
-#include "interface.h"
+#include "if.h"
 #include "dhcp.h"
 
 int configure(const struct options *, struct interface *,
diff --git a/dhcp.c b/dhcp.c
index cf7baafa189317b972a5ed9322de8a13b8b96650..1e0dffce43573e9ee03dfb7684a5d39f8acbbe1f 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -30,6 +30,9 @@
 #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>
 
@@ -46,7 +49,6 @@
 #include "common.h"
 #include "dhcpcd.h"
 #include "dhcp.h"
-#include "interface.h"
 #include "logger.h"
 #include "socket.h"
 
@@ -89,6 +91,133 @@ dhcp_message(int type)
        return NULL;
 }
 
+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 (nleft == 1) {
+               memcpy(&a, p.i, 1);
+               sum += ntohs(a) << 8;
+       }
+
+       sum = (sum >> 16) + (sum & 0xffff);
+       sum += (sum >> 16);
+
+       return ~sum;
+}
+
+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)
+{
+       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));
+}
+
+int
+valid_dhcp_packet(unsigned char *data)
+{
+       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;
+       }
+
+eexit:
+       d.packet->ip.ip_sum = ipsum;
+       d.packet->ip.ip_len = iplen;
+       d.packet->udp.uh_sum = udpsum;
+
+       return retval;
+}
+
 ssize_t
 send_message(const struct interface *iface, const struct dhcp *dhcp,
             uint32_t xid, char type, const struct options *options)
@@ -450,6 +579,23 @@ decode_CSR(const unsigned char *p, int len)
        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)
 {
@@ -472,6 +618,23 @@ free_dhcp(struct dhcp *dhcp)
        }
 }
 
+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);
+}
+
 static bool
 dhcp_add_address(struct address_head **addresses,
                 const unsigned char *data, int length)
diff --git a/dhcp.h b/dhcp.h
index 7b2f86bec7a9bb738b5bb8a10d2bb9e0c36c6672..e2c910c0cd3e0a5d0db5998c9d8435a8d67ca420 100644 (file)
--- a/dhcp.h
+++ b/dhcp.h
 #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 <netinet/in_systm.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
@@ -35,7 +41,7 @@
 #include <stdint.h>
 
 #include "dhcpcd.h"
-#include "interface.h"
+#include "if.h"
 
 /* Max MTU - defines dhcp option length */
 #define MTU_MAX             1500
@@ -126,6 +132,23 @@ struct fqdn
        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];
@@ -210,7 +233,10 @@ struct udp_dhcp_packet
 
 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 *);
+int valid_dhcp_packet(unsigned char *);
 
 #endif
index 1e1359b1b7e749b6289aeaefe78f1bec60fd00cb..233721992cace79759c47575b80aedd50b4c419c 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -48,7 +48,7 @@ const char copyright[] = "Copyright (c) 2006-2008 Roy Marples";
 #include "client.h"
 #include "dhcpcd.h"
 #include "dhcp.h"
-#include "interface.h"
+#include "if.h"
 #include "logger.h"
 #include "socket.h"
 #include "version.h"
diff --git a/duid.h b/duid.h
index cb3359425e70d5cfda5f83c70e980bb9de28b993..7a0580f490b815d92210005a7bba38e60e55430d 100644 (file)
--- a/duid.h
+++ b/duid.h
@@ -35,7 +35,7 @@
 #  define DUID_LEN 128 + 2
 #endif
 
-#include "interface.h"
+#include "if.h"
 
 size_t get_duid(unsigned char *, const struct interface *);
 #endif
diff --git a/if-bsd.c b/if-bsd.c
new file mode 100644 (file)
index 0000000..7113a31
--- /dev/null
+++ b/if-bsd.c
@@ -0,0 +1,200 @@
+/* 
+ * 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/socket.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+
+#include <arpa/inet.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <netinet/in.h>
+
+#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 "interface.h"
+#include "logger.h"
+
+/* Darwin doesn't define this for some very odd reason */
+#ifndef SA_SIZE
+# define SA_SIZE(sa)                                           \
+       (  (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ?  \
+          sizeof(long)         :                               \
+          1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
+#endif
+
+int
+if_address(const char *ifname, struct in_addr address,
+          struct in_addr netmask, struct in_addr broadcast, int del)
+{
+       int s;
+       struct ifaliasreq ifa;
+       union {
+               struct sockaddr *sa;
+               struct sockaddr_in *sin;
+       } _s;
+
+       if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+               logger(LOG_ERR, "socket: %s", strerror(errno));
+               return -1;
+       }
+
+       memset(&ifa, 0, sizeof(ifa));
+       strlcpy(ifa.ifra_name, ifname, sizeof(ifa.ifra_name));
+
+#define ADDADDR(_var, _addr) \
+       _s.sa = &_var; \
+       _s.sin->sin_family = AF_INET; \
+       _s.sin->sin_len = sizeof(*_s.sin); \
+       memcpy(&_s.sin->sin_addr, &_addr, sizeof(_s.sin->sin_addr));
+
+       ADDADDR(ifa.ifra_addr, address);
+       ADDADDR(ifa.ifra_mask, netmask);
+       if (!del)
+               ADDADDR(ifa.ifra_broadaddr, broadcast);
+#undef ADDADDR
+
+       if (ioctl(s, del ? SIOCDIFADDR : SIOCAIFADDR, &ifa) == -1) {
+               logger(LOG_ERR, "ioctl %s: %s",
+                      del ? "SIOCDIFADDR" : "SIOCAIFADDR",
+                      strerror(errno));
+               close(s);
+               return -1;
+       }
+
+       close(s);
+       return 0;
+}
+
+int
+if_route(const char *ifname, struct in_addr destination,
+        struct in_addr netmask, struct in_addr gateway,
+        int metric, int change, int del)
+{
+       int s;
+       static int seq;
+       union sockunion {
+               struct sockaddr sa;
+               struct sockaddr_in sin;
+#ifdef INET6
+               struct sockaddr_in6 sin6;
+#endif
+               struct sockaddr_dl sdl;
+               struct sockaddr_storage ss;
+       } su;
+       struct rtm 
+       {
+               struct rt_msghdr hdr;
+               char buffer[sizeof(su) * 3];
+       } rtm;
+       char *bp = rtm.buffer;
+       size_t l;
+       unsigned char *hwaddr;
+       size_t hwlen = 0;
+
+       log_route(destination, netmask, gateway, metric, change, del);
+
+       if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) {
+               logger(LOG_ERR, "socket: %s", strerror(errno));
+               return -1;
+       }
+
+       memset(&rtm, 0, sizeof(rtm));
+
+       rtm.hdr.rtm_version = RTM_VERSION;
+       rtm.hdr.rtm_seq = ++seq;
+       rtm.hdr.rtm_type = change ? RTM_CHANGE : del ? RTM_DELETE : RTM_ADD;
+       rtm.hdr.rtm_flags = RTF_UP | RTF_STATIC;
+
+       /* This order is important */
+       rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+
+#define ADDADDR(_addr) \
+       memset (&su, 0, sizeof(su)); \
+       su.sin.sin_family = AF_INET; \
+       su.sin.sin_len = sizeof(su.sin); \
+       memcpy (&su.sin.sin_addr, &_addr, sizeof(su.sin.sin_addr)); \
+       l = SA_SIZE (&(su.sa)); \
+       memcpy (bp, &(su), l); \
+       bp += l;
+
+       ADDADDR (destination);
+
+       if (netmask.s_addr == INADDR_BROADCAST ||
+           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);
+               memset(&su, 0, sizeof(su));
+               su.sdl.sdl_len = sizeof(su.sdl);
+               su.sdl.sdl_family = AF_LINK;
+               su.sdl.sdl_nlen = strlen(ifname);
+               memcpy(&su.sdl.sdl_data, ifname, (size_t)su.sdl.sdl_nlen);
+               su.sdl.sdl_alen = hwlen;
+               memcpy(((unsigned char *)&su.sdl.sdl_data) + su.sdl.sdl_nlen,
+                      hwaddr, (size_t)su.sdl.sdl_alen);
+
+               l = SA_SIZE(&(su.sa));
+               memcpy(bp, &su, l);
+               bp += l;
+               free(hwaddr);
+       } else {
+               rtm.hdr.rtm_flags |= RTF_GATEWAY;
+               ADDADDR(gateway);
+       }
+
+       ADDADDR(netmask);
+#undef ADDADDR
+
+       rtm.hdr.rtm_msglen = l = bp - (char *)&rtm;
+       if (write(s, &rtm, l) == -1) {
+               /* Don't report error about routes already existing */
+               if (errno != EEXIST)
+                       logger(LOG_ERR, "write: %s", strerror(errno));
+               close(s);
+               return -1;
+       }
+
+       close(s);
+       return 0;
+}
diff --git a/if-linux.c b/if-linux.c
new file mode 100644 (file)
index 0000000..0c3c163
--- /dev/null
@@ -0,0 +1,347 @@
+/* 
+ * 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 <asm/types.h> /* Needed for 2.4 kernels */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+
+#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"
+#include "logger.h"
+
+/* This netlink stuff is overly compex IMO.
+ * The BSD implementation is much cleaner and a lot less code.
+ * send_netlink handles the actual transmission so we can work out
+ * if there was an error or not. */
+#define BUFFERLEN 256
+static int
+send_netlink(struct nlmsghdr *hdr)
+{
+       int s;
+       pid_t mypid = getpid ();
+       struct sockaddr_nl nl;
+       struct iovec iov;
+       struct msghdr msg;
+       static unsigned int seq;
+       char *buffer;
+       ssize_t bytes;
+       union
+       {
+               char *buffer;
+               struct nlmsghdr *nlm;
+       } h;
+       int len, l;
+       struct nlmsgerr *err;
+
+       if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) {
+               logger(LOG_ERR, "socket: %s", strerror(errno));
+               return -1;
+       }
+
+       memset(&nl, 0, sizeof(nl));
+       nl.nl_family = AF_NETLINK;
+       if (bind(s, (struct sockaddr *)&nl, sizeof(nl)) == -1) {
+               logger(LOG_ERR, "bind: %s", strerror(errno));
+               close(s);
+               return -1;
+       }
+
+       memset(&iov, 0, sizeof(iov));
+       iov.iov_base = hdr;
+       iov.iov_len = hdr->nlmsg_len;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_name = &nl;
+       msg.msg_namelen = sizeof(nl);
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+
+       /* Request a reply */
+       hdr->nlmsg_flags |= NLM_F_ACK;
+       hdr->nlmsg_seq = ++seq;
+
+       if (sendmsg(s, &msg, 0) == -1) {
+               logger(LOG_ERR, "write: %s", strerror(errno));
+               close(s);
+               return -1;
+       }
+
+       buffer = xzalloc(sizeof(char) * BUFFERLEN);
+       iov.iov_base = buffer;
+
+       for (;;) {
+               iov.iov_len = BUFFERLEN;
+               bytes = recvmsg(s, &msg, 0);
+
+               if (bytes == -1) {
+                       if (errno != EINTR)
+                               logger (LOG_ERR, "recvmsg: %s",
+                                       strerror(errno));
+                       continue;
+               }
+
+               if (bytes == 0) {
+                       logger(LOG_ERR, "netlink: EOF");
+                       goto eexit;
+               }
+
+               if (msg.msg_namelen != sizeof(nl)) {
+                       logger(LOG_ERR,
+                              "netlink: sender address length mismatch");
+                       goto eexit;
+               }
+
+               for (h.buffer = buffer; bytes >= (signed) sizeof(*h.nlm); ) {
+                       len = h.nlm->nlmsg_len;
+                       l = len - sizeof(*h.nlm);
+                       err = (struct nlmsgerr *)NLMSG_DATA(h.nlm);
+
+                       if (l < 0 || len > bytes) {
+                               if (msg.msg_flags & MSG_TRUNC)
+                                       logger(LOG_ERR,
+                                              "netlink: truncated message");
+                               else
+                                       logger(LOG_ERR,
+                                              "netlink: malformed message");
+                               goto eexit;
+                       }
+
+                       /* Ensure it's our message */
+                       if (nl.nl_pid != 0 ||
+                           (pid_t)h.nlm->nlmsg_pid != mypid ||
+                           h.nlm->nlmsg_seq != seq)
+                       {
+                               /* Next Message */
+                               bytes -= NLMSG_ALIGN(len);
+                               h.buffer += NLMSG_ALIGN(len);
+                               continue;
+                       }
+
+                       /* We get an NLMSG_ERROR back with a code of zero for success */
+                       if (h.nlm->nlmsg_type != NLMSG_ERROR) {
+                               logger(LOG_ERR, "netlink: unexpected reply %d",
+                                      h.nlm->nlmsg_type);
+                               goto eexit;
+                       }
+
+                       if ((unsigned)l < sizeof(*err)) {
+                               logger(LOG_ERR, "netlink: error truncated");
+                               goto eexit;
+                       }
+
+                       if (err->error == 0) {
+                               close(s);
+                               free(buffer);
+                               return l;
+                       }
+
+                       errno = -err->error;
+                       /* Don't report on something already existing */
+                       if (errno != EEXIST)
+                               logger(LOG_ERR, "netlink: %s", strerror(errno));
+                       goto eexit;
+               }
+       }
+
+eexit:
+       close(s);
+       free(buffer);
+       return -1;
+}
+
+#define NLMSG_TAIL(nmsg) \
+       ((struct rtattr *)(((ptrdiff_t)(nmsg))+NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+static int
+add_attr_l(struct nlmsghdr *n, unsigned int maxlen, int type,
+          const void *data, int alen)
+{
+       int len = RTA_LENGTH(alen);
+       struct rtattr *rta;
+
+       if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
+               logger(LOG_ERR, "add_attr_l: message exceeded bound of %d\n",
+                      maxlen);
+               return -1;
+       }
+
+       rta = NLMSG_TAIL(n);
+       rta->rta_type = type;
+       rta->rta_len = len;
+       memcpy(RTA_DATA(rta), data, alen);
+       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+
+       return 0;
+}
+
+static int
+add_attr_32(struct nlmsghdr *n, unsigned int maxlen, int type, uint32_t data)
+{
+       int len = RTA_LENGTH(sizeof(data));
+       struct rtattr *rta;
+
+       if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
+               logger(LOG_ERR, "add_attr32: message exceeded bound of %d\n",
+                      maxlen);
+               return -1;
+       }
+
+       rta = NLMSG_TAIL(n);
+       rta->rta_type = type;
+       rta->rta_len = len;
+       memcpy(RTA_DATA(rta), &data, sizeof(data));
+       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+
+       return 0;
+}
+
+struct nlma
+{
+       struct nlmsghdr hdr;
+       struct ifaddrmsg ifa; 
+       char buffer[64];
+};
+
+struct nlmr
+{
+       struct nlmsghdr hdr;
+       struct rtmsg rt;
+       char buffer[256];
+};
+
+int
+if_address(const char *ifname,
+          struct in_addr address, struct in_addr netmask,
+          struct in_addr broadcast, int del)
+{
+       struct nlma *nlm;
+       int retval;
+
+       nlm = xzalloc(sizeof(*nlm));
+       nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+       nlm->hdr.nlmsg_flags = NLM_F_REQUEST;
+       if (!del)
+               nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
+       nlm->hdr.nlmsg_type = del ? RTM_DELADDR : RTM_NEWADDR;
+       if (!(nlm->ifa.ifa_index = if_nametoindex(ifname))) {
+               logger(LOG_ERR, "if_nametoindex: no index for interface `%s'",
+                      ifname);
+               free(nlm);
+               return -1;
+       }
+       nlm->ifa.ifa_family = AF_INET;
+       nlm->ifa.ifa_prefixlen = inet_ntocidr(netmask);
+       /* This creates the aliased interface */
+       add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL,
+                  ifname, strlen(ifname) + 1);
+       add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL,
+                  &address.s_addr, sizeof(address.s_addr));
+       if (!del)
+               add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_BROADCAST,
+                          &broadcast.s_addr, sizeof(broadcast.s_addr));
+
+       retval = send_netlink(&nlm->hdr);
+       free(nlm);
+       return retval;
+}
+
+int
+if_route(const char *ifname,
+        struct in_addr destination, struct in_addr netmask,
+        struct in_addr gateway, int metric, int change, int del)
+{
+       struct nlmr *nlm;
+       unsigned int ifindex;
+       int retval;
+
+       log_route(destination, netmask, gateway, metric, change, del);
+
+       if (!(ifindex = if_nametoindex(ifname))) {
+               logger(LOG_ERR, "if_nametoindex: no index for interface `%s'",
+                      ifname);
+               return -1;
+       }
+
+       nlm = xzalloc(sizeof(*nlm));
+       nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+       if (change)
+               nlm->hdr.nlmsg_flags = NLM_F_REPLACE;
+       else if (!del)
+               nlm->hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL;
+       nlm->hdr.nlmsg_flags |= NLM_F_REQUEST;
+       nlm->hdr.nlmsg_type = del ? RTM_DELROUTE : RTM_NEWROUTE;
+       nlm->rt.rtm_family = AF_INET;
+       nlm->rt.rtm_table = RT_TABLE_MAIN;
+
+       if (del)
+               nlm->rt.rtm_scope = RT_SCOPE_NOWHERE;
+       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)
+                       nlm->rt.rtm_scope = RT_SCOPE_LINK;
+               else
+                       nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE;
+               nlm->rt.rtm_type = RTN_UNICAST;
+       }
+
+       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_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, ifindex);
+       add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, metric);
+
+       retval = send_netlink(&nlm->hdr);
+       free(nlm);
+       return retval;
+}
diff --git a/if.c b/if.c
new file mode 100644 (file)
index 0000000..458dabe
--- /dev/null
+++ b/if.c
@@ -0,0 +1,551 @@
+/* 
+ * 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 <arpa/inet.h>
+
+#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"
+#include "logger.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;
+}
+
+static 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) {
+               logger(LOG_ERR, "socket: %s", strerror(errno));
+               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) {
+                               logger(LOG_ERR, "ioctl SIOCGIFCONF: %s",
+                                      strerror(errno));
+                               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) {
+                                       logger(LOG_ERR,
+                                              "ioctl SIOCGIFNETMASK: %s",
+                                              strerror(errno));
+                                       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) {
+               logger(LOG_ERR, "socket: %s", strerror(errno));
+               return NULL;
+       }
+
+#ifdef __linux__
+       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1) {
+               logger(LOG_ERR, "ioctl SIOCGIFHWADDR: %s", strerror(errno));
+               goto exit;
+       }
+
+       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;
+       default:
+               logger (LOG_ERR,
+                       "interface is not Ethernet, FireWire, " \
+                       "InfiniBand or Token Ring");
+               goto exit;
+       }
+
+       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) {
+               logger(LOG_ERR, "ioctl SIOCSIFMETRIC: %s", strerror(errno));
+               goto exit;
+       }
+
+       hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
+       if (do_interface(ifname, hwaddr, &hwlen, NULL, false, false) != 1) {
+               logger(LOG_ERR, "could not find interface %s", ifname);
+               goto exit;
+       }
+
+       family = ARPHRD_ETHER;
+#endif
+
+       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       if (ioctl(s, SIOCGIFMTU, &ifr) == -1) {
+               logger(LOG_ERR, "ioctl SIOCGIFMTU: %s", strerror(errno));
+               goto exit;
+       }
+
+       if (ifr.ifr_mtu < MTU_MIN) {
+               logger(LOG_DEBUG, "MTU of %d is too low, setting to %d",
+                      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) {
+                       logger(LOG_ERR, "ioctl SIOCSIFMTU,: %s",
+                              strerror(errno));
+                       goto exit;
+               }
+       }
+       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) {
+               logger(LOG_ERR, "ioctl SIOCGIFFLAGS: %s", strerror(errno));
+               goto exit;
+       }
+
+       if (!(ifr.ifr_flags & IFF_UP) || !(ifr.ifr_flags & IFF_RUNNING)) {
+               ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+               if (ioctl(s, SIOCSIFFLAGS, &ifr) != 0) {
+                       logger(LOG_ERR, "ioctl SIOCSIFFLAGS: %s",
+                              strerror(errno));
+                       goto exit;
+               }
+       }
+
+       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;
+
+       logger(LOG_INFO, "hardware address = %s",
+              hwaddr_ntoa(iface->hwaddr, iface->hwlen));
+
+       /* 0 is a valid fd, so init to -1 */
+       iface->fd = -1;
+#ifdef __linux__
+       iface->listen_fd = -1;
+#endif
+
+exit:
+       close(s);
+       free(hwaddr);
+       return iface;
+}
+
+int
+get_mtu(const char *ifname)
+{
+       struct ifreq ifr;
+       int r;
+       int s;
+
+       if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+               logger(LOG_ERR, "socket: %s", strerror(errno));
+               return -1;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       r = ioctl(s, SIOCGIFMTU, &ifr);
+       close(s);
+
+       if (r == -1) {
+               logger(LOG_ERR, "ioctl SIOCGIFMTU: %s", strerror(errno));
+               return -1;
+       }
+
+       return ifr.ifr_mtu;
+}
+
+int
+set_mtu(const char *ifname, short int mtu)
+{
+       struct ifreq ifr;
+       int r;
+       int s;
+
+       if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+               logger(LOG_ERR, "socket: %s", strerror(errno));
+               return -1;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       logger(LOG_DEBUG, "setting MTU to %d", mtu);
+       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       ifr.ifr_mtu = mtu;
+       r = ioctl(s, SIOCSIFMTU, &ifr);
+       close(s);
+
+       if (r == -1)
+               logger(LOG_ERR, "ioctl SIOCSIFMTU: %s", strerror(errno));
+
+       return r == 0 ? 0 : -1;
+}
+
+void
+log_route(struct in_addr destination, struct in_addr netmask,
+         struct in_addr gateway, _unused int metric, int change, 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,
+                      change ? "changing" : 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,
+                      change ? "changing" : del ? "removing" : "adding",
+                      inet_ntoa(gateway)
+
+#ifdef __linux__
+                      , metric
+#endif
+                     );
+       else
+               logger(LOG_INFO, "%s route to %s/%d via %s" METRIC,
+                      change ? "changing" : del ? "removing" : "adding",
+                      dstd, inet_ntocidr(netmask), inet_ntoa(gateway)
+#ifdef __linux__
+                      , metric
+#endif
+                     );
+
+       free(dstd);
+}
+
+int
+add_address(const char *ifname, struct in_addr address,
+           struct in_addr netmask, struct in_addr broadcast)
+{
+       logger(LOG_INFO, "adding IP address %s/%d",
+              inet_ntoa(address), inet_ntocidr(netmask));
+
+       return if_address(ifname, address, netmask, broadcast, 0);
+}
+
+int
+del_address(const char *ifname, struct in_addr address, struct in_addr netmask)
+{
+       struct in_addr t;
+
+       logger(LOG_INFO, "removing IP address %s/%d",
+              inet_ntoa(address), inet_ntocidr(netmask));
+
+       t.s_addr = 0;
+       return if_address(ifname, address, netmask, t, 1);
+}
+
+int
+add_route(const char *ifname, struct in_addr destination,
+         struct in_addr netmask, struct in_addr gateway, int metric)
+{
+       return if_route(ifname, destination, netmask, gateway, metric, 0, 0);
+}
+
+int
+change_route(const char *ifname, struct in_addr destination,
+             struct in_addr netmask, struct in_addr gateway, int metric)
+{
+       return if_route(ifname, destination, netmask, gateway, metric, 1, 0);
+}
+
+int
+del_route(const char *ifname, struct in_addr destination,
+         struct in_addr netmask, struct in_addr gateway, int metric)
+{
+       return if_route(ifname, destination, netmask, gateway, metric, 0, 1);
+}
+
+int
+flush_addresses(const char *ifname)
+{
+       return do_interface(ifname, NULL, NULL, NULL, true, false);
+}
+
+in_addr_t
+get_address(const char *ifname)
+{
+       struct in_addr address;
+
+       if (do_interface(ifname, NULL, NULL, &address, false, true) > 0)
+               return address.s_addr;
+       return 0;
+}
+
+int
+has_address(const char *ifname, struct in_addr address)
+{
+       return do_interface(ifname, NULL, NULL, &address, false, false);
+}
similarity index 88%
rename from interface.h
rename to if.h
index 76be7c392c60e64c5f850162303dfa779b444223..76cce404ed6e7faede519c36c85b4ba31e33e6d2 100644 (file)
+++ b/if.h
 #define NSTAILQ_FOREACH(var, head, field) \
                if (head) STAILQ_FOREACH (var, head, field)
 
-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 interface
 {
        char name[IF_NAMESIZE];
@@ -138,8 +122,6 @@ struct interface
        size_t clientid_len;
 };
 
-void free_address(struct address_head *);
-void free_route(struct route_head *);
 uint32_t get_netmask(uint32_t);
 char *hwaddr_ntoa(const unsigned char *, size_t);
 size_t hwaddr_aton(unsigned char *, const char *);
@@ -154,15 +136,19 @@ int flush_addresses(const char *);
 in_addr_t get_address(const char *);
 int has_address(const char *, struct in_addr);
 
-int add_route(const char *, struct in_addr, struct in_addr, struct in_addr, int);
-int change_route(const char *, struct in_addr, struct in_addr, struct in_addr, int);
-int del_route(const char *, struct in_addr, struct in_addr, struct in_addr, int);
+int add_route(const char *, struct in_addr, struct in_addr, struct in_addr,
+             int);
+int change_route(const char *, struct in_addr, struct in_addr, struct in_addr,
+                int);
+int del_route(const char *, struct in_addr, struct in_addr, struct in_addr,
+             int);
+void log_route(struct in_addr, struct in_addr, struct in_addr, int, int, int);
 
 int inet_ntocidr(struct in_addr);
 int inet_cidrtoaddr(int, struct in_addr *);
 
-#ifdef __linux__
-typedef int (*netlink_callback)(struct nlmsghdr *, void *);
-int send_netlink(struct nlmsghdr *, netlink_callback, void *);
-#endif
+int if_address(const char *, struct in_addr, struct in_addr, struct in_addr,
+              int del);
+int if_route(const char *, struct in_addr, struct in_addr, struct in_addr,
+            int metric, int change, int del);
 #endif
diff --git a/info.c b/info.c
index c75544247375d121c0b3428f93b71bed5ed83d98..3901efa221b1c2e3d37bbc30b02cd01eccb6a6c4 100644 (file)
--- a/info.c
+++ b/info.c
@@ -38,7 +38,7 @@
 #include "config.h"
 #include "common.h"
 #include "dhcp.h"
-#include "interface.h"
+#include "if.h"
 #include "logger.h"
 #include "info.h"
 
diff --git a/info.h b/info.h
index 1c875edc6235f4dd25c1316156d0f6387a879e62..2d6e2bf98a13c1a52602d91b01b4838b9958bc91 100644 (file)
--- a/info.h
+++ b/info.h
@@ -29,7 +29,7 @@
 #define INFO_H
 
 #include "dhcpcd.h"
-#include "interface.h"
+#include "if.h"
 #include "dhcp.h"
 
 #ifdef ENABLE_INFO
diff --git a/interface.c b/interface.c
deleted file mode 100644 (file)
index 7ad51d3..0000000
+++ /dev/null
@@ -1,1059 +0,0 @@
-/* 
- * 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/socket.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/param.h>
-
-#include <arpa/inet.h>
-
-/* Netlink suff */
-#ifdef __linux__
-#include <asm/types.h> /* Needed for 2.4 kernels */
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-#include <netinet/ether.h>
-#include <netpacket/packet.h>
-#else
-#include <net/if_dl.h>
-#include <net/if_types.h>
-#include <net/route.h>
-#include <netinet/in.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 "interface.h"
-#include "logger.h"
-
-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_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);
-}
-
-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;
-}
-
-static 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) {
-               logger(LOG_ERR, "socket: %s", strerror(errno));
-               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) {
-                               logger(LOG_ERR, "ioctl SIOCGIFCONF: %s",
-                                      strerror(errno));
-                               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) {
-                                       logger(LOG_ERR,
-                                              "ioctl SIOCGIFNETMASK: %s",
-                                              strerror(errno));
-                                       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) {
-               logger(LOG_ERR, "socket: %s", strerror(errno));
-               return NULL;
-       }
-
-#ifdef __linux__
-       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-       if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1) {
-               logger(LOG_ERR, "ioctl SIOCGIFHWADDR: %s", strerror(errno));
-               goto exit;
-       }
-
-       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;
-       default:
-               logger (LOG_ERR,
-                       "interface is not Ethernet, FireWire, " \
-                       "InfiniBand or Token Ring");
-               goto exit;
-       }
-
-       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) {
-               logger(LOG_ERR, "ioctl SIOCSIFMETRIC: %s", strerror(errno));
-               goto exit;
-       }
-
-       hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
-       if (_do_interface(ifname, hwaddr, &hwlen, NULL, false, false) != 1) {
-               logger(LOG_ERR, "could not find interface %s", ifname);
-               goto exit;
-       }
-
-       family = ARPHRD_ETHER;
-#endif
-
-       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-       if (ioctl(s, SIOCGIFMTU, &ifr) == -1) {
-               logger(LOG_ERR, "ioctl SIOCGIFMTU: %s", strerror(errno));
-               goto exit;
-       }
-
-       if (ifr.ifr_mtu < MTU_MIN) {
-               logger(LOG_DEBUG, "MTU of %d is too low, setting to %d",
-                      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) {
-                       logger(LOG_ERR, "ioctl SIOCSIFMTU,: %s",
-                              strerror(errno));
-                       goto exit;
-               }
-       }
-       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) {
-               logger(LOG_ERR, "ioctl SIOCGIFFLAGS: %s", strerror(errno));
-               goto exit;
-       }
-
-       if (!(ifr.ifr_flags & IFF_UP) || !(ifr.ifr_flags & IFF_RUNNING)) {
-               ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
-               if (ioctl(s, SIOCSIFFLAGS, &ifr) != 0) {
-                       logger(LOG_ERR, "ioctl SIOCSIFFLAGS: %s",
-                              strerror(errno));
-                       goto exit;
-               }
-       }
-
-       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;
-
-       logger(LOG_INFO, "hardware address = %s",
-              hwaddr_ntoa(iface->hwaddr, iface->hwlen));
-
-       /* 0 is a valid fd, so init to -1 */
-       iface->fd = -1;
-#ifdef __linux__
-       iface->listen_fd = -1;
-#endif
-
-exit:
-       close(s);
-       free(hwaddr);
-       return iface;
-}
-
-int
-get_mtu(const char *ifname)
-{
-       struct ifreq ifr;
-       int r;
-       int s;
-
-       if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
-               logger(LOG_ERR, "socket: %s", strerror(errno));
-               return -1;
-       }
-
-       memset(&ifr, 0, sizeof(ifr));
-       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-       r = ioctl(s, SIOCGIFMTU, &ifr);
-       close(s);
-
-       if (r == -1) {
-               logger(LOG_ERR, "ioctl SIOCGIFMTU: %s", strerror(errno));
-               return -1;
-       }
-
-       return ifr.ifr_mtu;
-}
-
-int
-set_mtu(const char *ifname, short int mtu)
-{
-       struct ifreq ifr;
-       int r;
-       int s;
-
-       if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
-               logger(LOG_ERR, "socket: %s", strerror(errno));
-               return -1;
-       }
-
-       memset(&ifr, 0, sizeof(ifr));
-       logger(LOG_DEBUG, "setting MTU to %d", mtu);
-       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-       ifr.ifr_mtu = mtu;
-       r = ioctl(s, SIOCSIFMTU, &ifr);
-       close(s);
-
-       if (r == -1)
-               logger(LOG_ERR, "ioctl SIOCSIFMTU: %s", strerror(errno));
-
-       return r == 0 ? 0 : -1;
-}
-
-static void
-log_route (struct in_addr destination, struct in_addr netmask,
-          struct in_addr gateway, _unused int metric, int change, 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,
-                      change ? "changing" : 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,
-                      change ? "changing" : del ? "removing" : "adding",
-                      inet_ntoa(gateway)
-
-#ifdef __linux__
-                      , metric
-#endif
-                     );
-       else
-               logger(LOG_INFO, "%s route to %s/%d via %s" METRIC,
-                      change ? "changing" : del ? "removing" : "adding",
-                      dstd, inet_ntocidr(netmask), inet_ntoa(gateway)
-#ifdef __linux__
-                      , metric
-#endif
-                     );
-
-       free(dstd);
-}
-
-#if defined(BSD) || defined(__FreeBSD_kernel__)
-
-/* Darwin doesn't define this for some very odd reason */
-#ifndef SA_SIZE
-# define SA_SIZE(sa)                                           \
-       (  (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ?  \
-          sizeof(long)         :                               \
-          1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
-#endif
-
-static int
-do_address(const char *ifname, struct in_addr address,
-          struct in_addr netmask, struct in_addr broadcast, int del)
-{
-       int s;
-       struct ifaliasreq ifa;
-       union {
-               struct sockaddr *sa;
-               struct sockaddr_in *sin;
-       } _s;
-
-       if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
-               logger(LOG_ERR, "socket: %s", strerror(errno));
-               return -1;
-       }
-
-       memset(&ifa, 0, sizeof(ifa));
-       strlcpy(ifa.ifra_name, ifname, sizeof(ifa.ifra_name));
-
-#define ADDADDR(_var, _addr) \
-       _s.sa = &_var; \
-       _s.sin->sin_family = AF_INET; \
-       _s.sin->sin_len = sizeof(*_s.sin); \
-       memcpy(&_s.sin->sin_addr, &_addr, sizeof(_s.sin->sin_addr));
-
-       ADDADDR(ifa.ifra_addr, address);
-       ADDADDR(ifa.ifra_mask, netmask);
-       if (!del)
-               ADDADDR(ifa.ifra_broadaddr, broadcast);
-#undef ADDADDR
-
-       if (ioctl(s, del ? SIOCDIFADDR : SIOCAIFADDR, &ifa) == -1) {
-               logger(LOG_ERR, "ioctl %s: %s",
-                      del ? "SIOCDIFADDR" : "SIOCAIFADDR",
-                      strerror(errno));
-               close(s);
-               return -1;
-       }
-
-       close(s);
-       return 0;
-}
-
-static int
-do_route (const char *ifname, struct in_addr destination,
-         struct in_addr netmask, struct in_addr gateway,
-         int metric, int change, int del)
-{
-       int s;
-       static int seq;
-       union sockunion {
-               struct sockaddr sa;
-               struct sockaddr_in sin;
-#ifdef INET6
-               struct sockaddr_in6 sin6;
-#endif
-               struct sockaddr_dl sdl;
-               struct sockaddr_storage ss;
-       } su;
-       struct rtm 
-       {
-               struct rt_msghdr hdr;
-               char buffer[sizeof(su) * 3];
-       } rtm;
-       char *bp = rtm.buffer;
-       size_t l;
-       unsigned char *hwaddr;
-       size_t hwlen = 0;
-
-       log_route(destination, netmask, gateway, metric, change, del);
-
-       if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) {
-               logger(LOG_ERR, "socket: %s", strerror(errno));
-               return -1;
-       }
-
-       memset(&rtm, 0, sizeof(rtm));
-
-       rtm.hdr.rtm_version = RTM_VERSION;
-       rtm.hdr.rtm_seq = ++seq;
-       rtm.hdr.rtm_type = change ? RTM_CHANGE : del ? RTM_DELETE : RTM_ADD;
-       rtm.hdr.rtm_flags = RTF_UP | RTF_STATIC;
-
-       /* This order is important */
-       rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
-
-#define ADDADDR(_addr) \
-       memset (&su, 0, sizeof(su)); \
-       su.sin.sin_family = AF_INET; \
-       su.sin.sin_len = sizeof(su.sin); \
-       memcpy (&su.sin.sin_addr, &_addr, sizeof(su.sin.sin_addr)); \
-       l = SA_SIZE (&(su.sa)); \
-       memcpy (bp, &(su), l); \
-       bp += l;
-
-       ADDADDR (destination);
-
-       if (netmask.s_addr == INADDR_BROADCAST ||
-           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);
-               memset(&su, 0, sizeof(su));
-               su.sdl.sdl_len = sizeof(su.sdl);
-               su.sdl.sdl_family = AF_LINK;
-               su.sdl.sdl_nlen = strlen(ifname);
-               memcpy(&su.sdl.sdl_data, ifname, (size_t)su.sdl.sdl_nlen);
-               su.sdl.sdl_alen = hwlen;
-               memcpy(((unsigned char *)&su.sdl.sdl_data) + su.sdl.sdl_nlen,
-                      hwaddr, (size_t)su.sdl.sdl_alen);
-
-               l = SA_SIZE(&(su.sa));
-               memcpy(bp, &su, l);
-               bp += l;
-               free(hwaddr);
-       } else {
-               rtm.hdr.rtm_flags |= RTF_GATEWAY;
-               ADDADDR(gateway);
-       }
-
-       ADDADDR(netmask);
-#undef ADDADDR
-
-       rtm.hdr.rtm_msglen = l = bp - (char *)&rtm;
-       if (write(s, &rtm, l) == -1) {
-               /* Don't report error about routes already existing */
-               if (errno != EEXIST)
-                       logger(LOG_ERR, "write: %s", strerror(errno));
-               close(s);
-               return -1;
-       }
-
-       close(s);
-       return 0;
-}
-
-#elif __linux__
-/* This netlink stuff is overly compex IMO.
- * The BSD implementation is much cleaner and a lot less code.
- * send_netlink handles the actual transmission so we can work out
- * if there was an error or not. */
-#define BUFFERLEN 256
-int
-send_netlink (struct nlmsghdr *hdr, netlink_callback callback, void *arg)
-{
-       int s;
-       pid_t mypid = getpid ();
-       struct sockaddr_nl nl;
-       struct iovec iov;
-       struct msghdr msg;
-       static unsigned int seq;
-       char *buffer;
-       ssize_t bytes;
-       union
-       {
-               char *buffer;
-               struct nlmsghdr *nlm;
-       } h;
-       int len, l;
-       struct nlmsgerr *err;
-
-       if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) {
-               logger(LOG_ERR, "socket: %s", strerror(errno));
-               return -1;
-       }
-
-       memset(&nl, 0, sizeof(nl));
-       nl.nl_family = AF_NETLINK;
-       if (bind(s, (struct sockaddr *)&nl, sizeof(nl)) == -1) {
-               logger(LOG_ERR, "bind: %s", strerror(errno));
-               close(s);
-               return -1;
-       }
-
-       memset(&iov, 0, sizeof(iov));
-       iov.iov_base = hdr;
-       iov.iov_len = hdr->nlmsg_len;
-
-       memset(&msg, 0, sizeof(msg));
-       msg.msg_name = &nl;
-       msg.msg_namelen = sizeof(nl);
-       msg.msg_iov = &iov;
-       msg.msg_iovlen = 1;
-
-       /* Request a reply */
-       hdr->nlmsg_flags |= NLM_F_ACK;
-       hdr->nlmsg_seq = ++seq;
-
-       if (sendmsg(s, &msg, 0) == -1) {
-               logger(LOG_ERR, "write: %s", strerror(errno));
-               close(s);
-               return -1;
-       }
-
-       buffer = xzalloc(sizeof(char) * BUFFERLEN);
-       iov.iov_base = buffer;
-
-       for (;;) {
-               iov.iov_len = BUFFERLEN;
-               bytes = recvmsg(s, &msg, 0);
-
-               if (bytes == -1) {
-                       if (errno != EINTR)
-                               logger (LOG_ERR, "recvmsg: %s",
-                                       strerror(errno));
-                       continue;
-               }
-
-               if (bytes == 0) {
-                       logger(LOG_ERR, "netlink: EOF");
-                       goto eexit;
-               }
-
-               if (msg.msg_namelen != sizeof(nl)) {
-                       logger(LOG_ERR,
-                              "netlink: sender address length mismatch");
-                       goto eexit;
-               }
-
-               for (h.buffer = buffer; bytes >= (signed) sizeof(*h.nlm); ) {
-                       len = h.nlm->nlmsg_len;
-                       l = len - sizeof(*h.nlm);
-                       err = (struct nlmsgerr *)NLMSG_DATA(h.nlm);
-
-                       if (l < 0 || len > bytes) {
-                               if (msg.msg_flags & MSG_TRUNC)
-                                       logger(LOG_ERR,
-                                              "netlink: truncated message");
-                               else
-                                       logger(LOG_ERR,
-                                              "netlink: malformed message");
-                               goto eexit;
-                       }
-
-                       /* Ensure it's our message */
-                       if (nl.nl_pid != 0 ||
-                           (pid_t)h.nlm->nlmsg_pid != mypid ||
-                           h.nlm->nlmsg_seq != seq)
-                       {
-                               /* Next Message */
-                               bytes -= NLMSG_ALIGN(len);
-                               h.buffer += NLMSG_ALIGN(len);
-                               continue;
-                       }
-
-                       /* We get an NLMSG_ERROR back with a code of zero for success */
-                       if (h.nlm->nlmsg_type != NLMSG_ERROR) {
-                               logger(LOG_ERR, "netlink: unexpected reply %d",
-                                      h.nlm->nlmsg_type);
-                               goto eexit;
-                       }
-
-                       if ((unsigned)l < sizeof(*err)) {
-                               logger(LOG_ERR, "netlink: error truncated");
-                               goto eexit;
-                       }
-
-                       if (err->error == 0) {
-                               close (s);
-                               if (callback) {
-                                       if ((l = callback (hdr, arg)) == -1)
-                                               logger(LOG_ERR,
-                                                      "netlink: callback failed");
-                               }
-                               free(buffer);
-                               return l;
-                       }
-
-                       errno = -err->error;
-                       /* Don't report on something already existing */
-                       if (errno != EEXIST)
-                               logger(LOG_ERR, "netlink: %s", strerror(errno));
-                       goto eexit;
-               }
-       }
-
-eexit:
-       close(s);
-       free(buffer);
-       return -1;
-}
-
-#define NLMSG_TAIL(nmsg) \
-       ((struct rtattr *)(((ptrdiff_t)(nmsg))+NLMSG_ALIGN((nmsg)->nlmsg_len)))
-
-static int
-add_attr_l(struct nlmsghdr *n, unsigned int maxlen, int type,
-          const void *data, int alen)
-{
-       int len = RTA_LENGTH(alen);
-       struct rtattr *rta;
-
-       if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
-               logger(LOG_ERR, "add_attr_l: message exceeded bound of %d\n",
-                      maxlen);
-               return -1;
-       }
-
-       rta = NLMSG_TAIL(n);
-       rta->rta_type = type;
-       rta->rta_len = len;
-       memcpy(RTA_DATA(rta), data, alen);
-       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
-
-       return 0;
-}
-
-static int
-add_attr_32(struct nlmsghdr *n, unsigned int maxlen, int type, uint32_t data)
-{
-       int len = RTA_LENGTH(sizeof(data));
-       struct rtattr *rta;
-
-       if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
-               logger(LOG_ERR, "add_attr32: message exceeded bound of %d\n",
-                      maxlen);
-               return -1;
-       }
-
-       rta = NLMSG_TAIL(n);
-       rta->rta_type = type;
-       rta->rta_len = len;
-       memcpy(RTA_DATA(rta), &data, sizeof(data));
-       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
-
-       return 0;
-}
-
-struct nlma
-{
-       struct nlmsghdr hdr;
-       struct ifaddrmsg ifa; 
-       char buffer[64];
-};
-
-struct nlmr
-{
-       struct nlmsghdr hdr;
-       struct rtmsg rt;
-       char buffer[256];
-};
-
-static int
-do_address(const char *ifname,
-          struct in_addr address, struct in_addr netmask,
-          struct in_addr broadcast, int del)
-{
-       struct nlma *nlm;
-       int retval;
-
-       nlm = xzalloc(sizeof(*nlm));
-       nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
-       nlm->hdr.nlmsg_flags = NLM_F_REQUEST;
-       if (!del)
-               nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
-       nlm->hdr.nlmsg_type = del ? RTM_DELADDR : RTM_NEWADDR;
-       if (!(nlm->ifa.ifa_index = if_nametoindex(ifname))) {
-               logger(LOG_ERR, "if_nametoindex: no index for interface `%s'",
-                      ifname);
-               free(nlm);
-               return -1;
-       }
-       nlm->ifa.ifa_family = AF_INET;
-       nlm->ifa.ifa_prefixlen = inet_ntocidr(netmask);
-       /* This creates the aliased interface */
-       add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL,
-                  ifname, strlen(ifname) + 1);
-       add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL,
-                  &address.s_addr, sizeof(address.s_addr));
-       if (!del)
-               add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_BROADCAST,
-                          &broadcast.s_addr, sizeof(broadcast.s_addr));
-
-       retval = send_netlink(&nlm->hdr, NULL, NULL);
-       free(nlm);
-       return retval;
-}
-
-static int
-do_route (const char *ifname,
-         struct in_addr destination, struct in_addr netmask,
-         struct in_addr gateway, int metric, int change, int del)
-{
-       struct nlmr *nlm;
-       unsigned int ifindex;
-       int retval;
-
-       log_route(destination, netmask, gateway, metric, change, del);
-
-       if (!(ifindex = if_nametoindex(ifname))) {
-               logger(LOG_ERR, "if_nametoindex: no index for interface `%s'",
-                      ifname);
-               return -1;
-       }
-
-       nlm = xzalloc(sizeof(*nlm));
-       nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
-       if (change)
-               nlm->hdr.nlmsg_flags = NLM_F_REPLACE;
-       else if (!del)
-               nlm->hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL;
-       nlm->hdr.nlmsg_flags |= NLM_F_REQUEST;
-       nlm->hdr.nlmsg_type = del ? RTM_DELROUTE : RTM_NEWROUTE;
-       nlm->rt.rtm_family = AF_INET;
-       nlm->rt.rtm_table = RT_TABLE_MAIN;
-
-       if (del)
-               nlm->rt.rtm_scope = RT_SCOPE_NOWHERE;
-       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)
-                       nlm->rt.rtm_scope = RT_SCOPE_LINK;
-               else
-                       nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE;
-               nlm->rt.rtm_type = RTN_UNICAST;
-       }
-
-       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_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, ifindex);
-       add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, metric);
-
-       retval = send_netlink(&nlm->hdr, NULL, NULL);
-       free(nlm);
-       return retval;
-}
-
-#else
- #error "Platform not supported!"
- #error "We currently support BPF and Linux sockets."
- #error "Other platforms may work using BPF. If yours does, please let me know"
- #error "so I can add it to our list."
-#endif
-
-int
-add_address(const char *ifname, struct in_addr address,
-           struct in_addr netmask, struct in_addr broadcast)
-{
-       logger(LOG_INFO, "adding IP address %s/%d",
-              inet_ntoa(address), inet_ntocidr(netmask));
-
-       return do_address(ifname, address, netmask, broadcast, 0);
-}
-
-int
-del_address(const char *ifname, struct in_addr address, struct in_addr netmask)
-{
-       struct in_addr t;
-
-       logger(LOG_INFO, "removing IP address %s/%d",
-              inet_ntoa(address), inet_ntocidr(netmask));
-
-       t.s_addr = 0;
-       return do_address(ifname, address, netmask, t, 1);
-}
-
-int
-add_route(const char *ifname, struct in_addr destination,
-         struct in_addr netmask, struct in_addr gateway, int metric)
-{
-       return do_route(ifname, destination, netmask, gateway, metric, 0, 0);
-}
-
-int
-change_route(const char *ifname, struct in_addr destination,
-             struct in_addr netmask, struct in_addr gateway, int metric)
-{
-       return do_route(ifname, destination, netmask, gateway, metric, 1, 0);
-}
-
-int
-del_route(const char *ifname, struct in_addr destination,
-         struct in_addr netmask, struct in_addr gateway, int metric)
-{
-       return do_route (ifname, destination, netmask, gateway, metric, 0, 1);
-}
-
-
-int
-flush_addresses(const char *ifname)
-{
-       return _do_interface(ifname, NULL, NULL, NULL, true, false);
-}
-
-in_addr_t
-get_address(const char *ifname)
-{
-       struct in_addr address;
-
-       if (_do_interface(ifname, NULL, NULL, &address, false, true) > 0)
-               return address.s_addr;
-       return 0;
-}
-
-int
-has_address(const char *ifname, struct in_addr address)
-{
-       return _do_interface(ifname, NULL, NULL, &address, false, false);
-}
index a4dea074aae5bbbe7005ae748ad84ded03a2f228..a21638e086842bcacc3d8edf5cdd3ac463549923 100644 (file)
--- a/ipv4ll.h
+++ b/ipv4ll.h
@@ -31,7 +31,7 @@
 #ifdef ENABLE_IPV4LL
 
 #include "dhcp.h"
-#include "interface.h"
+#include "if.h"
 
 int ipv4ll_get_address(struct interface *, struct dhcp *);
 
diff --git a/mk/os-BSD.mk b/mk/os-BSD.mk
new file mode 100644 (file)
index 0000000..bb6b55d
--- /dev/null
@@ -0,0 +1,5 @@
+# Setup OS specific variables
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+SRC_SOCKET=    bpf.c
+SRC_IF=                if-bsd.c
diff --git a/mk/os-Linux.mk b/mk/os-Linux.mk
new file mode 100644 (file)
index 0000000..3f251db
--- /dev/null
@@ -0,0 +1,29 @@
+# Setup OS specific variables
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+SRC_SOCKET=    socket.c
+SRC_IF=                if-linux.c
+
+CFLAGS+=       -D_BSD_SOURCE -D_XOPEN_SOURCE=600
+LIBRT=         -lrt
+
+CFLAGS+=       -DINFODIR=\"/var/lib/dhcpcd\"
+
+# Work out if our fork() works or not
+_HAVE_FORK_SH= if test "${HAVE_FORK}" = "yes"; then \
+               echo ""; \
+       elif test -n "${HAVE_FORK}"; then \
+               echo "-DTHERE_IS_NO_FORK"; \
+       else \
+               printf '\#include <stdlib.h>\n\#include <unistd.h>\nint main (void) { pid_t pid = fork(); if (pid == -1) exit (-1); exit (0); }\n' > .fork.c; \
+               ${CC} .fork.c -o .fork >/dev/null 2>&1; \
+               if ./.fork; then \
+                       echo ""; \
+               else \
+                       echo "-DTHERE_IS_NO_FORK"; \
+               fi; \
+               rm -f .fork.c .fork; \
+       fi;
+_HAVE_FORK!=   ${_HAVE_FORK_SH}
+CFLAGS+=       ${_HAVE_FORK}$(shell ${_HAVE_FORK_SH})
+
index af173fcaf014020d0d8048c11e487df50871a004..7eb638c988b10241615d65eb549f92a18ae31d7d 100644 (file)
--- a/mk/os.mk
+++ b/mk/os.mk
@@ -1,77 +1,7 @@
 # Setup OS specific variables
 # Copyright 2008 Roy Marples <roy@marples.name>
 
-# Work out if we need -lresolv or not
-_LIBRESOLV_SH= printf '\#include <netinet/in.h>\n\#include <resolv.h>\nint main (void) { return (res_init ()); }\n' > .res_init.c; \
-       if ${CC} .res_init.c -o .res_init >/dev/null 2>&1; then \
-               echo ""; \
-       elif ${CC} .res_init.c -lresolv -o .res_init >/dev/null 2>&1; then \
-               echo "-lresolv"; \
-       else \
-               echo "Cannot work out how to get res_init to link" >&2; \
-               rm -f .res_init.c .res_init; \
-               exit 1; \
-       fi; \
-       rm -f .res_init.c .res_init
-_LIBRESOLV!= ${_LIBRESOLV_SH}
-LIBRESOLV= ${_LIBRESOLV}$(shell ${_LIBRESOLV_SH})
-
-# Work out if we need -lrt or not
-_LIBRT_SH= printf '\#include <time.h>\n\#include <unistd.h>\n\nint main (void) { struct timespec ts;\n\#if defined(_POSIX_MONOTONIC_CLOCK) && defined(CLOCK_MONOTONIC)\nreturn (clock_gettime (CLOCK_MONOTONIC, &ts));\n\#else\nreturn -1;\n\#endif\n}\n' > .clock_gettime.c; \
-       if ${CC} .clock_gettime.c -o .clock_gettime >/dev/null 2>&1; then \
-               echo ""; \
-       elif ${CC} .clock_gettime.c -lrt -o .clock_gettime >/dev/null 2>&1; then \
-               echo "-lrt"; \
-       else \
-               echo ""; \
-       fi; \
-       rm -f .clock_gettime.c .clock_gettime
-_LIBRT!= ${_LIBRT_SH}
-LIBRT= ${_LIBRT}$(shell ${_LIBRT_SH})
-
-# Work out if our fork() works or not
-_HAVE_FORK_SH= if test "${HAVE_FORK}" = "yes"; then \
-               echo ""; \
-       elif test -n "${HAVE_FORK}"; then \
-               echo "-DTHERE_IS_NO_FORK"; \
-       else \
-               printf '\#include <stdlib.h>\n\#include <unistd.h>\nint main (void) { pid_t pid = fork(); if (pid == -1) exit (-1); exit (0); }\n' > .fork.c; \
-               ${CC} .fork.c -o .fork >/dev/null 2>&1; \
-               if ./.fork; then \
-                       echo ""; \
-               else \
-                       echo "-DTHERE_IS_NO_FORK"; \
-               fi; \
-               rm -f .fork.c .fork; \
-       fi;
-_HAVE_FORK!= ${_HAVE_FORK_SH}
-FORK= ${_HAVE_FORK}$(shell ${_HAVE_FORK_SH})
-
-# info dir defaults to /var/lib/dhcpcd on Linux and /var/db elsewhere
-_INFODIR_SH= if test -n "${INFODIR}"; then \
-                         echo "${INFODIR}"; \
-                         else \
-                         case `uname -s` in \
-                         Linux) echo "/var/lib/dhcpcd";; \
-                         *) echo "/var/db";; \
-                         esac \
-                         fi
-_INFODIR!= ${_INFODIR_SH}
-INFOD?= ${_INFODIR}$(shell ${_INFODIR_SH})
-
-# Work out how to restart services 
-_RC_SH= if test -n "${HAVE_INIT}"; then \
-                test "${HAVE_INIT}" = "no" || echo "-DENABLE_${HAVE_INIT}"; \
-                elif test -x /sbin/runscript; then echo "-DENABLE_OPENRC"; \
-                elif test -x /sbin/service; then echo "-DENABLE_SERVICE"; \
-                elif test -x /etc/rc.d/rc.S -a -x /etc/rc.d/rc.M; then echo "-DENABLE_SLACKRC"; \
-                elif test -d /etc/rc.d; then echo "-DENABLE_BSDRC"; \
-                elif test -d /etc/init.d; then echo "-DENABLE_SYSV"; \
-                fi
-_RC!= ${_RC_SH}
-RC= ${_RC}$(shell ${_RC_SH})
-
-# glibc requires _BSD_SOURCE and _XOPEN_SOURCE
-_DEF_SH=       case `uname -s` in Linux) echo "-D_BSD_SOURCE -D_XOPEN_SOURCE=600";; *) echo;; esac
-_DEF!=         ${_DEF_SH}
-CFLAGS+=       ${_DEF}$(shell ${_DEF_SH})
+_UNAME_S_SH=   case `uname -s` in *BSD|DragonFly) echo "BSD";; *) uname -s;; esac
+_UNAME_S!=     ${_UNAME_SH}
+UNAME_S=       ${_UNAME_S}$(shell ${_UNAME_S_SH})
+include ${MK}/os-${UNAME_S}.mk
index 6f2560cd1bc257e64bf90e42f5c344cfec5e7ad2..6161bb550958edd8e78806a6f101fc2d7a93fa76 100644 (file)
@@ -3,6 +3,9 @@
 
 # Copyright 2008 Roy Marples <roy@marples.name>
 
+include ${MK}/cc.mk
+include ${MK}/os.mk
+
 BINDIR?=       ${PREFIX}/usr/bin
 BINMODE?=      0755
 OBJS+=         ${SRCS:.c=.o}
index a894c9f1c3d0e6bc9abe019735142426c21e214c..4d345fbe6b5e91259bc6012a251d069b6cd47b2b 100644 (file)
--- a/socket.c
+++ b/socket.c
@@ -33,9 +33,6 @@
 #include <net/if.h>
 #include <netinet/in_systm.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 <arpa/inet.h>
 
 #include <errno.h>
@@ -46,9 +43,7 @@
 #include <time.h>
 #include <unistd.h>
 
-#if defined(BSD) || defined(__FreeBSD_kernel__)
-# include <net/bpf.h>
-#elif __linux__
+#ifdef __linux__
 # include <asm/types.h> /* needed for 2.4 kernels for the below header */
 # include <linux/filter.h>
 # include <netpacket/packet.h>
 
 #include "config.h"
 #include "dhcp.h"
-#include "interface.h"
+#include "if.h"
 #include "logger.h"
 #include "socket.h"
+#include "bpf-filter.h"
 
 /* A suitably large buffer for all transactions.
  * BPF buffer size is set by the kernel, so no define. */
@@ -74,55 +70,10 @@ static const uint8_t ipv4_bcast_addr[] = {
        0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
 };
 
-/* Credit where credit is due :)
- * The below BPF filter is taken from ISC DHCP */
-static struct bpf_insn dhcp_bpf_filter [] = {
-       /* Make sure this is an IP packet... */
-       BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12),
-       BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
-
-       /* Make sure it's a UDP packet... */
-       BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 23),
-       BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
-
-       /* Make sure this isn't a fragment... */
-       BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 20),
-       BPF_JUMP (BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
-
-       /* Get the IP header length... */
-       BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14),
-
-       /* Make sure it's to the right port... */
-       BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16),
-       BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, DHCP_CLIENT_PORT, 0, 1),
-
-       /* If we passed all the tests, ask for the whole packet. */
-       BPF_STMT (BPF_RET + BPF_K, ~0U),
-
-       /* Otherwise, drop it. */
-       BPF_STMT (BPF_RET + BPF_K, 0),
-};
-
-static struct bpf_insn arp_bpf_filter [] = {
-       /* Make sure this is an ARP packet... */
-       BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12),
-       BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_ARP, 0, 3),
-
-       /* Make sure this is an ARP REPLY... */
-       BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 20),
-       BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 0, 1),
-
-       /* If we passed all the tests, ask for the whole packet. */
-       BPF_STMT (BPF_RET + BPF_K, ~0U),
-
-       /* Otherwise, drop it. */
-       BPF_STMT (BPF_RET + BPF_K, 0),
-};
 
 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;
@@ -132,329 +83,8 @@ setup_packet_filters(void)
 
        arp_bpf_filter[1].jf = 0; /* skip the IP packet type check */
        arp_bpf_filter[2].k -= ETH_HLEN;
-#endif
-}
-
-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 (nleft == 1) {
-               memcpy(&a, p.i, 1);
-               sum += ntohs(a) << 8;
-       }
-
-       sum = (sum >> 16) + (sum & 0xffff);
-       sum += (sum >> 16);
-
-       return ~sum;
-}
-
-void
-make_dhcp_packet(struct udp_dhcp_packet *packet,
-                const unsigned char *data, size_t length,
-                struct in_addr source, struct in_addr dest)
-{
-       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));
 }
 
-static int
-valid_dhcp_packet(unsigned char *data)
-{
-       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;
-       }
-
-eexit:
-       d.packet->ip.ip_sum = ipsum;
-       d.packet->ip.ip_len = iplen;
-       d.packet->udp.uh_sum = udpsum;
-
-       return retval;
-}
-
-#if defined(BSD) || defined(__FreeBSD_kernel__)
-int
-open_socket(struct interface *iface, int protocol)
-{
-       int n = 0;
-       int fd = -1;
-       char *device;
-       int flags;
-       struct ifreq ifr;
-       int buf = 0;
-       struct bpf_program pf;
-
-       device = xmalloc(sizeof(char) * PATH_MAX);
-       do {
-               snprintf(device, PATH_MAX, "/dev/bpf%d",  n++);
-               fd = open(device, O_RDWR);
-       } while (fd == -1 && errno == EBUSY);
-       free(device);
-
-       if (fd == -1) {
-               logger(LOG_ERR, "unable to open a BPF device");
-               return -1;
-       }
-
-       close_on_exec(fd);
-
-       memset(&ifr, 0, sizeof(ifr));
-       strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
-       if (ioctl(fd, BIOCSETIF, &ifr) == -1) {
-               logger(LOG_ERR,
-                      "cannot attach interface `%s' to bpf device `%s': %s",
-                      iface->name, device, strerror(errno));
-               close(fd);
-               return -1;
-       }
-
-       /* Get the required BPF buffer length from the kernel. */
-       if (ioctl(fd, BIOCGBLEN, &buf) == -1) {
-               logger (LOG_ERR, "ioctl BIOCGBLEN: %s", strerror(errno));
-               close(fd);
-               return -1;
-       }
-       iface->buffer_length = buf;
-
-       flags = 1;
-       if (ioctl(fd, BIOCIMMEDIATE, &flags) == -1) {
-               logger(LOG_ERR, "ioctl BIOCIMMEDIATE: %s", strerror(errno));
-               close(fd);
-               return -1;
-       }
-
-       /* Install the DHCP filter */
-       if (protocol == ETHERTYPE_ARP) {
-               pf.bf_insns = arp_bpf_filter;
-               pf.bf_len = sizeof(arp_bpf_filter) / sizeof(arp_bpf_filter[0]);
-       } else {
-               pf.bf_insns = dhcp_bpf_filter;
-               pf.bf_len = sizeof(dhcp_bpf_filter)/sizeof(dhcp_bpf_filter[0]);
-       }
-       if (ioctl(fd, BIOCSETF, &pf) == -1) {
-               logger(LOG_ERR, "ioctl BIOCSETF: %s", strerror(errno));
-               close(fd);
-               return -1;
-       }
-
-       if (iface->fd > -1)
-               close(iface->fd);
-       iface->fd = fd;
-
-       return fd;
-}
-
-ssize_t
-send_packet(const struct interface *iface, int type,
-           const unsigned char *data, size_t len)
-{
-       ssize_t retval = -1;
-       struct iovec iov[2];
-       struct ether_header hw;
-
-       if (iface->family == ARPHRD_ETHER) {
-               memset(&hw, 0, sizeof(hw));
-               memset(&hw.ether_dhost, 0xff, ETHER_ADDR_LEN);
-               hw.ether_type = htons(type);
-
-               iov[0].iov_base = &hw;
-               iov[0].iov_len = sizeof(hw);
-       } else {
-               logger(LOG_ERR, "unsupported interace type %d", iface->family);
-               return -1;
-       }
-       iov[1].iov_base = (unsigned char *)data;
-       iov[1].iov_len = len;
-
-       if ((retval = writev(iface->fd, iov, 2)) == -1)
-               logger(LOG_ERR, "writev: %s", strerror(errno));
-
-       return retval;
-}
-
-/* BPF requires that we read the entire buffer.
- * So we pass the buffer in the API so we can loop on >1 dhcp packet. */
-ssize_t
-get_packet(const struct interface *iface, unsigned char *data,
-          unsigned char *buffer, size_t *buffer_len, size_t *buffer_pos)
-{
-       union
-       {
-               unsigned char *buffer;
-               struct bpf_hdr *packet;
-       } bpf;
-       union
-       {
-               unsigned char *buffer;
-               struct ether_header *hw;
-       } hdr;
-       union
-       {
-               unsigned char *buffer;
-               struct udp_dhcp_packet *packet;
-       } pay;
-       struct timespec ts;
-       size_t len;
-       unsigned char *payload;
-       bool have_data;
-
-       bpf.buffer = buffer;
-
-       if (*buffer_pos < 1) {
-               memset(bpf.buffer, 0, iface->buffer_length);
-               *buffer_len = read(iface->fd, bpf.buffer, iface->buffer_length);
-               *buffer_pos = 0;
-               if (*buffer_len < 1) {
-                       logger(LOG_ERR, "read: %s", strerror(errno));
-                       ts.tv_sec = 3;
-                       ts.tv_nsec = 0;
-                       nanosleep(&ts, NULL);
-                       return -1;
-               }
-       } else
-               bpf.buffer += *buffer_pos;
-
-       while (bpf.packet) {
-               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;
-
-               hdr.buffer = bpf.buffer + bpf.packet->bh_hdrlen;
-               payload = hdr.buffer + sizeof(*hdr.hw);
-
-               /* If it's an ARP reply, then just send it back */
-               if (hdr.hw->ether_type == htons (ETHERTYPE_ARP)) {
-                       len = bpf.packet->bh_caplen - sizeof(*hdr.hw);
-                       memcpy(data, payload, len);
-                       have_data = true;
-               } 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;
-                       }
-               }
-
-               /* Update the buffer_pos pointer */
-               bpf.buffer += BPF_WORDALIGN(bpf.packet->bh_hdrlen +
-                                           bpf.packet->bh_caplen);
-               if ((unsigned)(bpf.buffer - buffer) < *buffer_len)
-                       *buffer_pos = bpf.buffer - buffer;
-               else
-                       *buffer_pos = 0;
-
-               if (have_data)
-                       return len;
-
-               if (*buffer_pos == 0)
-                       break;
-       }
-
-       /* No valid packets left, so return */
-       *buffer_pos = 0;
-       return -1;
-}
-
-#elif __linux__
-
 int
 open_socket(struct interface *iface, int protocol)
 {
@@ -642,10 +272,3 @@ get_packet(const struct interface *iface, unsigned char *data,
        memcpy(data, &pay.packet->dhcp, bytes);
        return bytes;
 }
-
-#else
- #error "Platform not supported!"
- #error "We currently support BPF and Linux sockets."
- #error "Other platforms may work using BPF. If yours does, please let me know"
- #error "so I can add it to our list."
-#endif
index 5d371351a14936a05d8edcc7416eff11a8e50d2a..37b01b0af2c6763d1ceb03ddbaa81f5581be8518 100644 (file)
--- a/socket.h
+++ b/socket.h
 #include <stdbool.h>
 
 #include "dhcp.h"
-#include "interface.h"
+#include "if.h"
 
 void setup_packet_filters(void);
-void make_dhcp_packet(struct udp_dhcp_packet *, const unsigned char *,
-                     size_t, struct in_addr, struct in_addr);
-
 int open_socket(struct interface *, int);
 ssize_t send_packet(const struct interface *, int,
                    const unsigned char *, size_t);