diff --git a/Makefile b/Makefile index e7bc74c6..78031fc2 100644 --- a/Makefile +++ b/Makefile @@ -8,12 +8,15 @@ all: dhcpd dhclient .include -CFLAGS += -g -Wall -Wstrict-prototypes -Wno-unused \ +DEBUG=-g + +CFLAGS += $(DEBUG) -Wall -Wstrict-prototypes -Wno-unused \ + -Wno-implicit -Wno-comment \ -Wno-uninitialized -Werror dhclient: dhclient.o confpars.o alloc.o memory.o options.o \ hash.o tables.o inet.o convert.o conflex.o errwarn.o \ tree.o print.o db.o - cc -o dhclient dhclient.o confpars.o alloc.o memory.o options.o \ + $(CC) -o dhclient dhclient.o confpars.o alloc.o memory.o options.o \ hash.o tables.o inet.o convert.o conflex.o errwarn.o \ print.o tree.o db.o diff --git a/Makefile.std b/Makefile.std index 105141d4..ce31a376 100644 --- a/Makefile.std +++ b/Makefile.std @@ -11,7 +11,7 @@ DEBUG=-g CFLAGS=$(DEBUG) dhcpd: $(OBJS) $(COBJ) - cc -o dhcpd $(OBJS) $(COBJ) + $(CC) -o dhcpd $(OBJS) $(COBJ) dhclient: dhclient.o $(COBJ) - cc -o dhclient dhclient.o $(COBJ) + $(CC) -o dhclient dhclient.o $(COBJ) diff --git a/README b/README index 42a88dbd..685aed12 100644 --- a/README +++ b/README @@ -32,6 +32,26 @@ Bakeoff participants at Connectathon who tried their clients against dhcpd and told me where it was busted, or, later on, that it wasn't busted anymore. + DEBUGGING + +dhcpd logs to LOG_DAEMON. Depending on the logging level that you +choose with syslog, you can get quite a bit of information about what +dhcpd is doing. To get the most logging, put the following in your +/etc/syslog.conf file and restart syslog: + +daemon.debug: /var/log/daemon.log + +(obviously, change the filename to suit your taste). + +This change may have the unfortunate side effect of capturing a lot of +information from daemons other than dhcpd that you don't want to look +at. + +You can also compile dhcpd with ``make DEBUG="-g -DDEBUG"''. If you +do this, dhcpd will run in the foreground rather than as a daemon, and +will print its log messages to standard error. It will also dump the +contents of all packets it receives and sends. + BUGS Currently, ISC dhcpd supports the DHCP protocol strictly the standard diff --git a/bootp.c b/bootp.c index 54686c02..ff767088 100644 --- a/bootp.c +++ b/bootp.c @@ -151,8 +151,11 @@ void bootp (packet) #endif memset (to.sin_zero, 0, sizeof to.sin_zero); - note ("Sending bootp reply to %s, port %d", - inet_ntoa (to.sin_addr), to.sin_port); + note ("Sending BOOTREPLY to %s, address %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + inet_ntoa (packet -> raw -> yiaddr)); errno = 0; result = sendto (packet -> client_sock, &raw, outgoing.packet_length, diff --git a/cf/bsdos.h b/cf/bsdos.h index cde22a9a..4c86cafe 100644 --- a/cf/bsdos.h +++ b/cf/bsdos.h @@ -1,6 +1,6 @@ -/* netbsd.h +/* bsdos.h - System dependencies for NetBSD... */ + System dependencies for BSDI BSD/OS... */ /* * Copyright (c) 1996 The Internet Software Consortium. All rights reserved. diff --git a/common/conflex.c b/common/conflex.c index 5b7a91ef..57eb1ecf 100644 --- a/common/conflex.c +++ b/common/conflex.c @@ -106,7 +106,7 @@ static int get_token (cfile) ttok = read_string (cfile); break; } - if (isascii (c) && isdigit (c)) { + if ((isascii (c) && isdigit (c)) || c == '-') { ttok = read_number (c, cfile); break; } else if (isascii (c) && isalpha (c)) { diff --git a/common/options.c b/common/options.c index a5ff795a..354c10a4 100644 --- a/common/options.c +++ b/common/options.c @@ -173,12 +173,15 @@ void cons_options (inpacket, outpacket, options, overload) /* XXX Maybe it would be safe to assume that we can send a packet to the client that's as big as the one it sent us, even if it didn't specify a large MTU. */ - if (inpacket && inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].data) + if (inpacket && inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].data) { main_buffer_size = (getUShort (inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].data) - DHCP_FIXED_LEN); - else + /* Enforce a minimum packet size... */ + if (main_buffer_size < (576 - DHCP_FIXED_LEN)) + main_buffer_size = 576 - DHCP_FIXED_LEN; + } else main_buffer_size = 576 - DHCP_FIXED_LEN; /* Preload the option priority list with mandatory options. */ diff --git a/common/socket.c b/common/socket.c index 182398cf..1c5aafca 100644 --- a/common/socket.c +++ b/common/socket.c @@ -48,6 +48,27 @@ static char copyright[] = #include "dhcpd.h" #include +#ifdef USE_BPF + +#include +#include +#include +#include +#include +#include +#include +#include + +struct interface { + struct in_addr address; + int bpf; +}; + +static struct interface *if_list; +static int num_ifaces; + +#endif + /* List of sockets we're accepting packets on... */ struct socklist { struct socklist *next; @@ -86,17 +107,48 @@ u_int32_t *get_interface_list (count) second time to copy them into an array of addresses. */ for (i = 0; i < ic.ifc_len;) { struct ifreq *ifp = (struct ifreq *)((caddr_t)ic.ifc_req + i); +#ifdef HAVE_SIN_LEN i += (sizeof ifp -> ifr_name) + ifp -> ifr_addr.sa_len; +#else + i += sizeof *ifp; +#endif if (ifp -> ifr_addr.sa_family == AF_INET) { struct sockaddr_in *foo = (struct sockaddr_in *)(&ifp -> ifr_addr); /* We don't want the loopback interface. */ if (foo -> sin_addr.s_addr == INADDR_LOOPBACK) continue; - if (intbuf) - intbuf [ifix++] = foo -> sin_addr.s_addr; - else + if (intbuf) { + intbuf [ifix] = foo -> sin_addr.s_addr; +#ifdef USE_BPF + /* Open a bpf device for this interface */ + { + int b; + char filename[50]; + + for (b = 0; 1; b++) + { + snprintf(filename, sizeof(filename), + "/dev/bpf%d", b); + if ((if_list[ifix].bpf = + open(filename, O_RDWR, 0)) < 0) + if (errno == EBUSY) + continue; + else + error ("Can't find free bpf: %m"); + else + break; + } + if (ioctl(if_list[ifix].bpf, + BIOCSETIF, ifp) < 0) + error ("Can't BIOCSETIF on bpf: %m"); + } + if_list[ifix].address = foo->sin_addr; +#endif + ifix++; + } else { ++ifcount; + } } } /* If we haven't already filled our array, allocate it and go @@ -107,6 +159,13 @@ u_int32_t *get_interface_list (count) "get_interface_list"); if (!intbuf) return intbuf; +#ifdef USE_BPF + num_ifaces = ifcount; + if (!(if_list = (struct interface *)dmalloc + (num_ifaces * sizeof(*if_list), + "get_interface_list"))) + error ("Can't allocate memory for if_list"); +#endif goto again; } *count = ifcount; @@ -215,3 +274,198 @@ void dispatch () } while (1); } +#ifndef USE_BPF + +int sendpkt (packet, raw, len, to, tolen) + struct packet *packet; + struct dhcp_packet *raw; + size_t len; + struct sockaddr *to; + int tolen; +{ + return(sendto(packet->client_sock, raw, len, 0, to, tolen)); +} + +#else + +static void IpChecksum(struct ip *ip); +static void UdpChecksum(struct ip *ip); +static u_int32_t Checksum(u_int16_t *buf, int nwords); + +struct raw_packet +{ + u_int16_t space; + struct ether_header en_hdr; + struct ip ip; + struct udphdr udp; + struct dhcp_packet dhcp; +}; + +int sendpkt (in_packet, raw, len, to, tolen) + struct packet *in_packet; + struct dhcp_packet *raw; + size_t len; + struct sockaddr *to; + int tolen; +{ + int i, k; + struct iaddr dest; + struct subnet *subnet; + struct raw_packet out_packet; + struct raw_packet *const pkt = &out_packet; + +/* Find local subnet, or else forward to gateway */ + + dest.len = 4; + memcpy(&dest.iabuf, &((struct sockaddr_in *) to)->sin_addr, dest.len); + if ((subnet = find_subnet(dest)) == NULL) + return(sendto(in_packet->client_sock, raw, len, 0, to, tolen)); + +/* Find interface corresponding to subnet */ + + for (i = 0; i < num_ifaces; i++) + { + for (k = 0; k < subnet->net.len + && (dest.iabuf[k] & subnet->netmask.iabuf[k]) + == (subnet->net.iabuf[k] & subnet->netmask.iabuf[k]); + k++); + if (k == subnet->net.len) + break; + } + if (i == num_ifaces) + return(sendto(in_packet->client_sock, raw, len, 0, to, tolen)); + +/* EtherNet header */ + + memset(pkt->en_hdr.ether_dhost, 0xff, sizeof(pkt->en_hdr.ether_dhost)); + memset(pkt->en_hdr.ether_shost, 0x00, sizeof(pkt->en_hdr.ether_shost)); + pkt->en_hdr.ether_type = ETHERTYPE_IP; + +/* IP header (except for checksum) */ + + pkt->ip.ip_v = 4; + pkt->ip.ip_hl = 5; + pkt->ip.ip_tos = IPTOS_LOWDELAY; + pkt->ip.ip_len = htons(sizeof(pkt->ip) + sizeof(pkt->udp) + len); + pkt->ip.ip_id = 0; + pkt->ip.ip_off = 0; + pkt->ip.ip_ttl = 16; + pkt->ip.ip_p = IPPROTO_UDP; + pkt->ip.ip_sum = 0; + pkt->ip.ip_src = if_list[i].address; + inet_aton("255.255.255.255", &pkt->ip.ip_dst); + +/* UDP header */ + + pkt->udp.uh_sport = htons(67); /* XXX! */ + pkt->udp.uh_dport = in_packet->client_port; + pkt->udp.uh_ulen = htons(sizeof(pkt->udp) + len); + pkt->udp.uh_sum = 0; + +/* DHCP packet */ + + pkt->dhcp = *raw; + +/* Compute checksums */ + + UdpChecksum(&pkt->ip); + IpChecksum(&pkt->ip); + +/* Fire it off */ + + if (write(if_list[i].bpf, &pkt->en_hdr, + ntohs(pkt->ip.ip_len) + sizeof(pkt->en_hdr)) < 0) + warn ("Can't deliver packet: write: %m"); + return(0); +} + +/* + * UdpChecksum() + * + * Recompute a UDP checksum on a packet + * + * UDP pseudo-header (prot = IPPROTO_UDP = 17): + * + * | source IP address | + * | dest. IP address | + * | zero | prot | UDP leng | + * + */ + +static void +UdpChecksum(struct ip *ip) +{ + struct udphdr *udp = (struct udphdr *) ((long *) ip + ip->ip_hl); + u_int32_t sum; + +/* Pad with zero */ + + if (ntohs(udp->uh_ulen) & 0x1) + *((u_char *) udp + ntohs(udp->uh_ulen)) = 0; + +/* Do pseudo-header first */ + + sum = Checksum((u_int16_t *) &ip->ip_src, 4); + sum += (u_int16_t) IPPROTO_UDP; + sum += (u_int16_t) ntohs(udp->uh_ulen); + +/* Now do UDP packet itself */ + + udp->uh_sum = 0; + sum += Checksum((u_int16_t *) udp, + ((u_int16_t) ntohs(udp->uh_ulen) + 1) >> 1); + +/* Flip it & stick it */ + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + sum = ~sum; + + udp->uh_sum = htons(sum); +} + +/* + * IpChecksum() + * + * Recompute an IP header checksum + * + */ + +static void +IpChecksum(struct ip *ip) +{ + u_int32_t sum; + +/* Sum up IP header words */ + + ip->ip_sum = 0; + sum = Checksum((u_int16_t *) ip, ip->ip_hl * 2); + +/* Flip it & stick it */ + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + sum = ~sum; + + ip->ip_sum = htons(sum); +} + +/* + * Checksum() + * + * Do the one's complement sum thing over a range of words + * Ideally, this should get replaced by an assembly version. + */ + +static u_int32_t +Checksum(u_int16_t *buf, int nwords) +{ + u_int32_t sum = 0; + + while (nwords--) + sum += (u_int16_t) ntohs(*buf++); + return(sum); +} + +#endif + diff --git a/conflex.c b/conflex.c index 5b7a91ef..57eb1ecf 100644 --- a/conflex.c +++ b/conflex.c @@ -106,7 +106,7 @@ static int get_token (cfile) ttok = read_string (cfile); break; } - if (isascii (c) && isdigit (c)) { + if ((isascii (c) && isdigit (c)) || c == '-') { ttok = read_number (c, cfile); break; } else if (isascii (c) && isalpha (c)) { diff --git a/confpars.c b/confpars.c index 3617366e..42573393 100644 --- a/confpars.c +++ b/confpars.c @@ -1024,7 +1024,7 @@ TIME parse_date (cfile, bc) skip_to_semi (cfile); longjmp (*bc, 1); } - tm.tm_mon = atoi (val); + tm.tm_mon = atoi (val) - 1; /* Slash seperating month from day... */ token = next_token (&val, cfile); @@ -1093,9 +1093,12 @@ TIME parse_date (cfile, bc) } tm.tm_sec = atoi (val); +#ifndef BROKEN_TM_GMT + /* linux does not implement these yet */ tm.tm_zone = "GMT"; - tm.tm_isdst = 0; tm.tm_gmtoff = 0; +#endif + tm.tm_isdst = 0; /* XXX */ /* We assume that mktime does not use tm_yday. */ tm.tm_yday = 0; diff --git a/db.c b/db.c index e7f348d2..6774c095 100644 --- a/db.c +++ b/db.c @@ -117,7 +117,7 @@ void new_lease_file () /* Make a temporary lease file... */ time (&t); - sprintf (newfname, "%s.%d", _PATH_DHCPD_DB, t & 32767); + sprintf (newfname, "%s.%d", _PATH_DHCPD_DB, (int) (t & 32767)); if ((db_file = fopen (newfname, "w")) == NULL) { error ("Can't start new lease file: %m"); } diff --git a/dhcp.c b/dhcp.c index 9da03a17..6a70915f 100644 --- a/dhcp.c +++ b/dhcp.c @@ -83,6 +83,11 @@ void dhcpdiscover (packet) { struct lease *lease = find_lease (packet); + debug ("Received DHCPDISCOVER from %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr)); + /* If we didn't find a lease, try to allocate one... */ if (!lease) { lease = packet -> subnet -> last_lease; @@ -106,17 +111,29 @@ void dhcprequest (packet) struct lease *lease = find_lease (packet); struct iaddr cip; - /* If a client on our local network wants to renew a lease on - an address off our local network, NAK it. */ if (packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len) { cip.len = 4; memcpy (cip.iabuf, packet -> options [DHO_DHCP_REQUESTED_ADDRESS].data, 4); + } else { + cip.len = 4; + memcpy (cip.iabuf, &packet -> raw -> ciaddr.s_addr, 4); + } + + debug ("Received DHCPREQUEST from %s for %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + piaddr (cip)); + + /* If a client on our local network wants to renew a lease on + an address off our local network, NAK it. */ + if (packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len) { if (!addr_eq (packet -> subnet -> net, subnet_number (cip, packet -> subnet -> netmask))) { - nak_lease (packet); + nak_lease (packet, &cip); return; } } @@ -127,7 +144,7 @@ void dhcprequest (packet) if (!addr_eq (packet -> subnet -> net, subnet_number (cip, packet -> subnet -> netmask))) { - nak_lease (packet); + nak_lease (packet, &cip); return; } } @@ -149,7 +166,7 @@ void dhcprequest (packet) /* If we didn't find a lease, don't try to allocate one... */ if (!lease) { - nak_lease (packet); + nak_lease (packet, &cip); return; } @@ -161,6 +178,12 @@ void dhcprelease (packet) { struct lease *lease = find_lease (packet); + debug ("Received DHCPRELEASE from %s for %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + inet_ntoa (packet -> raw -> ciaddr)); + /* If we found a lease, release it. */ if (lease) { release_lease (lease); @@ -171,6 +194,22 @@ void dhcpdecline (packet) struct packet *packet; { struct lease *lease = find_lease (packet); + struct iaddr cip; + + if (packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len) { + cip.len = 4; + memcpy (cip.iabuf, + packet -> options [DHO_DHCP_REQUESTED_ADDRESS].data, + 4); + } else { + cip.len = 0; + } + + debug ("Received DHCPDECLINE from %s for %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + piaddr (cip)); /* If we found a lease, mark it as unusable and complain. */ if (lease) { @@ -181,10 +220,17 @@ void dhcpdecline (packet) void dhcpinform (packet) struct packet *packet; { + debug ("Received DHCPINFORM from %s for %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + inet_ntoa (packet -> raw -> ciaddr)); + } -void nak_lease (packet) +void nak_lease (packet, cip) struct packet *packet; + struct iaddr *cip; { struct sockaddr_in to; int result; @@ -244,8 +290,8 @@ void nak_lease (packet) to.sin_addr = raw.giaddr; to.sin_port = server_port; } else { - to.sin_addr.s_addr = INADDR_BROADCAST; - to.sin_port = htons (ntohs (server_port) + 1); /* XXX */ + memcpy (&to.sin_addr.s_addr, cip->iabuf, 4); + to.sin_port = packet->client_port; } to.sin_family = AF_INET; @@ -254,14 +300,17 @@ void nak_lease (packet) #endif memset (to.sin_zero, 0, sizeof to.sin_zero); - note ("Sending dhcp NAK to %s, port %d", + note ("Sending DHCPNAK to %s at IP address %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), inet_ntoa (to.sin_addr), htons (to.sin_port)); errno = 0; - result = sendto (packet -> client_sock, &raw, outgoing.packet_length, - 0, (struct sockaddr *)&to, sizeof to); + result = sendpkt (packet, &raw, outgoing.packet_length, + (struct sockaddr *) &to, sizeof(to)); if (result < 0) - warn ("sendto: %m"); + warn ("sendpkt: %m"); #ifdef DEBUG dump_packet (packet); @@ -471,6 +520,15 @@ void ack_lease (packet, lease, offer, when) options [DHO_DHCP_USER_CLASS_ID] -> tree = (struct tree *)0; } + /* Sanity check the lease time. */ + if ((lease->offered_expiry - cur_time) < 0) + putULong(lease_time_buf, packet->subnet->default_lease_time); + else if (lease -> offered_expiry - cur_time > + packet -> subnet -> max_lease_time) + putULong (lease_time_buf, packet -> subnet -> max_lease_time); + else + putULong(lease_time_buf, lease -> offered_expiry - cur_time); + putULong (lease_time_buf, lease -> offered_expiry - cur_time); options [DHO_DHCP_LEASE_TIME] = &lease_time_tree; options [DHO_DHCP_LEASE_TIME] -> value = lease_time_buf; @@ -508,7 +566,7 @@ void ack_lease (packet, lease, offer, when) /* Otherwise, broadcast it on the local network. */ } else { - to.sin_addr.s_addr = INADDR_BROADCAST; + memcpy (&to.sin_addr.s_addr, lease -> ip_addr.iabuf, 4); to.sin_port = htons (ntohs (server_port) + 1); /* XXX */ } @@ -518,14 +576,18 @@ void ack_lease (packet, lease, offer, when) #endif memset (to.sin_zero, 0, sizeof to.sin_zero); - note ("Sending dhcp reply to %s, port %d", + note ("Sending %s to %s at IP address %s", + offer == DHCPACK ? "DHCPACK" : "DHCPOFFER", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), inet_ntoa (to.sin_addr), htons (to.sin_port)); errno = 0; - result = sendto (packet -> client_sock, &raw, outgoing.packet_length, - 0, (struct sockaddr *)&to, sizeof to); + result = sendpkt (packet, &raw, outgoing.packet_length, + (struct sockaddr *) &to, sizeof(to)); if (result < 0) - warn ("sendto: %m"); + warn ("sendpkt: %m"); #ifdef DEBUG dump_packet (packet); diff --git a/dhcpd.c b/dhcpd.c index a3ddde93..97856c36 100644 --- a/dhcpd.c +++ b/dhcpd.c @@ -179,9 +179,9 @@ int main (argc, argv, envp) } /* Write a pid file. */ - if ((i = open (_PATH_DHCPD_PID, O_WRONLY | O_CREAT)) >= 0) { + if ((i = open (_PATH_DHCPD_PID, O_WRONLY | O_CREAT, 0640)) >= 0) { char obuf [20]; - sprintf (obuf, "%d\n", getpid ()); + sprintf (obuf, "%d\n", (int)getpid ()); write (i, obuf, strlen (obuf)); close (i); } diff --git a/dhcpd.h b/dhcpd.h index 45ec5fbb..abc74656 100644 --- a/dhcpd.h +++ b/dhcpd.h @@ -54,9 +54,9 @@ #include #include +#include "osdep.h" #include "dhcp.h" #include "cdefs.h" -#include "osdep.h" #include "tree.h" #include "hash.h" #include "inet.h" @@ -150,14 +150,21 @@ typedef unsigned char option_mask [16]; #define OPTION_SPACE(x) ((x) + 2 * ((x) / 255 + 1)) /* Default path to dhcpd config file. */ -#ifndef _PATH_DHCPD_CONF #ifdef DEBUG #define _PATH_DHCPD_CONF "dhcpd.conf" #define _PATH_DHCPD_DB "dhcpd.leases" #else +#ifndef _PATH_DHCPD_CONF #define _PATH_DHCPD_CONF "/etc/dhcpd.conf" +#endif + +#ifndef _PATH_DHCPD_DB #define _PATH_DHCPD_DB "/etc/dhcpd.leases" #endif + +#ifndef _PATH_DHCPD_PID +#define _PATH_DHCPD_PID "/var/run/dhcpd.pid" +#endif #endif #define MAX_TIME 0x7fffffff @@ -252,7 +259,7 @@ void dhcprequest PROTO ((struct packet *)); void dhcprelease PROTO ((struct packet *)); void dhcpdecline PROTO ((struct packet *)); void dhcpinform PROTO ((struct packet *)); -void nak_lease PROTO ((struct packet *)); +void nak_lease PROTO ((struct packet *, struct iaddr *cip)); void ack_lease PROTO ((struct packet *, struct lease *, unsigned char, TIME)); struct lease *find_lease PROTO ((struct packet *)); @@ -309,8 +316,11 @@ void dump_raw PROTO ((unsigned char *, int)); /* socket.c */ u_int32_t *get_interface_list PROTO ((int *)); +char *get_interface PROTO ((u_int32_t)); void listen_on PROTO ((u_int16_t, u_int32_t)); void dispatch PROTO ((void)); +int sendpkt PROTO ((struct packet *, struct dhcp_packet *, + size_t, struct sockaddr *, int)); /* hash.c */ struct hash_table *new_hash PROTO ((void)); diff --git a/includes/cf/bsdos.h b/includes/cf/bsdos.h index cde22a9a..4c86cafe 100644 --- a/includes/cf/bsdos.h +++ b/includes/cf/bsdos.h @@ -1,6 +1,6 @@ -/* netbsd.h +/* bsdos.h - System dependencies for NetBSD... */ + System dependencies for BSDI BSD/OS... */ /* * Copyright (c) 1996 The Internet Software Consortium. All rights reserved. diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 45ec5fbb..abc74656 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -54,9 +54,9 @@ #include #include +#include "osdep.h" #include "dhcp.h" #include "cdefs.h" -#include "osdep.h" #include "tree.h" #include "hash.h" #include "inet.h" @@ -150,14 +150,21 @@ typedef unsigned char option_mask [16]; #define OPTION_SPACE(x) ((x) + 2 * ((x) / 255 + 1)) /* Default path to dhcpd config file. */ -#ifndef _PATH_DHCPD_CONF #ifdef DEBUG #define _PATH_DHCPD_CONF "dhcpd.conf" #define _PATH_DHCPD_DB "dhcpd.leases" #else +#ifndef _PATH_DHCPD_CONF #define _PATH_DHCPD_CONF "/etc/dhcpd.conf" +#endif + +#ifndef _PATH_DHCPD_DB #define _PATH_DHCPD_DB "/etc/dhcpd.leases" #endif + +#ifndef _PATH_DHCPD_PID +#define _PATH_DHCPD_PID "/var/run/dhcpd.pid" +#endif #endif #define MAX_TIME 0x7fffffff @@ -252,7 +259,7 @@ void dhcprequest PROTO ((struct packet *)); void dhcprelease PROTO ((struct packet *)); void dhcpdecline PROTO ((struct packet *)); void dhcpinform PROTO ((struct packet *)); -void nak_lease PROTO ((struct packet *)); +void nak_lease PROTO ((struct packet *, struct iaddr *cip)); void ack_lease PROTO ((struct packet *, struct lease *, unsigned char, TIME)); struct lease *find_lease PROTO ((struct packet *)); @@ -309,8 +316,11 @@ void dump_raw PROTO ((unsigned char *, int)); /* socket.c */ u_int32_t *get_interface_list PROTO ((int *)); +char *get_interface PROTO ((u_int32_t)); void listen_on PROTO ((u_int16_t, u_int32_t)); void dispatch PROTO ((void)); +int sendpkt PROTO ((struct packet *, struct dhcp_packet *, + size_t, struct sockaddr *, int)); /* hash.c */ struct hash_table *new_hash PROTO ((void)); diff --git a/includes/osdep.h b/includes/osdep.h index 1ff4e68d..58ffc8d9 100644 --- a/includes/osdep.h +++ b/includes/osdep.h @@ -48,3 +48,19 @@ #include "cf/netbsd.h" #endif +#ifdef __FreeBSD__ +#include "cf/freebsd.h" +#endif + +#ifdef sun +#include "cf/sunos4.h" +#endif + + +#ifdef ultrix +#include "cf/ultrix.h" +#endif + +#ifdef linux +#include "cf/linux.h" +#endif diff --git a/options.c b/options.c index a5ff795a..354c10a4 100644 --- a/options.c +++ b/options.c @@ -173,12 +173,15 @@ void cons_options (inpacket, outpacket, options, overload) /* XXX Maybe it would be safe to assume that we can send a packet to the client that's as big as the one it sent us, even if it didn't specify a large MTU. */ - if (inpacket && inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].data) + if (inpacket && inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].data) { main_buffer_size = (getUShort (inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].data) - DHCP_FIXED_LEN); - else + /* Enforce a minimum packet size... */ + if (main_buffer_size < (576 - DHCP_FIXED_LEN)) + main_buffer_size = 576 - DHCP_FIXED_LEN; + } else main_buffer_size = 576 - DHCP_FIXED_LEN; /* Preload the option priority list with mandatory options. */ diff --git a/osdep.h b/osdep.h index 1ff4e68d..58ffc8d9 100644 --- a/osdep.h +++ b/osdep.h @@ -48,3 +48,19 @@ #include "cf/netbsd.h" #endif +#ifdef __FreeBSD__ +#include "cf/freebsd.h" +#endif + +#ifdef sun +#include "cf/sunos4.h" +#endif + + +#ifdef ultrix +#include "cf/ultrix.h" +#endif + +#ifdef linux +#include "cf/linux.h" +#endif diff --git a/server/bootp.c b/server/bootp.c index 54686c02..ff767088 100644 --- a/server/bootp.c +++ b/server/bootp.c @@ -151,8 +151,11 @@ void bootp (packet) #endif memset (to.sin_zero, 0, sizeof to.sin_zero); - note ("Sending bootp reply to %s, port %d", - inet_ntoa (to.sin_addr), to.sin_port); + note ("Sending BOOTREPLY to %s, address %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + inet_ntoa (packet -> raw -> yiaddr)); errno = 0; result = sendto (packet -> client_sock, &raw, outgoing.packet_length, diff --git a/server/confpars.c b/server/confpars.c index 3617366e..42573393 100644 --- a/server/confpars.c +++ b/server/confpars.c @@ -1024,7 +1024,7 @@ TIME parse_date (cfile, bc) skip_to_semi (cfile); longjmp (*bc, 1); } - tm.tm_mon = atoi (val); + tm.tm_mon = atoi (val) - 1; /* Slash seperating month from day... */ token = next_token (&val, cfile); @@ -1093,9 +1093,12 @@ TIME parse_date (cfile, bc) } tm.tm_sec = atoi (val); +#ifndef BROKEN_TM_GMT + /* linux does not implement these yet */ tm.tm_zone = "GMT"; - tm.tm_isdst = 0; tm.tm_gmtoff = 0; +#endif + tm.tm_isdst = 0; /* XXX */ /* We assume that mktime does not use tm_yday. */ tm.tm_yday = 0; diff --git a/server/db.c b/server/db.c index e7f348d2..6774c095 100644 --- a/server/db.c +++ b/server/db.c @@ -117,7 +117,7 @@ void new_lease_file () /* Make a temporary lease file... */ time (&t); - sprintf (newfname, "%s.%d", _PATH_DHCPD_DB, t & 32767); + sprintf (newfname, "%s.%d", _PATH_DHCPD_DB, (int) (t & 32767)); if ((db_file = fopen (newfname, "w")) == NULL) { error ("Can't start new lease file: %m"); } diff --git a/server/dhcp.c b/server/dhcp.c index 9da03a17..6a70915f 100644 --- a/server/dhcp.c +++ b/server/dhcp.c @@ -83,6 +83,11 @@ void dhcpdiscover (packet) { struct lease *lease = find_lease (packet); + debug ("Received DHCPDISCOVER from %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr)); + /* If we didn't find a lease, try to allocate one... */ if (!lease) { lease = packet -> subnet -> last_lease; @@ -106,17 +111,29 @@ void dhcprequest (packet) struct lease *lease = find_lease (packet); struct iaddr cip; - /* If a client on our local network wants to renew a lease on - an address off our local network, NAK it. */ if (packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len) { cip.len = 4; memcpy (cip.iabuf, packet -> options [DHO_DHCP_REQUESTED_ADDRESS].data, 4); + } else { + cip.len = 4; + memcpy (cip.iabuf, &packet -> raw -> ciaddr.s_addr, 4); + } + + debug ("Received DHCPREQUEST from %s for %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + piaddr (cip)); + + /* If a client on our local network wants to renew a lease on + an address off our local network, NAK it. */ + if (packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len) { if (!addr_eq (packet -> subnet -> net, subnet_number (cip, packet -> subnet -> netmask))) { - nak_lease (packet); + nak_lease (packet, &cip); return; } } @@ -127,7 +144,7 @@ void dhcprequest (packet) if (!addr_eq (packet -> subnet -> net, subnet_number (cip, packet -> subnet -> netmask))) { - nak_lease (packet); + nak_lease (packet, &cip); return; } } @@ -149,7 +166,7 @@ void dhcprequest (packet) /* If we didn't find a lease, don't try to allocate one... */ if (!lease) { - nak_lease (packet); + nak_lease (packet, &cip); return; } @@ -161,6 +178,12 @@ void dhcprelease (packet) { struct lease *lease = find_lease (packet); + debug ("Received DHCPRELEASE from %s for %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + inet_ntoa (packet -> raw -> ciaddr)); + /* If we found a lease, release it. */ if (lease) { release_lease (lease); @@ -171,6 +194,22 @@ void dhcpdecline (packet) struct packet *packet; { struct lease *lease = find_lease (packet); + struct iaddr cip; + + if (packet -> options [DHO_DHCP_REQUESTED_ADDRESS].len) { + cip.len = 4; + memcpy (cip.iabuf, + packet -> options [DHO_DHCP_REQUESTED_ADDRESS].data, + 4); + } else { + cip.len = 0; + } + + debug ("Received DHCPDECLINE from %s for %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + piaddr (cip)); /* If we found a lease, mark it as unusable and complain. */ if (lease) { @@ -181,10 +220,17 @@ void dhcpdecline (packet) void dhcpinform (packet) struct packet *packet; { + debug ("Received DHCPINFORM from %s for %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + inet_ntoa (packet -> raw -> ciaddr)); + } -void nak_lease (packet) +void nak_lease (packet, cip) struct packet *packet; + struct iaddr *cip; { struct sockaddr_in to; int result; @@ -244,8 +290,8 @@ void nak_lease (packet) to.sin_addr = raw.giaddr; to.sin_port = server_port; } else { - to.sin_addr.s_addr = INADDR_BROADCAST; - to.sin_port = htons (ntohs (server_port) + 1); /* XXX */ + memcpy (&to.sin_addr.s_addr, cip->iabuf, 4); + to.sin_port = packet->client_port; } to.sin_family = AF_INET; @@ -254,14 +300,17 @@ void nak_lease (packet) #endif memset (to.sin_zero, 0, sizeof to.sin_zero); - note ("Sending dhcp NAK to %s, port %d", + note ("Sending DHCPNAK to %s at IP address %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), inet_ntoa (to.sin_addr), htons (to.sin_port)); errno = 0; - result = sendto (packet -> client_sock, &raw, outgoing.packet_length, - 0, (struct sockaddr *)&to, sizeof to); + result = sendpkt (packet, &raw, outgoing.packet_length, + (struct sockaddr *) &to, sizeof(to)); if (result < 0) - warn ("sendto: %m"); + warn ("sendpkt: %m"); #ifdef DEBUG dump_packet (packet); @@ -471,6 +520,15 @@ void ack_lease (packet, lease, offer, when) options [DHO_DHCP_USER_CLASS_ID] -> tree = (struct tree *)0; } + /* Sanity check the lease time. */ + if ((lease->offered_expiry - cur_time) < 0) + putULong(lease_time_buf, packet->subnet->default_lease_time); + else if (lease -> offered_expiry - cur_time > + packet -> subnet -> max_lease_time) + putULong (lease_time_buf, packet -> subnet -> max_lease_time); + else + putULong(lease_time_buf, lease -> offered_expiry - cur_time); + putULong (lease_time_buf, lease -> offered_expiry - cur_time); options [DHO_DHCP_LEASE_TIME] = &lease_time_tree; options [DHO_DHCP_LEASE_TIME] -> value = lease_time_buf; @@ -508,7 +566,7 @@ void ack_lease (packet, lease, offer, when) /* Otherwise, broadcast it on the local network. */ } else { - to.sin_addr.s_addr = INADDR_BROADCAST; + memcpy (&to.sin_addr.s_addr, lease -> ip_addr.iabuf, 4); to.sin_port = htons (ntohs (server_port) + 1); /* XXX */ } @@ -518,14 +576,18 @@ void ack_lease (packet, lease, offer, when) #endif memset (to.sin_zero, 0, sizeof to.sin_zero); - note ("Sending dhcp reply to %s, port %d", + note ("Sending %s to %s at IP address %s", + offer == DHCPACK ? "DHCPACK" : "DHCPOFFER", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), inet_ntoa (to.sin_addr), htons (to.sin_port)); errno = 0; - result = sendto (packet -> client_sock, &raw, outgoing.packet_length, - 0, (struct sockaddr *)&to, sizeof to); + result = sendpkt (packet, &raw, outgoing.packet_length, + (struct sockaddr *) &to, sizeof(to)); if (result < 0) - warn ("sendto: %m"); + warn ("sendpkt: %m"); #ifdef DEBUG dump_packet (packet); diff --git a/server/dhcpd.c b/server/dhcpd.c index a3ddde93..97856c36 100644 --- a/server/dhcpd.c +++ b/server/dhcpd.c @@ -179,9 +179,9 @@ int main (argc, argv, envp) } /* Write a pid file. */ - if ((i = open (_PATH_DHCPD_PID, O_WRONLY | O_CREAT)) >= 0) { + if ((i = open (_PATH_DHCPD_PID, O_WRONLY | O_CREAT, 0640)) >= 0) { char obuf [20]; - sprintf (obuf, "%d\n", getpid ()); + sprintf (obuf, "%d\n", (int)getpid ()); write (i, obuf, strlen (obuf)); close (i); } diff --git a/socket.c b/socket.c index 182398cf..1c5aafca 100644 --- a/socket.c +++ b/socket.c @@ -48,6 +48,27 @@ static char copyright[] = #include "dhcpd.h" #include +#ifdef USE_BPF + +#include +#include +#include +#include +#include +#include +#include +#include + +struct interface { + struct in_addr address; + int bpf; +}; + +static struct interface *if_list; +static int num_ifaces; + +#endif + /* List of sockets we're accepting packets on... */ struct socklist { struct socklist *next; @@ -86,17 +107,48 @@ u_int32_t *get_interface_list (count) second time to copy them into an array of addresses. */ for (i = 0; i < ic.ifc_len;) { struct ifreq *ifp = (struct ifreq *)((caddr_t)ic.ifc_req + i); +#ifdef HAVE_SIN_LEN i += (sizeof ifp -> ifr_name) + ifp -> ifr_addr.sa_len; +#else + i += sizeof *ifp; +#endif if (ifp -> ifr_addr.sa_family == AF_INET) { struct sockaddr_in *foo = (struct sockaddr_in *)(&ifp -> ifr_addr); /* We don't want the loopback interface. */ if (foo -> sin_addr.s_addr == INADDR_LOOPBACK) continue; - if (intbuf) - intbuf [ifix++] = foo -> sin_addr.s_addr; - else + if (intbuf) { + intbuf [ifix] = foo -> sin_addr.s_addr; +#ifdef USE_BPF + /* Open a bpf device for this interface */ + { + int b; + char filename[50]; + + for (b = 0; 1; b++) + { + snprintf(filename, sizeof(filename), + "/dev/bpf%d", b); + if ((if_list[ifix].bpf = + open(filename, O_RDWR, 0)) < 0) + if (errno == EBUSY) + continue; + else + error ("Can't find free bpf: %m"); + else + break; + } + if (ioctl(if_list[ifix].bpf, + BIOCSETIF, ifp) < 0) + error ("Can't BIOCSETIF on bpf: %m"); + } + if_list[ifix].address = foo->sin_addr; +#endif + ifix++; + } else { ++ifcount; + } } } /* If we haven't already filled our array, allocate it and go @@ -107,6 +159,13 @@ u_int32_t *get_interface_list (count) "get_interface_list"); if (!intbuf) return intbuf; +#ifdef USE_BPF + num_ifaces = ifcount; + if (!(if_list = (struct interface *)dmalloc + (num_ifaces * sizeof(*if_list), + "get_interface_list"))) + error ("Can't allocate memory for if_list"); +#endif goto again; } *count = ifcount; @@ -215,3 +274,198 @@ void dispatch () } while (1); } +#ifndef USE_BPF + +int sendpkt (packet, raw, len, to, tolen) + struct packet *packet; + struct dhcp_packet *raw; + size_t len; + struct sockaddr *to; + int tolen; +{ + return(sendto(packet->client_sock, raw, len, 0, to, tolen)); +} + +#else + +static void IpChecksum(struct ip *ip); +static void UdpChecksum(struct ip *ip); +static u_int32_t Checksum(u_int16_t *buf, int nwords); + +struct raw_packet +{ + u_int16_t space; + struct ether_header en_hdr; + struct ip ip; + struct udphdr udp; + struct dhcp_packet dhcp; +}; + +int sendpkt (in_packet, raw, len, to, tolen) + struct packet *in_packet; + struct dhcp_packet *raw; + size_t len; + struct sockaddr *to; + int tolen; +{ + int i, k; + struct iaddr dest; + struct subnet *subnet; + struct raw_packet out_packet; + struct raw_packet *const pkt = &out_packet; + +/* Find local subnet, or else forward to gateway */ + + dest.len = 4; + memcpy(&dest.iabuf, &((struct sockaddr_in *) to)->sin_addr, dest.len); + if ((subnet = find_subnet(dest)) == NULL) + return(sendto(in_packet->client_sock, raw, len, 0, to, tolen)); + +/* Find interface corresponding to subnet */ + + for (i = 0; i < num_ifaces; i++) + { + for (k = 0; k < subnet->net.len + && (dest.iabuf[k] & subnet->netmask.iabuf[k]) + == (subnet->net.iabuf[k] & subnet->netmask.iabuf[k]); + k++); + if (k == subnet->net.len) + break; + } + if (i == num_ifaces) + return(sendto(in_packet->client_sock, raw, len, 0, to, tolen)); + +/* EtherNet header */ + + memset(pkt->en_hdr.ether_dhost, 0xff, sizeof(pkt->en_hdr.ether_dhost)); + memset(pkt->en_hdr.ether_shost, 0x00, sizeof(pkt->en_hdr.ether_shost)); + pkt->en_hdr.ether_type = ETHERTYPE_IP; + +/* IP header (except for checksum) */ + + pkt->ip.ip_v = 4; + pkt->ip.ip_hl = 5; + pkt->ip.ip_tos = IPTOS_LOWDELAY; + pkt->ip.ip_len = htons(sizeof(pkt->ip) + sizeof(pkt->udp) + len); + pkt->ip.ip_id = 0; + pkt->ip.ip_off = 0; + pkt->ip.ip_ttl = 16; + pkt->ip.ip_p = IPPROTO_UDP; + pkt->ip.ip_sum = 0; + pkt->ip.ip_src = if_list[i].address; + inet_aton("255.255.255.255", &pkt->ip.ip_dst); + +/* UDP header */ + + pkt->udp.uh_sport = htons(67); /* XXX! */ + pkt->udp.uh_dport = in_packet->client_port; + pkt->udp.uh_ulen = htons(sizeof(pkt->udp) + len); + pkt->udp.uh_sum = 0; + +/* DHCP packet */ + + pkt->dhcp = *raw; + +/* Compute checksums */ + + UdpChecksum(&pkt->ip); + IpChecksum(&pkt->ip); + +/* Fire it off */ + + if (write(if_list[i].bpf, &pkt->en_hdr, + ntohs(pkt->ip.ip_len) + sizeof(pkt->en_hdr)) < 0) + warn ("Can't deliver packet: write: %m"); + return(0); +} + +/* + * UdpChecksum() + * + * Recompute a UDP checksum on a packet + * + * UDP pseudo-header (prot = IPPROTO_UDP = 17): + * + * | source IP address | + * | dest. IP address | + * | zero | prot | UDP leng | + * + */ + +static void +UdpChecksum(struct ip *ip) +{ + struct udphdr *udp = (struct udphdr *) ((long *) ip + ip->ip_hl); + u_int32_t sum; + +/* Pad with zero */ + + if (ntohs(udp->uh_ulen) & 0x1) + *((u_char *) udp + ntohs(udp->uh_ulen)) = 0; + +/* Do pseudo-header first */ + + sum = Checksum((u_int16_t *) &ip->ip_src, 4); + sum += (u_int16_t) IPPROTO_UDP; + sum += (u_int16_t) ntohs(udp->uh_ulen); + +/* Now do UDP packet itself */ + + udp->uh_sum = 0; + sum += Checksum((u_int16_t *) udp, + ((u_int16_t) ntohs(udp->uh_ulen) + 1) >> 1); + +/* Flip it & stick it */ + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + sum = ~sum; + + udp->uh_sum = htons(sum); +} + +/* + * IpChecksum() + * + * Recompute an IP header checksum + * + */ + +static void +IpChecksum(struct ip *ip) +{ + u_int32_t sum; + +/* Sum up IP header words */ + + ip->ip_sum = 0; + sum = Checksum((u_int16_t *) ip, ip->ip_hl * 2); + +/* Flip it & stick it */ + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + sum = ~sum; + + ip->ip_sum = htons(sum); +} + +/* + * Checksum() + * + * Do the one's complement sum thing over a range of words + * Ideally, this should get replaced by an assembly version. + */ + +static u_int32_t +Checksum(u_int16_t *buf, int nwords) +{ + u_int32_t sum = 0; + + while (nwords--) + sum += (u_int16_t) ntohs(*buf++); + return(sum); +} + +#endif +