]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Assorted fixes for broken network devices: IP header length field is now
authorEvan Hunt <each@isc.org>
Fri, 27 Apr 2007 23:54:06 +0000 (23:54 +0000)
committerEvan Hunt <each@isc.org>
Fri, 27 Apr 2007 23:54:06 +0000 (23:54 +0000)
determined from payload, because some NIC drivers return more data than
they actually recived; IP and UDP packets now stored in aligned data
structures; outgoing packet TTL increased from 16 to 128. [rt15583]

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

index dd67d5b8e0b5e13701178ed758f22b531414078c..378d8226d97ddf3817f04f803c4210ad36bcb691 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -27,6 +27,12 @@ the README file.
 
                        Changes since 3.1.0b1
 
+- Assorted fixes for broken network devices:  IP header length field is now
+  determined from payload, because some NIC drivers return more data than
+  they actually recived; IP and UDP packets now stored in aligned data
+  structures; outgoing packet TTL increased from 16 to 128.  Thanks to Ted
+  Lemon for the patch.
+
 - A new server config option "fqdn-reply" specifies whether the server
   should send out option 81 (FQDN).  Defaults to "on".  If set to "off",
   the FQDN option is not sent, even if the client requested it.  This is
index 90083b3027751e1c5749605d08f4a59827c1fa3b..aa3fd9d2b33ba82f7d186e25689427aca0273c05 100644 (file)
@@ -34,7 +34,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: bpf.c,v 1.50 2005/03/17 20:14:56 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
+"$Id: bpf.c,v 1.51 2007/04/27 23:54:05 each Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -391,6 +391,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
        int length = 0;
        int offset = 0;
        struct bpf_hdr hdr;
+       unsigned paylen;
 
        /* All this complexity is because BPF doesn't guarantee
           that only one packet will be returned at a time.   We're
@@ -477,8 +478,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
                offset = decode_udp_ip_header (interface,
                                               interface -> rbuf,
                                               interface -> rbuf_offset,
-                                              from,
-                                              hdr.bh_caplen);
+                                              from, hdr.bh_caplen, &paylen);
 
                /* If the IP or UDP checksum was bad, skip the packet... */
                if (offset < 0) {
@@ -501,12 +501,11 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
                }
 
                /* Copy out the data in the packet... */
-               memcpy (buf, interface -> rbuf + interface -> rbuf_offset,
-                       hdr.bh_caplen);
+               memcpy(buf, interface->rbuf + interface->rbuf_offset, paylen);
                interface -> rbuf_offset =
                        BPF_WORDALIGN (interface -> rbuf_offset +
                                       hdr.bh_caplen);
-               return hdr.bh_caplen;
+               return paylen;
        } while (!length);
        return 0;
 }
index e43240c11ba00f18666480fe497fd08358349c1f..46d3da085f52ab5f51dce1e8e3ba683b4d4c0b8f 100644 (file)
@@ -79,7 +79,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: dlpi.c,v 1.29 2005/03/17 20:14:57 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
+"$Id: dlpi.c,v 1.30 2007/04/27 23:54:05 each Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -619,6 +619,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
        int offset = 0;
        int rslt;
        int bufix = 0;
+       int paylen;
        
 #ifdef USE_DLPI_RAW
        length = read (interface -> rfdesc, dbuf, sizeof (dbuf));
@@ -679,7 +680,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
        length -= offset;
 #endif
        offset = decode_udp_ip_header (interface, dbuf, bufix,
-                                      from, length);
+                                      from, length, &paylen);
 
        /* If the IP or UDP checksum was bad, skip the packet... */
        if (offset < 0) {
@@ -689,9 +690,12 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
        bufix += offset;
        length -= offset;
 
+       if (length < paylen)
+               log_fatal("Internal inconsistency at %s:%d.", MDL);
+
        /* Copy out the data in the packet... */
-       memcpy (buf, &dbuf [bufix], length);
-       return length;
+       memcpy(buf, &dbuf [bufix], paylen);
+       return paylen;
 }
 #endif
 
