From: Ted Lemon Date: Thu, 2 Jan 1997 12:00:19 +0000 (+0000) Subject: Initial merge of Poger dhclient and linux hacks X-Git-Tag: DHCP_970222~68 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=469cf3a4b58a0f0df84fc0fb56ad1cbaf2b2c67f;p=thirdparty%2Fdhcp.git Initial merge of Poger dhclient and linux hacks --- diff --git a/Makefile.dist b/Makefile.dist index 431fe06d8..8c1d61c24 100644 --- a/Makefile.dist +++ b/Makefile.dist @@ -185,10 +185,10 @@ VARDB = /var/db CSRC = options.c errwarn.c convert.c \ tree.c memory.c alloc.c print.c hash.c tables.c inet.c \ - dispatch.c bpf.c packet.c raw.c nit.c socket.c + dispatch.c bpf.c packet.c raw.c nit.c socket.c route.c COBJ = options.o errwarn.o convert.o \ tree.o memory.o alloc.o print.o hash.o tables.o inet.o \ - dispatch.o bpf.o packet.o raw.o nit.o socket.o + dispatch.o bpf.o packet.o raw.o nit.o socket.o route.o XOBJ = dhcpxlt.o xconflex.o SRCS = dhcpd.c dhcp.c bootp.c conflex.c confpars.c db.c OBJS = dhcpd.o dhcp.o bootp.o conflex.o confpars.o db.o diff --git a/bpf.c b/bpf.c index 1ef37958a..c0d802b8d 100644 --- a/bpf.c +++ b/bpf.c @@ -42,7 +42,7 @@ #ifndef lint static char copyright[] = -"$Id: bpf.c,v 1.13 1996/09/02 21:14:58 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: bpf.c,v 1.14 1997/01/02 12:00:14 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -368,3 +368,25 @@ size_t receive_packet (interface, buf, len, from, hfrom) return 0; } #endif + +#if defined (USE_BPF_SEND) +void if_enable (interface) + struct interface_info *interface; +{ + struct ifreq ifr; + int sock; + + if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + error ("Can't create addrlist socket"); + + /* Bring the interface down and then up again to clear + * all its routes. */ + strncpy(ifr.ifr_name, interface -> name, IFNAMSIZ); + if (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0) + error ("SIOCGIFFLAGS %s: %m", interface -> name); + + ifr.ifr_flags |= (IFF_UP|IFF_RUNNING); + if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) + error ("SIOCSIFFLAGS %s: %m", interface -> name); +} +#endif diff --git a/cf/linux.h b/cf/linux.h index 8e439d0b7..d08a986d0 100644 --- a/cf/linux.h +++ b/cf/linux.h @@ -60,6 +60,9 @@ typedef unsigned long u_int32_t; extern int h_errno; #include +#include +#include +#include #include /* gettimeofday()*/ #include /* also necessary */ diff --git a/cf/netbsd.h b/cf/netbsd.h index 23f0b30da..6e9ecc7b9 100644 --- a/cf/netbsd.h +++ b/cf/netbsd.h @@ -52,6 +52,10 @@ extern int h_errno; #include #include +#include +#include + +#define ifr_netmask ifr_addr /* Varargs stuff... */ #include diff --git a/client/dhclient.c b/client/dhclient.c index 8bc551ef9..a87773a53 100644 --- a/client/dhclient.c +++ b/client/dhclient.c @@ -1,6 +1,6 @@ -/* dhcp.c +/* dhclient.c - DHCP Client (really lame DHCP client). */ + DHCP Client (less lame DHCP client). */ /* * Copyright (c) 1995, 1996 The Internet Software Consortium. @@ -38,11 +38,14 @@ * Enterprises. To learn more about the Internet Software Consortium, * see ``http://www.vix.com/isc''. To learn more about Vixie * Enterprises, see ``http://www.vix.com''. + * + * This client was substantially modified and enhanced by Elliot Poger + * while he was working on the MosquitoNet project at Stanford. */ #ifndef lint static char copyright[] = -"$Id: dhclient.c,v 1.21 1996/09/11 18:53:32 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: dhclient.c,v 1.22 1997/01/02 12:00:14 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -56,6 +59,31 @@ struct iaddr server_identifier; int server_identifier_matched; int log_perror = 1; +const u_long broadcast_address = INADDR_BROADCAST; +const int max_retransmissions = 4; +const int min_delay = 4; + +/* Globals for the real_dhcp_client state machine. */ +TIME lease_expiry, T1_expiry, T2_expiry; +struct packet *sendpacket; +struct packet *recvpacket; +struct packet *leasepacket; +int sendpacket_flag, recvpacket_flag; +u_long destination; + +enum { + S_INIT, S_SELECTING, S_REQUESTING, + S_BOUND, S_RENEWING, S_REBINDING +} state; + +/* ASSERT_STATE() does nothing now; it used to be + * assert(state_is==state_shouldbe); */ +#define ASSERT_STATE(state_is, state_shouldbe) {} + +int retransmissions_left, retransmission_delay; +struct interface_info *dhclient_interface; + + #ifdef USE_FALLBACK struct interface_info fallback_interface; #endif @@ -99,13 +127,24 @@ int main (argc, argv, envp) server_port = htons (atoi (argv [i])); debug ("binding to user-specified port %d", ntohs (server_port)); - } else - usage (); + } else if (argv [i][0] == '-') { + usage (); + } else { + struct interface_info *tmp = + ((struct interface_info *) + dmalloc (sizeof *tmp, "specified_interface")); + if (!tmp) + error ("Insufficient memory to %s %s", + "record interface", argv [i]); + memset (tmp, 0, sizeof *tmp); + strcpy (tmp -> name, argv [i]); + tmp -> next = interfaces; + tmp -> flags = INTERFACE_REQUESTED; + interfaces = tmp; + } } - /* Default to the DHCP/BOOTP port. */ - if (!server_port) - { + if (!server_port) { ent = getservbyname ("dhcpc", "udp"); if (!ent) server_port = htons (68); @@ -120,25 +159,544 @@ int main (argc, argv, envp) /* Discover all the network interfaces and initialize them. */ discover_interfaces (0); - for (interface = interfaces; interface; interface = interface -> next) - send_discover (interface); - - /* Receive packets and dispatch them... */ - dispatch (); + /* This is a real DHCP client! + * SO, fork off a dhclient state machine for each + * interface. */ + for (dhclient_interface = interfaces; dhclient_interface; + dhclient_interface = dhclient_interface -> next) { + if (!dhclient_interface -> next || fork() == 0) { + srandom(cur_time + *(int *) + &dhclient_interface->hw_address.haddr); + dhclient_state_machine(); + dhclient_fail(); + } + } - /* Not reached */ return 0; } +/* This routine should be called when the DHCP client quits for any reason. + * It makes the interface unusable and outputs an error message before + * exiting. A warn() message should be given first for specific errors. */ + +void dhclient_fail() +{ + disable_interface(dhclient_interface); + error ("dhclient for '%s' halted", + dhclient_interface->name); +} + +/* Handles the KILL signal for this DHCP client process. + * Sends a DHCPRELEASE message to the server (to be a good neighbor) before + * calling failure routine. */ +void handle_kill(dummy) + int dummy; +{ + warn("dhclient has been killed!"); + /* XXX figure out whether the lease has expired... */ + if (leasepacket -> packet_type == DHCPACK) { + warn("sending DHCPRELEASE"); + send_release(leasepacket); + } + dhclient_fail (); +} + +/* Make this network interface unusable by most programs. This routine is + * called whenever dhclient discovers this host holds no lease. + * dhclient must still be able to use it, though, so we can get a lease!!! + * Just remove all the routes to this interface, and set its IP address to + * 0.0.0.0. dhclient will have to find some way to communicate with it. */ +void disable_interface(interface) + struct interface_info *interface; +{ + struct in_addr zero_addr; + + remove_all_if_routes (interface); + zero_addr.s_addr = 0; + set_ip_address (interface, zero_addr); +} + +void apply_parameters(interface, packet) + struct interface_info *interface; + struct packet *packet; +{ + struct in_addr net_addr, tmp; + + /* Eventually, set IP address, netmask, router, DNS servers, + * domain name, broadcast addr, etc. + * + * For now, set IP addr, netmask, and broadcast addr, add gateway, + * and update routes. + * Call OS-dependent routines to do the dirty work. + */ + + note("Setting parameters for %s interface...", interface->name); + + remove_all_if_routes (interface); + set_ip_address (interface, packet -> raw -> yiaddr); + + memcpy (&tmp, packet->options[DHO_SUBNET_MASK].data, sizeof tmp); + set_netmask (interface, tmp); + + memcpy (&tmp, packet->options[DHO_BROADCAST_ADDRESS].data, sizeof tmp); + set_broadcast_addr(interface, tmp); + + /* network addr = (my IP addr) AND (netmask) */ + memcpy (&tmp, packet -> options[DHO_SUBNET_MASK].data, sizeof tmp); + net_addr.s_addr = packet -> raw -> yiaddr.s_addr & tmp.s_addr; + memcpy (&tmp, packet->options[DHO_SUBNET_MASK].data, sizeof tmp); + add_route_net(interface, net_addr, tmp); + + memcpy (&tmp, packet->options[DHO_ROUTERS].data, sizeof tmp); + add_route_default_gateway(interface, tmp); +} + static void usage () { - error ("Usage: dhclient [-p ]"); + error ("Usage: dhclient [-c] [-p ] [interface]"); } void cleanup () { } +/* The state machine for a true DHCP client, on one particular interface + * (stored in global dhclient_interface). + * This function should not be exited except on a fatal error. */ +void dhclient_state_machine () +{ + fd_set read_fd; + struct timeval tv, timeout; + int reset_timer_flag; + long int rnd; + + recvpacket = new_packet("recvpacket"); + recvpacket->raw = new_dhcp_packet("recvpacket->raw"); + sendpacket = new_packet("sendpacket"); + sendpacket->raw = new_dhcp_packet("sendpacket->raw"); + leasepacket = new_packet("leasepacket"); + leasepacket->raw = new_dhcp_packet("leasepacket->raw"); + + /* Figure out when we time out... */ + gettimeofday (&timeout, (struct timezone *)0); + timeout.tv_sec++; + + state = S_INIT; + sendpacket_flag = 0; + recvpacket_flag = 0; + retransmissions_left = 0; + + signal(SIGTERM, handle_kill); + signal(SIGINT, handle_kill); + while (1) { + /* We have awakened for some reason. Possible reasons are: + * -> just entered INIT state (special case) + * -> packet received on this port (recvbuffer != NULL) + * -> timeout (we may want to retransmit a request) + */ + GET_TIME (&cur_time); + sendpacket_flag = 0; + + if (state == S_INIT) { + reset_timer_flag = state_init(); + } else if ( (!recvpacket_flag) && (retransmissions_left>0) ) { + /* Timed out; retransmit. */ + retransmissions_left--; + retransmission_delay = retransmission_delay*2; + reset_timer_flag = 1; + sendpacket_flag = 1; + } else { + /* Either a packet was received + * or we timed out and ran out of retransmissions. + */ + if (recvpacket_flag) { + read_packet(dhclient_interface, recvpacket); + } + /* If a packet was received, first check to make + * sure it's relevant to our last request; if it's + * not, ignore it and go back to sleep. + */ + if ((recvpacket_flag) && + ((!recvpacket->options_valid) || + (recvpacket->raw->op != BOOTREPLY) || + (recvpacket->raw->xid!=sendpacket->raw->xid) ) ) { + reset_timer_flag = 0; + } else { + note ("%s for %s", + (recvpacket -> packet_type == 0 + ? "BOOTREPLY" : + (recvpacket -> packet_type == DHCPOFFER + ? "DHCPOFFER" : + (recvpacket -> packet_type == DHCPACK + ? "DHCPACK" : + (recvpacket -> packet_type == DHCPNAK + ? "DHCPNAK" : "DHCP Unknown")))), + inet_ntoa (recvpacket -> raw -> yiaddr)); + + /* Call the appropriate routine for this state. + * This routine handles both timeout + * and packet-received cases. + * + * The routine returns 1 if the select() + * timer should be reset (which is ALWAYS the + * case when sending a new packet). + * Otherwise, we just continue our last select. + */ + switch (state) { + case S_SELECTING: + reset_timer_flag = state_selecting(); + break; + case S_REQUESTING: + reset_timer_flag = state_requesting(); + break; + case S_BOUND: + reset_timer_flag = state_bound(); + break; + case S_RENEWING: + reset_timer_flag = state_renewing(); + break; + case S_REBINDING: + reset_timer_flag = state_rebinding(); + break; + default: + warn("dhclient entered bad state"); + dhclient_fail(); + } + } + } + + /* Send a packet and reset the timer OR + * set a new timer with no outstanding request OR + * just go back to sleep because we're still waiting for + * something. */ + if (reset_timer_flag) { + if (sendpacket_flag) { + send_packet_struct(dhclient_interface, + destination, sendpacket); + } + rnd = random(); + tv.tv_sec = retransmission_delay - 1 + (rnd&1); + tv.tv_usec = rnd % 1000000; + gettimeofday (&timeout, (struct timezone *)0); + timeout.tv_usec += tv.tv_usec; + if (timeout.tv_usec > 1000000) { + timeout.tv_usec -= 1000000; + timeout.tv_sec++; + } + timeout.tv_sec += tv.tv_sec; + } else { + gettimeofday (&tv, (struct timezone *)0); + tv.tv_usec = timeout.tv_usec - tv.tv_usec; + if (tv.tv_usec < 0) { + tv.tv_usec += 1000000; + tv.tv_sec--; + } + tv.tv_sec = timeout.tv_sec - tv.tv_sec; + if (tv.tv_sec < 0) { + warn ("timer expired unexpectedly!"); + dhclient_fail (); + } + } + + FD_ZERO (&read_fd); + FD_SET (dhclient_interface->rfdesc, &read_fd); + recvpacket_flag = select(dhclient_interface->rfdesc+1, + &read_fd, NULL, NULL, &tv); + if (recvpacket_flag < 0) { + warn ("select: %m"); + dhclient_fail(); + } + } + /* keep cycling through the state machine until dhclient_fail() */ +} + +/* Individual States: + * + * Each routine is called from the dhclient_state_machine() in one of + * these conditions: + * -> entering INIT state + * -> recvpacket_flag == 0: timeout in this state + * -> otherwise: received a packet in this state + * + * Return conditions as handled by dhclient_state_machine(): + * Returns 1, sendpacket_flag = 1: send packet, reset timer. + * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone). + * Returns 0: finish the nap which was interrupted for no good reason. + * + * Several global variables are used to keep track of the process: + * recvpacket: most recent packet received + * sendpacket: most recent packet sent or to be sent + * leasepacket: copy of most recent valid lease DHCPACK packet + * recvpacket_flag: recvpacket is not old hat + * sendpacket_flag: sendpacket is to be sent + * destination: IP address to send sendpacket to + * retransmissions_left: # of times remaining to resend this sendpacket + * retransmission_delay: # of seconds to wait before next retransmission + * T1_expiry, T2_expiry, lease_expiry: lease milestones + */ + +int state_init() +{ + ASSERT_STATE(state, S_INIT); + + /* Don't let anyone else use this interface until we get a lease, + * but make sure we can use it to obtain a lease! */ + disable_interface(dhclient_interface); + if_enable (dhclient_interface); + + make_discover(dhclient_interface, sendpacket); + sendpacket_flag = 1; + retransmissions_left = max_retransmissions; + retransmission_delay = min_delay; + destination = broadcast_address; + state = S_SELECTING; + return 1; +} + +int state_selecting() +{ + ASSERT_STATE(state, S_SELECTING); + + if (!recvpacket_flag) { + warn("no DHCP server found"); + dhclient_fail(); + /*NOTREACHED*/ + return 0; + } else if (recvpacket->packet_type != DHCPOFFER) { + return 0; /* wait for a DHCPOFFER */ + } else { + /* Got a DHCPOFFER! */ + make_request(recvpacket, sendpacket); + sendpacket->raw->xid = random(); + sendpacket_flag = 1; + destination = broadcast_address; + retransmissions_left = max_retransmissions; + retransmission_delay = min_delay; + state = S_REQUESTING; + return 1; + } +} + +int state_requesting() +{ + ASSERT_STATE(state, S_REQUESTING); + + if (!recvpacket_flag) { + warn("lost contact with DHCP server"); + state = S_INIT; + return state_init(); + } else if (recvpacket->packet_type == DHCPNAK) { + warn("DHCPOFFER withdrawn"); + state = S_INIT; + return state_init(); + } else if (recvpacket->packet_type == DHCPACK) { + /* We got a lease! Apply its parameters, + * record lease info, and set timer to T1. */ + deep_packet_copy(leasepacket, recvpacket); + apply_parameters(dhclient_interface, leasepacket); + lease_expiry = abs_time(leasepacket, DHO_DHCP_LEASE_TIME); + T1_expiry = abs_time(leasepacket, DHO_DHCP_RENEWAL_TIME); + T2_expiry = abs_time(leasepacket, DHO_DHCP_REBINDING_TIME); + retransmissions_left = 0; + retransmission_delay = T1_expiry - cur_time; + sendpacket_flag = 0; /* just set the timer, that's all */ + state = S_BOUND; + return 1; + } else { + /* Ignore some superfluous packet. */ + return 0; + } +} + +int state_bound() +{ + ASSERT_STATE(state, S_BOUND); + + if (!recvpacket_flag) { + /* T1 has expired. */ + make_request(leasepacket, sendpacket); + sendpacket_flag = 1; + /* XXX: should really unicast here, but to whom? */ + destination = broadcast_address; + /* XXX: SHOULD WE DO RETRANSMISSIONS HERE? */ + retransmissions_left = 0; + retransmission_delay = T2_expiry - cur_time; + state = S_RENEWING; + return 1; + } else { + /* Ignore some superfluous packet. */ + return 0; + } +} + +int state_renewing() +{ + ASSERT_STATE(state, S_RENEWING); + + if (!recvpacket_flag) { + /* T2 has expired. */ + debug("T2 expired"); + sendpacket_flag = 1; + destination = broadcast_address; + /* XXX: SHOULD WE DO RETRANSMISSIONS HERE? */ + retransmissions_left = 0; + retransmission_delay = lease_expiry - cur_time; + state = S_REBINDING; + return 1; + } else if (recvpacket->packet_type == DHCPNAK) { + warn("lease renewal denied!"); + state = S_INIT; + return state_init(); + } else if (recvpacket->packet_type == DHCPACK) { + /* Record new lease info, and set timer to T1. */ + debug("lease renewed"); + deep_packet_copy(leasepacket, recvpacket); + lease_expiry = abs_time(leasepacket, DHO_DHCP_LEASE_TIME); + T1_expiry = abs_time(leasepacket, DHO_DHCP_RENEWAL_TIME); + T2_expiry = abs_time(leasepacket, DHO_DHCP_REBINDING_TIME); + retransmissions_left = 0; + retransmission_delay = T1_expiry - cur_time; + sendpacket_flag = 0; /* just set the timer, that's all */ + state = S_BOUND; + return 1; + } else { + /* Ignore some superfluous packet. */ + return 0; + } +} + +int state_rebinding() +{ + ASSERT_STATE(state, S_REBINDING); + + if (!recvpacket_flag) { + /* Lease has expired! */ + warn("DHCP lease expired!"); + state = S_INIT; + return state_init(); + } else if (recvpacket->packet_type == DHCPNAK) { + warn("lease rebinding denied!"); + state = S_INIT; + return state_init(); + } else if (recvpacket->packet_type == DHCPACK) { + /* Record new lease info, and set timer to T1. */ + debug("lease rebound"); + deep_packet_copy(leasepacket, recvpacket); + lease_expiry = abs_time(leasepacket, DHO_DHCP_LEASE_TIME); + T1_expiry = abs_time(leasepacket, DHO_DHCP_RENEWAL_TIME); + T2_expiry = abs_time(leasepacket, DHO_DHCP_REBINDING_TIME); + retransmissions_left = 0; + retransmission_delay = T1_expiry - cur_time; + sendpacket_flag = 0; /* just set the timer, that's all */ + state = S_BOUND; + return 1; + } else { + /* Ignore some superfluous packet. */ + return 0; + } +} + +/* Read the time offset from a given DHCP option and return it as an + * absolute moment in time (offset from cur_time). */ +TIME abs_time(pkt, optnum) + struct packet *pkt; + int optnum; +{ + if (pkt == NULL) { + error ("Bad parameter passed to abs_time"); + } + + return cur_time + + (TIME) ntohl(*((u_int32_t *)pkt->options[optnum].data)); +} + +/* This little number lets you record an interesting packet's contents in + * another (pre-allocated) packet struct. */ +/* XXX: what about haddr? */ +void deep_packet_copy(to, from) + struct packet *to; + struct packet *from; +{ + struct dhcp_packet *raw; + + if ((to==NULL) || (to->raw==NULL)) { + error("Bad parameter passed to deep_packet_copy"); + } + + raw = to->raw; + memcpy (to->raw, from->raw, sizeof *from->raw); + memcpy (to, from, sizeof *from); + to->raw = raw; +} + + +/* Read a DHCP packet from this interface into this (pre-allocated) packet. + * If the data on this interface is bogus (non-DHCP), + * set pkt->options_valid = 0. + * Much of this code is stolen from dispatch.c. + */ +void read_packet(interface, pkt) + struct interface_info *interface; + struct packet *pkt; +{ + struct dhcp_packet *dhcp_pkt; + struct sockaddr_in from; + struct hardware *hfrom; + struct hardware fudge_factor; /* XXX */ + struct iaddr ifrom; + int len; + static unsigned char packbuf [4095]; /* Packet input buffer. + Must be as large as largest + possible MTU. */ + + if (pkt==NULL) { + error("Bad parameter passed to read_packet"); + } + pkt->haddr = &fudge_factor; + if ((pkt->haddr==NULL) || (pkt->raw==NULL)) { + error("Bad parameter passed to read_packet"); + } + + hfrom = pkt->haddr; + if ((len = receive_packet (interface, packbuf, sizeof packbuf, + &from, hfrom)) < 0) { + warn ("receive_packet failed on %s: %m", interface -> name); + pkt->options_valid = 0; + return; + } + if (len == 0) { + pkt->options_valid = 0; + return; + } + + ifrom.len = 4; + memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len); + + dhcp_pkt = pkt->raw; + memcpy (dhcp_pkt, packbuf, len); + memset (pkt, 0, sizeof (struct packet)); + pkt->raw = dhcp_pkt; + pkt->packet_length = len; + pkt->client_port = from.sin_port; + pkt->client_addr = ifrom; + pkt->interface = interface; + pkt->haddr = hfrom; + + parse_options (pkt); + if (pkt->options_valid && + pkt->options [DHO_DHCP_MESSAGE_TYPE].data) { + pkt->packet_type = + pkt->options [DHO_DHCP_MESSAGE_TYPE].data [0]; + } else { + pkt->options_valid = 0; + } + pkt->haddr = NULL; /* XXX */ + +#ifdef DEBUG + dump_packet(pkt); +#endif +} + int commit_leases () { return 0; @@ -193,6 +751,7 @@ void dhcpoffer (packet) packet -> raw -> chaddr)); dump_packet (packet); + note ("DHCPREQUEST to %s", packet -> interface -> name); send_request (packet); } @@ -215,34 +774,109 @@ void dhcpnak (packet) packet -> raw -> chaddr)); } -static u_int8_t requested_options [] = { - DHO_DHCP_REQUESTED_ADDRESS, - DHO_SUBNET_MASK, - DHO_ROUTERS, - DHO_DOMAIN_NAME_SERVERS, - DHO_HOST_NAME, - DHO_DOMAIN_NAME, - DHO_BROADCAST_ADDRESS }; - void send_discover (interface) struct interface_info *interface; +{ + struct packet outgoing; + struct dhcp_packet raw; + + outgoing.raw = &raw; + make_discover (interface, &outgoing); + send_packet_struct (interface, htonl(INADDR_BROADCAST), &outgoing); +} + +void send_request (packet) + struct packet *packet; /* incoming packet we're responding to */ +{ + struct packet outgoing; + struct dhcp_packet raw; + + outgoing.raw = &raw; + make_request (packet, &outgoing); + send_packet_struct (packet->interface, htonl(INADDR_BROADCAST), + &outgoing); +} + +void send_release (packet) + struct packet *packet; /* DHCPACK packet from lease we're releasing */ +{ + struct packet outgoing; + struct dhcp_packet raw; + + outgoing.raw = &raw; + make_release (packet, &outgoing); + send_packet_struct (packet->interface, htonl(INADDR_BROADCAST), + &outgoing); +} + +/* Send the given packet to the given host IPaddr, over the given interface. */ +void send_packet_struct (interface, dest_host, sendpkt) + struct interface_info *interface; + u_long dest_host; /* in network order! */ + struct packet *sendpkt; { struct sockaddr_in to; int result; - struct dhcp_packet raw; + char dhcpbuf [128]; + + /* Set up the common stuff... */ + to.sin_family = AF_INET; +#ifdef HAVE_SA_LEN + to.sin_len = sizeof to; +#endif + memset (to.sin_zero, 0, sizeof to.sin_zero); + + to.sin_addr.s_addr = dest_host; + to.sin_port = htons (ntohs (server_port) - 1); /* XXX */ + + sprintf (dhcpbuf, "DHCP Unknown %d", sendpkt -> packet_type); + note ("%s to %s", + sendpkt -> packet_type == DHCPDISCOVER ? "DHCPDISCOVER" + : (sendpkt -> packet_type == DHCPREQUEST ? "DHCPREQUEST" + : (sendpkt -> packet_type == DHCPNAK ? "DHCPNAK" + : (sendpkt -> packet_type == DHCPINFORM ? "DHCPINFORM" + : dhcpbuf))), + inet_ntoa (to.sin_addr)); + + if (sendpkt->packet_length < DHCP_MIN_LEN) + sendpkt->packet_length = DHCP_MIN_LEN; + + errno = 0; + result = send_packet (interface, (struct packet *)0, + sendpkt->raw, sendpkt->packet_length, + sendpkt->raw->siaddr, &to, (struct hardware *)0); + /* XXX: is the above 'siaddr' correct? */ + if (result < 0) + warn ("send_packet: %m"); +} + +void make_discover (interface, sendpkt) + struct interface_info *interface; + struct packet *sendpkt; +{ + struct dhcp_packet *raw; unsigned char discover = DHCPDISCOVER; - struct packet outgoing; struct tree_cache *options [256]; struct tree_cache dhcpdiscover_tree; struct tree_cache dhcprqo_tree; + u_int8_t requested_options [] = { + DHO_SUBNET_MASK, + DHO_ROUTERS, + DHO_DOMAIN_NAME_SERVERS, + DHO_HOST_NAME, + DHO_DOMAIN_NAME, + DHO_BROADCAST_ADDRESS }; + + raw = sendpkt->raw; memset (options, 0, sizeof options); - memset (&outgoing, 0, sizeof outgoing); - memset (&raw, 0, sizeof raw); - outgoing.raw = &raw; + memset (sendpkt, 0, sizeof (*sendpkt)); + memset (raw, 0, sizeof (*raw)); + sendpkt->raw = raw; + sendpkt -> packet_type = DHCPDISCOVER; - /* Set DHCP_MESSAGE_TYPE to DHCPNAK */ + /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */ options [DHO_DHCP_MESSAGE_TYPE] = &dhcpdiscover_tree; options [DHO_DHCP_MESSAGE_TYPE] -> value = &discover; options [DHO_DHCP_MESSAGE_TYPE] -> len = sizeof discover; @@ -250,65 +884,44 @@ void send_discover (interface) options [DHO_DHCP_MESSAGE_TYPE] -> timeout = 0xFFFFFFFF; options [DHO_DHCP_MESSAGE_TYPE] -> tree = (struct tree *)0; - /* Set DHCP_MESSAGE to whatever the message is */ - options [DHO_DHCP_MESSAGE] = &dhcprqo_tree; - options [DHO_DHCP_MESSAGE] -> value = requested_options; - options [DHO_DHCP_MESSAGE] -> len = sizeof requested_options; - options [DHO_DHCP_MESSAGE] -> buf_size = sizeof requested_options; - options [DHO_DHCP_MESSAGE] -> timeout = 0xFFFFFFFF; - options [DHO_DHCP_MESSAGE] -> tree = (struct tree *)0; + /* Request the parameters we want */ + options [DHO_DHCP_PARAMETER_REQUEST_LIST] = &dhcprqo_tree; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> value = requested_options; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> len = sizeof requested_options; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> buf_size = sizeof requested_options; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> timeout = 0xFFFFFFFF; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> tree = (struct tree *)0; /* Set up the option buffer... */ - cons_options ((struct packet *)0, &outgoing, options, 0, 0); + cons_options ((struct packet *)0, sendpkt, options, 0, 0); - memset (&raw.ciaddr, 0, sizeof raw.ciaddr); - memset (&raw.siaddr, 0, sizeof raw.siaddr); - memset (&raw.giaddr, 0, sizeof raw.giaddr); - memcpy (raw.chaddr, + raw->op = BOOTREQUEST; + raw->htype = interface -> hw_address.htype; + raw->hlen = interface -> hw_address.hlen; + raw->hops = 0; + raw->xid = random (); + raw->secs = 0; /* XXX */ + raw->flags = htons (BOOTP_BROADCAST); + memset (&(raw->ciaddr), 0, sizeof raw->ciaddr); + memset (&(raw->yiaddr), 0, sizeof raw->yiaddr); + memset (&(raw->siaddr), 0, sizeof raw->siaddr); + memset (&(raw->giaddr), 0, sizeof raw->giaddr); + memcpy (raw->chaddr, interface -> hw_address.haddr, interface -> hw_address.hlen); - raw.hlen = interface -> hw_address.hlen; - raw.htype = interface -> hw_address.htype; - - raw.xid = random (); - raw.secs = 1; - raw.flags = htons (BOOTP_BROADCAST); - raw.hops = 0; - raw.op = BOOTREQUEST; - - /* Report what we're sending... */ - note ("DHCPDISCOVER to %s", interface -> name); #ifdef DEBUG_PACKET - dump_packet (&outgoing); - dump_raw ((unsigned char *)&raw, outgoing.packet_length); + dump_packet (sendpkt); + dump_raw ((unsigned char *)raw, sendpkt->packet_length); #endif - - /* Set up the common stuff... */ - to.sin_family = AF_INET; -#ifdef HAVE_SA_LEN - to.sin_len = sizeof to; -#endif - memset (to.sin_zero, 0, sizeof to.sin_zero); - - to.sin_addr.s_addr = htonl (INADDR_BROADCAST); - to.sin_port = htons (ntohs (server_port) - 1); /* XXX */ - - errno = 0; - result = send_packet (interface, (struct packet *)0, - &raw, outgoing.packet_length, - raw.siaddr, &to, (struct hardware *)0); - if (result < 0) - warn ("send_packet: %m"); } -void send_request (packet) - struct packet *packet; + +void make_request (recvpkt, sendpkt) + struct packet *recvpkt; + struct packet *sendpkt; { - struct sockaddr_in to; - int result; - struct dhcp_packet raw; + struct dhcp_packet *raw; unsigned char request = DHCPREQUEST; - struct packet outgoing; struct tree_cache *options [256]; struct tree_cache dhcprequest_tree; @@ -316,12 +929,23 @@ void send_request (packet) struct tree_cache dhcprqa_tree; struct tree_cache dhcpsid_tree; + u_int8_t requested_options [] = { + DHO_SUBNET_MASK, + DHO_ROUTERS, + DHO_DOMAIN_NAME_SERVERS, + DHO_HOST_NAME, + DHO_DOMAIN_NAME, + DHO_BROADCAST_ADDRESS }; + + raw = sendpkt->raw; memset (options, 0, sizeof options); - memset (&outgoing, 0, sizeof outgoing); - memset (&raw, 0, sizeof raw); - outgoing.raw = &raw; + memset (sendpkt, 0, sizeof (*sendpkt)); + memset (raw, 0, sizeof (*raw)); + sendpkt->raw = raw; - /* Set DHCP_MESSAGE_TYPE to DHCPNAK */ + sendpkt -> packet_type = DHCPREQUEST; + + /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */ options [DHO_DHCP_MESSAGE_TYPE] = &dhcprequest_tree; options [DHO_DHCP_MESSAGE_TYPE] -> value = &request; options [DHO_DHCP_MESSAGE_TYPE] -> len = sizeof request; @@ -329,74 +953,113 @@ void send_request (packet) options [DHO_DHCP_MESSAGE_TYPE] -> timeout = 0xFFFFFFFF; options [DHO_DHCP_MESSAGE_TYPE] -> tree = (struct tree *)0; - /* Set DHCP_MESSAGE to whatever the message is */ - options [DHO_DHCP_MESSAGE] = &dhcprqo_tree; - options [DHO_DHCP_MESSAGE] -> value = requested_options; - options [DHO_DHCP_MESSAGE] -> len = sizeof requested_options; - options [DHO_DHCP_MESSAGE] -> buf_size = sizeof requested_options; - options [DHO_DHCP_MESSAGE] -> timeout = 0xFFFFFFFF; - options [DHO_DHCP_MESSAGE] -> tree = (struct tree *)0; - - /* Request the address we were offered... */ - options [DHO_DHCP_REQUESTED_ADDRESS] = &dhcprqa_tree; - options [DHO_DHCP_REQUESTED_ADDRESS] -> value = - (unsigned char *)&packet -> raw -> yiaddr; - options [DHO_DHCP_REQUESTED_ADDRESS] -> len = 4; - options [DHO_DHCP_REQUESTED_ADDRESS] -> buf_size = 4; - options [DHO_DHCP_REQUESTED_ADDRESS] -> timeout = 0xFFFFFFFF; - options [DHO_DHCP_REQUESTED_ADDRESS] -> tree = (struct tree *)0; + /* Request the parameters we want */ + options [DHO_DHCP_PARAMETER_REQUEST_LIST] = &dhcprqo_tree; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> value = requested_options; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> len = sizeof requested_options; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> buf_size = sizeof requested_options; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> timeout = 0xFFFFFFFF; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> tree = (struct tree *)0; /* Send back the server identifier... */ options [DHO_DHCP_SERVER_IDENTIFIER] = &dhcpsid_tree; options [DHO_DHCP_SERVER_IDENTIFIER] -> value = - packet -> options [DHO_DHCP_SERVER_IDENTIFIER].data; + recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].data; options [DHO_DHCP_SERVER_IDENTIFIER] -> len = - packet -> options [DHO_DHCP_SERVER_IDENTIFIER].len; + recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len; options [DHO_DHCP_SERVER_IDENTIFIER] -> buf_size = - packet -> options [DHO_DHCP_SERVER_IDENTIFIER].len; + recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len; options [DHO_DHCP_SERVER_IDENTIFIER] -> timeout = 0xFFFFFFFF; options [DHO_DHCP_SERVER_IDENTIFIER] -> tree = (struct tree *)0; /* Set up the option buffer... */ - cons_options ((struct packet *)0, &outgoing, options, 0, 0); - - memset (&raw.ciaddr, 0, sizeof raw.ciaddr); - raw.siaddr = packet -> raw -> siaddr; - raw.giaddr = packet -> raw -> giaddr; - memcpy (raw.chaddr, - packet -> interface -> hw_address.haddr, - packet -> interface -> hw_address.hlen); - raw.hlen = packet -> interface -> hw_address.hlen; - raw.htype = packet -> interface -> hw_address.htype; - - raw.xid = packet -> raw -> xid; - raw.secs = packet -> raw -> secs; - raw.flags = htons (BOOTP_BROADCAST); - raw.hops = packet -> raw -> hops; - raw.op = BOOTREQUEST; - - /* Report what we're sending... */ - note ("DHCPREQUEST to %s", packet -> interface -> name); + cons_options ((struct packet *)0, sendpkt, options, 0, 0); + + raw->op = BOOTREQUEST; + raw->htype = recvpkt -> interface -> hw_address.htype; + raw->hlen = recvpkt -> interface -> hw_address.hlen; + raw->hops = 0; + raw->xid = recvpkt -> raw -> xid; + raw->secs = recvpkt -> raw -> secs; /* XXX */ + raw->flags = htons (BOOTP_BROADCAST); + raw->ciaddr = recvpkt -> raw -> yiaddr; /* XXX ????? */ + memset (&raw->yiaddr, 0, sizeof raw->yiaddr); + memset (&raw->siaddr, 0, sizeof raw->siaddr); + memset (&raw->giaddr, 0, sizeof raw->giaddr); + memcpy (raw->chaddr, + recvpkt -> interface -> hw_address.haddr, + recvpkt -> interface -> hw_address.hlen); #ifdef DEBUG_PACKET - dump_packet (&outgoing); - dump_raw ((unsigned char *)&raw, outgoing.packet_length); + dump_packet (sendpkt); + dump_raw ((unsigned char *)raw, sendpkt->packet_length); #endif +} - /* Set up the common stuff... */ - to.sin_family = AF_INET; -#ifdef HAVE_SA_LEN - to.sin_len = sizeof to; -#endif - memset (to.sin_zero, 0, sizeof to.sin_zero); +void make_release (recvpkt, sendpkt) + struct packet *recvpkt; + struct packet *sendpkt; +{ + struct dhcp_packet *raw; + unsigned char request = DHCPRELEASE; - to.sin_addr.s_addr = htonl (INADDR_BROADCAST); - to.sin_port = htons (ntohs (server_port) - 1); /* XXX */ + struct tree_cache *options [256]; + struct tree_cache dhcprequest_tree; + struct tree_cache dhcprqo_tree; + struct tree_cache dhcprqa_tree; + struct tree_cache dhcpsid_tree; - errno = 0; - result = send_packet (packet -> interface, (struct packet *)0, - &raw, outgoing.packet_length, - raw.siaddr, &to, (struct hardware *)0); - if (result < 0) - warn ("send_packet: %m"); + raw = sendpkt->raw; + memset (options, 0, sizeof options); + memset (sendpkt, 0, sizeof (*sendpkt)); + memset (raw, 0, sizeof (*raw)); + sendpkt->raw = raw; + + sendpkt -> packet_type = DHCPRELEASE; + + /* Set DHCP_MESSAGE_TYPE to DHCPRELEASE */ + options [DHO_DHCP_MESSAGE_TYPE] = &dhcprequest_tree; + options [DHO_DHCP_MESSAGE_TYPE] -> value = &request; + options [DHO_DHCP_MESSAGE_TYPE] -> len = sizeof request; + options [DHO_DHCP_MESSAGE_TYPE] -> buf_size = sizeof request; + options [DHO_DHCP_MESSAGE_TYPE] -> timeout = 0xFFFFFFFF; + options [DHO_DHCP_MESSAGE_TYPE] -> tree = (struct tree *)0; + + /* Send back the server identifier... */ + options [DHO_DHCP_SERVER_IDENTIFIER] = &dhcpsid_tree; + options [DHO_DHCP_SERVER_IDENTIFIER] -> value = + recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].data; + options [DHO_DHCP_SERVER_IDENTIFIER] -> len = + recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len; + options [DHO_DHCP_SERVER_IDENTIFIER] -> buf_size = + recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len; + options [DHO_DHCP_SERVER_IDENTIFIER] -> timeout = 0xFFFFFFFF; + options [DHO_DHCP_SERVER_IDENTIFIER] -> tree = (struct tree *)0; + + /* Set DHCP_MESSAGE to whatever the message is */ + /* XXX */ + + /* Set up the option buffer... */ + cons_options ((struct packet *)0, sendpkt, options, 0, 0); + + raw->op = BOOTREQUEST; + raw->htype = recvpkt -> interface -> hw_address.htype; + raw->hlen = recvpkt -> interface -> hw_address.hlen; + raw->hops = 0; + raw->xid = recvpkt -> raw -> xid; + raw->secs = 0; + raw->flags = 0; + raw->ciaddr = recvpkt -> raw -> yiaddr; + memset (&raw->yiaddr, 0, sizeof raw->yiaddr); + memset (&raw->siaddr, 0, sizeof raw->siaddr); + memset (&raw->giaddr, 0, sizeof raw->giaddr); + memcpy (raw->chaddr, + recvpkt -> interface -> hw_address.haddr, + recvpkt -> interface -> hw_address.hlen); + + +#ifdef DEBUG_PACKET + dump_packet (sendpkt); + dump_raw ((unsigned char *)raw, sendpkt->packet_length); +#endif } diff --git a/common/bpf.c b/common/bpf.c index 1ef37958a..c0d802b8d 100644 --- a/common/bpf.c +++ b/common/bpf.c @@ -42,7 +42,7 @@ #ifndef lint static char copyright[] = -"$Id: bpf.c,v 1.13 1996/09/02 21:14:58 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: bpf.c,v 1.14 1997/01/02 12:00:14 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -368,3 +368,25 @@ size_t receive_packet (interface, buf, len, from, hfrom) return 0; } #endif + +#if defined (USE_BPF_SEND) +void if_enable (interface) + struct interface_info *interface; +{ + struct ifreq ifr; + int sock; + + if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + error ("Can't create addrlist socket"); + + /* Bring the interface down and then up again to clear + * all its routes. */ + strncpy(ifr.ifr_name, interface -> name, IFNAMSIZ); + if (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0) + error ("SIOCGIFFLAGS %s: %m", interface -> name); + + ifr.ifr_flags |= (IFF_UP|IFF_RUNNING); + if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) + error ("SIOCSIFFLAGS %s: %m", interface -> name); +} +#endif diff --git a/common/dispatch.c b/common/dispatch.c index 1985fb4e9..929c8b3d4 100644 --- a/common/dispatch.c +++ b/common/dispatch.c @@ -42,7 +42,7 @@ #ifndef lint static char copyright[] = -"$Id: dispatch.c,v 1.27 1996/11/08 20:06:29 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: dispatch.c,v 1.28 1997/01/02 12:00:16 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -162,6 +162,44 @@ void discover_interfaces (serverP) if (ifp -> ifr_addr.sa_family == AF_INET) { struct iaddr addr; +#if defined (SIOCGIFHWADDR) && !defined (AF_LINK) + struct ifreq ifr; + struct sockaddr sa; + int b, sk; + + /* Read the hardware address from this interface. */ + ifr = *ifp; + if (ioctl (sock, SIOCGIFHWADDR, &ifr) < 0) + error ("Can't get hardware address for %s: %m", + ifr.ifr_name); + + sa = *(struct sockaddr *)&ifr.ifr_hwaddr; + + switch (sa.sa_family) { + case ARPHRD_LOOPBACK: + /* ignore loopback interface */ + break; + + case ARPHRD_ETHER: + tmp -> hw_address.hlen = 6; + tmp -> hw_address.htype = ARPHRD_ETHER; + memcpy (tmp -> hw_address.haddr, + sa.sa_data, 6); + break; + + case ARPHRD_METRICOM: + tmp -> hw_address.hlen = 6; + tmp -> hw_address.htype = ARPHRD_METRICOM; + memcpy (tmp -> hw_address.haddr, + sa.sa_data, 6); + break; + + default: + error ("%s: unknown hardware address type %d", + ifr.ifr_name, sa.sa_family); + } +#endif /* defined (SIOCGIFHWADDR) && !defined (AF_LINK) */ + /* Get a pointer to the address... */ memcpy (&foo, &ifp -> ifr_addr, sizeof ifp -> ifr_addr); diff --git a/common/nit.c b/common/nit.c index 43fa7e496..0de8d62bc 100644 --- a/common/nit.c +++ b/common/nit.c @@ -42,7 +42,7 @@ #ifndef lint static char copyright[] = -"$Id: nit.c,v 1.9 1996/09/05 23:56:52 mellon Exp $ Copyright (c) 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: nit.c,v 1.10 1997/01/02 12:00:17 mellon Exp $ Copyright (c) 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -329,3 +329,21 @@ size_t receive_packet (interface, buf, len, from, hfrom) return length; } #endif + +#if defined (USE_NIT_SEND) +void if_enable (interface) + struct interface_info *interface; +{ + struct ifreq ifr; + + /* Bring the interface down and then up again to clear + * all its routes. */ + strncpy(ifr.ifr_name, interface -> name, IFNAMSIZ); + if (ioctl(interface -> rfdesc, SIOCGIFFLAGS, &ifr) < 0) + error ("SIOCGIFFLAGS %s: %m", interface -> name); + + ifr.ifr_flags |= (IFF_UP|IFF_RUNNING); + if (ioctl(interface -> rfdesc, SIOCSIFFLAGS, &ifr) == -1) + error ("SIOCSIFFLAGS %s: %m", interface -> name); +} +#endif diff --git a/common/socket.c b/common/socket.c index dcd758a57..7e7bc912a 100644 --- a/common/socket.c +++ b/common/socket.c @@ -40,9 +40,17 @@ * Enterprises, see ``http://www.vix.com''. */ +/* SO_BINDTODEVICE support added by Elliot Poger (poger@leland.stanford.edu). + * This sockopt allows a socket to be bound to a particular interface, + * thus enabling the use of DHCPD on a multihomed host. + * If SO_BINDTODEVICE is defined in your system header files, the use of + * this sockopt will be automatically enabled. + * I have implemented it under Linux; other systems should be doable also. + */ + #ifndef lint static char copyright[] = -"$Id: socket.c,v 1.16 1996/08/27 09:54:48 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: socket.c,v 1.17 1997/01/02 12:00:18 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -62,6 +70,8 @@ int if_register_socket (info, interface) struct sockaddr_in name; int sock; int flag; + +#ifndef SO_BINDTODEVICE static int once = 0; /* Make sure only one interface is registered. */ @@ -72,22 +82,8 @@ int if_register_socket (info, interface) "you must compile in BPF or NIT support. If neither ", "option is supported on your system, please let us ", "know."); - - /* Technically, we need to know what interface every packet - comes in on, which means that we can only operate on a - machine with a single interface configured. However, - we generally don't expect to get broadcast packets on - point-to-point interfaces, so we can bend the rules a bit - and not count them. This won't allow DHCP-over-PPP, - but it's probably right in a lot of cases, and the issue - will have to be revisited when DHCP-over-PPP support is - done. Currently we determine whether an interface is - point-to-point by seeing if it has a link-level address. - This only works on 4.4BSD and derivative networking. */ -#ifdef AF_LINK - if (info -> hw_address.hlen) /* XXX */ + once = 1; #endif - once = 1; /* Set up the address we're going to bind to. */ name.sin_family = AF_INET; @@ -111,10 +107,26 @@ int if_register_socket (info, interface) (char *)&flag, sizeof flag) < 0) error ("Can't set SO_BROADCAST option on dhcp socket: %m"); +#ifndef USE_SOCKET_FALLBACK + /* The following will make all-ones broadcasts go out this interface + * on those platforms which use the standard sockets API (assuming + * the OS-specific routines called by enable_sending() are present + * for this platform). */ + if_enable (info); +#endif + /* Bind the socket to this interface's IP address. */ if (bind (sock, (struct sockaddr *)&name, sizeof name) < 0) error ("Can't bind to dhcp address: %m"); +#ifdef SO_BINDTODEVICE + /* Bind this socket to this interface. */ + if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, + (char *)interface, sizeof(*interface)) < 0) { + error("setting SO_BINDTODEVICE"); + } +#endif + return sock; } #endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE */ @@ -197,3 +209,30 @@ size_t fallback_discard (interface) (struct sockaddr *)&from, &flen); } #endif /* USE_SOCKET_RECEIVE */ + +#if defined (USE_SOCKET_SEND) && !defined (USE_SOCKET_FALLBACK) +/* If we're using the standard socket API without SO_BINDTODEVICE, + * we need this kludge to force DHCP broadcasts to go out + * this interface, even though it's not available for general + * use until we get a lease! + * This should work _OK_, but it will cause ALL all-ones + * broadcasts on this host to go out this interface--it + * could interfere with other interfaces. And God help you + * if you run this on multiple interfaces simultaneously. + * SO_BINDTODEVICE really is better! */ +void if_enable (interface) + struct interface_info *interface; +{ +#ifndef SO_BINDTODEVICE + struct in_addr broad_addr; + broad_addr.s_addr = htonl(INADDR_BROADCAST); + + /* Delete old routes for broadcast address. */ + remove_routes(NULL, &broad_addr); + + /* Add a route for broadcast address to this interface. */ + /* POTENTIAL PROBLEM: Don't do this to more than one interface! */ + add_route_direct(interface, &broad_addr); +#endif +} +#endif diff --git a/dhclient.c b/dhclient.c index 8bc551ef9..a87773a53 100644 --- a/dhclient.c +++ b/dhclient.c @@ -1,6 +1,6 @@ -/* dhcp.c +/* dhclient.c - DHCP Client (really lame DHCP client). */ + DHCP Client (less lame DHCP client). */ /* * Copyright (c) 1995, 1996 The Internet Software Consortium. @@ -38,11 +38,14 @@ * Enterprises. To learn more about the Internet Software Consortium, * see ``http://www.vix.com/isc''. To learn more about Vixie * Enterprises, see ``http://www.vix.com''. + * + * This client was substantially modified and enhanced by Elliot Poger + * while he was working on the MosquitoNet project at Stanford. */ #ifndef lint static char copyright[] = -"$Id: dhclient.c,v 1.21 1996/09/11 18:53:32 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: dhclient.c,v 1.22 1997/01/02 12:00:14 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -56,6 +59,31 @@ struct iaddr server_identifier; int server_identifier_matched; int log_perror = 1; +const u_long broadcast_address = INADDR_BROADCAST; +const int max_retransmissions = 4; +const int min_delay = 4; + +/* Globals for the real_dhcp_client state machine. */ +TIME lease_expiry, T1_expiry, T2_expiry; +struct packet *sendpacket; +struct packet *recvpacket; +struct packet *leasepacket; +int sendpacket_flag, recvpacket_flag; +u_long destination; + +enum { + S_INIT, S_SELECTING, S_REQUESTING, + S_BOUND, S_RENEWING, S_REBINDING +} state; + +/* ASSERT_STATE() does nothing now; it used to be + * assert(state_is==state_shouldbe); */ +#define ASSERT_STATE(state_is, state_shouldbe) {} + +int retransmissions_left, retransmission_delay; +struct interface_info *dhclient_interface; + + #ifdef USE_FALLBACK struct interface_info fallback_interface; #endif @@ -99,13 +127,24 @@ int main (argc, argv, envp) server_port = htons (atoi (argv [i])); debug ("binding to user-specified port %d", ntohs (server_port)); - } else - usage (); + } else if (argv [i][0] == '-') { + usage (); + } else { + struct interface_info *tmp = + ((struct interface_info *) + dmalloc (sizeof *tmp, "specified_interface")); + if (!tmp) + error ("Insufficient memory to %s %s", + "record interface", argv [i]); + memset (tmp, 0, sizeof *tmp); + strcpy (tmp -> name, argv [i]); + tmp -> next = interfaces; + tmp -> flags = INTERFACE_REQUESTED; + interfaces = tmp; + } } - /* Default to the DHCP/BOOTP port. */ - if (!server_port) - { + if (!server_port) { ent = getservbyname ("dhcpc", "udp"); if (!ent) server_port = htons (68); @@ -120,25 +159,544 @@ int main (argc, argv, envp) /* Discover all the network interfaces and initialize them. */ discover_interfaces (0); - for (interface = interfaces; interface; interface = interface -> next) - send_discover (interface); - - /* Receive packets and dispatch them... */ - dispatch (); + /* This is a real DHCP client! + * SO, fork off a dhclient state machine for each + * interface. */ + for (dhclient_interface = interfaces; dhclient_interface; + dhclient_interface = dhclient_interface -> next) { + if (!dhclient_interface -> next || fork() == 0) { + srandom(cur_time + *(int *) + &dhclient_interface->hw_address.haddr); + dhclient_state_machine(); + dhclient_fail(); + } + } - /* Not reached */ return 0; } +/* This routine should be called when the DHCP client quits for any reason. + * It makes the interface unusable and outputs an error message before + * exiting. A warn() message should be given first for specific errors. */ + +void dhclient_fail() +{ + disable_interface(dhclient_interface); + error ("dhclient for '%s' halted", + dhclient_interface->name); +} + +/* Handles the KILL signal for this DHCP client process. + * Sends a DHCPRELEASE message to the server (to be a good neighbor) before + * calling failure routine. */ +void handle_kill(dummy) + int dummy; +{ + warn("dhclient has been killed!"); + /* XXX figure out whether the lease has expired... */ + if (leasepacket -> packet_type == DHCPACK) { + warn("sending DHCPRELEASE"); + send_release(leasepacket); + } + dhclient_fail (); +} + +/* Make this network interface unusable by most programs. This routine is + * called whenever dhclient discovers this host holds no lease. + * dhclient must still be able to use it, though, so we can get a lease!!! + * Just remove all the routes to this interface, and set its IP address to + * 0.0.0.0. dhclient will have to find some way to communicate with it. */ +void disable_interface(interface) + struct interface_info *interface; +{ + struct in_addr zero_addr; + + remove_all_if_routes (interface); + zero_addr.s_addr = 0; + set_ip_address (interface, zero_addr); +} + +void apply_parameters(interface, packet) + struct interface_info *interface; + struct packet *packet; +{ + struct in_addr net_addr, tmp; + + /* Eventually, set IP address, netmask, router, DNS servers, + * domain name, broadcast addr, etc. + * + * For now, set IP addr, netmask, and broadcast addr, add gateway, + * and update routes. + * Call OS-dependent routines to do the dirty work. + */ + + note("Setting parameters for %s interface...", interface->name); + + remove_all_if_routes (interface); + set_ip_address (interface, packet -> raw -> yiaddr); + + memcpy (&tmp, packet->options[DHO_SUBNET_MASK].data, sizeof tmp); + set_netmask (interface, tmp); + + memcpy (&tmp, packet->options[DHO_BROADCAST_ADDRESS].data, sizeof tmp); + set_broadcast_addr(interface, tmp); + + /* network addr = (my IP addr) AND (netmask) */ + memcpy (&tmp, packet -> options[DHO_SUBNET_MASK].data, sizeof tmp); + net_addr.s_addr = packet -> raw -> yiaddr.s_addr & tmp.s_addr; + memcpy (&tmp, packet->options[DHO_SUBNET_MASK].data, sizeof tmp); + add_route_net(interface, net_addr, tmp); + + memcpy (&tmp, packet->options[DHO_ROUTERS].data, sizeof tmp); + add_route_default_gateway(interface, tmp); +} + static void usage () { - error ("Usage: dhclient [-p ]"); + error ("Usage: dhclient [-c] [-p ] [interface]"); } void cleanup () { } +/* The state machine for a true DHCP client, on one particular interface + * (stored in global dhclient_interface). + * This function should not be exited except on a fatal error. */ +void dhclient_state_machine () +{ + fd_set read_fd; + struct timeval tv, timeout; + int reset_timer_flag; + long int rnd; + + recvpacket = new_packet("recvpacket"); + recvpacket->raw = new_dhcp_packet("recvpacket->raw"); + sendpacket = new_packet("sendpacket"); + sendpacket->raw = new_dhcp_packet("sendpacket->raw"); + leasepacket = new_packet("leasepacket"); + leasepacket->raw = new_dhcp_packet("leasepacket->raw"); + + /* Figure out when we time out... */ + gettimeofday (&timeout, (struct timezone *)0); + timeout.tv_sec++; + + state = S_INIT; + sendpacket_flag = 0; + recvpacket_flag = 0; + retransmissions_left = 0; + + signal(SIGTERM, handle_kill); + signal(SIGINT, handle_kill); + while (1) { + /* We have awakened for some reason. Possible reasons are: + * -> just entered INIT state (special case) + * -> packet received on this port (recvbuffer != NULL) + * -> timeout (we may want to retransmit a request) + */ + GET_TIME (&cur_time); + sendpacket_flag = 0; + + if (state == S_INIT) { + reset_timer_flag = state_init(); + } else if ( (!recvpacket_flag) && (retransmissions_left>0) ) { + /* Timed out; retransmit. */ + retransmissions_left--; + retransmission_delay = retransmission_delay*2; + reset_timer_flag = 1; + sendpacket_flag = 1; + } else { + /* Either a packet was received + * or we timed out and ran out of retransmissions. + */ + if (recvpacket_flag) { + read_packet(dhclient_interface, recvpacket); + } + /* If a packet was received, first check to make + * sure it's relevant to our last request; if it's + * not, ignore it and go back to sleep. + */ + if ((recvpacket_flag) && + ((!recvpacket->options_valid) || + (recvpacket->raw->op != BOOTREPLY) || + (recvpacket->raw->xid!=sendpacket->raw->xid) ) ) { + reset_timer_flag = 0; + } else { + note ("%s for %s", + (recvpacket -> packet_type == 0 + ? "BOOTREPLY" : + (recvpacket -> packet_type == DHCPOFFER + ? "DHCPOFFER" : + (recvpacket -> packet_type == DHCPACK + ? "DHCPACK" : + (recvpacket -> packet_type == DHCPNAK + ? "DHCPNAK" : "DHCP Unknown")))), + inet_ntoa (recvpacket -> raw -> yiaddr)); + + /* Call the appropriate routine for this state. + * This routine handles both timeout + * and packet-received cases. + * + * The routine returns 1 if the select() + * timer should be reset (which is ALWAYS the + * case when sending a new packet). + * Otherwise, we just continue our last select. + */ + switch (state) { + case S_SELECTING: + reset_timer_flag = state_selecting(); + break; + case S_REQUESTING: + reset_timer_flag = state_requesting(); + break; + case S_BOUND: + reset_timer_flag = state_bound(); + break; + case S_RENEWING: + reset_timer_flag = state_renewing(); + break; + case S_REBINDING: + reset_timer_flag = state_rebinding(); + break; + default: + warn("dhclient entered bad state"); + dhclient_fail(); + } + } + } + + /* Send a packet and reset the timer OR + * set a new timer with no outstanding request OR + * just go back to sleep because we're still waiting for + * something. */ + if (reset_timer_flag) { + if (sendpacket_flag) { + send_packet_struct(dhclient_interface, + destination, sendpacket); + } + rnd = random(); + tv.tv_sec = retransmission_delay - 1 + (rnd&1); + tv.tv_usec = rnd % 1000000; + gettimeofday (&timeout, (struct timezone *)0); + timeout.tv_usec += tv.tv_usec; + if (timeout.tv_usec > 1000000) { + timeout.tv_usec -= 1000000; + timeout.tv_sec++; + } + timeout.tv_sec += tv.tv_sec; + } else { + gettimeofday (&tv, (struct timezone *)0); + tv.tv_usec = timeout.tv_usec - tv.tv_usec; + if (tv.tv_usec < 0) { + tv.tv_usec += 1000000; + tv.tv_sec--; + } + tv.tv_sec = timeout.tv_sec - tv.tv_sec; + if (tv.tv_sec < 0) { + warn ("timer expired unexpectedly!"); + dhclient_fail (); + } + } + + FD_ZERO (&read_fd); + FD_SET (dhclient_interface->rfdesc, &read_fd); + recvpacket_flag = select(dhclient_interface->rfdesc+1, + &read_fd, NULL, NULL, &tv); + if (recvpacket_flag < 0) { + warn ("select: %m"); + dhclient_fail(); + } + } + /* keep cycling through the state machine until dhclient_fail() */ +} + +/* Individual States: + * + * Each routine is called from the dhclient_state_machine() in one of + * these conditions: + * -> entering INIT state + * -> recvpacket_flag == 0: timeout in this state + * -> otherwise: received a packet in this state + * + * Return conditions as handled by dhclient_state_machine(): + * Returns 1, sendpacket_flag = 1: send packet, reset timer. + * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone). + * Returns 0: finish the nap which was interrupted for no good reason. + * + * Several global variables are used to keep track of the process: + * recvpacket: most recent packet received + * sendpacket: most recent packet sent or to be sent + * leasepacket: copy of most recent valid lease DHCPACK packet + * recvpacket_flag: recvpacket is not old hat + * sendpacket_flag: sendpacket is to be sent + * destination: IP address to send sendpacket to + * retransmissions_left: # of times remaining to resend this sendpacket + * retransmission_delay: # of seconds to wait before next retransmission + * T1_expiry, T2_expiry, lease_expiry: lease milestones + */ + +int state_init() +{ + ASSERT_STATE(state, S_INIT); + + /* Don't let anyone else use this interface until we get a lease, + * but make sure we can use it to obtain a lease! */ + disable_interface(dhclient_interface); + if_enable (dhclient_interface); + + make_discover(dhclient_interface, sendpacket); + sendpacket_flag = 1; + retransmissions_left = max_retransmissions; + retransmission_delay = min_delay; + destination = broadcast_address; + state = S_SELECTING; + return 1; +} + +int state_selecting() +{ + ASSERT_STATE(state, S_SELECTING); + + if (!recvpacket_flag) { + warn("no DHCP server found"); + dhclient_fail(); + /*NOTREACHED*/ + return 0; + } else if (recvpacket->packet_type != DHCPOFFER) { + return 0; /* wait for a DHCPOFFER */ + } else { + /* Got a DHCPOFFER! */ + make_request(recvpacket, sendpacket); + sendpacket->raw->xid = random(); + sendpacket_flag = 1; + destination = broadcast_address; + retransmissions_left = max_retransmissions; + retransmission_delay = min_delay; + state = S_REQUESTING; + return 1; + } +} + +int state_requesting() +{ + ASSERT_STATE(state, S_REQUESTING); + + if (!recvpacket_flag) { + warn("lost contact with DHCP server"); + state = S_INIT; + return state_init(); + } else if (recvpacket->packet_type == DHCPNAK) { + warn("DHCPOFFER withdrawn"); + state = S_INIT; + return state_init(); + } else if (recvpacket->packet_type == DHCPACK) { + /* We got a lease! Apply its parameters, + * record lease info, and set timer to T1. */ + deep_packet_copy(leasepacket, recvpacket); + apply_parameters(dhclient_interface, leasepacket); + lease_expiry = abs_time(leasepacket, DHO_DHCP_LEASE_TIME); + T1_expiry = abs_time(leasepacket, DHO_DHCP_RENEWAL_TIME); + T2_expiry = abs_time(leasepacket, DHO_DHCP_REBINDING_TIME); + retransmissions_left = 0; + retransmission_delay = T1_expiry - cur_time; + sendpacket_flag = 0; /* just set the timer, that's all */ + state = S_BOUND; + return 1; + } else { + /* Ignore some superfluous packet. */ + return 0; + } +} + +int state_bound() +{ + ASSERT_STATE(state, S_BOUND); + + if (!recvpacket_flag) { + /* T1 has expired. */ + make_request(leasepacket, sendpacket); + sendpacket_flag = 1; + /* XXX: should really unicast here, but to whom? */ + destination = broadcast_address; + /* XXX: SHOULD WE DO RETRANSMISSIONS HERE? */ + retransmissions_left = 0; + retransmission_delay = T2_expiry - cur_time; + state = S_RENEWING; + return 1; + } else { + /* Ignore some superfluous packet. */ + return 0; + } +} + +int state_renewing() +{ + ASSERT_STATE(state, S_RENEWING); + + if (!recvpacket_flag) { + /* T2 has expired. */ + debug("T2 expired"); + sendpacket_flag = 1; + destination = broadcast_address; + /* XXX: SHOULD WE DO RETRANSMISSIONS HERE? */ + retransmissions_left = 0; + retransmission_delay = lease_expiry - cur_time; + state = S_REBINDING; + return 1; + } else if (recvpacket->packet_type == DHCPNAK) { + warn("lease renewal denied!"); + state = S_INIT; + return state_init(); + } else if (recvpacket->packet_type == DHCPACK) { + /* Record new lease info, and set timer to T1. */ + debug("lease renewed"); + deep_packet_copy(leasepacket, recvpacket); + lease_expiry = abs_time(leasepacket, DHO_DHCP_LEASE_TIME); + T1_expiry = abs_time(leasepacket, DHO_DHCP_RENEWAL_TIME); + T2_expiry = abs_time(leasepacket, DHO_DHCP_REBINDING_TIME); + retransmissions_left = 0; + retransmission_delay = T1_expiry - cur_time; + sendpacket_flag = 0; /* just set the timer, that's all */ + state = S_BOUND; + return 1; + } else { + /* Ignore some superfluous packet. */ + return 0; + } +} + +int state_rebinding() +{ + ASSERT_STATE(state, S_REBINDING); + + if (!recvpacket_flag) { + /* Lease has expired! */ + warn("DHCP lease expired!"); + state = S_INIT; + return state_init(); + } else if (recvpacket->packet_type == DHCPNAK) { + warn("lease rebinding denied!"); + state = S_INIT; + return state_init(); + } else if (recvpacket->packet_type == DHCPACK) { + /* Record new lease info, and set timer to T1. */ + debug("lease rebound"); + deep_packet_copy(leasepacket, recvpacket); + lease_expiry = abs_time(leasepacket, DHO_DHCP_LEASE_TIME); + T1_expiry = abs_time(leasepacket, DHO_DHCP_RENEWAL_TIME); + T2_expiry = abs_time(leasepacket, DHO_DHCP_REBINDING_TIME); + retransmissions_left = 0; + retransmission_delay = T1_expiry - cur_time; + sendpacket_flag = 0; /* just set the timer, that's all */ + state = S_BOUND; + return 1; + } else { + /* Ignore some superfluous packet. */ + return 0; + } +} + +/* Read the time offset from a given DHCP option and return it as an + * absolute moment in time (offset from cur_time). */ +TIME abs_time(pkt, optnum) + struct packet *pkt; + int optnum; +{ + if (pkt == NULL) { + error ("Bad parameter passed to abs_time"); + } + + return cur_time + + (TIME) ntohl(*((u_int32_t *)pkt->options[optnum].data)); +} + +/* This little number lets you record an interesting packet's contents in + * another (pre-allocated) packet struct. */ +/* XXX: what about haddr? */ +void deep_packet_copy(to, from) + struct packet *to; + struct packet *from; +{ + struct dhcp_packet *raw; + + if ((to==NULL) || (to->raw==NULL)) { + error("Bad parameter passed to deep_packet_copy"); + } + + raw = to->raw; + memcpy (to->raw, from->raw, sizeof *from->raw); + memcpy (to, from, sizeof *from); + to->raw = raw; +} + + +/* Read a DHCP packet from this interface into this (pre-allocated) packet. + * If the data on this interface is bogus (non-DHCP), + * set pkt->options_valid = 0. + * Much of this code is stolen from dispatch.c. + */ +void read_packet(interface, pkt) + struct interface_info *interface; + struct packet *pkt; +{ + struct dhcp_packet *dhcp_pkt; + struct sockaddr_in from; + struct hardware *hfrom; + struct hardware fudge_factor; /* XXX */ + struct iaddr ifrom; + int len; + static unsigned char packbuf [4095]; /* Packet input buffer. + Must be as large as largest + possible MTU. */ + + if (pkt==NULL) { + error("Bad parameter passed to read_packet"); + } + pkt->haddr = &fudge_factor; + if ((pkt->haddr==NULL) || (pkt->raw==NULL)) { + error("Bad parameter passed to read_packet"); + } + + hfrom = pkt->haddr; + if ((len = receive_packet (interface, packbuf, sizeof packbuf, + &from, hfrom)) < 0) { + warn ("receive_packet failed on %s: %m", interface -> name); + pkt->options_valid = 0; + return; + } + if (len == 0) { + pkt->options_valid = 0; + return; + } + + ifrom.len = 4; + memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len); + + dhcp_pkt = pkt->raw; + memcpy (dhcp_pkt, packbuf, len); + memset (pkt, 0, sizeof (struct packet)); + pkt->raw = dhcp_pkt; + pkt->packet_length = len; + pkt->client_port = from.sin_port; + pkt->client_addr = ifrom; + pkt->interface = interface; + pkt->haddr = hfrom; + + parse_options (pkt); + if (pkt->options_valid && + pkt->options [DHO_DHCP_MESSAGE_TYPE].data) { + pkt->packet_type = + pkt->options [DHO_DHCP_MESSAGE_TYPE].data [0]; + } else { + pkt->options_valid = 0; + } + pkt->haddr = NULL; /* XXX */ + +#ifdef DEBUG + dump_packet(pkt); +#endif +} + int commit_leases () { return 0; @@ -193,6 +751,7 @@ void dhcpoffer (packet) packet -> raw -> chaddr)); dump_packet (packet); + note ("DHCPREQUEST to %s", packet -> interface -> name); send_request (packet); } @@ -215,34 +774,109 @@ void dhcpnak (packet) packet -> raw -> chaddr)); } -static u_int8_t requested_options [] = { - DHO_DHCP_REQUESTED_ADDRESS, - DHO_SUBNET_MASK, - DHO_ROUTERS, - DHO_DOMAIN_NAME_SERVERS, - DHO_HOST_NAME, - DHO_DOMAIN_NAME, - DHO_BROADCAST_ADDRESS }; - void send_discover (interface) struct interface_info *interface; +{ + struct packet outgoing; + struct dhcp_packet raw; + + outgoing.raw = &raw; + make_discover (interface, &outgoing); + send_packet_struct (interface, htonl(INADDR_BROADCAST), &outgoing); +} + +void send_request (packet) + struct packet *packet; /* incoming packet we're responding to */ +{ + struct packet outgoing; + struct dhcp_packet raw; + + outgoing.raw = &raw; + make_request (packet, &outgoing); + send_packet_struct (packet->interface, htonl(INADDR_BROADCAST), + &outgoing); +} + +void send_release (packet) + struct packet *packet; /* DHCPACK packet from lease we're releasing */ +{ + struct packet outgoing; + struct dhcp_packet raw; + + outgoing.raw = &raw; + make_release (packet, &outgoing); + send_packet_struct (packet->interface, htonl(INADDR_BROADCAST), + &outgoing); +} + +/* Send the given packet to the given host IPaddr, over the given interface. */ +void send_packet_struct (interface, dest_host, sendpkt) + struct interface_info *interface; + u_long dest_host; /* in network order! */ + struct packet *sendpkt; { struct sockaddr_in to; int result; - struct dhcp_packet raw; + char dhcpbuf [128]; + + /* Set up the common stuff... */ + to.sin_family = AF_INET; +#ifdef HAVE_SA_LEN + to.sin_len = sizeof to; +#endif + memset (to.sin_zero, 0, sizeof to.sin_zero); + + to.sin_addr.s_addr = dest_host; + to.sin_port = htons (ntohs (server_port) - 1); /* XXX */ + + sprintf (dhcpbuf, "DHCP Unknown %d", sendpkt -> packet_type); + note ("%s to %s", + sendpkt -> packet_type == DHCPDISCOVER ? "DHCPDISCOVER" + : (sendpkt -> packet_type == DHCPREQUEST ? "DHCPREQUEST" + : (sendpkt -> packet_type == DHCPNAK ? "DHCPNAK" + : (sendpkt -> packet_type == DHCPINFORM ? "DHCPINFORM" + : dhcpbuf))), + inet_ntoa (to.sin_addr)); + + if (sendpkt->packet_length < DHCP_MIN_LEN) + sendpkt->packet_length = DHCP_MIN_LEN; + + errno = 0; + result = send_packet (interface, (struct packet *)0, + sendpkt->raw, sendpkt->packet_length, + sendpkt->raw->siaddr, &to, (struct hardware *)0); + /* XXX: is the above 'siaddr' correct? */ + if (result < 0) + warn ("send_packet: %m"); +} + +void make_discover (interface, sendpkt) + struct interface_info *interface; + struct packet *sendpkt; +{ + struct dhcp_packet *raw; unsigned char discover = DHCPDISCOVER; - struct packet outgoing; struct tree_cache *options [256]; struct tree_cache dhcpdiscover_tree; struct tree_cache dhcprqo_tree; + u_int8_t requested_options [] = { + DHO_SUBNET_MASK, + DHO_ROUTERS, + DHO_DOMAIN_NAME_SERVERS, + DHO_HOST_NAME, + DHO_DOMAIN_NAME, + DHO_BROADCAST_ADDRESS }; + + raw = sendpkt->raw; memset (options, 0, sizeof options); - memset (&outgoing, 0, sizeof outgoing); - memset (&raw, 0, sizeof raw); - outgoing.raw = &raw; + memset (sendpkt, 0, sizeof (*sendpkt)); + memset (raw, 0, sizeof (*raw)); + sendpkt->raw = raw; + sendpkt -> packet_type = DHCPDISCOVER; - /* Set DHCP_MESSAGE_TYPE to DHCPNAK */ + /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */ options [DHO_DHCP_MESSAGE_TYPE] = &dhcpdiscover_tree; options [DHO_DHCP_MESSAGE_TYPE] -> value = &discover; options [DHO_DHCP_MESSAGE_TYPE] -> len = sizeof discover; @@ -250,65 +884,44 @@ void send_discover (interface) options [DHO_DHCP_MESSAGE_TYPE] -> timeout = 0xFFFFFFFF; options [DHO_DHCP_MESSAGE_TYPE] -> tree = (struct tree *)0; - /* Set DHCP_MESSAGE to whatever the message is */ - options [DHO_DHCP_MESSAGE] = &dhcprqo_tree; - options [DHO_DHCP_MESSAGE] -> value = requested_options; - options [DHO_DHCP_MESSAGE] -> len = sizeof requested_options; - options [DHO_DHCP_MESSAGE] -> buf_size = sizeof requested_options; - options [DHO_DHCP_MESSAGE] -> timeout = 0xFFFFFFFF; - options [DHO_DHCP_MESSAGE] -> tree = (struct tree *)0; + /* Request the parameters we want */ + options [DHO_DHCP_PARAMETER_REQUEST_LIST] = &dhcprqo_tree; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> value = requested_options; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> len = sizeof requested_options; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> buf_size = sizeof requested_options; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> timeout = 0xFFFFFFFF; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> tree = (struct tree *)0; /* Set up the option buffer... */ - cons_options ((struct packet *)0, &outgoing, options, 0, 0); + cons_options ((struct packet *)0, sendpkt, options, 0, 0); - memset (&raw.ciaddr, 0, sizeof raw.ciaddr); - memset (&raw.siaddr, 0, sizeof raw.siaddr); - memset (&raw.giaddr, 0, sizeof raw.giaddr); - memcpy (raw.chaddr, + raw->op = BOOTREQUEST; + raw->htype = interface -> hw_address.htype; + raw->hlen = interface -> hw_address.hlen; + raw->hops = 0; + raw->xid = random (); + raw->secs = 0; /* XXX */ + raw->flags = htons (BOOTP_BROADCAST); + memset (&(raw->ciaddr), 0, sizeof raw->ciaddr); + memset (&(raw->yiaddr), 0, sizeof raw->yiaddr); + memset (&(raw->siaddr), 0, sizeof raw->siaddr); + memset (&(raw->giaddr), 0, sizeof raw->giaddr); + memcpy (raw->chaddr, interface -> hw_address.haddr, interface -> hw_address.hlen); - raw.hlen = interface -> hw_address.hlen; - raw.htype = interface -> hw_address.htype; - - raw.xid = random (); - raw.secs = 1; - raw.flags = htons (BOOTP_BROADCAST); - raw.hops = 0; - raw.op = BOOTREQUEST; - - /* Report what we're sending... */ - note ("DHCPDISCOVER to %s", interface -> name); #ifdef DEBUG_PACKET - dump_packet (&outgoing); - dump_raw ((unsigned char *)&raw, outgoing.packet_length); + dump_packet (sendpkt); + dump_raw ((unsigned char *)raw, sendpkt->packet_length); #endif - - /* Set up the common stuff... */ - to.sin_family = AF_INET; -#ifdef HAVE_SA_LEN - to.sin_len = sizeof to; -#endif - memset (to.sin_zero, 0, sizeof to.sin_zero); - - to.sin_addr.s_addr = htonl (INADDR_BROADCAST); - to.sin_port = htons (ntohs (server_port) - 1); /* XXX */ - - errno = 0; - result = send_packet (interface, (struct packet *)0, - &raw, outgoing.packet_length, - raw.siaddr, &to, (struct hardware *)0); - if (result < 0) - warn ("send_packet: %m"); } -void send_request (packet) - struct packet *packet; + +void make_request (recvpkt, sendpkt) + struct packet *recvpkt; + struct packet *sendpkt; { - struct sockaddr_in to; - int result; - struct dhcp_packet raw; + struct dhcp_packet *raw; unsigned char request = DHCPREQUEST; - struct packet outgoing; struct tree_cache *options [256]; struct tree_cache dhcprequest_tree; @@ -316,12 +929,23 @@ void send_request (packet) struct tree_cache dhcprqa_tree; struct tree_cache dhcpsid_tree; + u_int8_t requested_options [] = { + DHO_SUBNET_MASK, + DHO_ROUTERS, + DHO_DOMAIN_NAME_SERVERS, + DHO_HOST_NAME, + DHO_DOMAIN_NAME, + DHO_BROADCAST_ADDRESS }; + + raw = sendpkt->raw; memset (options, 0, sizeof options); - memset (&outgoing, 0, sizeof outgoing); - memset (&raw, 0, sizeof raw); - outgoing.raw = &raw; + memset (sendpkt, 0, sizeof (*sendpkt)); + memset (raw, 0, sizeof (*raw)); + sendpkt->raw = raw; - /* Set DHCP_MESSAGE_TYPE to DHCPNAK */ + sendpkt -> packet_type = DHCPREQUEST; + + /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */ options [DHO_DHCP_MESSAGE_TYPE] = &dhcprequest_tree; options [DHO_DHCP_MESSAGE_TYPE] -> value = &request; options [DHO_DHCP_MESSAGE_TYPE] -> len = sizeof request; @@ -329,74 +953,113 @@ void send_request (packet) options [DHO_DHCP_MESSAGE_TYPE] -> timeout = 0xFFFFFFFF; options [DHO_DHCP_MESSAGE_TYPE] -> tree = (struct tree *)0; - /* Set DHCP_MESSAGE to whatever the message is */ - options [DHO_DHCP_MESSAGE] = &dhcprqo_tree; - options [DHO_DHCP_MESSAGE] -> value = requested_options; - options [DHO_DHCP_MESSAGE] -> len = sizeof requested_options; - options [DHO_DHCP_MESSAGE] -> buf_size = sizeof requested_options; - options [DHO_DHCP_MESSAGE] -> timeout = 0xFFFFFFFF; - options [DHO_DHCP_MESSAGE] -> tree = (struct tree *)0; - - /* Request the address we were offered... */ - options [DHO_DHCP_REQUESTED_ADDRESS] = &dhcprqa_tree; - options [DHO_DHCP_REQUESTED_ADDRESS] -> value = - (unsigned char *)&packet -> raw -> yiaddr; - options [DHO_DHCP_REQUESTED_ADDRESS] -> len = 4; - options [DHO_DHCP_REQUESTED_ADDRESS] -> buf_size = 4; - options [DHO_DHCP_REQUESTED_ADDRESS] -> timeout = 0xFFFFFFFF; - options [DHO_DHCP_REQUESTED_ADDRESS] -> tree = (struct tree *)0; + /* Request the parameters we want */ + options [DHO_DHCP_PARAMETER_REQUEST_LIST] = &dhcprqo_tree; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> value = requested_options; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> len = sizeof requested_options; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> buf_size = sizeof requested_options; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> timeout = 0xFFFFFFFF; + options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> tree = (struct tree *)0; /* Send back the server identifier... */ options [DHO_DHCP_SERVER_IDENTIFIER] = &dhcpsid_tree; options [DHO_DHCP_SERVER_IDENTIFIER] -> value = - packet -> options [DHO_DHCP_SERVER_IDENTIFIER].data; + recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].data; options [DHO_DHCP_SERVER_IDENTIFIER] -> len = - packet -> options [DHO_DHCP_SERVER_IDENTIFIER].len; + recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len; options [DHO_DHCP_SERVER_IDENTIFIER] -> buf_size = - packet -> options [DHO_DHCP_SERVER_IDENTIFIER].len; + recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len; options [DHO_DHCP_SERVER_IDENTIFIER] -> timeout = 0xFFFFFFFF; options [DHO_DHCP_SERVER_IDENTIFIER] -> tree = (struct tree *)0; /* Set up the option buffer... */ - cons_options ((struct packet *)0, &outgoing, options, 0, 0); - - memset (&raw.ciaddr, 0, sizeof raw.ciaddr); - raw.siaddr = packet -> raw -> siaddr; - raw.giaddr = packet -> raw -> giaddr; - memcpy (raw.chaddr, - packet -> interface -> hw_address.haddr, - packet -> interface -> hw_address.hlen); - raw.hlen = packet -> interface -> hw_address.hlen; - raw.htype = packet -> interface -> hw_address.htype; - - raw.xid = packet -> raw -> xid; - raw.secs = packet -> raw -> secs; - raw.flags = htons (BOOTP_BROADCAST); - raw.hops = packet -> raw -> hops; - raw.op = BOOTREQUEST; - - /* Report what we're sending... */ - note ("DHCPREQUEST to %s", packet -> interface -> name); + cons_options ((struct packet *)0, sendpkt, options, 0, 0); + + raw->op = BOOTREQUEST; + raw->htype = recvpkt -> interface -> hw_address.htype; + raw->hlen = recvpkt -> interface -> hw_address.hlen; + raw->hops = 0; + raw->xid = recvpkt -> raw -> xid; + raw->secs = recvpkt -> raw -> secs; /* XXX */ + raw->flags = htons (BOOTP_BROADCAST); + raw->ciaddr = recvpkt -> raw -> yiaddr; /* XXX ????? */ + memset (&raw->yiaddr, 0, sizeof raw->yiaddr); + memset (&raw->siaddr, 0, sizeof raw->siaddr); + memset (&raw->giaddr, 0, sizeof raw->giaddr); + memcpy (raw->chaddr, + recvpkt -> interface -> hw_address.haddr, + recvpkt -> interface -> hw_address.hlen); #ifdef DEBUG_PACKET - dump_packet (&outgoing); - dump_raw ((unsigned char *)&raw, outgoing.packet_length); + dump_packet (sendpkt); + dump_raw ((unsigned char *)raw, sendpkt->packet_length); #endif +} - /* Set up the common stuff... */ - to.sin_family = AF_INET; -#ifdef HAVE_SA_LEN - to.sin_len = sizeof to; -#endif - memset (to.sin_zero, 0, sizeof to.sin_zero); +void make_release (recvpkt, sendpkt) + struct packet *recvpkt; + struct packet *sendpkt; +{ + struct dhcp_packet *raw; + unsigned char request = DHCPRELEASE; - to.sin_addr.s_addr = htonl (INADDR_BROADCAST); - to.sin_port = htons (ntohs (server_port) - 1); /* XXX */ + struct tree_cache *options [256]; + struct tree_cache dhcprequest_tree; + struct tree_cache dhcprqo_tree; + struct tree_cache dhcprqa_tree; + struct tree_cache dhcpsid_tree; - errno = 0; - result = send_packet (packet -> interface, (struct packet *)0, - &raw, outgoing.packet_length, - raw.siaddr, &to, (struct hardware *)0); - if (result < 0) - warn ("send_packet: %m"); + raw = sendpkt->raw; + memset (options, 0, sizeof options); + memset (sendpkt, 0, sizeof (*sendpkt)); + memset (raw, 0, sizeof (*raw)); + sendpkt->raw = raw; + + sendpkt -> packet_type = DHCPRELEASE; + + /* Set DHCP_MESSAGE_TYPE to DHCPRELEASE */ + options [DHO_DHCP_MESSAGE_TYPE] = &dhcprequest_tree; + options [DHO_DHCP_MESSAGE_TYPE] -> value = &request; + options [DHO_DHCP_MESSAGE_TYPE] -> len = sizeof request; + options [DHO_DHCP_MESSAGE_TYPE] -> buf_size = sizeof request; + options [DHO_DHCP_MESSAGE_TYPE] -> timeout = 0xFFFFFFFF; + options [DHO_DHCP_MESSAGE_TYPE] -> tree = (struct tree *)0; + + /* Send back the server identifier... */ + options [DHO_DHCP_SERVER_IDENTIFIER] = &dhcpsid_tree; + options [DHO_DHCP_SERVER_IDENTIFIER] -> value = + recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].data; + options [DHO_DHCP_SERVER_IDENTIFIER] -> len = + recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len; + options [DHO_DHCP_SERVER_IDENTIFIER] -> buf_size = + recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len; + options [DHO_DHCP_SERVER_IDENTIFIER] -> timeout = 0xFFFFFFFF; + options [DHO_DHCP_SERVER_IDENTIFIER] -> tree = (struct tree *)0; + + /* Set DHCP_MESSAGE to whatever the message is */ + /* XXX */ + + /* Set up the option buffer... */ + cons_options ((struct packet *)0, sendpkt, options, 0, 0); + + raw->op = BOOTREQUEST; + raw->htype = recvpkt -> interface -> hw_address.htype; + raw->hlen = recvpkt -> interface -> hw_address.hlen; + raw->hops = 0; + raw->xid = recvpkt -> raw -> xid; + raw->secs = 0; + raw->flags = 0; + raw->ciaddr = recvpkt -> raw -> yiaddr; + memset (&raw->yiaddr, 0, sizeof raw->yiaddr); + memset (&raw->siaddr, 0, sizeof raw->siaddr); + memset (&raw->giaddr, 0, sizeof raw->giaddr); + memcpy (raw->chaddr, + recvpkt -> interface -> hw_address.haddr, + recvpkt -> interface -> hw_address.hlen); + + +#ifdef DEBUG_PACKET + dump_packet (sendpkt); + dump_raw ((unsigned char *)raw, sendpkt->packet_length); +#endif } diff --git a/dhcp.h b/dhcp.h index 23a8bf737..181277579 100644 --- a/dhcp.h +++ b/dhcp.h @@ -52,6 +52,7 @@ #define DHCP_OPTION_LEN (DHCP_MTU_MAX - DHCP_FIXED_LEN) #define BOOTP_MIN_LEN 300 +#define DHCP_MIN_LEN 548 struct dhcp_packet { u_int8_t op; /* Message opcode/type */ diff --git a/dhcpd.h b/dhcpd.h index 34aed0b56..d62f78f2e 100644 --- a/dhcpd.h +++ b/dhcpd.h @@ -451,6 +451,9 @@ size_t receive_packet PROTO ((struct interface_info *, unsigned char *, size_t, struct sockaddr_in *, struct hardware *)); #endif +#if defined (USE_SOCKET_SEND) && !defined (USE_SOCKET_FALLBACK) +void if_enable PROTO ((struct interface_info *)); +#endif /* bpf.c */ #if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE) @@ -469,6 +472,9 @@ size_t receive_packet PROTO ((struct interface_info *, unsigned char *, size_t, struct sockaddr_in *, struct hardware *)); #endif +#if defined (USE_BPF_SEND) +void if_enable PROTO ((struct interface_info *)); +#endif /* nit.c */ #ifdef USE_NIT_SEND @@ -484,6 +490,9 @@ size_t receive_packet PROTO ((struct interface_info *, unsigned char *, size_t, struct sockaddr_in *, struct hardware *)); #endif +#if defined (USE_BPF_SEND) +void if_enable PROTO ((struct interface_info *)); +#endif /* raw.c */ #ifdef USE_RAW_SEND @@ -541,6 +550,27 @@ void dhcpack PROTO ((struct packet *)); void dhcpnak PROTO ((struct packet *)); void send_discover PROTO ((struct interface_info *)); void send_request PROTO ((struct packet *)); +void send_release PROTO ((struct packet *)); +void dhclient_fail PROTO ((void)); +void handle_kill PROTO ((int)); +void disable_interface PROTO ((struct interface_info *)); +void route_broadcasts PROTO ((struct interface_info *)); +void apply_parameters PROTO ((struct interface_info *, struct packet *)); +void dhclient_state_machine PROTO ((void)); +int state_init PROTO ((void)); +int state_selecting PROTO ((void)); +int state_requesting PROTO ((void)); +int state_bound PROTO ((void)); +int state_renewing PROTO ((void)); +int state_rebinding PROTO ((void)); +TIME abs_time PROTO ((struct packet *, int)); +void deep_packet_copy PROTO ((struct packet *, struct packet *)); +void read_packet PROTO ((struct interface_info *, struct packet *)); +void send_packet_struct PROTO ((struct interface_info *, + u_long, struct packet *)); +void make_discover PROTO ((struct interface_info *, struct packet *)); +void make_request PROTO ((struct packet *, struct packet *)); +void make_release PROTO ((struct packet *, struct packet *)); /* db.c */ int write_lease PROTO ((struct lease *)); @@ -584,3 +614,16 @@ void convert_address_range PROTO ((FILE *, jrefproto)); void convert_date PROTO ((FILE *, jrefproto, char *)); void convert_numeric_aggregate PROTO ((FILE *, jrefproto, int, int, int, int)); void indent PROTO ((int)); + +/* route.c */ +void add_route_direct PROTO ((struct interface_info *, struct in_addr)); +void add_route_net PROTO ((struct interface_info *, struct in_addr, + struct in_addr)); +void add_route_default_gateway PROTO ((struct interface_info *, + struct in_addr)); +void remove_routes PROTO ((struct in_addr)); +void remove_if_route PROTO ((struct interface_info *, struct in_addr)); +void remove_all_if_routes PROTO ((struct interface_info *)); +void set_netmask PROTO ((struct interface_info *, struct in_addr)); +void set_broadcast_addr PROTO ((struct interface_info *, struct in_addr)); +void set_ip_address PROTO ((struct interface_info *, struct in_addr)); diff --git a/dispatch.c b/dispatch.c index 1985fb4e9..929c8b3d4 100644 --- a/dispatch.c +++ b/dispatch.c @@ -42,7 +42,7 @@ #ifndef lint static char copyright[] = -"$Id: dispatch.c,v 1.27 1996/11/08 20:06:29 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: dispatch.c,v 1.28 1997/01/02 12:00:16 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -162,6 +162,44 @@ void discover_interfaces (serverP) if (ifp -> ifr_addr.sa_family == AF_INET) { struct iaddr addr; +#if defined (SIOCGIFHWADDR) && !defined (AF_LINK) + struct ifreq ifr; + struct sockaddr sa; + int b, sk; + + /* Read the hardware address from this interface. */ + ifr = *ifp; + if (ioctl (sock, SIOCGIFHWADDR, &ifr) < 0) + error ("Can't get hardware address for %s: %m", + ifr.ifr_name); + + sa = *(struct sockaddr *)&ifr.ifr_hwaddr; + + switch (sa.sa_family) { + case ARPHRD_LOOPBACK: + /* ignore loopback interface */ + break; + + case ARPHRD_ETHER: + tmp -> hw_address.hlen = 6; + tmp -> hw_address.htype = ARPHRD_ETHER; + memcpy (tmp -> hw_address.haddr, + sa.sa_data, 6); + break; + + case ARPHRD_METRICOM: + tmp -> hw_address.hlen = 6; + tmp -> hw_address.htype = ARPHRD_METRICOM; + memcpy (tmp -> hw_address.haddr, + sa.sa_data, 6); + break; + + default: + error ("%s: unknown hardware address type %d", + ifr.ifr_name, sa.sa_family); + } +#endif /* defined (SIOCGIFHWADDR) && !defined (AF_LINK) */ + /* Get a pointer to the address... */ memcpy (&foo, &ifp -> ifr_addr, sizeof ifp -> ifr_addr); diff --git a/includes/cf/linux.h b/includes/cf/linux.h index 8e439d0b7..d08a986d0 100644 --- a/includes/cf/linux.h +++ b/includes/cf/linux.h @@ -60,6 +60,9 @@ typedef unsigned long u_int32_t; extern int h_errno; #include +#include +#include +#include #include /* gettimeofday()*/ #include /* also necessary */ diff --git a/includes/cf/netbsd.h b/includes/cf/netbsd.h index 23f0b30da..6e9ecc7b9 100644 --- a/includes/cf/netbsd.h +++ b/includes/cf/netbsd.h @@ -52,6 +52,10 @@ extern int h_errno; #include #include +#include +#include + +#define ifr_netmask ifr_addr /* Varargs stuff... */ #include diff --git a/includes/dhcp.h b/includes/dhcp.h index 23a8bf737..181277579 100644 --- a/includes/dhcp.h +++ b/includes/dhcp.h @@ -52,6 +52,7 @@ #define DHCP_OPTION_LEN (DHCP_MTU_MAX - DHCP_FIXED_LEN) #define BOOTP_MIN_LEN 300 +#define DHCP_MIN_LEN 548 struct dhcp_packet { u_int8_t op; /* Message opcode/type */ diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 34aed0b56..d62f78f2e 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -451,6 +451,9 @@ size_t receive_packet PROTO ((struct interface_info *, unsigned char *, size_t, struct sockaddr_in *, struct hardware *)); #endif +#if defined (USE_SOCKET_SEND) && !defined (USE_SOCKET_FALLBACK) +void if_enable PROTO ((struct interface_info *)); +#endif /* bpf.c */ #if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE) @@ -469,6 +472,9 @@ size_t receive_packet PROTO ((struct interface_info *, unsigned char *, size_t, struct sockaddr_in *, struct hardware *)); #endif +#if defined (USE_BPF_SEND) +void if_enable PROTO ((struct interface_info *)); +#endif /* nit.c */ #ifdef USE_NIT_SEND @@ -484,6 +490,9 @@ size_t receive_packet PROTO ((struct interface_info *, unsigned char *, size_t, struct sockaddr_in *, struct hardware *)); #endif +#if defined (USE_BPF_SEND) +void if_enable PROTO ((struct interface_info *)); +#endif /* raw.c */ #ifdef USE_RAW_SEND @@ -541,6 +550,27 @@ void dhcpack PROTO ((struct packet *)); void dhcpnak PROTO ((struct packet *)); void send_discover PROTO ((struct interface_info *)); void send_request PROTO ((struct packet *)); +void send_release PROTO ((struct packet *)); +void dhclient_fail PROTO ((void)); +void handle_kill PROTO ((int)); +void disable_interface PROTO ((struct interface_info *)); +void route_broadcasts PROTO ((struct interface_info *)); +void apply_parameters PROTO ((struct interface_info *, struct packet *)); +void dhclient_state_machine PROTO ((void)); +int state_init PROTO ((void)); +int state_selecting PROTO ((void)); +int state_requesting PROTO ((void)); +int state_bound PROTO ((void)); +int state_renewing PROTO ((void)); +int state_rebinding PROTO ((void)); +TIME abs_time PROTO ((struct packet *, int)); +void deep_packet_copy PROTO ((struct packet *, struct packet *)); +void read_packet PROTO ((struct interface_info *, struct packet *)); +void send_packet_struct PROTO ((struct interface_info *, + u_long, struct packet *)); +void make_discover PROTO ((struct interface_info *, struct packet *)); +void make_request PROTO ((struct packet *, struct packet *)); +void make_release PROTO ((struct packet *, struct packet *)); /* db.c */ int write_lease PROTO ((struct lease *)); @@ -584,3 +614,16 @@ void convert_address_range PROTO ((FILE *, jrefproto)); void convert_date PROTO ((FILE *, jrefproto, char *)); void convert_numeric_aggregate PROTO ((FILE *, jrefproto, int, int, int, int)); void indent PROTO ((int)); + +/* route.c */ +void add_route_direct PROTO ((struct interface_info *, struct in_addr)); +void add_route_net PROTO ((struct interface_info *, struct in_addr, + struct in_addr)); +void add_route_default_gateway PROTO ((struct interface_info *, + struct in_addr)); +void remove_routes PROTO ((struct in_addr)); +void remove_if_route PROTO ((struct interface_info *, struct in_addr)); +void remove_all_if_routes PROTO ((struct interface_info *)); +void set_netmask PROTO ((struct interface_info *, struct in_addr)); +void set_broadcast_addr PROTO ((struct interface_info *, struct in_addr)); +void set_ip_address PROTO ((struct interface_info *, struct in_addr)); diff --git a/nit.c b/nit.c index 43fa7e496..0de8d62bc 100644 --- a/nit.c +++ b/nit.c @@ -42,7 +42,7 @@ #ifndef lint static char copyright[] = -"$Id: nit.c,v 1.9 1996/09/05 23:56:52 mellon Exp $ Copyright (c) 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: nit.c,v 1.10 1997/01/02 12:00:17 mellon Exp $ Copyright (c) 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -329,3 +329,21 @@ size_t receive_packet (interface, buf, len, from, hfrom) return length; } #endif + +#if defined (USE_NIT_SEND) +void if_enable (interface) + struct interface_info *interface; +{ + struct ifreq ifr; + + /* Bring the interface down and then up again to clear + * all its routes. */ + strncpy(ifr.ifr_name, interface -> name, IFNAMSIZ); + if (ioctl(interface -> rfdesc, SIOCGIFFLAGS, &ifr) < 0) + error ("SIOCGIFFLAGS %s: %m", interface -> name); + + ifr.ifr_flags |= (IFF_UP|IFF_RUNNING); + if (ioctl(interface -> rfdesc, SIOCSIFFLAGS, &ifr) == -1) + error ("SIOCSIFFLAGS %s: %m", interface -> name); +} +#endif diff --git a/route.c b/route.c index 8b421b0e8..b1d8ec83f 100644 --- a/route.c +++ b/route.c @@ -1,62 +1,234 @@ -/* socket.c - - BSD socket interface code... */ - -/* - * Copyright (c) 1995, 1996 The Internet Software Consortium. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: +/* route.c * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of The Internet Software Consortium nor the names - * of its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * Routines for updating routing tables and configuring interfaces. + * + * Copyright 1996 The Board of Trustees of The Leland Stanford + * Junior University. All Rights Reserved. + * Code originally written by Elliot Poger (poger@leland.stanford.edu). * - * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND - * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This software has been written for the Internet Software Consortium - * by Ted Lemon in cooperation with Vixie - * Enterprises. To learn more about the Internet Software Consortium, - * see ``http://www.vix.com/isc''. To learn more about Vixie - * Enterprises, see ``http://www.vix.com''. + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. Stanford University + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. */ -#ifndef lint -static char copyright[] = -"$Id: route.c,v 1.2 1996/08/27 09:53:58 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; -#endif /* not lint */ - #include "dhcpd.h" -#if defined (OLD_ROUTE_HACK) -#include +/* Add a route to this destination through this interface. */ +void add_route_direct(interface, destination) + struct interface_info *interface; + struct in_addr destination; +{ + note ("add_route_direct %s: %s", + interface -> name, inet_ntoa (destination)); +#if 0 + struct in_addr directmask; + + directmask.s_addr = htonl (INADDR_BROADCAST); /* this addr only */ + add_route_net(interface, destination, &directmask); +#endif +} + +/* Add a route to this subnet through this interface. */ +void add_route_net(interface, destination, netmask) + struct interface_info *interface; + struct in_addr destination; + struct in_addr netmask; +{ + char nbuf [128]; + + strncpy (nbuf, inet_ntoa (netmask), sizeof nbuf); + nbuf [(sizeof nbuf) - 1] = 0; + + note ("add_route_net %s: %s %s", + interface -> name, inet_ntoa (destination), nbuf); +#if 0 + int sock; + struct sockaddr_in *sin; + struct rtentry rt; + + memset((char *) &rt, 0, sizeof(struct rtentry)); + sin = (struct sockaddr_in *) &rt.rt_dst; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = destination.s_addr; + sin = (struct sockaddr_in *) &rt.rt_genmask; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = netmask.s_addr; + rt.rt_flags = RTF_UP; + rt.rt_dev = interface->name; + if (ioctl(interface -> rfdesc, SIOCADDRT, &rt) < 0) + error ("Can't add route to %s through %s: %m", + inet_ntoa (destination), interface->name); +#endif +} -route_hack (sock) - int sock; +/* Add a route to the default gateway through this interface. */ +void add_route_default_gateway(interface, gateway) + struct interface_info *interface; + struct in_addr gateway; { - int rsock = socket (PF_ROUTE, SOCK_RAW, AF_INET); - struct rt_msghdr + note ("add_route_default_gateway %s: %s", + interface -> name, inet_ntoa (gateway)); +#if 0 + int sock; + struct sockaddr_in *sin; + struct rtentry rt; - if (rsock < 0) - error ("Can't make routing socket: %m"); + /* Route through the gateway. */ + memset((char *) &rt, 0, sizeof(struct rtentry)); + sin = (struct sockaddr_in *) &rt.rt_dst; + sin->sin_family = AF_INET; + sin = (struct sockaddr_in *) &rt.rt_gateway; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = gateway.s_addr; + rt.rt_flags = RTF_UP | RTF_GATEWAY; + rt.rt_dev = interface->name; + if (ioctl(interface -> rfdesc, SIOCADDRT, &rt) < 0) + error ("Can't add route to default gateway"); +#endif +} + + +/* Remove all routes matching the specified destination. */ +void remove_routes(destination) + struct in_addr destination; +{ + note ("remove_routes: %s", inet_ntoa (destination)); +#if 0 + int sock; + struct ifreq ifr; + struct sockaddr_in *sin; + struct rtentry rt; + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + error("Can't open socket to remove routes"); + } + + /* Remove all routes to this IP destination. */ + memset((char *) &rt, 0, sizeof(struct rtentry)); + sin = (struct sockaddr_in *) &rt.rt_dst; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = destination->s_addr; + while (ioctl(sock, SIOCDELRT, &rt) >= 0) + ; + close(sock); +#endif +} + +/* Remove routes on the specified interface matching the specified + destination. */ +void remove_if_route (interface, destination) + struct interface_info *interface; + struct in_addr destination; +{ + note ("remove_if_routes %s: %s", + interface -> name, inet_ntoa (destination)); +#if 0 + int sock; + struct ifreq ifr; + struct sockaddr_in *sin; + struct rtentry rt; + + /* Remove one specific route. */ + /* XXX: does this even work? */ + memset((char *) &rt, 0, sizeof(struct rtentry)); + sin = (struct sockaddr_in *) &rt.rt_dst; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = destination.s_addr; + rt.rt_dev = interface->name; + if (ioctl(interface -> rfdesc, SIOCDELRT, &rt) == -1) + warn("Error removing route."); +#endif +} + +/* Remove all routes on the specified interface. */ + +void remove_all_if_routes(interface) + struct interface_info *interface; +{ + note ("remove_all_if_routes %s", interface -> name); +#if 0 + struct ifreq ifr; + + /* Bring the interface down and then up again to clear + * all its routes. */ + strncpy(ifr.ifr_name, interface->name, IFNAMSIZ); + if (ioctl(interface -> rfdesc, SIOCGIFFLAGS, &ifr) == -1) + error ("SIOCGIFFLAGS %s: %m", interface -> name); + + ifr.ifr_flags &= ~(IFF_UP|IFF_RUNNING); + if (ioctl(interface -> rfdesc, SIOCSIFFLAGS, &ifr) == -1) + error ("Can't bring down interface"); + + strncpy(ifr.ifr_name,interface->name,IFNAMSIZ); + ifr.ifr_flags |= (IFF_UP|IFF_RUNNING); + if (ioctl(interface -> rfdesc, SIOCSIFFLAGS, &ifr) == -1) + error("Can't bring interface back up"); +#endif +} + +/* Set the netmask (in network byte order!) of this interface. */ +void set_netmask(interface, netmask) + struct interface_info *interface; + struct in_addr netmask; +{ + note ("set_netmask %s: %s", + interface -> name, inet_ntoa (netmask)); +#if 0 + int sock; + struct ifreq ifr; + + if (ioctl (interface -> rfdesc, SIOCGIFNETMASK, &ifr) == -1) + error ("Can't get old netmask of interface"); + + (*(struct sockaddr_in *)&ifr.ifr_netmask).sin_addr = netmask; + if (ioctl (interface -> rfdesc, SIOCSIFNETMASK, &ifr) == -1) + error ("Can't set new netmask"); +#endif +} + +/* Set the broadcast address (in network byte order!) of this interface. */ +void set_broadcast_addr(interface, broadcast_addr) + struct interface_info *interface; + struct in_addr broadcast_addr; +{ + note ("set_broadcast_addr %s: %s", + interface -> name, inet_ntoa (broadcast_addr)); +#if 0 + int sock; + struct ifreq ifr; + + if (ioctl(interface -> rfdesc, SIOCGIFBRDADDR, &ifr) == -1) + error("Can't get old broadcast address of interface"); + + (*(struct sockaddr_in *)&ifr.ifr_broadaddr).sin_addr = broadcast_addr; + if (ioctl(sock, SIOCSIFBRDADDR, &ifr) == -1) { + error("Can't set new broadcast address"); + } +#endif +} + +/* Set the IP address (in network byte order!) of this interface. */ +void set_ip_address(interface, ip_addr) + struct interface_info *interface; + struct in_addr ip_addr; +{ + note ("set_ip_address %s: %s", + interface -> name, inet_ntoa (ip_addr)); +#if 0 + int sock; + struct ifreq ifr; + strncpy(ifr.ifr_name, interface->name, IFNAMSIZ); + if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) + error ("Can't get old IP address of interface"); + (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr = ip_addr; + if (ioctl(sock, SIOCSIFADDR, &ifr) < 0) + error("Can't set IP address"); + } +#endif +} diff --git a/socket.c b/socket.c index dcd758a57..7e7bc912a 100644 --- a/socket.c +++ b/socket.c @@ -40,9 +40,17 @@ * Enterprises, see ``http://www.vix.com''. */ +/* SO_BINDTODEVICE support added by Elliot Poger (poger@leland.stanford.edu). + * This sockopt allows a socket to be bound to a particular interface, + * thus enabling the use of DHCPD on a multihomed host. + * If SO_BINDTODEVICE is defined in your system header files, the use of + * this sockopt will be automatically enabled. + * I have implemented it under Linux; other systems should be doable also. + */ + #ifndef lint static char copyright[] = -"$Id: socket.c,v 1.16 1996/08/27 09:54:48 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: socket.c,v 1.17 1997/01/02 12:00:18 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -62,6 +70,8 @@ int if_register_socket (info, interface) struct sockaddr_in name; int sock; int flag; + +#ifndef SO_BINDTODEVICE static int once = 0; /* Make sure only one interface is registered. */ @@ -72,22 +82,8 @@ int if_register_socket (info, interface) "you must compile in BPF or NIT support. If neither ", "option is supported on your system, please let us ", "know."); - - /* Technically, we need to know what interface every packet - comes in on, which means that we can only operate on a - machine with a single interface configured. However, - we generally don't expect to get broadcast packets on - point-to-point interfaces, so we can bend the rules a bit - and not count them. This won't allow DHCP-over-PPP, - but it's probably right in a lot of cases, and the issue - will have to be revisited when DHCP-over-PPP support is - done. Currently we determine whether an interface is - point-to-point by seeing if it has a link-level address. - This only works on 4.4BSD and derivative networking. */ -#ifdef AF_LINK - if (info -> hw_address.hlen) /* XXX */ + once = 1; #endif - once = 1; /* Set up the address we're going to bind to. */ name.sin_family = AF_INET; @@ -111,10 +107,26 @@ int if_register_socket (info, interface) (char *)&flag, sizeof flag) < 0) error ("Can't set SO_BROADCAST option on dhcp socket: %m"); +#ifndef USE_SOCKET_FALLBACK + /* The following will make all-ones broadcasts go out this interface + * on those platforms which use the standard sockets API (assuming + * the OS-specific routines called by enable_sending() are present + * for this platform). */ + if_enable (info); +#endif + /* Bind the socket to this interface's IP address. */ if (bind (sock, (struct sockaddr *)&name, sizeof name) < 0) error ("Can't bind to dhcp address: %m"); +#ifdef SO_BINDTODEVICE + /* Bind this socket to this interface. */ + if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, + (char *)interface, sizeof(*interface)) < 0) { + error("setting SO_BINDTODEVICE"); + } +#endif + return sock; } #endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE */ @@ -197,3 +209,30 @@ size_t fallback_discard (interface) (struct sockaddr *)&from, &flen); } #endif /* USE_SOCKET_RECEIVE */ + +#if defined (USE_SOCKET_SEND) && !defined (USE_SOCKET_FALLBACK) +/* If we're using the standard socket API without SO_BINDTODEVICE, + * we need this kludge to force DHCP broadcasts to go out + * this interface, even though it's not available for general + * use until we get a lease! + * This should work _OK_, but it will cause ALL all-ones + * broadcasts on this host to go out this interface--it + * could interfere with other interfaces. And God help you + * if you run this on multiple interfaces simultaneously. + * SO_BINDTODEVICE really is better! */ +void if_enable (interface) + struct interface_info *interface; +{ +#ifndef SO_BINDTODEVICE + struct in_addr broad_addr; + broad_addr.s_addr = htonl(INADDR_BROADCAST); + + /* Delete old routes for broadcast address. */ + remove_routes(NULL, &broad_addr); + + /* Add a route for broadcast address to this interface. */ + /* POTENTIAL PROBLEM: Don't do this to more than one interface! */ + add_route_direct(interface, &broad_addr); +#endif +} +#endif