Robustify get_raw_packet(). Error messages are now more specific, and

read() properly handles short reads and EINTR/EAGAIN/EWOULDBLOCK.
Update documentation.
This commit is contained in:
Nicholas J. Kain 2010-11-12 13:24:07 -05:00
parent 81398c79fb
commit 2262845be6
2 changed files with 65 additions and 21 deletions

2
README
View File

@ -92,9 +92,11 @@ USAGE
# cd /var/lib/ndhc # cd /var/lib/ndhc
# mkdir dev # mkdir dev
# mknod dev/urandom c 1 9 # mknod dev/urandom c 1 9
# mknod dev/null c 1 3
# chown -R root.root dev # chown -R root.root dev
# chmod a+rx dev # chmod a+rx dev
# chmod a+r dev/urandom # chmod a+r dev/urandom
# chmod a+rw dev/null
d) (optional) If you wish for logging to properly work, you d) (optional) If you wish for logging to properly work, you
will need to properly configure your logging daemon so that it will need to properly configure your logging daemon so that it

View File

@ -180,41 +180,83 @@ int send_release(unsigned long server, unsigned long ciaddr)
* -2 for those that aren't */ * -2 for those that aren't */
int get_raw_packet(struct dhcpMessage *payload, int fd) int get_raw_packet(struct dhcpMessage *payload, int fd)
{ {
int bytes;
struct udp_dhcp_packet packet; struct udp_dhcp_packet packet;
uint32_t source, dest; uint32_t source, dest;
uint16_t check; uint16_t check;
ssize_t len = 0;
const ssize_t wanted = sizeof(struct iphdr) + sizeof(struct udphdr);
memset(&packet, 0, sizeof(struct udp_dhcp_packet)); memset(&packet, 0, sizeof(struct udp_dhcp_packet));
bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet)); while (len < wanted) {
if (bytes < 0) { ssize_t r = read(fd, &packet + len,
log_line("couldn't read on raw listening socket -- ignoring"); sizeof(struct udp_dhcp_packet) - len);
usleep(500000); /* possible down interface, looping condition */ if (r == 0)
return -1; break;
if (r == -1) {
if (errno == EINTR)
continue;
if (errno == EAGAIN || errno == EWOULDBLOCK) {
log_line("EAGAIN or EWOULDBLOCK hit");
break;
}
log_line("couldn't read on raw listening socket -- ignoring");
usleep(500000); /* possible down interface, looping condition */
return -1;
}
len += r;
} }
if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) { if (len == 0) {
log_line("message too short, ignoring"); usleep(50000);
return -2; return -2;
} }
if (bytes < ntohs(packet.ip.tot_len)) { log_line("len: %d wanted: %d", len, wanted);
if (len < wanted) {
log_line("Message too short to contain IP + UDP headers, ignoring");
sleep(1);
return -2;
}
if (len < ntohs(packet.ip.tot_len)) {
log_line("Truncated packet"); log_line("Truncated packet");
return -2; return -2;
} }
/* ignore any extra garbage bytes */ /* ignore any extra garbage bytes */
bytes = ntohs(packet.ip.tot_len); len = ntohs(packet.ip.tot_len);
/* Make sure its the right packet for us, and that it passes /* Make sure its the right packet for us, and that it passes
* sanity checks */ * sanity checks */
if (packet.ip.protocol != IPPROTO_UDP if (packet.ip.protocol != IPPROTO_UDP) {
|| packet.ip.version != IPVERSION log_line("IP header is not UDP");
|| packet.ip.ihl != sizeof(packet.ip) >> 2 sleep(1);
|| packet.udp.dest != htons(CLIENT_PORT) return -2;
|| bytes > (int)sizeof(struct udp_dhcp_packet) }
|| ntohs(packet.udp.len) != (short)(bytes - sizeof(packet.ip))) { if (packet.ip.version != IPVERSION) {
log_line("unrelated/bogus packet"); log_line("IP version is not IPv4");
sleep(1);
return -2;
}
if (packet.ip.ihl != sizeof(packet.ip) >> 2) {
log_line("IP header length incorrect");
sleep(1);
return -2;
}
if (packet.udp.dest != htons(CLIENT_PORT)) {
log_line("UDP destination port incorrect");
sleep(1);
return -2;
}
if (len > (int)sizeof(struct udp_dhcp_packet)) {
log_line("Data longer than that of a IP+UDP+DHCP message");
sleep(1);
return -2;
}
if (ntohs(packet.udp.len) != (short)(len - sizeof(packet.ip))) {
log_line("UDP header length incorrect");
sleep(1);
return -2; return -2;
} }
@ -237,20 +279,20 @@ int get_raw_packet(struct dhcpMessage *payload, int fd)
packet.ip.saddr = source; packet.ip.saddr = source;
packet.ip.daddr = dest; packet.ip.daddr = dest;
packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */ packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */
if (check && check != checksum(&packet, bytes)) { if (check && check != checksum(&packet, len)) {
log_error("packet with bad UDP checksum received, ignoring"); log_error("packet with bad UDP checksum received, ignoring");
return -2; return -2;
} }
memcpy(payload, &(packet.data), memcpy(payload, &(packet.data),
bytes - (sizeof(packet.ip) + sizeof(packet.udp))); len - (sizeof(packet.ip) + sizeof(packet.udp)));
if (ntohl(payload->cookie) != DHCP_MAGIC) { if (ntohl(payload->cookie) != DHCP_MAGIC) {
log_error("received bogus message (bad magic) -- ignoring"); log_error("received bogus message (bad magic) -- ignoring");
return -2; return -2;
} }
log_line("oooooh!!! got some!"); log_line("oooooh!!! got some!");
return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); return len - (sizeof(packet.ip) + sizeof(packet.udp));
} }