state = ARP_STATE(ifp);
if (state->fd == -1) {
- state->fd = if_openraw(ifp, ETHERTYPE_ARP);
+ state->fd = if_openraw(ifp, ETHERTYPE_ARP, bpf_arp);
if (state->fd == -1) {
logger(ifp->ctx, LOG_ERR, "%s: %s: %m",
__func__, ifp->name);
astate->addr = *addr;
state = ARP_STATE(ifp);
TAILQ_INSERT_TAIL(&state->arp_states, astate, next);
+
+ bpf_arp(ifp, state->fd);
+
return astate;
}
arp_close(ifp);
free(state);
ifp->if_data[IF_DATA_ARP] = NULL;
- }
+ } else
+ bpf_arp(ifp, state->fd);
}
static void
+++ /dev/null
-/*
- * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2017 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.
- */
-
-#ifndef BPF_ETHCOOK
-# define BPF_ETHCOOK 0
-#endif
-#ifndef BPF_WHOLEPACKET
-# define BPF_WHOLEPACKET ~0U
-#endif
-
-static const struct bpf_insn arp_bpf_filter [] = {
-#ifndef BPF_SKIPTYPE
- /* 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, 1, 0),
- BPF_STMT(BPF_RET + BPF_K, 0),
-#endif
- /* Make sure this is for IP ... */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 16 + BPF_ETHCOOK),
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
- BPF_STMT(BPF_RET + BPF_K, 0),
- /* Make sure this is an ARP REQUEST... */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 + BPF_ETHCOOK),
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0),
- /* or ARP REPLY... */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 1),
- BPF_STMT(BPF_RET + BPF_K, 0),
- /* Pass back the whole packet. */
- BPF_STMT(BPF_RET + BPF_K, BPF_WHOLEPACKET),
-};
-#define arp_bpf_filter_len __arraycount(arp_bpf_filter)
-
-static const struct bpf_insn bootp_bpf_filter [] = {
-#ifndef BPF_SKIPTYPE
- /* 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, 1, 0),
- BPF_STMT(BPF_RET + BPF_K, 0),
-#endif
- /* Make sure it's a UDP packet... */
- BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23 + BPF_ETHCOOK),
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0),
- BPF_STMT(BPF_RET + BPF_K, 0),
- /* Make sure this isn't a fragment... */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 + BPF_ETHCOOK),
- BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 0, 1),
- BPF_STMT(BPF_RET + BPF_K, 0),
- /* Get the IP header length... */
- BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14 + BPF_ETHCOOK),
- /* Make sure it's to the right port... */
- BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16 + BPF_ETHCOOK),
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTPC, 1, 0),
- BPF_STMT(BPF_RET + BPF_K, 0),
- /* Pass back the whole packet. */
- BPF_STMT(BPF_RET + BPF_K, BPF_WHOLEPACKET),
-};
-#define bootp_bpf_filter_len __arraycount(bootp_bpf_filter)
--- /dev/null
+/*
+ * dhcpcd: BPF arp and bootp functions
+ * Copyright (c) 2006-2017 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <arpa/inet.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#ifdef __linux__
+#include <linux/filter.h>
+#else
+#include <net/bpf.h>
+#endif
+
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "common.h"
+#include "arp.h"
+#include "dhcp.h"
+#include "if.h"
+
+#define ARP_ADDRS_MAX 3
+
+/* BPF helper macros */
+#ifdef __linux__
+#define BPF_L2L 0
+#define BPF_L2I 0
+#define BPF_WHOLEPACKET 0x0fffffff /* work around buggy LPF filters */
+#else
+#define BPF_L2L ETHER_ADDR_LEN + ETHER_ADDR_LEN + 2
+#define BPF_L2I 3
+#define BPF_WHOLEPACKET ~0U
+#endif
+
+/* Macros to update the BPF structure */
+#define BPF_SET_STMT(insn, c, v) { \
+ (insn)->code = (c); \
+ (insn)->jt = 0; \
+ (insn)->jf = 0; \
+ (insn)->k = (uint32_t)(v); \
+};
+
+#define BPF_SET_JUMP(insn, c, v, t, f) { \
+ (insn)->code = (c); \
+ (insn)->jt = (t); \
+ (insn)->jf = (f); \
+ (insn)->k = (uint32_t)(v); \
+};
+
+static unsigned int
+bpf_cmp_hwaddr(struct bpf_insn *bpf, size_t bpf_len, size_t off,
+ bool equal, uint8_t *hwaddr, size_t hwaddr_len)
+{
+ struct bpf_insn *bp;
+ size_t maclen, nlft, njmps;
+ uint32_t mac32;
+ uint16_t mac16;
+ uint8_t jt, jf;
+
+ /* Calc the number of jumps */
+ if ((hwaddr_len / 4) >= 128) {
+ errno = EINVAL;
+ return 0;
+ }
+ njmps = (hwaddr_len / 4) * 2; /* 2 instructions per check */
+ /* We jump after the 1st check. */
+ if (njmps)
+ njmps -= 2;
+ nlft = hwaddr_len % 4;
+ if (nlft) {
+ njmps += (nlft / 2) * 2;
+ nlft = nlft % 2;
+ if (nlft)
+ njmps += 2;
+
+ }
+
+ /* Skip to positive finish. */
+ njmps++;
+ jt = equal ? (uint8_t)njmps : 0;
+ jf = equal ? 0 : (uint8_t)njmps;
+
+ bp = bpf;
+ for (; hwaddr_len > 0;
+ hwaddr += maclen, hwaddr_len -= maclen, off += maclen)
+ {
+ if (bpf_len < 3) {
+ errno = ENOBUFS;
+ return 0;
+ }
+ bpf_len -= 3;
+
+ if (hwaddr_len >= 4) {
+ maclen = sizeof(mac32);
+ memcpy(&mac32, hwaddr, maclen);
+ BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_ABS,
+ BPF_L2L + off);
+ bp++;
+ BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
+ htonl(mac32), jt, jf);
+ } else if (hwaddr_len >= 2) {
+ maclen = sizeof(mac16);
+ memcpy(&mac16, hwaddr, maclen);
+ BPF_SET_STMT(bp, BPF_LD + BPF_H + BPF_ABS,
+ BPF_L2L + off);
+ bp++;
+ BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
+ htons(mac16), jt, jf);
+ } else {
+ maclen = sizeof(*hwaddr);
+ BPF_SET_STMT(bp, BPF_LD + BPF_B + BPF_ABS,
+ BPF_L2L + off);
+ bp++;
+ BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
+ *hwaddr, jt, jf);
+ }
+ if (jt)
+ jt = (uint8_t)(jt - 2);
+ if (jf)
+ jf = (uint8_t)(jf - 2);
+ bp++;
+ }
+
+ /* Last step is always return failure.
+ * Next step is a positive finish. */
+ BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
+ bp++;
+
+ return (unsigned int)(bp - bpf);
+}
+
+#ifdef ARP
+static const struct bpf_insn arp_bpf_filter [] = {
+ /* Ensure packet is at least correct size. */
+ BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0),
+ BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K,
+ BPF_L2L + sizeof(struct arphdr)
+ + (ETHER_ADDR_LEN * 2)
+ + (sizeof(in_addr_t) * 2), 1, 0),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+#if BPF_L2L > 0
+ /* Make sure this is an ARP packet. */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
+ offsetof(struct ether_header, ether_type)),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_ARP, 1, 0),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+#endif
+ /* Make sure this is for IP. */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
+ BPF_L2L + offsetof(struct arphdr, ar_pro)),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+ /* Make sure this is an ARP REQUEST. */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
+ BPF_L2L + offsetof(struct arphdr, ar_op)),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0),
+ /* or ARP REPLY. */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 1),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+ /* Make sure the hardware length matches. */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
+ BPF_L2L + offsetof(struct arphdr, ar_hln)),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHER_ADDR_LEN, 1, 0),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+ /* Make sure the protocol length matches. */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
+ BPF_L2L + offsetof(struct arphdr, ar_pln)),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(in_addr_t), 1, 0),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+ /* Pass back the whole packet. */
+ BPF_STMT(BPF_RET + BPF_K, BPF_WHOLEPACKET),
+};
+#define arp_bpf_filter_len __arraycount(arp_bpf_filter)
+#define arp_bpf_extra ((ARP_ADDRS_MAX * 2) * 2) + 2
+
+int
+bpf_arp(struct interface *ifp, int s)
+{
+ size_t bpf_hw = ((((size_t)ifp->hwlen / 4) + 2) * 2) + 1;
+ struct bpf_insn bpf[arp_bpf_filter_len + bpf_hw + arp_bpf_extra];
+ struct bpf_insn *bp;
+ struct iarp_state *state;
+
+ if (s == -1)
+ return 0;
+ memcpy(bpf, arp_bpf_filter, sizeof(arp_bpf_filter));
+ bp = &bpf[arp_bpf_filter_len];
+
+ /* Ensure it's not from us. */
+ bp--;
+ bp += bpf_cmp_hwaddr(bp, bpf_hw, sizeof(struct arphdr),
+ false, ifp->hwaddr, ifp->hwlen);
+
+ state = ARP_STATE(ifp);
+ if (TAILQ_FIRST(&state->arp_states)) {
+ struct arp_state *astate;
+ size_t naddrs;
+
+ /* Match sender protocol address */
+ BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_ABS,
+ BPF_L2L + sizeof(struct arphdr) + ifp->hwlen);
+ bp++;
+ naddrs = 0;
+ TAILQ_FOREACH(astate, &state->arp_states, next) {
+ if (++naddrs > ARP_ADDRS_MAX) {
+ errno = ENOBUFS;
+ logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
+ break;
+ }
+ BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
+ htonl(astate->addr.s_addr), 0, 1);
+ bp++;
+ BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
+ bp++;
+ }
+
+ /* Match target protocol address */
+ BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_ABS,
+ BPF_L2L + sizeof(struct arphdr) + ifp->hwlen);
+ bp++;
+ naddrs = 0;
+ TAILQ_FOREACH(astate, &state->arp_states, next) {
+ if (++naddrs > ARP_ADDRS_MAX) {
+ /* Already logged error above. */
+ break;
+ }
+ BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
+ htonl(astate->addr.s_addr), 0, 1);
+ bp++;
+ BPF_SET_STMT(bp, BPF_RET + BPF_K,
+ BPF_WHOLEPACKET);
+ bp++;
+ }
+
+ /* Return nothing, no protocol address match. */
+ BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
+ bp++;
+ }
+
+ /* Replace ETHER_ADDR_LEN for Infiniband if needed. */
+ if (ifp->hwlen != ETHER_ADDR_LEN) {
+ bpf[1].k += (uint32_t)(ifp->hwlen - ETHER_ADDR_LEN) * 2;
+ bpf[BPF_L2I + 11].k = ifp->hwlen;
+ }
+
+ return if_bpf_attach(s, bpf, (unsigned int)(bp - bpf));
+}
+#endif
+
+static const struct bpf_insn bootp_bpf_filter[] = {
+ /* Ensure packet is at least correct size. */
+ BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0),
+ BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K,
+ BPF_L2L + sizeof(struct ip) + sizeof(struct udphdr)
+ + offsetof(struct bootp, vend), 1, 0),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+#if BPF_L2L
+ /* Make sure this is an IP packet. */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
+ offsetof(struct ether_header, ether_type)),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+#endif
+ /* Make sure it's a UDP packet. */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
+ BPF_L2L + offsetof(struct bootp_pkt, ip.ip_p)),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+ /* Make sure this isn't a fragment. */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
+ BPF_L2L + offsetof(struct bootp_pkt, ip.ip_off)),
+ BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 0, 1),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+ /* Make sure it's to the right port. */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
+ BPF_L2L + offsetof(struct bootp_pkt, udp.uh_dport)),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTPC, 1, 0),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+ /* Make sure it's BOOTREPLY. */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
+ BPF_L2L + offsetof(struct bootp_pkt, bootp.op)),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+ /* Pass back the whole packet. */
+ BPF_STMT(BPF_RET + BPF_K, BPF_WHOLEPACKET),
+};
+#define bootp_bpf_filter_len __arraycount(bootp_bpf_filter)
+#define bootp_bpf_extra 3 + ((BOOTP_CHADDR_LEN / 4) * 3)
+
+int
+bpf_bootp(struct interface *ifp, int fd)
+{
+ const struct dhcp_state *state = D_CSTATE(ifp);
+ struct bpf_insn bpf[bootp_bpf_filter_len + bootp_bpf_extra];
+ struct bpf_insn *bp;
+ unsigned int bpf_len = bootp_bpf_extra;
+
+ if (fd == -1)
+ return 0;
+
+ memcpy(bpf, bootp_bpf_filter, sizeof(bootp_bpf_filter));
+ bp = &bpf[bootp_bpf_filter_len];
+
+ if (state->state != DHS_BOUND ||
+ ifp->hwlen <= sizeof(((struct bootp *)0)->chaddr))
+ bp--;
+
+ if (state->state != DHS_BOUND) {
+ /* Make sure the BOOTP packet is for us. */
+ BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_ABS,
+ BPF_L2L + offsetof(struct bootp_pkt, bootp.xid));
+ bp++;
+ BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
+ state->xid, 1, 0);
+ bp++;
+ BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
+ bp++;
+ bpf_len -= 3;
+ }
+
+ if (ifp->hwlen <= sizeof(((struct bootp *)0)->chaddr))
+ bp += bpf_cmp_hwaddr(bp, bpf_len,
+ offsetof(struct bootp_pkt, bootp.chaddr),
+ true, ifp->hwaddr, ifp->hwlen);
+
+ if (state->state != DHS_BOUND ||
+ ifp->hwlen <= sizeof(((struct bootp *)0)->chaddr))
+ {
+ BPF_SET_STMT(bp, BPF_RET + BPF_K,
+ BPF_WHOLEPACKET);
+ bp++;
+ }
+
+ return if_bpf_attach(fd, bpf, (unsigned int)(bp - bpf));
+}
if [ -z "$INET" -o "$INET" = yes ]; then
echo "Enabling INET support"
echo "CPPFLAGS+= -DINET" >>$CONFIG_MK
- echo "DHCPCD_SRCS+= dhcp.c ipv4.c" >>$CONFIG_MK
+ echo "DHCPCD_SRCS+= dhcp.c ipv4.c bpf.c" >>$CONFIG_MK
if [ -z "$ARP" -o "$ARP" = yes ]; then
echo "Enabling ARP support"
echo "CPPFLAGS+= -DARP" >>$CONFIG_MK
NULL
};
-struct udp_bootp_packet
-{
- struct ip ip;
- struct udphdr udp;
- uint8_t bootp[];
-};
-
static int dhcp_open(struct interface *);
#ifdef ARP
static void dhcp_arp_conflicted(struct arp_state *, const struct arp_msg *);
dhcpcd_startinterface(iface);
}
-static uint32_t
-dhcp_xid(const struct interface *ifp)
+static void
+dhcp_new_xid(struct interface *ifp)
{
- uint32_t xid;
+ struct dhcp_state *state;
+ state = D_STATE(ifp);
if (ifp->options->options & DHCPCD_XID_HWADDR &&
- ifp->hwlen >= sizeof(xid))
+ ifp->hwlen >= sizeof(state->xid))
/* The lower bits are probably more unique on the network */
- memcpy(&xid, (ifp->hwaddr + ifp->hwlen) - sizeof(xid),
- sizeof(xid));
+ memcpy(&state->xid,
+ (ifp->hwaddr + ifp->hwlen) - sizeof(state->xid),
+ sizeof(state->xid));
else
- xid = arc4random();
+ state->xid = arc4random();
- return xid;
+ /* As the XID changes, re-apply the filter. */
+ if (state->raw_fd != -1)
+ bpf_bootp(ifp, state->raw_fd);
}
void
return (uint16_t)~htons((uint16_t)sum);
}
-static struct udp_bootp_packet *
+static struct bootp_pkt *
dhcp_makeudppacket(size_t *sz, const uint8_t *data, size_t length,
struct in_addr source, struct in_addr dest)
{
- struct udp_bootp_packet *udpp;
+ struct bootp_pkt *udpp;
struct ip *ip;
struct udphdr *udp;
struct dhcp_state *state = D_STATE(ifp);
struct if_options *ifo = ifp->options;
struct bootp *bootp;
- struct udp_bootp_packet *udp;
+ struct bootp_pkt *udp;
size_t len;
ssize_t r;
struct in_addr from, to;
struct if_options *ifo = ifp->options;
state->state = DHS_DISCOVER;
- state->xid = dhcp_xid(ifp);
+ dhcp_new_xid(ifp);
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
if (ifo->fallback)
eloop_timeout_add_sec(ifp->ctx->eloop,
logger(ifp->ctx, LOG_DEBUG, "%s: renewing lease of %s",
ifp->name, inet_ntoa(lease->addr));
state->state = DHS_RENEW;
- state->xid = dhcp_xid(ifp);
+ dhcp_new_xid(ifp);
state->interval = 0;
send_renew(ifp);
}
ifp->name, lease->renewaltime, lease->rebindtime);
}
state->state = DHS_BOUND;
+ /* Re-apply the filter because we need to accept any XID anymore. */
+ bpf_bootp(ifp, state->raw_fd);
if (!state->lease.frominfo &&
!(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
if (write_lease(ifp, state->new, state->new_len) == -1)
state->offer_len = dhcp_message_new(&state->offer,
&ia->addr, &ia->mask);
if (state->offer_len) {
- state->xid = dhcp_xid(ifp);
+ dhcp_new_xid(ifp);
get_lease(ifp, &state->lease, state->offer, state->offer_len);
send_inform(ifp);
}
logger(ifp->ctx, LOG_INFO, "%s: rebinding lease of %s",
ifp->name, inet_ntoa(state->lease.addr));
- state->xid = dhcp_xid(ifp);
+ dhcp_new_xid(ifp);
state->lease.server.s_addr = 0;
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
{
logger(ifp->ctx, LOG_INFO, "%s: releasing lease of %s",
ifp->name, inet_ntoa(state->lease.addr));
- state->xid = dhcp_xid(ifp);
+ dhcp_new_xid(ifp);
send_message(ifp, DHCP_RELEASE, NULL);
#ifdef RELEASE_SLOW
/* Give the packet a chance to go */
#define LOGDHCP(l, m) \
log_dhcp((l), (m), ifp, bootp, bootp_len, from, 1)
+ /* Handled in our BPF filter. */
+#if 0
if (bootp->op != BOOTREPLY) {
logger(ifp->ctx, LOG_DEBUG, "%s: op (%d) is not BOOTREPLY",
ifp->name, bootp->op);
return;
}
- /* Ensure packet is for us */
if (ifp->hwlen <= sizeof(bootp->chaddr) &&
memcmp(bootp->chaddr, ifp->hwaddr, ifp->hwlen))
{
buf, sizeof(buf)));
return;
}
+#endif
/* We may have found a BOOTP server */
if (get_option_uint8(ifp->ctx, &type,
return;
}
+ /* Handled in our BPF filter. */
+#if 0
/* Ensure it's the right transaction */
if (state->xid != ntohl(bootp->xid)) {
logger(ifp->ctx, LOG_DEBUG,
inet_ntoa(*from));
return;
}
+#endif
if (state->state == DHS_PROBE) {
/* Ignore any DHCP messages whilst probing a lease to bind. */
static void *
get_udp_data(void *udp, size_t *len)
{
- struct udp_bootp_packet *p;
+ struct bootp_pkt *p;
- p = (struct udp_bootp_packet *)udp;
+ p = (struct bootp_pkt *)udp;
*len = ntohs(p->ip.ip_len) - sizeof(p->ip) - sizeof(p->udp);
- return (char *)udp + offsetof(struct udp_bootp_packet, bootp);
+ return (char *)udp + offsetof(struct bootp_pkt, bootp);
}
static int
valid_udp_packet(void *data, size_t data_len, struct in_addr *from,
int noudpcsum)
{
- struct udp_bootp_packet *p;
+ struct bootp_pkt *p;
uint16_t bytes;
if (data_len < sizeof(p->ip) + sizeof(p->udp)) {
errno = EINVAL;
return -1;
}
- p = (struct udp_bootp_packet *)data;
+ p = (struct bootp_pkt *)data;
if (from)
from->s_addr = p->ip.ip_src.s_addr;
if (checksum(&p->ip, sizeof(p->ip)) != 0) {
}
}
+
static int
dhcp_open(struct interface *ifp)
{
state = D_STATE(ifp);
if (state->raw_fd == -1) {
- state->raw_fd = if_openraw(ifp, ETHERTYPE_IP);
+ state->raw_fd = if_openraw(ifp, ETHERTYPE_IP, bpf_bootp);
if (state->raw_fd == -1) {
if (errno == ENOENT) {
logger(ifp->ctx, LOG_ERR,
script_runreason(ifp, state->reason);
if (ifo->options & DHCPCD_INFORM) {
state->state = DHS_INFORM;
- state->xid = dhcp_xid(ifp);
+ dhcp_new_xid(ifp);
state->lease.server.s_addr = INADDR_ANY;
state->addr = ia;
dhcp_inform(ifp);
#include <arpa/inet.h>
#include <netinet/in.h>
+#include <netinet/ip.h>
+#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */
+#include <netinet/udp.h>
+#undef __FAVOR_BSD
+
#include <limits.h>
#include <stdint.h>
/* DHCP allows a variable length vendor area */
};
+struct bootp_pkt
+{
+ struct ip ip;
+ struct udphdr udp;
+ struct bootp bootp;
+};
+
struct dhcp_lease {
struct in_addr addr;
struct in_addr mask;
#include "route.h"
#include "sa.h"
-#include "bpf-filter.h"
-
#ifndef RT_ROUNDUP
#define RT_ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
}
int
-if_openraw(struct interface *ifp, uint16_t protocol)
+if_openraw(struct interface *ifp, __unused uint16_t protocol,
+ int (*filter)(struct interface *, int))
{
struct ipv4_state *state;
int fd = -1;
int ibuf_len = 0;
size_t buf_len;
struct bpf_version pv;
- struct bpf_program pf;
#ifdef BIOCIMMEDIATE
int flags;
#endif
goto eexit;
}
+ if (filter(ifp, fd) != 0)
+ goto eexit;
+
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
if (ioctl(fd, BIOCSETIF, &ifr) == -1)
goto eexit;
#endif
- /* Install the filter. */
- memset(&pf, 0, sizeof(pf));
- if (protocol == ETHERTYPE_ARP) {
- pf.bf_insns = UNCONST(arp_bpf_filter);
- pf.bf_len = arp_bpf_filter_len;
- } else {
- pf.bf_insns = UNCONST(bootp_bpf_filter);
- pf.bf_len = bootp_bpf_filter_len;
- }
- if (ioctl(fd, BIOCSETF, &pf) == -1)
- goto eexit;
-
return fd;
eexit:
}
}
+int
+if_bpf_attach(int s, struct bpf_insn *filter, unsigned int filter_len)
+{
+ struct bpf_program pf;
+
+ /* Install the filter. */
+ memset(&pf, 0, sizeof(pf));
+ pf.bf_insns = filter;
+ pf.bf_len = filter_len;
+ return ioctl(s, BIOCSETF, &pf);
+}
+
int
if_address(unsigned char cmd, const struct ipv4_addr *ia)
{
#define BPF_ETHCOOK -ETH_HLEN
#define BPF_WHOLEPACKET 0x0fffffff /* work around buggy LPF filters */
-#include "bpf-filter.h"
-
struct priv {
int route_fd;
uint32_t route_pid;
}
int
-if_openraw(struct interface *ifp, uint16_t protocol)
+if_openraw(struct interface *ifp, uint16_t protocol,
+ int (*filter)(struct interface *, int))
{
int s;
union sockunion {
struct sockaddr_ll sll;
struct sockaddr_storage ss;
} su;
- struct sock_fprog pf;
#ifdef PACKET_AUXDATA
int n;
#endif
return -1;
#undef SF
- /* Install the filter. */
- memset(&pf, 0, sizeof(pf));
- if (protocol == ETHERTYPE_ARP) {
- pf.filter = UNCONST(arp_bpf_filter);
- pf.len = arp_bpf_filter_len;
- } else {
- pf.filter = UNCONST(bootp_bpf_filter);
- pf.len = bootp_bpf_filter_len;
- }
- if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf)) != 0)
+ if (filter(ifp, s) != 0)
goto eexit;
+
#ifdef PACKET_AUXDATA
n = 1;
if (setsockopt(s, SOL_PACKET, PACKET_AUXDATA, &n, sizeof(n)) != 0) {
return bytes;
}
+int
+if_bpf_attach(int s, struct bpf_insn *filter, unsigned int filter_len)
+{
+ struct sock_fprog pf;
+
+ /* Install the filter. */
+ memset(&pf, 0, sizeof(pf));
+ pf.filter = filter;
+ pf.len = (unsigned short)filter_len;
+ return setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf));
+}
+
int
if_address(unsigned char cmd, const struct ipv4_addr *addr)
{
#endif
#ifdef INET
+#ifdef __linux__
+#define bpf_insn sock_filter
+#endif
+struct bpf_insn;
extern const char *if_pfname;
-int if_openraw(struct interface *, uint16_t);
+int if_openraw(struct interface *, uint16_t, int (*)(struct interface *, int));
ssize_t if_sendraw(const struct interface *, int, uint16_t,
const void *, size_t);
ssize_t if_readraw(struct interface *, int, void *, size_t, int *);
void if_closeraw(struct interface *, int);
+int if_bpf_attach(int, struct bpf_insn *, unsigned int);
+int bpf_arp(struct interface *, int);
+int bpf_bootp(struct interface *, int);
int if_address(unsigned char, const struct ipv4_addr *);
int if_addrflags(const struct interface *, const struct in_addr *,