2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-31 14:25:41 +00:00

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]
This commit is contained in:
Evan Hunt
2007-04-27 23:54:06 +00:00
parent 46153375b0
commit 83c0372e28
9 changed files with 124 additions and 91 deletions

View File

@@ -27,6 +27,12 @@ the README file.
Changes since 3.1.0b1 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 - A new server config option "fqdn-reply" specifies whether the server
should send out option 81 (FQDN). Defaults to "on". If set to "off", 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 the FQDN option is not sent, even if the client requested it. This is

View File

@@ -34,7 +34,7 @@
#ifndef lint #ifndef lint
static char copyright[] = 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 */ #endif /* not lint */
#include "dhcpd.h" #include "dhcpd.h"
@@ -391,6 +391,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
int length = 0; int length = 0;
int offset = 0; int offset = 0;
struct bpf_hdr hdr; struct bpf_hdr hdr;
unsigned paylen;
/* All this complexity is because BPF doesn't guarantee /* All this complexity is because BPF doesn't guarantee
that only one packet will be returned at a time. We're 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, offset = decode_udp_ip_header (interface,
interface -> rbuf, interface -> rbuf,
interface -> rbuf_offset, interface -> rbuf_offset,
from, from, hdr.bh_caplen, &paylen);
hdr.bh_caplen);
/* If the IP or UDP checksum was bad, skip the packet... */ /* If the IP or UDP checksum was bad, skip the packet... */
if (offset < 0) { if (offset < 0) {
@@ -501,12 +501,11 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
} }
/* Copy out the data in the packet... */ /* Copy out the data in the packet... */
memcpy (buf, interface -> rbuf + interface -> rbuf_offset, memcpy(buf, interface->rbuf + interface->rbuf_offset, paylen);
hdr.bh_caplen);
interface -> rbuf_offset = interface -> rbuf_offset =
BPF_WORDALIGN (interface -> rbuf_offset + BPF_WORDALIGN (interface -> rbuf_offset +
hdr.bh_caplen); hdr.bh_caplen);
return hdr.bh_caplen; return paylen;
} while (!length); } while (!length);
return 0; return 0;
} }

View File

@@ -79,7 +79,7 @@
#ifndef lint #ifndef lint
static char copyright[] = 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 */ #endif /* not lint */
#include "dhcpd.h" #include "dhcpd.h"
@@ -619,6 +619,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
int offset = 0; int offset = 0;
int rslt; int rslt;
int bufix = 0; int bufix = 0;
int paylen;
#ifdef USE_DLPI_RAW #ifdef USE_DLPI_RAW
length = read (interface -> rfdesc, dbuf, sizeof (dbuf)); length = read (interface -> rfdesc, dbuf, sizeof (dbuf));
@@ -679,7 +680,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
length -= offset; length -= offset;
#endif #endif
offset = decode_udp_ip_header (interface, dbuf, bufix, 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 the IP or UDP checksum was bad, skip the packet... */
if (offset < 0) { if (offset < 0) {
@@ -689,9 +690,12 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
bufix += offset; bufix += offset;
length -= offset; length -= offset;
if (length < paylen)
log_fatal("Internal inconsistency at %s:%d.", MDL);
/* Copy out the data in the packet... */ /* Copy out the data in the packet... */
memcpy (buf, &dbuf [bufix], length); memcpy(buf, &dbuf [bufix], paylen);
return length; return paylen;
} }
#endif #endif

View File

@@ -28,7 +28,7 @@
#ifndef lint #ifndef lint
static char copyright[] = 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 */ #endif /* not lint */
#include "dhcpd.h" #include "dhcpd.h"
@@ -339,6 +339,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
int offset = 0; int offset = 0;
unsigned char ibuf [1536]; unsigned char ibuf [1536];
unsigned bufix = 0; unsigned bufix = 0;
unsigned paylen;
length = read (interface -> rfdesc, ibuf, sizeof ibuf); length = read (interface -> rfdesc, ibuf, sizeof ibuf);
if (length <= 0) if (length <= 0)
@@ -360,7 +361,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
/* Decode the IP and UDP headers... */ /* Decode the IP and UDP headers... */
offset = decode_udp_ip_header (interface, ibuf, bufix, from, 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 the IP or UDP checksum was bad, skip the packet... */
if (offset < 0) if (offset < 0)
@@ -369,9 +370,12 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
bufix += offset; bufix += offset;
length -= offset; length -= offset;
if (length < paylen)
log_fatal("Internal inconsistency at %s:%d.", MDL);
/* Copy out the data in the packet... */ /* Copy out the data in the packet... */
memcpy (buf, &ibuf [bufix], length); memcpy(buf, &ibuf[bufix], paylen);
return length; return paylen;
} }
int can_unicast_without_arp (ip) int can_unicast_without_arp (ip)

