Index: head/sbin/dhclient/dhclient.c =================================================================== --- head/sbin/dhclient/dhclient.c (revision 355018) +++ head/sbin/dhclient/dhclient.c (revision 355019) @@ -1,2848 +1,2849 @@ /* $OpenBSD: dhclient.c,v 1.63 2005/02/06 17:10:13 krw Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright 2004 Henning Brauer * Copyright (c) 1995, 1996, 1997, 1998, 1999 * 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 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. */ #include __FBSDID("$FreeBSD$"); #include "dhcpd.h" #include "privsep.h" #include #include #include #include #include #ifndef _PATH_VAREMPTY #define _PATH_VAREMPTY "/var/empty" #endif #define PERIOD 0x2e #define hyphenchar(c) ((c) == 0x2d) #define bslashchar(c) ((c) == 0x5c) #define periodchar(c) ((c) == PERIOD) #define asterchar(c) ((c) == 0x2a) #define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) || \ ((c) >= 0x61 && (c) <= 0x7a)) #define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) #define whitechar(c) ((c) == ' ' || (c) == '\t') #define borderchar(c) (alphachar(c) || digitchar(c)) #define middlechar(c) (borderchar(c) || hyphenchar(c)) #define domainchar(c) ((c) > 0x20 && (c) < 0x7f) #define CLIENT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin" cap_channel_t *capsyslog; time_t cur_time; static time_t default_lease_time = 43200; /* 12 hours... */ const char *path_dhclient_conf = _PATH_DHCLIENT_CONF; char *path_dhclient_db = NULL; int log_perror = 1; static int privfd; static int nullfd = -1; static char hostname[_POSIX_HOST_NAME_MAX + 1]; static struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } }; static struct in_addr inaddr_any, inaddr_broadcast; static char *path_dhclient_pidfile; struct pidfh *pidfile; /* * ASSERT_STATE() does nothing now; it used to be * assert (state_is == state_shouldbe). */ #define ASSERT_STATE(state_is, state_shouldbe) {} /* * We need to check that the expiry, renewal and rebind times are not beyond * the end of time (~2038 when a 32-bit time_t is being used). */ #define TIME_MAX ((((time_t) 1 << (sizeof(time_t) * CHAR_BIT - 2)) - 1) * 2 + 1) int log_priority; static int no_daemon; static int unknown_ok = 1; static int routefd; struct interface_info *ifi; int findproto(char *, int); struct sockaddr *get_ifa(char *, int); void routehandler(struct protocol *); void usage(void); int check_option(struct client_lease *l, int option); int check_classless_option(unsigned char *data, int len); int ipv4addrs(const char * buf); int res_hnok(const char *dn); int check_search(const char *srch); const char *option_as_string(unsigned int code, unsigned char *data, int len); int fork_privchld(int, int); #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) /* Minimum MTU is 68 as per RFC791, p. 24 */ #define MIN_MTU 68 static time_t scripttime; int findproto(char *cp, int n) { struct sockaddr *sa; unsigned i; if (n == 0) return -1; for (i = 1; i; i <<= 1) { if (i & n) { sa = (struct sockaddr *)cp; switch (i) { case RTA_IFA: case RTA_DST: case RTA_GATEWAY: case RTA_NETMASK: if (sa->sa_family == AF_INET) return AF_INET; if (sa->sa_family == AF_INET6) return AF_INET6; break; case RTA_IFP: break; } ADVANCE(cp, sa); } } return (-1); } struct sockaddr * get_ifa(char *cp, int n) { struct sockaddr *sa; unsigned i; if (n == 0) return (NULL); for (i = 1; i; i <<= 1) if (i & n) { sa = (struct sockaddr *)cp; if (i == RTA_IFA) return (sa); ADVANCE(cp, sa); } return (NULL); } static struct iaddr defaddr = { .len = 4 }; static uint8_t curbssid[6]; static void disassoc(void *arg) { struct interface_info *_ifi = arg; /* * Clear existing state. */ if (_ifi->client->active != NULL) { script_init("EXPIRE", NULL); script_write_params("old_", _ifi->client->active); if (_ifi->client->alias) script_write_params("alias_", _ifi->client->alias); script_go(); } _ifi->client->state = S_INIT; } void routehandler(struct protocol *p __unused) { char msg[2048], *addr; struct rt_msghdr *rtm; struct if_msghdr *ifm; struct ifa_msghdr *ifam; struct if_announcemsghdr *ifan; struct ieee80211_join_event *jev; struct client_lease *l; time_t t = time(NULL); struct sockaddr_in *sa; struct iaddr a; ssize_t n; int linkstat; n = read(routefd, &msg, sizeof(msg)); rtm = (struct rt_msghdr *)msg; if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < (ssize_t)rtm->rtm_msglen || rtm->rtm_version != RTM_VERSION) return; switch (rtm->rtm_type) { case RTM_NEWADDR: case RTM_DELADDR: ifam = (struct ifa_msghdr *)rtm; if (ifam->ifam_index != ifi->index) break; if (findproto((char *)(ifam + 1), ifam->ifam_addrs) != AF_INET) break; if (scripttime == 0 || t < scripttime + 10) break; sa = (struct sockaddr_in*)get_ifa((char *)(ifam + 1), ifam->ifam_addrs); if (sa == NULL) break; if ((a.len = sizeof(struct in_addr)) > sizeof(a.iabuf)) error("king bula sez: len mismatch"); memcpy(a.iabuf, &sa->sin_addr, a.len); if (addr_eq(a, defaddr)) break; for (l = ifi->client->active; l != NULL; l = l->next) if (addr_eq(a, l->address)) break; if (l == NULL) /* added/deleted addr is not the one we set */ break; addr = inet_ntoa(sa->sin_addr); if (rtm->rtm_type == RTM_NEWADDR) { /* * XXX: If someone other than us adds our address, * should we assume they are taking over from us, * delete the lease record, and exit without modifying * the interface? */ warning("My address (%s) was re-added", addr); } else { warning("My address (%s) was deleted, dhclient exiting", addr); goto die; } break; case RTM_IFINFO: ifm = (struct if_msghdr *)rtm; if (ifm->ifm_index != ifi->index) break; if ((rtm->rtm_flags & RTF_UP) == 0) { warning("Interface %s is down, dhclient exiting", ifi->name); goto die; } linkstat = interface_link_status(ifi->name); if (linkstat != ifi->linkstat) { debug("%s link state %s -> %s", ifi->name, ifi->linkstat ? "up" : "down", linkstat ? "up" : "down"); ifi->linkstat = linkstat; if (linkstat) state_reboot(ifi); } break; case RTM_IFANNOUNCE: ifan = (struct if_announcemsghdr *)rtm; if (ifan->ifan_what == IFAN_DEPARTURE && ifan->ifan_index == ifi->index) { warning("Interface %s is gone, dhclient exiting", ifi->name); goto die; } break; case RTM_IEEE80211: ifan = (struct if_announcemsghdr *)rtm; if (ifan->ifan_index != ifi->index) break; switch (ifan->ifan_what) { case RTM_IEEE80211_ASSOC: case RTM_IEEE80211_REASSOC: /* * Use assoc/reassoc event to kick state machine * in case we roam. Otherwise fall back to the * normal state machine just like a wired network. */ jev = (struct ieee80211_join_event *) &ifan[1]; if (memcmp(curbssid, jev->iev_addr, 6)) { disassoc(ifi); state_reboot(ifi); } memcpy(curbssid, jev->iev_addr, 6); break; } break; default: break; } return; die: script_init("FAIL", NULL); if (ifi->client->alias) script_write_params("alias_", ifi->client->alias); script_go(); if (pidfile != NULL) pidfile_remove(pidfile); exit(1); } static void init_casper(void) { cap_channel_t *casper; casper = cap_init(); if (casper == NULL) error("unable to start casper"); capsyslog = cap_service_open(casper, "system.syslog"); cap_close(casper); if (capsyslog == NULL) error("unable to open system.syslog service"); } int main(int argc, char *argv[]) { u_int capmode; int ch, fd, quiet = 0, i = 0; int pipe_fd[2]; int immediate_daemon = 0; struct passwd *pw; pid_t otherpid; cap_rights_t rights; init_casper(); /* Initially, log errors to stderr as well as to syslogd. */ cap_openlog(capsyslog, getprogname(), LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY); cap_setlogmask(capsyslog, LOG_UPTO(LOG_DEBUG)); while ((ch = getopt(argc, argv, "bc:dl:p:qu")) != -1) switch (ch) { case 'b': immediate_daemon = 1; break; case 'c': path_dhclient_conf = optarg; break; case 'd': no_daemon = 1; break; case 'l': path_dhclient_db = optarg; break; case 'p': path_dhclient_pidfile = optarg; break; case 'q': quiet = 1; break; case 'u': unknown_ok = 0; break; default: usage(); } argc -= optind; argv += optind; if (argc != 1) usage(); if (path_dhclient_pidfile == NULL) { asprintf(&path_dhclient_pidfile, "%s/dhclient/dhclient.%s.pid", _PATH_VARRUN, *argv); if (path_dhclient_pidfile == NULL) error("asprintf"); } pidfile = pidfile_open(path_dhclient_pidfile, 0644, &otherpid); if (pidfile == NULL) { if (errno == EEXIST) error("dhclient already running, pid: %d.", otherpid); if (errno == EAGAIN) error("dhclient already running."); warning("Cannot open or create pidfile: %m"); } if ((ifi = calloc(1, sizeof(struct interface_info))) == NULL) error("calloc"); if (strlcpy(ifi->name, argv[0], IFNAMSIZ) >= IFNAMSIZ) error("Interface name too long"); if (path_dhclient_db == NULL && asprintf(&path_dhclient_db, "%s.%s", _PATH_DHCLIENT_DB, ifi->name) == -1) error("asprintf"); if (quiet) log_perror = 0; tzset(); time(&cur_time); inaddr_broadcast.s_addr = INADDR_BROADCAST; inaddr_any.s_addr = INADDR_ANY; read_client_conf(); /* The next bit is potentially very time-consuming, so write out the pidfile right away. We will write it out again with the correct pid after daemonizing. */ if (pidfile != NULL) pidfile_write(pidfile); if (!interface_link_status(ifi->name)) { fprintf(stderr, "%s: no link ...", ifi->name); fflush(stderr); sleep(1); while (!interface_link_status(ifi->name)) { fprintf(stderr, "."); fflush(stderr); if (++i > 10) { fprintf(stderr, " giving up\n"); exit(1); } sleep(1); } fprintf(stderr, " got link\n"); } ifi->linkstat = 1; if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) error("cannot open %s: %m", _PATH_DEVNULL); if ((pw = getpwnam("_dhcp")) == NULL) { warning("no such user: _dhcp, falling back to \"nobody\""); if ((pw = getpwnam("nobody")) == NULL) error("no such user: nobody"); } /* * Obtain hostname before entering capability mode - it won't be * possible then, as reading kern.hostname is not permitted. */ if (gethostname(hostname, sizeof(hostname)) < 0) hostname[0] = '\0'; priv_script_init("PREINIT", NULL); if (ifi->client->alias) priv_script_write_params("alias_", ifi->client->alias); priv_script_go(); /* set up the interface */ discover_interfaces(ifi); if (pipe(pipe_fd) == -1) error("pipe"); fork_privchld(pipe_fd[0], pipe_fd[1]); close(ifi->ufdesc); ifi->ufdesc = -1; close(ifi->wfdesc); ifi->wfdesc = -1; close(pipe_fd[0]); privfd = pipe_fd[1]; cap_rights_init(&rights, CAP_READ, CAP_WRITE); if (caph_rights_limit(privfd, &rights) < 0) error("can't limit private descriptor: %m"); if ((fd = open(path_dhclient_db, O_RDONLY|O_EXLOCK|O_CREAT, 0)) == -1) error("can't open and lock %s: %m", path_dhclient_db); read_client_leases(); rewrite_client_leases(); close(fd); if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) != -1) add_protocol("AF_ROUTE", routefd, routehandler, ifi); if (shutdown(routefd, SHUT_WR) < 0) error("can't shutdown route socket: %m"); cap_rights_init(&rights, CAP_EVENT, CAP_READ); if (caph_rights_limit(routefd, &rights) < 0) error("can't limit route socket: %m"); endpwent(); setproctitle("%s", ifi->name); /* setgroups(2) is not permitted in capability mode. */ if (setgroups(1, &pw->pw_gid) != 0) error("can't restrict groups: %m"); if (caph_enter_casper() < 0) error("can't enter capability mode: %m"); /* * If we are not in capability mode (i.e., Capsicum or libcasper is * disabled), try to restrict filesystem access. This will fail if * kern.chroot_allow_open_directories is 0 or the process is jailed. */ if (cap_getmode(&capmode) < 0 || capmode == 0) { if (chroot(_PATH_VAREMPTY) == -1) error("chroot"); if (chdir("/") == -1) error("chdir(\"/\")"); } if (setegid(pw->pw_gid) || setgid(pw->pw_gid) || seteuid(pw->pw_uid) || setuid(pw->pw_uid)) error("can't drop privileges: %m"); if (immediate_daemon) go_daemon(); ifi->client->state = S_INIT; state_reboot(ifi); bootp_packet_handler = do_packet; dispatch(); /* not reached */ return (0); } void usage(void) { fprintf(stderr, "usage: %s [-bdqu] ", getprogname()); fprintf(stderr, "[-c conffile] [-l leasefile] interface\n"); exit(1); } /* * 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(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_request doesn't initialize xid because it normally comes from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER, so pick an xid now. */ ip->client->xid = arc4random(); /* Make a DHCPREQUEST packet, and set appropriate per-interface flags. */ make_request(ip, ip->client->active); 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 = NULL; /* 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(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(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 = NULL; 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("ARPCHECK", lp->medium); script_write_params("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()) { make_decline(ip, lp); send_decline(ip); goto freeit; } picked = lp; picked->next = NULL; } else { freeit: free_client_lease(lp); } } ip->client->offered_leases = NULL; /* 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(struct packet *packet) { struct interface_info *ip = packet->interface; struct client_lease *lease; /* 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 || (packet->interface->hw_address.hlen != packet->raw->hlen) || (memcmp(packet->interface->hw_address.haddr, packet->raw->chaddr, packet->raw->hlen))) return; if (ip->client->state != S_REBOOTING && ip->client->state != S_REQUESTING && ip->client->state != S_RENEWING && ip->client->state != S_REBINDING) 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. */ if (ip->client->config->default_actions[DHO_DHCP_LEASE_TIME] == ACTION_SUPERSEDE) ip->client->new->expiry = getULong( ip->client->config->defaults[DHO_DHCP_LEASE_TIME].data); else if (ip->client->new->options[DHO_DHCP_LEASE_TIME].data) ip->client->new->expiry = getULong( ip->client->new->options[DHO_DHCP_LEASE_TIME].data); else ip->client->new->expiry = default_lease_time; /* A number that looks negative here is really just very large, because the lease expiry offset is unsigned. Also make sure that the addition of cur_time below does not overflow (a 32 bit) time_t. */ if (ip->client->new->expiry < 0 || ip->client->new->expiry > TIME_MAX - cur_time) ip->client->new->expiry = TIME_MAX - cur_time; /* XXX should be fixed by resetting the client state */ if (ip->client->new->expiry < 60) ip->client->new->expiry = 60; /* Unless overridden in the config, take the server-provided renewal * time if there is one. Otherwise figure it out according to the spec. * Also make sure the renewal time does not exceed the expiry time. */ if (ip->client->config->default_actions[DHO_DHCP_RENEWAL_TIME] == ACTION_SUPERSEDE) ip->client->new->renewal = getULong( ip->client->config->defaults[DHO_DHCP_RENEWAL_TIME].data); else 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; if (ip->client->new->renewal < 0 || ip->client->new->renewal > ip->client->new->expiry / 2) ip->client->new->renewal = ip->client->new->expiry / 2; /* Same deal with the rebind time. */ if (ip->client->config->default_actions[DHO_DHCP_REBINDING_TIME] == ACTION_SUPERSEDE) ip->client->new->rebind = getULong( ip->client->config->defaults[DHO_DHCP_REBINDING_TIME].data); else 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 / 4 * 7; if (ip->client->new->rebind < 0 || ip->client->new->rebind > ip->client->new->renewal / 4 * 7) ip->client->new->rebind = ip->client->new->renewal / 4 * 7; /* Convert the time offsets into seconds-since-the-epoch */ ip->client->new->expiry += cur_time; ip->client->new->renewal += cur_time; ip->client->new->rebind += cur_time; bind_lease(ip); } void bind_lease(struct interface_info *ip) { struct option_data *opt; /* Remember the medium. */ ip->client->new->medium = ip->client->medium; opt = &ip->client->new->options[DHO_INTERFACE_MTU]; if (opt->len == sizeof(u_int16_t)) { u_int16_t mtu = 0; u_int16_t old_mtu = 0; bool supersede = (ip->client->config->default_actions[DHO_INTERFACE_MTU] == ACTION_SUPERSEDE); if (supersede) mtu = getUShort(ip->client->config->defaults[DHO_INTERFACE_MTU].data); else mtu = be16dec(opt->data); if (ip->client->active) { opt = &ip->client->active->options[DHO_INTERFACE_MTU]; if (opt->len == sizeof(u_int16_t)) { old_mtu = be16dec(opt->data); } } if (mtu < MIN_MTU) { /* Treat 0 like a user intentionally doesn't want to change MTU and, * therefore, warning is not needed */ if (!supersede || mtu != 0) warning("mtu size %u < %d: ignored", (unsigned)mtu, MIN_MTU); } else if (ip->client->state != S_RENEWING || mtu != old_mtu) { interface_set_mtu_unpriv(privfd, mtu); } } /* Write out the new lease. */ write_client_lease(ip, ip->client->new, 0); /* Run the client script with the new parameters. */ script_init((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("old_", ip->client->active); script_write_params("new_", ip->client->new); if (ip->client->alias) script_write_params("alias_", ip->client->alias); script_go(); /* 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 = NULL; /* 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), (int)(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(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); } void bootp(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(struct packet *packet) { struct iaddrlist *ap; void (*handler)(struct packet *); const 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(struct packet *packet) { struct interface_info *ip = packet->interface; struct client_lease *lease, *lp; int i; int arp_timeout_needed, stop_selecting; const char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ? "DHCPOFFER" : "BOOTREPLY"; /* 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 || (packet->interface->hw_address.hlen != packet->raw->hlen) || (memcmp(packet->interface->hw_address.haddr, packet->raw->chaddr, packet->raw->hlen))) 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("ARPSEND", lease->medium); script_write_params("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()) 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 = NULL; if (!ip->client->offered_leases) ip->client->offered_leases = lease; else { for (lp = ip->client->offered_leases; lp->next; lp = lp->next) ; /* nothing */ 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(struct packet *packet) { struct client_lease *lease; int i; lease = malloc(sizeof(struct client_lease)); if (!lease) { warning("dhcpoffer: no memory to record lease."); return (NULL); } memset(lease, 0, sizeof(*lease)); /* Copy the lease options. */ for (i = 0; i < 256; i++) { if (packet->options[i].len) { lease->options[i].data = malloc(packet->options[i].len + 1); if (!lease->options[i].data) { warning("dhcpoffer: no memory for option %d", i); free_client_lease(lease); return (NULL); } 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; } if (!check_option(lease,i)) { /* ignore a bogus lease offer */ warning("Invalid lease option - ignoring offer"); free_client_lease(lease); return (NULL); } } } lease->address.len = sizeof(packet->raw->yiaddr); memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len); lease->nextserver.len = sizeof(packet->raw->siaddr); memcpy(lease->nextserver.iabuf, &packet->raw->siaddr, lease->nextserver.len); /* If the server name was filled out, copy it. Do not attempt to validate the server name as a host name. RFC 2131 merely states that sname is NUL-terminated (which do do not assume) and that it is the server's host name. Since the ISC client and server allow arbitrary characters, we do as well. */ if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len || !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) && packet->raw->sname[0]) { lease->server_name = malloc(DHCP_SNAME_LEN + 1); if (!lease->server_name) { warning("dhcpoffer: no memory for server name."); free_client_lease(lease); return (NULL); } memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN); lease->server_name[DHCP_SNAME_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]) { /* Don't count on the NUL terminator. */ lease->filename = malloc(DHCP_FILE_LEN + 1); if (!lease->filename) { warning("dhcpoffer: no memory for filename."); free_client_lease(lease); return (NULL); } memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN); lease->filename[DHCP_FILE_LEN]='\0'; } return lease; } void dhcpnak(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 || (packet->interface->hw_address.hlen != packet->raw->hlen) || (memcmp(packet->interface->hw_address.haddr, packet->raw->chaddr, packet->raw->hlen))) return; if (ip->client->state != S_REBOOTING && ip->client->state != S_REQUESTING && ip->client->state != S_RENEWING && ip->client->state != S_REBINDING) 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 = NULL; /* 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(void *ipp) { struct interface_info *ip = ipp; int interval, 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("MEDIUM", ip->client->medium); if (script_go()) 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 += (arc4random() >> 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) + ((arc4random() >> 2) % ip->client->config->backoff_cutoff)); } 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 < 65536) ip->client->packet.secs = htons(interval); else ip->client->packet.secs = htons(65535); ip->client->secs = ip->client->packet.secs; note("DHCPDISCOVER on %s to %s port %d interval %d", ip->name, inet_ntoa(inaddr_broadcast), REMOTE_PORT, (int)ip->client->interval); /* Send out a packet. */ send_packet_unpriv(privfd, &ip->client->packet, ip->client->packet_length, inaddr_any, inaddr_broadcast); 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(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("TIMEOUT", ip->client->active->medium); script_write_params("new_", ip->client->active); if (ip->client->alias) script_write_params("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()) { if (cur_time < ip->client->active->renewal) { ip->client->state = S_BOUND; note("bound: renewal in %d seconds.", (int)(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 = NULL; 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 = NULL; 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("FAIL", NULL); if (ip->client->alias) script_write_params("alias_", ip->client->alias); script_go(); ip->client->state = S_INIT; add_timeout(cur_time + ip->client->config->retry_interval, state_init, ip); go_daemon(); } void send_request(void *ipp) { struct interface_info *ip = ipp; struct in_addr from, to; int interval; /* 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("MEDIUM", ip->client->active->medium); /* If the medium we chose won't fly, go to INIT state. */ if (script_go()) 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("EXPIRE", NULL); script_write_params("old_", ip->client->active); if (ip->client->alias) script_write_params("alias_", ip->client->alias); script_go(); /* Now do a preinit on the interface so that we can discover a new address. */ script_init("PREINIT", NULL); if (ip->client->alias) script_write_params("alias_", ip->client->alias); script_go(); 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 += ((arc4random() >> 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) + ((arc4random() >> 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 || ip->client->state == S_REBOOTING || cur_time > ip->client->active->rebind) to.s_addr = INADDR_BROADCAST; else memcpy(&to.s_addr, ip->client->destination.iabuf, sizeof(to.s_addr)); if (ip->client->state != S_REQUESTING && ip->client->state != S_REBOOTING) 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 (ip->client->state == S_REQUESTING) ip->client->packet.secs = ip->client->secs; else { if (interval < 65536) ip->client->packet.secs = htons(interval); else ip->client->packet.secs = htons(65535); } note("DHCPREQUEST on %s to %s port %d", ip->name, inet_ntoa(to), REMOTE_PORT); /* Send out a packet. */ send_packet_unpriv(privfd, &ip->client->packet, ip->client->packet_length, from, to); add_timeout(cur_time + ip->client->interval, send_request, ip); } void send_decline(void *ipp) { struct interface_info *ip = ipp; note("DHCPDECLINE on %s to %s port %d", ip->name, inet_ntoa(inaddr_broadcast), REMOTE_PORT); /* Send out a packet. */ send_packet_unpriv(privfd, &ip->client->packet, ip->client->packet_length, inaddr_any, inaddr_broadcast); } void make_discover(struct interface_info *ip, struct client_lease *lease) { unsigned char discover = DHCPDISCOVER; struct tree_cache *options[256]; struct tree_cache option_elements[256]; int i; 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; /* 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; /* 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; } 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; } /* send host name if not set via config file. */ if (!options[DHO_HOST_NAME]) { if (hostname[0] != '\0') { size_t len; char* posDot = strchr(hostname, '.'); if (posDot != NULL) len = posDot - hostname; else len = strlen(hostname); options[DHO_HOST_NAME] = &option_elements[DHO_HOST_NAME]; options[DHO_HOST_NAME]->value = hostname; options[DHO_HOST_NAME]->len = len; options[DHO_HOST_NAME]->buf_size = len; options[DHO_HOST_NAME]->timeout = 0xFFFFFFFF; } } /* set unique client identifier */ char client_ident[sizeof(ip->hw_address.haddr) + 1]; if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) { int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ? ip->hw_address.hlen : sizeof(client_ident)-1; client_ident[0] = ip->hw_address.htype; memcpy(&client_ident[1], ip->hw_address.haddr, hwlen); options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER]; options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident; options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1; options[DHO_DHCP_CLIENT_IDENTIFIER]->buf_size = hwlen+1; options[DHO_DHCP_CLIENT_IDENTIFIER]->timeout = 0xFFFFFFFF; } /* Set up the option buffer... */ ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, options, 0, 0, 0, NULL, 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 = arc4random(); ip->client->packet.secs = 0; /* filled in by send_discover. */ ip->client->packet.flags = 0; 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); } void make_request(struct interface_info *ip, struct client_lease * lease) { unsigned char request = DHCPREQUEST; struct tree_cache *options[256]; struct tree_cache option_elements[256]; int i; 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; /* 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; /* 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; } 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; } 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; } /* send host name if not set via config file. */ if (!options[DHO_HOST_NAME]) { if (hostname[0] != '\0') { size_t len; char* posDot = strchr(hostname, '.'); if (posDot != NULL) len = posDot - hostname; else len = strlen(hostname); options[DHO_HOST_NAME] = &option_elements[DHO_HOST_NAME]; options[DHO_HOST_NAME]->value = hostname; options[DHO_HOST_NAME]->len = len; options[DHO_HOST_NAME]->buf_size = len; options[DHO_HOST_NAME]->timeout = 0xFFFFFFFF; } } /* set unique client identifier */ char client_ident[sizeof(struct hardware)]; if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) { int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ? ip->hw_address.hlen : sizeof(client_ident)-1; client_ident[0] = ip->hw_address.htype; memcpy(&client_ident[1], ip->hw_address.haddr, hwlen); options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER]; options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident; options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1; options[DHO_DHCP_CLIENT_IDENTIFIER]->buf_size = hwlen+1; options[DHO_DHCP_CLIENT_IDENTIFIER]->timeout = 0xFFFFFFFF; } /* Set up the option buffer... */ ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, options, 0, 0, 0, NULL, 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. */ /* 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); ip->client->packet.flags = 0; } else { memset(&ip->client->packet.ciaddr, 0, sizeof(ip->client->packet.ciaddr)); ip->client->packet.flags = 0; } 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); } void make_decline(struct interface_info *ip, struct client_lease *lease) { struct tree_cache *options[256], message_type_tree; struct tree_cache requested_address_tree; struct tree_cache server_id_tree, client_id_tree; unsigned char decline = DHCPDECLINE; int i; 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; /* 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; /* 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; /* 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; } /* Set up the option buffer... */ ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, options, 0, 0, 0, NULL, 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 = 0; /* 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); } void free_client_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); } static FILE *leaseFile; void rewrite_client_leases(void) { struct client_lease *lp; cap_rights_t rights; if (!leaseFile) { leaseFile = fopen(path_dhclient_db, "w"); if (!leaseFile) error("can't create %s: %m", path_dhclient_db); cap_rights_init(&rights, CAP_FCNTL, CAP_FSTAT, CAP_FSYNC, CAP_FTRUNCATE, CAP_SEEK, CAP_WRITE); if (caph_rights_limit(fileno(leaseFile), &rights) < 0) { error("can't limit lease descriptor: %m"); } if (caph_fcntls_limit(fileno(leaseFile), CAP_FCNTL_GETFL) < 0) { error("can't limit lease descriptor fcntls: %m"); } } else { fflush(leaseFile); rewind(leaseFile); } for (lp = ifi->client->leases; lp; lp = lp->next) write_client_lease(ifi, lp, 1); if (ifi->client->active) write_client_lease(ifi, ifi->client->active, 1); fflush(leaseFile); ftruncate(fileno(leaseFile), ftello(leaseFile)); fsync(fileno(leaseFile)); } void write_client_lease(struct interface_info *ip, struct client_lease *lease, int rewrite) { static int leases_written; struct tm *t; int i; if (!rewrite) { if (leases_written++ > 20) { rewrite_client_leases(); leases_written = 0; } } /* 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 %s: %m", path_dhclient_db); } 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->nextserver.len == sizeof(inaddr_any) && 0 != memcmp(lease->nextserver.iabuf, &inaddr_any, sizeof(inaddr_any))) fprintf(leaseFile, " next-server %s;\n", piaddr(lease->nextserver)); if (lease->filename) fprintf(leaseFile, " filename \"%s\";\n", lease->filename); if (lease->server_name) fprintf(leaseFile, " server-name \"%s\";\n", lease->server_name); 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); } void script_init(const char *reason, struct string_list *medium) { size_t len, mediumlen = 0; struct imsg_hdr hdr; struct buf *buf; int errs; if (medium != NULL && medium->string != NULL) mediumlen = strlen(medium->string); hdr.code = IMSG_SCRIPT_INIT; hdr.len = sizeof(struct imsg_hdr) + sizeof(size_t) + mediumlen + sizeof(size_t) + strlen(reason); if ((buf = buf_open(hdr.len)) == NULL) error("buf_open: %m"); errs = 0; errs += buf_add(buf, &hdr, sizeof(hdr)); errs += buf_add(buf, &mediumlen, sizeof(mediumlen)); if (mediumlen > 0) errs += buf_add(buf, medium->string, mediumlen); len = strlen(reason); errs += buf_add(buf, &len, sizeof(len)); errs += buf_add(buf, reason, len); if (errs) error("buf_add: %m"); if (buf_close(privfd, buf) == -1) error("buf_close: %m"); } void priv_script_init(const char *reason, char *medium) { struct interface_info *ip = ifi; if (ip) { ip->client->scriptEnvsize = 100; if (ip->client->scriptEnv == NULL) ip->client->scriptEnv = malloc(ip->client->scriptEnvsize * sizeof(char *)); if (ip->client->scriptEnv == NULL) error("script_init: no memory for environment"); ip->client->scriptEnv[0] = strdup(CLIENT_PATH); if (ip->client->scriptEnv[0] == NULL) error("script_init: no memory for environment"); ip->client->scriptEnv[1] = NULL; script_set_env(ip->client, "", "interface", ip->name); if (medium) script_set_env(ip->client, "", "medium", medium); script_set_env(ip->client, "", "reason", reason); } } void priv_script_write_params(const char *prefix, struct client_lease *lease) { struct interface_info *ip = ifi; u_int8_t dbuf[1500], *dp = NULL; int i; size_t len; char tbuf[128]; script_set_env(ip->client, prefix, "ip_address", piaddr(lease->address)); if (ip->client->config->default_actions[DHO_SUBNET_MASK] == ACTION_SUPERSEDE) { dp = ip->client->config->defaults[DHO_SUBNET_MASK].data; len = ip->client->config->defaults[DHO_SUBNET_MASK].len; } else { dp = lease->options[DHO_SUBNET_MASK].data; len = lease->options[DHO_SUBNET_MASK].len; } if (len && (len < sizeof(lease->address.iabuf))) { struct iaddr netmask, subnet, broadcast; memcpy(netmask.iabuf, dp, len); netmask.len = len; subnet = subnet_number(lease->address, netmask); if (subnet.len) { script_set_env(ip->client, prefix, "network_number", piaddr(subnet)); if (!lease->options[DHO_BROADCAST_ADDRESS].len) { broadcast = broadcast_addr(subnet, netmask); if (broadcast.len) script_set_env(ip->client, prefix, "broadcast_address", piaddr(broadcast)); } } } if (lease->filename) script_set_env(ip->client, prefix, "filename", lease->filename); if (lease->server_name) script_set_env(ip->client, prefix, "server_name", lease->server_name); for (i = 0; i < 256; i++) { len = 0; 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)) { warning("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); dp[len] = '\0'; break; case ACTION_APPEND: /* * When we append, we assume that we're * appending to text. Some MS servers * include a NUL byte at the end of * the search string provided. */ len = ip->client-> config->defaults[i].len + lease->options[i].len; if (len >= sizeof(dbuf)) { warning("no space to %s %s", "append option", dhcp_options[i].name); goto supersede; } memcpy(dbuf, lease->options[i].data, lease->options[i].len); for (dp = dbuf + lease->options[i].len; dp > dbuf; dp--, len--) if (dp[-1] != '\0') break; memcpy(dp, ip->client-> config->defaults[i].data, ip->client-> config->defaults[i].len); dp = dbuf; dp[len] = '\0'; } } 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 name[256]; if (dhcp_option_ev_name(name, sizeof(name), &dhcp_options[i])) script_set_env(ip->client, prefix, name, pretty_print_option(i, dp, len, 0, 0)); } } snprintf(tbuf, sizeof(tbuf), "%d", (int)lease->expiry); script_set_env(ip->client, prefix, "expiry", tbuf); } void script_write_params(const char *prefix, struct client_lease *lease) { size_t fn_len = 0, sn_len = 0, pr_len = 0; struct imsg_hdr hdr; struct buf *buf; int errs, i; if (lease->filename != NULL) fn_len = strlen(lease->filename); if (lease->server_name != NULL) sn_len = strlen(lease->server_name); if (prefix != NULL) pr_len = strlen(prefix); hdr.code = IMSG_SCRIPT_WRITE_PARAMS; hdr.len = sizeof(hdr) + sizeof(*lease) + sizeof(fn_len) + fn_len + sizeof(sn_len) + sn_len + sizeof(pr_len) + pr_len; for (i = 0; i < 256; i++) { hdr.len += sizeof(lease->options[i].len); hdr.len += lease->options[i].len; } scripttime = time(NULL); if ((buf = buf_open(hdr.len)) == NULL) error("buf_open: %m"); errs = 0; errs += buf_add(buf, &hdr, sizeof(hdr)); errs += buf_add(buf, lease, sizeof(*lease)); errs += buf_add(buf, &fn_len, sizeof(fn_len)); errs += buf_add(buf, lease->filename, fn_len); errs += buf_add(buf, &sn_len, sizeof(sn_len)); errs += buf_add(buf, lease->server_name, sn_len); errs += buf_add(buf, &pr_len, sizeof(pr_len)); errs += buf_add(buf, prefix, pr_len); for (i = 0; i < 256; i++) { errs += buf_add(buf, &lease->options[i].len, sizeof(lease->options[i].len)); errs += buf_add(buf, lease->options[i].data, lease->options[i].len); } if (errs) error("buf_add: %m"); if (buf_close(privfd, buf) == -1) error("buf_close: %m"); } int script_go(void) { struct imsg_hdr hdr; struct buf *buf; int ret; hdr.code = IMSG_SCRIPT_GO; hdr.len = sizeof(struct imsg_hdr); if ((buf = buf_open(hdr.len)) == NULL) error("buf_open: %m"); if (buf_add(buf, &hdr, sizeof(hdr))) error("buf_add: %m"); if (buf_close(privfd, buf) == -1) error("buf_close: %m"); bzero(&hdr, sizeof(hdr)); buf_read(privfd, &hdr, sizeof(hdr)); if (hdr.code != IMSG_SCRIPT_GO_RET) error("unexpected msg type %u", hdr.code); if (hdr.len != sizeof(hdr) + sizeof(int)) error("received corrupted message"); buf_read(privfd, &ret, sizeof(ret)); scripttime = time(NULL); return (ret); } int priv_script_go(void) { char *scriptName, *argv[2], **envp, *epp[3], reason[] = "REASON=NBI"; static char client_path[] = CLIENT_PATH; struct interface_info *ip = ifi; int pid, wpid, wstatus; scripttime = time(NULL); if (ip) { scriptName = ip->client->config->script_name; envp = ip->client->scriptEnv; } else { scriptName = top_level_config.script_name; epp[0] = reason; epp[1] = client_path; epp[2] = NULL; envp = epp; } argv[0] = scriptName; argv[1] = NULL; pid = fork(); if (pid < 0) { error("fork: %m"); wstatus = 0; } else if (pid) { do { wpid = wait(&wstatus); } while (wpid != pid && wpid > 0); if (wpid < 0) { error("wait: %m"); wstatus = 0; } } else { execve(scriptName, argv, envp); error("execve (%s, ...): %m", scriptName); } if (ip) script_flush_env(ip->client); return (WIFEXITED(wstatus) ? WEXITSTATUS(wstatus) : 128 + WTERMSIG(wstatus)); } void script_set_env(struct client_state *client, const char *prefix, const char *name, const char *value) { int i, namelen; size_t j; /* No `` or $() command substitution allowed in environment values! */ for (j=0; j < strlen(value); j++) switch (value[j]) { case '`': case '$': warning("illegal character (%c) in value '%s'", value[j], value); /* Ignore this option */ return; } namelen = strlen(name); for (i = 0; client->scriptEnv[i]; i++) if (strncmp(client->scriptEnv[i], name, namelen) == 0 && client->scriptEnv[i][namelen] == '=') break; if (client->scriptEnv[i]) /* Reuse the slot. */ free(client->scriptEnv[i]); else { /* New variable. Expand if necessary. */ if (i >= client->scriptEnvsize - 1) { char **newscriptEnv; int newscriptEnvsize = client->scriptEnvsize + 50; newscriptEnv = realloc(client->scriptEnv, newscriptEnvsize); if (newscriptEnv == NULL) { free(client->scriptEnv); client->scriptEnv = NULL; client->scriptEnvsize = 0; error("script_set_env: no memory for variable"); } client->scriptEnv = newscriptEnv; client->scriptEnvsize = newscriptEnvsize; } /* need to set the NULL pointer at end of array beyond the new slot. */ client->scriptEnv[i + 1] = NULL; } /* Allocate space and format the variable in the appropriate slot. */ client->scriptEnv[i] = malloc(strlen(prefix) + strlen(name) + 1 + strlen(value) + 1); if (client->scriptEnv[i] == NULL) error("script_set_env: no memory for variable assignment"); snprintf(client->scriptEnv[i], strlen(prefix) + strlen(name) + 1 + strlen(value) + 1, "%s%s=%s", prefix, name, value); } void script_flush_env(struct client_state *client) { int i; for (i = 0; client->scriptEnv[i]; i++) { free(client->scriptEnv[i]); client->scriptEnv[i] = NULL; } client->scriptEnvsize = 0; } int dhcp_option_ev_name(char *buf, size_t buflen, struct option *option) { size_t i; for (i = 0; option->name[i]; i++) { if (i + 1 == buflen) return 0; if (option->name[i] == '-') buf[i] = '_'; else buf[i] = option->name[i]; } buf[i] = 0; return 1; } void go_daemon(void) { static int state = 0; cap_rights_t rights; if (no_daemon || state) return; state = 1; /* Stop logging to stderr... */ log_perror = 0; if (daemonfd(-1, nullfd) == -1) error("daemon"); cap_rights_init(&rights); if (pidfile != NULL) { pidfile_write(pidfile); if (caph_rights_limit(pidfile_fileno(pidfile), &rights) < 0) error("can't limit pidfile descriptor: %m"); } if (nullfd != -1) { close(nullfd); nullfd = -1; } if (caph_rights_limit(STDIN_FILENO, &rights) < 0) error("can't limit stdin: %m"); cap_rights_init(&rights, CAP_WRITE); if (caph_rights_limit(STDOUT_FILENO, &rights) < 0) error("can't limit stdout: %m"); if (caph_rights_limit(STDERR_FILENO, &rights) < 0) error("can't limit stderr: %m"); } int check_option(struct client_lease *l, int option) { const char *opbuf; const char *sbuf; /* we use this, since this is what gets passed to dhclient-script */ opbuf = pretty_print_option(option, l->options[option].data, l->options[option].len, 0, 0); sbuf = option_as_string(option, l->options[option].data, l->options[option].len); switch (option) { case DHO_SUBNET_MASK: case DHO_TIME_SERVERS: case DHO_NAME_SERVERS: case DHO_ROUTERS: case DHO_DOMAIN_NAME_SERVERS: case DHO_LOG_SERVERS: case DHO_COOKIE_SERVERS: case DHO_LPR_SERVERS: case DHO_IMPRESS_SERVERS: case DHO_RESOURCE_LOCATION_SERVERS: case DHO_SWAP_SERVER: case DHO_BROADCAST_ADDRESS: case DHO_NIS_SERVERS: case DHO_NTP_SERVERS: case DHO_NETBIOS_NAME_SERVERS: case DHO_NETBIOS_DD_SERVER: case DHO_FONT_SERVERS: case DHO_DHCP_SERVER_IDENTIFIER: case DHO_NISPLUS_SERVERS: case DHO_MOBILE_IP_HOME_AGENT: case DHO_SMTP_SERVER: case DHO_POP_SERVER: case DHO_NNTP_SERVER: case DHO_WWW_SERVER: case DHO_FINGER_SERVER: case DHO_IRC_SERVER: case DHO_STREETTALK_SERVER: case DHO_STREETTALK_DA_SERVER: if (!ipv4addrs(opbuf)) { warning("Invalid IP address in option: %s", opbuf); return (0); } return (1) ; case DHO_HOST_NAME: case DHO_NIS_DOMAIN: case DHO_NISPLUS_DOMAIN: case DHO_TFTP_SERVER_NAME: if (!res_hnok(sbuf)) { warning("Bogus Host Name option %d: %s (%s)", option, sbuf, opbuf); l->options[option].len = 0; free(l->options[option].data); } return (1); case DHO_DOMAIN_NAME: case DHO_DOMAIN_SEARCH: if (!res_hnok(sbuf)) { if (!check_search(sbuf)) { warning("Bogus domain search list %d: %s (%s)", option, sbuf, opbuf); l->options[option].len = 0; free(l->options[option].data); } } return (1); case DHO_PAD: case DHO_TIME_OFFSET: case DHO_BOOT_SIZE: case DHO_MERIT_DUMP: case DHO_ROOT_PATH: case DHO_EXTENSIONS_PATH: case DHO_IP_FORWARDING: case DHO_NON_LOCAL_SOURCE_ROUTING: case DHO_POLICY_FILTER: case DHO_MAX_DGRAM_REASSEMBLY: case DHO_DEFAULT_IP_TTL: case DHO_PATH_MTU_AGING_TIMEOUT: case DHO_PATH_MTU_PLATEAU_TABLE: case DHO_INTERFACE_MTU: case DHO_ALL_SUBNETS_LOCAL: case DHO_PERFORM_MASK_DISCOVERY: case DHO_MASK_SUPPLIER: case DHO_ROUTER_DISCOVERY: case DHO_ROUTER_SOLICITATION_ADDRESS: case DHO_STATIC_ROUTES: case DHO_TRAILER_ENCAPSULATION: case DHO_ARP_CACHE_TIMEOUT: case DHO_IEEE802_3_ENCAPSULATION: case DHO_DEFAULT_TCP_TTL: case DHO_TCP_KEEPALIVE_INTERVAL: case DHO_TCP_KEEPALIVE_GARBAGE: case DHO_VENDOR_ENCAPSULATED_OPTIONS: case DHO_NETBIOS_NODE_TYPE: case DHO_NETBIOS_SCOPE: case DHO_X_DISPLAY_MANAGER: case DHO_DHCP_REQUESTED_ADDRESS: case DHO_DHCP_LEASE_TIME: case DHO_DHCP_OPTION_OVERLOAD: case DHO_DHCP_MESSAGE_TYPE: case DHO_DHCP_PARAMETER_REQUEST_LIST: case DHO_DHCP_MESSAGE: case DHO_DHCP_MAX_MESSAGE_SIZE: case DHO_DHCP_RENEWAL_TIME: case DHO_DHCP_REBINDING_TIME: case DHO_DHCP_CLASS_IDENTIFIER: case DHO_DHCP_CLIENT_IDENTIFIER: case DHO_BOOTFILE_NAME: case DHO_DHCP_USER_CLASS_ID: + case DHO_URL: case DHO_END: return (1); case DHO_CLASSLESS_ROUTES: return (check_classless_option(l->options[option].data, l->options[option].len)); default: warning("unknown dhcp option value 0x%x", option); return (unknown_ok); } } /* RFC 3442 The Classless Static Routes option checks */ int check_classless_option(unsigned char *data, int len) { int i = 0; unsigned char width; in_addr_t addr, mask; if (len < 5) { warning("Too small length: %d", len); return (0); } while(i < len) { width = data[i++]; if (width == 0) { i += 4; continue; } else if (width < 9) { addr = (in_addr_t)(data[i] << 24); i += 1; } else if (width < 17) { addr = (in_addr_t)(data[i] << 24) + (in_addr_t)(data[i + 1] << 16); i += 2; } else if (width < 25) { addr = (in_addr_t)(data[i] << 24) + (in_addr_t)(data[i + 1] << 16) + (in_addr_t)(data[i + 2] << 8); i += 3; } else if (width < 33) { addr = (in_addr_t)(data[i] << 24) + (in_addr_t)(data[i + 1] << 16) + (in_addr_t)(data[i + 2] << 8) + data[i + 3]; i += 4; } else { warning("Incorrect subnet width: %d", width); return (0); } mask = (in_addr_t)(~0) << (32 - width); addr = ntohl(addr); mask = ntohl(mask); /* * From RFC 3442: * ... After deriving a subnet number and subnet mask * from each destination descriptor, the DHCP client * MUST zero any bits in the subnet number where the * corresponding bit in the mask is zero... */ if ((addr & mask) != addr) { addr &= mask; data[i - 1] = (unsigned char)( (addr >> (((32 - width)/8)*8)) & 0xFF); } i += 4; } if (i > len) { warning("Incorrect data length: %d (must be %d)", len, i); return (0); } return (1); } int res_hnok(const char *dn) { int pch = PERIOD, ch = *dn++; while (ch != '\0') { int nch = *dn++; if (periodchar(ch)) { ; } else if (periodchar(pch)) { if (!borderchar(ch)) return (0); } else if (periodchar(nch) || nch == '\0') { if (!borderchar(ch)) return (0); } else { if (!middlechar(ch)) return (0); } pch = ch, ch = nch; } return (1); } int check_search(const char *srch) { int pch = PERIOD, ch = *srch++; int domains = 1; /* 256 char limit re resolv.conf(5) */ if (strlen(srch) > 256) return (0); while (whitechar(ch)) ch = *srch++; while (ch != '\0') { int nch = *srch++; if (periodchar(ch) || whitechar(ch)) { ; } else if (periodchar(pch)) { if (!borderchar(ch)) return (0); } else if (periodchar(nch) || nch == '\0') { if (!borderchar(ch)) return (0); } else { if (!middlechar(ch)) return (0); } if (!whitechar(ch)) { pch = ch; } else { while (whitechar(nch)) { nch = *srch++; } if (nch != '\0') domains++; pch = PERIOD; } ch = nch; } /* 6 domain limit re resolv.conf(5) */ if (domains > 6) return (0); return (1); } /* Does buf consist only of dotted decimal ipv4 addrs? * return how many if so, * otherwise, return 0 */ int ipv4addrs(const char * buf) { struct in_addr jnk; int count = 0; while (inet_aton(buf, &jnk) == 1){ count++; while (periodchar(*buf) || digitchar(*buf)) buf++; if (*buf == '\0') return (count); while (*buf == ' ') buf++; } return (0); } const char * option_as_string(unsigned int code, unsigned char *data, int len) { static char optbuf[32768]; /* XXX */ char *op = optbuf; int opleft = sizeof(optbuf); unsigned char *dp = data; if (code > 255) error("option_as_string: bad code %d", code); for (; dp < data + len; dp++) { if (!isascii(*dp) || !isprint(*dp)) { if (dp + 1 != data + len || *dp != 0) { snprintf(op, opleft, "\\%03o", *dp); op += 4; opleft -= 4; } } else if (*dp == '"' || *dp == '\'' || *dp == '$' || *dp == '`' || *dp == '\\') { *op++ = '\\'; *op++ = *dp; opleft -= 2; } else { *op++ = *dp; opleft--; } } if (opleft < 1) goto toobig; *op = 0; return optbuf; toobig: warning("dhcp option too large"); return ""; } int fork_privchld(int fd, int fd2) { struct pollfd pfd[1]; int nfds; switch (fork()) { case -1: error("cannot fork"); case 0: break; default: return (0); } setproctitle("%s [priv]", ifi->name); setsid(); dup2(nullfd, STDIN_FILENO); dup2(nullfd, STDOUT_FILENO); dup2(nullfd, STDERR_FILENO); close(nullfd); close(fd2); close(ifi->rfdesc); ifi->rfdesc = -1; for (;;) { pfd[0].fd = fd; pfd[0].events = POLLIN; if ((nfds = poll(pfd, 1, INFTIM)) == -1) if (errno != EINTR) error("poll error"); if (nfds == 0 || !(pfd[0].revents & POLLIN)) continue; dispatch_imsg(ifi, fd); } } Index: head/sbin/dhclient/dhcp-options.5 =================================================================== --- head/sbin/dhclient/dhcp-options.5 (revision 355018) +++ head/sbin/dhclient/dhcp-options.5 (revision 355019) @@ -1,610 +1,613 @@ .\" $OpenBSD: dhcp-options.5,v 1.5 2005/03/02 15:30:42 jmc Exp $ .\" .\" Copyright (c) 1995, 1996, 1997, 1998 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 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''. .\" .\" $FreeBSD$ .\" .Dd January 1, 1995 .Dt DHCP-OPTIONS 5 .Os .Sh NAME .Nm dhcp-options .Nd Dynamic Host Configuration Protocol options .Sh DESCRIPTION The Dynamic Host Configuration protocol allows the client to receive .Ic options from the DHCP server describing the network configuration and various services that are available on the network. When configuring .Xr dhcpd 8 or .Xr dhclient 8 , options must often be declared. The syntax for declaring options, and the names and formats of the options that can be declared, are documented here. .Sh REFERENCE: OPTION STATEMENTS DHCP .Ic option statements always start with the .Ic option keyword, followed by an option name, followed by option data. The option names and data formats are described below. It is not necessary to exhaustively specify all DHCP options - only those options which are needed by clients must be specified. .Pp Option data comes in a variety of formats, as defined below: .Pp The .Ar ip-address data type can be entered either as an explicit IP address (e.g., .Li 239.254.197.10 ) or as a domain name (e.g., .Li haagen.isc.org ) . A domain name must resolve to a single IP address. .Pp The .Ar int32 data type specifies a signed 32-bit integer. The .Ar uint32 data type specifies an unsigned 32-bit integer. The .Ar int16 and .Ar uint16 data types specify signed and unsigned 16-bit integers. The .Ar int8 and .Ar uint8 data types specify signed and unsigned 8-bit integers. Unsigned 8-bit integers are also sometimes referred to as octets. .Pp The .Ar string data type specifies an .Tn NVT .Pq Network Virtual Terminal .Tn ASCII string, which must be enclosed in double quotes - for example, to specify a domain-name option, the syntax would be .Pp .Dl option domain-name \&"isc.org"; .Pp The .Ar flag data type specifies a boolean value. Booleans can be either .Li true or .Li false (or .Li on or .Li off , if that makes more sense to you). .Pp The .Ar data-string data type specifies either an .Tn NVT ASCII string enclosed in double quotes, or a series of octets specified in hexadecimal, separated by colons. For example: .Pp .Dl option dhcp-client-identifier \&"CLIENT-FOO"; or .Dl option dhcp-client-identifier 43:4c:49:45:54:2d:46:4f:4f; .Pp The documentation for the various options mentioned below is taken from the IETF draft document on DHCP options, RFC 2132. Options which are not listed by name may be defined by the name .Li option- Ns Ar nnn , where .Ar nnn is the decimal number of the option code. These options may be followed either by a string, enclosed in quotes, or by a series of octets, expressed as two-digit hexadecimal numbers separated by colons. For example: .Bd -literal -offset indent option option-133 "my-option-133-text"; option option-129 1:54:c9:2b:47; .Ed .Pp Because .Xr dhcpd 8 does not know the format of these undefined option codes, no checking is done to ensure the correctness of the entered data. .Pp The standard options are: .Ss RFC 1497 Vendor Extensions .Bl -tag -width indent .It Ic option subnet-mask Ar ip-address ; The .Ic subnet-mask option specifies the client's subnet mask as per RFC 950. If no subnet-mask option is provided anywhere in scope, as a last resort .Xr dhcpd 8 will use the subnet mask from the subnet declaration for the network on which an address is being assigned. However, .Em any subnet-mask option declaration that is in scope for the address being assigned will override the subnet mask specified in the subnet declaration. .It Ic option time-offset Ar int32 ; The .Ic time-offset option specifies the offset of the client's subnet in seconds from Coordinated Universal Time (UTC). .It Xo .Ic option routers Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic routers option specifies a list of IP addresses for routers on the client's subnet. Routers should be listed in order of preference. .It Xo .Ic option time-servers Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic time-server option specifies a list of RFC 868 time servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option ien116-name-servers Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic ien116-name-servers option specifies a list of IEN 116 name servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option domain-name-servers Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic domain-name-servers option specifies a list of Domain Name System (STD 13, RFC 1035) name servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option log-servers Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic log-servers option specifies a list of MIT-LCS UDP log servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option cookie-servers Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic cookie-servers option specifies a list of RFC 865 cookie servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option lpr-servers Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic lpr-servers option specifies a list of RFC 1179 line printer servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option impress-servers Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic impress-servers option specifies a list of Imagen Impress servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option resource-location-servers Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc This option specifies a list of RFC 887 Resource Location servers available to the client. Servers should be listed in order of preference. .It Ic option host-name Ar string ; This option specifies the name of the client. The name may or may not be qualified with the local domain name (it is preferable to use the .Ic domain-name option to specify the domain name). See RFC 1035 for character set restrictions. .It Ic option boot-size Ar uint16 ; This option specifies the length in 512-octet blocks of the default boot image for the client. .It Ic option merit-dump Ar string ; This option specifies the pathname of a file to which the client's core image should be dumped in the event the client crashes. The path is formatted as a character string consisting of characters from the .Tn NVT ASCII character set. .It Ic option domain-name Ar string ; This option specifies the domain name that the client should use when resolving hostnames via the Domain Name System. .It Ic option domain-search Ar string ; This option specifies a list of domain names that the client should use when resolving hostnames via the Domain Name System. This option is defined in RFC 3397. .It Ic option swap-server Ar ip-address ; This specifies the IP address of the client's swap server. .It Ic option root-path Ar string ; This option specifies the pathname that contains the client's root disk. The path is formatted as a character string consisting of characters from the .Tn NVT ASCII character set. .El .Ss IP Layer Parameters per Host .Bl -tag -width indent .It Ic option ip-forwarding Ar flag ; This option specifies whether the client should configure its IP layer for packet forwarding. A value of 0 means disable IP forwarding, and a value of 1 means enable IP forwarding. .It Ic option non-local-source-routing Ar flag ; This option specifies whether the client should configure its IP layer to allow forwarding of datagrams with non-local source routes (see Section 3.3.5 of [4] for a discussion of this topic). A value of 0 means disallow forwarding of such datagrams, and a value of 1 means allow forwarding. .It Xo .Ic option policy-filter Ar ip-address ip-address .Oo , Ar ip-address ip-address ... Oc ; .Xc This option specifies policy filters for non-local source routing. The filters consist of a list of IP addresses and masks which specify destination/mask pairs with which to filter incoming source routes. .Pp Any source-routed datagram whose next-hop address does not match one of the filters should be discarded by the client. .Pp See STD 3 (RFC 1122) for further information. .It Ic option max-dgram-reassembly Ar uint16 ; This option specifies the maximum size datagram that the client should be prepared to reassemble. The minimum legal value is 576. .It Ic option default-ip-ttl Ar uint8 ; This option specifies the default time-to-live that the client should use on outgoing datagrams. .It Ic option path-mtu-aging-timeout Ar uint32 ; This option specifies the timeout (in seconds) to use when aging Path MTU values discovered by the mechanism defined in RFC 1191. .It Xo .Ic option path-mtu-plateau-table Ar uint16 .Oo , Ar uint16 ... Oc ; .Xc This option specifies a table of MTU sizes to use when performing Path MTU Discovery as defined in RFC 1191. The table is formatted as a list of 16-bit unsigned integers, ordered from smallest to largest. The minimum MTU value cannot be smaller than 68. .El .Ss IP Layer Parameters per Interface .Bl -tag -width indent .It Ic option interface-mtu Ar uint16 ; This option specifies the MTU to use on this interface. The minimum legal value for the MTU is 68. .It Ic option all-subnets-local Ar flag ; This option specifies whether or not the client may assume that all subnets of the IP network to which the client is connected use the same MTU as the subnet of that network to which the client is directly connected. A value of 1 indicates that all subnets share the same MTU. A value of 0 means that the client should assume that some subnets of the directly connected network may have smaller MTUs. .It Ic option broadcast-address Ar ip-address ; This option specifies the broadcast address in use on the client's subnet. Legal values for broadcast addresses are specified in section 3.2.1.3 of STD 3 (RFC 1122). .It Ic option perform-mask-discovery Ar flag ; This option specifies whether or not the client should perform subnet mask discovery using ICMP. A value of 0 indicates that the client should not perform mask discovery. A value of 1 means that the client should perform mask discovery. .It Ic option mask-supplier Ar flag ; This option specifies whether or not the client should respond to subnet mask requests using ICMP. A value of 0 indicates that the client should not respond. A value of 1 means that the client should respond. .It Ic option router-discovery Ar flag ; This option specifies whether or not the client should solicit routers using the Router Discovery mechanism defined in RFC 1256. A value of 0 indicates that the client should not perform router discovery. A value of 1 means that the client should perform router discovery. .It Ic option router-solicitation-address Ar ip-address ; This option specifies the address to which the client should transmit router solicitation requests. .It Xo .Ic option static-routes Ar ip-address ip-address .Oo , Ar ip-address ip-address ... Oc ; .Xc This option specifies a list of static routes that the client should install in its routing cache. If multiple routes to the same destination are specified, they are listed in descending order of priority. .Pp The routes consist of a list of IP address pairs. The first address is the destination address, and the second address is the router for the destination. .Pp The default route (0.0.0.0) is an illegal destination for a static route. To specify the default route, use the .Ic routers option. .El .Ss Link Layer Parameters per Interface .Bl -tag -width indent .It Ic option trailer-encapsulation Ar flag ; This option specifies whether or not the client should negotiate the use of trailers (RFC 893 [14]) when using the ARP protocol. A value of 0 indicates that the client should not attempt to use trailers. A value of 1 means that the client should attempt to use trailers. .It Ic option arp-cache-timeout Ar uint32 ; This option specifies the timeout in seconds for ARP cache entries. .It Ic option ieee802-3-encapsulation Ar flag ; This option specifies whether or not the client should use Ethernet Version 2 (RFC 894) or IEEE 802.3 (RFC 1042) encapsulation if the interface is an Ethernet. A value of 0 indicates that the client should use RFC 894 encapsulation. A value of 1 means that the client should use RFC 1042 encapsulation. .El .Ss TCP Parameters .Bl -tag -width indent .It Ic option default-tcp-ttl Ar uint8 ; This option specifies the default TTL that the client should use when sending TCP segments. The minimum value is 1. .It Ic option tcp-keepalive-interval Ar uint32 ; This option specifies the interval (in seconds) that the client TCP should wait before sending a keepalive message on a TCP connection. The time is specified as a 32-bit unsigned integer. A value of zero indicates that the client should not generate keepalive messages on connections unless specifically requested by an application. .It Ic option tcp-keepalive-garbage Ar flag ; This option specifies whether or not the client should send TCP keepalive messages with an octet of garbage for compatibility with older implementations. A value of 0 indicates that a garbage octet should not be sent. A value of 1 indicates that a garbage octet should be sent. .El .Ss Application and Service Parameters .Bl -tag -width indent .It Ic option nis-domain Ar string ; This option specifies the name of the client's NIS (Sun Network Information Services) domain. The domain is formatted as a character string consisting of characters from the .Tn NVT ASCII character set. .It Xo .Ic option nis-servers Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc This option specifies a list of IP addresses indicating NIS servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option ntp-servers Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc This option specifies a list of IP addresses indicating NTP (RFC 1305) servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option netbios-name-servers Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The NetBIOS name server (NBNS) option specifies a list of RFC 1001/1002 NBNS name servers listed in order of preference. NetBIOS Name Service is currently more commonly referred to as WINS. WINS servers can be specified using the .Ic netbios-name-servers option. .It Xo .Ic option netbios-dd-server Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The NetBIOS datagram distribution server (NBDD) option specifies a list of RFC 1001/1002 NBDD servers listed in order of preference. .It Ic option netbios-node-type Ar uint8 ; The NetBIOS node type option allows NetBIOS over TCP/IP clients which are configurable to be configured as described in RFC 1001/1002. The value is specified as a single octet which identifies the client type. .Pp Possible node types are: .Bl -tag -width indent .It 1 B-node: Broadcast - no WINS .It 2 P-node: Peer - WINS only .It 4 M-node: Mixed - broadcast, then WINS .It 8 H-node: Hybrid - WINS, then broadcast .El .It Ic option netbios-scope Ar string ; The NetBIOS scope option specifies the NetBIOS over TCP/IP scope parameter for the client as specified in RFC 1001/1002. See RFC 1001, RFC 1002, and RFC 1035 for character-set restrictions. .It Xo .Ic option font-servers Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc This option specifies a list of X Window System Font servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option x-display-manager Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc This option specifies a list of systems that are running the X Window System Display Manager and are available to the client. Addresses should be listed in order of preference. .It Ic option dhcp-client-identifier Ar data-string ; This option can be used to specify a DHCP client identifier in a host declaration, so that .Xr dhcpd 8 can find the host record by matching against the client identifier. .It Ic option nisplus-domain Ar string ; This option specifies the name of the client's NIS+ domain. The domain is formatted as a character string consisting of characters from the .Tn NVT ASCII character set. .It Xo .Ic option nisplus-servers Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc This option specifies a list of IP addresses indicating NIS+ servers available to the client. Servers should be listed in order of preference. .It Ic option tftp-server-name Ar string ; This option is used to identify a TFTP server and, if supported by the client, should have the same effect as the .Ic server-name declaration. BOOTP clients are unlikely to support this option. Some DHCP clients will support it, and others actually require it. .It Ic option bootfile-name Ar string ; This option is used to identify a bootstrap file. If supported by the client, it should have the same effect as the .Ic filename declaration. BOOTP clients are unlikely to support this option. Some DHCP clients will support it, and others actually require it. .It Xo .Ic option mobile-ip-home-agent Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc This option specifies a list of IP addresses indicating mobile IP home agents available to the client. Agents should be listed in order of preference, although normally there will be only one such agent. .It Xo .Ic option smtp-server Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic smtp-server option specifies a list of SMTP servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option pop-server Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic pop-server option specifies a list of POP3 servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option nntp-server Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic nntp-server option specifies a list of NNTP servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option www-server Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic www-server option specifies a list of WWW servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option finger-server Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic finger-server option specifies a list of .Xr finger 1 servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option irc-server Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic irc-server option specifies a list of IRC servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option streettalk-server Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The .Ic streettalk-server option specifies a list of StreetTalk servers available to the client. Servers should be listed in order of preference. .It Xo .Ic option streettalk-directory-assistance-server Ar ip-address .Oo , Ar ip-address ... Oc ; .Xc The StreetTalk Directory Assistance (STDA) server option specifies a list of STDA servers available to the client. Servers should be listed in order of preference. +.It Ic option url Ar string ; +This option specifies the URL that the client may use when using UEFI +boot from a HTTP server. .El .Sh SEE ALSO .Xr dhclient.conf 5 , .Xr dhcpd.conf 5 , .Xr dhcpd.leases 5 , .Xr dhclient 8 , .Xr dhcpd 8 .Rs -.%R "RFC 2131, RFC 2132" +.%R "RFC 2131, RFC 2132, RFC 3769" .Re .Sh AUTHORS .An -nosplit The .Xr dhcpd 8 utility was written by .An Ted Lemon Aq Mt mellon@vix.com under a contract with Vixie Labs. .Pp The current implementation was reworked by .An Henning Brauer Aq Mt henning@openbsd.org . Index: head/sbin/dhclient/dhcp.h =================================================================== --- head/sbin/dhclient/dhcp.h (revision 355018) +++ head/sbin/dhclient/dhcp.h (revision 355019) @@ -1,186 +1,187 @@ /* $OpenBSD: dhcp.h,v 1.5 2004/05/04 15:49:49 deraadt Exp $ */ /* $FreeBSD$ */ /* Protocol structures... */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995, 1996 The Internet Software Consortium. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 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 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''. */ #define DHCP_UDP_OVERHEAD (14 + /* Ethernet header */ \ 20 + /* IP header */ \ 8) /* UDP header */ #define DHCP_SNAME_LEN 64 #define DHCP_FILE_LEN 128 #define DHCP_FIXED_NON_UDP 236 #define DHCP_FIXED_LEN (DHCP_FIXED_NON_UDP + DHCP_UDP_OVERHEAD) /* Everything but options. */ #define DHCP_MTU_MAX 1500 #define DHCP_OPTION_LEN (DHCP_MTU_MAX - DHCP_FIXED_LEN) #define BOOTP_MIN_LEN 300 #define DHCP_MIN_LEN 548 struct dhcp_packet { u_int8_t op; /* Message opcode/type */ u_int8_t htype; /* Hardware addr type (see net/if_types.h) */ u_int8_t hlen; /* Hardware addr length */ u_int8_t hops; /* Number of relay agent hops from client */ u_int32_t xid; /* Transaction ID */ u_int16_t secs; /* Seconds since client started looking */ u_int16_t flags; /* Flag bits */ struct in_addr ciaddr; /* Client IP address (if already in use) */ struct in_addr yiaddr; /* Client IP address */ struct in_addr siaddr; /* IP address of next server to talk to */ struct in_addr giaddr; /* DHCP relay agent IP address */ unsigned char chaddr[16]; /* Client hardware address */ char sname[DHCP_SNAME_LEN]; /* Server name */ char file[DHCP_FILE_LEN]; /* Boot filename */ unsigned char options[DHCP_OPTION_LEN]; /* Optional parameters (actual length dependent on MTU). */ }; /* BOOTP (rfc951) message types */ #define BOOTREQUEST 1 #define BOOTREPLY 2 /* Possible values for flags field... */ #define BOOTP_BROADCAST 32768L /* Possible values for hardware type (htype) field... */ #define HTYPE_ETHER 1 /* Ethernet */ #define HTYPE_IEEE802 6 /* IEEE 802.2 Token Ring... */ #define HTYPE_FDDI 8 /* FDDI... */ /* Magic cookie validating dhcp options field (and bootp vendor extensions field). */ #define DHCP_OPTIONS_COOKIE "\143\202\123\143" /* DHCP Option codes: */ #define DHO_PAD 0 #define DHO_SUBNET_MASK 1 #define DHO_TIME_OFFSET 2 #define DHO_ROUTERS 3 #define DHO_TIME_SERVERS 4 #define DHO_NAME_SERVERS 5 #define DHO_DOMAIN_NAME_SERVERS 6 #define DHO_LOG_SERVERS 7 #define DHO_COOKIE_SERVERS 8 #define DHO_LPR_SERVERS 9 #define DHO_IMPRESS_SERVERS 10 #define DHO_RESOURCE_LOCATION_SERVERS 11 #define DHO_HOST_NAME 12 #define DHO_BOOT_SIZE 13 #define DHO_MERIT_DUMP 14 #define DHO_DOMAIN_NAME 15 #define DHO_SWAP_SERVER 16 #define DHO_ROOT_PATH 17 #define DHO_EXTENSIONS_PATH 18 #define DHO_IP_FORWARDING 19 #define DHO_NON_LOCAL_SOURCE_ROUTING 20 #define DHO_POLICY_FILTER 21 #define DHO_MAX_DGRAM_REASSEMBLY 22 #define DHO_DEFAULT_IP_TTL 23 #define DHO_PATH_MTU_AGING_TIMEOUT 24 #define DHO_PATH_MTU_PLATEAU_TABLE 25 #define DHO_INTERFACE_MTU 26 #define DHO_ALL_SUBNETS_LOCAL 27 #define DHO_BROADCAST_ADDRESS 28 #define DHO_PERFORM_MASK_DISCOVERY 29 #define DHO_MASK_SUPPLIER 30 #define DHO_ROUTER_DISCOVERY 31 #define DHO_ROUTER_SOLICITATION_ADDRESS 32 #define DHO_STATIC_ROUTES 33 #define DHO_TRAILER_ENCAPSULATION 34 #define DHO_ARP_CACHE_TIMEOUT 35 #define DHO_IEEE802_3_ENCAPSULATION 36 #define DHO_DEFAULT_TCP_TTL 37 #define DHO_TCP_KEEPALIVE_INTERVAL 38 #define DHO_TCP_KEEPALIVE_GARBAGE 39 #define DHO_NIS_DOMAIN 40 #define DHO_NIS_SERVERS 41 #define DHO_NTP_SERVERS 42 #define DHO_VENDOR_ENCAPSULATED_OPTIONS 43 #define DHO_NETBIOS_NAME_SERVERS 44 #define DHO_NETBIOS_DD_SERVER 45 #define DHO_NETBIOS_NODE_TYPE 46 #define DHO_NETBIOS_SCOPE 47 #define DHO_FONT_SERVERS 48 #define DHO_X_DISPLAY_MANAGER 49 #define DHO_DHCP_REQUESTED_ADDRESS 50 #define DHO_DHCP_LEASE_TIME 51 #define DHO_DHCP_OPTION_OVERLOAD 52 #define DHO_DHCP_MESSAGE_TYPE 53 #define DHO_DHCP_SERVER_IDENTIFIER 54 #define DHO_DHCP_PARAMETER_REQUEST_LIST 55 #define DHO_DHCP_MESSAGE 56 #define DHO_DHCP_MAX_MESSAGE_SIZE 57 #define DHO_DHCP_RENEWAL_TIME 58 #define DHO_DHCP_REBINDING_TIME 59 #define DHO_DHCP_CLASS_IDENTIFIER 60 #define DHO_DHCP_CLIENT_IDENTIFIER 61 #define DHO_NISPLUS_DOMAIN 64 #define DHO_NISPLUS_SERVERS 65 #define DHO_TFTP_SERVER_NAME 66 #define DHO_BOOTFILE_NAME 67 #define DHO_MOBILE_IP_HOME_AGENT 68 #define DHO_SMTP_SERVER 69 #define DHO_POP_SERVER 70 #define DHO_NNTP_SERVER 71 #define DHO_WWW_SERVER 72 #define DHO_FINGER_SERVER 73 #define DHO_IRC_SERVER 74 #define DHO_STREETTALK_SERVER 75 #define DHO_STREETTALK_DA_SERVER 76 #define DHO_DHCP_USER_CLASS_ID 77 +#define DHO_URL 114 #define DHO_DOMAIN_SEARCH 119 #define DHO_CLASSLESS_ROUTES 121 #define DHO_END 255 /* DHCP message types. */ #define DHCPDISCOVER 1 #define DHCPOFFER 2 #define DHCPREQUEST 3 #define DHCPDECLINE 4 #define DHCPACK 5 #define DHCPNAK 6 #define DHCPRELEASE 7 #define DHCPINFORM 8 Index: head/sbin/dhclient/tables.c =================================================================== --- head/sbin/dhclient/tables.c (revision 355018) +++ head/sbin/dhclient/tables.c (revision 355019) @@ -1,450 +1,451 @@ /* $OpenBSD: tables.c,v 1.4 2004/05/04 20:28:40 deraadt Exp $ */ /* Tables of information... */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995, 1996 The Internet Software Consortium. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 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 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''. */ #include __FBSDID("$FreeBSD$"); #include "dhcpd.h" /* * DHCP Option names, formats and codes, from RFC1533. * * Format codes: * * e - end of data * I - IP address * l - 32-bit signed integer * L - 32-bit unsigned integer * s - 16-bit signed integer * S - 16-bit unsigned integer * b - 8-bit signed integer * B - 8-bit unsigned integer * t - ASCII text * f - flag (true or false) * A - array of whatever precedes (e.g., IA means array of IP addresses) */ struct universe dhcp_universe; struct option dhcp_options[256] = { { "pad", "", &dhcp_universe, 0 }, { "subnet-mask", "I", &dhcp_universe, 1 }, { "time-offset", "l", &dhcp_universe, 2 }, { "routers", "IA", &dhcp_universe, 3 }, { "time-servers", "IA", &dhcp_universe, 4 }, { "ien116-name-servers", "IA", &dhcp_universe, 5 }, { "domain-name-servers", "IA", &dhcp_universe, 6 }, { "log-servers", "IA", &dhcp_universe, 7 }, { "cookie-servers", "IA", &dhcp_universe, 8 }, { "lpr-servers", "IA", &dhcp_universe, 9 }, { "impress-servers", "IA", &dhcp_universe, 10 }, { "resource-location-servers", "IA", &dhcp_universe, 11 }, { "host-name", "t", &dhcp_universe, 12 }, { "boot-size", "S", &dhcp_universe, 13 }, { "merit-dump", "t", &dhcp_universe, 14 }, { "domain-name", "t", &dhcp_universe, 15 }, { "swap-server", "I", &dhcp_universe, 16 }, { "root-path", "t", &dhcp_universe, 17 }, { "extensions-path", "t", &dhcp_universe, 18 }, { "ip-forwarding", "f", &dhcp_universe, 19 }, { "non-local-source-routing", "f", &dhcp_universe, 20 }, { "policy-filter", "IIA", &dhcp_universe, 21 }, { "max-dgram-reassembly", "S", &dhcp_universe, 22 }, { "default-ip-ttl", "B", &dhcp_universe, 23 }, { "path-mtu-aging-timeout", "L", &dhcp_universe, 24 }, { "path-mtu-plateau-table", "SA", &dhcp_universe, 25 }, { "interface-mtu", "S", &dhcp_universe, 26 }, { "all-subnets-local", "f", &dhcp_universe, 27 }, { "broadcast-address", "I", &dhcp_universe, 28 }, { "perform-mask-discovery", "f", &dhcp_universe, 29 }, { "mask-supplier", "f", &dhcp_universe, 30 }, { "router-discovery", "f", &dhcp_universe, 31 }, { "router-solicitation-address", "I", &dhcp_universe, 32 }, { "static-routes", "IIA", &dhcp_universe, 33 }, { "trailer-encapsulation", "f", &dhcp_universe, 34 }, { "arp-cache-timeout", "L", &dhcp_universe, 35 }, { "ieee802-3-encapsulation", "f", &dhcp_universe, 36 }, { "default-tcp-ttl", "B", &dhcp_universe, 37 }, { "tcp-keepalive-interval", "L", &dhcp_universe, 38 }, { "tcp-keepalive-garbage", "f", &dhcp_universe, 39 }, { "nis-domain", "t", &dhcp_universe, 40 }, { "nis-servers", "IA", &dhcp_universe, 41 }, { "ntp-servers", "IA", &dhcp_universe, 42 }, { "vendor-encapsulated-options", "X", &dhcp_universe, 43 }, { "netbios-name-servers", "IA", &dhcp_universe, 44 }, { "netbios-dd-server", "IA", &dhcp_universe, 45 }, { "netbios-node-type", "B", &dhcp_universe, 46 }, { "netbios-scope", "t", &dhcp_universe, 47 }, { "font-servers", "IA", &dhcp_universe, 48 }, { "x-display-manager", "IA", &dhcp_universe, 49 }, { "dhcp-requested-address", "I", &dhcp_universe, 50 }, { "dhcp-lease-time", "L", &dhcp_universe, 51 }, { "dhcp-option-overload", "B", &dhcp_universe, 52 }, { "dhcp-message-type", "B", &dhcp_universe, 53 }, { "dhcp-server-identifier", "I", &dhcp_universe, 54 }, { "dhcp-parameter-request-list", "BA", &dhcp_universe, 55 }, { "dhcp-message", "t", &dhcp_universe, 56 }, { "dhcp-max-message-size", "S", &dhcp_universe, 57 }, { "dhcp-renewal-time", "L", &dhcp_universe, 58 }, { "dhcp-rebinding-time", "L", &dhcp_universe, 59 }, { "dhcp-class-identifier", "t", &dhcp_universe, 60 }, { "dhcp-client-identifier", "X", &dhcp_universe, 61 }, { "option-62", "X", &dhcp_universe, 62 }, { "option-63", "X", &dhcp_universe, 63 }, { "nisplus-domain", "t", &dhcp_universe, 64 }, { "nisplus-servers", "IA", &dhcp_universe, 65 }, { "tftp-server-name", "t", &dhcp_universe, 66 }, { "bootfile-name", "t", &dhcp_universe, 67 }, { "mobile-ip-home-agent", "IA", &dhcp_universe, 68 }, { "smtp-server", "IA", &dhcp_universe, 69 }, { "pop-server", "IA", &dhcp_universe, 70 }, { "nntp-server", "IA", &dhcp_universe, 71 }, { "www-server", "IA", &dhcp_universe, 72 }, { "finger-server", "IA", &dhcp_universe, 73 }, { "irc-server", "IA", &dhcp_universe, 74 }, { "streettalk-server", "IA", &dhcp_universe, 75 }, { "streettalk-directory-assistance-server", "IA", &dhcp_universe, 76 }, { "user-class", "t", &dhcp_universe, 77 }, { "option-78", "X", &dhcp_universe, 78 }, { "option-79", "X", &dhcp_universe, 79 }, { "option-80", "X", &dhcp_universe, 80 }, { "option-81", "X", &dhcp_universe, 81 }, { "option-82", "X", &dhcp_universe, 82 }, { "option-83", "X", &dhcp_universe, 83 }, { "option-84", "X", &dhcp_universe, 84 }, { "nds-servers", "IA", &dhcp_universe, 85 }, { "nds-tree-name", "X", &dhcp_universe, 86 }, { "nds-context", "X", &dhcp_universe, 87 }, { "option-88", "X", &dhcp_universe, 88 }, { "option-89", "X", &dhcp_universe, 89 }, { "option-90", "X", &dhcp_universe, 90 }, { "option-91", "X", &dhcp_universe, 91 }, { "option-92", "X", &dhcp_universe, 92 }, { "option-93", "X", &dhcp_universe, 93 }, { "option-94", "X", &dhcp_universe, 94 }, { "option-95", "X", &dhcp_universe, 95 }, { "option-96", "X", &dhcp_universe, 96 }, { "option-97", "X", &dhcp_universe, 97 }, { "option-98", "X", &dhcp_universe, 98 }, { "option-99", "X", &dhcp_universe, 99 }, { "option-100", "X", &dhcp_universe, 100 }, { "option-101", "X", &dhcp_universe, 101 }, { "option-102", "X", &dhcp_universe, 102 }, { "option-103", "X", &dhcp_universe, 103 }, { "option-104", "X", &dhcp_universe, 104 }, { "option-105", "X", &dhcp_universe, 105 }, { "option-106", "X", &dhcp_universe, 106 }, { "option-107", "X", &dhcp_universe, 107 }, { "option-108", "X", &dhcp_universe, 108 }, { "option-109", "X", &dhcp_universe, 109 }, { "option-110", "X", &dhcp_universe, 110 }, { "option-111", "X", &dhcp_universe, 111 }, { "option-112", "X", &dhcp_universe, 112 }, { "option-113", "X", &dhcp_universe, 113 }, - { "option-114", "X", &dhcp_universe, 114 }, + { "url", "t", &dhcp_universe, 114 }, { "option-115", "X", &dhcp_universe, 115 }, { "option-116", "X", &dhcp_universe, 116 }, { "option-117", "X", &dhcp_universe, 117 }, { "option-118", "X", &dhcp_universe, 118 }, { "domain-search", "t", &dhcp_universe, 119 }, { "option-120", "X", &dhcp_universe, 120 }, { "classless-routes", "BA", &dhcp_universe, 121 }, { "option-122", "X", &dhcp_universe, 122 }, { "option-123", "X", &dhcp_universe, 123 }, { "option-124", "X", &dhcp_universe, 124 }, { "option-125", "X", &dhcp_universe, 125 }, { "option-126", "X", &dhcp_universe, 126 }, { "option-127", "X", &dhcp_universe, 127 }, { "option-128", "X", &dhcp_universe, 128 }, { "option-129", "X", &dhcp_universe, 129 }, { "option-130", "X", &dhcp_universe, 130 }, { "option-131", "X", &dhcp_universe, 131 }, { "option-132", "X", &dhcp_universe, 132 }, { "option-133", "X", &dhcp_universe, 133 }, { "option-134", "X", &dhcp_universe, 134 }, { "option-135", "X", &dhcp_universe, 135 }, { "option-136", "X", &dhcp_universe, 136 }, { "option-137", "X", &dhcp_universe, 137 }, { "option-138", "X", &dhcp_universe, 138 }, { "option-139", "X", &dhcp_universe, 139 }, { "option-140", "X", &dhcp_universe, 140 }, { "option-141", "X", &dhcp_universe, 141 }, { "option-142", "X", &dhcp_universe, 142 }, { "option-143", "X", &dhcp_universe, 143 }, { "option-144", "X", &dhcp_universe, 144 }, { "option-145", "X", &dhcp_universe, 145 }, { "option-146", "X", &dhcp_universe, 146 }, { "option-147", "X", &dhcp_universe, 147 }, { "option-148", "X", &dhcp_universe, 148 }, { "option-149", "X", &dhcp_universe, 149 }, { "option-150", "X", &dhcp_universe, 150 }, { "option-151", "X", &dhcp_universe, 151 }, { "option-152", "X", &dhcp_universe, 152 }, { "option-153", "X", &dhcp_universe, 153 }, { "option-154", "X", &dhcp_universe, 154 }, { "option-155", "X", &dhcp_universe, 155 }, { "option-156", "X", &dhcp_universe, 156 }, { "option-157", "X", &dhcp_universe, 157 }, { "option-158", "X", &dhcp_universe, 158 }, { "option-159", "X", &dhcp_universe, 159 }, { "option-160", "X", &dhcp_universe, 160 }, { "option-161", "X", &dhcp_universe, 161 }, { "option-162", "X", &dhcp_universe, 162 }, { "option-163", "X", &dhcp_universe, 163 }, { "option-164", "X", &dhcp_universe, 164 }, { "option-165", "X", &dhcp_universe, 165 }, { "option-166", "X", &dhcp_universe, 166 }, { "option-167", "X", &dhcp_universe, 167 }, { "option-168", "X", &dhcp_universe, 168 }, { "option-169", "X", &dhcp_universe, 169 }, { "option-170", "X", &dhcp_universe, 170 }, { "option-171", "X", &dhcp_universe, 171 }, { "option-172", "X", &dhcp_universe, 172 }, { "option-173", "X", &dhcp_universe, 173 }, { "option-174", "X", &dhcp_universe, 174 }, { "option-175", "X", &dhcp_universe, 175 }, { "option-176", "X", &dhcp_universe, 176 }, { "option-177", "X", &dhcp_universe, 177 }, { "option-178", "X", &dhcp_universe, 178 }, { "option-179", "X", &dhcp_universe, 179 }, { "option-180", "X", &dhcp_universe, 180 }, { "option-181", "X", &dhcp_universe, 181 }, { "option-182", "X", &dhcp_universe, 182 }, { "option-183", "X", &dhcp_universe, 183 }, { "option-184", "X", &dhcp_universe, 184 }, { "option-185", "X", &dhcp_universe, 185 }, { "option-186", "X", &dhcp_universe, 186 }, { "option-187", "X", &dhcp_universe, 187 }, { "option-188", "X", &dhcp_universe, 188 }, { "option-189", "X", &dhcp_universe, 189 }, { "option-190", "X", &dhcp_universe, 190 }, { "option-191", "X", &dhcp_universe, 191 }, { "option-192", "X", &dhcp_universe, 192 }, { "option-193", "X", &dhcp_universe, 193 }, { "option-194", "X", &dhcp_universe, 194 }, { "option-195", "X", &dhcp_universe, 195 }, { "option-196", "X", &dhcp_universe, 196 }, { "option-197", "X", &dhcp_universe, 197 }, { "option-198", "X", &dhcp_universe, 198 }, { "option-199", "X", &dhcp_universe, 199 }, { "option-200", "X", &dhcp_universe, 200 }, { "option-201", "X", &dhcp_universe, 201 }, { "option-202", "X", &dhcp_universe, 202 }, { "option-203", "X", &dhcp_universe, 203 }, { "option-204", "X", &dhcp_universe, 204 }, { "option-205", "X", &dhcp_universe, 205 }, { "option-206", "X", &dhcp_universe, 206 }, { "option-207", "X", &dhcp_universe, 207 }, { "option-208", "X", &dhcp_universe, 208 }, { "option-209", "X", &dhcp_universe, 209 }, { "option-210", "X", &dhcp_universe, 210 }, { "option-211", "X", &dhcp_universe, 211 }, { "option-212", "X", &dhcp_universe, 212 }, { "option-213", "X", &dhcp_universe, 213 }, { "option-214", "X", &dhcp_universe, 214 }, { "option-215", "X", &dhcp_universe, 215 }, { "option-216", "X", &dhcp_universe, 216 }, { "option-217", "X", &dhcp_universe, 217 }, { "option-218", "X", &dhcp_universe, 218 }, { "option-219", "X", &dhcp_universe, 219 }, { "option-220", "X", &dhcp_universe, 220 }, { "option-221", "X", &dhcp_universe, 221 }, { "option-222", "X", &dhcp_universe, 222 }, { "option-223", "X", &dhcp_universe, 223 }, { "option-224", "X", &dhcp_universe, 224 }, { "option-225", "X", &dhcp_universe, 225 }, { "option-226", "X", &dhcp_universe, 226 }, { "option-227", "X", &dhcp_universe, 227 }, { "option-228", "X", &dhcp_universe, 228 }, { "option-229", "X", &dhcp_universe, 229 }, { "option-230", "X", &dhcp_universe, 230 }, { "option-231", "X", &dhcp_universe, 231 }, { "option-232", "X", &dhcp_universe, 232 }, { "option-233", "X", &dhcp_universe, 233 }, { "option-234", "X", &dhcp_universe, 234 }, { "option-235", "X", &dhcp_universe, 235 }, { "option-236", "X", &dhcp_universe, 236 }, { "option-237", "X", &dhcp_universe, 237 }, { "option-238", "X", &dhcp_universe, 238 }, { "option-239", "X", &dhcp_universe, 239 }, { "option-240", "X", &dhcp_universe, 240 }, { "option-241", "X", &dhcp_universe, 241 }, { "option-242", "X", &dhcp_universe, 242 }, { "option-243", "X", &dhcp_universe, 243 }, { "option-244", "X", &dhcp_universe, 244 }, { "option-245", "X", &dhcp_universe, 245 }, { "option-246", "X", &dhcp_universe, 246 }, { "option-247", "X", &dhcp_universe, 247 }, { "option-248", "X", &dhcp_universe, 248 }, { "option-249", "X", &dhcp_universe, 249 }, { "option-250", "X", &dhcp_universe, 250 }, { "option-251", "X", &dhcp_universe, 251 }, { "option-252", "X", &dhcp_universe, 252 }, { "option-253", "X", &dhcp_universe, 253 }, { "option-254", "X", &dhcp_universe, 254 }, { "option-end", "e", &dhcp_universe, 255 }, }; /* * Default dhcp option priority list (this is ad hoc and should not be * mistaken for a carefully crafted and optimized list). */ unsigned char dhcp_option_default_priority_list[] = { DHO_DHCP_REQUESTED_ADDRESS, DHO_DHCP_OPTION_OVERLOAD, DHO_DHCP_MAX_MESSAGE_SIZE, DHO_DHCP_RENEWAL_TIME, DHO_DHCP_REBINDING_TIME, DHO_DHCP_CLASS_IDENTIFIER, DHO_DHCP_CLIENT_IDENTIFIER, DHO_SUBNET_MASK, DHO_TIME_OFFSET, DHO_CLASSLESS_ROUTES, DHO_ROUTERS, DHO_TIME_SERVERS, DHO_NAME_SERVERS, DHO_DOMAIN_NAME_SERVERS, DHO_HOST_NAME, DHO_LOG_SERVERS, DHO_COOKIE_SERVERS, DHO_LPR_SERVERS, DHO_IMPRESS_SERVERS, DHO_RESOURCE_LOCATION_SERVERS, DHO_HOST_NAME, DHO_BOOT_SIZE, DHO_MERIT_DUMP, DHO_DOMAIN_NAME, DHO_SWAP_SERVER, DHO_ROOT_PATH, DHO_EXTENSIONS_PATH, DHO_IP_FORWARDING, DHO_NON_LOCAL_SOURCE_ROUTING, DHO_POLICY_FILTER, DHO_MAX_DGRAM_REASSEMBLY, DHO_DEFAULT_IP_TTL, DHO_PATH_MTU_AGING_TIMEOUT, DHO_PATH_MTU_PLATEAU_TABLE, DHO_INTERFACE_MTU, DHO_ALL_SUBNETS_LOCAL, DHO_BROADCAST_ADDRESS, DHO_PERFORM_MASK_DISCOVERY, DHO_MASK_SUPPLIER, DHO_ROUTER_DISCOVERY, DHO_ROUTER_SOLICITATION_ADDRESS, DHO_STATIC_ROUTES, DHO_TRAILER_ENCAPSULATION, DHO_ARP_CACHE_TIMEOUT, DHO_IEEE802_3_ENCAPSULATION, DHO_DEFAULT_TCP_TTL, DHO_TCP_KEEPALIVE_INTERVAL, DHO_TCP_KEEPALIVE_GARBAGE, DHO_NIS_DOMAIN, DHO_NIS_SERVERS, DHO_NTP_SERVERS, DHO_VENDOR_ENCAPSULATED_OPTIONS, DHO_NETBIOS_NAME_SERVERS, DHO_NETBIOS_DD_SERVER, DHO_NETBIOS_NODE_TYPE, DHO_NETBIOS_SCOPE, DHO_FONT_SERVERS, DHO_X_DISPLAY_MANAGER, DHO_DHCP_PARAMETER_REQUEST_LIST, DHO_NISPLUS_DOMAIN, DHO_NISPLUS_SERVERS, DHO_TFTP_SERVER_NAME, DHO_BOOTFILE_NAME, DHO_MOBILE_IP_HOME_AGENT, DHO_SMTP_SERVER, DHO_POP_SERVER, DHO_NNTP_SERVER, DHO_WWW_SERVER, DHO_FINGER_SERVER, DHO_IRC_SERVER, DHO_STREETTALK_SERVER, DHO_STREETTALK_DA_SERVER, DHO_DHCP_USER_CLASS_ID, DHO_DOMAIN_SEARCH, + DHO_URL, /* Presently-undefined options... */ 62, 63, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, - 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 106, 107, 108, 109, 110, 111, 112, 113, 115, 116, 117, 118, 120, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, }; int sizeof_dhcp_option_default_priority_list = sizeof(dhcp_option_default_priority_list); struct hash_table universe_hash; void initialize_universes(void) { int i; dhcp_universe.name = "dhcp"; dhcp_universe.hash = new_hash(); if (!dhcp_universe.hash) error("Can't allocate dhcp option hash table."); for (i = 0; i < 256; i++) { dhcp_universe.options[i] = &dhcp_options[i]; add_hash(dhcp_universe.hash, (const unsigned char *)dhcp_options[i].name, 0, (unsigned char *)&dhcp_options[i]); } universe_hash.hash_count = DEFAULT_HASH_SIZE; add_hash(&universe_hash, (const unsigned char *)dhcp_universe.name, 0, (unsigned char *)&dhcp_universe); }