From: Roy Marples Date: Sun, 27 Jan 2008 11:31:01 +0000 (+0000) Subject: Linux has LPF, which is almost like BPF. Let's use and let the kernel filter out... X-Git-Tag: v3.2.3~61 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8404debd0687f23f35e44a11a66073a6ca362e63;p=thirdparty%2Fdhcpcd.git Linux has LPF, which is almost like BPF. Let's use and let the kernel filter out what we don't need. --- diff --git a/arp.c b/arp.c index 3b4ea750..e427fa99 100644 --- a/arp.c +++ b/arp.c @@ -133,7 +133,7 @@ int arp_claim (interface_t *iface, struct in_addr address) "checking %s is available on attached networks", inet_ntoa (address)); - if (! open_socket (iface, true)) + if (! open_socket (iface, ETHERTYPE_ARP)) return (-1); memset (&null_address, 0, sizeof (struct in_addr)); diff --git a/client.c b/client.c index d74d02b4..c87da559 100644 --- a/client.c +++ b/client.c @@ -374,7 +374,7 @@ static bool do_socket (state_t *state, int mode) state->interface->fd = -1; if (mode == SOCKET_OPEN) - if (open_socket (state->interface, false) == -1) + if (open_socket (state->interface, ETHERTYPE_IP) == -1) return (false); state->socket = mode; return (true); @@ -989,7 +989,6 @@ int dhcp_run (const options_t *options, int *pidfd) if (! iface) goto eexit; - state = xmalloc (sizeof (state_t)); memset (state, 0, sizeof (state_t)); diff --git a/dhcpcd.c b/dhcpcd.c index 22746282..cb150fba 100644 --- a/dhcpcd.c +++ b/dhcpcd.c @@ -54,6 +54,7 @@ const char copyright[] = "Copyright (c) 2006-2008 Roy Marples"; #include "dhcp.h" #include "interface.h" #include "logger.h" +#include "socket.h" #include "version.h" static int doversion = 0; @@ -625,6 +626,9 @@ int main(int argc, char **argv) /* Seed random */ srandomdev (); + /* Massage our filters per platform */ + setup_packet_filters (); + if (dhcp_run (options, &pidfd) == 0) retval = EXIT_SUCCESS; diff --git a/interface.c b/interface.c index 6ff5a7c4..8d222205 100644 --- a/interface.c +++ b/interface.c @@ -186,7 +186,7 @@ size_t hwaddr_aton (unsigned char *buffer, const char *addr) } static int _do_interface (const char *ifname, - unsigned char *hwaddr, size_t *hwlen, + _unused unsigned char *hwaddr, _unused size_t *hwlen, struct in_addr *addr, bool flush, bool get) { @@ -249,11 +249,7 @@ static int _do_interface (const char *ifname, if (strcmp (ifname, ifr->ifr_name) != 0) continue; -#ifdef __linux__ - /* Do something with the values at least */ - if (hwaddr && hwlen) - *hwlen = 0; -#else +#ifdef AF_LINK if (hwaddr && hwlen && ifr->ifr_addr.sa_family == AF_LINK) { struct sockaddr_dl sdl; @@ -303,7 +299,7 @@ static int _do_interface (const char *ifname, return retval; } -interface_t *read_interface (const char *ifname, int metric) +interface_t *read_interface (const char *ifname, _unused int metric) { int s; struct ifreq ifr; @@ -328,8 +324,6 @@ interface_t *read_interface (const char *ifname, int metric) } #ifdef __linux__ - /* Do something with the metric parameter to satisfy the compiler warning */ - metric = 0; strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); if (ioctl (s, SIOCGIFHWADDR, &ifr) == -1) { logger (LOG_ERR, "ioctl SIOCGIFHWADDR: %s", strerror (errno)); diff --git a/socket.c b/socket.c index ab396483..280a91c1 100644 --- a/socket.c +++ b/socket.c @@ -46,6 +46,15 @@ #include #include +#ifdef BSD +# include +#elif __linux__ +# include +# include +# define bpf_insn sock_filter +#endif + +#include "config.h" #include "dhcp.h" #include "interface.h" #include "logger.h" @@ -64,6 +73,66 @@ 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; + dhcp_bpf_filter[4].k -= ETH_HLEN; + dhcp_bpf_filter[6].k -= ETH_HLEN; + dhcp_bpf_filter[7].k -= ETH_HLEN; + + arp_bpf_filter[1].jf = 0; /* skip the IP packet type check */ + arp_bpf_filter[2].k -= ETH_HLEN; +#endif +} + static uint16_t checksum (unsigned char *addr, uint16_t len) { uint32_t sum = 0; @@ -191,55 +260,7 @@ eexit: } #ifdef BSD -/* Credit where credit is due :) - The below BPF filter is taken from ISC DHCP */ - -# include - -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, (u_int) - 1), - - /* 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, (u_int) - 1), - - /* Otherwise, drop it. */ - BPF_STMT (BPF_RET+BPF_K, 0), -}; - -int open_socket (interface_t *iface, bool arp) +int open_socket (interface_t *iface, int protocol) { int n = 0; int fd = -1; @@ -247,7 +268,7 @@ int open_socket (interface_t *iface, bool arp) int flags; struct ifreq ifr; int buf = 0; - struct bpf_program p; + struct bpf_program pf; device = xmalloc (sizeof (char) * PATH_MAX); do { @@ -295,14 +316,16 @@ int open_socket (interface_t *iface, bool arp) } /* Install the DHCP filter */ - if (arp) { - p.bf_insns = arp_bpf_filter; - p.bf_len = sizeof (arp_bpf_filter) / sizeof (struct bpf_insn); + if (protocol == ETHERTYPE_ARP) { + pf.bf_insns = arp_bpf_filter; + pf.bf_len = sizeof (arp_bpf_filter) + / sizeof (arp_bpf_insn[0]); } else { - p.bf_insns = dhcp_bpf_filter; - p.bf_len = sizeof (dhcp_bpf_filter) / sizeof (struct bpf_insn); + pf.bf_insns = dhcp_bpf_filter; + pf.bf_len = sizeof (dhcp_bpf_filter) + / sizeof (dhcp_bpf_insn[0]); } - if (ioctl (fd, BIOCSETF, &p) == -1) { + if (ioctl (fd, BIOCSETF, &pf) == -1) { logger (LOG_ERR, "ioctl BIOCSETF: %s", strerror (errno)); close (fd); return -1; @@ -433,9 +456,7 @@ ssize_t get_packet (const interface_t *iface, unsigned char *data, #elif __linux__ -#include - -int open_socket (interface_t *iface, bool arp) +int open_socket (interface_t *iface, int protocol) { int fd; int flags; @@ -444,10 +465,11 @@ int open_socket (interface_t *iface, bool arp) struct sockaddr_ll sll; struct sockaddr_storage ss; } su; + struct sock_fprog pf; - if ((fd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_IP))) == -1) { + if ((fd = socket (PF_PACKET, SOCK_DGRAM, htons (protocol))) == -1) { logger (LOG_ERR, "socket: %s", strerror (errno)); - return -1; + return (-1); } if ((flags = fcntl (fd, F_GETFD, 0)) == -1 @@ -455,39 +477,50 @@ int open_socket (interface_t *iface, bool arp) { logger (LOG_ERR, "fcntl: %s", strerror (errno)); close (fd); - return -1; + return (-1); } memset (&su, 0, sizeof (struct sockaddr_storage)); - su.sll.sll_family = AF_PACKET; - su.sll.sll_hatype = htons (iface->family); - if (arp) - su.sll.sll_protocol = htons (ETH_P_ARP); - else - su.sll.sll_protocol = htons (ETH_P_IP); + su.sll.sll_family = PF_PACKET; + su.sll.sll_protocol = htons (protocol); if (! (su.sll.sll_ifindex = if_nametoindex (iface->name))) { logger (LOG_ERR, "if_nametoindex: no index for interface `%s'", iface->name); close (fd); - return -1; + return (-1); + } + + /* Install the DHCP filter */ + if (protocol == ETHERTYPE_ARP) { + pf.filter = arp_bpf_filter; + pf.len = sizeof (arp_bpf_filter) / sizeof (arp_bpf_filter[0]); + } else { + pf.filter = dhcp_bpf_filter; + pf.len = sizeof (dhcp_bpf_filter) / sizeof (dhcp_bpf_filter[0]); + } + if (setsockopt (fd, SOL_SOCKET, SO_ATTACH_FILTER, + &pf, sizeof (struct sock_fprog)) != 0) + { + logger (LOG_ERR, "SO_ATTACH_FILTER: %s", strerror (errno)); + close (fd); + return (-1); } if (bind (fd, &su.sa, sizeof (struct sockaddr_storage)) == -1) { logger (LOG_ERR, "bind: %s", strerror (errno)); close (fd); - return -1; + return (-1); } if (iface->fd > -1) close (iface->fd); iface->fd = fd; - iface->socket_protocol = ntohs (su.sll.sll_protocol); - + iface->socket_protocol = protocol; iface->buffer_length = BUFFER_LENGTH; - return fd; + return (fd); } ssize_t send_packet (const interface_t *iface, int type, @@ -501,7 +534,7 @@ ssize_t send_packet (const interface_t *iface, int type, ssize_t retval; if (! iface) - return -1; + return (-1); memset (&su, 0, sizeof (struct sockaddr_storage)); su.sll.sll_family = AF_PACKET; @@ -510,7 +543,7 @@ ssize_t send_packet (const interface_t *iface, int type, if (! (su.sll.sll_ifindex = if_nametoindex (iface->name))) { logger (LOG_ERR, "if_nametoindex: no index for interface `%s'", iface->name); - return -1; + return (-1); } su.sll.sll_hatype = htons (iface->family); @@ -525,7 +558,7 @@ ssize_t send_packet (const interface_t *iface, int type, sizeof (struct sockaddr_storage))) == -1) logger (LOG_ERR, "sendto: %s", strerror (errno)); - return retval; + return (retval); } /* Linux has no need for the buffer as we can read as much as we want. @@ -553,48 +586,34 @@ ssize_t get_packet (const interface_t *iface, unsigned char *data, tv.tv_sec = 3; tv.tv_usec = 0; select (0, NULL, NULL, NULL, &tv); - return -1; + return (-1); } *buffer_len = bytes; /* If it's an ARP reply, then just send it back */ - if (iface->socket_protocol == ETH_P_ARP) { + if (iface->socket_protocol == ETHERTYPE_ARP) { memcpy (data, buffer, bytes); - return bytes; + return (bytes); } if ((unsigned) bytes < (sizeof (struct ip) + sizeof (struct udphdr))) { logger (LOG_DEBUG, "message too short, ignoring"); - return -1; + return (-1); } pay.buffer = buffer; if (bytes < ntohs (pay.packet->ip.ip_len)) { logger (LOG_DEBUG, "truncated packet, ignoring"); - return -1; - } - - bytes = ntohs (pay.packet->ip.ip_len); - - /* This is like our BPF filter above */ - if (pay.packet->ip.ip_p != IPPROTO_UDP || - pay.packet->ip.ip_v != IPVERSION || - pay.packet->ip.ip_hl != sizeof (pay.packet->ip) >> 2 || - pay.packet->udp.uh_dport != htons (DHCP_CLIENT_PORT) || - bytes > (int) sizeof (struct udp_dhcp_packet) || - ntohs (pay.packet->udp.uh_ulen) - != (uint16_t) (bytes - sizeof (pay.packet->ip))) - { - return -1; + return (-1); } if (valid_dhcp_packet (buffer) == -1) - return -1; - - memcpy(data, &pay.packet->dhcp, - bytes - (sizeof (pay.packet->ip) + sizeof (pay.packet->udp))); + return (-1); - return bytes - (sizeof (pay.packet->ip) + sizeof (pay.packet->udp)); + bytes = ntohs (pay.packet->ip.ip_len) - + (sizeof (pay.packet->ip) + sizeof (pay.packet->udp)); + memcpy (data, &pay.packet->dhcp, bytes); + return (bytes); } #else diff --git a/socket.h b/socket.h index 1ad890cd..bdf26d0a 100644 --- a/socket.h +++ b/socket.h @@ -33,11 +33,12 @@ #include "dhcp.h" #include "interface.h" +void setup_packet_filters (void); void make_dhcp_packet(struct udp_dhcp_packet *packet, const unsigned char *data, size_t length, struct in_addr source, struct in_addr dest); -int open_socket (interface_t *iface, bool arp); +int open_socket (interface_t *iface, int protocol); ssize_t send_packet (const interface_t *iface, int type, const unsigned char *data, size_t len); ssize_t get_packet (const interface_t *iface, unsigned char *data,