index 0d706da4bbd838d163d172ed87c3faa2ebb43950..12c46bc1dbbed180866c4f0f97ecab0a91c7dcec 100644 (file)
@@ -28,7 +28,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: lpf.c,v 1.30 2005/03/17 20:14:59 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
+"$Id: lpf.c,v 1.31 2007/04/27 23:54:05 each Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -339,6 +339,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
        int offset = 0;
        unsigned char ibuf [1536];
        unsigned bufix = 0;
+       unsigned paylen;
 
        length = read (interface -> rfdesc, ibuf, sizeof ibuf);
        if (length <= 0)
@@ -360,7 +361,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);
+                                      (unsigned)length, &paylen);
 
        /* If the IP or UDP checksum was bad, skip the packet... */
        if (offset < 0)
@@ -369,9 +370,12 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
        bufix += offset;
        length -= offset;
 
+       if (length < paylen)
+               log_fatal("Internal inconsistency at %s:%d.", MDL);
+
        /* Copy out the data in the packet... */
-       memcpy (buf, &ibuf [bufix], length);
-       return length;
+       memcpy(buf, &ibuf[bufix], paylen);
+       return paylen;
 }
 
 int can_unicast_without_arp (ip)
index 4fc1fb6dc1739eebd48041c037a0d854d75f625c..26549412d8a5a6a1c556833920f4675a32eca910 100644 (file)
@@ -35,7 +35,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: nit.c,v 1.35 2005/03/17 20:14:59 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
+"$Id: nit.c,v 1.36 2007/04/27 23:54:05 each Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -350,6 +350,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
        int offset = 0;
        unsigned char ibuf [1536];
        int bufix = 0;
+       unsigned paylen;
 
        length = read (interface -> rfdesc, ibuf, sizeof ibuf);
        if (length <= 0)
@@ -370,7 +371,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);
+                                      from, length, &paylen);
 
        /* If the IP or UDP checksum was bad, skip the packet... */
        if (offset < 0)
@@ -379,9 +380,12 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
        bufix += offset;
        length -= offset;
 
+       if (length < paylen)
+               log_fatal("Internal inconsistency at %s:%d.", MDL);
+
        /* Copy out the data in the packet... */
-       memcpy (buf, &ibuf [bufix], length);
-       return length;
+       memcpy(buf, &ibuf[bufix], paylen);
+       return paylen;
 }
 
 int can_unicast_without_arp (ip)
index a6e8e56736b012e35eb17f9fbaf6cf3cc6b1f2b1..351a453005e51d8355fc118e0fe8fa8253654a5e 100644 (file)
@@ -33,7 +33,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: packet.c,v 1.45 2006/08/09 14:57:47 dhankins Exp $ Copyright (c) 2004-2005 Internet Systems Consortium.  All rights reserved.\n";
+"$Id: packet.c,v 1.46 2007/04/27 23:54:05 each Exp $ Copyright (c) 2004-2005 Internet Systems Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -210,17 +210,17 @@ ssize_t decode_hw_header (interface, buf, bufix, from)
 
 /* UDP header and IP header decoded together for convenience. */
 
