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
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
#include "config.h"
#include "common.h"
#include "arp.h"
-#include "interface.h"
+#include "if.h"
#include "logger.h"
#include "signal.h"
#include "socket.h"
#ifdef ENABLE_ARP
#include <netinet/in.h>
-#include "interface.h"
+#include "if.h"
int arp_claim(struct interface *, struct in_addr);
#endif
--- /dev/null
+/*
+ * 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),
+};
--- /dev/null
+/*
+ * 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;
+}
#include "dhcp.h"
#include "dhcpcd.h"
#include "info.h"
-#include "interface.h"
+#include "if.h"
#ifdef ENABLE_IPV4LL
# include "ipv4ll.h"
#endif
#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"
#ifdef ENABLE_INFO
# include "info.h"
#endif
-#include "interface.h"
+#include "if.h"
#include "dhcpcd.h"
#include "logger.h"
#include "signal.h"
#define DHCPCONFIG_H
#include "dhcpcd.h"
-#include "interface.h"
+#include "if.h"
#include "dhcp.h"
int configure(const struct options *, struct interface *,
#include <sys/time.h>
#include <netinet/in.h>
+#define __FAVOR_BSD /* Nasty hack so we can use BSD semantics for UDP */
+#include <netinet/udp.h>
+#undef __FAVOR_BSD
#include <net/if_arp.h>
#include <arpa/inet.h>
#include "common.h"
#include "dhcpcd.h"
#include "dhcp.h"
-#include "interface.h"
#include "logger.h"
#include "socket.h"
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)
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)
{
}
}
+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)
#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>
#include <stdint.h>
#include "dhcpcd.h"
-#include "interface.h"
+#include "if.h"
/* Max MTU - defines dhcp option length */
#define MTU_MAX 1500
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];
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
#include "client.h"
#include "dhcpcd.h"
#include "dhcp.h"
-#include "interface.h"
+#include "if.h"
#include "logger.h"
#include "socket.h"
#include "version.h"
# define DUID_LEN 128 + 2
#endif
-#include "interface.h"
+#include "if.h"
size_t get_duid(unsigned char *, const struct interface *);
#endif
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <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;
+}
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <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;
+}
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <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);
+}
#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];
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 *);
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
#include "config.h"
#include "common.h"
#include "dhcp.h"
-#include "interface.h"
+#include "if.h"
#include "logger.h"
#include "info.h"
#define INFO_H
#include "dhcpcd.h"
-#include "interface.h"
+#include "if.h"
#include "dhcp.h"
#ifdef ENABLE_INFO
+++ /dev/null
-/*
- * dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
- * All rights reserved
-
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/types.h>
-#include <sys/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);
-}
#ifdef ENABLE_IPV4LL
#include "dhcp.h"
-#include "interface.h"
+#include "if.h"
int ipv4ll_get_address(struct interface *, struct dhcp *);
--- /dev/null
+# Setup OS specific variables
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+SRC_SOCKET= bpf.c
+SRC_IF= if-bsd.c
--- /dev/null
+# 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})
+
# 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
# 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}
#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>
#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. */
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;
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)
{
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
#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);