diff options
| author | Roy Marples <roy@marples.name> | 2017-03-11 12:17:45 +0000 |
|---|---|---|
| committer | Roy Marples <roy@marples.name> | 2017-03-11 12:17:45 +0000 |
| commit | de29cb22d9f734bf26791e0edd68abaf6bd20e3a (patch) | |
| tree | 4b578cd0da9bee5e829bd37afeedf08cf4946aa0 | |
| parent | 175dd2089af6aad3770dad7945eadd3d68800edd (diff) | |
| download | parpd-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.h | 57 | ||||
| -rw-r--r-- | bpf.c | 23 | ||||
| -rw-r--r-- | lpf.c | 80 | ||||
| -rw-r--r-- | parpd.c | 6 |
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]) @@ -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); @@ -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; } @@ -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; |
