From: Roy Marples Date: Mon, 24 Mar 2008 01:29:33 +0000 (+0000) Subject: Split interface and socket out into OS bpf, if-bsd and if-linux. X-Git-Tag: v4.0.2~539 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a9819bfb1fdcb3233f49d5e0fee7939868005a4d;p=thirdparty%2Fdhcpcd.git Split interface and socket out into OS bpf, if-bsd and if-linux. --- diff --git a/Makefile b/Makefile index 1db5df58..d6ac36c5 100644 --- 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 b390fa3d..399af0c7 100644 --- 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 e8928ce7..2bc3ecf8 100644 --- a/arp.h +++ b/arp.h @@ -31,7 +31,7 @@ #ifdef ENABLE_ARP #include -#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 index 00000000..17ba8453 --- /dev/null +++ b/bpf-filter.h @@ -0,0 +1,71 @@ +/* + * dhcpcd - DHCP client daemon + * Copyright 2006-2008 Roy Marples + * + * 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 index 00000000..b6af76d4 --- /dev/null +++ b/bpf.c @@ -0,0 +1,248 @@ +/* + * dhcpcd - DHCP client daemon + * Copyright 2006-2008 Roy Marples + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/client.c b/client.c index 23dd9701..edc20719 100644 --- 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 diff --git a/config.h b/config.h index 2c0991bf..4c0cd719 100644 --- 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" diff --git a/configure.c b/configure.c index 6cc14bca..9f16c7fc 100644 --- a/configure.c +++ b/configure.c @@ -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" diff --git a/configure.h b/configure.h index 8dfbf1b2..dc282a88 100644 --- a/configure.h +++ b/configure.h @@ -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 cf7baafa..1e0dffce 100644 --- a/dhcp.c +++ b/dhcp.c @@ -30,6 +30,9 @@ #include #include +#define __FAVOR_BSD /* Nasty hack so we can use BSD semantics for UDP */ +#include +#undef __FAVOR_BSD #include #include @@ -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 7b2f86be..e2c910c0 100644 --- a/dhcp.h +++ b/dhcp.h @@ -28,6 +28,12 @@ #ifndef DHCP_H #define DHCP_H +#ifdef __linux__ +# include "queue.h" /* not all libc's support queue.h, so include our own */ +#else +# include +#endif + #include #include #include @@ -35,7 +41,7 @@ #include #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 diff --git a/dhcpcd.c b/dhcpcd.c index 1e1359b1..23372199 100644 --- 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 cb335942..7a0580f4 100644 --- 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 index 00000000..7113a31d --- /dev/null +++ b/if-bsd.c @@ -0,0 +1,200 @@ +/* + * dhcpcd - DHCP client daemon + * Copyright 2006-2008 Roy Marples + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 index 00000000..0c3c1638 --- /dev/null +++ b/if-linux.c @@ -0,0 +1,347 @@ +/* + * dhcpcd - DHCP client daemon + * Copyright 2006-2008 Roy Marples + * 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 /* Needed for 2.4 kernels */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 index 00000000..458dabe2 --- /dev/null +++ b/if.c @@ -0,0 +1,551 @@ +/* + * dhcpcd - DHCP client daemon + * Copyright 2006-2008 Roy Marples + * 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 +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/interface.h b/if.h similarity index 88% rename from interface.h rename to if.h index 76be7c39..76cce404 100644 --- a/interface.h +++ b/if.h @@ -92,22 +92,6 @@ #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 c7554424..3901efa2 100644 --- 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 1c875edc..2d6e2bf9 100644 --- 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 index 7ad51d35..00000000 --- a/interface.c +++ /dev/null @@ -1,1059 +0,0 @@ -/* - * dhcpcd - DHCP client daemon - * Copyright 2006-2008 Roy Marples - * 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 -#include -#include -#include -#include - -#include - -/* Netlink suff */ -#ifdef __linux__ -#include /* Needed for 2.4 kernels */ -#include -#include -#include -#include -#else -#include -#include -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#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); -} diff --git a/ipv4ll.h b/ipv4ll.h index a4dea074..a21638e0 100644 --- 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 index 00000000..bb6b55d6 --- /dev/null +++ b/mk/os-BSD.mk @@ -0,0 +1,5 @@ +# Setup OS specific variables +# Copyright 2008 Roy Marples + +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 index 00000000..3f251db9 --- /dev/null +++ b/mk/os-Linux.mk @@ -0,0 +1,29 @@ +# Setup OS specific variables +# Copyright 2008 Roy Marples + +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 \n\#include \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}) + diff --git a/mk/os.mk b/mk/os.mk index af173fca..7eb638c9 100644 --- a/mk/os.mk +++ b/mk/os.mk @@ -1,77 +1,7 @@ # Setup OS specific variables # Copyright 2008 Roy Marples -# Work out if we need -lresolv or not -_LIBRESOLV_SH= printf '\#include \n\#include \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 \n\#include \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 \n\#include \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 diff --git a/mk/prog.mk b/mk/prog.mk index 6f2560cd..6161bb55 100644 --- a/mk/prog.mk +++ b/mk/prog.mk @@ -3,6 +3,9 @@ # Copyright 2008 Roy Marples +include ${MK}/cc.mk +include ${MK}/os.mk + BINDIR?= ${PREFIX}/usr/bin BINMODE?= 0755 OBJS+= ${SRCS:.c=.o} diff --git a/socket.c b/socket.c index a894c9f1..4d345fbe 100644 --- a/socket.c +++ b/socket.c @@ -33,9 +33,6 @@ #include #include #include -#define __FAVOR_BSD /* Nasty hack so we can use BSD semantics for UDP */ -#include -#undef __FAVOR_BSD #include #include @@ -46,9 +43,7 @@ #include #include -#if defined(BSD) || defined(__FreeBSD_kernel__) -# include -#elif __linux__ +#ifdef __linux__ # include /* needed for 2.4 kernels for the below header */ # include # include @@ -57,9 +52,10 @@ #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 diff --git a/socket.h b/socket.h index 5d371351..37b01b0a 100644 --- a/socket.h +++ b/socket.h @@ -31,12 +31,9 @@ #include #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);