-ssize_t decode_udp_ip_header (interface, buf, bufix, from, buflen)
-       struct interface_info *interface;
-       unsigned char *buf;
-       unsigned bufix;
-       struct sockaddr_in *from;
-       unsigned buflen;
+ssize_t
+decode_udp_ip_header(struct interface_info *interface,
+                    unsigned char *buf, unsigned bufix,
+                    struct sockaddr_in *from, unsigned buflen,
+                    unsigned *rbuflen)
 {
   unsigned char *data;
   struct ip ip;
-  struct udphdr *udp;
-  u_int32_t ip_len = (buf [bufix] & 0xf) << 2;
+  struct udphdr udp;
+  unsigned char *upp, *endbuf;
+  u_int32_t ip_len, ulen, pkt_len;
   u_int32_t sum, usum;
   static int ip_packets_seen;
   static int ip_packets_bad_checksum;
@@ -229,11 +229,34 @@ ssize_t decode_udp_ip_header (interface, buf, bufix, from, buflen)
   static int udp_packets_length_checked;
   static int udp_packets_length_overflow;
   unsigned len;
-  unsigned ulen;
-  int ignore = 0;
 
-  memcpy(&ip, buf + bufix, sizeof (struct ip));
-  udp = (struct udphdr *)(buf + bufix + ip_len);
+  /* Designate the end of the input buffer for bounds checks. */
+  endbuf = buf + bufix + buflen;
+
+  /* Assure there is at least an IP header there. */
+  if ((buf + bufix + sizeof(ip)) > endbuf)
+         return -1;
+
+  /* Copy the IP header into a stack aligned structure for inspection.
+   * There may be bits in the IP header that we're not decoding, so we
+   * copy out the bits we grok and skip ahead by ip.ip_hl * 4.
+   */
+  upp = buf + bufix;
+  memcpy(&ip, upp, sizeof(ip));
+  ip_len = (*upp & 0x0f) << 2;
+  upp += ip_len;
+
+  /* Check the IP packet length. */
+  pkt_len = ntohs(ip.ip_len);
+  if (pkt_len > buflen)
+       return -1;
+
+  /* Assure after ip_len bytes that there is enough room for a UDP header. */
+  if ((upp + sizeof(udp)) > endbuf)
+         return -1;
+
+  /* Copy the UDP header into a stack alined structure for inspection. */
+  memcpy(&udp, upp, sizeof(udp));
 
 #ifdef USERLAND_FILTER
   /* Is it a UDP packet? */
@@ -241,17 +264,32 @@ ssize_t decode_udp_ip_header (interface, buf, bufix, from, buflen)
          return -1;
 
   /* Is it to the port we're serving? */
-  if (udp -> uh_dport != local_port)
+  if (udp.uh_dport != local_port)
          return -1;
 #endif /* USERLAND_FILTER */
 
-  ulen = ntohs (udp -> uh_ulen);
-  if (ulen < sizeof *udp ||
-      ((unsigned char *)udp) + ulen > buf + bufix + buflen) {
-         log_info ("bogus UDP packet length: %d", ulen);
-         return -1;
+  ulen = ntohs(udp.uh_ulen);
+  if (ulen < sizeof(udp))
+       return -1;
+
+  udp_packets_length_checked++;
+  if ((upp + ulen) > endbuf) {
+       udp_packets_length_overflow++;
+       if ((udp_packets_length_checked > 4) &&
+           ((udp_packets_length_checked /
+             udp_packets_length_overflow) < 2)) {
+               log_info("%d udp packets in %d too long - dropped",
+                        udp_packets_length_overflow,
+                        udp_packets_length_checked);
+               udp_packets_length_overflow = 0;
+               udp_packets_length_checked = 0;
+       }
+       return -1;
   }
 
+  if ((ulen < sizeof(udp)) || ((upp + ulen) > endbuf))
+       return -1;
+
   /* Check the IP header checksum - it should be zero. */
   ++ip_packets_seen;
   if (wrapsum (checksum (buf + bufix, ip_len, 0))) {
@@ -265,57 +303,26 @@ ssize_t decode_udp_ip_header (interface, buf, bufix, from, buflen)
          return -1;
   }
 
-  /* Check the IP packet length. */
-  if (ntohs (ip.ip_len) != buflen) {
-         if ((ntohs (ip.ip_len + 2) & ~1) == buflen)
-                 ignore = 1;
-         else
-                 log_debug ("ip length %d disagrees with bytes received %d.",
-                            ntohs (ip.ip_len), buflen);
-  }
-
   /* Copy out the IP source address... */
-  memcpy (&from -> sin_addr, &ip.ip_src, 4);
+  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 = buf + bufix + ip_len + sizeof *udp;
-  len = ulen - sizeof *udp;
-  ++udp_packets_length_checked;
-  if (len + data > buf + bufix + buflen) {
-         ++udp_packets_length_overflow;
-         if (udp_packets_length_checked > 4 &&
-             (udp_packets_length_checked /
-              udp_packets_length_overflow) < 2) {
-                 log_info ("%d udp packets in %d too long - dropped",
-                           udp_packets_length_overflow,
-                           udp_packets_length_checked);
-                 udp_packets_length_overflow =
-                         udp_packets_length_checked = 0;
-         }
-         return -1;
-  }
-  if (len + data < buf + bufix + buflen &&
-      len + data != buf + bufix + buflen && !ignore)
-         log_debug ("accepting packet with data after udp payload.");
-  if (len + data > buf + bufix + buflen) {
-         log_debug ("dropping packet with bogus uh_ulen %ld",
-                    (long)(len + sizeof *udp));
-         return -1;
-  }
+  data = upp + sizeof(udp);
+  len = ulen - sizeof(udp);
 
-  usum = udp -> uh_sum;
-  udp -> uh_sum = 0;
+  usum = udp.uh_sum;
+  udp.uh_sum = 0;
 
-  sum = wrapsum (checksum ((unsigned char *)udp, sizeof *udp,
-                          checksum (data, len,
-                                    checksum ((unsigned char *)
-                                              &ip.ip_src,
-                                              2 * sizeof ip.ip_src,
-                                              IPPROTO_UDP +
-                                              (u_int32_t)ulen))));
+  /* 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_packets_seen++;
   if (usum && usum != sum) {
@@ -330,8 +337,13 @@ ssize_t decode_udp_ip_header (interface, buf, bufix, from, buflen)
   }
 
   /* Copy out the port... */
-  memcpy (&from -> sin_port, &udp -> uh_sport, sizeof udp -> uh_sport);
+  memcpy (&from -> sin_port, &udp.uh_sport, sizeof udp.uh_sport);
+
+  /* Save the length of the UDP payload. */
+  if (rbuflen != NULL)
+       *rbuflen = len;
 
-  return ip_len + sizeof *udp;
+  /* Return the index to the UDP payload. */
+  return ip_len + sizeof udp;
 }
 #endif /* PACKET_DECODING */
index b246b11399da430e5867956fffe046b28d92bec1..568d632d93246fde637975aa103d64379cc32592 100644 (file)
@@ -28,7 +28,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: tr.c,v 1.9 2006/02/24 23:16:29 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium.  All rights reserved.\n";
+"$Id: tr.c,v 1.10 2007/04/27 23:54:05 each Exp $ Copyright (c) 2004-2006 Internet Systems Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -59,9 +59,9 @@ struct routing_entry {
         struct routing_entry *next;
         unsigned char addr[TR_ALEN];
         unsigned char iface[5];
-        u_int16_t rcf;                      /* route control field */
-        u_int16_t rseg[8];                  /* routing registers */
-        unsigned long access_time;      /* time we last used this entry */
+        u_int16_t rcf;                 /* route control field */
+        u_int16_t rseg[8];             /* routing registers */
+        unsigned long access_time;     /* time we last used this entry */
 };
 
 static struct routing_entry *routing_info = NULL;
