summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvan Hunt <each@isc.org>2007-04-27 23:54:06 +0000
committerEvan Hunt <each@isc.org>2007-04-27 23:54:06 +0000
commit83c0372e288d764944bf6bb4b78fb4e95159b52e (patch)
treeb576cdcd4821049217ae044f7d0e5258fbc45aab
parent46153375b006bf72b3bb7c7b097ff32f0ccd1427 (diff)
downloadisc-dhcp-83c0372e288d764944bf6bb4b78fb4e95159b52e.tar.gz
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. [rt15583]
-rw-r--r--RELNOTES6
-rw-r--r--common/bpf.c11
-rw-r--r--common/dlpi.c12
-rw-r--r--common/lpf.c12
-rw-r--r--common/nit.c12
-rw-r--r--common/packet.c140
-rw-r--r--common/tr.c8
-rw-r--r--common/upf.c12
-rw-r--r--includes/dhcpd.h2
9 files changed, 124 insertions, 91 deletions
diff --git a/RELNOTES b/RELNOTES
index dd67d5b8..378d8226 100644
--- 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
diff --git a/common/bpf.c b/common/bpf.c
index 90083b30..aa3fd9d2 100644
--- a/common/bpf.c
+++ b/common/bpf.c
@@ -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;
}
diff --git a/common/dlpi.c b/common/dlpi.c
index e43240c1..46d3da08 100644
--- a/common/dlpi.c
+++ b/common/dlpi.c
@@ -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
diff --git a/common/lpf.c b/common/lpf.c
index 0d706da4..12c46bc1 100644
--- a/common/lpf.c
+++ b/common/lpf.c
@@ -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)
diff --git a/common/nit.c b/common/nit.c
index 4fc1fb6d..26549412 100644
--- a/common/nit.c
+++ b/common/nit.c
@@ -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)
diff --git a/common/packet.c b/common/packet.c
index a6e8e567..351a4530 100644
--- a/common/packet.c
+++ b/common/packet.c
@@ -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 */
diff --git a/common/tr.c b/common/tr.c
index b246b113..568d632d 100644
--- a/common/tr.c
+++ b/common/tr.c
@@ -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;
diff --git a/common/upf.c b/common/upf.c
index 3cae8db2..24d380b3 100644
--- a/common/upf.c
+++ b/common/upf.c
@@ -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)
diff --git a/includes/dhcpd.h b/includes/dhcpd.h
index c52a9125..b621eb95 100644
--- a/includes/dhcpd.h
+++ b/includes/dhcpd.h
@@ -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 *,