View File

@@ -35,7 +35,7 @@
#ifndef lint #ifndef lint
static char copyright[] = 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 */ #endif /* not lint */
#include "dhcpd.h" #include "dhcpd.h"
@@ -350,6 +350,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
int offset = 0; int offset = 0;
unsigned char ibuf [1536]; unsigned char ibuf [1536];
int bufix = 0; int bufix = 0;
unsigned paylen;
length = read (interface -> rfdesc, ibuf, sizeof ibuf); length = read (interface -> rfdesc, ibuf, sizeof ibuf);
if (length <= 0) if (length <= 0)
@@ -370,7 +371,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
/* Decode the IP and UDP headers... */ /* Decode the IP and UDP headers... */
offset = decode_udp_ip_header (interface, ibuf, bufix, 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 the IP or UDP checksum was bad, skip the packet... */
if (offset < 0) if (offset < 0)
@@ -379,9 +380,12 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
bufix += offset; bufix += offset;
length -= offset; length -= offset;
if (length < paylen)
log_fatal("Internal inconsistency at %s:%d.", MDL);
/* Copy out the data in the packet... */ /* Copy out the data in the packet... */
memcpy (buf, &ibuf [bufix], length); memcpy(buf, &ibuf[bufix], paylen);
return length; return paylen;
} }
int can_unicast_without_arp (ip) int can_unicast_without_arp (ip)

View File

@@ -33,7 +33,7 @@
#ifndef lint #ifndef lint
static char copyright[] = 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 */ #endif /* not lint */
#include "dhcpd.h" #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. */ /* UDP header and IP header decoded together for convenience. */
ssize_t decode_udp_ip_header (interface, buf, bufix, from, buflen) ssize_t
struct interface_info *interface; decode_udp_ip_header(struct interface_info *interface,
unsigned char *buf; unsigned char *buf, unsigned bufix,
unsigned bufix; struct sockaddr_in *from, unsigned buflen,
struct sockaddr_in *from; unsigned *rbuflen)
unsigned buflen;
{ {
unsigned char *data; unsigned char *data;
struct ip ip; struct ip ip;
struct udphdr *udp; struct udphdr udp;
u_int32_t ip_len = (buf [bufix] & 0xf) << 2; unsigned char *upp, *endbuf;
u_int32_t ip_len, ulen, pkt_len;
u_int32_t sum, usum; u_int32_t sum, usum;
static int ip_packets_seen; static int ip_packets_seen;
static int ip_packets_bad_checksum; 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_checked;
static int udp_packets_length_overflow; static int udp_packets_length_overflow;
unsigned len; unsigned len;
unsigned ulen;
int ignore = 0;
memcpy(&ip, buf + bufix, sizeof (struct ip)); /* Designate the end of the input buffer for bounds checks. */
udp = (struct udphdr *)(buf + bufix + ip_len); 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 #ifdef USERLAND_FILTER
/* Is it a UDP packet? */ /* Is it a UDP packet? */
@@ -241,17 +264,32 @@ ssize_t decode_udp_ip_header (interface, buf, bufix, from, buflen)
return -1; return -1;
/* Is it to the port we're serving? */ /* Is it to the port we're serving? */
if (udp -> uh_dport != local_port) if (udp.uh_dport != local_port)
return -1; return -1;
#endif /* USERLAND_FILTER */ #endif /* USERLAND_FILTER */
ulen = ntohs (udp -> uh_ulen); ulen = ntohs(udp.uh_ulen);
if (ulen < sizeof *udp || if (ulen < sizeof(udp))
((unsigned char *)udp) + ulen > buf + bufix + buflen) { return -1;
log_info ("bogus UDP packet length: %d", ulen);
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. */ /* Check the IP header checksum - it should be zero. */
++ip_packets_seen; ++ip_packets_seen;
if (wrapsum (checksum (buf + bufix, ip_len, 0))) { 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; 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... */ /* 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 /* Compute UDP checksums, including the ``pseudo-header'', the UDP
header and the data. If the UDP checksum field is zero, we're header and the data. If the UDP checksum field is zero, we're
not supposed to do a checksum. */ not supposed to do a checksum. */
data = buf + bufix + ip_len + sizeof *udp; data = upp + sizeof(udp);
len = ulen - 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;
}
usum = udp -> uh_sum; usum = udp.uh_sum;
udp -> uh_sum = 0; udp.uh_sum = 0;
sum = wrapsum (checksum ((unsigned char *)udp, sizeof *udp, /* XXX: We have to pass &udp, because we have to zero the checksum
checksum (data, len, * field before calculating the sum...'upp' isn't zeroed.
checksum ((unsigned char *) */
&ip.ip_src, sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
2 * sizeof ip.ip_src, checksum(data, len,
IPPROTO_UDP + checksum((unsigned char *)&ip.ip_src,
(u_int32_t)ulen)))); 8, IPPROTO_UDP + ulen))));
udp_packets_seen++; udp_packets_seen++;
if (usum && usum != sum) { if (usum && usum != sum) {
@@ -330,8 +337,13 @@ ssize_t decode_udp_ip_header (interface, buf, bufix, from, buflen)
} }
/* Copy out the port... */ /* 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);
return ip_len + sizeof *udp; /* Save the length of the UDP payload. */
if (rbuflen != NULL)
*rbuflen = len;
/* Return the index to the UDP payload. */
return ip_len + sizeof udp;
} }
#endif /* PACKET_DECODING */ #endif /* PACKET_DECODING */

