summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2017-03-11 12:17:45 +0000
committerRoy Marples <roy@marples.name>2017-03-11 12:17:45 +0000
commitde29cb22d9f734bf26791e0edd68abaf6bd20e3a (patch)
tree4b578cd0da9bee5e829bd37afeedf08cf4946aa0
parent175dd2089af6aad3770dad7945eadd3d68800edd (diff)
downloadparpd-de29cb22d9f734bf26791e0edd68abaf6bd20e3a.tar.xz
Improve the BPF filter based on the latest dhcpcd code.
This does hardwire the filter to ethernet, but then it already was on non Linux platforms anyway.
-rw-r--r--bpf-filter.h57
-rw-r--r--bpf.c23
-rw-r--r--lpf.c80
-rw-r--r--parpd.c6
4 files changed, 117 insertions, 49 deletions
diff --git a/bpf-filter.h b/bpf-filter.h
index 4e77a2f..c92177d 100644
--- a/bpf-filter.h
+++ b/bpf-filter.h
@@ -24,24 +24,55 @@
* SUCH DAMAGE.
*/
-#ifndef BPF_ETHCOOK
-# define BPF_ETHCOOK 0
-#endif
#ifndef BPF_WHOLEPACKET
# define BPF_WHOLEPACKET ~0U
#endif
+
+/* This is hardcoded for ethernet. */
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, 0, 3),
-#endif
- /* 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, 0, 1),
+ /* Ensure packet is at least correct size. */
+ BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0),
+ BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+
+ /* Check 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),
+
+ /* Load frame header length into X */
+ BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)),
+
+ /* Make sure the hardware family matches. */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_hrd)),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+
+ /* Make sure the hardware length matches. */
+ BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_hln)),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K,
+ sizeof((struct ether_arp *)0)->arp_sha, 1, 0),
+ BPF_STMT(BPF_RET + BPF_K, 0),
+
+ /* Make sure this is for IP. */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 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_IND, 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 protocol length matches. */
+ BPF_STMT(BPF_LD + BPF_B + BPF_IND, 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),
+
/* If we passed all the tests, ask for the whole packet. */
BPF_STMT(BPF_RET + BPF_K, BPF_WHOLEPACKET),
- /* Otherwise, drop it. */
- BPF_STMT(BPF_RET + BPF_K, 0),
};
#define arp_bpf_filter_len sizeof(arp_bpf_filter) / sizeof(arp_bpf_filter[0])
diff --git a/bpf.c b/bpf.c
index 08b1d51..8703d00 100644
--- a/bpf.c
+++ b/bpf.c
@@ -1,6 +1,6 @@
/*
- * parpd - Proxy ARP Daemon
- * Copyright (c) 2008-2016 Roy Marples <roy@marples.name>
+ * parpd: Berkley Packet Filter
+ * Copyright (c) 2008-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
@@ -39,6 +39,7 @@
#include <fcntl.h>
#include <limits.h>
#include <paths.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -127,13 +128,19 @@ send_raw_packet(const struct interface *ifp,
const void *data, size_t len)
{
struct iovec iov[2];
- struct ether_header hw;
+ struct ether_header eh;
- memset(&hw, 0, ETHER_HDR_LEN);
- memcpy(&hw.ether_dhost, hwaddr, hwlen);
- hw.ether_type = htons(ETHERTYPE_ARP);
- iov[0].iov_base = &hw;
- iov[0].iov_len = ETHER_HDR_LEN;
+ if (ifp->hwlen != hwlen || hwlen != sizeof(eh.ether_dhost)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&eh, 0, sizeof(eh));
+ memcpy(&eh.ether_dhost, hwaddr, sizeof(eh.ether_dhost));
+ memcpy(&eh.ether_shost, ifp->hwaddr, sizeof(eh.ether_shost));
+ eh.ether_type = htons(ETHERTYPE_ARP);
+ iov[0].iov_base = &eh;
+ iov[0].iov_len = sizeof(eh);
iov[1].iov_base = UNCONST(data);
iov[1].iov_len = len;
return writev(ifp->fd, iov, 2);
diff --git a/lpf.c b/lpf.c
index 43f73ec..0a49c8e 100644
--- a/lpf.c
+++ b/lpf.c
@@ -1,6 +1,6 @@
/*
- * parpd - Proxy ARP Daemon
- * Copyright (c) 2008-2014 Roy Marples <roy@marples.name>
+ * parpd: Linux Packet Filter
+ * Copyright (c) 2008-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
@@ -31,6 +31,7 @@
#include <arpa/inet.h>
#include <net/if.h>
#include <net/if_arp.h>
+#include <netinet/if_ether.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
@@ -39,12 +40,11 @@
#include <net/ethernet.h>
#include <netpacket/packet.h>
#define bpf_insn sock_filter
-#define BPF_SKIPTYPE
-#define BPF_ETHCOOK -ETH_HLEN
-#define BPF_WHOLEPACKET 0x0fffffff /* work around buggy LPF filters */
+#define BPF_WHOLEPACKET 0x7fffffff /* work around buggy LPF filters */
#include <errno.h>
#include <fcntl.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -57,20 +57,17 @@ int
open_arp(struct interface *ifp)
{
int s, flags;
- struct sockaddr_ll sll;
+ union sockunion {
+ struct sockaddr sa;
+ struct sockaddr_ll sll;
+ struct sockaddr_storage ss;
+ } su;
struct sock_fprog pf;
- if ((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_ARP))) == -1)
+ if ((s = socket(PF_PACKET, SOCK_RAW, htons(ETHERTYPE_ARP))) == -1)
return -1;
- memset(&sll, 0, sizeof(sll));
- sll.sll_family = PF_PACKET;
- sll.sll_protocol = htons(ETHERTYPE_ARP);
- if (!(sll.sll_ifindex = if_nametoindex(ifp->ifname))) {
- errno = ENOENT;
- goto eexit;
- }
- /* Install the DHCP filter */
+ /* Install the ARP filter */
memset(&pf, 0, sizeof(pf));
pf.filter = UNCONST(arp_bpf_filter);
pf.len = arp_bpf_filter_len;
@@ -79,8 +76,25 @@ open_arp(struct interface *ifp)
if ((flags = fcntl(s, F_GETFL, 0)) == -1
|| fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1)
goto eexit;
- if (bind(s, (struct sockaddr *)&sll, sizeof(sll)) == -1)
+
+ /* Allocate a suitably large buffer for a single packet. */
+ if (ifp->buffer_size < ETH_DATA_LEN) {
+ void *nb;
+
+ if ((nb = realloc(ifp->buffer, ETH_DATA_LEN)) == NULL)
+ goto eexit;
+ ifp->buffer = nb;
+ ifp->buffer_size = ETH_DATA_LEN;
+ ifp->buffer_len = ifp->buffer_pos = 0;
+ }
+
+ memset(&su, 0, sizeof(su));
+ su.sll.sll_family = PF_PACKET;
+ su.sll.sll_protocol = htons(ETH_P_ALL);
+ su.sll.sll_ifindex = if_nametoindex(ifp->ifname);
+ if (bind(s, &su.sa, sizeof(su.sll)) == -1)
goto eexit;
+
return s;
eexit:
@@ -93,21 +107,23 @@ send_raw_packet(const struct interface *ifp,
const uint8_t *hwaddr, size_t hwlen,
const void *data, size_t len)
{
- struct sockaddr_ll sll;
+ struct iovec iov[2];
+ struct ether_header eh;
- memset(&sll, 0, sizeof(sll));
- sll.sll_family = AF_PACKET;
- sll.sll_protocol = htons(ETHERTYPE_ARP);
- if (!(sll.sll_ifindex = if_nametoindex(ifp->ifname))) {
- errno = ENOENT;
+ if (ifp->hwlen != hwlen || hwlen != sizeof(eh.ether_dhost)) {
+ errno = EINVAL;
return -1;
}
- sll.sll_hatype = htons(ifp->family);
- sll.sll_halen = hwlen;
- memcpy(sll.sll_addr, hwaddr, hwlen);
- return sendto(ifp->fd, data, len, 0,
- (struct sockaddr *)&sll, sizeof(sll));
+ memset(&eh, 0, sizeof(eh));
+ memcpy(&eh.ether_dhost, hwaddr, sizeof(eh.ether_dhost));
+ memcpy(&eh.ether_shost, ifp->hwaddr, sizeof(eh.ether_shost));
+ eh.ether_type = htons(ETHERTYPE_ARP);
+ iov[0].iov_base = &eh;
+ iov[0].iov_len = sizeof(eh);
+ iov[1].iov_base = UNCONST(data);
+ iov[1].iov_len = len;
+ return writev(ifp->fd, iov, 2);
}
ssize_t
@@ -115,8 +131,16 @@ get_raw_packet(struct interface *ifp, void *data, size_t len)
{
ssize_t bytes;
- bytes = read(ifp->fd, data, len);
+ bytes = read(ifp->fd, ifp->buffer, ifp->buffer_size);
if (bytes == -1)
return errno == EAGAIN ? 0 : -1;
+ if (bytes < ETHER_HDR_LEN) {
+ errno = EINVAL;
+ return -1;
+ }
+ bytes -= ETHER_HDR_LEN;
+ if ((size_t)bytes > len)
+ bytes = len;
+ memcpy(data, ifp->buffer + ETHER_HDR_LEN, bytes);
return bytes;
}
diff --git a/parpd.c b/parpd.c
index 9b51fb0..2f9af85 100644
--- a/parpd.c
+++ b/parpd.c
@@ -406,6 +406,9 @@ handle_arp(void *arg)
if ((size_t)bytes < sizeof(ar))
continue;
memcpy(&ar, arp_buffer, sizeof(ar));
+
+ /* Below checks are now enforced by BPF */
+#if 0
/* Protocol must be IP. */
if (ar.ar_pro != htons(ETHERTYPE_IP))
continue;
@@ -415,6 +418,7 @@ handle_arp(void *arg)
* Should we make REPLY knock out our config entries? */
if (ar.ar_op != htons(ARPOP_REQUEST))
continue;
+#endif
/* Get pointers to the hardware addreses */
shw = arp_buffer + sizeof(ar);
@@ -446,6 +450,8 @@ handle_arp(void *arg)
continue;
if (action == PARPD_HALFPROXY && sip == INADDR_ANY)
continue;
+ /* If no hardware address specified in config,
+ * use the interface hardware address */
if (hwlen == 0) {
phw = ifp->hwaddr;
hwlen = ifp->hwlen;