1147072Sbrooks/* $OpenBSD: dhclient.c,v 1.63 2005/02/06 17:10:13 krw Exp $ */ 2147072Sbrooks 3147072Sbrooks/* 4147072Sbrooks * Copyright 2004 Henning Brauer <henning@openbsd.org> 5147072Sbrooks * Copyright (c) 1995, 1996, 1997, 1998, 1999 6147072Sbrooks * The Internet Software Consortium. All rights reserved. 7147072Sbrooks * 8147072Sbrooks * Redistribution and use in source and binary forms, with or without 9147072Sbrooks * modification, are permitted provided that the following conditions 10147072Sbrooks * are met: 11147072Sbrooks * 12147072Sbrooks * 1. Redistributions of source code must retain the above copyright 13147072Sbrooks * notice, this list of conditions and the following disclaimer. 14147072Sbrooks * 2. Redistributions in binary form must reproduce the above copyright 15147072Sbrooks * notice, this list of conditions and the following disclaimer in the 16147072Sbrooks * documentation and/or other materials provided with the distribution. 17147072Sbrooks * 3. Neither the name of The Internet Software Consortium nor the names 18147072Sbrooks * of its contributors may be used to endorse or promote products derived 19147072Sbrooks * from this software without specific prior written permission. 20147072Sbrooks * 21147072Sbrooks * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 22147072Sbrooks * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 23147072Sbrooks * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 24147072Sbrooks * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25147072Sbrooks * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 26147072Sbrooks * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27147072Sbrooks * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28147072Sbrooks * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 29147072Sbrooks * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 30147072Sbrooks * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31147072Sbrooks * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32147072Sbrooks * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33147072Sbrooks * SUCH DAMAGE. 34147072Sbrooks * 35147072Sbrooks * This software has been written for the Internet Software Consortium 36147072Sbrooks * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 37147072Sbrooks * Enterprises. To learn more about the Internet Software Consortium, 38147072Sbrooks * see ``http://www.vix.com/isc''. To learn more about Vixie 39147072Sbrooks * Enterprises, see ``http://www.vix.com''. 40147072Sbrooks * 41147072Sbrooks * This client was substantially modified and enhanced by Elliot Poger 42147072Sbrooks * for use on Linux while he was working on the MosquitoNet project at 43147072Sbrooks * Stanford. 44147072Sbrooks * 45147072Sbrooks * The current version owes much to Elliot's Linux enhancements, but 46147072Sbrooks * was substantially reorganized and partially rewritten by Ted Lemon 47147072Sbrooks * so as to use the same networking framework that the Internet Software 48147072Sbrooks * Consortium DHCP server uses. Much system-specific configuration code 49147072Sbrooks * was moved into a shell script so that as support for more operating 50147072Sbrooks * systems is added, it will not be necessary to port and maintain 51147072Sbrooks * system-specific configuration code to these operating systems - instead, 52147072Sbrooks * the shell script can invoke the native tools to accomplish the same 53147072Sbrooks * purpose. 54147072Sbrooks */ 55147072Sbrooks 56149399Sbrooks#include <sys/cdefs.h> 57149399Sbrooks__FBSDID("$FreeBSD$"); 58149399Sbrooks 59147072Sbrooks#include "dhcpd.h" 60147072Sbrooks#include "privsep.h" 61147072Sbrooks 62147085Sbrooks#include <net80211/ieee80211_freebsd.h> 63147085Sbrooks 64147085Sbrooks#ifndef _PATH_VAREMPTY 65147085Sbrooks#define _PATH_VAREMPTY "/var/empty" 66147085Sbrooks#endif 67147085Sbrooks 68147072Sbrooks#define PERIOD 0x2e 69147072Sbrooks#define hyphenchar(c) ((c) == 0x2d) 70147072Sbrooks#define bslashchar(c) ((c) == 0x5c) 71147072Sbrooks#define periodchar(c) ((c) == PERIOD) 72147072Sbrooks#define asterchar(c) ((c) == 0x2a) 73147072Sbrooks#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) || \ 74147072Sbrooks ((c) >= 0x61 && (c) <= 0x7a)) 75147072Sbrooks#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) 76149639Sbrooks#define whitechar(c) ((c) == ' ' || (c) == '\t') 77147072Sbrooks 78147072Sbrooks#define borderchar(c) (alphachar(c) || digitchar(c)) 79147072Sbrooks#define middlechar(c) (borderchar(c) || hyphenchar(c)) 80147072Sbrooks#define domainchar(c) ((c) > 0x20 && (c) < 0x7f) 81147072Sbrooks 82147072Sbrooks#define CLIENT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin" 83147072Sbrooks 84147072Sbrookstime_t cur_time; 85147072Sbrookstime_t default_lease_time = 43200; /* 12 hours... */ 86147072Sbrooks 87147072Sbrookschar *path_dhclient_conf = _PATH_DHCLIENT_CONF; 88147072Sbrookschar *path_dhclient_db = NULL; 89147072Sbrooks 90147072Sbrooksint log_perror = 1; 91147072Sbrooksint privfd; 92147072Sbrooksint nullfd = -1; 93147072Sbrooks 94147072Sbrooksstruct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } }; 95147072Sbrooksstruct in_addr inaddr_any; 96147072Sbrooksstruct sockaddr_in sockaddr_broadcast; 97147072Sbrooks 98231277Sbaptchar *path_dhclient_pidfile; 99231277Sbaptstruct pidfh *pidfile; 100231277Sbapt 101147072Sbrooks/* 102147072Sbrooks * ASSERT_STATE() does nothing now; it used to be 103147072Sbrooks * assert (state_is == state_shouldbe). 104147072Sbrooks */ 105147072Sbrooks#define ASSERT_STATE(state_is, state_shouldbe) {} 106147072Sbrooks 107147072Sbrooks#define TIME_MAX 2147483647 108147072Sbrooks 109147072Sbrooksint log_priority; 110147072Sbrooksint no_daemon; 111147072Sbrooksint unknown_ok = 1; 112147072Sbrooksint routefd; 113147072Sbrooks 114147072Sbrooksstruct interface_info *ifi; 115147072Sbrooks 116147072Sbrooksint findproto(char *, int); 117147072Sbrooksstruct sockaddr *get_ifa(char *, int); 118147072Sbrooksvoid routehandler(struct protocol *); 119147072Sbrooksvoid usage(void); 120147072Sbrooksint check_option(struct client_lease *l, int option); 121166602Semasteint check_classless_option(unsigned char *data, int len); 122147072Sbrooksint ipv4addrs(char * buf); 123147072Sbrooksint res_hnok(const char *dn); 124149639Sbrooksint check_search(const char *srch); 125147072Sbrookschar *option_as_string(unsigned int code, unsigned char *data, int len); 126147072Sbrooksint fork_privchld(int, int); 127147072Sbrooks 128147072Sbrooks#define ROUNDUP(a) \ 129147072Sbrooks ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 130147072Sbrooks#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 131147072Sbrooks 132209756Sbrianstatic time_t scripttime; 133147072Sbrooks 134147072Sbrooksint 135147072Sbrooksfindproto(char *cp, int n) 136147072Sbrooks{ 137147072Sbrooks struct sockaddr *sa; 138147072Sbrooks int i; 139147072Sbrooks 140147072Sbrooks if (n == 0) 141147072Sbrooks return -1; 142147072Sbrooks for (i = 1; i; i <<= 1) { 143147072Sbrooks if (i & n) { 144147072Sbrooks sa = (struct sockaddr *)cp; 145147072Sbrooks switch (i) { 146147072Sbrooks case RTA_IFA: 147147072Sbrooks case RTA_DST: 148147072Sbrooks case RTA_GATEWAY: 149147072Sbrooks case RTA_NETMASK: 150147072Sbrooks if (sa->sa_family == AF_INET) 151147072Sbrooks return AF_INET; 152147072Sbrooks if (sa->sa_family == AF_INET6) 153147072Sbrooks return AF_INET6; 154147072Sbrooks break; 155147072Sbrooks case RTA_IFP: 156147072Sbrooks break; 157147072Sbrooks } 158147072Sbrooks ADVANCE(cp, sa); 159147072Sbrooks } 160147072Sbrooks } 161147072Sbrooks return (-1); 162147072Sbrooks} 163147072Sbrooks 164147072Sbrooksstruct sockaddr * 165147072Sbrooksget_ifa(char *cp, int n) 166147072Sbrooks{ 167147072Sbrooks struct sockaddr *sa; 168147072Sbrooks int i; 169147072Sbrooks 170147072Sbrooks if (n == 0) 171147072Sbrooks return (NULL); 172147072Sbrooks for (i = 1; i; i <<= 1) 173147072Sbrooks if (i & n) { 174147072Sbrooks sa = (struct sockaddr *)cp; 175147072Sbrooks if (i == RTA_IFA) 176147072Sbrooks return (sa); 177147072Sbrooks ADVANCE(cp, sa); 178147072Sbrooks } 179147072Sbrooks 180147072Sbrooks return (NULL); 181147072Sbrooks} 182177501Ssam 183147072Sbrooksstruct iaddr defaddr = { 4 }; 184177501Ssamuint8_t curbssid[6]; 185147072Sbrooks 186177501Ssamstatic void 187177501Ssamdisassoc(void *arg) 188177501Ssam{ 189177501Ssam struct interface_info *ifi = arg; 190177501Ssam 191177501Ssam /* 192177501Ssam * Clear existing state. 193177501Ssam */ 194177501Ssam if (ifi->client->active != NULL) { 195177501Ssam script_init("EXPIRE", NULL); 196177501Ssam script_write_params("old_", 197177501Ssam ifi->client->active); 198177501Ssam if (ifi->client->alias) 199177501Ssam script_write_params("alias_", 200177501Ssam ifi->client->alias); 201177501Ssam script_go(); 202177501Ssam } 203177501Ssam ifi->client->state = S_INIT; 204177501Ssam} 205177501Ssam 206147072Sbrooks/* ARGSUSED */ 207147072Sbrooksvoid 208147072Sbrooksroutehandler(struct protocol *p) 209147072Sbrooks{ 210209756Sbrian char msg[2048], *addr; 211147072Sbrooks struct rt_msghdr *rtm; 212147072Sbrooks struct if_msghdr *ifm; 213147072Sbrooks struct ifa_msghdr *ifam; 214147072Sbrooks struct if_announcemsghdr *ifan; 215177501Ssam struct ieee80211_join_event *jev; 216147072Sbrooks struct client_lease *l; 217147072Sbrooks time_t t = time(NULL); 218147072Sbrooks struct sockaddr *sa; 219147072Sbrooks struct iaddr a; 220147072Sbrooks ssize_t n; 221247335Sjhb int linkstat; 222147072Sbrooks 223147072Sbrooks n = read(routefd, &msg, sizeof(msg)); 224147072Sbrooks rtm = (struct rt_msghdr *)msg; 225147072Sbrooks if (n < sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen || 226147072Sbrooks rtm->rtm_version != RTM_VERSION) 227147072Sbrooks return; 228147072Sbrooks 229147072Sbrooks switch (rtm->rtm_type) { 230147072Sbrooks case RTM_NEWADDR: 231154161Sbrooks case RTM_DELADDR: 232147072Sbrooks ifam = (struct ifa_msghdr *)rtm; 233154161Sbrooks 234147072Sbrooks if (ifam->ifam_index != ifi->index) 235147072Sbrooks break; 236147072Sbrooks if (findproto((char *)(ifam + 1), ifam->ifam_addrs) != AF_INET) 237147072Sbrooks break; 238154161Sbrooks if (scripttime == 0 || t < scripttime + 10) 239154161Sbrooks break; 240154161Sbrooks 241147072Sbrooks sa = get_ifa((char *)(ifam + 1), ifam->ifam_addrs); 242147072Sbrooks if (sa == NULL) 243209756Sbrian break; 244147072Sbrooks 245147072Sbrooks if ((a.len = sizeof(struct in_addr)) > sizeof(a.iabuf)) 246147072Sbrooks error("king bula sez: len mismatch"); 247147072Sbrooks memcpy(a.iabuf, &((struct sockaddr_in *)sa)->sin_addr, a.len); 248147072Sbrooks if (addr_eq(a, defaddr)) 249147072Sbrooks break; 250147072Sbrooks 251147072Sbrooks for (l = ifi->client->active; l != NULL; l = l->next) 252147072Sbrooks if (addr_eq(a, l->address)) 253147072Sbrooks break; 254147072Sbrooks 255209756Sbrian if (l == NULL) /* added/deleted addr is not the one we set */ 256147072Sbrooks break; 257209756Sbrian 258209756Sbrian addr = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr); 259209756Sbrian if (rtm->rtm_type == RTM_NEWADDR) { 260209756Sbrian /* 261209756Sbrian * XXX: If someone other than us adds our address, 262209756Sbrian * should we assume they are taking over from us, 263209756Sbrian * delete the lease record, and exit without modifying 264209756Sbrian * the interface? 265209756Sbrian */ 266209756Sbrian warning("My address (%s) was re-added", addr); 267209756Sbrian } else { 268209756Sbrian warning("My address (%s) was deleted, dhclient exiting", 269209756Sbrian addr); 270209756Sbrian goto die; 271209756Sbrian } 272209756Sbrian break; 273147072Sbrooks case RTM_IFINFO: 274147072Sbrooks ifm = (struct if_msghdr *)rtm; 275147072Sbrooks if (ifm->ifm_index != ifi->index) 276147072Sbrooks break; 277209756Sbrian if ((rtm->rtm_flags & RTF_UP) == 0) { 278209756Sbrian warning("Interface %s is down, dhclient exiting", 279209756Sbrian ifi->name); 280147072Sbrooks goto die; 281209756Sbrian } 282247335Sjhb linkstat = interface_link_status(ifi->name); 283247335Sjhb if (linkstat != ifi->linkstat) { 284247335Sjhb debug("%s link state %s -> %s", ifi->name, 285247335Sjhb ifi->linkstat ? "up" : "down", 286247335Sjhb linkstat ? "up" : "down"); 287247335Sjhb ifi->linkstat = linkstat; 288247335Sjhb if (linkstat) 289247335Sjhb state_reboot(ifi); 290247335Sjhb } 291147072Sbrooks break; 292147072Sbrooks case RTM_IFANNOUNCE: 293147072Sbrooks ifan = (struct if_announcemsghdr *)rtm; 294147072Sbrooks if (ifan->ifan_what == IFAN_DEPARTURE && 295209756Sbrian ifan->ifan_index == ifi->index) { 296209756Sbrian warning("Interface %s is gone, dhclient exiting", 297209756Sbrian ifi->name); 298147072Sbrooks goto die; 299209756Sbrian } 300147072Sbrooks break; 301147085Sbrooks case RTM_IEEE80211: 302147085Sbrooks ifan = (struct if_announcemsghdr *)rtm; 303147085Sbrooks if (ifan->ifan_index != ifi->index) 304147085Sbrooks break; 305147085Sbrooks switch (ifan->ifan_what) { 306147085Sbrooks case RTM_IEEE80211_ASSOC: 307148373Ssam case RTM_IEEE80211_REASSOC: 308147085Sbrooks /* 309177501Ssam * Use assoc/reassoc event to kick state machine 310177501Ssam * in case we roam. Otherwise fall back to the 311177501Ssam * normal state machine just like a wired network. 312147085Sbrooks */ 313177501Ssam jev = (struct ieee80211_join_event *) &ifan[1]; 314177501Ssam if (memcmp(curbssid, jev->iev_addr, 6)) { 315177501Ssam disassoc(ifi); 316177501Ssam state_reboot(ifi); 317147351Sbrooks } 318177501Ssam memcpy(curbssid, jev->iev_addr, 6); 319147085Sbrooks break; 320147085Sbrooks } 321147085Sbrooks break; 322147072Sbrooks default: 323147072Sbrooks break; 324147072Sbrooks } 325147072Sbrooks return; 326147072Sbrooks 327147072Sbrooksdie: 328147072Sbrooks script_init("FAIL", NULL); 329147072Sbrooks if (ifi->client->alias) 330147072Sbrooks script_write_params("alias_", ifi->client->alias); 331147072Sbrooks script_go(); 332231277Sbapt if (pidfile != NULL) 333231277Sbapt pidfile_remove(pidfile); 334147072Sbrooks exit(1); 335147072Sbrooks} 336147072Sbrooks 337147072Sbrooksint 338147072Sbrooksmain(int argc, char *argv[]) 339147072Sbrooks{ 340147072Sbrooks extern char *__progname; 341147072Sbrooks int ch, fd, quiet = 0, i = 0; 342147072Sbrooks int pipe_fd[2]; 343147085Sbrooks int immediate_daemon = 0; 344147072Sbrooks struct passwd *pw; 345231277Sbapt pid_t otherpid; 346147072Sbrooks 347147072Sbrooks /* Initially, log errors to stderr as well as to syslogd. */ 348147072Sbrooks openlog(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY); 349177500Ssam setlogmask(LOG_UPTO(LOG_DEBUG)); 350147072Sbrooks 351231277Sbapt while ((ch = getopt(argc, argv, "bc:dl:p:qu")) != -1) 352147072Sbrooks switch (ch) { 353147085Sbrooks case 'b': 354147085Sbrooks immediate_daemon = 1; 355147085Sbrooks break; 356147072Sbrooks case 'c': 357147072Sbrooks path_dhclient_conf = optarg; 358147072Sbrooks break; 359147072Sbrooks case 'd': 360147072Sbrooks no_daemon = 1; 361147072Sbrooks break; 362147072Sbrooks case 'l': 363147072Sbrooks path_dhclient_db = optarg; 364147072Sbrooks break; 365231277Sbapt case 'p': 366231277Sbapt path_dhclient_pidfile = optarg; 367231277Sbapt break; 368147072Sbrooks case 'q': 369147072Sbrooks quiet = 1; 370147072Sbrooks break; 371147072Sbrooks case 'u': 372147072Sbrooks unknown_ok = 0; 373147072Sbrooks break; 374147072Sbrooks default: 375147072Sbrooks usage(); 376147072Sbrooks } 377147072Sbrooks 378147072Sbrooks argc -= optind; 379147072Sbrooks argv += optind; 380147072Sbrooks 381147072Sbrooks if (argc != 1) 382147072Sbrooks usage(); 383147072Sbrooks 384231277Sbapt if (path_dhclient_pidfile == NULL) { 385231277Sbapt asprintf(&path_dhclient_pidfile, 386231277Sbapt "%sdhclient.%s.pid", _PATH_VARRUN, *argv); 387231277Sbapt if (path_dhclient_pidfile == NULL) 388231277Sbapt error("asprintf"); 389231277Sbapt } 390231277Sbapt pidfile = pidfile_open(path_dhclient_pidfile, 0600, &otherpid); 391231277Sbapt if (pidfile == NULL) { 392231277Sbapt if (errno == EEXIST) 393231277Sbapt error("dhclient already running, pid: %d.", otherpid); 394231277Sbapt if (errno == EAGAIN) 395231277Sbapt error("dhclient already running."); 396231277Sbapt warning("Cannot open or create pidfile: %m"); 397231277Sbapt } 398231277Sbapt 399147072Sbrooks if ((ifi = calloc(1, sizeof(struct interface_info))) == NULL) 400147072Sbrooks error("calloc"); 401147072Sbrooks if (strlcpy(ifi->name, argv[0], IFNAMSIZ) >= IFNAMSIZ) 402147072Sbrooks error("Interface name too long"); 403147072Sbrooks if (path_dhclient_db == NULL && asprintf(&path_dhclient_db, "%s.%s", 404147072Sbrooks _PATH_DHCLIENT_DB, ifi->name) == -1) 405147072Sbrooks error("asprintf"); 406147072Sbrooks 407147072Sbrooks if (quiet) 408147072Sbrooks log_perror = 0; 409147072Sbrooks 410147072Sbrooks tzset(); 411147072Sbrooks time(&cur_time); 412147072Sbrooks 413147072Sbrooks memset(&sockaddr_broadcast, 0, sizeof(sockaddr_broadcast)); 414147072Sbrooks sockaddr_broadcast.sin_family = AF_INET; 415147072Sbrooks sockaddr_broadcast.sin_port = htons(REMOTE_PORT); 416147072Sbrooks sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST; 417147072Sbrooks sockaddr_broadcast.sin_len = sizeof(sockaddr_broadcast); 418147072Sbrooks inaddr_any.s_addr = INADDR_ANY; 419147072Sbrooks 420147072Sbrooks read_client_conf(); 421147072Sbrooks 422231277Sbapt /* The next bit is potentially very time-consuming, so write out 423231277Sbapt the pidfile right away. We will write it out again with the 424231277Sbapt correct pid after daemonizing. */ 425231277Sbapt if (pidfile != NULL) 426231277Sbapt pidfile_write(pidfile); 427231277Sbapt 428147072Sbrooks if (!interface_link_status(ifi->name)) { 429147072Sbrooks fprintf(stderr, "%s: no link ...", ifi->name); 430147072Sbrooks fflush(stderr); 431147072Sbrooks sleep(1); 432147072Sbrooks while (!interface_link_status(ifi->name)) { 433147072Sbrooks fprintf(stderr, "."); 434147072Sbrooks fflush(stderr); 435147072Sbrooks if (++i > 10) { 436161514Sbrian fprintf(stderr, " giving up\n"); 437161514Sbrian exit(1); 438147072Sbrooks } 439147072Sbrooks sleep(1); 440147072Sbrooks } 441161514Sbrian fprintf(stderr, " got link\n"); 442147072Sbrooks } 443247335Sjhb ifi->linkstat = 1; 444147072Sbrooks 445147072Sbrooks if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) 446147072Sbrooks error("cannot open %s: %m", _PATH_DEVNULL); 447147072Sbrooks 448147072Sbrooks if ((pw = getpwnam("_dhcp")) == NULL) { 449147072Sbrooks warning("no such user: _dhcp, falling back to \"nobody\""); 450147072Sbrooks if ((pw = getpwnam("nobody")) == NULL) 451147072Sbrooks error("no such user: nobody"); 452147072Sbrooks } 453147072Sbrooks 454147072Sbrooks if (pipe(pipe_fd) == -1) 455147072Sbrooks error("pipe"); 456147072Sbrooks 457147072Sbrooks fork_privchld(pipe_fd[0], pipe_fd[1]); 458147072Sbrooks 459147072Sbrooks close(pipe_fd[0]); 460147072Sbrooks privfd = pipe_fd[1]; 461147072Sbrooks 462147072Sbrooks if ((fd = open(path_dhclient_db, O_RDONLY|O_EXLOCK|O_CREAT, 0)) == -1) 463147072Sbrooks error("can't open and lock %s: %m", path_dhclient_db); 464147072Sbrooks read_client_leases(); 465147072Sbrooks rewrite_client_leases(); 466147072Sbrooks close(fd); 467147072Sbrooks 468147072Sbrooks priv_script_init("PREINIT", NULL); 469147072Sbrooks if (ifi->client->alias) 470147072Sbrooks priv_script_write_params("alias_", ifi->client->alias); 471147072Sbrooks priv_script_go(); 472147072Sbrooks 473147072Sbrooks if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) != -1) 474147072Sbrooks add_protocol("AF_ROUTE", routefd, routehandler, ifi); 475147072Sbrooks 476147072Sbrooks /* set up the interface */ 477147072Sbrooks discover_interfaces(ifi); 478147072Sbrooks 479147072Sbrooks if (chroot(_PATH_VAREMPTY) == -1) 480147072Sbrooks error("chroot"); 481147072Sbrooks if (chdir("/") == -1) 482147072Sbrooks error("chdir(\"/\")"); 483147072Sbrooks 484147072Sbrooks if (setgroups(1, &pw->pw_gid) || 485147072Sbrooks setegid(pw->pw_gid) || setgid(pw->pw_gid) || 486147072Sbrooks seteuid(pw->pw_uid) || setuid(pw->pw_uid)) 487147072Sbrooks error("can't drop privileges: %m"); 488147072Sbrooks 489147072Sbrooks endpwent(); 490147072Sbrooks 491147072Sbrooks setproctitle("%s", ifi->name); 492147072Sbrooks 493147085Sbrooks if (immediate_daemon) 494147085Sbrooks go_daemon(); 495147085Sbrooks 496147072Sbrooks ifi->client->state = S_INIT; 497147072Sbrooks state_reboot(ifi); 498147072Sbrooks 499147072Sbrooks bootp_packet_handler = do_packet; 500147072Sbrooks 501147072Sbrooks dispatch(); 502147072Sbrooks 503147072Sbrooks /* not reached */ 504147072Sbrooks return (0); 505147072Sbrooks} 506147072Sbrooks 507147072Sbrooksvoid 508147072Sbrooksusage(void) 509147072Sbrooks{ 510147072Sbrooks extern char *__progname; 511147072Sbrooks 512161514Sbrian fprintf(stderr, "usage: %s [-bdqu] ", __progname); 513147072Sbrooks fprintf(stderr, "[-c conffile] [-l leasefile] interface\n"); 514147072Sbrooks exit(1); 515147072Sbrooks} 516147072Sbrooks 517147072Sbrooks/* 518147072Sbrooks * Individual States: 519147072Sbrooks * 520147072Sbrooks * Each routine is called from the dhclient_state_machine() in one of 521147072Sbrooks * these conditions: 522147072Sbrooks * -> entering INIT state 523147072Sbrooks * -> recvpacket_flag == 0: timeout in this state 524147072Sbrooks * -> otherwise: received a packet in this state 525147072Sbrooks * 526147072Sbrooks * Return conditions as handled by dhclient_state_machine(): 527147072Sbrooks * Returns 1, sendpacket_flag = 1: send packet, reset timer. 528147072Sbrooks * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone). 529147072Sbrooks * Returns 0: finish the nap which was interrupted for no good reason. 530147072Sbrooks * 531147072Sbrooks * Several per-interface variables are used to keep track of the process: 532147072Sbrooks * active_lease: the lease that is being used on the interface 533147072Sbrooks * (null pointer if not configured yet). 534147072Sbrooks * offered_leases: leases corresponding to DHCPOFFER messages that have 535147072Sbrooks * been sent to us by DHCP servers. 536147072Sbrooks * acked_leases: leases corresponding to DHCPACK messages that have been 537147072Sbrooks * sent to us by DHCP servers. 538147072Sbrooks * sendpacket: DHCP packet we're trying to send. 539147072Sbrooks * destination: IP address to send sendpacket to 540147072Sbrooks * In addition, there are several relevant per-lease variables. 541147072Sbrooks * T1_expiry, T2_expiry, lease_expiry: lease milestones 542147072Sbrooks * In the active lease, these control the process of renewing the lease; 543147072Sbrooks * In leases on the acked_leases list, this simply determines when we 544147072Sbrooks * can no longer legitimately use the lease. 545147072Sbrooks */ 546147072Sbrooks 547147072Sbrooksvoid 548147072Sbrooksstate_reboot(void *ipp) 549147072Sbrooks{ 550147072Sbrooks struct interface_info *ip = ipp; 551147072Sbrooks 552147072Sbrooks /* If we don't remember an active lease, go straight to INIT. */ 553147072Sbrooks if (!ip->client->active || ip->client->active->is_bootp) { 554147072Sbrooks state_init(ip); 555147072Sbrooks return; 556147072Sbrooks } 557147072Sbrooks 558147072Sbrooks /* We are in the rebooting state. */ 559147072Sbrooks ip->client->state = S_REBOOTING; 560147072Sbrooks 561147072Sbrooks /* make_request doesn't initialize xid because it normally comes 562147072Sbrooks from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER, 563147072Sbrooks so pick an xid now. */ 564147072Sbrooks ip->client->xid = arc4random(); 565147072Sbrooks 566147072Sbrooks /* Make a DHCPREQUEST packet, and set appropriate per-interface 567147072Sbrooks flags. */ 568147072Sbrooks make_request(ip, ip->client->active); 569147072Sbrooks ip->client->destination = iaddr_broadcast; 570147072Sbrooks ip->client->first_sending = cur_time; 571147072Sbrooks ip->client->interval = ip->client->config->initial_interval; 572147072Sbrooks 573147072Sbrooks /* Zap the medium list... */ 574147072Sbrooks ip->client->medium = NULL; 575147072Sbrooks 576147072Sbrooks /* Send out the first DHCPREQUEST packet. */ 577147072Sbrooks send_request(ip); 578147072Sbrooks} 579147072Sbrooks 580147072Sbrooks/* 581147072Sbrooks * Called when a lease has completely expired and we've 582147072Sbrooks * been unable to renew it. 583147072Sbrooks */ 584147072Sbrooksvoid 585147072Sbrooksstate_init(void *ipp) 586147072Sbrooks{ 587147072Sbrooks struct interface_info *ip = ipp; 588147072Sbrooks 589147072Sbrooks ASSERT_STATE(state, S_INIT); 590147072Sbrooks 591147072Sbrooks /* Make a DHCPDISCOVER packet, and set appropriate per-interface 592147072Sbrooks flags. */ 593147072Sbrooks make_discover(ip, ip->client->active); 594147072Sbrooks ip->client->xid = ip->client->packet.xid; 595147072Sbrooks ip->client->destination = iaddr_broadcast; 596147072Sbrooks ip->client->state = S_SELECTING; 597147072Sbrooks ip->client->first_sending = cur_time; 598147072Sbrooks ip->client->interval = ip->client->config->initial_interval; 599147072Sbrooks 600147072Sbrooks /* Add an immediate timeout to cause the first DHCPDISCOVER packet 601147072Sbrooks to go out. */ 602147072Sbrooks send_discover(ip); 603147072Sbrooks} 604147072Sbrooks 605147072Sbrooks/* 606147072Sbrooks * state_selecting is called when one or more DHCPOFFER packets 607147072Sbrooks * have been received and a configurable period of time has passed. 608147072Sbrooks */ 609147072Sbrooksvoid 610147072Sbrooksstate_selecting(void *ipp) 611147072Sbrooks{ 612147072Sbrooks struct interface_info *ip = ipp; 613147072Sbrooks struct client_lease *lp, *next, *picked; 614147072Sbrooks 615147072Sbrooks ASSERT_STATE(state, S_SELECTING); 616147072Sbrooks 617147072Sbrooks /* Cancel state_selecting and send_discover timeouts, since either 618147072Sbrooks one could have got us here. */ 619147072Sbrooks cancel_timeout(state_selecting, ip); 620147072Sbrooks cancel_timeout(send_discover, ip); 621147072Sbrooks 622147072Sbrooks /* We have received one or more DHCPOFFER packets. Currently, 623147072Sbrooks the only criterion by which we judge leases is whether or 624147072Sbrooks not we get a response when we arp for them. */ 625147072Sbrooks picked = NULL; 626147072Sbrooks for (lp = ip->client->offered_leases; lp; lp = next) { 627147072Sbrooks next = lp->next; 628147072Sbrooks 629147072Sbrooks /* Check to see if we got an ARPREPLY for the address 630147072Sbrooks in this particular lease. */ 631147072Sbrooks if (!picked) { 632147072Sbrooks script_init("ARPCHECK", lp->medium); 633147072Sbrooks script_write_params("check_", lp); 634147072Sbrooks 635147072Sbrooks /* If the ARPCHECK code detects another 636147072Sbrooks machine using the offered address, it exits 637147072Sbrooks nonzero. We need to send a DHCPDECLINE and 638147072Sbrooks toss the lease. */ 639147072Sbrooks if (script_go()) { 640147072Sbrooks make_decline(ip, lp); 641147072Sbrooks send_decline(ip); 642147072Sbrooks goto freeit; 643147072Sbrooks } 644147072Sbrooks picked = lp; 645147072Sbrooks picked->next = NULL; 646147072Sbrooks } else { 647147072Sbrooksfreeit: 648147072Sbrooks free_client_lease(lp); 649147072Sbrooks } 650147072Sbrooks } 651147072Sbrooks ip->client->offered_leases = NULL; 652147072Sbrooks 653147072Sbrooks /* If we just tossed all the leases we were offered, go back 654147072Sbrooks to square one. */ 655147072Sbrooks if (!picked) { 656147072Sbrooks ip->client->state = S_INIT; 657147072Sbrooks state_init(ip); 658147072Sbrooks return; 659147072Sbrooks } 660147072Sbrooks 661147072Sbrooks /* If it was a BOOTREPLY, we can just take the address right now. */ 662147072Sbrooks if (!picked->options[DHO_DHCP_MESSAGE_TYPE].len) { 663147072Sbrooks ip->client->new = picked; 664147072Sbrooks 665147072Sbrooks /* Make up some lease expiry times 666147072Sbrooks XXX these should be configurable. */ 667147072Sbrooks ip->client->new->expiry = cur_time + 12000; 668147072Sbrooks ip->client->new->renewal += cur_time + 8000; 669147072Sbrooks ip->client->new->rebind += cur_time + 10000; 670147072Sbrooks 671147072Sbrooks ip->client->state = S_REQUESTING; 672147072Sbrooks 673147072Sbrooks /* Bind to the address we received. */ 674147072Sbrooks bind_lease(ip); 675147072Sbrooks return; 676147072Sbrooks } 677147072Sbrooks 678147072Sbrooks /* Go to the REQUESTING state. */ 679147072Sbrooks ip->client->destination = iaddr_broadcast; 680147072Sbrooks ip->client->state = S_REQUESTING; 681147072Sbrooks ip->client->first_sending = cur_time; 682147072Sbrooks ip->client->interval = ip->client->config->initial_interval; 683147072Sbrooks 684147072Sbrooks /* Make a DHCPREQUEST packet from the lease we picked. */ 685147072Sbrooks make_request(ip, picked); 686147072Sbrooks ip->client->xid = ip->client->packet.xid; 687147072Sbrooks 688147072Sbrooks /* Toss the lease we picked - we'll get it back in a DHCPACK. */ 689147072Sbrooks free_client_lease(picked); 690147072Sbrooks 691147072Sbrooks /* Add an immediate timeout to send the first DHCPREQUEST packet. */ 692147072Sbrooks send_request(ip); 693147072Sbrooks} 694147072Sbrooks 695147072Sbrooks/* state_requesting is called when we receive a DHCPACK message after 696147072Sbrooks having sent out one or more DHCPREQUEST packets. */ 697147072Sbrooks 698147072Sbrooksvoid 699147072Sbrooksdhcpack(struct packet *packet) 700147072Sbrooks{ 701147072Sbrooks struct interface_info *ip = packet->interface; 702147072Sbrooks struct client_lease *lease; 703147072Sbrooks 704147072Sbrooks /* If we're not receptive to an offer right now, or if the offer 705147072Sbrooks has an unrecognizable transaction id, then just drop it. */ 706147072Sbrooks if (packet->interface->client->xid != packet->raw->xid || 707147072Sbrooks (packet->interface->hw_address.hlen != packet->raw->hlen) || 708147072Sbrooks (memcmp(packet->interface->hw_address.haddr, 709147072Sbrooks packet->raw->chaddr, packet->raw->hlen))) 710147072Sbrooks return; 711147072Sbrooks 712147072Sbrooks if (ip->client->state != S_REBOOTING && 713147072Sbrooks ip->client->state != S_REQUESTING && 714147072Sbrooks ip->client->state != S_RENEWING && 715147072Sbrooks ip->client->state != S_REBINDING) 716147072Sbrooks return; 717147072Sbrooks 718147072Sbrooks note("DHCPACK from %s", piaddr(packet->client_addr)); 719147072Sbrooks 720147072Sbrooks lease = packet_to_lease(packet); 721147072Sbrooks if (!lease) { 722147072Sbrooks note("packet_to_lease failed."); 723147072Sbrooks return; 724147072Sbrooks } 725147072Sbrooks 726147072Sbrooks ip->client->new = lease; 727147072Sbrooks 728147072Sbrooks /* Stop resending DHCPREQUEST. */ 729147072Sbrooks cancel_timeout(send_request, ip); 730147072Sbrooks 731147072Sbrooks /* Figure out the lease time. */ 732147072Sbrooks if (ip->client->new->options[DHO_DHCP_LEASE_TIME].data) 733147072Sbrooks ip->client->new->expiry = getULong( 734147072Sbrooks ip->client->new->options[DHO_DHCP_LEASE_TIME].data); 735147072Sbrooks else 736147072Sbrooks ip->client->new->expiry = default_lease_time; 737147072Sbrooks /* A number that looks negative here is really just very large, 738147072Sbrooks because the lease expiry offset is unsigned. */ 739147072Sbrooks if (ip->client->new->expiry < 0) 740147072Sbrooks ip->client->new->expiry = TIME_MAX; 741147072Sbrooks /* XXX should be fixed by resetting the client state */ 742147072Sbrooks if (ip->client->new->expiry < 60) 743147072Sbrooks ip->client->new->expiry = 60; 744147072Sbrooks 745147072Sbrooks /* Take the server-provided renewal time if there is one; 746147072Sbrooks otherwise figure it out according to the spec. */ 747147072Sbrooks if (ip->client->new->options[DHO_DHCP_RENEWAL_TIME].len) 748147072Sbrooks ip->client->new->renewal = getULong( 749147072Sbrooks ip->client->new->options[DHO_DHCP_RENEWAL_TIME].data); 750147072Sbrooks else 751147072Sbrooks ip->client->new->renewal = ip->client->new->expiry / 2; 752147072Sbrooks 753147072Sbrooks /* Same deal with the rebind time. */ 754147072Sbrooks if (ip->client->new->options[DHO_DHCP_REBINDING_TIME].len) 755147072Sbrooks ip->client->new->rebind = getULong( 756147072Sbrooks ip->client->new->options[DHO_DHCP_REBINDING_TIME].data); 757147072Sbrooks else 758147072Sbrooks ip->client->new->rebind = ip->client->new->renewal + 759147072Sbrooks ip->client->new->renewal / 2 + ip->client->new->renewal / 4; 760147072Sbrooks 761147072Sbrooks ip->client->new->expiry += cur_time; 762147072Sbrooks /* Lease lengths can never be negative. */ 763147072Sbrooks if (ip->client->new->expiry < cur_time) 764147072Sbrooks ip->client->new->expiry = TIME_MAX; 765147072Sbrooks ip->client->new->renewal += cur_time; 766147072Sbrooks if (ip->client->new->renewal < cur_time) 767147072Sbrooks ip->client->new->renewal = TIME_MAX; 768147072Sbrooks ip->client->new->rebind += cur_time; 769147072Sbrooks if (ip->client->new->rebind < cur_time) 770147072Sbrooks ip->client->new->rebind = TIME_MAX; 771147072Sbrooks 772147072Sbrooks bind_lease(ip); 773147072Sbrooks} 774147072Sbrooks 775147072Sbrooksvoid 776147072Sbrooksbind_lease(struct interface_info *ip) 777147072Sbrooks{ 778147072Sbrooks /* Remember the medium. */ 779147072Sbrooks ip->client->new->medium = ip->client->medium; 780147072Sbrooks 781147072Sbrooks /* Write out the new lease. */ 782147072Sbrooks write_client_lease(ip, ip->client->new, 0); 783147072Sbrooks 784147072Sbrooks /* Run the client script with the new parameters. */ 785147072Sbrooks script_init((ip->client->state == S_REQUESTING ? "BOUND" : 786147072Sbrooks (ip->client->state == S_RENEWING ? "RENEW" : 787147072Sbrooks (ip->client->state == S_REBOOTING ? "REBOOT" : "REBIND"))), 788147072Sbrooks ip->client->new->medium); 789147072Sbrooks if (ip->client->active && ip->client->state != S_REBOOTING) 790147072Sbrooks script_write_params("old_", ip->client->active); 791147072Sbrooks script_write_params("new_", ip->client->new); 792147072Sbrooks if (ip->client->alias) 793147072Sbrooks script_write_params("alias_", ip->client->alias); 794147072Sbrooks script_go(); 795147072Sbrooks 796147072Sbrooks /* Replace the old active lease with the new one. */ 797147072Sbrooks if (ip->client->active) 798147072Sbrooks free_client_lease(ip->client->active); 799147072Sbrooks ip->client->active = ip->client->new; 800147072Sbrooks ip->client->new = NULL; 801147072Sbrooks 802147072Sbrooks /* Set up a timeout to start the renewal process. */ 803147072Sbrooks add_timeout(ip->client->active->renewal, state_bound, ip); 804147072Sbrooks 805147072Sbrooks note("bound to %s -- renewal in %d seconds.", 806147072Sbrooks piaddr(ip->client->active->address), 807147106Sbrooks (int)(ip->client->active->renewal - cur_time)); 808147072Sbrooks ip->client->state = S_BOUND; 809147072Sbrooks reinitialize_interfaces(); 810147072Sbrooks go_daemon(); 811147072Sbrooks} 812147072Sbrooks 813147072Sbrooks/* 814147072Sbrooks * state_bound is called when we've successfully bound to a particular 815147072Sbrooks * lease, but the renewal time on that lease has expired. We are 816147072Sbrooks * expected to unicast a DHCPREQUEST to the server that gave us our 817147072Sbrooks * original lease. 818147072Sbrooks */ 819147072Sbrooksvoid 820147072Sbrooksstate_bound(void *ipp) 821147072Sbrooks{ 822147072Sbrooks struct interface_info *ip = ipp; 823147072Sbrooks 824147072Sbrooks ASSERT_STATE(state, S_BOUND); 825147072Sbrooks 826147072Sbrooks /* T1 has expired. */ 827147072Sbrooks make_request(ip, ip->client->active); 828147072Sbrooks ip->client->xid = ip->client->packet.xid; 829147072Sbrooks 830147072Sbrooks if (ip->client->active->options[DHO_DHCP_SERVER_IDENTIFIER].len == 4) { 831147072Sbrooks memcpy(ip->client->destination.iabuf, ip->client->active-> 832147072Sbrooks options[DHO_DHCP_SERVER_IDENTIFIER].data, 4); 833147072Sbrooks ip->client->destination.len = 4; 834147072Sbrooks } else 835147072Sbrooks ip->client->destination = iaddr_broadcast; 836147072Sbrooks 837147072Sbrooks ip->client->first_sending = cur_time; 838147072Sbrooks ip->client->interval = ip->client->config->initial_interval; 839147072Sbrooks ip->client->state = S_RENEWING; 840147072Sbrooks 841147072Sbrooks /* Send the first packet immediately. */ 842147072Sbrooks send_request(ip); 843147072Sbrooks} 844147072Sbrooks 845147072Sbrooksvoid 846147072Sbrooksbootp(struct packet *packet) 847147072Sbrooks{ 848147072Sbrooks struct iaddrlist *ap; 849147072Sbrooks 850147072Sbrooks if (packet->raw->op != BOOTREPLY) 851147072Sbrooks return; 852147072Sbrooks 853147072Sbrooks /* If there's a reject list, make sure this packet's sender isn't 854147072Sbrooks on it. */ 855147072Sbrooks for (ap = packet->interface->client->config->reject_list; 856147072Sbrooks ap; ap = ap->next) { 857147072Sbrooks if (addr_eq(packet->client_addr, ap->addr)) { 858147072Sbrooks note("BOOTREPLY from %s rejected.", piaddr(ap->addr)); 859147072Sbrooks return; 860147072Sbrooks } 861147072Sbrooks } 862147072Sbrooks dhcpoffer(packet); 863147072Sbrooks} 864147072Sbrooks 865147072Sbrooksvoid 866147072Sbrooksdhcp(struct packet *packet) 867147072Sbrooks{ 868147072Sbrooks struct iaddrlist *ap; 869147072Sbrooks void (*handler)(struct packet *); 870147072Sbrooks char *type; 871147072Sbrooks 872147072Sbrooks switch (packet->packet_type) { 873147072Sbrooks case DHCPOFFER: 874147072Sbrooks handler = dhcpoffer; 875147072Sbrooks type = "DHCPOFFER"; 876147072Sbrooks break; 877147072Sbrooks case DHCPNAK: 878147072Sbrooks handler = dhcpnak; 879147072Sbrooks type = "DHCPNACK"; 880147072Sbrooks break; 881147072Sbrooks case DHCPACK: 882147072Sbrooks handler = dhcpack; 883147072Sbrooks type = "DHCPACK"; 884147072Sbrooks break; 885147072Sbrooks default: 886147072Sbrooks return; 887147072Sbrooks } 888147072Sbrooks 889147072Sbrooks /* If there's a reject list, make sure this packet's sender isn't 890147072Sbrooks on it. */ 891147072Sbrooks for (ap = packet->interface->client->config->reject_list; 892147072Sbrooks ap; ap = ap->next) { 893147072Sbrooks if (addr_eq(packet->client_addr, ap->addr)) { 894147072Sbrooks note("%s from %s rejected.", type, piaddr(ap->addr)); 895147072Sbrooks return; 896147072Sbrooks } 897147072Sbrooks } 898147072Sbrooks (*handler)(packet); 899147072Sbrooks} 900147072Sbrooks 901147072Sbrooksvoid 902147072Sbrooksdhcpoffer(struct packet *packet) 903147072Sbrooks{ 904147072Sbrooks struct interface_info *ip = packet->interface; 905147072Sbrooks struct client_lease *lease, *lp; 906147072Sbrooks int i; 907147072Sbrooks int arp_timeout_needed, stop_selecting; 908147072Sbrooks char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ? 909147072Sbrooks "DHCPOFFER" : "BOOTREPLY"; 910147072Sbrooks 911147072Sbrooks /* If we're not receptive to an offer right now, or if the offer 912147072Sbrooks has an unrecognizable transaction id, then just drop it. */ 913147072Sbrooks if (ip->client->state != S_SELECTING || 914147072Sbrooks packet->interface->client->xid != packet->raw->xid || 915147072Sbrooks (packet->interface->hw_address.hlen != packet->raw->hlen) || 916147072Sbrooks (memcmp(packet->interface->hw_address.haddr, 917147072Sbrooks packet->raw->chaddr, packet->raw->hlen))) 918147072Sbrooks return; 919147072Sbrooks 920147072Sbrooks note("%s from %s", name, piaddr(packet->client_addr)); 921147072Sbrooks 922147072Sbrooks 923147072Sbrooks /* If this lease doesn't supply the minimum required parameters, 924147072Sbrooks blow it off. */ 925147072Sbrooks for (i = 0; ip->client->config->required_options[i]; i++) { 926147072Sbrooks if (!packet->options[ip->client->config-> 927147072Sbrooks required_options[i]].len) { 928147072Sbrooks note("%s isn't satisfactory.", name); 929147072Sbrooks return; 930147072Sbrooks } 931147072Sbrooks } 932147072Sbrooks 933147072Sbrooks /* If we've already seen this lease, don't record it again. */ 934147072Sbrooks for (lease = ip->client->offered_leases; 935147072Sbrooks lease; lease = lease->next) { 936147072Sbrooks if (lease->address.len == sizeof(packet->raw->yiaddr) && 937147072Sbrooks !memcmp(lease->address.iabuf, 938147072Sbrooks &packet->raw->yiaddr, lease->address.len)) { 939147072Sbrooks debug("%s already seen.", name); 940147072Sbrooks return; 941147072Sbrooks } 942147072Sbrooks } 943147072Sbrooks 944147072Sbrooks lease = packet_to_lease(packet); 945147072Sbrooks if (!lease) { 946147072Sbrooks note("packet_to_lease failed."); 947147072Sbrooks return; 948147072Sbrooks } 949147072Sbrooks 950147072Sbrooks /* If this lease was acquired through a BOOTREPLY, record that 951147072Sbrooks fact. */ 952147072Sbrooks if (!packet->options[DHO_DHCP_MESSAGE_TYPE].len) 953147072Sbrooks lease->is_bootp = 1; 954147072Sbrooks 955147072Sbrooks /* Record the medium under which this lease was offered. */ 956147072Sbrooks lease->medium = ip->client->medium; 957147072Sbrooks 958147072Sbrooks /* Send out an ARP Request for the offered IP address. */ 959147072Sbrooks script_init("ARPSEND", lease->medium); 960147072Sbrooks script_write_params("check_", lease); 961147072Sbrooks /* If the script can't send an ARP request without waiting, 962147072Sbrooks we'll be waiting when we do the ARPCHECK, so don't wait now. */ 963147072Sbrooks if (script_go()) 964147072Sbrooks arp_timeout_needed = 0; 965147072Sbrooks else 966147072Sbrooks arp_timeout_needed = 2; 967147072Sbrooks 968147072Sbrooks /* Figure out when we're supposed to stop selecting. */ 969147072Sbrooks stop_selecting = 970147072Sbrooks ip->client->first_sending + ip->client->config->select_interval; 971147072Sbrooks 972147072Sbrooks /* If this is the lease we asked for, put it at the head of the 973147072Sbrooks list, and don't mess with the arp request timeout. */ 974147072Sbrooks if (lease->address.len == ip->client->requested_address.len && 975147072Sbrooks !memcmp(lease->address.iabuf, 976147072Sbrooks ip->client->requested_address.iabuf, 977147072Sbrooks ip->client->requested_address.len)) { 978147072Sbrooks lease->next = ip->client->offered_leases; 979147072Sbrooks ip->client->offered_leases = lease; 980147072Sbrooks } else { 981147072Sbrooks /* If we already have an offer, and arping for this 982147072Sbrooks offer would take us past the selection timeout, 983147072Sbrooks then don't extend the timeout - just hope for the 984147072Sbrooks best. */ 985147072Sbrooks if (ip->client->offered_leases && 986147072Sbrooks (cur_time + arp_timeout_needed) > stop_selecting) 987147072Sbrooks arp_timeout_needed = 0; 988147072Sbrooks 989147072Sbrooks /* Put the lease at the end of the list. */ 990147072Sbrooks lease->next = NULL; 991147072Sbrooks if (!ip->client->offered_leases) 992147072Sbrooks ip->client->offered_leases = lease; 993147072Sbrooks else { 994147072Sbrooks for (lp = ip->client->offered_leases; lp->next; 995147072Sbrooks lp = lp->next) 996147072Sbrooks ; /* nothing */ 997147072Sbrooks lp->next = lease; 998147072Sbrooks } 999147072Sbrooks } 1000147072Sbrooks 1001147072Sbrooks /* If we're supposed to stop selecting before we've had time 1002147072Sbrooks to wait for the ARPREPLY, add some delay to wait for 1003147072Sbrooks the ARPREPLY. */ 1004147072Sbrooks if (stop_selecting - cur_time < arp_timeout_needed) 1005147072Sbrooks stop_selecting = cur_time + arp_timeout_needed; 1006147072Sbrooks 1007147072Sbrooks /* If the selecting interval has expired, go immediately to 1008147072Sbrooks state_selecting(). Otherwise, time out into 1009147072Sbrooks state_selecting at the select interval. */ 1010147072Sbrooks if (stop_selecting <= 0) 1011147072Sbrooks state_selecting(ip); 1012147072Sbrooks else { 1013147072Sbrooks add_timeout(stop_selecting, state_selecting, ip); 1014147072Sbrooks cancel_timeout(send_discover, ip); 1015147072Sbrooks } 1016147072Sbrooks} 1017147072Sbrooks 1018147072Sbrooks/* Allocate a client_lease structure and initialize it from the parameters 1019147072Sbrooks in the specified packet. */ 1020147072Sbrooks 1021147072Sbrooksstruct client_lease * 1022147072Sbrookspacket_to_lease(struct packet *packet) 1023147072Sbrooks{ 1024147072Sbrooks struct client_lease *lease; 1025147072Sbrooks int i; 1026147072Sbrooks 1027147072Sbrooks lease = malloc(sizeof(struct client_lease)); 1028147072Sbrooks 1029147072Sbrooks if (!lease) { 1030147072Sbrooks warning("dhcpoffer: no memory to record lease."); 1031147072Sbrooks return (NULL); 1032147072Sbrooks } 1033147072Sbrooks 1034147072Sbrooks memset(lease, 0, sizeof(*lease)); 1035147072Sbrooks 1036147072Sbrooks /* Copy the lease options. */ 1037147072Sbrooks for (i = 0; i < 256; i++) { 1038147072Sbrooks if (packet->options[i].len) { 1039147072Sbrooks lease->options[i].data = 1040147072Sbrooks malloc(packet->options[i].len + 1); 1041147072Sbrooks if (!lease->options[i].data) { 1042147072Sbrooks warning("dhcpoffer: no memory for option %d", i); 1043147072Sbrooks free_client_lease(lease); 1044147072Sbrooks return (NULL); 1045147072Sbrooks } else { 1046147072Sbrooks memcpy(lease->options[i].data, 1047147072Sbrooks packet->options[i].data, 1048147072Sbrooks packet->options[i].len); 1049147072Sbrooks lease->options[i].len = 1050147072Sbrooks packet->options[i].len; 1051147072Sbrooks lease->options[i].data[lease->options[i].len] = 1052147072Sbrooks 0; 1053147072Sbrooks } 1054147072Sbrooks if (!check_option(lease,i)) { 1055147072Sbrooks /* ignore a bogus lease offer */ 1056147072Sbrooks warning("Invalid lease option - ignoring offer"); 1057147072Sbrooks free_client_lease(lease); 1058147072Sbrooks return (NULL); 1059147072Sbrooks } 1060147072Sbrooks } 1061147072Sbrooks } 1062147072Sbrooks 1063147072Sbrooks lease->address.len = sizeof(packet->raw->yiaddr); 1064147072Sbrooks memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len); 1065147072Sbrooks 1066148465Sbrooks /* If the server name was filled out, copy it. 1067148465Sbrooks Do not attempt to validate the server name as a host name. 1068148465Sbrooks RFC 2131 merely states that sname is NUL-terminated (which do 1069148465Sbrooks do not assume) and that it is the server's host name. Since 1070148465Sbrooks the ISC client and server allow arbitrary characters, we do 1071148465Sbrooks as well. */ 1072147072Sbrooks if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len || 1073147072Sbrooks !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) && 1074147072Sbrooks packet->raw->sname[0]) { 1075147072Sbrooks lease->server_name = malloc(DHCP_SNAME_LEN + 1); 1076147072Sbrooks if (!lease->server_name) { 1077147072Sbrooks warning("dhcpoffer: no memory for server name."); 1078147072Sbrooks free_client_lease(lease); 1079147072Sbrooks return (NULL); 1080147072Sbrooks } 1081147072Sbrooks memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN); 1082147072Sbrooks lease->server_name[DHCP_SNAME_LEN]='\0'; 1083147072Sbrooks } 1084147072Sbrooks 1085147072Sbrooks /* Ditto for the filename. */ 1086147072Sbrooks if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len || 1087147072Sbrooks !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) && 1088147072Sbrooks packet->raw->file[0]) { 1089147072Sbrooks /* Don't count on the NUL terminator. */ 1090147072Sbrooks lease->filename = malloc(DHCP_FILE_LEN + 1); 1091147072Sbrooks if (!lease->filename) { 1092147072Sbrooks warning("dhcpoffer: no memory for filename."); 1093147072Sbrooks free_client_lease(lease); 1094147072Sbrooks return (NULL); 1095147072Sbrooks } 1096147072Sbrooks memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN); 1097147072Sbrooks lease->filename[DHCP_FILE_LEN]='\0'; 1098147072Sbrooks } 1099147072Sbrooks return lease; 1100147072Sbrooks} 1101147072Sbrooks 1102147072Sbrooksvoid 1103147072Sbrooksdhcpnak(struct packet *packet) 1104147072Sbrooks{ 1105147072Sbrooks struct interface_info *ip = packet->interface; 1106147072Sbrooks 1107147072Sbrooks /* If we're not receptive to an offer right now, or if the offer 1108147072Sbrooks has an unrecognizable transaction id, then just drop it. */ 1109147072Sbrooks if (packet->interface->client->xid != packet->raw->xid || 1110147072Sbrooks (packet->interface->hw_address.hlen != packet->raw->hlen) || 1111147072Sbrooks (memcmp(packet->interface->hw_address.haddr, 1112147072Sbrooks packet->raw->chaddr, packet->raw->hlen))) 1113147072Sbrooks return; 1114147072Sbrooks 1115147072Sbrooks if (ip->client->state != S_REBOOTING && 1116147072Sbrooks ip->client->state != S_REQUESTING && 1117147072Sbrooks ip->client->state != S_RENEWING && 1118147072Sbrooks ip->client->state != S_REBINDING) 1119147072Sbrooks return; 1120147072Sbrooks 1121147072Sbrooks note("DHCPNAK from %s", piaddr(packet->client_addr)); 1122147072Sbrooks 1123147072Sbrooks if (!ip->client->active) { 1124147072Sbrooks note("DHCPNAK with no active lease.\n"); 1125147072Sbrooks return; 1126147072Sbrooks } 1127147072Sbrooks 1128147072Sbrooks free_client_lease(ip->client->active); 1129147072Sbrooks ip->client->active = NULL; 1130147072Sbrooks 1131147072Sbrooks /* Stop sending DHCPREQUEST packets... */ 1132147072Sbrooks cancel_timeout(send_request, ip); 1133147072Sbrooks 1134147072Sbrooks ip->client->state = S_INIT; 1135147072Sbrooks state_init(ip); 1136147072Sbrooks} 1137147072Sbrooks 1138147072Sbrooks/* Send out a DHCPDISCOVER packet, and set a timeout to send out another 1139147072Sbrooks one after the right interval has expired. If we don't get an offer by 1140147072Sbrooks the time we reach the panic interval, call the panic function. */ 1141147072Sbrooks 1142147072Sbrooksvoid 1143147072Sbrookssend_discover(void *ipp) 1144147072Sbrooks{ 1145147072Sbrooks struct interface_info *ip = ipp; 1146147072Sbrooks int interval, increase = 1; 1147147072Sbrooks 1148147072Sbrooks /* Figure out how long it's been since we started transmitting. */ 1149147072Sbrooks interval = cur_time - ip->client->first_sending; 1150147072Sbrooks 1151147072Sbrooks /* If we're past the panic timeout, call the script and tell it 1152147072Sbrooks we haven't found anything for this interface yet. */ 1153147072Sbrooks if (interval > ip->client->config->timeout) { 1154147072Sbrooks state_panic(ip); 1155147072Sbrooks return; 1156147072Sbrooks } 1157147072Sbrooks 1158147072Sbrooks /* If we're selecting media, try the whole list before doing 1159147072Sbrooks the exponential backoff, but if we've already received an 1160147072Sbrooks offer, stop looping, because we obviously have it right. */ 1161147072Sbrooks if (!ip->client->offered_leases && 1162147072Sbrooks ip->client->config->media) { 1163147072Sbrooks int fail = 0; 1164147072Sbrooksagain: 1165147072Sbrooks if (ip->client->medium) { 1166147072Sbrooks ip->client->medium = ip->client->medium->next; 1167147072Sbrooks increase = 0; 1168147072Sbrooks } 1169147072Sbrooks if (!ip->client->medium) { 1170147072Sbrooks if (fail) 1171147072Sbrooks error("No valid media types for %s!", ip->name); 1172147072Sbrooks ip->client->medium = ip->client->config->media; 1173147072Sbrooks increase = 1; 1174147072Sbrooks } 1175147072Sbrooks 1176147072Sbrooks note("Trying medium \"%s\" %d", ip->client->medium->string, 1177147072Sbrooks increase); 1178147072Sbrooks script_init("MEDIUM", ip->client->medium); 1179147072Sbrooks if (script_go()) 1180147072Sbrooks goto again; 1181147072Sbrooks } 1182147072Sbrooks 1183147072Sbrooks /* 1184147072Sbrooks * If we're supposed to increase the interval, do so. If it's 1185147072Sbrooks * currently zero (i.e., we haven't sent any packets yet), set 1186147072Sbrooks * it to one; otherwise, add to it a random number between zero 1187147072Sbrooks * and two times itself. On average, this means that it will 1188147072Sbrooks * double with every transmission. 1189147072Sbrooks */ 1190147072Sbrooks if (increase) { 1191147072Sbrooks if (!ip->client->interval) 1192147072Sbrooks ip->client->interval = 1193147072Sbrooks ip->client->config->initial_interval; 1194147072Sbrooks else { 1195147072Sbrooks ip->client->interval += (arc4random() >> 2) % 1196147072Sbrooks (2 * ip->client->interval); 1197147072Sbrooks } 1198147072Sbrooks 1199147072Sbrooks /* Don't backoff past cutoff. */ 1200147072Sbrooks if (ip->client->interval > 1201147072Sbrooks ip->client->config->backoff_cutoff) 1202147072Sbrooks ip->client->interval = 1203147072Sbrooks ((ip->client->config->backoff_cutoff / 2) 1204147072Sbrooks + ((arc4random() >> 2) % 1205147072Sbrooks ip->client->config->backoff_cutoff)); 1206147072Sbrooks } else if (!ip->client->interval) 1207147072Sbrooks ip->client->interval = 1208147072Sbrooks ip->client->config->initial_interval; 1209147072Sbrooks 1210147072Sbrooks /* If the backoff would take us to the panic timeout, just use that 1211147072Sbrooks as the interval. */ 1212147072Sbrooks if (cur_time + ip->client->interval > 1213147072Sbrooks ip->client->first_sending + ip->client->config->timeout) 1214147072Sbrooks ip->client->interval = 1215147072Sbrooks (ip->client->first_sending + 1216147072Sbrooks ip->client->config->timeout) - cur_time + 1; 1217147072Sbrooks 1218147072Sbrooks /* Record the number of seconds since we started sending. */ 1219147072Sbrooks if (interval < 65536) 1220147072Sbrooks ip->client->packet.secs = htons(interval); 1221147072Sbrooks else 1222147072Sbrooks ip->client->packet.secs = htons(65535); 1223147072Sbrooks ip->client->secs = ip->client->packet.secs; 1224147072Sbrooks 1225147072Sbrooks note("DHCPDISCOVER on %s to %s port %d interval %d", 1226147072Sbrooks ip->name, inet_ntoa(sockaddr_broadcast.sin_addr), 1227147106Sbrooks ntohs(sockaddr_broadcast.sin_port), 1228147106Sbrooks (int)ip->client->interval); 1229147072Sbrooks 1230147072Sbrooks /* Send out a packet. */ 1231147072Sbrooks (void)send_packet(ip, &ip->client->packet, ip->client->packet_length, 1232147072Sbrooks inaddr_any, &sockaddr_broadcast, NULL); 1233147072Sbrooks 1234147072Sbrooks add_timeout(cur_time + ip->client->interval, send_discover, ip); 1235147072Sbrooks} 1236147072Sbrooks 1237147072Sbrooks/* 1238147072Sbrooks * state_panic gets called if we haven't received any offers in a preset 1239147072Sbrooks * amount of time. When this happens, we try to use existing leases 1240147072Sbrooks * that haven't yet expired, and failing that, we call the client script 1241147072Sbrooks * and hope it can do something. 1242147072Sbrooks */ 1243147072Sbrooksvoid 1244147072Sbrooksstate_panic(void *ipp) 1245147072Sbrooks{ 1246147072Sbrooks struct interface_info *ip = ipp; 1247147072Sbrooks struct client_lease *loop = ip->client->active; 1248147072Sbrooks struct client_lease *lp; 1249147072Sbrooks 1250147072Sbrooks note("No DHCPOFFERS received."); 1251147072Sbrooks 1252147072Sbrooks /* We may not have an active lease, but we may have some 1253147072Sbrooks predefined leases that we can try. */ 1254147072Sbrooks if (!ip->client->active && ip->client->leases) 1255147072Sbrooks goto activate_next; 1256147072Sbrooks 1257147072Sbrooks /* Run through the list of leases and see if one can be used. */ 1258147072Sbrooks while (ip->client->active) { 1259147072Sbrooks if (ip->client->active->expiry > cur_time) { 1260147072Sbrooks note("Trying recorded lease %s", 1261147072Sbrooks piaddr(ip->client->active->address)); 1262147072Sbrooks /* Run the client script with the existing 1263147072Sbrooks parameters. */ 1264147072Sbrooks script_init("TIMEOUT", 1265147072Sbrooks ip->client->active->medium); 1266147072Sbrooks script_write_params("new_", ip->client->active); 1267147072Sbrooks if (ip->client->alias) 1268147072Sbrooks script_write_params("alias_", 1269147072Sbrooks ip->client->alias); 1270147072Sbrooks 1271147072Sbrooks /* If the old lease is still good and doesn't 1272147072Sbrooks yet need renewal, go into BOUND state and 1273147072Sbrooks timeout at the renewal time. */ 1274147072Sbrooks if (!script_go()) { 1275147072Sbrooks if (cur_time < 1276147072Sbrooks ip->client->active->renewal) { 1277147072Sbrooks ip->client->state = S_BOUND; 1278147072Sbrooks note("bound: renewal in %d seconds.", 1279147106Sbrooks (int)(ip->client->active->renewal - 1280147106Sbrooks cur_time)); 1281147072Sbrooks add_timeout( 1282147072Sbrooks ip->client->active->renewal, 1283147072Sbrooks state_bound, ip); 1284147072Sbrooks } else { 1285147072Sbrooks ip->client->state = S_BOUND; 1286147072Sbrooks note("bound: immediate renewal."); 1287147072Sbrooks state_bound(ip); 1288147072Sbrooks } 1289147072Sbrooks reinitialize_interfaces(); 1290147072Sbrooks go_daemon(); 1291147072Sbrooks return; 1292147072Sbrooks } 1293147072Sbrooks } 1294147072Sbrooks 1295147072Sbrooks /* If there are no other leases, give up. */ 1296147072Sbrooks if (!ip->client->leases) { 1297147072Sbrooks ip->client->leases = ip->client->active; 1298147072Sbrooks ip->client->active = NULL; 1299147072Sbrooks break; 1300147072Sbrooks } 1301147072Sbrooks 1302147072Sbrooksactivate_next: 1303147072Sbrooks /* Otherwise, put the active lease at the end of the 1304147072Sbrooks lease list, and try another lease.. */ 1305147072Sbrooks for (lp = ip->client->leases; lp->next; lp = lp->next) 1306147072Sbrooks ; 1307147072Sbrooks lp->next = ip->client->active; 1308147072Sbrooks if (lp->next) 1309147072Sbrooks lp->next->next = NULL; 1310147072Sbrooks ip->client->active = ip->client->leases; 1311147072Sbrooks ip->client->leases = ip->client->leases->next; 1312147072Sbrooks 1313147072Sbrooks /* If we already tried this lease, we've exhausted the 1314147072Sbrooks set of leases, so we might as well give up for 1315147072Sbrooks now. */ 1316147072Sbrooks if (ip->client->active == loop) 1317147072Sbrooks break; 1318147072Sbrooks else if (!loop) 1319147072Sbrooks loop = ip->client->active; 1320147072Sbrooks } 1321147072Sbrooks 1322147072Sbrooks /* No leases were available, or what was available didn't work, so 1323147072Sbrooks tell the shell script that we failed to allocate an address, 1324147072Sbrooks and try again later. */ 1325147072Sbrooks note("No working leases in persistent database - sleeping.\n"); 1326147072Sbrooks script_init("FAIL", NULL); 1327147072Sbrooks if (ip->client->alias) 1328147072Sbrooks script_write_params("alias_", ip->client->alias); 1329147072Sbrooks script_go(); 1330147072Sbrooks ip->client->state = S_INIT; 1331147072Sbrooks add_timeout(cur_time + ip->client->config->retry_interval, state_init, 1332147072Sbrooks ip); 1333147072Sbrooks go_daemon(); 1334147072Sbrooks} 1335147072Sbrooks 1336147072Sbrooksvoid 1337147072Sbrookssend_request(void *ipp) 1338147072Sbrooks{ 1339147072Sbrooks struct interface_info *ip = ipp; 1340147072Sbrooks struct sockaddr_in destination; 1341147072Sbrooks struct in_addr from; 1342147072Sbrooks int interval; 1343147072Sbrooks 1344147072Sbrooks /* Figure out how long it's been since we started transmitting. */ 1345147072Sbrooks interval = cur_time - ip->client->first_sending; 1346147072Sbrooks 1347147072Sbrooks /* If we're in the INIT-REBOOT or REQUESTING state and we're 1348147072Sbrooks past the reboot timeout, go to INIT and see if we can 1349147072Sbrooks DISCOVER an address... */ 1350147072Sbrooks /* XXX In the INIT-REBOOT state, if we don't get an ACK, it 1351147072Sbrooks means either that we're on a network with no DHCP server, 1352147072Sbrooks or that our server is down. In the latter case, assuming 1353147072Sbrooks that there is a backup DHCP server, DHCPDISCOVER will get 1354147072Sbrooks us a new address, but we could also have successfully 1355147072Sbrooks reused our old address. In the former case, we're hosed 1356147072Sbrooks anyway. This is not a win-prone situation. */ 1357147072Sbrooks if ((ip->client->state == S_REBOOTING || 1358147072Sbrooks ip->client->state == S_REQUESTING) && 1359147072Sbrooks interval > ip->client->config->reboot_timeout) { 1360147072Sbrookscancel: 1361147072Sbrooks ip->client->state = S_INIT; 1362147072Sbrooks cancel_timeout(send_request, ip); 1363147072Sbrooks state_init(ip); 1364147072Sbrooks return; 1365147072Sbrooks } 1366147072Sbrooks 1367147072Sbrooks /* If we're in the reboot state, make sure the media is set up 1368147072Sbrooks correctly. */ 1369147072Sbrooks if (ip->client->state == S_REBOOTING && 1370147072Sbrooks !ip->client->medium && 1371147072Sbrooks ip->client->active->medium ) { 1372147072Sbrooks script_init("MEDIUM", ip->client->active->medium); 1373147072Sbrooks 1374147072Sbrooks /* If the medium we chose won't fly, go to INIT state. */ 1375147072Sbrooks if (script_go()) 1376147072Sbrooks goto cancel; 1377147072Sbrooks 1378147072Sbrooks /* Record the medium. */ 1379147072Sbrooks ip->client->medium = ip->client->active->medium; 1380147072Sbrooks } 1381147072Sbrooks 1382147072Sbrooks /* If the lease has expired, relinquish the address and go back 1383147072Sbrooks to the INIT state. */ 1384147072Sbrooks if (ip->client->state != S_REQUESTING && 1385147072Sbrooks cur_time > ip->client->active->expiry) { 1386147072Sbrooks /* Run the client script with the new parameters. */ 1387147072Sbrooks script_init("EXPIRE", NULL); 1388147072Sbrooks script_write_params("old_", ip->client->active); 1389147072Sbrooks if (ip->client->alias) 1390147072Sbrooks script_write_params("alias_", ip->client->alias); 1391147072Sbrooks script_go(); 1392147072Sbrooks 1393147072Sbrooks /* Now do a preinit on the interface so that we can 1394147072Sbrooks discover a new address. */ 1395147072Sbrooks script_init("PREINIT", NULL); 1396147072Sbrooks if (ip->client->alias) 1397147072Sbrooks script_write_params("alias_", ip->client->alias); 1398147072Sbrooks script_go(); 1399147072Sbrooks 1400147072Sbrooks ip->client->state = S_INIT; 1401147072Sbrooks state_init(ip); 1402147072Sbrooks return; 1403147072Sbrooks } 1404147072Sbrooks 1405147072Sbrooks /* Do the exponential backoff... */ 1406147072Sbrooks if (!ip->client->interval) 1407147072Sbrooks ip->client->interval = ip->client->config->initial_interval; 1408147072Sbrooks else 1409147072Sbrooks ip->client->interval += ((arc4random() >> 2) % 1410147072Sbrooks (2 * ip->client->interval)); 1411147072Sbrooks 1412147072Sbrooks /* Don't backoff past cutoff. */ 1413147072Sbrooks if (ip->client->interval > 1414147072Sbrooks ip->client->config->backoff_cutoff) 1415147072Sbrooks ip->client->interval = 1416147072Sbrooks ((ip->client->config->backoff_cutoff / 2) + 1417147072Sbrooks ((arc4random() >> 2) % ip->client->interval)); 1418147072Sbrooks 1419147072Sbrooks /* If the backoff would take us to the expiry time, just set the 1420147072Sbrooks timeout to the expiry time. */ 1421147072Sbrooks if (ip->client->state != S_REQUESTING && 1422147072Sbrooks cur_time + ip->client->interval > 1423147072Sbrooks ip->client->active->expiry) 1424147072Sbrooks ip->client->interval = 1425147072Sbrooks ip->client->active->expiry - cur_time + 1; 1426147072Sbrooks 1427147072Sbrooks /* If the lease T2 time has elapsed, or if we're not yet bound, 1428147072Sbrooks broadcast the DHCPREQUEST rather than unicasting. */ 1429147072Sbrooks memset(&destination, 0, sizeof(destination)); 1430147072Sbrooks if (ip->client->state == S_REQUESTING || 1431147072Sbrooks ip->client->state == S_REBOOTING || 1432147072Sbrooks cur_time > ip->client->active->rebind) 1433147072Sbrooks destination.sin_addr.s_addr = INADDR_BROADCAST; 1434147072Sbrooks else 1435147072Sbrooks memcpy(&destination.sin_addr.s_addr, 1436147072Sbrooks ip->client->destination.iabuf, 1437147072Sbrooks sizeof(destination.sin_addr.s_addr)); 1438147072Sbrooks destination.sin_port = htons(REMOTE_PORT); 1439147072Sbrooks destination.sin_family = AF_INET; 1440147072Sbrooks destination.sin_len = sizeof(destination); 1441147072Sbrooks 1442147072Sbrooks if (ip->client->state != S_REQUESTING) 1443147072Sbrooks memcpy(&from, ip->client->active->address.iabuf, 1444147072Sbrooks sizeof(from)); 1445147072Sbrooks else 1446147072Sbrooks from.s_addr = INADDR_ANY; 1447147072Sbrooks 1448147072Sbrooks /* Record the number of seconds since we started sending. */ 1449147072Sbrooks if (ip->client->state == S_REQUESTING) 1450147072Sbrooks ip->client->packet.secs = ip->client->secs; 1451147072Sbrooks else { 1452147072Sbrooks if (interval < 65536) 1453147072Sbrooks ip->client->packet.secs = htons(interval); 1454147072Sbrooks else 1455147072Sbrooks ip->client->packet.secs = htons(65535); 1456147072Sbrooks } 1457147072Sbrooks 1458147072Sbrooks note("DHCPREQUEST on %s to %s port %d", ip->name, 1459147072Sbrooks inet_ntoa(destination.sin_addr), ntohs(destination.sin_port)); 1460147072Sbrooks 1461147072Sbrooks /* Send out a packet. */ 1462147072Sbrooks (void) send_packet(ip, &ip->client->packet, ip->client->packet_length, 1463147072Sbrooks from, &destination, NULL); 1464147072Sbrooks 1465147072Sbrooks add_timeout(cur_time + ip->client->interval, send_request, ip); 1466147072Sbrooks} 1467147072Sbrooks 1468147072Sbrooksvoid 1469147072Sbrookssend_decline(void *ipp) 1470147072Sbrooks{ 1471147072Sbrooks struct interface_info *ip = ipp; 1472147072Sbrooks 1473147072Sbrooks note("DHCPDECLINE on %s to %s port %d", ip->name, 1474147072Sbrooks inet_ntoa(sockaddr_broadcast.sin_addr), 1475147072Sbrooks ntohs(sockaddr_broadcast.sin_port)); 1476147072Sbrooks 1477147072Sbrooks /* Send out a packet. */ 1478147072Sbrooks (void) send_packet(ip, &ip->client->packet, ip->client->packet_length, 1479147072Sbrooks inaddr_any, &sockaddr_broadcast, NULL); 1480147072Sbrooks} 1481147072Sbrooks 1482147072Sbrooksvoid 1483147072Sbrooksmake_discover(struct interface_info *ip, struct client_lease *lease) 1484147072Sbrooks{ 1485147072Sbrooks unsigned char discover = DHCPDISCOVER; 1486147072Sbrooks struct tree_cache *options[256]; 1487147072Sbrooks struct tree_cache option_elements[256]; 1488147072Sbrooks int i; 1489147072Sbrooks 1490147072Sbrooks memset(option_elements, 0, sizeof(option_elements)); 1491147072Sbrooks memset(options, 0, sizeof(options)); 1492147072Sbrooks memset(&ip->client->packet, 0, sizeof(ip->client->packet)); 1493147072Sbrooks 1494147072Sbrooks /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */ 1495147072Sbrooks i = DHO_DHCP_MESSAGE_TYPE; 1496147072Sbrooks options[i] = &option_elements[i]; 1497147072Sbrooks options[i]->value = &discover; 1498147072Sbrooks options[i]->len = sizeof(discover); 1499147072Sbrooks options[i]->buf_size = sizeof(discover); 1500147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1501147072Sbrooks 1502147072Sbrooks /* Request the options we want */ 1503147072Sbrooks i = DHO_DHCP_PARAMETER_REQUEST_LIST; 1504147072Sbrooks options[i] = &option_elements[i]; 1505147072Sbrooks options[i]->value = ip->client->config->requested_options; 1506147072Sbrooks options[i]->len = ip->client->config->requested_option_count; 1507147072Sbrooks options[i]->buf_size = 1508147072Sbrooks ip->client->config->requested_option_count; 1509147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1510147072Sbrooks 1511147072Sbrooks /* If we had an address, try to get it again. */ 1512147072Sbrooks if (lease) { 1513147072Sbrooks ip->client->requested_address = lease->address; 1514147072Sbrooks i = DHO_DHCP_REQUESTED_ADDRESS; 1515147072Sbrooks options[i] = &option_elements[i]; 1516147072Sbrooks options[i]->value = lease->address.iabuf; 1517147072Sbrooks options[i]->len = lease->address.len; 1518147072Sbrooks options[i]->buf_size = lease->address.len; 1519147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1520147072Sbrooks } else 1521147072Sbrooks ip->client->requested_address.len = 0; 1522147072Sbrooks 1523147072Sbrooks /* Send any options requested in the config file. */ 1524147072Sbrooks for (i = 0; i < 256; i++) 1525147072Sbrooks if (!options[i] && 1526147072Sbrooks ip->client->config->send_options[i].data) { 1527147072Sbrooks options[i] = &option_elements[i]; 1528147072Sbrooks options[i]->value = 1529147072Sbrooks ip->client->config->send_options[i].data; 1530147072Sbrooks options[i]->len = 1531147072Sbrooks ip->client->config->send_options[i].len; 1532147072Sbrooks options[i]->buf_size = 1533147072Sbrooks ip->client->config->send_options[i].len; 1534147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1535147072Sbrooks } 1536158353Sbrooks 1537158353Sbrooks /* send host name if not set via config file. */ 1538158353Sbrooks char hostname[_POSIX_HOST_NAME_MAX+1]; 1539158353Sbrooks if (!options[DHO_HOST_NAME]) { 1540158353Sbrooks if (gethostname(hostname, sizeof(hostname)) == 0) { 1541158353Sbrooks size_t len; 1542158353Sbrooks char* posDot = strchr(hostname, '.'); 1543158353Sbrooks if (posDot != NULL) 1544158353Sbrooks len = posDot - hostname; 1545158353Sbrooks else 1546158353Sbrooks len = strlen(hostname); 1547158353Sbrooks options[DHO_HOST_NAME] = &option_elements[DHO_HOST_NAME]; 1548158353Sbrooks options[DHO_HOST_NAME]->value = hostname; 1549158353Sbrooks options[DHO_HOST_NAME]->len = len; 1550158353Sbrooks options[DHO_HOST_NAME]->buf_size = len; 1551158353Sbrooks options[DHO_HOST_NAME]->timeout = 0xFFFFFFFF; 1552158353Sbrooks } 1553158353Sbrooks } 1554147072Sbrooks 1555158353Sbrooks /* set unique client identifier */ 1556158353Sbrooks char client_ident[sizeof(struct hardware)]; 1557158353Sbrooks if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) { 1558158353Sbrooks int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ? 1559158353Sbrooks ip->hw_address.hlen : sizeof(client_ident)-1; 1560158353Sbrooks client_ident[0] = ip->hw_address.htype; 1561158353Sbrooks memcpy(&client_ident[1], ip->hw_address.haddr, hwlen); 1562158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER]; 1563158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident; 1564158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1; 1565158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->buf_size = hwlen+1; 1566158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->timeout = 0xFFFFFFFF; 1567158353Sbrooks } 1568158353Sbrooks 1569147072Sbrooks /* Set up the option buffer... */ 1570147072Sbrooks ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, 1571147072Sbrooks options, 0, 0, 0, NULL, 0); 1572147072Sbrooks if (ip->client->packet_length < BOOTP_MIN_LEN) 1573147072Sbrooks ip->client->packet_length = BOOTP_MIN_LEN; 1574147072Sbrooks 1575147072Sbrooks ip->client->packet.op = BOOTREQUEST; 1576147072Sbrooks ip->client->packet.htype = ip->hw_address.htype; 1577147072Sbrooks ip->client->packet.hlen = ip->hw_address.hlen; 1578147072Sbrooks ip->client->packet.hops = 0; 1579147072Sbrooks ip->client->packet.xid = arc4random(); 1580147072Sbrooks ip->client->packet.secs = 0; /* filled in by send_discover. */ 1581147072Sbrooks ip->client->packet.flags = 0; 1582147072Sbrooks 1583147072Sbrooks memset(&(ip->client->packet.ciaddr), 1584147072Sbrooks 0, sizeof(ip->client->packet.ciaddr)); 1585147072Sbrooks memset(&(ip->client->packet.yiaddr), 1586147072Sbrooks 0, sizeof(ip->client->packet.yiaddr)); 1587147072Sbrooks memset(&(ip->client->packet.siaddr), 1588147072Sbrooks 0, sizeof(ip->client->packet.siaddr)); 1589147072Sbrooks memset(&(ip->client->packet.giaddr), 1590147072Sbrooks 0, sizeof(ip->client->packet.giaddr)); 1591147072Sbrooks memcpy(ip->client->packet.chaddr, 1592147072Sbrooks ip->hw_address.haddr, ip->hw_address.hlen); 1593147072Sbrooks} 1594147072Sbrooks 1595147072Sbrooks 1596147072Sbrooksvoid 1597147072Sbrooksmake_request(struct interface_info *ip, struct client_lease * lease) 1598147072Sbrooks{ 1599147072Sbrooks unsigned char request = DHCPREQUEST; 1600147072Sbrooks struct tree_cache *options[256]; 1601147072Sbrooks struct tree_cache option_elements[256]; 1602147072Sbrooks int i; 1603147072Sbrooks 1604147072Sbrooks memset(options, 0, sizeof(options)); 1605147072Sbrooks memset(&ip->client->packet, 0, sizeof(ip->client->packet)); 1606147072Sbrooks 1607147072Sbrooks /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */ 1608147072Sbrooks i = DHO_DHCP_MESSAGE_TYPE; 1609147072Sbrooks options[i] = &option_elements[i]; 1610147072Sbrooks options[i]->value = &request; 1611147072Sbrooks options[i]->len = sizeof(request); 1612147072Sbrooks options[i]->buf_size = sizeof(request); 1613147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1614147072Sbrooks 1615147072Sbrooks /* Request the options we want */ 1616147072Sbrooks i = DHO_DHCP_PARAMETER_REQUEST_LIST; 1617147072Sbrooks options[i] = &option_elements[i]; 1618147072Sbrooks options[i]->value = ip->client->config->requested_options; 1619147072Sbrooks options[i]->len = ip->client->config->requested_option_count; 1620147072Sbrooks options[i]->buf_size = 1621147072Sbrooks ip->client->config->requested_option_count; 1622147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1623147072Sbrooks 1624147072Sbrooks /* If we are requesting an address that hasn't yet been assigned 1625147072Sbrooks to us, use the DHCP Requested Address option. */ 1626147072Sbrooks if (ip->client->state == S_REQUESTING) { 1627147072Sbrooks /* Send back the server identifier... */ 1628147072Sbrooks i = DHO_DHCP_SERVER_IDENTIFIER; 1629147072Sbrooks options[i] = &option_elements[i]; 1630147072Sbrooks options[i]->value = lease->options[i].data; 1631147072Sbrooks options[i]->len = lease->options[i].len; 1632147072Sbrooks options[i]->buf_size = lease->options[i].len; 1633147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1634147072Sbrooks } 1635147072Sbrooks if (ip->client->state == S_REQUESTING || 1636147072Sbrooks ip->client->state == S_REBOOTING) { 1637147072Sbrooks ip->client->requested_address = lease->address; 1638147072Sbrooks i = DHO_DHCP_REQUESTED_ADDRESS; 1639147072Sbrooks options[i] = &option_elements[i]; 1640147072Sbrooks options[i]->value = lease->address.iabuf; 1641147072Sbrooks options[i]->len = lease->address.len; 1642147072Sbrooks options[i]->buf_size = lease->address.len; 1643147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1644147072Sbrooks } else 1645147072Sbrooks ip->client->requested_address.len = 0; 1646147072Sbrooks 1647147072Sbrooks /* Send any options requested in the config file. */ 1648147072Sbrooks for (i = 0; i < 256; i++) 1649147072Sbrooks if (!options[i] && 1650147072Sbrooks ip->client->config->send_options[i].data) { 1651147072Sbrooks options[i] = &option_elements[i]; 1652147072Sbrooks options[i]->value = 1653147072Sbrooks ip->client->config->send_options[i].data; 1654147072Sbrooks options[i]->len = 1655147072Sbrooks ip->client->config->send_options[i].len; 1656147072Sbrooks options[i]->buf_size = 1657147072Sbrooks ip->client->config->send_options[i].len; 1658147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1659147072Sbrooks } 1660158353Sbrooks 1661158353Sbrooks /* send host name if not set via config file. */ 1662158353Sbrooks char hostname[_POSIX_HOST_NAME_MAX+1]; 1663158353Sbrooks if (!options[DHO_HOST_NAME]) { 1664158353Sbrooks if (gethostname(hostname, sizeof(hostname)) == 0) { 1665158353Sbrooks size_t len; 1666158353Sbrooks char* posDot = strchr(hostname, '.'); 1667158353Sbrooks if (posDot != NULL) 1668158353Sbrooks len = posDot - hostname; 1669158353Sbrooks else 1670158353Sbrooks len = strlen(hostname); 1671158353Sbrooks options[DHO_HOST_NAME] = &option_elements[DHO_HOST_NAME]; 1672158353Sbrooks options[DHO_HOST_NAME]->value = hostname; 1673158353Sbrooks options[DHO_HOST_NAME]->len = len; 1674158353Sbrooks options[DHO_HOST_NAME]->buf_size = len; 1675158353Sbrooks options[DHO_HOST_NAME]->timeout = 0xFFFFFFFF; 1676158353Sbrooks } 1677158353Sbrooks } 1678147072Sbrooks 1679158353Sbrooks /* set unique client identifier */ 1680158353Sbrooks char client_ident[sizeof(struct hardware)]; 1681158353Sbrooks if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) { 1682158353Sbrooks int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ? 1683158353Sbrooks ip->hw_address.hlen : sizeof(client_ident)-1; 1684158353Sbrooks client_ident[0] = ip->hw_address.htype; 1685158353Sbrooks memcpy(&client_ident[1], ip->hw_address.haddr, hwlen); 1686158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER]; 1687158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident; 1688158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1; 1689158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->buf_size = hwlen+1; 1690158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->timeout = 0xFFFFFFFF; 1691158353Sbrooks } 1692158353Sbrooks 1693147072Sbrooks /* Set up the option buffer... */ 1694147072Sbrooks ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, 1695147072Sbrooks options, 0, 0, 0, NULL, 0); 1696147072Sbrooks if (ip->client->packet_length < BOOTP_MIN_LEN) 1697147072Sbrooks ip->client->packet_length = BOOTP_MIN_LEN; 1698147072Sbrooks 1699147072Sbrooks ip->client->packet.op = BOOTREQUEST; 1700147072Sbrooks ip->client->packet.htype = ip->hw_address.htype; 1701147072Sbrooks ip->client->packet.hlen = ip->hw_address.hlen; 1702147072Sbrooks ip->client->packet.hops = 0; 1703147072Sbrooks ip->client->packet.xid = ip->client->xid; 1704147072Sbrooks ip->client->packet.secs = 0; /* Filled in by send_request. */ 1705147072Sbrooks 1706147072Sbrooks /* If we own the address we're requesting, put it in ciaddr; 1707147072Sbrooks otherwise set ciaddr to zero. */ 1708147072Sbrooks if (ip->client->state == S_BOUND || 1709147072Sbrooks ip->client->state == S_RENEWING || 1710147072Sbrooks ip->client->state == S_REBINDING) { 1711147072Sbrooks memcpy(&ip->client->packet.ciaddr, 1712147072Sbrooks lease->address.iabuf, lease->address.len); 1713147072Sbrooks ip->client->packet.flags = 0; 1714147072Sbrooks } else { 1715147072Sbrooks memset(&ip->client->packet.ciaddr, 0, 1716147072Sbrooks sizeof(ip->client->packet.ciaddr)); 1717147072Sbrooks ip->client->packet.flags = 0; 1718147072Sbrooks } 1719147072Sbrooks 1720147072Sbrooks memset(&ip->client->packet.yiaddr, 0, 1721147072Sbrooks sizeof(ip->client->packet.yiaddr)); 1722147072Sbrooks memset(&ip->client->packet.siaddr, 0, 1723147072Sbrooks sizeof(ip->client->packet.siaddr)); 1724147072Sbrooks memset(&ip->client->packet.giaddr, 0, 1725147072Sbrooks sizeof(ip->client->packet.giaddr)); 1726147072Sbrooks memcpy(ip->client->packet.chaddr, 1727147072Sbrooks ip->hw_address.haddr, ip->hw_address.hlen); 1728147072Sbrooks} 1729147072Sbrooks 1730147072Sbrooksvoid 1731147072Sbrooksmake_decline(struct interface_info *ip, struct client_lease *lease) 1732147072Sbrooks{ 1733147072Sbrooks struct tree_cache *options[256], message_type_tree; 1734147072Sbrooks struct tree_cache requested_address_tree; 1735147072Sbrooks struct tree_cache server_id_tree, client_id_tree; 1736147072Sbrooks unsigned char decline = DHCPDECLINE; 1737147072Sbrooks int i; 1738147072Sbrooks 1739147072Sbrooks memset(options, 0, sizeof(options)); 1740147072Sbrooks memset(&ip->client->packet, 0, sizeof(ip->client->packet)); 1741147072Sbrooks 1742147072Sbrooks /* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */ 1743147072Sbrooks i = DHO_DHCP_MESSAGE_TYPE; 1744147072Sbrooks options[i] = &message_type_tree; 1745147072Sbrooks options[i]->value = &decline; 1746147072Sbrooks options[i]->len = sizeof(decline); 1747147072Sbrooks options[i]->buf_size = sizeof(decline); 1748147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1749147072Sbrooks 1750147072Sbrooks /* Send back the server identifier... */ 1751147072Sbrooks i = DHO_DHCP_SERVER_IDENTIFIER; 1752147072Sbrooks options[i] = &server_id_tree; 1753147072Sbrooks options[i]->value = lease->options[i].data; 1754147072Sbrooks options[i]->len = lease->options[i].len; 1755147072Sbrooks options[i]->buf_size = lease->options[i].len; 1756147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1757147072Sbrooks 1758147072Sbrooks /* Send back the address we're declining. */ 1759147072Sbrooks i = DHO_DHCP_REQUESTED_ADDRESS; 1760147072Sbrooks options[i] = &requested_address_tree; 1761147072Sbrooks options[i]->value = lease->address.iabuf; 1762147072Sbrooks options[i]->len = lease->address.len; 1763147072Sbrooks options[i]->buf_size = lease->address.len; 1764147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1765147072Sbrooks 1766147072Sbrooks /* Send the uid if the user supplied one. */ 1767147072Sbrooks i = DHO_DHCP_CLIENT_IDENTIFIER; 1768147072Sbrooks if (ip->client->config->send_options[i].len) { 1769147072Sbrooks options[i] = &client_id_tree; 1770147072Sbrooks options[i]->value = ip->client->config->send_options[i].data; 1771147072Sbrooks options[i]->len = ip->client->config->send_options[i].len; 1772147072Sbrooks options[i]->buf_size = ip->client->config->send_options[i].len; 1773147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1774147072Sbrooks } 1775147072Sbrooks 1776147072Sbrooks 1777147072Sbrooks /* Set up the option buffer... */ 1778147072Sbrooks ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, 1779147072Sbrooks options, 0, 0, 0, NULL, 0); 1780147072Sbrooks if (ip->client->packet_length < BOOTP_MIN_LEN) 1781147072Sbrooks ip->client->packet_length = BOOTP_MIN_LEN; 1782147072Sbrooks 1783147072Sbrooks ip->client->packet.op = BOOTREQUEST; 1784147072Sbrooks ip->client->packet.htype = ip->hw_address.htype; 1785147072Sbrooks ip->client->packet.hlen = ip->hw_address.hlen; 1786147072Sbrooks ip->client->packet.hops = 0; 1787147072Sbrooks ip->client->packet.xid = ip->client->xid; 1788147072Sbrooks ip->client->packet.secs = 0; /* Filled in by send_request. */ 1789147072Sbrooks ip->client->packet.flags = 0; 1790147072Sbrooks 1791147072Sbrooks /* ciaddr must always be zero. */ 1792147072Sbrooks memset(&ip->client->packet.ciaddr, 0, 1793147072Sbrooks sizeof(ip->client->packet.ciaddr)); 1794147072Sbrooks memset(&ip->client->packet.yiaddr, 0, 1795147072Sbrooks sizeof(ip->client->packet.yiaddr)); 1796147072Sbrooks memset(&ip->client->packet.siaddr, 0, 1797147072Sbrooks sizeof(ip->client->packet.siaddr)); 1798147072Sbrooks memset(&ip->client->packet.giaddr, 0, 1799147072Sbrooks sizeof(ip->client->packet.giaddr)); 1800147072Sbrooks memcpy(ip->client->packet.chaddr, 1801147072Sbrooks ip->hw_address.haddr, ip->hw_address.hlen); 1802147072Sbrooks} 1803147072Sbrooks 1804147072Sbrooksvoid 1805147072Sbrooksfree_client_lease(struct client_lease *lease) 1806147072Sbrooks{ 1807147072Sbrooks int i; 1808147072Sbrooks 1809147072Sbrooks if (lease->server_name) 1810147072Sbrooks free(lease->server_name); 1811147072Sbrooks if (lease->filename) 1812147072Sbrooks free(lease->filename); 1813147072Sbrooks for (i = 0; i < 256; i++) { 1814147072Sbrooks if (lease->options[i].len) 1815147072Sbrooks free(lease->options[i].data); 1816147072Sbrooks } 1817147072Sbrooks free(lease); 1818147072Sbrooks} 1819147072Sbrooks 1820147072SbrooksFILE *leaseFile; 1821147072Sbrooks 1822147072Sbrooksvoid 1823147072Sbrooksrewrite_client_leases(void) 1824147072Sbrooks{ 1825147072Sbrooks struct client_lease *lp; 1826147072Sbrooks 1827147072Sbrooks if (!leaseFile) { 1828147072Sbrooks leaseFile = fopen(path_dhclient_db, "w"); 1829147072Sbrooks if (!leaseFile) 1830147072Sbrooks error("can't create %s: %m", path_dhclient_db); 1831147072Sbrooks } else { 1832147072Sbrooks fflush(leaseFile); 1833147072Sbrooks rewind(leaseFile); 1834147072Sbrooks } 1835147072Sbrooks 1836147072Sbrooks for (lp = ifi->client->leases; lp; lp = lp->next) 1837147072Sbrooks write_client_lease(ifi, lp, 1); 1838147072Sbrooks if (ifi->client->active) 1839147072Sbrooks write_client_lease(ifi, ifi->client->active, 1); 1840147072Sbrooks 1841147072Sbrooks fflush(leaseFile); 1842147072Sbrooks ftruncate(fileno(leaseFile), ftello(leaseFile)); 1843147072Sbrooks fsync(fileno(leaseFile)); 1844147072Sbrooks} 1845147072Sbrooks 1846147072Sbrooksvoid 1847147072Sbrookswrite_client_lease(struct interface_info *ip, struct client_lease *lease, 1848147072Sbrooks int rewrite) 1849147072Sbrooks{ 1850147072Sbrooks static int leases_written; 1851147072Sbrooks struct tm *t; 1852147072Sbrooks int i; 1853147072Sbrooks 1854147072Sbrooks if (!rewrite) { 1855147072Sbrooks if (leases_written++ > 20) { 1856147072Sbrooks rewrite_client_leases(); 1857147072Sbrooks leases_written = 0; 1858147072Sbrooks } 1859147072Sbrooks } 1860147072Sbrooks 1861147072Sbrooks /* If the lease came from the config file, we don't need to stash 1862147072Sbrooks a copy in the lease database. */ 1863147072Sbrooks if (lease->is_static) 1864147072Sbrooks return; 1865147072Sbrooks 1866147072Sbrooks if (!leaseFile) { /* XXX */ 1867147072Sbrooks leaseFile = fopen(path_dhclient_db, "w"); 1868147072Sbrooks if (!leaseFile) 1869147072Sbrooks error("can't create %s: %m", path_dhclient_db); 1870147072Sbrooks } 1871147072Sbrooks 1872147072Sbrooks fprintf(leaseFile, "lease {\n"); 1873147072Sbrooks if (lease->is_bootp) 1874147072Sbrooks fprintf(leaseFile, " bootp;\n"); 1875147072Sbrooks fprintf(leaseFile, " interface \"%s\";\n", ip->name); 1876147072Sbrooks fprintf(leaseFile, " fixed-address %s;\n", piaddr(lease->address)); 1877147072Sbrooks if (lease->filename) 1878147072Sbrooks fprintf(leaseFile, " filename \"%s\";\n", lease->filename); 1879147072Sbrooks if (lease->server_name) 1880147072Sbrooks fprintf(leaseFile, " server-name \"%s\";\n", 1881147072Sbrooks lease->server_name); 1882147072Sbrooks if (lease->medium) 1883147072Sbrooks fprintf(leaseFile, " medium \"%s\";\n", lease->medium->string); 1884147072Sbrooks for (i = 0; i < 256; i++) 1885147072Sbrooks if (lease->options[i].len) 1886147072Sbrooks fprintf(leaseFile, " option %s %s;\n", 1887147072Sbrooks dhcp_options[i].name, 1888147072Sbrooks pretty_print_option(i, lease->options[i].data, 1889147072Sbrooks lease->options[i].len, 1, 1)); 1890147072Sbrooks 1891147072Sbrooks t = gmtime(&lease->renewal); 1892147072Sbrooks fprintf(leaseFile, " renew %d %d/%d/%d %02d:%02d:%02d;\n", 1893147072Sbrooks t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, 1894147072Sbrooks t->tm_hour, t->tm_min, t->tm_sec); 1895147072Sbrooks t = gmtime(&lease->rebind); 1896147072Sbrooks fprintf(leaseFile, " rebind %d %d/%d/%d %02d:%02d:%02d;\n", 1897147072Sbrooks t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, 1898147072Sbrooks t->tm_hour, t->tm_min, t->tm_sec); 1899147072Sbrooks t = gmtime(&lease->expiry); 1900147072Sbrooks fprintf(leaseFile, " expire %d %d/%d/%d %02d:%02d:%02d;\n", 1901147072Sbrooks t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, 1902147072Sbrooks t->tm_hour, t->tm_min, t->tm_sec); 1903147072Sbrooks fprintf(leaseFile, "}\n"); 1904147072Sbrooks fflush(leaseFile); 1905147072Sbrooks} 1906147072Sbrooks 1907147072Sbrooksvoid 1908147072Sbrooksscript_init(char *reason, struct string_list *medium) 1909147072Sbrooks{ 1910147072Sbrooks size_t len, mediumlen = 0; 1911147072Sbrooks struct imsg_hdr hdr; 1912147072Sbrooks struct buf *buf; 1913147072Sbrooks int errs; 1914147072Sbrooks 1915147072Sbrooks if (medium != NULL && medium->string != NULL) 1916147072Sbrooks mediumlen = strlen(medium->string); 1917147072Sbrooks 1918147072Sbrooks hdr.code = IMSG_SCRIPT_INIT; 1919147072Sbrooks hdr.len = sizeof(struct imsg_hdr) + 1920147072Sbrooks sizeof(size_t) + mediumlen + 1921147072Sbrooks sizeof(size_t) + strlen(reason); 1922147072Sbrooks 1923147072Sbrooks if ((buf = buf_open(hdr.len)) == NULL) 1924147072Sbrooks error("buf_open: %m"); 1925147072Sbrooks 1926147072Sbrooks errs = 0; 1927147072Sbrooks errs += buf_add(buf, &hdr, sizeof(hdr)); 1928147072Sbrooks errs += buf_add(buf, &mediumlen, sizeof(mediumlen)); 1929147072Sbrooks if (mediumlen > 0) 1930147072Sbrooks errs += buf_add(buf, medium->string, mediumlen); 1931147072Sbrooks len = strlen(reason); 1932147072Sbrooks errs += buf_add(buf, &len, sizeof(len)); 1933147072Sbrooks errs += buf_add(buf, reason, len); 1934147072Sbrooks 1935147072Sbrooks if (errs) 1936147072Sbrooks error("buf_add: %m"); 1937147072Sbrooks 1938147072Sbrooks if (buf_close(privfd, buf) == -1) 1939147072Sbrooks error("buf_close: %m"); 1940147072Sbrooks} 1941147072Sbrooks 1942147072Sbrooksvoid 1943147072Sbrookspriv_script_init(char *reason, char *medium) 1944147072Sbrooks{ 1945147072Sbrooks struct interface_info *ip = ifi; 1946147072Sbrooks 1947147072Sbrooks if (ip) { 1948147072Sbrooks ip->client->scriptEnvsize = 100; 1949147072Sbrooks if (ip->client->scriptEnv == NULL) 1950147072Sbrooks ip->client->scriptEnv = 1951147072Sbrooks malloc(ip->client->scriptEnvsize * sizeof(char *)); 1952147072Sbrooks if (ip->client->scriptEnv == NULL) 1953147072Sbrooks error("script_init: no memory for environment"); 1954147072Sbrooks 1955147072Sbrooks ip->client->scriptEnv[0] = strdup(CLIENT_PATH); 1956147072Sbrooks if (ip->client->scriptEnv[0] == NULL) 1957147072Sbrooks error("script_init: no memory for environment"); 1958147072Sbrooks 1959147072Sbrooks ip->client->scriptEnv[1] = NULL; 1960147072Sbrooks 1961147072Sbrooks script_set_env(ip->client, "", "interface", ip->name); 1962147072Sbrooks 1963147072Sbrooks if (medium) 1964147072Sbrooks script_set_env(ip->client, "", "medium", medium); 1965147072Sbrooks 1966147072Sbrooks script_set_env(ip->client, "", "reason", reason); 1967147072Sbrooks } 1968147072Sbrooks} 1969147072Sbrooks 1970147072Sbrooksvoid 1971147072Sbrookspriv_script_write_params(char *prefix, struct client_lease *lease) 1972147072Sbrooks{ 1973147072Sbrooks struct interface_info *ip = ifi; 1974149727Sbrooks u_int8_t dbuf[1500], *dp = NULL; 1975149727Sbrooks int i, len; 1976147072Sbrooks char tbuf[128]; 1977147072Sbrooks 1978147072Sbrooks script_set_env(ip->client, prefix, "ip_address", 1979147072Sbrooks piaddr(lease->address)); 1980147072Sbrooks 1981149727Sbrooks if (ip->client->config->default_actions[DHO_SUBNET_MASK] == 1982149727Sbrooks ACTION_SUPERSEDE) { 1983149727Sbrooks dp = ip->client->config->defaults[DHO_SUBNET_MASK].data; 1984149727Sbrooks len = ip->client->config->defaults[DHO_SUBNET_MASK].len; 1985149727Sbrooks } else { 1986149727Sbrooks dp = lease->options[DHO_SUBNET_MASK].data; 1987149727Sbrooks len = lease->options[DHO_SUBNET_MASK].len; 1988149727Sbrooks } 1989149727Sbrooks if (len && (len < sizeof(lease->address.iabuf))) { 1990147072Sbrooks struct iaddr netmask, subnet, broadcast; 1991147072Sbrooks 1992149727Sbrooks memcpy(netmask.iabuf, dp, len); 1993149727Sbrooks netmask.len = len; 1994147072Sbrooks subnet = subnet_number(lease->address, netmask); 1995147072Sbrooks if (subnet.len) { 1996147072Sbrooks script_set_env(ip->client, prefix, "network_number", 1997147072Sbrooks piaddr(subnet)); 1998147072Sbrooks if (!lease->options[DHO_BROADCAST_ADDRESS].len) { 1999147072Sbrooks broadcast = broadcast_addr(subnet, netmask); 2000147072Sbrooks if (broadcast.len) 2001147072Sbrooks script_set_env(ip->client, prefix, 2002147072Sbrooks "broadcast_address", 2003147072Sbrooks piaddr(broadcast)); 2004147072Sbrooks } 2005147072Sbrooks } 2006147072Sbrooks } 2007147072Sbrooks 2008147072Sbrooks if (lease->filename) 2009147072Sbrooks script_set_env(ip->client, prefix, "filename", lease->filename); 2010147072Sbrooks if (lease->server_name) 2011147072Sbrooks script_set_env(ip->client, prefix, "server_name", 2012147072Sbrooks lease->server_name); 2013147072Sbrooks for (i = 0; i < 256; i++) { 2014149727Sbrooks len = 0; 2015147072Sbrooks 2016147072Sbrooks if (ip->client->config->defaults[i].len) { 2017147072Sbrooks if (lease->options[i].len) { 2018147072Sbrooks switch ( 2019147072Sbrooks ip->client->config->default_actions[i]) { 2020147072Sbrooks case ACTION_DEFAULT: 2021147072Sbrooks dp = lease->options[i].data; 2022147072Sbrooks len = lease->options[i].len; 2023147072Sbrooks break; 2024147072Sbrooks case ACTION_SUPERSEDE: 2025147072Sbrookssupersede: 2026147072Sbrooks dp = ip->client-> 2027147072Sbrooks config->defaults[i].data; 2028147072Sbrooks len = ip->client-> 2029147072Sbrooks config->defaults[i].len; 2030147072Sbrooks break; 2031147072Sbrooks case ACTION_PREPEND: 2032147072Sbrooks len = ip->client-> 2033147072Sbrooks config->defaults[i].len + 2034147072Sbrooks lease->options[i].len; 2035193765Sbrian if (len >= sizeof(dbuf)) { 2036147072Sbrooks warning("no space to %s %s", 2037147072Sbrooks "prepend option", 2038147072Sbrooks dhcp_options[i].name); 2039147072Sbrooks goto supersede; 2040147072Sbrooks } 2041147072Sbrooks dp = dbuf; 2042147072Sbrooks memcpy(dp, 2043147072Sbrooks ip->client-> 2044147072Sbrooks config->defaults[i].data, 2045147072Sbrooks ip->client-> 2046147072Sbrooks config->defaults[i].len); 2047147072Sbrooks memcpy(dp + ip->client-> 2048147072Sbrooks config->defaults[i].len, 2049147072Sbrooks lease->options[i].data, 2050147072Sbrooks lease->options[i].len); 2051147072Sbrooks dp[len] = '\0'; 2052147072Sbrooks break; 2053147072Sbrooks case ACTION_APPEND: 2054193765Sbrian /* 2055193765Sbrian * When we append, we assume that we're 2056193765Sbrian * appending to text. Some MS servers 2057193765Sbrian * include a NUL byte at the end of 2058193765Sbrian * the search string provided. 2059193765Sbrian */ 2060147072Sbrooks len = ip->client-> 2061147072Sbrooks config->defaults[i].len + 2062147072Sbrooks lease->options[i].len; 2063193765Sbrian if (len >= sizeof(dbuf)) { 2064147072Sbrooks warning("no space to %s %s", 2065147072Sbrooks "append option", 2066147072Sbrooks dhcp_options[i].name); 2067147072Sbrooks goto supersede; 2068147072Sbrooks } 2069193765Sbrian memcpy(dbuf, 2070147072Sbrooks lease->options[i].data, 2071147072Sbrooks lease->options[i].len); 2072193765Sbrian for (dp = dbuf + lease->options[i].len; 2073193765Sbrian dp > dbuf; dp--, len--) 2074193765Sbrian if (dp[-1] != '\0') 2075193765Sbrian break; 2076193765Sbrian memcpy(dp, 2077147072Sbrooks ip->client-> 2078147072Sbrooks config->defaults[i].data, 2079147072Sbrooks ip->client-> 2080147072Sbrooks config->defaults[i].len); 2081193765Sbrian dp = dbuf; 2082147072Sbrooks dp[len] = '\0'; 2083147072Sbrooks } 2084147072Sbrooks } else { 2085147072Sbrooks dp = ip->client-> 2086147072Sbrooks config->defaults[i].data; 2087147072Sbrooks len = ip->client-> 2088147072Sbrooks config->defaults[i].len; 2089147072Sbrooks } 2090147072Sbrooks } else if (lease->options[i].len) { 2091147072Sbrooks len = lease->options[i].len; 2092147072Sbrooks dp = lease->options[i].data; 2093147072Sbrooks } else { 2094147072Sbrooks len = 0; 2095147072Sbrooks } 2096147072Sbrooks if (len) { 2097147072Sbrooks char name[256]; 2098147072Sbrooks 2099147072Sbrooks if (dhcp_option_ev_name(name, sizeof(name), 2100147072Sbrooks &dhcp_options[i])) 2101147072Sbrooks script_set_env(ip->client, prefix, name, 2102147072Sbrooks pretty_print_option(i, dp, len, 0, 0)); 2103147072Sbrooks } 2104147072Sbrooks } 2105147072Sbrooks snprintf(tbuf, sizeof(tbuf), "%d", (int)lease->expiry); 2106147072Sbrooks script_set_env(ip->client, prefix, "expiry", tbuf); 2107147072Sbrooks} 2108147072Sbrooks 2109147072Sbrooksvoid 2110147072Sbrooksscript_write_params(char *prefix, struct client_lease *lease) 2111147072Sbrooks{ 2112147072Sbrooks size_t fn_len = 0, sn_len = 0, pr_len = 0; 2113147072Sbrooks struct imsg_hdr hdr; 2114147072Sbrooks struct buf *buf; 2115147072Sbrooks int errs, i; 2116147072Sbrooks 2117147072Sbrooks if (lease->filename != NULL) 2118147072Sbrooks fn_len = strlen(lease->filename); 2119147072Sbrooks if (lease->server_name != NULL) 2120147072Sbrooks sn_len = strlen(lease->server_name); 2121147072Sbrooks if (prefix != NULL) 2122147072Sbrooks pr_len = strlen(prefix); 2123147072Sbrooks 2124147072Sbrooks hdr.code = IMSG_SCRIPT_WRITE_PARAMS; 2125147072Sbrooks hdr.len = sizeof(hdr) + sizeof(struct client_lease) + 2126147072Sbrooks sizeof(size_t) + fn_len + sizeof(size_t) + sn_len + 2127147072Sbrooks sizeof(size_t) + pr_len; 2128147072Sbrooks 2129147072Sbrooks for (i = 0; i < 256; i++) 2130147072Sbrooks hdr.len += sizeof(int) + lease->options[i].len; 2131147072Sbrooks 2132147072Sbrooks scripttime = time(NULL); 2133147072Sbrooks 2134147072Sbrooks if ((buf = buf_open(hdr.len)) == NULL) 2135147072Sbrooks error("buf_open: %m"); 2136147072Sbrooks 2137147072Sbrooks errs = 0; 2138147072Sbrooks errs += buf_add(buf, &hdr, sizeof(hdr)); 2139147072Sbrooks errs += buf_add(buf, lease, sizeof(struct client_lease)); 2140147072Sbrooks errs += buf_add(buf, &fn_len, sizeof(fn_len)); 2141147072Sbrooks errs += buf_add(buf, lease->filename, fn_len); 2142147072Sbrooks errs += buf_add(buf, &sn_len, sizeof(sn_len)); 2143147072Sbrooks errs += buf_add(buf, lease->server_name, sn_len); 2144147072Sbrooks errs += buf_add(buf, &pr_len, sizeof(pr_len)); 2145147072Sbrooks errs += buf_add(buf, prefix, pr_len); 2146147072Sbrooks 2147147072Sbrooks for (i = 0; i < 256; i++) { 2148147072Sbrooks errs += buf_add(buf, &lease->options[i].len, 2149147072Sbrooks sizeof(lease->options[i].len)); 2150147072Sbrooks errs += buf_add(buf, lease->options[i].data, 2151147072Sbrooks lease->options[i].len); 2152147072Sbrooks } 2153147072Sbrooks 2154147072Sbrooks if (errs) 2155147072Sbrooks error("buf_add: %m"); 2156147072Sbrooks 2157147072Sbrooks if (buf_close(privfd, buf) == -1) 2158147072Sbrooks error("buf_close: %m"); 2159147072Sbrooks} 2160147072Sbrooks 2161147072Sbrooksint 2162147072Sbrooksscript_go(void) 2163147072Sbrooks{ 2164147072Sbrooks struct imsg_hdr hdr; 2165147072Sbrooks struct buf *buf; 2166147072Sbrooks int ret; 2167147072Sbrooks 2168147072Sbrooks hdr.code = IMSG_SCRIPT_GO; 2169147072Sbrooks hdr.len = sizeof(struct imsg_hdr); 2170147072Sbrooks 2171147072Sbrooks if ((buf = buf_open(hdr.len)) == NULL) 2172147072Sbrooks error("buf_open: %m"); 2173147072Sbrooks 2174147072Sbrooks if (buf_add(buf, &hdr, sizeof(hdr))) 2175147072Sbrooks error("buf_add: %m"); 2176147072Sbrooks 2177147072Sbrooks if (buf_close(privfd, buf) == -1) 2178147072Sbrooks error("buf_close: %m"); 2179147072Sbrooks 2180147072Sbrooks bzero(&hdr, sizeof(hdr)); 2181147072Sbrooks buf_read(privfd, &hdr, sizeof(hdr)); 2182147072Sbrooks if (hdr.code != IMSG_SCRIPT_GO_RET) 2183147072Sbrooks error("unexpected msg type %u", hdr.code); 2184147072Sbrooks if (hdr.len != sizeof(hdr) + sizeof(int)) 2185147072Sbrooks error("received corrupted message"); 2186147072Sbrooks buf_read(privfd, &ret, sizeof(ret)); 2187147072Sbrooks 2188209756Sbrian scripttime = time(NULL); 2189209756Sbrian 2190147072Sbrooks return (ret); 2191147072Sbrooks} 2192147072Sbrooks 2193147072Sbrooksint 2194147072Sbrookspriv_script_go(void) 2195147072Sbrooks{ 2196147072Sbrooks char *scriptName, *argv[2], **envp, *epp[3], reason[] = "REASON=NBI"; 2197147072Sbrooks static char client_path[] = CLIENT_PATH; 2198147072Sbrooks struct interface_info *ip = ifi; 2199147072Sbrooks int pid, wpid, wstatus; 2200147072Sbrooks 2201147072Sbrooks scripttime = time(NULL); 2202147072Sbrooks 2203147072Sbrooks if (ip) { 2204147072Sbrooks scriptName = ip->client->config->script_name; 2205147072Sbrooks envp = ip->client->scriptEnv; 2206147072Sbrooks } else { 2207147072Sbrooks scriptName = top_level_config.script_name; 2208147072Sbrooks epp[0] = reason; 2209147072Sbrooks epp[1] = client_path; 2210147072Sbrooks epp[2] = NULL; 2211147072Sbrooks envp = epp; 2212147072Sbrooks } 2213147072Sbrooks 2214147072Sbrooks argv[0] = scriptName; 2215147072Sbrooks argv[1] = NULL; 2216147072Sbrooks 2217147072Sbrooks pid = fork(); 2218147072Sbrooks if (pid < 0) { 2219147072Sbrooks error("fork: %m"); 2220147072Sbrooks wstatus = 0; 2221147072Sbrooks } else if (pid) { 2222147072Sbrooks do { 2223147072Sbrooks wpid = wait(&wstatus); 2224147072Sbrooks } while (wpid != pid && wpid > 0); 2225147072Sbrooks if (wpid < 0) { 2226147072Sbrooks error("wait: %m"); 2227147072Sbrooks wstatus = 0; 2228147072Sbrooks } 2229147072Sbrooks } else { 2230147072Sbrooks execve(scriptName, argv, envp); 2231147072Sbrooks error("execve (%s, ...): %m", scriptName); 2232147072Sbrooks } 2233147072Sbrooks 2234147072Sbrooks if (ip) 2235147072Sbrooks script_flush_env(ip->client); 2236147072Sbrooks 2237147072Sbrooks return (wstatus & 0xff); 2238147072Sbrooks} 2239147072Sbrooks 2240147072Sbrooksvoid 2241147072Sbrooksscript_set_env(struct client_state *client, const char *prefix, 2242147072Sbrooks const char *name, const char *value) 2243147072Sbrooks{ 2244147072Sbrooks int i, j, namelen; 2245147072Sbrooks 2246147072Sbrooks namelen = strlen(name); 2247147072Sbrooks 2248147072Sbrooks for (i = 0; client->scriptEnv[i]; i++) 2249147072Sbrooks if (strncmp(client->scriptEnv[i], name, namelen) == 0 && 2250147072Sbrooks client->scriptEnv[i][namelen] == '=') 2251147072Sbrooks break; 2252147072Sbrooks 2253147072Sbrooks if (client->scriptEnv[i]) 2254147072Sbrooks /* Reuse the slot. */ 2255147072Sbrooks free(client->scriptEnv[i]); 2256147072Sbrooks else { 2257147072Sbrooks /* New variable. Expand if necessary. */ 2258147072Sbrooks if (i >= client->scriptEnvsize - 1) { 2259147072Sbrooks char **newscriptEnv; 2260147072Sbrooks int newscriptEnvsize = client->scriptEnvsize + 50; 2261147072Sbrooks 2262147072Sbrooks newscriptEnv = realloc(client->scriptEnv, 2263147072Sbrooks newscriptEnvsize); 2264147072Sbrooks if (newscriptEnv == NULL) { 2265147072Sbrooks free(client->scriptEnv); 2266147072Sbrooks client->scriptEnv = NULL; 2267147072Sbrooks client->scriptEnvsize = 0; 2268147072Sbrooks error("script_set_env: no memory for variable"); 2269147072Sbrooks } 2270147072Sbrooks client->scriptEnv = newscriptEnv; 2271147072Sbrooks client->scriptEnvsize = newscriptEnvsize; 2272147072Sbrooks } 2273147072Sbrooks /* need to set the NULL pointer at end of array beyond 2274147072Sbrooks the new slot. */ 2275147072Sbrooks client->scriptEnv[i + 1] = NULL; 2276147072Sbrooks } 2277147072Sbrooks /* Allocate space and format the variable in the appropriate slot. */ 2278147072Sbrooks client->scriptEnv[i] = malloc(strlen(prefix) + strlen(name) + 1 + 2279147072Sbrooks strlen(value) + 1); 2280147072Sbrooks if (client->scriptEnv[i] == NULL) 2281147072Sbrooks error("script_set_env: no memory for variable assignment"); 2282147072Sbrooks 2283147072Sbrooks /* No `` or $() command substitution allowed in environment values! */ 2284147072Sbrooks for (j=0; j < strlen(value); j++) 2285147072Sbrooks switch (value[j]) { 2286147072Sbrooks case '`': 2287147072Sbrooks case '$': 2288147072Sbrooks error("illegal character (%c) in value '%s'", value[j], 2289147072Sbrooks value); 2290147072Sbrooks /* not reached */ 2291147072Sbrooks } 2292147072Sbrooks snprintf(client->scriptEnv[i], strlen(prefix) + strlen(name) + 2293147072Sbrooks 1 + strlen(value) + 1, "%s%s=%s", prefix, name, value); 2294147072Sbrooks} 2295147072Sbrooks 2296147072Sbrooksvoid 2297147072Sbrooksscript_flush_env(struct client_state *client) 2298147072Sbrooks{ 2299147072Sbrooks int i; 2300147072Sbrooks 2301147072Sbrooks for (i = 0; client->scriptEnv[i]; i++) { 2302147072Sbrooks free(client->scriptEnv[i]); 2303147072Sbrooks client->scriptEnv[i] = NULL; 2304147072Sbrooks } 2305147072Sbrooks client->scriptEnvsize = 0; 2306147072Sbrooks} 2307147072Sbrooks 2308147072Sbrooksint 2309147072Sbrooksdhcp_option_ev_name(char *buf, size_t buflen, struct option *option) 2310147072Sbrooks{ 2311147072Sbrooks int i; 2312147072Sbrooks 2313147072Sbrooks for (i = 0; option->name[i]; i++) { 2314147072Sbrooks if (i + 1 == buflen) 2315147072Sbrooks return 0; 2316147072Sbrooks if (option->name[i] == '-') 2317147072Sbrooks buf[i] = '_'; 2318147072Sbrooks else 2319147072Sbrooks buf[i] = option->name[i]; 2320147072Sbrooks } 2321147072Sbrooks 2322147072Sbrooks buf[i] = 0; 2323147072Sbrooks return 1; 2324147072Sbrooks} 2325147072Sbrooks 2326147072Sbrooksvoid 2327147072Sbrooksgo_daemon(void) 2328147072Sbrooks{ 2329147072Sbrooks static int state = 0; 2330147072Sbrooks 2331147072Sbrooks if (no_daemon || state) 2332147072Sbrooks return; 2333147072Sbrooks 2334147072Sbrooks state = 1; 2335147072Sbrooks 2336147072Sbrooks /* Stop logging to stderr... */ 2337147072Sbrooks log_perror = 0; 2338147072Sbrooks 2339147072Sbrooks if (daemon(1, 0) == -1) 2340147072Sbrooks error("daemon"); 2341147072Sbrooks 2342231277Sbapt if (pidfile != NULL) 2343231277Sbapt pidfile_write(pidfile); 2344231277Sbapt 2345147072Sbrooks /* we are chrooted, daemon(3) fails to open /dev/null */ 2346147072Sbrooks if (nullfd != -1) { 2347147072Sbrooks dup2(nullfd, STDIN_FILENO); 2348147072Sbrooks dup2(nullfd, STDOUT_FILENO); 2349147072Sbrooks dup2(nullfd, STDERR_FILENO); 2350147072Sbrooks close(nullfd); 2351147072Sbrooks nullfd = -1; 2352147072Sbrooks } 2353147072Sbrooks} 2354147072Sbrooks 2355147072Sbrooksint 2356147072Sbrookscheck_option(struct client_lease *l, int option) 2357147072Sbrooks{ 2358147072Sbrooks char *opbuf; 2359147072Sbrooks char *sbuf; 2360147072Sbrooks 2361147072Sbrooks /* we use this, since this is what gets passed to dhclient-script */ 2362147072Sbrooks 2363147072Sbrooks opbuf = pretty_print_option(option, l->options[option].data, 2364147072Sbrooks l->options[option].len, 0, 0); 2365147072Sbrooks 2366147072Sbrooks sbuf = option_as_string(option, l->options[option].data, 2367147072Sbrooks l->options[option].len); 2368147072Sbrooks 2369147072Sbrooks switch (option) { 2370147072Sbrooks case DHO_SUBNET_MASK: 2371147072Sbrooks case DHO_TIME_SERVERS: 2372147072Sbrooks case DHO_NAME_SERVERS: 2373147072Sbrooks case DHO_ROUTERS: 2374147072Sbrooks case DHO_DOMAIN_NAME_SERVERS: 2375147072Sbrooks case DHO_LOG_SERVERS: 2376147072Sbrooks case DHO_COOKIE_SERVERS: 2377147072Sbrooks case DHO_LPR_SERVERS: 2378147072Sbrooks case DHO_IMPRESS_SERVERS: 2379147072Sbrooks case DHO_RESOURCE_LOCATION_SERVERS: 2380147072Sbrooks case DHO_SWAP_SERVER: 2381147072Sbrooks case DHO_BROADCAST_ADDRESS: 2382147072Sbrooks case DHO_NIS_SERVERS: 2383147072Sbrooks case DHO_NTP_SERVERS: 2384147072Sbrooks case DHO_NETBIOS_NAME_SERVERS: 2385147072Sbrooks case DHO_NETBIOS_DD_SERVER: 2386147072Sbrooks case DHO_FONT_SERVERS: 2387147072Sbrooks case DHO_DHCP_SERVER_IDENTIFIER: 2388183974Sbrooks case DHO_NISPLUS_SERVERS: 2389183974Sbrooks case DHO_MOBILE_IP_HOME_AGENT: 2390147689Sbrooks case DHO_SMTP_SERVER: 2391147689Sbrooks case DHO_POP_SERVER: 2392147689Sbrooks case DHO_NNTP_SERVER: 2393147689Sbrooks case DHO_WWW_SERVER: 2394147689Sbrooks case DHO_FINGER_SERVER: 2395147689Sbrooks case DHO_IRC_SERVER: 2396183974Sbrooks case DHO_STREETTALK_SERVER: 2397183974Sbrooks case DHO_STREETTALK_DA_SERVER: 2398147072Sbrooks if (!ipv4addrs(opbuf)) { 2399147072Sbrooks warning("Invalid IP address in option: %s", opbuf); 2400147072Sbrooks return (0); 2401147072Sbrooks } 2402147072Sbrooks return (1) ; 2403147072Sbrooks case DHO_HOST_NAME: 2404147072Sbrooks case DHO_NIS_DOMAIN: 2405183974Sbrooks case DHO_NISPLUS_DOMAIN: 2406183974Sbrooks case DHO_TFTP_SERVER_NAME: 2407147072Sbrooks if (!res_hnok(sbuf)) { 2408147072Sbrooks warning("Bogus Host Name option %d: %s (%s)", option, 2409147072Sbrooks sbuf, opbuf); 2410153287Sbrooks l->options[option].len = 0; 2411153287Sbrooks free(l->options[option].data); 2412147072Sbrooks } 2413147072Sbrooks return (1); 2414147686Sbrooks case DHO_DOMAIN_NAME: 2415230597Sdumbbell case DHO_DOMAIN_SEARCH: 2416149639Sbrooks if (!res_hnok(sbuf)) { 2417149639Sbrooks if (!check_search(sbuf)) { 2418149639Sbrooks warning("Bogus domain search list %d: %s (%s)", 2419149639Sbrooks option, sbuf, opbuf); 2420153287Sbrooks l->options[option].len = 0; 2421153287Sbrooks free(l->options[option].data); 2422149639Sbrooks } 2423149639Sbrooks } 2424149639Sbrooks return (1); 2425147072Sbrooks case DHO_PAD: 2426147072Sbrooks case DHO_TIME_OFFSET: 2427147072Sbrooks case DHO_BOOT_SIZE: 2428147072Sbrooks case DHO_MERIT_DUMP: 2429147072Sbrooks case DHO_ROOT_PATH: 2430147072Sbrooks case DHO_EXTENSIONS_PATH: 2431147072Sbrooks case DHO_IP_FORWARDING: 2432147072Sbrooks case DHO_NON_LOCAL_SOURCE_ROUTING: 2433147072Sbrooks case DHO_POLICY_FILTER: 2434147072Sbrooks case DHO_MAX_DGRAM_REASSEMBLY: 2435147072Sbrooks case DHO_DEFAULT_IP_TTL: 2436147072Sbrooks case DHO_PATH_MTU_AGING_TIMEOUT: 2437147072Sbrooks case DHO_PATH_MTU_PLATEAU_TABLE: 2438147072Sbrooks case DHO_INTERFACE_MTU: 2439147072Sbrooks case DHO_ALL_SUBNETS_LOCAL: 2440147072Sbrooks case DHO_PERFORM_MASK_DISCOVERY: 2441147072Sbrooks case DHO_MASK_SUPPLIER: 2442147072Sbrooks case DHO_ROUTER_DISCOVERY: 2443147072Sbrooks case DHO_ROUTER_SOLICITATION_ADDRESS: 2444147072Sbrooks case DHO_STATIC_ROUTES: 2445147072Sbrooks case DHO_TRAILER_ENCAPSULATION: 2446147072Sbrooks case DHO_ARP_CACHE_TIMEOUT: 2447147072Sbrooks case DHO_IEEE802_3_ENCAPSULATION: 2448147072Sbrooks case DHO_DEFAULT_TCP_TTL: 2449147072Sbrooks case DHO_TCP_KEEPALIVE_INTERVAL: 2450147072Sbrooks case DHO_TCP_KEEPALIVE_GARBAGE: 2451147072Sbrooks case DHO_VENDOR_ENCAPSULATED_OPTIONS: 2452147072Sbrooks case DHO_NETBIOS_NODE_TYPE: 2453147072Sbrooks case DHO_NETBIOS_SCOPE: 2454147072Sbrooks case DHO_X_DISPLAY_MANAGER: 2455147072Sbrooks case DHO_DHCP_REQUESTED_ADDRESS: 2456147072Sbrooks case DHO_DHCP_LEASE_TIME: 2457147072Sbrooks case DHO_DHCP_OPTION_OVERLOAD: 2458147072Sbrooks case DHO_DHCP_MESSAGE_TYPE: 2459147072Sbrooks case DHO_DHCP_PARAMETER_REQUEST_LIST: 2460147072Sbrooks case DHO_DHCP_MESSAGE: 2461147072Sbrooks case DHO_DHCP_MAX_MESSAGE_SIZE: 2462147072Sbrooks case DHO_DHCP_RENEWAL_TIME: 2463147072Sbrooks case DHO_DHCP_REBINDING_TIME: 2464147072Sbrooks case DHO_DHCP_CLASS_IDENTIFIER: 2465147072Sbrooks case DHO_DHCP_CLIENT_IDENTIFIER: 2466183974Sbrooks case DHO_BOOTFILE_NAME: 2467147072Sbrooks case DHO_DHCP_USER_CLASS_ID: 2468147072Sbrooks case DHO_END: 2469147072Sbrooks return (1); 2470166602Semaste case DHO_CLASSLESS_ROUTES: 2471166602Semaste return (check_classless_option(l->options[option].data, 2472166602Semaste l->options[option].len)); 2473147072Sbrooks default: 2474147072Sbrooks warning("unknown dhcp option value 0x%x", option); 2475147072Sbrooks return (unknown_ok); 2476147072Sbrooks } 2477147072Sbrooks} 2478147072Sbrooks 2479166602Semaste/* RFC 3442 The Classless Static Routes option checks */ 2480147072Sbrooksint 2481166602Semastecheck_classless_option(unsigned char *data, int len) 2482166602Semaste{ 2483166602Semaste int i = 0; 2484166602Semaste unsigned char width; 2485166602Semaste in_addr_t addr, mask; 2486166602Semaste 2487166602Semaste if (len < 5) { 2488166602Semaste warning("Too small length: %d", len); 2489166602Semaste return (0); 2490166602Semaste } 2491166602Semaste while(i < len) { 2492166602Semaste width = data[i++]; 2493166602Semaste if (width == 0) { 2494166602Semaste i += 4; 2495166602Semaste continue; 2496166602Semaste } else if (width < 9) { 2497166602Semaste addr = (in_addr_t)(data[i] << 24); 2498166602Semaste i += 1; 2499166602Semaste } else if (width < 17) { 2500166602Semaste addr = (in_addr_t)(data[i] << 24) + 2501166602Semaste (in_addr_t)(data[i + 1] << 16); 2502166602Semaste i += 2; 2503166602Semaste } else if (width < 25) { 2504166602Semaste addr = (in_addr_t)(data[i] << 24) + 2505166602Semaste (in_addr_t)(data[i + 1] << 16) + 2506166602Semaste (in_addr_t)(data[i + 2] << 8); 2507166602Semaste i += 3; 2508166602Semaste } else if (width < 33) { 2509166602Semaste addr = (in_addr_t)(data[i] << 24) + 2510166602Semaste (in_addr_t)(data[i + 1] << 16) + 2511166602Semaste (in_addr_t)(data[i + 2] << 8) + 2512166602Semaste data[i + 3]; 2513166602Semaste i += 4; 2514166602Semaste } else { 2515166602Semaste warning("Incorrect subnet width: %d", width); 2516166602Semaste return (0); 2517166602Semaste } 2518166602Semaste mask = (in_addr_t)(~0) << (32 - width); 2519166602Semaste addr = ntohl(addr); 2520166602Semaste mask = ntohl(mask); 2521166602Semaste 2522166602Semaste /* 2523166602Semaste * From RFC 3442: 2524166602Semaste * ... After deriving a subnet number and subnet mask 2525166602Semaste * from each destination descriptor, the DHCP client 2526166602Semaste * MUST zero any bits in the subnet number where the 2527166602Semaste * corresponding bit in the mask is zero... 2528166602Semaste */ 2529166602Semaste if ((addr & mask) != addr) { 2530166602Semaste addr &= mask; 2531166602Semaste data[i - 1] = (unsigned char)( 2532166602Semaste (addr >> (((32 - width)/8)*8)) & 0xFF); 2533166602Semaste } 2534166602Semaste i += 4; 2535166602Semaste } 2536166602Semaste if (i > len) { 2537166602Semaste warning("Incorrect data length: %d (must be %d)", len, i); 2538166602Semaste return (0); 2539166602Semaste } 2540166602Semaste return (1); 2541166602Semaste} 2542166602Semaste 2543166602Semasteint 2544147072Sbrooksres_hnok(const char *dn) 2545147072Sbrooks{ 2546147072Sbrooks int pch = PERIOD, ch = *dn++; 2547147072Sbrooks 2548147072Sbrooks while (ch != '\0') { 2549147072Sbrooks int nch = *dn++; 2550147072Sbrooks 2551147072Sbrooks if (periodchar(ch)) { 2552147072Sbrooks ; 2553147072Sbrooks } else if (periodchar(pch)) { 2554147072Sbrooks if (!borderchar(ch)) 2555147072Sbrooks return (0); 2556147072Sbrooks } else if (periodchar(nch) || nch == '\0') { 2557147072Sbrooks if (!borderchar(ch)) 2558147072Sbrooks return (0); 2559147072Sbrooks } else { 2560147072Sbrooks if (!middlechar(ch)) 2561147072Sbrooks return (0); 2562147072Sbrooks } 2563147072Sbrooks pch = ch, ch = nch; 2564147072Sbrooks } 2565147072Sbrooks return (1); 2566147072Sbrooks} 2567147072Sbrooks 2568149639Sbrooksint 2569149639Sbrookscheck_search(const char *srch) 2570149639Sbrooks{ 2571149639Sbrooks int pch = PERIOD, ch = *srch++; 2572149639Sbrooks int domains = 1; 2573149639Sbrooks 2574149639Sbrooks /* 256 char limit re resolv.conf(5) */ 2575149639Sbrooks if (strlen(srch) > 256) 2576149639Sbrooks return (0); 2577149639Sbrooks 2578149639Sbrooks while (whitechar(ch)) 2579149639Sbrooks ch = *srch++; 2580149639Sbrooks 2581149639Sbrooks while (ch != '\0') { 2582149639Sbrooks int nch = *srch++; 2583149639Sbrooks 2584149639Sbrooks if (periodchar(ch) || whitechar(ch)) { 2585149639Sbrooks ; 2586149639Sbrooks } else if (periodchar(pch)) { 2587149639Sbrooks if (!borderchar(ch)) 2588149639Sbrooks return (0); 2589149639Sbrooks } else if (periodchar(nch) || nch == '\0') { 2590149639Sbrooks if (!borderchar(ch)) 2591149639Sbrooks return (0); 2592149639Sbrooks } else { 2593149639Sbrooks if (!middlechar(ch)) 2594149639Sbrooks return (0); 2595149639Sbrooks } 2596149639Sbrooks if (!whitechar(ch)) { 2597149639Sbrooks pch = ch; 2598149639Sbrooks } else { 2599149639Sbrooks while (whitechar(nch)) { 2600149639Sbrooks nch = *srch++; 2601149639Sbrooks } 2602149639Sbrooks if (nch != '\0') 2603149639Sbrooks domains++; 2604149639Sbrooks pch = PERIOD; 2605149639Sbrooks } 2606149639Sbrooks ch = nch; 2607149639Sbrooks } 2608149639Sbrooks /* 6 domain limit re resolv.conf(5) */ 2609149639Sbrooks if (domains > 6) 2610149639Sbrooks return (0); 2611149639Sbrooks return (1); 2612149639Sbrooks} 2613149639Sbrooks 2614147072Sbrooks/* Does buf consist only of dotted decimal ipv4 addrs? 2615147072Sbrooks * return how many if so, 2616147072Sbrooks * otherwise, return 0 2617147072Sbrooks */ 2618147072Sbrooksint 2619147072Sbrooksipv4addrs(char * buf) 2620147072Sbrooks{ 2621147072Sbrooks struct in_addr jnk; 2622147072Sbrooks int count = 0; 2623147072Sbrooks 2624147072Sbrooks while (inet_aton(buf, &jnk) == 1){ 2625147072Sbrooks count++; 2626147072Sbrooks while (periodchar(*buf) || digitchar(*buf)) 2627147072Sbrooks buf++; 2628147072Sbrooks if (*buf == '\0') 2629147072Sbrooks return (count); 2630147072Sbrooks while (*buf == ' ') 2631147072Sbrooks buf++; 2632147072Sbrooks } 2633147072Sbrooks return (0); 2634147072Sbrooks} 2635147072Sbrooks 2636147072Sbrooks 2637147072Sbrookschar * 2638147072Sbrooksoption_as_string(unsigned int code, unsigned char *data, int len) 2639147072Sbrooks{ 2640147072Sbrooks static char optbuf[32768]; /* XXX */ 2641147072Sbrooks char *op = optbuf; 2642147072Sbrooks int opleft = sizeof(optbuf); 2643147072Sbrooks unsigned char *dp = data; 2644147072Sbrooks 2645147072Sbrooks if (code > 255) 2646147072Sbrooks error("option_as_string: bad code %d", code); 2647147072Sbrooks 2648147072Sbrooks for (; dp < data + len; dp++) { 2649147072Sbrooks if (!isascii(*dp) || !isprint(*dp)) { 2650147072Sbrooks if (dp + 1 != data + len || *dp != 0) { 2651147072Sbrooks snprintf(op, opleft, "\\%03o", *dp); 2652147072Sbrooks op += 4; 2653147072Sbrooks opleft -= 4; 2654147072Sbrooks } 2655147072Sbrooks } else if (*dp == '"' || *dp == '\'' || *dp == '$' || 2656147072Sbrooks *dp == '`' || *dp == '\\') { 2657147072Sbrooks *op++ = '\\'; 2658147072Sbrooks *op++ = *dp; 2659147072Sbrooks opleft -= 2; 2660147072Sbrooks } else { 2661147072Sbrooks *op++ = *dp; 2662147072Sbrooks opleft--; 2663147072Sbrooks } 2664147072Sbrooks } 2665147072Sbrooks if (opleft < 1) 2666147072Sbrooks goto toobig; 2667147072Sbrooks *op = 0; 2668147072Sbrooks return optbuf; 2669147072Sbrookstoobig: 2670147072Sbrooks warning("dhcp option too large"); 2671147072Sbrooks return "<error>"; 2672147072Sbrooks} 2673147072Sbrooks 2674147072Sbrooksint 2675147072Sbrooksfork_privchld(int fd, int fd2) 2676147072Sbrooks{ 2677147072Sbrooks struct pollfd pfd[1]; 2678147072Sbrooks int nfds; 2679147072Sbrooks 2680147072Sbrooks switch (fork()) { 2681147072Sbrooks case -1: 2682147072Sbrooks error("cannot fork"); 2683147072Sbrooks case 0: 2684147072Sbrooks break; 2685147072Sbrooks default: 2686147072Sbrooks return (0); 2687147072Sbrooks } 2688147072Sbrooks 2689147072Sbrooks setproctitle("%s [priv]", ifi->name); 2690147072Sbrooks 2691180130Sed setsid(); 2692147072Sbrooks dup2(nullfd, STDIN_FILENO); 2693147072Sbrooks dup2(nullfd, STDOUT_FILENO); 2694147072Sbrooks dup2(nullfd, STDERR_FILENO); 2695147072Sbrooks close(nullfd); 2696147072Sbrooks close(fd2); 2697147072Sbrooks 2698147072Sbrooks for (;;) { 2699147072Sbrooks pfd[0].fd = fd; 2700147072Sbrooks pfd[0].events = POLLIN; 2701147072Sbrooks if ((nfds = poll(pfd, 1, INFTIM)) == -1) 2702147072Sbrooks if (errno != EINTR) 2703147072Sbrooks error("poll error"); 2704147072Sbrooks 2705147072Sbrooks if (nfds == 0 || !(pfd[0].revents & POLLIN)) 2706147072Sbrooks continue; 2707147072Sbrooks 2708147072Sbrooks dispatch_imsg(fd); 2709147072Sbrooks } 2710147072Sbrooks} 2711