View File

@@ -28,7 +28,7 @@
#ifndef lint #ifndef lint
static char copyright[] = 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 */ #endif /* not lint */
#include "dhcpd.h" #include "dhcpd.h"
@@ -59,9 +59,9 @@ struct routing_entry {
struct routing_entry *next; struct routing_entry *next;
unsigned char addr[TR_ALEN]; unsigned char addr[TR_ALEN];
unsigned char iface[5]; unsigned char iface[5];
u_int16_t rcf; /* route control field */ u_int16_t rcf; /* route control field */
u_int16_t rseg[8]; /* routing registers */ u_int16_t rseg[8]; /* routing registers */
unsigned long access_time; /* time we last used this entry */ unsigned long access_time; /* time we last used this entry */
}; };
static struct routing_entry *routing_info = NULL; static struct routing_entry *routing_info = NULL;

View File

@@ -34,7 +34,7 @@
#ifndef lint #ifndef lint
static char copyright[] = 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 */ #endif /* not lint */
#include "dhcpd.h" #include "dhcpd.h"
@@ -300,6 +300,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
int offset = 0; int offset = 0;
unsigned char ibuf [1500 + sizeof (struct enstamp)]; unsigned char ibuf [1500 + sizeof (struct enstamp)];
int bufix = 0; int bufix = 0;
unsigned paylen;
length = read (interface -> rfdesc, ibuf, sizeof ibuf); length = read (interface -> rfdesc, ibuf, sizeof ibuf);
if (length <= 0) if (length <= 0)
@@ -321,7 +322,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
/* Decode the IP and UDP headers... */ /* Decode the IP and UDP headers... */
offset = decode_udp_ip_header (interface, ibuf, bufix, 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 the IP or UDP checksum was bad, skip the packet... */
if (offset < 0) if (offset < 0)
@@ -330,9 +331,12 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
bufix += offset; bufix += offset;
length -= offset; length -= offset;
if (length < paylen)
log_fatal("Internal inconsistency at %s:%d.", MDL);
/* Copy out the data in the packet... */ /* Copy out the data in the packet... */
memcpy (buf, &ibuf [bufix], length); memcpy (buf, &ibuf[bufix], paylen);
return length; return paylen;
} }
int can_unicast_without_arp (ip) int can_unicast_without_arp (ip)

View File

@@ -2109,7 +2109,7 @@ ssize_t decode_hw_header PROTO ((struct interface_info *, unsigned char *,
unsigned, struct hardware *)); unsigned, struct hardware *));
ssize_t decode_udp_ip_header PROTO ((struct interface_info *, unsigned char *, ssize_t decode_udp_ip_header PROTO ((struct interface_info *, unsigned char *,
unsigned, struct sockaddr_in *, unsigned, struct sockaddr_in *,
unsigned)); unsigned, unsigned *));
/* ethernet.c */ /* ethernet.c */
void assemble_ethernet_header PROTO ((struct interface_info *, unsigned char *, void assemble_ethernet_header PROTO ((struct interface_info *, unsigned char *,