]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
[master] Checkum handling fixes
authorThomas Markwalder <tmark@isc.org>
Thu, 18 Sep 2014 19:25:02 +0000 (15:25 -0400)
committerThomas Markwalder <tmark@isc.org>
Thu, 18 Sep 2014 19:25:02 +0000 (15:25 -0400)
    Merges in rt22806

RELNOTES
common/bpf.c
common/dlpi.c
common/lpf.c
common/nit.c
common/packet.c
common/upf.c
includes/dhcpd.h

index 8cb0f494fe323043621eba91502408c5d1bf9e09..a6ca36e05613c8fbd1a0493e551ccbd3a9d24377 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -95,6 +95,25 @@ by Eric Young (eay@cryptsoft.com).
   are within the declared. Thanks to Jiri Popelka at Red Hat for the bug
   report and patch.
   [ISC-Bugs #32453]
+  [ISC-Bugs #17766]
+  [ISC-Bugs #18510]
+  [ISC-Bugs #23698]
+  [ISC-Bugs #28883]
+
+- Addressed checksum issues:
+  Added checksum readiness check to Linux packet filtering which eliminates
+  invalid packet drops due to checksum errors when checksum offloading is
+  in use.  Based on dhcp-4.2.2-xen-checksum.patch made to the Fedora project.
+  [ISC-Bugs #22806]
+  [ISC-Bugs #15902]
+  [ISC-Bugs #17739]
+  [ISC-Bugs #18010]
+  [ISC-Bugs #22556]
+  [ISC-Bugs #29769]
+  Inbound packets with UPD checksums of 0xffff now validate correctly rather
+  than being dropped.
+  [ISC-Bus #24216]
+  [ISC-Bus #25587]
 
                        Changes since 4.3.1b1
 
index a4269181298c25e6432d229d52b5c78962b4c874..39d4f45d75264dd277171c6827a90c8bf498db7d 100644 (file)
@@ -480,8 +480,8 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
 
                /* Decode the IP and UDP headers... */
                offset = decode_udp_ip_header(interface, interface->rbuf,
-                                              interface->rbuf_offset,
-                                              from, hdr.bh_caplen, &paylen);
+                                             interface->rbuf_offset,
+                                              from, hdr.bh_caplen, &paylen, 1);
 
                /* If the IP or UDP checksum was bad, skip the packet... */
                if (offset < 0) {
index 391fb91f3e9bac160ee59a798b4fc0781f094cd8..c34adc392ab1ea65b7105461484737497a2f0a05 100644 (file)
@@ -3,7 +3,7 @@
    Data Link Provider Interface (DLPI) network interface code. */
 
 /*
- * Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2009-2011,2014 by Internet Systems Consortium, Inc. ("ISC")
  * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
  * Copyright (c) 1996-2003 by Internet Software Consortium
  *
@@ -691,7 +691,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
        length -= offset;
 #endif
        offset = decode_udp_ip_header (interface, dbuf, bufix,
-                                      from, length, &paylen);
+                                      from, length, &paylen, 1);
 
        /*
         * If the IP or UDP checksum was bad, skip the packet...
index a63d61ba8e86b13a86f93469213e3fe0b14f4e96..99937e6cb794e3ea5776a829c02329f7fc87d098 100644 (file)
@@ -4,7 +4,7 @@
    Support Services in Vancouver, B.C. */
 
 /*
- * Copyright (c) 2009,2012 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2009,2012,2014 by Internet Systems Consortium, Inc. ("ISC")
  * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
  * Copyright (c) 1996-2003 by Internet Software Consortium
  *
@@ -35,8 +35,8 @@
 #include <asm/types.h>
 #include <linux/filter.h>
 #include <linux/if_ether.h>
+#include <linux/if_packet.h>
 #include <netinet/in_systm.h>
-#include <net/if_packet.h>
 #include "includes/netinet/ip.h"
 #include "includes/netinet/udp.h"
 #include "includes/netinet/if_ether.h"
@@ -44,6 +44,7 @@
 
 #if defined (USE_LPF_RECEIVE) || defined (USE_LPF_HWADDR)
 #include <sys/ioctl.h>
+#include <sys/socket.h>
 #include <net/if.h>
 #endif
 
@@ -73,10 +74,14 @@ int if_register_lpf (info)
        struct interface_info *info;
 {
        int sock;
-       struct sockaddr sa;
+       union {
+               struct sockaddr_ll ll;
+               struct sockaddr common;
+               } sa;
+       struct ifreq ifr;
 
        /* Make an LPF socket. */
-       if ((sock = socket(PF_PACKET, SOCK_PACKET,
+       if ((sock = socket(PF_PACKET, SOCK_RAW,
                           htons((short)ETH_P_ALL))) < 0) {
                if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
                    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
@@ -91,12 +96,17 @@ int if_register_lpf (info)
                log_fatal ("Open a socket for LPF: %m");
        }
 
+       memset (&ifr, 0, sizeof ifr);
+       strncpy (ifr.ifr_name, (const char *)info -> ifp, sizeof ifr.ifr_name);
+       ifr.ifr_name[IFNAMSIZ-1] = '\0';
+       if (ioctl (sock, SIOCGIFINDEX, &ifr))
+               log_fatal ("Failed to get interface index: %m");
+
        /* Bind to the interface name */
        memset (&sa, 0, sizeof sa);
-       sa.sa_family = AF_PACKET;
-       strncpy (sa.sa_data, (const char *)info -> ifp, sizeof sa.sa_data);
-       sa.sa_data[sizeof(sa.sa_data)-1] = '\0';
-       if (bind (sock, &sa, sizeof sa)) {
+       sa.ll.sll_family = AF_PACKET;
+       sa.ll.sll_ifindex = ifr.ifr_ifindex;
+       if (bind (sock, &sa.common, sizeof sa)) {
                if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
                    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
                    errno == EAFNOSUPPORT || errno == EINVAL) {
@@ -182,6 +192,20 @@ void if_register_receive (info)
        /* Open a LPF device and hang it on this interface... */
        info -> rfdesc = if_register_lpf (info);
 
+#ifdef PACKET_AUXDATA
+       {
+       int val = 1;
+
+       if (setsockopt(info->rfdesc, SOL_PACKET, PACKET_AUXDATA,
+                      &val, sizeof(val)) < 0) {
+               if (errno != ENOPROTOOPT) {
+                       log_fatal ("Failed to set auxiliary packet data: %m");
+               }
+       }
+       }
+#endif
+
+
 #if defined (HAVE_TR_SUPPORT)
        if (info -> hw_address.hbuf [0] == HTYPE_IEEE802)
                lpf_tr_filter_setup (info);
@@ -303,7 +327,6 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto)
        double hh [16];
        double ih [1536 / sizeof (double)];
        unsigned char *buf = (unsigned char *)ih;
-       struct sockaddr_pkt sa;
        int result;
        int fudge;
 
@@ -323,19 +346,7 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto)
                                to -> sin_addr.s_addr, to -> sin_port,
                                (unsigned char *)raw, len);
        memcpy (buf + ibufp, raw, len);
-
-       /* For some reason, SOCK_PACKET sockets can't be connected,
-          so we have to do a sentdo every time. */
-       memset (&sa, 0, sizeof sa);
-       sa.spkt_family = AF_PACKET;
-       strncpy ((char *)sa.spkt_device,
-                (const char *)interface -> ifp, sizeof sa.spkt_device);
-       sa.spkt_device[sizeof(sa.spkt_device) - 1] = '\0';
-       sa.spkt_protocol = htons(ETH_P_IP);
-
-       result = sendto (interface -> wfdesc,
-                        buf + fudge, ibufp + len - fudge, 0, 
-                        (const struct sockaddr *)&sa, sizeof sa);
+       result = write(interface->wfdesc, buf + fudge, ibufp + len - fudge);
        if (result < 0)
                log_error ("send_packet: %m");
        return result;
@@ -352,14 +363,44 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
 {
        int length = 0;
        int offset = 0;
+       int csum_ready = 1;
        unsigned char ibuf [1536];
        unsigned bufix = 0;
        unsigned paylen;
-
-       length = read (interface -> rfdesc, ibuf, sizeof ibuf);
+       unsigned char cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
+       struct iovec iov = {
+               .iov_base = ibuf,
+               .iov_len = sizeof ibuf,
+       };
+       struct msghdr msg = {
+               .msg_iov = &iov,
+               .msg_iovlen = 1,
+               .msg_control = cmsgbuf,
+               .msg_controllen = sizeof(cmsgbuf),
+       };
+
+       length = recvmsg (interface->rfdesc, &msg, 0);
        if (length <= 0)
                return length;
 
+#ifdef PACKET_AUXDATA
+       {
+       /*  Determine if checksum is valid for use. It may not be if checksum
+           offloading is enabled on the interface.  */
+       struct cmsghdr *cmsg;
+
+       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+               if (cmsg->cmsg_level == SOL_PACKET &&
+                   cmsg->cmsg_type == PACKET_AUXDATA) {
+                       struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg);
+                       csum_ready = ((aux->tp_status & TP_STATUS_CSUMNOTREADY)
+                                     ? 0 : 1);
+               }
+       }
+
+       }
+#endif
+
        bufix = 0;
        /* Decode the physical header... */
        offset = decode_hw_header (interface, ibuf, bufix, hfrom);
@@ -376,7 +417,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
 
        /* Decode the IP and UDP headers... */
        offset = decode_udp_ip_header (interface, ibuf, bufix, from,
-                                      (unsigned)length, &paylen);
+                                      (unsigned)length, &paylen, csum_ready);
 
        /* If the IP or UDP checksum was bad, skip the packet... */
        if (offset < 0)
index a43bcf3e5e7f9dd102184f35fb1a38ada5cd0a78..316e85f96e85a3fd7282eda23f765f8051899159 100644 (file)
@@ -363,7 +363,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
 
        /* Decode the IP and UDP headers... */
        offset = decode_udp_ip_header (interface, ibuf, bufix,
-                                      from, length, &paylen);
+                                      from, length, &paylen, 1);
 
        /* If the IP or UDP checksum was bad, skip the packet... */
        if (offset < 0)
index 7460f3dbb608f95c0c809a1e95c7c4205a27eda6..b5304320f0390de5476aad1cd6daa986ff9826cb 100644 (file)
@@ -226,14 +226,13 @@ ssize_t
 decode_udp_ip_header(struct interface_info *interface,
                     unsigned char *buf, unsigned bufix,
                     struct sockaddr_in *from, unsigned buflen,
-                    unsigned *rbuflen)
+                    unsigned *rbuflen, int csum_ready)
 {
   unsigned char *data;
   struct ip ip;
   struct udphdr udp;
   unsigned char *upp, *endbuf;
   u_int32_t ip_len, ulen, pkt_len;
-  u_int32_t sum, usum;
   static unsigned int ip_packets_seen = 0;
   static unsigned int ip_packets_bad_checksum = 0;
   static unsigned int udp_packets_seen = 0;
@@ -327,34 +326,30 @@ decode_udp_ip_header(struct interface_info *interface,
   /* Copy out the IP source address... */
   memcpy(&from->sin_addr, &ip.ip_src, 4);
 
-  /* Compute UDP checksums, including the ``pseudo-header'', the UDP
-     header and the data.   If the UDP checksum field is zero, we're
-     not supposed to do a checksum. */
-
   data = upp + sizeof(udp);
   len = ulen - sizeof(udp);
 
-  usum = udp.uh_sum;
-  udp.uh_sum = 0;
-
-  /* XXX: We have to pass &udp, because we have to zero the checksum
-   * field before calculating the sum...'upp' isn't zeroed.
-   */
-  sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
-                        checksum(data, len,
-                                 checksum((unsigned char *)&ip.ip_src,
-                                          8, IPPROTO_UDP + ulen))));
-
+  /* UDP check sum may be optional (udp.uh_sum == 0) or not ready if checksum
+   * offloading is in use */
   udp_packets_seen++;
-  if (usum && usum != sum) {
-         udp_packets_bad_checksum++;
-         if (((udp_packets_seen > 4) && (udp_packets_bad_checksum != 0)) &&
-             ((udp_packets_seen / udp_packets_bad_checksum) < 2)) {
-                 log_info ("%u bad udp checksums in %u packets",
-                           udp_packets_bad_checksum, udp_packets_seen);
-                 udp_packets_seen = udp_packets_bad_checksum = 0;
-         }
-         return -1;
+  if (udp.uh_sum && csum_ready) {
+       /* Check the UDP header checksum - since the received packet header
+        * contains the UDP checksum calculated by the transmitter, calculating
+        * it now should come out to zero. */
+       if (wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
+                              checksum(data, len,
+                                       checksum((unsigned char *)&ip.ip_src,
+                                                8, IPPROTO_UDP + ulen))))) {
+               udp_packets_bad_checksum++;
+               if (((udp_packets_seen > 4) && (udp_packets_bad_checksum != 0))
+                   && ((udp_packets_seen / udp_packets_bad_checksum) < 2)) {
+                       log_info ("%u bad udp checksums in %u packets",
+                                 udp_packets_bad_checksum, udp_packets_seen);
+                       udp_packets_seen = udp_packets_bad_checksum = 0;
+               }
+
+               return -1;
+       }
   }
 
   /* If at least 5 with less than 50% bad, start over */
index de2129e73297fa6dfb16bd35dbdee4d738eff15d..34011ebe2888a21df078ecba23d40bddad374061 100644 (file)
@@ -314,7 +314,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
 
        /* Decode the IP and UDP headers... */
        offset = decode_udp_ip_header (interface, ibuf, bufix,
-                                      from, length, &paylen);
+                                      from, length, &paylen, 1);
 
        /* If the IP or UDP checksum was bad, skip the packet... */
        if (offset < 0)
index 6692026c889f6b2f5633e7ff1122d872829f2f43..faec6fdce92e2f56ea40692a42fcb65fa5847201 100644 (file)
@@ -2878,7 +2878,7 @@ ssize_t decode_hw_header (struct interface_info *, unsigned char *,
                          unsigned, struct hardware *);
 ssize_t decode_udp_ip_header (struct interface_info *, unsigned char *,
                              unsigned, struct sockaddr_in *,
-                             unsigned, unsigned *);
+                             unsigned, unsigned *, int);
 
 /* ethernet.c */
 void assemble_ethernet_header (struct interface_info *, unsigned char *,