index 3cae8db29eade71f6053b92cd33c286e486a14ee..24d380b3f7a6b4296725832e65aa593ce076eb9c 100644 (file)
@@ -34,7 +34,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: upf.c,v 1.22 2005/03/17 20:15:01 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
+"$Id: upf.c,v 1.23 2007/04/27 23:54:05 each Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -300,6 +300,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
        int offset = 0;
        unsigned char ibuf [1500 + sizeof (struct enstamp)];
        int bufix = 0;
+       unsigned paylen;
 
        length = read (interface -> rfdesc, ibuf, sizeof ibuf);
        if (length <= 0)
@@ -321,7 +322,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);
+                                      from, length, &paylen);
 
        /* If the IP or UDP checksum was bad, skip the packet... */
        if (offset < 0)
@@ -330,9 +331,12 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
        bufix += offset;
        length -= offset;
 
+       if (length < paylen)
+               log_fatal("Internal inconsistency at %s:%d.", MDL);
+
        /* Copy out the data in the packet... */
-       memcpy (buf, &ibuf [bufix], length);
-       return length;
+       memcpy (buf, &ibuf[bufix], paylen);
+       return paylen;
 }
 
 int can_unicast_without_arp (ip)
index c52a912549d42b7a34c21527d061e617d02bb793..b621eb953b1c68265ddaf73ef2352afe764203b7 100644 (file)
@@ -2109,7 +2109,7 @@ ssize_t decode_hw_header PROTO ((struct interface_info *, unsigned char *,
                                 unsigned, struct hardware *));
 ssize_t decode_udp_ip_header PROTO ((struct interface_info *, unsigned char *,
                                     unsigned, struct sockaddr_in *,
-                                    unsigned));
+                                    unsigned, unsigned *));
 
 /* ethernet.c */
 void assemble_ethernet_header PROTO ((struct interface_info *, unsigned char *,