diff options
author | obrien <obrien@FreeBSD.org> | 1999-02-10 09:10:13 +0000 |
---|---|---|
committer | obrien <obrien@FreeBSD.org> | 1999-02-10 09:10:13 +0000 |
commit | 117fbe9a9bc304e0e099d61d60b1a5d016afff04 (patch) | |
tree | 238471ef1d73dff2645de45ab083d2123bf56b83 /contrib/isc-dhcp/client | |
download | FreeBSD-src-117fbe9a9bc304e0e099d61d60b1a5d016afff04.zip FreeBSD-src-117fbe9a9bc304e0e099d61d60b1a5d016afff04.tar.gz |
Virgin import of ISC-DHCP v2.0b1pl6
Diffstat (limited to 'contrib/isc-dhcp/client')
-rw-r--r-- | contrib/isc-dhcp/client/clparse.c | 1026 | ||||
-rw-r--r-- | contrib/isc-dhcp/client/dhclient-script.8 | 191 | ||||
-rw-r--r-- | contrib/isc-dhcp/client/dhclient.8 | 163 | ||||
-rw-r--r-- | contrib/isc-dhcp/client/dhclient.c | 2052 | ||||
-rw-r--r-- | contrib/isc-dhcp/client/dhclient.conf | 36 | ||||
-rw-r--r-- | contrib/isc-dhcp/client/dhclient.conf.5 | 543 | ||||
-rw-r--r-- | contrib/isc-dhcp/client/dhclient.leases.5 | 62 | ||||
-rwxr-xr-x | contrib/isc-dhcp/client/scripts/freebsd | 174 |
8 files changed, 4247 insertions, 0 deletions
diff --git a/contrib/isc-dhcp/client/clparse.c b/contrib/isc-dhcp/client/clparse.c new file mode 100644 index 0000000..37fafd3 --- /dev/null +++ b/contrib/isc-dhcp/client/clparse.c @@ -0,0 +1,1026 @@ +/* clparse.c + + Parser for dhclient config and lease files... */ + +/* + * Copyright (c) 1997 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: + * + * 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. + * + * 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 <mellon@fugue.com> 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''. + */ + +#ifndef lint +static char copyright[] = +"$Id: clparse.c,v 1.13.2.1 1998/06/25 21:11:27 mellon Exp $ Copyright (c) 1997 The Internet Software Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#include "dhctoken.h" + +static TIME parsed_time; + +struct client_config top_level_config; +u_int32_t requested_lease_time; + +/* client-conf-file :== client-declarations EOF + client-declarations :== <nil> + | client-declaration + | client-declarations client-declaration */ + +int read_client_conf () +{ + FILE *cfile; + char *val; + int token; + int declaration = 0; + struct client_config *config; + struct client_state *state; + struct interface_info *ip; + + new_parse (path_dhclient_conf); + + /* Set up the initial dhcp option universe. */ + initialize_universes (); + + /* Initialize the top level client configuration. */ + memset (&top_level_config, 0, sizeof top_level_config); + + /* Set some defaults... */ + top_level_config.timeout = 60; + top_level_config.select_interval = 0; + top_level_config.reboot_timeout = 10; + top_level_config.retry_interval = 300; + top_level_config.backoff_cutoff = 120; + top_level_config.initial_interval = 10; + top_level_config.bootp_policy = ACCEPT; + top_level_config.script_name = "/etc/dhclient-script"; + top_level_config.requested_options + [top_level_config.requested_option_count++] = + DHO_SUBNET_MASK; + top_level_config.requested_options + [top_level_config.requested_option_count++] = + DHO_BROADCAST_ADDRESS; + top_level_config.requested_options + [top_level_config.requested_option_count++] = + DHO_TIME_OFFSET; + top_level_config.requested_options + [top_level_config.requested_option_count++] = + DHO_ROUTERS; + top_level_config.requested_options + [top_level_config.requested_option_count++] = + DHO_DOMAIN_NAME; + top_level_config.requested_options + [top_level_config.requested_option_count++] = + DHO_DOMAIN_NAME_SERVERS; + top_level_config.requested_options + [top_level_config.requested_option_count++] = + DHO_HOST_NAME; + requested_lease_time = 7200; + top_level_config.send_options [DHO_DHCP_LEASE_TIME].data + = (unsigned char *)&requested_lease_time; + top_level_config.send_options [DHO_DHCP_LEASE_TIME].len + = sizeof requested_lease_time; + + if ((cfile = fopen (path_dhclient_conf, "r")) == NULL) + error ("Can't open %s: %m", path_dhclient_conf); + do { + token = peek_token (&val, cfile); + if (token == EOF) + break; + parse_client_statement (cfile, (struct interface_info *)0, + &top_level_config); + } while (1); + token = next_token (&val, cfile); /* Clear the peek buffer */ + + /* Set up state and config structures for clients that don't + have per-interface configuration declarations. */ + config = (struct client_config *)0; + for (ip = interfaces; ip; ip = ip -> next) { + if (!ip -> client) { + ip -> client = (struct client_state *) + malloc (sizeof (struct client_state)); + if (!ip -> client) + error ("no memory for client state."); + memset (ip -> client, 0, sizeof *(ip -> client)); + } + if (!ip -> client -> config) { + if (!config) { + config = (struct client_config *) + malloc (sizeof (struct client_config)); + if (!config) + error ("no memory for client config."); + memcpy (config, &top_level_config, + sizeof top_level_config); + } + ip -> client -> config = config; + } + } + + return !warnings_occurred; +} + +/* lease-file :== client-lease-statements EOF + client-lease-statements :== <nil> + | client-lease-statements LEASE client-lease-statement */ + +void read_client_leases () +{ + FILE *cfile; + char *val; + int token; + + new_parse (path_dhclient_db); + + /* Open the lease file. If we can't open it, just return - + we can safely trust the server to remember our state. */ + if ((cfile = fopen (path_dhclient_db, "r")) == NULL) + return; + do { + token = next_token (&val, cfile); + if (token == EOF) + break; + if (token != LEASE) { + warn ("Corrupt lease file - possible data loss!"); + skip_to_semi (cfile); + break; + } else + parse_client_lease_statement (cfile, 0); + + } while (1); +} + +/* client-declaration :== + SEND option-decl | + DEFAULT option-decl | + SUPERSEDE option-decl | + PREPEND option-decl | + APPEND option-decl | + hardware-declaration | + REQUEST option-list | + REQUIRE option-list | + TIMEOUT number | + RETRY number | + REBOOT number | + SELECT_TIMEOUT number | + SCRIPT string | + interface-declaration | + LEASE client-lease-statement | + ALIAS client-lease-statement */ + +void parse_client_statement (cfile, ip, config) + FILE *cfile; + struct interface_info *ip; + struct client_config *config; +{ + int token; + char *val; + struct option *option; + + switch (next_token (&val, cfile)) { + case SEND: + parse_option_decl (cfile, &config -> send_options [0]); + return; + + case DEFAULT: + option = parse_option_decl (cfile, &config -> defaults [0]); + if (option) + config -> default_actions [option -> code] = + ACTION_DEFAULT; + return; + + case SUPERSEDE: + option = parse_option_decl (cfile, &config -> defaults [0]); + if (option) + config -> default_actions [option -> code] = + ACTION_SUPERSEDE; + return; + + case APPEND: + option = parse_option_decl (cfile, &config -> defaults [0]); + if (option) + config -> default_actions [option -> code] = + ACTION_APPEND; + return; + + case PREPEND: + option = parse_option_decl (cfile, &config -> defaults [0]); + if (option) + config -> default_actions [option -> code] = + ACTION_PREPEND; + return; + + case MEDIA: + parse_string_list (cfile, &config -> media, 1); + return; + + case HARDWARE: + if (ip) { + parse_hardware_param (cfile, &ip -> hw_address); + } else { + parse_warn ("hardware address parameter %s", + "not allowed here."); + skip_to_semi (cfile); + } + return; + + case REQUEST: + config -> requested_option_count = + parse_option_list (cfile, config -> requested_options); + return; + + case REQUIRE: + memset (config -> required_options, 0, + sizeof config -> required_options); + parse_option_list (cfile, config -> required_options); + return; + + case TIMEOUT: + parse_lease_time (cfile, &config -> timeout); + return; + + case RETRY: + parse_lease_time (cfile, &config -> retry_interval); + return; + + case SELECT_TIMEOUT: + parse_lease_time (cfile, &config -> select_interval); + return; + + case REBOOT: + parse_lease_time (cfile, &config -> reboot_timeout); + return; + + case BACKOFF_CUTOFF: + parse_lease_time (cfile, &config -> backoff_cutoff); + return; + + case INITIAL_INTERVAL: + parse_lease_time (cfile, &config -> initial_interval); + return; + + case SCRIPT: + config -> script_name = parse_string (cfile); + return; + + case INTERFACE: + if (ip) + parse_warn ("nested interface declaration."); + parse_interface_declaration (cfile, config); + return; + + case LEASE: + parse_client_lease_statement (cfile, 1); + return; + + case ALIAS: + parse_client_lease_statement (cfile, 2); + return; + + case REJECT: + parse_reject_statement (cfile, config); + return; + + default: + parse_warn ("expecting a statement."); + skip_to_semi (cfile); + break; + } + token = next_token (&val, cfile); + if (token != SEMI) { + parse_warn ("semicolon expected."); + skip_to_semi (cfile); + } +} + +int parse_X (cfile, buf, max) + FILE *cfile; + u_int8_t *buf; + int max; +{ + int token; + char *val; + int len; + u_int8_t *s; + + token = peek_token (&val, cfile); + if (token == NUMBER_OR_NAME || token == NUMBER) { + len = 0; + do { + token = next_token (&val, cfile); + if (token != NUMBER && token != NUMBER_OR_NAME) { + parse_warn ("expecting hexadecimal constant."); + skip_to_semi (cfile); + return 0; + } + convert_num (&buf [len], val, 16, 8); + if (len++ > max) { + parse_warn ("hexadecimal constant too long."); + skip_to_semi (cfile); + return 0; + } + token = peek_token (&val, cfile); + if (token == COLON) + token = next_token (&val, cfile); + } while (token == COLON); + val = (char *)buf; + } else if (token == STRING) { + token = next_token (&val, cfile); + len = strlen (val); + if (len + 1 > max) { + parse_warn ("string constant too long."); + skip_to_semi (cfile); + return 0; + } + memcpy (buf, val, len + 1); + } else { + parse_warn ("expecting string or hexadecimal data"); + skip_to_semi (cfile); + return 0; + } + return len; +} + +/* option-list :== option_name | + option_list COMMA option_name */ + +int parse_option_list (cfile, list) + FILE *cfile; + u_int8_t *list; +{ + int ix, i; + int token; + char *val; + + ix = 0; + do { + token = next_token (&val, cfile); + if (!is_identifier (token)) { + parse_warn ("expected option name."); + skip_to_semi (cfile); + return 0; + } + for (i = 0; i < 256; i++) { + if (!strcasecmp (dhcp_options [i].name, val)) + break; + } + if (i == 256) { + parse_warn ("%s: expected option name."); + skip_to_semi (cfile); + return 0; + } + list [ix++] = i; + if (ix == 256) { + parse_warn ("%s: too many options.", val); + skip_to_semi (cfile); + return 0; + } + token = next_token (&val, cfile); + } while (token == COMMA); + if (token != SEMI) { + parse_warn ("expecting semicolon."); + skip_to_semi (cfile); + return 0; + } + return ix; +} + +/* interface-declaration :== + INTERFACE string LBRACE client-declarations RBRACE */ + +void parse_interface_declaration (cfile, outer_config) + FILE *cfile; + struct client_config *outer_config; +{ + int token; + char *val; + + struct interface_info dummy_interface, *ip; + struct client_state dummy_state; + struct client_config dummy_config; + + token = next_token (&val, cfile); + if (token != STRING) { + parse_warn ("expecting interface name (in quotes)."); + skip_to_semi (cfile); + return; + } + + ip = interface_or_dummy (val); + + if (!ip -> client) + make_client_state (ip); + + if (!ip -> client -> config) + make_client_config (ip, outer_config); + + ip -> flags &= ~INTERFACE_AUTOMATIC; + interfaces_requested = 1; + + token = next_token (&val, cfile); + if (token != LBRACE) { + parse_warn ("expecting left brace."); + skip_to_semi (cfile); + return; + } + + do { + token = peek_token (&val, cfile); + if (token == EOF) { + parse_warn ("unterminated interface declaration."); + return; + } + if (token == RBRACE) + break; + parse_client_statement (cfile, ip, ip -> client -> config); + } while (1); + token = next_token (&val, cfile); +} + +struct interface_info *interface_or_dummy (name) + char *name; +{ + struct interface_info *ip; + + /* Find the interface (if any) that matches the name. */ + for (ip = interfaces; ip; ip = ip -> next) { + if (!strcmp (ip -> name, name)) + break; + } + + /* If it's not a real interface, see if it's on the dummy list. */ + if (!ip) { + for (ip = dummy_interfaces; ip; ip = ip -> next) { + if (!strcmp (ip -> name, name)) + break; + } + } + + /* If we didn't find an interface, make a dummy interface as + a placeholder. */ + if (!ip) { + ip = ((struct interface_info *)malloc (sizeof *ip)); + if (!ip) + error ("Insufficient memory to record interface %s", + name); + memset (ip, 0, sizeof *ip); + strcpy (ip -> name, name); + ip -> next = dummy_interfaces; + dummy_interfaces = ip; + } + return ip; +} + +void make_client_state (ip) + struct interface_info *ip; +{ + ip -> client = + ((struct client_state *)malloc (sizeof *(ip -> client))); + if (!ip -> client) + error ("no memory for state on %s\n", ip -> name); + memset (ip -> client, 0, sizeof *(ip -> client)); +} + +void make_client_config (ip, config) + struct interface_info *ip; + struct client_config *config; +{ + ip -> client -> config = + ((struct client_config *) + malloc (sizeof (struct client_config))); + if (!ip -> client -> config) + error ("no memory for config for %s\n", ip -> name); + memset (ip -> client -> config, 0, + sizeof *(ip -> client -> config)); + memcpy (ip -> client -> config, config, sizeof *config); +} + +/* client-lease-statement :== + RBRACE client-lease-declarations LBRACE + + client-lease-declarations :== + <nil> | + client-lease-declaration | + client-lease-declarations client-lease-declaration */ + + +void parse_client_lease_statement (cfile, is_static) + FILE *cfile; + int is_static; +{ + struct client_lease *lease, *lp, *pl; + struct interface_info *ip; + int token; + char *val; + + token = next_token (&val, cfile); + if (token != LBRACE) { + parse_warn ("expecting left brace."); + skip_to_semi (cfile); + return; + } + + lease = (struct client_lease *)malloc (sizeof (struct client_lease)); + if (!lease) + error ("no memory for lease.\n"); + memset (lease, 0, sizeof *lease); + lease -> is_static = is_static; + + ip = (struct interface_info *)0; + + do { + token = peek_token (&val, cfile); + if (token == EOF) { + parse_warn ("unterminated lease declaration."); + return; + } + if (token == RBRACE) + break; + parse_client_lease_declaration (cfile, lease, &ip); + } while (1); + token = next_token (&val, cfile); + + /* If the lease declaration didn't include an interface + declaration that we recognized, it's of no use to us. */ + if (!ip) { + free_client_lease (lease); + return; + } + + /* Make sure there's a client state structure... */ + if (!ip -> client) + make_client_state (ip); + + /* If this is an alias lease, it doesn't need to be sorted in. */ + if (is_static == 2) { + ip -> client -> alias = lease; + return; + } + + /* The new lease may supersede a lease that's not the + active lease but is still on the lease list, so scan the + lease list looking for a lease with the same address, and + if we find it, toss it. */ + pl = (struct client_lease *)0; + for (lp = ip -> client -> leases; lp; lp = lp -> next) { + if (lp -> address.len == lease -> address.len && + !memcmp (lp -> address.iabuf, lease -> address.iabuf, + lease -> address.len)) { + if (pl) + pl -> next = lp -> next; + else + ip -> client -> leases = lp -> next; + free_client_lease (lp); + break; + } + } + + /* If this is a preloaded lease, just put it on the list of recorded + leases - don't make it the active lease. */ + if (is_static) { + lease -> next = ip -> client -> leases; + ip -> client -> leases = lease; + return; + } + + /* The last lease in the lease file on a particular interface is + the active lease for that interface. Of course, we don't know + what the last lease in the file is until we've parsed the whole + file, so at this point, we assume that the lease we just parsed + is the active lease for its interface. If there's already + an active lease for the interface, and this lease is for the same + ip address, then we just toss the old active lease and replace + it with this one. If this lease is for a different address, + then if the old active lease has expired, we dump it; if not, + we put it on the list of leases for this interface which are + still valid but no longer active. */ + if (ip -> client -> active) { + if (ip -> client -> active -> expiry < cur_time) + free_client_lease (ip -> client -> active); + else if (ip -> client -> active -> address.len == + lease -> address.len && + !memcmp (ip -> client -> active -> address.iabuf, + lease -> address.iabuf, + lease -> address.len)) + free_client_lease (ip -> client -> active); + else { + ip -> client -> active -> next = + ip -> client -> leases; + ip -> client -> leases = ip -> client -> active; + } + } + ip -> client -> active = lease; + + /* phew. */ +} + +/* client-lease-declaration :== + BOOTP | + INTERFACE string | + FIXED_ADDR ip_address | + FILENAME string | + SERVER_NAME string | + OPTION option-decl | + RENEW time-decl | + REBIND time-decl | + EXPIRE time-decl */ + +void parse_client_lease_declaration (cfile, lease, ipp) + FILE *cfile; + struct client_lease *lease; + struct interface_info **ipp; +{ + int token; + char *val; + char *t, *n; + struct interface_info *ip; + + switch (next_token (&val, cfile)) { + case BOOTP: + lease -> is_bootp = 1; + break; + + case INTERFACE: + token = next_token (&val, cfile); + if (token != STRING) { + parse_warn ("expecting interface name (in quotes)."); + skip_to_semi (cfile); + break; + } + ip = interface_or_dummy (val); + *ipp = ip; + break; + + case FIXED_ADDR: + if (!parse_ip_addr (cfile, &lease -> address)) + return; + break; + + case MEDIUM: + parse_string_list (cfile, &lease -> medium, 0); + return; + + case FILENAME: + lease -> filename = parse_string (cfile); + return; + + case SERVER_NAME: + lease -> server_name = parse_string (cfile); + return; + + case RENEW: + lease -> renewal = parse_date (cfile); + return; + + case REBIND: + lease -> rebind = parse_date (cfile); + return; + + case EXPIRE: + lease -> expiry = parse_date (cfile); + return; + + case OPTION: + parse_option_decl (cfile, lease -> options); + return; + + default: + parse_warn ("expecting lease declaration."); + skip_to_semi (cfile); + break; + } + token = next_token (&val, cfile); + if (token != SEMI) { + parse_warn ("expecting semicolon."); + skip_to_semi (cfile); + } +} + +struct option *parse_option_decl (cfile, options) + FILE *cfile; + struct option_data *options; +{ + char *val; + int token; + u_int8_t buf [4]; + u_int8_t hunkbuf [1024]; + int hunkix = 0; + char *vendor; + char *fmt; + struct universe *universe; + struct option *option; + struct iaddr ip_addr; + u_int8_t *dp; + int len; + int nul_term = 0; + + token = next_token (&val, cfile); + if (!is_identifier (token)) { + parse_warn ("expecting identifier after option keyword."); + if (token != SEMI) + skip_to_semi (cfile); + return (struct option *)0; + } + vendor = malloc (strlen (val) + 1); + if (!vendor) + error ("no memory for vendor information."); + strcpy (vendor, val); + token = peek_token (&val, cfile); + if (token == DOT) { + /* Go ahead and take the DOT token... */ + token = next_token (&val, cfile); + + /* The next token should be an identifier... */ + token = next_token (&val, cfile); + if (!is_identifier (token)) { + parse_warn ("expecting identifier after '.'"); + if (token != SEMI) + skip_to_semi (cfile); + return (struct option *)0; + } + + /* Look up the option name hash table for the specified + vendor. */ + universe = ((struct universe *) + hash_lookup (&universe_hash, + (unsigned char *)vendor, 0)); + /* If it's not there, we can't parse the rest of the + declaration. */ + if (!universe) { + parse_warn ("no vendor named %s.", vendor); + skip_to_semi (cfile); + return (struct option *)0; + } + } else { + /* Use the default hash table, which contains all the + standard dhcp option names. */ + val = vendor; + universe = &dhcp_universe; + } + + /* Look up the actual option info... */ + option = (struct option *)hash_lookup (universe -> hash, + (unsigned char *)val, 0); + + /* If we didn't get an option structure, it's an undefined option. */ + if (!option) { + if (val == vendor) + parse_warn ("no option named %s", val); + else + parse_warn ("no option named %s for vendor %s", + val, vendor); + skip_to_semi (cfile); + return (struct option *)0; + } + + /* Free the initial identifier token. */ + free (vendor); + + /* Parse the option data... */ + do { + /* Set a flag if this is an array of a simple type (i.e., + not an array of pairs of IP addresses, or something + like that. */ + int uniform = option -> format [1] == 'A'; + + for (fmt = option -> format; *fmt; fmt++) { + if (*fmt == 'A') + break; + switch (*fmt) { + case 'X': + len = parse_X (cfile, &hunkbuf [hunkix], + sizeof hunkbuf - hunkix); + hunkix += len; + break; + + case 't': /* Text string... */ + token = next_token (&val, cfile); + if (token != STRING) { + parse_warn ("expecting string."); + skip_to_semi (cfile); + return (struct option *)0; + } + len = strlen (val); + if (hunkix + len + 1 > sizeof hunkbuf) { + parse_warn ("option data buffer %s", + "overflow"); + skip_to_semi (cfile); + return (struct option *)0; + } + memcpy (&hunkbuf [hunkix], val, len + 1); + nul_term = 1; + hunkix += len; + break; + + case 'I': /* IP address. */ + if (!parse_ip_addr (cfile, &ip_addr)) + return (struct option *)0; + len = ip_addr.len; + dp = ip_addr.iabuf; + + alloc: + if (hunkix + len > sizeof hunkbuf) { + parse_warn ("option data buffer %s", + "overflow"); + skip_to_semi (cfile); + return (struct option *)0; + } + memcpy (&hunkbuf [hunkix], dp, len); + hunkix += len; + break; + + case 'L': /* Unsigned 32-bit integer... */ + case 'l': /* Signed 32-bit integer... */ + token = next_token (&val, cfile); + if (token != NUMBER) { + need_number: + parse_warn ("expecting number."); + if (token != SEMI) + skip_to_semi (cfile); + return (struct option *)0; + } + convert_num (buf, val, 0, 32); + len = 4; + dp = buf; + goto alloc; + + case 's': /* Signed 16-bit integer. */ + case 'S': /* Unsigned 16-bit integer. */ + token = next_token (&val, cfile); + if (token != NUMBER) + goto need_number; + convert_num (buf, val, 0, 16); + len = 2; + dp = buf; + goto alloc; + + case 'b': /* Signed 8-bit integer. */ + case 'B': /* Unsigned 8-bit integer. */ + token = next_token (&val, cfile); + if (token != NUMBER) + goto need_number; + convert_num (buf, val, 0, 8); + len = 1; + dp = buf; + goto alloc; + + case 'f': /* Boolean flag. */ + token = next_token (&val, cfile); + if (!is_identifier (token)) { + parse_warn ("expecting identifier."); + bad_flag: + if (token != SEMI) + skip_to_semi (cfile); + return (struct option *)0; + } + if (!strcasecmp (val, "true") + || !strcasecmp (val, "on")) + buf [0] = 1; + else if (!strcasecmp (val, "false") + || !strcasecmp (val, "off")) + buf [0] = 0; + else { + parse_warn ("expecting boolean."); + goto bad_flag; + } + len = 1; + dp = buf; + goto alloc; + + default: + warn ("Bad format %c in parse_option_param.", + *fmt); + skip_to_semi (cfile); + return (struct option *)0; + } + } + token = next_token (&val, cfile); + } while (*fmt == 'A' && token == COMMA); + + if (token != SEMI) { + parse_warn ("semicolon expected."); + skip_to_semi (cfile); + return (struct option *)0; + } + + options [option -> code].data = + (unsigned char *)malloc (hunkix + nul_term); + if (!options [option -> code].data) + error ("out of memory allocating option data."); + memcpy (options [option -> code].data, hunkbuf, hunkix + nul_term); + options [option -> code].len = hunkix; + return option; +} + +void parse_string_list (cfile, lp, multiple) + FILE *cfile; + struct string_list **lp; + int multiple; +{ + int token; + char *val; + struct string_list *cur, *tmp; + + /* Find the last medium in the media list. */ + if (*lp) { + for (cur = *lp; cur -> next; cur = cur -> next) + ; + } else { + cur = (struct string_list *)0; + } + + do { + token = next_token (&val, cfile); + if (token != STRING) { + parse_warn ("Expecting media options."); + skip_to_semi (cfile); + return; + } + + tmp = (struct string_list *)malloc (strlen (val) + 1 + + sizeof + (struct string_list *)); + if (!tmp) + error ("no memory for string list entry."); + + strcpy (tmp -> string, val); + tmp -> next = (struct string_list *)0; + + /* Store this medium at the end of the media list. */ + if (cur) + cur -> next = tmp; + else + *lp = tmp; + cur = tmp; + + token = next_token (&val, cfile); + } while (multiple && token == COMMA); + + if (token != SEMI) { + parse_warn ("expecting semicolon."); + skip_to_semi (cfile); + } +} + +void parse_reject_statement (cfile, config) + FILE *cfile; + struct client_config *config; +{ + int token; + char *val; + struct iaddr addr; + struct iaddrlist *list; + + do { + if (!parse_ip_addr (cfile, &addr)) { + parse_warn ("expecting IP address."); + skip_to_semi (cfile); + return; + } + + list = (struct iaddrlist *)malloc (sizeof (struct iaddrlist)); + if (!list) + error ("no memory for reject list!"); + + list -> addr = addr; + list -> next = config -> reject_list; + config -> reject_list = list; + + token = next_token (&val, cfile); + } while (token == COMMA); + + if (token != SEMI) { + parse_warn ("expecting semicolon."); + skip_to_semi (cfile); + } +} diff --git a/contrib/isc-dhcp/client/dhclient-script.8 b/contrib/isc-dhcp/client/dhclient-script.8 new file mode 100644 index 0000000..bae59b4 --- /dev/null +++ b/contrib/isc-dhcp/client/dhclient-script.8 @@ -0,0 +1,191 @@ +.\" dhclient-script.8 +.\" +.\" Copyright (c) 1997 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: +.\" +.\" 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. +.\" +.\" 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 <mellon@fugue.com> in cooperation with Vixie +.\" Enterprises. To learn more about the Internet Software Consortium, +.\" see ``http://www.isc.org/isc''. To learn more about Vixie +.\" Enterprises, see ``http://www.vix.com''. +.TH dhclient 8 +.SH NAME +dhclient-script - DHCP client network configuration script +.SH DESCRIPTION +The DHCP client network configuration script is invoked from time to +time by \fBdhclient(8)\fR. This script is used by the dhcp client to +set each interface's initial configuration prior to requesting an +address, to test the address once it has been offered, and to set the +interface's final configuration once a lease has been acquired. If no +lease is acquired, the script is used to test predefined leases, if +any, and also called once if no valid lease can be identified. +.PP +This script is not meant to be customized by the end user. However, +the script may not work on particular versions of particular operating +systems (indeed, no standard script exists for some operating +systems), so a pioneering user may well need to create a new script or +modify an existing one. In general, customizations specific to a +particular computer should be done in the +.B ETCDIR/dhclient.conf +script. If you find that you can't make such a customization without +customizing dhclient.conf, please submit a bug report. +.SH OPERATION +When dhclient needs to invoke the client configuration script, it +writes a shell script into /tmp which defines a variety of variables. +In all cases, $reason is set to the name of the reason why the script +has been invoked. The following reasons are currently defined: +MEDIUM, PREINIT, ARPCHECK, ARPSEND, BOUND, RENEW, REBIND, REBOOT, +EXPIRE, FAIL and TIMEOUT. +.PP +.SH MEDIUM +The DHCP client is requesting that an interface's media type +be set. The interface name is passed in $interface, and the media +type is passed in $medium. +.SH PREINIT +The DHCP client is requesting that an interface be configured as +required in order to send packets prior to receiving an actual +address. For clients which use the BSD socket library, this means +configuring the interface with an IP address of 0.0.0.0 and a +broadcast address of 255.255.255.255. For other clients, it may be +possible to simply configure the interface up without actually giving +it an IP address at all. The interface name is passed in $interface, +and the media type in $medium. +.PP +If an IP alias has been declared in dhclient.conf, its address will be +passed in $alias_ip_address, and that ip alias should be deleted from +the interface, along with any routes to it. +.SH ARPSEND +The DHCP client is requesting that an address that has been offered to +it be checked to see if somebody else is using it, by sending an ARP +request for that address. It's not clear how to implement this, so +no examples exist yet. The IP address to check is passed in +$new_ip_address, and the interface name is passed in $interface. +.SH ARPCHECK +The DHCP client wants to know if a response to the ARP request send +using ARPCHECK has been received. If one has, the script should exit +with a nonzero status, indicating that the offered address has already +been requested and should be declined. $new_ip_address and +$interface are set as with ARPSEND. +.SH BOUND +The DHCP client has done an initial binding to a new address. The +new ip address is passed in $new_ip_address, and the interface name is +passed in $interface. The media type is passed in $medium. Any +options acquired from the server are passed using the option name +described in \fBdhcp-options\fR, except that dashes ('-') are replaced +by underscores ('_') in order to make valid shell variables, and the +variable names start with new_. So for example, the new subnet mask +would be passed in $new_subnet_mask. +.PP +When a binding has been completed, a lot of network parameters are +likely to need to be set up. A new /etc/resolv.conf needs to be +created, using the values of $new_domain_name and +$new_domain_name_servers (which may list more than one server, +seperated by spaces). A default route should be set using +$new_routers, and static routes may need to be set up using +$new_static_routes. +.PP +If an IP alias has been declared, it must be set up here. The alias +IP address will be written as $alias_ip_address, and other DHCP +options that are set for the alias (e.g., subnet mask) will be passed +in variables named as described previously except starting with +$alias_ instead of $new_. Care should be taken that the alias IP +address not be used if it is identical to the bound IP address +($new_ip_address), since the other alias parameters may be incorrect +in this case. +.SH RENEW +When a binding has been renewed, the script is called as in BOUND, +except that in addition to all the variables starting with $new_, +there is another set of variables starting with $old_. Persistent +settings that may have changed need to be deleted - for example, if a +local route to the bound address is being configured, the old local +route should be deleted. If the default route has changed, the old default +route should be deleted. If the static routes have changed, the old +ones should be deleted. Otherwise, processing can be done as with +BOUND. +.SH REBIND +The DHCP client has rebound to a new DHCP server. This can be handled +as with RENEW, except that if the IP address has changed, the ARP +table should be cleared. +.SH REBOOT +The DHCP client has successfully reacquired its old address after a +reboot. This can be processed as with BOUND. +.SH EXPIRE +The DHCP client has failed to renew its lease or acquire a new one, +and the lease has expired. The IP address must be relinquished, and +all related parameters should be deleted, as in RENEW and REBIND. +.SH FAIL +The DHCP client has been unable to contact any DHCP servers, and any +leases that have been tested have not proved to be valid. The +parameters from the last lease tested should be deconfigured. This +can be handled in the same way as EXPIRE. +.SH TIMEOUT +The DHCP client has been unable to contact any DHCP servers. +However, an old lease has been identified, and its parameters have +been passed in as with BOUND. The client configuration script should +test these parameters and, if it has reason to believe they are valid, +should exit with a value of zero. If not, it should exit with a +nonzero value. +.PP +The usual way to test a lease is to set up the network as with REBIND +(since this may be called to test more than one lease) and then ping +the first router defined in $routers. If a response is received, the +lease must be valid for the network to which the interface is +currently connected. It would be more complete to try to ping all of +the routers listed in $new_routers, as well as those listed in +$new_static_routes, but current scripts do not do this. +.SH FILES +Each operating system should generally have its own script file, +although the script files for similar operating systems may be similar +or even identical. The script files included in the Internet +Software Consortium DHCP distribution appear in the distribution tree +under client/scripts, and bear the names of the operating systems on +which they are intended to work. +.SH BUGS +If more than one interface is being used, there's no obvious way to +avoid clashes between server-supplied configuration parameters - for +example, the stock dhclient-script rewrites /etc/resolv.conf. If +more than one interface is being configured, /etc/resolv.conf will be +repeatedly initialized to the values provided by one server, and then +the other. Assuming the information provided by both servers is +valid, this shouldn't cause any real problems, but it could be +confusing. +.SH SEE ALSO +dhclient(8), dhcpd(8), dhcrelay(8), dhclient.conf(5) and +dhclient.leases(5). +.SH AUTHOR +.B dhclient-script(8) +has been written for the Internet Software Consortium +by Ted Lemon <mellon@fugue.com> in cooperation with Vixie +Enterprises. To learn more about the Internet Software Consortium, +see +.B http://www.vix.com/isc. +To learn more about Vixie +Enterprises, see +.B http://www.vix.com. diff --git a/contrib/isc-dhcp/client/dhclient.8 b/contrib/isc-dhcp/client/dhclient.8 new file mode 100644 index 0000000..0406c49 --- /dev/null +++ b/contrib/isc-dhcp/client/dhclient.8 @@ -0,0 +1,163 @@ +.\" dhclient.8 +.\" +.\" Copyright (c) 1997 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: +.\" +.\" 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. +.\" +.\" 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 <mellon@fugue.com> in cooperation with Vixie +.\" Enterprises. To learn more about the Internet Software Consortium, +.\" see ``http://www.isc.org/isc''. To learn more about Vixie +.\" Enterprises, see ``http://www.vix.com''. +.TH dhclient 8 +.SH NAME +dhclient - Dynamic Host Configuration Protocol Client +.SH SYNOPSIS +.B dhclient +[ +.B -p +.I port +] +[ +.B -d +] +[ +.I if0 +[ +.I ...ifN +] +] +.SH DESCRIPTION +The Internet Software Consortium DHCP Client, dhclient, provides a +means for configuring one or more network interfaces using the Dynamic +Host Configuration Protocol, BOOTP protocol, or if these protocols +fail, by statically assigning an address. +.SH OPERATION +.PP +The DHCP protocol allows a host to contact a central server which +maintains a list of IP addresses which may be assigned on one or more +subnets. A DHCP client may request an address from this pool, and +then use it on a temporary basis for communication on network. The +DHCP protocol also provides a mechanism whereby a client can learn +important details about the network to which it is attached, such as +the location of a default router, the location of a name server, and +so on. +.PP +On startup, dhclient reads the +.IR dhclient.conf +for configuration instructions. It then gets a list of all the +network interfaces that are configured in the current system. For +each interface, it attempts to configure the interface using the DHCP +protocol. +.PP +In order to keep track of leases across system reboots and server +restarts, dhclient keeps a list of leases it has been assigned in the +dhclient.leases(5) file. On startup, after reading the dhclient.conf +file, dhclient reads the dhclient.leases file to refresh its memory +about what leases it has been assigned. +.PP +When a new lease is acquired, it is appended to the end of the +dhclient.leases file. In order to prevent the file from becoming +arbitrarily large, from time to time dhclient creates a new +dhclient.leases file from its in-core lease database. The old version +of the dhclient.leases file is retained under the name +.IR dhcpd.leases~ +until the next time dhclient rewrites the database. +.PP +Old leases are kept around in case the DHCP server is unavailable when +dhclient is first invoked (generally during the initial system boot +process). In that event, old leases from the dhclient.leases file +which have not yet expired are tested, and if they are determined to +be valid, they are used until either they expire or the DHCP server +becomes available. +.PP +A mobile host which may sometimes need to access a network on which no +DHCP server exists may be preloaded with a lease for a fixed +address on that network. When all attempts to contact a DHCP server +have failed, dhclient will try to validate the static lease, and if it +succeeds, will use that lease until it is restarted. +.PP +A mobile host may also travel to some networks on which DHCP is not +available but BOOTP is. In that case, it may be advantageous to +arrange with the network administrator for an entry on the BOOTP +database, so that the host can boot quickly on that network rather +than cycling through the list of old leases. +.SH COMMAND LINE +.PP +The names of the network interfaces that dhclient should attempt to +configure may be specified on the command line. If no interface names +are specified on the command line dhclient will identify all network +interfaces, elimininating non-broadcast interfaces if possible, and +attempt to configure each interface. +.PP +If dhclient should listen and transmit on a port other than the +standard (port 68), the +.B -p +flag may used. It should be followed by the udp port number that +dhclient should use. This is mostly useful for debugging purposes. +.PP +Dhclient will normally run in the foreground until it has configured +an interface, and then will revert to running in the background. +To run force dhclient to always run as a foreground process, the +.B -d +flag should be specified. This is useful when running dhclient under +a debugger, or when running it out of inittab on System V systems. +.PP +.SH CONFIGURATION +The syntax of the dhclient.conf(8) file is discussed seperately. +.SH FILES +.B ETCDIR/dhclient.conf, DBDIR/dhclient.leases, RUNDIR/dhclient.pid, +.B DBDIR/dhclient.leases~. +.SH SEE ALSO +dhcpd(8), dhcrelay(8), dhclient.conf(5), dhclient.leases(5) +.SH AUTHOR +.B dhclient(8) +has been written for the Internet Software Consortium +by Ted Lemon <mellon@fugue.com> in cooperation with Vixie +Enterprises. To learn more about the Internet Software Consortium, +see +.B http://www.vix.com/isc. +To learn more about Vixie +Enterprises, see +.B http://www.vix.com. +.PP +This client was substantially modified and enhanced by Elliot Poger +for use on Linux while he was working on the MosquitoNet project at +Stanford. +.PP +The current version owes much to Elliot's Linux enhancements, but +was substantially reorganized and partially rewritten by Ted Lemon +so as to use the same networking framework that the Internet Software +Consortium DHCP server uses. Much system-specific configuration code +was moved into a shell script so that as support for more operating +systems is added, it will not be necessary to port and maintain +system-specific configuration code to these operating systems - instead, +the shell script can invoke the native tools to accomplish the same +purpose. +.PP diff --git a/contrib/isc-dhcp/client/dhclient.c b/contrib/isc-dhcp/client/dhclient.c new file mode 100644 index 0000000..eb4d268 --- /dev/null +++ b/contrib/isc-dhcp/client/dhclient.c @@ -0,0 +1,2052 @@ +/* dhclient.c + + DHCP Client. */ + +/* + * Copyright (c) 1995, 1996, 1997 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: + * + * 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. + * + * 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 <mellon@fugue.com> 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''. + * + * This client was substantially modified and enhanced by Elliot Poger + * for use on Linux while he was working on the MosquitoNet project at + * Stanford. + * + * The current version owes much to Elliot's Linux enhancements, but + * was substantially reorganized and partially rewritten by Ted Lemon + * so as to use the same networking framework that the Internet Software + * Consortium DHCP server uses. Much system-specific configuration code + * was moved into a shell script so that as support for more operating + * systems is added, it will not be necessary to port and maintain + * system-specific configuration code to these operating systems - instead, + * the shell script can invoke the native tools to accomplish the same + * purpose. + */ + +#ifndef lint +static char copyright[] = +"$Id: dhclient.c,v 1.44.2.1 1997/12/06 11:24:31 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +TIME cur_time; +TIME default_lease_time = 43200; /* 12 hours... */ +TIME max_lease_time = 86400; /* 24 hours... */ +struct tree_cache *global_options [256]; + +char *path_dhclient_conf = _PATH_DHCLIENT_CONF; +char *path_dhclient_db = _PATH_DHCLIENT_DB; +char *path_dhclient_pid = _PATH_DHCLIENT_PID; + +int interfaces_requested = 0; + +int log_perror = 1; + +struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } }; +struct iaddr iaddr_any = { 4, { 0, 0, 0, 0 } }; +struct in_addr inaddr_any; +struct sockaddr_in sockaddr_broadcast; + +/* ASSERT_STATE() does nothing now; it used to be + assert (state_is == state_shouldbe). */ +#define ASSERT_STATE(state_is, state_shouldbe) {} + +#ifdef USE_FALLBACK +struct interface_info fallback_interface; +#endif + +u_int16_t local_port; +u_int16_t remote_port; +int log_priority; +int no_daemon; +int save_scripts; + +static void usage PROTO ((void)); + +int main (argc, argv, envp) + int argc; + char **argv, **envp; +{ + int i; + struct servent *ent; + struct interface_info *ip; + int seed; + +#ifdef SYSLOG_4_2 + openlog ("dhclient", LOG_NDELAY); + log_priority = LOG_DAEMON; +#else + openlog ("dhclient", LOG_NDELAY, LOG_DAEMON); +#endif + +#if !(defined (DEBUG) || defined (SYSLOG_4_2) || defined (__CYGWIN32__)) + setlogmask (LOG_UPTO (LOG_INFO)); +#endif + + for (i = 1; i < argc; i++) { + if (!strcmp (argv [i], "-p")) { + if (++i == argc) + usage (); + local_port = htons (atoi (argv [i])); + debug ("binding to user-specified port %d", + ntohs (local_port)); + } else if (!strcmp (argv [i], "-d")) { + no_daemon = 1; + } else if (!strcmp (argv [i], "-D")) { + save_scripts = 1; + } 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_requested = 1; + interfaces = tmp; + } + } + /* Default to the DHCP/BOOTP port. */ + if (!local_port) { + ent = getservbyname ("dhcpc", "udp"); + if (!ent) + local_port = htons (68); + else + local_port = ent -> s_port; +#ifndef __CYGWIN32__ + endservent (); +#endif + } + remote_port = htons (ntohs (local_port) - 1); /* XXX */ + + /* Get the current time... */ + GET_TIME (&cur_time); + + sockaddr_broadcast.sin_family = AF_INET; + sockaddr_broadcast.sin_port = remote_port; + sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST; +#ifdef HAVE_SA_LEN + sockaddr_broadcast.sin_len = sizeof sockaddr_broadcast; +#endif + inaddr_any.s_addr = INADDR_ANY; + + /* Discover all the network interfaces. */ + discover_interfaces (DISCOVER_UNCONFIGURED); + + /* Parse the dhclient.conf file. */ + read_client_conf (); + + /* Parse the lease database. */ + read_client_leases (); + + /* Rewrite the lease database... */ + rewrite_client_leases (); + + /* If no broadcast interfaces were discovered, call the script + and tell it so. */ + if (!interfaces) { + script_init ((struct interface_info *)0, "NBI", + (struct string_list *)0); + script_go ((struct interface_info *)0); + + /* Nothing more to do. */ + exit (0); + } else { + /* Call the script with the list of interfaces. */ + for (ip = interfaces; ip; ip = ip -> next) { + script_init (ip, "PREINIT", (struct string_list *)0); + if (ip -> client -> alias) + script_write_params (ip, "alias_", + ip -> client -> alias); + script_go (ip); + } + } + + /* At this point, all the interfaces that the script thinks + are relevant should be running, so now we once again call + discover_interfaces(), and this time ask it to actually set + up the interfaces. */ + discover_interfaces (interfaces_requested + ? DISCOVER_REQUESTED + : DISCOVER_RUNNING); + + /* Make up a seed for the random number generator from current + time plus the sum of the last four bytes of each + interface's hardware address interpreted as an integer. + Not much entropy, but we're booting, so we're not likely to + find anything better. */ + seed = 0; /* Unfortunately, what's on the stack isn't random. :') */ + for (ip = interfaces; ip; ip = ip -> next) { + int junk; + memcpy (&junk, + &ip -> hw_address.haddr [ip -> hw_address.hlen - + sizeof seed], sizeof seed); + seed += junk; + } + srandom (seed + cur_time); + + /* Start a configuration state machine for each interface. */ + for (ip = interfaces; ip; ip = ip -> next) { + ip -> client -> state = S_INIT; + state_reboot (ip); + } + + /* Set up the bootp packet handler... */ + bootp_packet_handler = do_packet; + + /* Start dispatching packets and timeouts... */ + dispatch (); + + /*NOTREACHED*/ + return 0; +} + +static void usage () +{ + error ("Usage: dhclient [-c] [-p <port>] [interface]"); +} + +void cleanup () +{ +} + +/* 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 per-interface variables are used to keep track of the process: + * active_lease: the lease that is being used on the interface + * (null pointer if not configured yet). + * offered_leases: leases corresponding to DHCPOFFER messages that have + * been sent to us by DHCP servers. + * acked_leases: leases corresponding to DHCPACK messages that have been + * sent to us by DHCP servers. + * sendpacket: DHCP packet we're trying to send. + * destination: IP address to send sendpacket to + * In addition, there are several relevant per-lease variables. + * T1_expiry, T2_expiry, lease_expiry: lease milestones + * In the active lease, these control the process of renewing the lease; + * In leases on the acked_leases list, this simply determines when we + * can no longer legitimately use the lease. + */ + +void state_reboot (ipp) + void *ipp; +{ + struct interface_info *ip = ipp; + + /* If we don't remember an active lease, go straight to INIT. */ + if (!ip -> client -> active || + ip -> client -> active -> is_bootp) { + state_init (ip); + return; + } + + /* We are in the rebooting state. */ + ip -> client -> state = S_REBOOTING; + + /* Make a DHCPREQUEST packet, and set appropriate per-interface + flags. */ + make_request (ip, ip -> client -> active); + ip -> client -> xid = ip -> client -> packet.xid; + ip -> client -> destination = iaddr_broadcast; + ip -> client -> first_sending = cur_time; + ip -> client -> interval = ip -> client -> config -> initial_interval; + + /* Zap the medium list... */ + ip -> client -> medium = (struct string_list *)0; + + /* Send out the first DHCPREQUEST packet. */ + send_request (ip); +} + +/* Called when a lease has completely expired and we've been unable to + renew it. */ + +void state_init (ipp) + void *ipp; +{ + struct interface_info *ip = ipp; + + ASSERT_STATE(state, S_INIT); + + /* Make a DHCPDISCOVER packet, and set appropriate per-interface + flags. */ + make_discover (ip, ip -> client -> active); + ip -> client -> xid = ip -> client -> packet.xid; + ip -> client -> destination = iaddr_broadcast; + ip -> client -> state = S_SELECTING; + ip -> client -> first_sending = cur_time; + ip -> client -> interval = ip -> client -> config -> initial_interval; + + /* Add an immediate timeout to cause the first DHCPDISCOVER packet + to go out. */ + send_discover (ip); +} + +/* state_selecting is called when one or more DHCPOFFER packets have been + received and a configurable period of time has passed. */ + +void state_selecting (ipp) + void *ipp; +{ + struct interface_info *ip = ipp; + + struct client_lease *lp, *next, *picked; + + ASSERT_STATE(state, S_SELECTING); + + /* Cancel state_selecting and send_discover timeouts, since either + one could have got us here. */ + cancel_timeout (state_selecting, ip); + cancel_timeout (send_discover, ip); + + /* We have received one or more DHCPOFFER packets. Currently, + the only criterion by which we judge leases is whether or + not we get a response when we arp for them. */ + picked = (struct client_lease *)0; + for (lp = ip -> client -> offered_leases; lp; lp = next) { + next = lp -> next; + + /* Check to see if we got an ARPREPLY for the address + in this particular lease. */ + if (!picked) { + script_init (ip, "ARPCHECK", lp -> medium); + script_write_params (ip, "check_", lp); + + /* If the ARPCHECK code detects another + machine using the offered address, it exits + nonzero. We need to send a DHCPDECLINE and + toss the lease. */ + if (script_go (ip)) { + make_decline (ip, lp); + send_decline (ip); + goto freeit; + } + picked = lp; + picked -> next = (struct client_lease *)0; + } else { + freeit: + free_client_lease (lp); + } + } + ip -> client -> offered_leases = (struct client_lease *)0; + + /* If we just tossed all the leases we were offered, go back + to square one. */ + if (!picked) { + ip -> client -> state = S_INIT; + state_init (ip); + return; + } + + /* If it was a BOOTREPLY, we can just take the address right now. */ + if (!picked -> options [DHO_DHCP_MESSAGE_TYPE].len) { + ip -> client -> new = picked; + + /* Make up some lease expiry times + XXX these should be configurable. */ + ip -> client -> new -> expiry = cur_time + 12000; + ip -> client -> new -> renewal += cur_time + 8000; + ip -> client -> new -> rebind += cur_time + 10000; + + ip -> client -> state = S_REQUESTING; + + /* Bind to the address we received. */ + bind_lease (ip); + return; + } + + /* Go to the REQUESTING state. */ + ip -> client -> destination = iaddr_broadcast; + ip -> client -> state = S_REQUESTING; + ip -> client -> first_sending = cur_time; + ip -> client -> interval = ip -> client -> config -> initial_interval; + + /* Make a DHCPREQUEST packet from the lease we picked. */ + make_request (ip, picked); + ip -> client -> xid = ip -> client -> packet.xid; + + /* Toss the lease we picked - we'll get it back in a DHCPACK. */ + free_client_lease (picked); + + /* Add an immediate timeout to send the first DHCPREQUEST packet. */ + send_request (ip); +} + +/* state_requesting is called when we receive a DHCPACK message after + having sent out one or more DHCPREQUEST packets. */ + +void dhcpack (packet) + struct packet *packet; +{ + struct interface_info *ip = packet -> interface; + struct client_lease *lease; + int i; + + /* If we're not receptive to an offer right now, or if the offer + has an unrecognizable transaction id, then just drop it. */ + if (packet -> interface -> client -> xid != packet -> raw -> xid) { + debug ("DHCPACK in wrong transaction."); + return; + } + + if (ip -> client -> state != S_REBOOTING && + ip -> client -> state != S_REQUESTING && + ip -> client -> state != S_RENEWING && + ip -> client -> state != S_REBINDING) { + debug ("DHCPACK in wrong state."); + return; + } + + note ("DHCPACK from %s", piaddr (packet -> client_addr)); + + lease = packet_to_lease (packet); + if (!lease) { + note ("packet_to_lease failed."); + return; + } + + ip -> client -> new = lease; + + /* Stop resending DHCPREQUEST. */ + cancel_timeout (send_request, ip); + + /* Figure out the lease time. */ + ip -> client -> new -> expiry = + getULong (ip -> client -> + new -> options [DHO_DHCP_LEASE_TIME].data); + + /* Take the server-provided renewal time if there is one; + otherwise figure it out according to the spec. */ + if (ip -> client -> new -> options [DHO_DHCP_RENEWAL_TIME].len) + ip -> client -> new -> renewal = + getULong (ip -> client -> + new -> options [DHO_DHCP_RENEWAL_TIME].data); + else + ip -> client -> new -> renewal = + ip -> client -> new -> expiry / 2; + + /* Same deal with the rebind time. */ + if (ip -> client -> new -> options [DHO_DHCP_REBINDING_TIME].len) + ip -> client -> new -> rebind = + getULong (ip -> client -> new -> + options [DHO_DHCP_REBINDING_TIME].data); + else + ip -> client -> new -> rebind = + ip -> client -> new -> renewal + + ip -> client -> new -> renewal / 2 + + ip -> client -> new -> renewal / 4; + + ip -> client -> new -> expiry += cur_time; + ip -> client -> new -> renewal += cur_time; + ip -> client -> new -> rebind += cur_time; + + bind_lease (ip); +} + +void bind_lease (ip) + struct interface_info *ip; +{ + /* Remember the medium. */ + ip -> client -> new -> medium = ip -> client -> medium; + + /* Write out the new lease. */ + write_client_lease (ip, ip -> client -> new); + + /* Run the client script with the new parameters. */ + script_init (ip, (ip -> client -> state == S_REQUESTING + ? "BOUND" + : (ip -> client -> state == S_RENEWING + ? "RENEW" + : (ip -> client -> state == S_REBOOTING + ? "REBOOT" : "REBIND"))), + ip -> client -> new -> medium); + if (ip -> client -> active && ip -> client -> state != S_REBOOTING) + script_write_params (ip, "old_", ip -> client -> active); + script_write_params (ip, "new_", ip -> client -> new); + if (ip -> client -> alias) + script_write_params (ip, "alias_", ip -> client -> alias); + script_go (ip); + + /* Replace the old active lease with the new one. */ + if (ip -> client -> active) + free_client_lease (ip -> client -> active); + ip -> client -> active = ip -> client -> new; + ip -> client -> new = (struct client_lease *)0; + + /* Set up a timeout to start the renewal process. */ + add_timeout (ip -> client -> active -> renewal, + state_bound, ip); + + note ("bound to %s -- renewal in %d seconds.", + piaddr (ip -> client -> active -> address), + ip -> client -> active -> renewal - cur_time); + ip -> client -> state = S_BOUND; + reinitialize_interfaces (); + go_daemon (); +} + +/* state_bound is called when we've successfully bound to a particular + lease, but the renewal time on that lease has expired. We are + expected to unicast a DHCPREQUEST to the server that gave us our + original lease. */ + +void state_bound (ipp) + void *ipp; +{ + struct interface_info *ip = ipp; + + ASSERT_STATE(state, S_BOUND); + + /* T1 has expired. */ + make_request (ip, ip -> client -> active); + ip -> client -> xid = ip -> client -> packet.xid; + + if (ip -> client -> active -> + options [DHO_DHCP_SERVER_IDENTIFIER].len == 4) { + memcpy (ip -> client -> destination.iabuf, + ip -> client -> active -> + options [DHO_DHCP_SERVER_IDENTIFIER].data, 4); + ip -> client -> destination.len = 4; + } else + ip -> client -> destination = iaddr_broadcast; + + ip -> client -> first_sending = cur_time; + ip -> client -> interval = ip -> client -> config -> initial_interval; + ip -> client -> state = S_RENEWING; + + /* Send the first packet immediately. */ + send_request (ip); +} + +int commit_leases () +{ + return 0; +} + +int write_lease (lease) + struct lease *lease; +{ + return 0; +} + +void db_startup () +{ +} + +void bootp (packet) + struct packet *packet; +{ + struct iaddrlist *ap; + + if (packet -> raw -> op != BOOTREPLY) + return; + + /* If there's a reject list, make sure this packet's sender isn't + on it. */ + for (ap = packet -> interface -> client -> config -> reject_list; + ap; ap = ap -> next) { + if (addr_eq (packet -> client_addr, ap -> addr)) { + note ("BOOTREPLY from %s rejected.", + piaddr (ap -> addr)); + return; + } + } + + dhcpoffer (packet); + +} + +void dhcp (packet) + struct packet *packet; +{ + struct iaddrlist *ap; + void (*handler) PROTO ((struct packet *)); + char *type; + + switch (packet -> packet_type) { + case DHCPOFFER: + handler = dhcpoffer; + type = "DHCPOFFER"; + break; + + case DHCPNAK: + handler = dhcpnak; + type = "DHCPNACK"; + break; + + case DHCPACK: + handler = dhcpack; + type = "DHCPACK"; + break; + + default: + return; + } + + /* If there's a reject list, make sure this packet's sender isn't + on it. */ + for (ap = packet -> interface -> client -> config -> reject_list; + ap; ap = ap -> next) { + if (addr_eq (packet -> client_addr, ap -> addr)) { + note ("%s from %s rejected.", + type, piaddr (ap -> addr)); + return; + } + } + (*handler) (packet); +} + +void dhcpoffer (packet) + struct packet *packet; +{ + struct interface_info *ip = packet -> interface; + struct client_lease *lease, *lp; + int i; + int arp_timeout_needed, stop_selecting; + char *name = (packet -> options [DHO_DHCP_MESSAGE_TYPE].len + ? "DHCPOFFER" : "BOOTREPLY"); + struct iaddrlist *ap; + +#ifdef DEBUG_PACKET + dump_packet (packet); +#endif + + /* If we're not receptive to an offer right now, or if the offer + has an unrecognizable transaction id, then just drop it. */ + if (ip -> client -> state != S_SELECTING || + packet -> interface -> client -> xid != packet -> raw -> xid) { + debug ("%s in wrong transaction.", name); + return; + } + + note ("%s from %s", name, piaddr (packet -> client_addr)); + + + /* If this lease doesn't supply the minimum required parameters, + blow it off. */ + for (i = 0; ip -> client -> config -> required_options [i]; i++) { + if (!packet -> options [ip -> client -> config -> + required_options [i]].len) { + note ("%s isn't satisfactory.", name); + return; + } + } + + /* If we've already seen this lease, don't record it again. */ + for (lease = ip -> client -> offered_leases; + lease; lease = lease -> next) { + if (lease -> address.len == sizeof packet -> raw -> yiaddr && + !memcmp (lease -> address.iabuf, + &packet -> raw -> yiaddr, lease -> address.len)) { + debug ("%s already seen.", name); + return; + } + } + + lease = packet_to_lease (packet); + if (!lease) { + note ("packet_to_lease failed."); + return; + } + + /* If this lease was acquired through a BOOTREPLY, record that + fact. */ + if (!packet -> options [DHO_DHCP_MESSAGE_TYPE].len) + lease -> is_bootp = 1; + + /* Record the medium under which this lease was offered. */ + lease -> medium = ip -> client -> medium; + + /* Send out an ARP Request for the offered IP address. */ + script_init (ip, "ARPSEND", lease -> medium); + script_write_params (ip, "check_", lease); + /* If the script can't send an ARP request without waiting, + we'll be waiting when we do the ARPCHECK, so don't wait now. */ + if (script_go (ip)) + arp_timeout_needed = 0; + else + arp_timeout_needed = 2; + + /* Figure out when we're supposed to stop selecting. */ + stop_selecting = (ip -> client -> first_sending + + ip -> client -> config -> select_interval); + + /* If this is the lease we asked for, put it at the head of the + list, and don't mess with the arp request timeout. */ + if (lease -> address.len == ip -> client -> requested_address.len && + !memcmp (lease -> address.iabuf, + ip -> client -> requested_address.iabuf, + ip -> client -> requested_address.len)) { + lease -> next = ip -> client -> offered_leases; + ip -> client -> offered_leases = lease; + } else { + /* If we already have an offer, and arping for this + offer would take us past the selection timeout, + then don't extend the timeout - just hope for the + best. */ + if (ip -> client -> offered_leases && + (cur_time + arp_timeout_needed) > stop_selecting) + arp_timeout_needed = 0; + + /* Put the lease at the end of the list. */ + lease -> next = (struct client_lease *)0; + if (!ip -> client -> offered_leases) + ip -> client -> offered_leases = lease; + else { + for (lp = ip -> client -> offered_leases; lp -> next; + lp = lp -> next) + ; + lp -> next = lease; + } + } + + /* If we're supposed to stop selecting before we've had time + to wait for the ARPREPLY, add some delay to wait for + the ARPREPLY. */ + if (stop_selecting - cur_time < arp_timeout_needed) + stop_selecting = cur_time + arp_timeout_needed; + + /* If the selecting interval has expired, go immediately to + state_selecting(). Otherwise, time out into + state_selecting at the select interval. */ + if (stop_selecting <= 0) + state_selecting (ip); + else { + add_timeout (stop_selecting, state_selecting, ip); + cancel_timeout (send_discover, ip); + } +} + +/* Allocate a client_lease structure and initialize it from the parameters + in the specified packet. */ + +struct client_lease *packet_to_lease (packet) + struct packet *packet; +{ + struct client_lease *lease; + int i; + + lease = (struct client_lease *)malloc (sizeof (struct client_lease)); + + if (!lease) { + warn ("dhcpoffer: no memory to record lease.\n"); + return (struct client_lease *)0; + } + + memset (lease, 0, sizeof *lease); + + /* Copy the lease options. */ + for (i = 0; i < 256; i++) { + if (packet -> options [i].len) { + lease -> options [i].data = + (unsigned char *) + malloc (packet -> options [i].len + 1); + if (!lease -> options [i].data) { + warn ("dhcpoffer: no memory for option %d\n", + i); + free_client_lease (lease); + return (struct client_lease *)0; + } else { + memcpy (lease -> options [i].data, + packet -> options [i].data, + packet -> options [i].len); + lease -> options [i].len = + packet -> options [i].len; + lease -> options [i].data + [lease -> options [i].len] = 0; + } + } + } + + lease -> address.len = sizeof (packet -> raw -> yiaddr); + memcpy (lease -> address.iabuf, &packet -> raw -> yiaddr, + lease -> address.len); + + /* If the server name was filled out, copy it. */ + if ((!packet -> options [DHO_DHCP_OPTION_OVERLOAD].len || + !(packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 2)) && + packet -> raw -> sname [0]) { + int len; + /* Don't count on the NUL terminator. */ + for (len = 0; len < 64; len++) + if (!packet -> raw -> sname [len]) + break; + lease -> server_name = malloc (len + 1); + if (!lease -> server_name) { + warn ("dhcpoffer: no memory for filename.\n"); + free_client_lease (lease); + return (struct client_lease *)0; + } else { + memcpy (lease -> server_name, + packet -> raw -> sname, len); + lease -> server_name [len] = 0; + } + } + + /* Ditto for the filename. */ + if ((!packet -> options [DHO_DHCP_OPTION_OVERLOAD].len || + !(packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 1)) && + packet -> raw -> file [0]) { + int len; + /* Don't count on the NUL terminator. */ + for (len = 0; len < 64; len++) + if (!packet -> raw -> file [len]) + break; + lease -> filename = malloc (len + 1); + if (!lease -> filename) { + warn ("dhcpoffer: no memory for filename.\n"); + free_client_lease (lease); + return (struct client_lease *)0; + } else { + memcpy (lease -> filename, + packet -> raw -> file, len); + lease -> filename [len] = 0; + } + } + return lease; +} + +void dhcpnak (packet) + struct packet *packet; +{ + struct interface_info *ip = packet -> interface; + + /* If we're not receptive to an offer right now, or if the offer + has an unrecognizable transaction id, then just drop it. */ + if (packet -> interface -> client -> xid != packet -> raw -> xid) { + debug ("DHCPNAK in wrong transaction."); + return; + } + + if (ip -> client -> state != S_REBOOTING && + ip -> client -> state != S_REQUESTING && + ip -> client -> state != S_RENEWING && + ip -> client -> state != S_REBINDING) { + debug ("DHCPNAK in wrong state."); + return; + } + + note ("DHCPNAK from %s", piaddr (packet -> client_addr)); + + if (!ip -> client -> active) { + note ("DHCPNAK with no active lease.\n"); + return; + } + + free_client_lease (ip -> client -> active); + ip -> client -> active = (struct client_lease *)0; + + /* Stop sending DHCPREQUEST packets... */ + cancel_timeout (send_request, ip); + + ip -> client -> state = S_INIT; + state_init (ip); +} + +/* Send out a DHCPDISCOVER packet, and set a timeout to send out another + one after the right interval has expired. If we don't get an offer by + the time we reach the panic interval, call the panic function. */ + +void send_discover (ipp) + void *ipp; +{ + struct interface_info *ip = ipp; + + int result; + int interval; + int increase = 1; + + /* Figure out how long it's been since we started transmitting. */ + interval = cur_time - ip -> client -> first_sending; + + /* If we're past the panic timeout, call the script and tell it + we haven't found anything for this interface yet. */ + if (interval > ip -> client -> config -> timeout) { + state_panic (ip); + return; + } + + /* If we're selecting media, try the whole list before doing + the exponential backoff, but if we've already received an + offer, stop looping, because we obviously have it right. */ + if (!ip -> client -> offered_leases && + ip -> client -> config -> media) { + int fail = 0; + again: + if (ip -> client -> medium) { + ip -> client -> medium = + ip -> client -> medium -> next; + increase = 0; + } + if (!ip -> client -> medium) { + if (fail) + error ("No valid media types for %s!", + ip -> name); + ip -> client -> medium = + ip -> client -> config -> media; + increase = 1; + } + + note ("Trying medium \"%s\" %d", + ip -> client -> medium -> string, increase); + script_init (ip, "MEDIUM", ip -> client -> medium); + if (script_go (ip)) { + goto again; + } + } + + /* If we're supposed to increase the interval, do so. If it's + currently zero (i.e., we haven't sent any packets yet), set + it to one; otherwise, add to it a random number between + zero and two times itself. On average, this means that it + will double with every transmission. */ + if (increase) { + if (!ip -> client -> interval) + ip -> client -> interval = + ip -> client -> config -> initial_interval; + else { + ip -> client -> interval += + ((random () >> 2) % + (2 * ip -> client -> interval)); + } + + /* Don't backoff past cutoff. */ + if (ip -> client -> interval > + ip -> client -> config -> backoff_cutoff) + ip -> client -> interval = + ((ip -> client -> config -> backoff_cutoff / 2) + + ((random () >> 2) + % ip -> client -> interval)); + } else if (!ip -> client -> interval) + ip -> client -> interval = + ip -> client -> config -> initial_interval; + + /* If the backoff would take us to the panic timeout, just use that + as the interval. */ + if (cur_time + ip -> client -> interval > + ip -> client -> first_sending + ip -> client -> config -> timeout) + ip -> client -> interval = + (ip -> client -> first_sending + + ip -> client -> config -> timeout) - cur_time + 1; + + /* Record the number of seconds since we started sending. */ + if (interval < 255) + ip -> client -> packet.secs = interval; + else + ip -> client -> packet.secs = 255; + + note ("DHCPDISCOVER on %s to %s port %d interval %ld", + ip -> name, + inet_ntoa (sockaddr_broadcast.sin_addr), + ntohs (sockaddr_broadcast.sin_port), ip -> client -> interval); + + /* Send out a packet. */ + result = send_packet (ip, (struct packet *)0, + &ip -> client -> packet, + ip -> client -> packet_length, + inaddr_any, &sockaddr_broadcast, + (struct hardware *)0); + if (result < 0) + warn ("send_packet: %m"); + + add_timeout (cur_time + ip -> client -> interval, send_discover, ip); +} + +/* state_panic gets called if we haven't received any offers in a preset + amount of time. When this happens, we try to use existing leases that + haven't yet expired, and failing that, we call the client script and + hope it can do something. */ + +void state_panic (ipp) + void *ipp; +{ + struct interface_info *ip = ipp; + + struct client_lease *loop = ip -> client -> active; + struct client_lease *lp; + + note ("No DHCPOFFERS received."); + + /* We may not have an active lease, but we may have some + predefined leases that we can try. */ + if (!ip -> client -> active && ip -> client -> leases) + goto activate_next; + + /* Run through the list of leases and see if one can be used. */ + while (ip -> client -> active) { + if (ip -> client -> active -> expiry > cur_time) { + note ("Trying recorded lease %s", + piaddr (ip -> client -> active -> address)); + /* Run the client script with the existing + parameters. */ + script_init (ip, "TIMEOUT", + ip -> client -> active -> medium); + script_write_params (ip, "new_", + ip -> client -> active); + if (ip -> client -> alias) + script_write_params (ip, "alias_", + ip -> client -> alias); + + /* If the old lease is still good and doesn't + yet need renewal, go into BOUND state and + timeout at the renewal time. */ + if (!script_go (ip)) { + if (cur_time < + ip -> client -> active -> renewal) { + ip -> client -> state = S_BOUND; + note ("bound: renewal in %d seconds.", + ip -> client -> active -> renewal + - cur_time); + add_timeout ((ip -> client -> + active -> renewal), + state_bound, ip); + } else { + ip -> client -> state = S_BOUND; + note ("bound: immediate renewal."); + state_bound (ip); + } + reinitialize_interfaces (); + go_daemon (); + return; + } + } + + /* If there are no other leases, give up. */ + if (!ip -> client -> leases) { + ip -> client -> leases = ip -> client -> active; + ip -> client -> active = (struct client_lease *)0; + break; + } + + activate_next: + /* Otherwise, put the active lease at the end of the + lease list, and try another lease.. */ + for (lp = ip -> client -> leases; lp -> next; lp = lp -> next) + ; + lp -> next = ip -> client -> active; + if (lp -> next) { + lp -> next -> next = (struct client_lease *)0; + } + ip -> client -> active = ip -> client -> leases; + ip -> client -> leases = ip -> client -> leases -> next; + + /* If we already tried this lease, we've exhausted the + set of leases, so we might as well give up for + now. */ + if (ip -> client -> active == loop) + break; + else if (!loop) + loop = ip -> client -> active; + } + + /* No leases were available, or what was available didn't work, so + tell the shell script that we failed to allocate an address, + and try again later. */ + note ("No working leases in persistent database - sleeping.\n"); + script_init (ip, "FAIL", (struct string_list *)0); + if (ip -> client -> alias) + script_write_params (ip, "alias_", ip -> client -> alias); + script_go (ip); + ip -> client -> state = S_INIT; + add_timeout (cur_time + ip -> client -> config -> retry_interval, + state_init, ip); + go_daemon (); +} + +void send_request (ipp) + void *ipp; +{ + struct interface_info *ip = ipp; + + int result; + int interval; + struct sockaddr_in destination; + struct in_addr from; + + /* Figure out how long it's been since we started transmitting. */ + interval = cur_time - ip -> client -> first_sending; + + /* If we're in the INIT-REBOOT or REQUESTING state and we're + past the reboot timeout, go to INIT and see if we can + DISCOVER an address... */ + /* XXX In the INIT-REBOOT state, if we don't get an ACK, it + means either that we're on a network with no DHCP server, + or that our server is down. In the latter case, assuming + that there is a backup DHCP server, DHCPDISCOVER will get + us a new address, but we could also have successfully + reused our old address. In the former case, we're hosed + anyway. This is not a win-prone situation. */ + if ((ip -> client -> state == S_REBOOTING || + ip -> client -> state == S_REQUESTING) && + interval > ip -> client -> config -> reboot_timeout) { + cancel: + ip -> client -> state = S_INIT; + cancel_timeout (send_request, ip); + state_init (ip); + return; + } + + /* If we're in the reboot state, make sure the media is set up + correctly. */ + if (ip -> client -> state == S_REBOOTING && + !ip -> client -> medium && + ip -> client -> active -> medium ) { + script_init (ip, "MEDIUM", ip -> client -> active -> medium); + + /* If the medium we chose won't fly, go to INIT state. */ + if (script_go (ip)) + goto cancel; + + /* Record the medium. */ + ip -> client -> medium = ip -> client -> active -> medium; + } + + /* If the lease has expired, relinquish the address and go back + to the INIT state. */ + if (ip -> client -> state != S_REQUESTING && + cur_time > ip -> client -> active -> expiry) { + /* Run the client script with the new parameters. */ + script_init (ip, "EXPIRE", (struct string_list *)0); + script_write_params (ip, "old_", ip -> client -> active); + if (ip -> client -> alias) + script_write_params (ip, "alias_", + ip -> client -> alias); + script_go (ip); + + ip -> client -> state = S_INIT; + state_init (ip); + return; + } + + /* Do the exponential backoff... */ + if (!ip -> client -> interval) + ip -> client -> interval = + ip -> client -> config -> initial_interval; + else { + ip -> client -> interval += + ((random () >> 2) % + (2 * ip -> client -> interval)); + } + + /* Don't backoff past cutoff. */ + if (ip -> client -> interval > + ip -> client -> config -> backoff_cutoff) + ip -> client -> interval = + ((ip -> client -> config -> backoff_cutoff / 2) + + ((random () >> 2) + % ip -> client -> interval)); + + /* If the backoff would take us to the expiry time, just set the + timeout to the expiry time. */ + if (ip -> client -> state != S_REQUESTING && + cur_time + ip -> client -> interval > + ip -> client -> active -> expiry) + ip -> client -> interval = + ip -> client -> active -> expiry - cur_time + 1; + + /* If the lease T2 time has elapsed, or if we're not yet bound, + broadcast the DHCPREQUEST rather than unicasting. */ + if (ip -> client -> state == S_REQUESTING || + cur_time > ip -> client -> active -> rebind) + destination.sin_addr.s_addr = INADDR_BROADCAST; + else + memcpy (&destination.sin_addr.s_addr, + ip -> client -> destination.iabuf, + sizeof destination.sin_addr.s_addr); + destination.sin_port = remote_port; + destination.sin_family = AF_INET; +#ifdef HAVE_SA_LEN + destination.sin_len = sizeof destination; +#endif + + if (ip -> client -> state != S_REQUESTING) + memcpy (&from, ip -> client -> active -> address.iabuf, + sizeof from); + else + from.s_addr = INADDR_ANY; + + /* Record the number of seconds since we started sending. */ + if (interval < 255) + ip -> client -> packet.secs = interval; + else + ip -> client -> packet.secs = 255; + + note ("DHCPREQUEST on %s to %s port %d", ip -> name, + inet_ntoa (destination.sin_addr), + ntohs (destination.sin_port)); + +#ifdef USE_FALLBACK + if (destination.sin_addr.s_addr != INADDR_BROADCAST) + result = send_fallback (&fallback_interface, + (struct packet *)0, + &ip -> client -> packet, + ip -> client -> packet_length, + from, &destination, + (struct hardware *)0); + else +#endif /* USE_FALLBACK */ + /* Send out a packet. */ + result = send_packet (ip, (struct packet *)0, + &ip -> client -> packet, + ip -> client -> packet_length, + from, &destination, + (struct hardware *)0); + + if (result < 0) + warn ("send_packet: %m"); + + add_timeout (cur_time + ip -> client -> interval, + send_request, ip); +} + +void send_decline (ipp) + void *ipp; +{ + struct interface_info *ip = ipp; + + int result; + + note ("DHCPDECLINE on %s to %s port %d", ip -> name, + inet_ntoa (sockaddr_broadcast.sin_addr), + ntohs (sockaddr_broadcast.sin_port)); + + /* Send out a packet. */ + result = send_packet (ip, (struct packet *)0, + &ip -> client -> packet, + ip -> client -> packet_length, + inaddr_any, &sockaddr_broadcast, + (struct hardware *)0); + if (result < 0) + warn ("send_packet: %m"); +} + +void send_release (ipp) + void *ipp; +{ + struct interface_info *ip = ipp; + + int result; + + note ("DHCPRELEASE on %s to %s port %d", ip -> name, + inet_ntoa (sockaddr_broadcast.sin_addr), + ntohs (sockaddr_broadcast.sin_port)); + + /* Send out a packet. */ + result = send_packet (ip, (struct packet *)0, + &ip -> client -> packet, + ip -> client -> packet_length, + inaddr_any, &sockaddr_broadcast, + (struct hardware *)0); + if (result < 0) + warn ("send_packet: %m"); +} + +void make_discover (ip, lease) + struct interface_info *ip; + struct client_lease *lease; +{ + struct dhcp_packet *raw; + unsigned char discover = DHCPDISCOVER; + int i; + + struct tree_cache *options [256]; + struct tree_cache option_elements [256]; + + memset (option_elements, 0, sizeof option_elements); + memset (options, 0, sizeof options); + memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet)); + + /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */ + i = DHO_DHCP_MESSAGE_TYPE; + options [i] = &option_elements [i]; + options [i] -> value = &discover; + options [i] -> len = sizeof discover; + options [i] -> buf_size = sizeof discover; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + + /* Request the options we want */ + i = DHO_DHCP_PARAMETER_REQUEST_LIST; + options [i] = &option_elements [i]; + options [i] -> value = ip -> client -> config -> requested_options; + options [i] -> len = ip -> client -> config -> requested_option_count; + options [i] -> buf_size = + ip -> client -> config -> requested_option_count; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + + /* If we had an address, try to get it again. */ + if (lease) { + ip -> client -> requested_address = lease -> address; + i = DHO_DHCP_REQUESTED_ADDRESS; + options [i] = &option_elements [i]; + options [i] -> value = lease -> address.iabuf; + options [i] -> len = lease -> address.len; + options [i] -> buf_size = lease -> address.len; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + } else { + ip -> client -> requested_address.len = 0; + } + + /* Send any options requested in the config file. */ + for (i = 0; i < 256; i++) { + if (!options [i] && + ip -> client -> config -> send_options [i].data) { + options [i] = &option_elements [i]; + options [i] -> value = ip -> client -> config -> + send_options [i].data; + options [i] -> len = ip -> client -> config -> + send_options [i].len; + options [i] -> buf_size = ip -> client -> config -> + send_options [i].len; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + } + } + + /* Set up the option buffer... */ + ip -> client -> packet_length = + cons_options ((struct packet *)0, &ip -> client -> packet, + options, 0, 0, 0); + if (ip -> client -> packet_length < BOOTP_MIN_LEN) + ip -> client -> packet_length = BOOTP_MIN_LEN; + + ip -> client -> packet.op = BOOTREQUEST; + ip -> client -> packet.htype = ip -> hw_address.htype; + ip -> client -> packet.hlen = ip -> hw_address.hlen; + ip -> client -> packet.hops = 0; + ip -> client -> packet.xid = random (); + ip -> client -> packet.secs = 0; /* filled in by send_discover. */ + ip -> client -> packet.flags = htons (BOOTP_BROADCAST); /* XXX */ + memset (&(ip -> client -> packet.ciaddr), + 0, sizeof ip -> client -> packet.ciaddr); + memset (&(ip -> client -> packet.yiaddr), + 0, sizeof ip -> client -> packet.yiaddr); + memset (&(ip -> client -> packet.siaddr), + 0, sizeof ip -> client -> packet.siaddr); + memset (&(ip -> client -> packet.giaddr), + 0, sizeof ip -> client -> packet.giaddr); + memcpy (ip -> client -> packet.chaddr, + ip -> hw_address.haddr, ip -> hw_address.hlen); + +#ifdef DEBUG_PACKET + dump_packet (sendpkt); + dump_raw ((unsigned char *)ip -> client -> packet, + sendpkt->packet_length); +#endif +} + + +void make_request (ip, lease) + struct interface_info *ip; + struct client_lease *lease; +{ + unsigned char request = DHCPREQUEST; + int i; + + struct tree_cache *options [256]; + struct tree_cache option_elements [256]; + + memset (options, 0, sizeof options); + memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet)); + + /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */ + i = DHO_DHCP_MESSAGE_TYPE; + options [i] = &option_elements [i]; + options [i] -> value = &request; + options [i] -> len = sizeof request; + options [i] -> buf_size = sizeof request; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + + /* Request the options we want */ + i = DHO_DHCP_PARAMETER_REQUEST_LIST; + options [i] = &option_elements [i]; + options [i] -> value = ip -> client -> config -> requested_options; + options [i] -> len = ip -> client -> config -> requested_option_count; + options [i] -> buf_size = + ip -> client -> config -> requested_option_count; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + + /* If we are requesting an address that hasn't yet been assigned + to us, use the DHCP Requested Address option. */ + if (ip -> client -> state == S_REQUESTING) { + /* Send back the server identifier... */ + i = DHO_DHCP_SERVER_IDENTIFIER; + options [i] = &option_elements [i]; + options [i] -> value = lease -> options [i].data; + options [i] -> len = lease -> options [i].len; + options [i] -> buf_size = lease -> options [i].len; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + } + if (ip -> client -> state == S_REQUESTING || + ip -> client -> state == S_REBOOTING) { + ip -> client -> requested_address = lease -> address; + i = DHO_DHCP_REQUESTED_ADDRESS; + options [i] = &option_elements [i]; + options [i] -> value = lease -> address.iabuf; + options [i] -> len = lease -> address.len; + options [i] -> buf_size = lease -> address.len; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + } else { + ip -> client -> requested_address.len = 0; + } + + /* Send any options requested in the config file. */ + for (i = 0; i < 256; i++) { + if (!options [i] && + ip -> client -> config -> send_options [i].data) { + options [i] = &option_elements [i]; + options [i] -> value = ip -> client -> config -> + send_options [i].data; + options [i] -> len = ip -> client -> config -> + send_options [i].len; + options [i] -> buf_size = ip -> client -> config -> + send_options [i].len; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + } + } + + /* Set up the option buffer... */ + ip -> client -> packet_length = + cons_options ((struct packet *)0, &ip -> client -> packet, + options, 0, 0, 0); + if (ip -> client -> packet_length < BOOTP_MIN_LEN) + ip -> client -> packet_length = BOOTP_MIN_LEN; + + ip -> client -> packet.op = BOOTREQUEST; + ip -> client -> packet.htype = ip -> hw_address.htype; + ip -> client -> packet.hlen = ip -> hw_address.hlen; + ip -> client -> packet.hops = 0; + ip -> client -> packet.xid = ip -> client -> xid; + ip -> client -> packet.secs = 0; /* Filled in by send_request. */ + ip -> client -> packet.flags = htons (BOOTP_BROADCAST); + + /* If we own the address we're requesting, put it in ciaddr; + otherwise set ciaddr to zero. */ + if (ip -> client -> state == S_BOUND || + ip -> client -> state == S_RENEWING || + ip -> client -> state == S_REBINDING) + memcpy (&ip -> client -> packet.ciaddr, + lease -> address.iabuf, lease -> address.len); + else + memset (&ip -> client -> packet.ciaddr, 0, + sizeof ip -> client -> packet.ciaddr); + + memset (&ip -> client -> packet.yiaddr, 0, + sizeof ip -> client -> packet.yiaddr); + memset (&ip -> client -> packet.siaddr, 0, + sizeof ip -> client -> packet.siaddr); + memset (&ip -> client -> packet.giaddr, 0, + sizeof ip -> client -> packet.giaddr); + memcpy (ip -> client -> packet.chaddr, + ip -> hw_address.haddr, ip -> hw_address.hlen); + +#ifdef DEBUG_PACKET + dump_packet (sendpkt); + dump_raw ((unsigned char *)ip -> client -> packet, sendpkt->packet_length); +#endif +} + +void make_decline (ip, lease) + struct interface_info *ip; + struct client_lease *lease; +{ + unsigned char decline = DHCPDECLINE; + int i; + + struct tree_cache *options [256]; + struct tree_cache message_type_tree; + struct tree_cache requested_address_tree; + struct tree_cache server_id_tree; + struct tree_cache client_id_tree; + + memset (options, 0, sizeof options); + memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet)); + + /* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */ + i = DHO_DHCP_MESSAGE_TYPE; + options [i] = &message_type_tree; + options [i] -> value = &decline; + options [i] -> len = sizeof decline; + options [i] -> buf_size = sizeof decline; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + + /* Send back the server identifier... */ + i = DHO_DHCP_SERVER_IDENTIFIER; + options [i] = &server_id_tree; + options [i] -> value = lease -> options [i].data; + options [i] -> len = lease -> options [i].len; + options [i] -> buf_size = lease -> options [i].len; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + + /* Send back the address we're declining. */ + i = DHO_DHCP_REQUESTED_ADDRESS; + options [i] = &requested_address_tree; + options [i] -> value = lease -> address.iabuf; + options [i] -> len = lease -> address.len; + options [i] -> buf_size = lease -> address.len; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + + /* Send the uid if the user supplied one. */ + i = DHO_DHCP_CLIENT_IDENTIFIER; + if (ip -> client -> config -> send_options [i].len) { + options [i] = &client_id_tree; + options [i] -> value = ip -> client -> config -> + send_options [i].data; + options [i] -> len = ip -> client -> config -> + send_options [i].len; + options [i] -> buf_size = ip -> client -> config -> + send_options [i].len; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + } + + + /* Set up the option buffer... */ + ip -> client -> packet_length = + cons_options ((struct packet *)0, &ip -> client -> packet, + options, 0, 0, 0); + if (ip -> client -> packet_length < BOOTP_MIN_LEN) + ip -> client -> packet_length = BOOTP_MIN_LEN; + + ip -> client -> packet.op = BOOTREQUEST; + ip -> client -> packet.htype = ip -> hw_address.htype; + ip -> client -> packet.hlen = ip -> hw_address.hlen; + ip -> client -> packet.hops = 0; + ip -> client -> packet.xid = ip -> client -> xid; + ip -> client -> packet.secs = 0; /* Filled in by send_request. */ + ip -> client -> packet.flags = htons (BOOTP_BROADCAST); + + /* ciaddr must always be zero. */ + memset (&ip -> client -> packet.ciaddr, 0, + sizeof ip -> client -> packet.ciaddr); + memset (&ip -> client -> packet.yiaddr, 0, + sizeof ip -> client -> packet.yiaddr); + memset (&ip -> client -> packet.siaddr, 0, + sizeof ip -> client -> packet.siaddr); + memset (&ip -> client -> packet.giaddr, 0, + sizeof ip -> client -> packet.giaddr); + memcpy (ip -> client -> packet.chaddr, + ip -> hw_address.haddr, ip -> hw_address.hlen); + +#ifdef DEBUG_PACKET + dump_packet (sendpkt); + dump_raw ((unsigned char *)ip -> client -> packet, sendpkt->packet_length); +#endif +} + +void make_release (ip, lease) + struct interface_info *ip; + struct client_lease *lease; +{ + unsigned char request = DHCPRELEASE; + int i; + + struct tree_cache *options [256]; + struct tree_cache message_type_tree; + struct tree_cache requested_address_tree; + struct tree_cache server_id_tree; + + memset (options, 0, sizeof options); + memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet)); + + /* Set DHCP_MESSAGE_TYPE to DHCPRELEASE */ + i = DHO_DHCP_MESSAGE_TYPE; + options [i] = &message_type_tree; + options [i] -> value = &request; + options [i] -> len = sizeof request; + options [i] -> buf_size = sizeof request; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + + /* Send back the server identifier... */ + i = DHO_DHCP_SERVER_IDENTIFIER; + options [i] = &server_id_tree; + options [i] -> value = lease -> options [i].data; + options [i] -> len = lease -> options [i].len; + options [i] -> buf_size = lease -> options [i].len; + options [i] -> timeout = 0xFFFFFFFF; + options [i] -> tree = (struct tree *)0; + + /* Set up the option buffer... */ + ip -> client -> packet_length = + cons_options ((struct packet *)0, &ip -> client -> packet, + options, 0, 0, 0); + if (ip -> client -> packet_length < BOOTP_MIN_LEN) + ip -> client -> packet_length = BOOTP_MIN_LEN; + + ip -> client -> packet.op = BOOTREQUEST; + ip -> client -> packet.htype = ip -> hw_address.htype; + ip -> client -> packet.hlen = ip -> hw_address.hlen; + ip -> client -> packet.hops = 0; + ip -> client -> packet.xid = ip -> client -> packet.xid; + ip -> client -> packet.secs = 0; + ip -> client -> packet.flags = 0; + memcpy (&ip -> client -> packet.ciaddr, + lease -> address.iabuf, lease -> address.len); + memset (&ip -> client -> packet.yiaddr, 0, + sizeof ip -> client -> packet.yiaddr); + memset (&ip -> client -> packet.siaddr, 0, + sizeof ip -> client -> packet.siaddr); + memset (&ip -> client -> packet.giaddr, 0, + sizeof ip -> client -> packet.giaddr); + memcpy (ip -> client -> packet.chaddr, + ip -> hw_address.haddr, ip -> hw_address.hlen); + +#ifdef DEBUG_PACKET + dump_packet (sendpkt); + dump_raw ((unsigned char *)ip -> client -> packet, + ip -> client -> packet_length); +#endif +} + +void free_client_lease (lease) + struct client_lease *lease; +{ + int i; + + if (lease -> server_name) + free (lease -> server_name); + if (lease -> filename) + free (lease -> filename); + for (i = 0; i < 256; i++) { + if (lease -> options [i].len) + free (lease -> options [i].data); + } + free (lease); +} + +FILE *leaseFile; + +void rewrite_client_leases () +{ + struct interface_info *ip; + struct client_lease *lp; + + if (leaseFile) + fclose (leaseFile); + leaseFile = fopen (path_dhclient_db, "w"); + if (!leaseFile) + error ("can't create /var/db/dhclient.leases: %m"); + + /* Write out all the leases attached to configured interfaces that + we know about. */ + for (ip = interfaces; ip; ip = ip -> next) { + for (lp = ip -> client -> leases; lp; lp = lp -> next) { + write_client_lease (ip, lp); + } + if (ip -> client -> active) + write_client_lease (ip, ip -> client -> active); + } + + /* Write out any leases that are attached to interfaces that aren't + currently configured. */ + for (ip = dummy_interfaces; ip; ip = ip -> next) { + for (lp = ip -> client -> leases; lp; lp = lp -> next) { + write_client_lease (ip, lp); + } + if (ip -> client -> active) + write_client_lease (ip, ip -> client -> active); + } + fflush (leaseFile); +} + +void write_client_lease (ip, lease) + struct interface_info *ip; + struct client_lease *lease; +{ + int i; + struct tm *t; + + /* If the lease came from the config file, we don't need to stash + a copy in the lease database. */ + if (lease -> is_static) + return; + + if (!leaseFile) { /* XXX */ + leaseFile = fopen (path_dhclient_db, "w"); + if (!leaseFile) + error ("can't create /var/db/dhclient.leases: %m"); + } + + fprintf (leaseFile, "lease {\n"); + if (lease -> is_bootp) + fprintf (leaseFile, " bootp;\n"); + fprintf (leaseFile, " interface \"%s\";\n", ip -> name); + fprintf (leaseFile, " fixed-address %s;\n", + piaddr (lease -> address)); + if (lease -> filename) + fprintf (leaseFile, " filename \"%s\";\n", + lease -> filename); + if (lease -> server_name) + fprintf (leaseFile, " server-name \"%s\";\n", + lease -> filename); + if (lease -> medium) + fprintf (leaseFile, " medium \"%s\";\n", + lease -> medium -> string); + for (i = 0; i < 256; i++) { + if (lease -> options [i].len) { + fprintf (leaseFile, + " option %s %s;\n", + dhcp_options [i].name, + pretty_print_option + (i, lease -> options [i].data, + lease -> options [i].len, 1, 1)); + } + } + t = gmtime (&lease -> renewal); + fprintf (leaseFile, + " renew %d %d/%d/%d %02d:%02d:%02d;\n", + t -> tm_wday, t -> tm_year + 1900, + t -> tm_mon + 1, t -> tm_mday, + t -> tm_hour, t -> tm_min, t -> tm_sec); + t = gmtime (&lease -> rebind); + fprintf (leaseFile, + " rebind %d %d/%d/%d %02d:%02d:%02d;\n", + t -> tm_wday, t -> tm_year + 1900, + t -> tm_mon + 1, t -> tm_mday, + t -> tm_hour, t -> tm_min, t -> tm_sec); + t = gmtime (&lease -> expiry); + fprintf (leaseFile, + " expire %d %d/%d/%d %02d:%02d:%02d;\n", + t -> tm_wday, t -> tm_year + 1900, + t -> tm_mon + 1, t -> tm_mday, + t -> tm_hour, t -> tm_min, t -> tm_sec); + fprintf (leaseFile, "}\n"); + fflush (leaseFile); +} + +/* Variables holding name of script and file pointer for writing to + script. Needless to say, this is not reentrant - only one script + can be invoked at a time. */ +char scriptName [256]; +FILE *scriptFile; + +void script_init (ip, reason, medium) + struct interface_info *ip; + char *reason; + struct string_list *medium; +{ + int fd; +#ifndef HAVE_MKSTEMP + + do { +#endif + strcpy (scriptName, "/tmp/dcsXXXXXX"); +#ifdef HAVE_MKSTEMP + fd = mkstemp (scriptName); +#else + mktemp (scriptName); + fd = creat (scriptName, 0600); + } while (fd < 0); +#endif + + scriptFile = fdopen (fd, "w"); + if (!scriptFile) + error ("can't write script file: %m"); + fprintf (scriptFile, "#!/bin/sh\n\n"); + if (ip) { + fprintf (scriptFile, "interface=\"%s\"\n", ip -> name); + fprintf (scriptFile, "export interface\n"); + } + if (medium) { + fprintf (scriptFile, "medium=\"%s\"\n", medium -> string); + fprintf (scriptFile, "export medium\n"); + } + fprintf (scriptFile, "reason=\"%s\"\n", reason); + fprintf (scriptFile, "export reason\n"); +} + +void script_write_params (ip, prefix, lease) + struct interface_info *ip; + char *prefix; + struct client_lease *lease; +{ + int i; + u_int8_t dbuf [1500]; + int len; + + fprintf (scriptFile, "%sip_address=\"%s\"\n", + prefix, piaddr (lease -> address)); + fprintf (scriptFile, "export %sip_address\n", prefix); + + /* For the benefit of Linux (and operating systems which may + have similar needs), compute the network address based on + the supplied ip address and netmask, if provided. Also + compute the broadcast address (the host address all ones + broadcast address, not the host address all zeroes + broadcast address). */ + + if (lease -> options [DHO_SUBNET_MASK].len && + (lease -> options [DHO_SUBNET_MASK].len < + sizeof lease -> address.iabuf)) { + struct iaddr netmask, subnet, broadcast; + + memcpy (netmask.iabuf, + lease -> options [DHO_SUBNET_MASK].data, + lease -> options [DHO_SUBNET_MASK].len); + netmask.len = lease -> options [DHO_SUBNET_MASK].len; + + subnet = subnet_number (lease -> address, netmask); + if (subnet.len) { + fprintf (scriptFile, "%snetwork_number=\"%s\";\n", + prefix, piaddr (subnet)); + fprintf (scriptFile, "export %snetwork_number\n", + prefix); + + if (!lease -> options [DHO_BROADCAST_ADDRESS].len) { + broadcast = broadcast_addr (subnet, netmask); + if (broadcast.len) { + fprintf (scriptFile, + "%s%s=\"%s\";\n", prefix, + "broadcast_address", + piaddr (broadcast)); + fprintf (scriptFile, + "export %s%s\n", prefix, + "broadcast_address"); + } + } + } + } + + if (lease -> filename) { + fprintf (scriptFile, "%sfilename=\"%s\";\n", + prefix, lease -> filename); + fprintf (scriptFile, "export %sfilename\n", prefix); + } + if (lease -> server_name) { + fprintf (scriptFile, "%sserver_name=\"%s\";\n", + prefix, lease -> server_name); + fprintf (scriptFile, "export %sserver_name\n", prefix); + } + for (i = 0; i < 256; i++) { + u_int8_t *dp; + + if (ip -> client -> config -> defaults [i].len) { + if (lease -> options [i].len) { + switch (ip -> client -> + config -> default_actions [i]) { + case ACTION_DEFAULT: + dp = lease -> options [i].data; + len = lease -> options [i].len; + break; + case ACTION_SUPERSEDE: + supersede: + dp = ip -> client -> + config -> defaults [i].data; + len = ip -> client -> + config -> defaults [i].len; + break; + case ACTION_PREPEND: + len = (ip -> client -> + config -> defaults [i].len + + lease -> options [i].len); + if (len > sizeof dbuf) { + warn ("no space to %s %s", + "prepend option", + dhcp_options [i].name); + goto supersede; + } + dp = dbuf; + memcpy (dp, + ip -> client -> + config -> defaults [i].data, + ip -> client -> + config -> defaults [i].len); + memcpy (dp + ip -> client -> + config -> defaults [i].len, + lease -> options [i].data, + lease -> options [i].len); + break; + case ACTION_APPEND: + len = (ip -> client -> + config -> defaults [i].len + + lease -> options [i].len); + if (len > sizeof dbuf) { + warn ("no space to %s %s", + "prepend option", + dhcp_options [i].name); + goto supersede; + } + dp = dbuf; + memcpy (dp, + ip -> client -> + config -> defaults [i].data, + ip -> client -> + config -> defaults [i].len); + memcpy (dp + ip -> client -> + config -> defaults [i].len, + lease -> options [i].data, + lease -> options [i].len); + } + } else { + dp = ip -> client -> + config -> defaults [i].data; + len = ip -> client -> + config -> defaults [i].len; + } + } else if (lease -> options [i].len) { + len = lease -> options [i].len; + dp = lease -> options [i].data; + } else { + len = 0; + } + if (len) { + char *s = dhcp_option_ev_name (&dhcp_options [i]); + + fprintf (scriptFile, "%s%s=\"%s\"\n", prefix, s, + pretty_print_option (i, dp, len, 0, 0)); + fprintf (scriptFile, "export %s%s\n", prefix, s); + } + } + fprintf (scriptFile, "%sexpiry=\"%d\"\n", + prefix, (int)lease -> expiry); /* XXX */ + fprintf (scriptFile, "export %sexpiry\n", prefix); +} + +int script_go (ip) + struct interface_info *ip; +{ + int rval; + + if (ip) + fprintf (scriptFile, "%s\n", + ip -> client -> config -> script_name); + else + fprintf (scriptFile, "%s\n", + top_level_config.script_name); + fprintf (scriptFile, "exit $?\n"); + fclose (scriptFile); + chmod (scriptName, 0700); + rval = system (scriptName); + if (!save_scripts) + unlink (scriptName); + return rval; +} + +char *dhcp_option_ev_name (option) + struct option *option; +{ + static char evbuf [256]; + int i; + + if (strlen (option -> name) + 1 > sizeof evbuf) + error ("option %s name is larger than static buffer."); + for (i = 0; option -> name [i]; i++) { + if (option -> name [i] == '-') + evbuf [i] = '_'; + else + evbuf [i] = option -> name [i]; + } + + evbuf [i] = 0; + return evbuf; +} + +void go_daemon () +{ + static int state = 0; + int pid; + + /* Don't become a daemon if the user requested otherwise. */ + if (no_daemon) { + write_client_pid_file (); + return; + } + + /* Only do it once. */ + if (state) + return; + state = 1; + + /* Stop logging to stderr... */ + log_perror = 0; + + /* Become a daemon... */ + if ((pid = fork ()) < 0) + error ("Can't fork daemon: %m"); + else if (pid) + exit (0); + /* Become session leader and get pid... */ + pid = setsid (); + + write_client_pid_file (); +} + +void write_client_pid_file () +{ + FILE *pf; + int pfdesc; + + pfdesc = open (path_dhclient_pid, O_CREAT | O_TRUNC | O_WRONLY, 0644); + + if (pfdesc < 0) { + warn ("Can't create %s: %m", path_dhclient_pid); + return; + } + + pf = fdopen (pfdesc, "w"); + if (!pf) + warn ("Can't fdopen %s: %m", path_dhclient_pid); + else { + fprintf (pf, "%ld\n", (long)getpid ()); + fclose (pf); + } +} diff --git a/contrib/isc-dhcp/client/dhclient.conf b/contrib/isc-dhcp/client/dhclient.conf new file mode 100644 index 0000000..147e004 --- /dev/null +++ b/contrib/isc-dhcp/client/dhclient.conf @@ -0,0 +1,36 @@ +send host-name "andare.fugue.com"; +send dhcp-client-identifier 1:0:a0:24:ab:fb:9c; +send dhcp-lease-time 3600; +supersede domain-name "fugue.com home.vix.com"; +prepend domain-name-servers 127.0.0.1; +request subnet-mask, broadcast-address, time-offset, routers, + domain-name, domain-name-servers, host-name; +require subnet-mask, domain-name-servers; +timeout 60; +retry 60; +reboot 10; +select-timeout 5; +initial-interval 2; +script "/etc/dhclient-script"; +media "-link0 -link1 -link2", "link0 link1"; +reject 192.33.137.209; + +alias { + interface "ep0"; + fixed-address 192.5.5.213; + option subnet-mask 255.255.255.255; +} + +lease { + interface "ep0"; + fixed-address 192.33.137.200; + medium "link0 link1"; + option host-name "andare.swiftmedia.com"; + option subnet-mask 255.255.255.0; + option broadcast-address 192.33.137.255; + option routers 192.33.137.250; + option domain-name-servers 127.0.0.1; + renew 2 2000/1/12 00:00:01; + rebind 2 2000/1/12 00:00:01; + expire 2 2000/1/12 00:00:01; +} diff --git a/contrib/isc-dhcp/client/dhclient.conf.5 b/contrib/isc-dhcp/client/dhclient.conf.5 new file mode 100644 index 0000000..c5af648 --- /dev/null +++ b/contrib/isc-dhcp/client/dhclient.conf.5 @@ -0,0 +1,543 @@ +.\" dhclient.conf.5 +.\" +.\" Copyright (c) 1997 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: +.\" +.\" 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. +.\" +.\" 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 <mellon@fugue.com> in cooperation with Vixie +.\" Enterprises. To learn more about the Internet Software Consortium, +.\" see ``http://www.isc.org/isc''. To learn more about Vixie +.\" Enterprises, see ``http://www.vix.com''. +.TH dhclient.conf 5 +.SH NAME +dhclient.conf - DHCP client configuration file +.SH DESCRIPTION +The dhclient.conf file contains configuration information for +.IR dhclient, +the Internet Software Consortium DHCP Client. +.PP +The dhclient.conf file is a free-form ASCII text file. It is parsed by +the recursive-descent parser built into dhclient. The file may contain +extra tabs and newlines for formatting purposes. Keywords in the file +are case-insensitive. Comments may be placed anywhere within the +file (except within quotes). Comments begin with the # character and +end at the end of the line. +.PP +The dhclient.conf file can be used to configure the behaviour of the +client in a wide variety of ways: protocol timing, information +requested from the server, information required of the server, +defaults to use if the server does not provide certain information, +values with which to override information provided by the server, or +values to prepend or append to information provided by the server. +The configuration file can also be preinitialized with addresses to +use on networks that don't have DHCP servers. +.SH PROTOCOL TIMING +The timing behaviour of the client need not be configured by the user. +If no timing configuration is provided by the user, a fairly +reasonable timing behaviour will be used by default - one which +results in fairly timely updates without placing an inordinate load on +the server. +.PP +The following statements can be used to adjust the timing behaviour of +the DHCP client if required, however: +.PP +.I The +.B timeout +.I statement +.PP +.B timeout +.I time +.B ; +.PP +The +.I timeout +statement determines the amount of time that must pass between the +time that the client begins to try to determine its address and the +time that it decides that it's not going to be able to contact a +server. By default, this timeout is sixty seconds. After the +timeout has passed, if there are any static leases defined in the +configuration file, or any leases remaining in the lease database that +have not yet expired, the client will loop through these leases +attempting to validate them, and if it finds one that appears to be +valid, it will use that lease's address. If there are no valid +static leases or unexpired leases in the lease database, the client +will restart the protocol after the defined retry interval. +.PP +.I The +.B retry +.I statement +.PP + \fBretry \fItime\fR\fB;\fR +.PP +The +.I retry +statement determines the time that must pass after the client has +determined that there is no DHCP server present before it tries again +to contact a DHCP server. By default, this is five minutes. +.PP +.I The +.B select-timeout +.I statement +.PP + \fBselect-timeout \fItime\fR\fB;\fR +.PP +It is possible (some might say desirable) for there to be more than +one DHCP server serving any given network. In this case, it is +possible that a client may be sent more than one offer in response to +its initial lease discovery message. It may be that one of these +offers is preferable to the other (e.g., one offer may have the +address the client previously used, and the other may not). +.PP +The +.I select-timeout +is the time after the client sends its first lease discovery request +at which it stops waiting for offers from servers, assuming that it +has received at least one such offer. If no offers have been +received by the time the +.I select-timeout +has expired, the client will accept the first offer that arrives. +.PP +By default, the select-timeout is zero seconds - that is, the client +will take the first offer it sees. +.PP +.I The +.B reboot +.I statement +.PP + \fBreboot \fItime\fR\fB;\fR +.PP +When the client is restarted, it first tries to reacquire the last +address it had. This is called the INIT-REBOOT state. If it is +still attached to the same network it was attached to when it last +ran, this is the quickest way to get started. The +.I reboot +statement sets the time that must elapse after the client first tries +to reacquire its old address before it gives up and tries to discover +a new address. By default, the reboot timeout is ten seconds. +.PP +.I The +.B backoff-cutoff +.I statement +.PP + \fBbackoff-cutoff \fItime\fR\fB;\fR +.PP +The client uses an exponential backoff algorithm with some randomness, +so that if many clients try to configure themselves at the same time, +they will not make their requests in lockstep. The +.I backoff-cutoff +statement determines the maximum amount of time that the client is +allowed to back off. It defaults to two minutes. +.PP +.I The +.B initial-interval +.I statement +.PP + \fBinitial-interval \fItime\fR\fB;\fR +.PP +The +.I initial-interval +statement sets the amount of time between the first attempt to reach a +server and the second attempt to reach a server. Each time a message +is sent, the interval between messages is incremented by twice the +current interval multiplied by a random number between zero and one. +If it is greater than the backoff-cutoff amount, it is set to that +amount. It defaults to ten seconds. +.SH LEASE REQUIREMENTS AND REQUESTS +The DHCP protocol allows the client to request that the server send it +specific information, and not send it other information that it is not +prepared to accept. The protocol also allows the client to reject +offers from servers if they don't contain information the client +needs, or if the information provided is not satisfactory. +.PP +There is a variety of data contained in offers that DHCP servers send +to DHCP clients. The data that can be specifically requested is what +are called \fIDHCP Options\fR. DHCP Options are defined in + \fBdhcp-options(5)\fR. +.PP +.I The +.B request +.I statement +.PP + \fBrequest [ \fIoption\fR ] [\fB,\fI ... \fIoption\fR ]\fB;\fR +.PP +The request statement causes the client to request that any server +responding to the client send the client its values for the specified +options. Only the option names should be specified in the request +statement - not option parameters. +.PP +.I The +.B require +.I statement +.PP + \fBrequire [ \fIoption\fR ] [\fB,\fI ... \fIoption ]\fB;\fR +.PP +The require statement lists options that must be sent in order for an +offer to be accepted. Offers that do not contain all the listed +options will be ignored. +.PP +.I The +.B send +.I statement +.PP + \fBsend { [ \fIoption declaration\fR ] +[\fB,\fI ... \fIoption declaration\fR ]\fB}\fR +.PP +The send statement causes the client to send the specified options to +the server with the specified values. These are full option +declarations as described in \fBdhcp-options(5)\fR. Options that are +always sent in the DHCP protocol should not be specified here, except +that the client can specify a \fBrequested-lease-time\fR option other +than the default requested lease time, which is two hours. The other +obvious use for this statement is to send information to the server +that will allow it to differentiate between this client and other +clients or kinds of clients. +.SH OPTION MODIFIERS +In some cases, a client may receive option data from the server which +is not really appropriate for that client, or may not receive +information that it needs, and for which a useful default value +exists. It may also receive information which is useful, but which +needs to be supplemented with local information. To handle these +needs, several option modifiers are available. +.PP +.I The +.B default +.I statement +.PP + \fBdefault { [ \fIoption declaration\fR ] +[\fB,\fI ... \fIoption declaration\fR ]\fB}\fR +.PP +If for some set of options the client should use the value supplied by +the server, but needs to use some default value if no value was supplied +by the server, these values can be defined in the +.B default +statement. +.PP +.I The +.B supersede +.I statement +.PP + \fBsupersede { [ \fIoption declaration\fR ] +[\fB,\fI ... \fIoption declaration\fR ]\fB}\fR +.PP +If for some set of options the client should always use its own value +rather than any value supplied by the server, these values can be +defined in the +.B supersede +statement. +.PP +.I The +.B prepend +.I statement +.PP + \fBprepend { [ \fIoption declaration\fR ] +[\fB,\fI ... \fIoption declaration\fR ]\fB}\fR +.PP +If for some set of options the client should first a value it +supplies, and then use the values supplied by +the server, if any, these values can be defined in the +.B prepend +statement. The +.B prepend +statement can only be used for options which +allow more than one value to be given. +.PP +.I The +.B append +.I statement +.PP + \fBappend { [ \fIoption declaration\fR ] +[\fB,\fI ... \fIoption declaration\fR ]\fB}\fR +.PP +If for some set of options the client should first a value it +supplies, and then use the values supplied by +the server, if any, these values can be defined in the +.B append +statement. The +.B append +statement can only be used for options which +allow more than one value to be given. +.SH LEASE DECLARATIONS +.PP +.I The +.B lease +.I declaration +.PP + \fBlease {\fR \fIlease-declaration\fR [ ... \fIlease-declaration ] \fB}\fR +.PP +The DHCP client may decide after some period of time (see \fBPROTOCOL +TIMING\fR) decide that it is not going to succeed in contacting a +server. At that time, it consults its own database of old leases and +tests each one that has not yet timed out by pinging the listed router +for that lease to see if that lease could work. It is possible to +define one or more \fIfixed\fR leases in the client configuration file +for networks where there is no DHCP or BOOTP service, so that the +client can still automatically configure its address. This is done +with the +.B lease +statement. +.PP +NOTE: the lease statement is also used in the dhclient.leases file in +order to record leases that have been received from DHCP servers. +Some of the syntax for leases as described below is only needed in the +dhclient.leases file. Such syntax is documented here for +completeness. +.PP +A lease statement consists of the lease keyword, followed by a left +curly brace, followed by one or more lease declaration statements, +followed by a right curly brace. The following lease declarations +are possible: +.PP + \fBbootp;\fR +.PP +The +.B bootp +statement is used to indicate that the lease was acquired using the +BOOTP protocol rather than the DHCP protocol. It is never necessary +to specify this in the client configuration file. The client uses +this syntax in its lease database file. +.PP + \fBinterface\fR \fB"\fR\fIstring\fR\fB";\fR +.PP +The +.B interface +lease statement is used to indicate the interface on which the lease +is valid. If set, this lease will only be tried on a particular +interface. When the client receives a lease from a server, it always +records the interface number on which it received that lease. +If predefined leases are specified in the dhclient.conf file, the +interface should also be specified, although this is not required. +.PP + \fBfixed-address\fR \fIip-address\fR\fB;\fR +.PP +The +.B fixed-address +statement is used to set the ip address of a particular lease. This +is required for all lease statements. The IP address must be +specified as a dotted quad (e.g., 12.34.56.78). +.PP + \fBfilename "\fR\fIstring\fR\fB";\fR +.PP +The +.B filename +statement specifies the name of the boot filename to use. This is +not used by the standard client configuration script, but is included +for completeness. +.PP + \fBserver-name "\fR\fIstring\fR\fB";\fR +.PP +The +.B server-name +statement specifies the name of the boot server name to use. This is +also not used by the standard client configuration script. +.PP + \fBoption\fR \fIoption-declaration\fR\fB;\fR +.PP +The +.B option +statement is used to specify the value of an option supplied by the +server, or, in the case of predefined leases declared in +dhclient.conf, the value that the user wishes the client configuration +script to use if the predefined lease is used. +.PP + \fBscript "\fIscript-name\fB";\fR +.PP +The +.B script +statement is used to specify the pathname of the dhcp client +configuration script. This script is used by the dhcp client to set +each interface's initial configuration prior to requesting an address, +to test the address once it has been offered, and to set the +interface's final configuration once a lease has been acquired. If +no lease is acquired, the script is used to test predefined leases, if +any, and also called once if no valid lease can be identified. For +more information, see +.B dhclient-lease(8). +.PP + \fBmedium "\fImedia setup\fB";\fR +.PP +The +.B medium +statement can be used on systems where network interfaces cannot +automatically determine the type of network to which they are +connected. The media setup string is a system-dependent parameter +which is passed to the dhcp client configuration script when +initializing the interface. On Unix and Unix-like systems, the +argument is passed on the ifconfig command line when configuring te +interface. +.PP +The dhcp client automatically declares this parameter if it used a +media type (see the +.B media +statement) when configuring the interface in order to obtain a lease. +This statement should be used in predefined leases only if the network +interface requires media type configuration. +.PP + \fBrenew\fR \fIdate\fB;\fR +.PP + \fBrebind\fR \fIdate\fB;\fR +.PP + \fBexpire\fR \fIdate\fB;\fR +.PP +The \fBrenew\fR statement defines the time at which the dhcp client +should begin trying to contact its server to renew a lease that it is +using. The \fBrebind\fR statement defines the time at which the dhcp +client should begin to try to contact \fIany\fR dhcp server in order +to renew its lease. The \fBexpire\fR statement defines the time at +which the dhcp client must stop using a lease if it has not been able +to contact a server in order to renew it. +.PP +These declarations are automatically set in leases acquired by the +DHCP client, but must also be configured in predefined leases - a +predefined lease whose expiry time has passed will not be used by the +DHCP client. +.PP +Dates are specified as follows: +.PP + \fI<weekday> <year>\fB/\fI<month>\fB/\fI<day> +<hour>\fB:\fI<minute>\fB:\fI<second>\fR +.PP +The weekday is present to make it easy for a human to tell when a +lease expires - it's specified as a number from zero to six, with zero +being Sunday. When declaring a predefined lease, it can always be +specified as zero. The year is specified with the century, so it +should generally be four digits except for really long leases. The +month is specified as a number starting with 1 for January. The day +of the month is likewise specified starting with 1. The hour is a +number between 0 and 23, the minute a number between 0 and 69, and the +second also a number between 0 and 69. +.SH ALIAS DECLARATIONS + \fBalias { \fI declarations ... \fB}\fR +.PP +Some DHCP clients running TCP/IP roaming protocols may require that in +addition to the lease they may acquire via DHCP, their interface also +be configured with a predefined IP alias so that they can have a +permanent IP address even while roaming. The Internet Software +Consortium DHCP client doesn't support roaming with fixed addresses +directly, but in order to facilitate such experimentation, the dhcp +client can be set up to configure an IP alias using the +.B alias +declaration. +.PP +The alias declaration resembles a lease declaration, except that +options other than the subnet-mask option are ignored by the standard +client configuration script, and expiry times are ignored. A typical +alias declaration includes an interface declaration, a fixed-address +declaration for the IP alias address, and a subnet-mask option +declaration. A medium statement should never be included in an alias +declaration. +.SH OTHER DECLARATIONS + \fBreject \fIip-address\fB;\fR +.PP +The reject statement causes the DHCP client to reject offers from +servers who use the specified address as a server identifier. This +can be used to avoid being configured by rogue or misconfigured dhcp +servers, although it should be a last resort - better to track down +the bad DHCP server and fix it. +.PP + \fBinterface "\fIname\fB" { \fIdeclarations ... \fB } +.PP +A client with more than one network interface may require different +behaviour depending on which interface is being configured. All +timing parameters and declarations other than lease and alias +declarations can be enclosed in an interface declaration, and those +parameters will then be used only for the interface that matches the +specified name. Interfaces for which there is no interface +declaration will use the parameters declared outside of any interface +declaration, or the default settings. +.PP + \fBmedia "\fImedia setup\fB"\fI [ \fB, "\fImedia setup\fB", \fI... ]\fB;\fR +.PP +The +.B media +statement defines one or more media configuration parameters which may +be tried while attempting to acquire an IP address. The dhcp client +will cycle through each media setup string on the list, configuring +the interface using that setup and attempting to boot, and then trying +the next one. This can be used for network interfaces which aren't +capable of sensing the media type unaided - whichever media type +succeeds in getting a request to the server and hearing the reply is +probably right (no guarantees). +.PP +The media setup is only used for the initial phase of address +acquisition (the DHCPDISCOVER and DHCPOFFER packtes). Once an +address has been acquired, the dhcp client will record it in its lease +database and will record the media type used to acquire the address. +Whenever the client tries to renew the lease, it will use that same +media type. The lease must expire before the client will go back to +cycling through media types. +.SH SAMPLE +The following configuration file is used on a laptop running NetBSD +1.3. The laptop has an IP alias of 192.5.5.213, and has one +interface, ep0 (a 3com 3C589C). Booting intervals have been +shortened somewhat from the default, because the client is known to +spend most of its time on networks with little DHCP activity. The +laptop does roam to multiple networks. + +.nf + +timeout 60; +retry 60; +reboot 10; +select-timeout 5; +initial-interval 2; +reject 192.33.137.209; + +interface "ep0" { + send host-name "andare.fugue.com"; + send dhcp-client-identifier 1:0:a0:24:ab:fb:9c; + send dhcp-lease-time 3600; + supersede domain-name "fugue.com rc.vix.com home.vix.com"; + prepend domain-name-servers 127.0.0.1; + request subnet-mask, broadcast-address, time-offset, routers, + domain-name, domain-name-servers, host-name; + require subnet-mask, domain-name-servers; + script "/etc/dhclient-script"; + media "media 10baseT/UTP", "media 10base2/BNC"; +} + +alias { + interface "ep0"; + fixed-address 192.5.5.213; + option subnet-mask 255.255.255.255; +} +.fi +This is a very complicated dhclient.conf file - in general, yours +should be much simpler. In many cases, it's sufficient to just +create an empty dhclient.conf file - the defaults are usually fine. +.SH SEE ALSO +dhcp-options(5), dhclient.leases(5), dhcpd(8), dhcpd.conf(5), RFC2132, +RFC2131. +.SH AUTHOR +.B dhclient(8) +was written by Ted Lemon <mellon@vix.com> +under a contract with Vixie Labs. Funding +for this project was provided by the Internet Software Corporation. +Information about the Internet Software Consortium can be found at +.B http://www.isc.org/isc. diff --git a/contrib/isc-dhcp/client/dhclient.leases.5 b/contrib/isc-dhcp/client/dhclient.leases.5 new file mode 100644 index 0000000..e0da360 --- /dev/null +++ b/contrib/isc-dhcp/client/dhclient.leases.5 @@ -0,0 +1,62 @@ +.\" dhclient.conf.5 +.\" +.\" Copyright (c) 1997 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: +.\" +.\" 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. +.\" +.\" 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 <mellon@fugue.com> in cooperation with Vixie +.\" Enterprises. To learn more about the Internet Software Consortium, +.\" see ``http://www.isc.org/isc''. To learn more about Vixie +.\" Enterprises, see ``http://www.vix.com''. +.TH dhclient.leases 5 +.SH NAME +dhclient.leases - DHCP client lease database +.SH DESCRIPTION +The Internet Software Consortium DHCP client keeps a persistent +database of leases that it has acquired that are still valid. The +database is a free-form ASCII file containing one valid declaration +per lease. If more than one declaration appears for a given lease, +the last one in the file is used. The file is written as a log, so +this is not an unusual occurrance. +.PP +The format of the lease declarations is described in +.B dhclient.conf(5). +.SH FILES +.B DBDIR/dhclient.leases +.SH SEE ALSO +dhclient(8), dhcp-options(5), dhclient.conf(5), dhcpd(8), +dhcpd.conf(5), RFC2132, RFC2131. +.SH AUTHOR +.B dhclient(8) +was written by Ted Lemon <mellon@vix.com> +under a contract with Vixie Labs. Funding +for this project was provided by the Internet Software Corporation. +Information about the Internet Software Consortium can be found at +.B http://www.isc.org/isc. diff --git a/contrib/isc-dhcp/client/scripts/freebsd b/contrib/isc-dhcp/client/scripts/freebsd new file mode 100755 index 0000000..7236a67 --- /dev/null +++ b/contrib/isc-dhcp/client/scripts/freebsd @@ -0,0 +1,174 @@ +#!/bin/sh + +if [ x$new_network_number != x ]; then + echo New Network Number: $new_network_number +fi + +if [ x$new_broadcast_address != x ]; then + echo New Broadcast Address: $new_broadcast_address + new_broadcast_arg="broadcast $new_broadcast_address" +fi +if [ x$old_broadcast_address != x ]; then + old_broadcast_arg="broadcast $old_broadcast_address" +fi +if [ x$new_subnet_mask != x ]; then + new_netmask_arg="netmask $new_subnet_mask" +fi +if [ x$old_subnet_mask != x ]; then + old_netmask_arg="netmask $old_subnet_mask" +fi +if [ x$alias_subnet_mask != x ]; then + alias_subnet_arg="netmask $alias_subnet_mask" +fi + +if [ x$reason = xMEDIUM ]; then + ifconfig $interface $medium + ifconfig $interface inet -alias 0.0.0.0 $medium >/dev/null 2>&1 + sleep 1 + exit 0 +fi + +if [ x$reason = xPREINIT ]; then + if [ x$alias_ip_address != x ]; then + ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 + route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 + fi + ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 \ + broadcast 255.255.255.255 up + exit 0 +fi + +if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then + exit 0; +fi + +if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \ + [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then + if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \ + [ x$alias_ip_address != x$old_ip_address ]; then + ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 + route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 + fi + if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ]; then + ifconfig $interface inet -alias $old_ip_address $medium + route delete $old_ip_address 127.1 >/dev/null 2>&1 + for router in $old_routers; do + route delete default $router >/dev/null 2>&1 + done + if [ "$old_static_routes" != "" ]; then + set $old_static_routes + while [ $# -gt 1 ]; do + route delete $1 $2 + shift; shift + done + fi + arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' |sh + fi + if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \ + [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then + ifconfig $interface inet $new_ip_address $new_netmask_arg \ + $new_broadcast_arg $medium + route add $new_ip_address 127.1 >/dev/null 2>&1 + for router in $new_routers; do + route add default $router >/dev/null 2>&1 + done + if [ "$new_static_routes" != "" ]; then + set $new_static_routes + while [ $# -gt 1 ]; do + route add $1 $2 + shift; shift + done + fi + fi + if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ]; + then + ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg + route add $alias_ip_address 127.0.0.1 + fi + echo search $new_domain_name >/etc/resolv.conf + for nameserver in $new_domain_name_servers; do + echo nameserver $nameserver >>/etc/resolv.conf + done + exit 0 +fi + +if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ]; then + if [ x$alias_ip_address != x ]; then + ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 + route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 + fi + if [ x$old_ip_address != x ]; then + ifconfig $interface inet -alias $old_ip_address $medium + route delete $old_ip_address 127.1 >/dev/null 2>&1 + for router in $old_routers; do + route delete default $router >/dev/null 2>&1 + done + if [ "$old_static_routes" != "" ]; then + set $old_static_routes + while [ $# -gt 1 ]; do + route delete $1 $2 + shift; shift + done + fi + arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' \ + |sh >/dev/null 2>&1 + fi + if [ x$alias_ip_address != x ]; then + ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg + route add $alias_ip_address 127.0.0.1 + fi + exit 0 +fi + +if [ x$reason = xTIMEOUT ]; then + if [ x$alias_ip_address != x ]; then + ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 + route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 + fi + ifconfig $interface inet $new_ip_address $new_netmask_arg \ + $new_broadcast_arg $medium + sleep 1 + if [ "$new_routers" != "" ]; then + set $new_routers + if ping -q -c 1 -w 1 $1; then + if [ x$new_ip_address != x$alias_ip_address ] && \ + [ x$alias_ip_address != x ]; then + ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg + route add $alias_ip_address 127.0.0.1 + fi + route add $new_ip_address 127.1 >/dev/null 2>&1 + for router in $new_routers; do + route add default $router >/dev/null 2>&1 + done + set $new_static_routes + while [ $# -gt 1 ]; do + route add $0 $1 + shift; shift + done + echo search $new_domain_name >/etc/resolv.conf.std + for nameserver in $new_domain_name_servers; do + echo nameserver $nameserver >>/etc/resolv.conf.std + done + if [ -f /etc/resolv.conf ]; then + rm -f /etc/resolv.conf + fi + mv /etc/resolv.conf.std /etc/resolv.conf + exit 0 + fi + ifconfig $interface inet -alias $new_ip_address $medium + for router in $old_routers; do + route delete default $router >/dev/null 2>&1 + done + if [ "$old_static_routes" != "" ]; then + set $old_static_routes + while [ $# -gt 1 ]; do + route delete $1 $2 + shift; shift + done + fi + arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' \ + |sh >/dev/null 2>&1 + exit 1 +fi + +exit 0 |