]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Linux has LPF, which is almost like BPF. Let's use and let the kernel filter out...
authorRoy Marples <roy@marples.name>
Sun, 27 Jan 2008 11:31:01 +0000 (11:31 +0000)
committerRoy Marples <roy@marples.name>
Sun, 27 Jan 2008 11:31:01 +0000 (11:31 +0000)
arp.c
client.c
dhcpcd.c
interface.c
socket.c
socket.h

diff --git a/arp.c b/arp.c
index 3b4ea75046b09020a7a7ba07e043899cfabb2374..e427fa992b3a7851ac3c164d1c1005c11dc94803 100644 (file)
--- 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));
index d74d02b47a24bf55321035ba82de15014b5ad881..c87da559a2f64a34d5fa4444a3bd7c964279a75c 100644 (file)
--- 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));
        
index 227462822901b92131c6ae5fa3bc8108b44b1853..cb150fba4b18304a5f8e9d56b0ca321e6dc118d9 100644 (file)
--- 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;
 
index 6ff5a7c47238b14902e064fcf81d2f800daec960..8d222205c6302b4c4b5ca6fe3950078237632101 100644 (file)
@@ -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));
index ab3964836a1db882ff17d58d47065a15b2aa29a4..280a91c186225306ed6f60594447c718745ff3f0 100644 (file)
--- a/socket.c
+++ b/socket.c
 #include <string.h>
 #include <unistd.h>
 
+#ifdef BSD
+# include <net/bpf.h>
+#elif __linux__
+# include <linux/filter.h>
+# include <netpacket/packet.h>
+# 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 <net/bpf.h>
-
-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 <netpacket/packet.h>
-
-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
index 1ad890cdd1362bb127303e01ab7af6ec5377914b..bdf26d0acf2a1b425874b262dcebf4186ca7aa10 100644 (file)
--- a/socket.h
+++ b/socket.h
 #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,