Commit 83c0372e authored by Evan Hunt's avatar Evan Hunt
Browse files

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]
parent 46153375
......@@ -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
......
......@@ -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;
}
......
......@@ -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
......
......@@ -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)
......
......@@ -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)
......
......@@ -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 */
......@@ -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;
......
......@@ -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)
......
......@@ -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 *,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment