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: stable/10/sbin/dhclient/dhclient.c 330693 2018-03-09 14:39:28Z dab $"); 58149399Sbrooks 59280250Srwatson#include <sys/capsicum.h> 60255219Spjd 61147072Sbrooks#include "dhcpd.h" 62147072Sbrooks#include "privsep.h" 63147072Sbrooks 64280250Srwatson#include <sys/capsicum.h> 65252629Spjd 66147085Sbrooks#include <net80211/ieee80211_freebsd.h> 67147085Sbrooks 68147085Sbrooks#ifndef _PATH_VAREMPTY 69147085Sbrooks#define _PATH_VAREMPTY "/var/empty" 70147085Sbrooks#endif 71147085Sbrooks 72147072Sbrooks#define PERIOD 0x2e 73147072Sbrooks#define hyphenchar(c) ((c) == 0x2d) 74147072Sbrooks#define bslashchar(c) ((c) == 0x5c) 75147072Sbrooks#define periodchar(c) ((c) == PERIOD) 76147072Sbrooks#define asterchar(c) ((c) == 0x2a) 77147072Sbrooks#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) || \ 78147072Sbrooks ((c) >= 0x61 && (c) <= 0x7a)) 79147072Sbrooks#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) 80149639Sbrooks#define whitechar(c) ((c) == ' ' || (c) == '\t') 81147072Sbrooks 82147072Sbrooks#define borderchar(c) (alphachar(c) || digitchar(c)) 83147072Sbrooks#define middlechar(c) (borderchar(c) || hyphenchar(c)) 84147072Sbrooks#define domainchar(c) ((c) > 0x20 && (c) < 0x7f) 85147072Sbrooks 86147072Sbrooks#define CLIENT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin" 87147072Sbrooks 88147072Sbrookstime_t cur_time; 89147072Sbrookstime_t default_lease_time = 43200; /* 12 hours... */ 90147072Sbrooks 91147072Sbrookschar *path_dhclient_conf = _PATH_DHCLIENT_CONF; 92147072Sbrookschar *path_dhclient_db = NULL; 93147072Sbrooks 94147072Sbrooksint log_perror = 1; 95147072Sbrooksint privfd; 96147072Sbrooksint nullfd = -1; 97147072Sbrooks 98252623Spjdchar hostname[_POSIX_HOST_NAME_MAX + 1]; 99252623Spjd 100147072Sbrooksstruct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } }; 101252616Spjdstruct in_addr inaddr_any, inaddr_broadcast; 102147072Sbrooks 103226345Sdeschar *path_dhclient_pidfile; 104226345Sdesstruct pidfh *pidfile; 105226345Sdes 106147072Sbrooks/* 107147072Sbrooks * ASSERT_STATE() does nothing now; it used to be 108147072Sbrooks * assert (state_is == state_shouldbe). 109147072Sbrooks */ 110147072Sbrooks#define ASSERT_STATE(state_is, state_shouldbe) {} 111147072Sbrooks 112147072Sbrooks#define TIME_MAX 2147483647 113147072Sbrooks 114147072Sbrooksint log_priority; 115147072Sbrooksint no_daemon; 116147072Sbrooksint unknown_ok = 1; 117147072Sbrooksint routefd; 118147072Sbrooks 119147072Sbrooksstruct interface_info *ifi; 120147072Sbrooks 121147072Sbrooksint findproto(char *, int); 122147072Sbrooksstruct sockaddr *get_ifa(char *, int); 123147072Sbrooksvoid routehandler(struct protocol *); 124147072Sbrooksvoid usage(void); 125147072Sbrooksint check_option(struct client_lease *l, int option); 126166602Semasteint check_classless_option(unsigned char *data, int len); 127147072Sbrooksint ipv4addrs(char * buf); 128147072Sbrooksint res_hnok(const char *dn); 129149639Sbrooksint check_search(const char *srch); 130147072Sbrookschar *option_as_string(unsigned int code, unsigned char *data, int len); 131147072Sbrooksint fork_privchld(int, int); 132147072Sbrooks 133147072Sbrooks#define ROUNDUP(a) \ 134147072Sbrooks ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 135147072Sbrooks#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 136147072Sbrooks 137209756Sbrianstatic time_t scripttime; 138147072Sbrooks 139147072Sbrooksint 140147072Sbrooksfindproto(char *cp, int n) 141147072Sbrooks{ 142147072Sbrooks struct sockaddr *sa; 143147072Sbrooks int i; 144147072Sbrooks 145147072Sbrooks if (n == 0) 146147072Sbrooks return -1; 147147072Sbrooks for (i = 1; i; i <<= 1) { 148147072Sbrooks if (i & n) { 149147072Sbrooks sa = (struct sockaddr *)cp; 150147072Sbrooks switch (i) { 151147072Sbrooks case RTA_IFA: 152147072Sbrooks case RTA_DST: 153147072Sbrooks case RTA_GATEWAY: 154147072Sbrooks case RTA_NETMASK: 155147072Sbrooks if (sa->sa_family == AF_INET) 156147072Sbrooks return AF_INET; 157147072Sbrooks if (sa->sa_family == AF_INET6) 158147072Sbrooks return AF_INET6; 159147072Sbrooks break; 160147072Sbrooks case RTA_IFP: 161147072Sbrooks break; 162147072Sbrooks } 163147072Sbrooks ADVANCE(cp, sa); 164147072Sbrooks } 165147072Sbrooks } 166147072Sbrooks return (-1); 167147072Sbrooks} 168147072Sbrooks 169147072Sbrooksstruct sockaddr * 170147072Sbrooksget_ifa(char *cp, int n) 171147072Sbrooks{ 172147072Sbrooks struct sockaddr *sa; 173147072Sbrooks int i; 174147072Sbrooks 175147072Sbrooks if (n == 0) 176147072Sbrooks return (NULL); 177147072Sbrooks for (i = 1; i; i <<= 1) 178147072Sbrooks if (i & n) { 179147072Sbrooks sa = (struct sockaddr *)cp; 180147072Sbrooks if (i == RTA_IFA) 181147072Sbrooks return (sa); 182147072Sbrooks ADVANCE(cp, sa); 183147072Sbrooks } 184147072Sbrooks 185147072Sbrooks return (NULL); 186147072Sbrooks} 187177501Ssam 188147072Sbrooksstruct iaddr defaddr = { 4 }; 189177501Ssamuint8_t curbssid[6]; 190147072Sbrooks 191177501Ssamstatic void 192177501Ssamdisassoc(void *arg) 193177501Ssam{ 194177501Ssam struct interface_info *ifi = arg; 195177501Ssam 196177501Ssam /* 197177501Ssam * Clear existing state. 198177501Ssam */ 199177501Ssam if (ifi->client->active != NULL) { 200177501Ssam script_init("EXPIRE", NULL); 201177501Ssam script_write_params("old_", 202177501Ssam ifi->client->active); 203177501Ssam if (ifi->client->alias) 204177501Ssam script_write_params("alias_", 205177501Ssam ifi->client->alias); 206177501Ssam script_go(); 207177501Ssam } 208177501Ssam ifi->client->state = S_INIT; 209177501Ssam} 210177501Ssam 211147072Sbrooks/* ARGSUSED */ 212147072Sbrooksvoid 213147072Sbrooksroutehandler(struct protocol *p) 214147072Sbrooks{ 215209756Sbrian char msg[2048], *addr; 216147072Sbrooks struct rt_msghdr *rtm; 217147072Sbrooks struct if_msghdr *ifm; 218147072Sbrooks struct ifa_msghdr *ifam; 219147072Sbrooks struct if_announcemsghdr *ifan; 220177501Ssam struct ieee80211_join_event *jev; 221147072Sbrooks struct client_lease *l; 222147072Sbrooks time_t t = time(NULL); 223147072Sbrooks struct sockaddr *sa; 224147072Sbrooks struct iaddr a; 225147072Sbrooks ssize_t n; 226239564Sjhb int linkstat; 227147072Sbrooks 228147072Sbrooks n = read(routefd, &msg, sizeof(msg)); 229147072Sbrooks rtm = (struct rt_msghdr *)msg; 230147072Sbrooks if (n < sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen || 231147072Sbrooks rtm->rtm_version != RTM_VERSION) 232147072Sbrooks return; 233147072Sbrooks 234147072Sbrooks switch (rtm->rtm_type) { 235147072Sbrooks case RTM_NEWADDR: 236154161Sbrooks case RTM_DELADDR: 237147072Sbrooks ifam = (struct ifa_msghdr *)rtm; 238154161Sbrooks 239147072Sbrooks if (ifam->ifam_index != ifi->index) 240147072Sbrooks break; 241147072Sbrooks if (findproto((char *)(ifam + 1), ifam->ifam_addrs) != AF_INET) 242147072Sbrooks break; 243154161Sbrooks if (scripttime == 0 || t < scripttime + 10) 244154161Sbrooks break; 245154161Sbrooks 246147072Sbrooks sa = get_ifa((char *)(ifam + 1), ifam->ifam_addrs); 247147072Sbrooks if (sa == NULL) 248209756Sbrian break; 249147072Sbrooks 250147072Sbrooks if ((a.len = sizeof(struct in_addr)) > sizeof(a.iabuf)) 251147072Sbrooks error("king bula sez: len mismatch"); 252147072Sbrooks memcpy(a.iabuf, &((struct sockaddr_in *)sa)->sin_addr, a.len); 253147072Sbrooks if (addr_eq(a, defaddr)) 254147072Sbrooks break; 255147072Sbrooks 256147072Sbrooks for (l = ifi->client->active; l != NULL; l = l->next) 257147072Sbrooks if (addr_eq(a, l->address)) 258147072Sbrooks break; 259147072Sbrooks 260209756Sbrian if (l == NULL) /* added/deleted addr is not the one we set */ 261147072Sbrooks break; 262209756Sbrian 263209756Sbrian addr = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr); 264209756Sbrian if (rtm->rtm_type == RTM_NEWADDR) { 265209756Sbrian /* 266209756Sbrian * XXX: If someone other than us adds our address, 267209756Sbrian * should we assume they are taking over from us, 268209756Sbrian * delete the lease record, and exit without modifying 269209756Sbrian * the interface? 270209756Sbrian */ 271209756Sbrian warning("My address (%s) was re-added", addr); 272209756Sbrian } else { 273209756Sbrian warning("My address (%s) was deleted, dhclient exiting", 274209756Sbrian addr); 275209756Sbrian goto die; 276209756Sbrian } 277209756Sbrian break; 278147072Sbrooks case RTM_IFINFO: 279147072Sbrooks ifm = (struct if_msghdr *)rtm; 280147072Sbrooks if (ifm->ifm_index != ifi->index) 281147072Sbrooks break; 282209756Sbrian if ((rtm->rtm_flags & RTF_UP) == 0) { 283209756Sbrian warning("Interface %s is down, dhclient exiting", 284209756Sbrian ifi->name); 285147072Sbrooks goto die; 286209756Sbrian } 287239564Sjhb linkstat = interface_link_status(ifi->name); 288239564Sjhb if (linkstat != ifi->linkstat) { 289239564Sjhb debug("%s link state %s -> %s", ifi->name, 290239564Sjhb ifi->linkstat ? "up" : "down", 291239564Sjhb linkstat ? "up" : "down"); 292239564Sjhb ifi->linkstat = linkstat; 293239564Sjhb if (linkstat) 294239564Sjhb state_reboot(ifi); 295239356Sjhb } 296147072Sbrooks break; 297147072Sbrooks case RTM_IFANNOUNCE: 298147072Sbrooks ifan = (struct if_announcemsghdr *)rtm; 299147072Sbrooks if (ifan->ifan_what == IFAN_DEPARTURE && 300209756Sbrian ifan->ifan_index == ifi->index) { 301209756Sbrian warning("Interface %s is gone, dhclient exiting", 302209756Sbrian ifi->name); 303147072Sbrooks goto die; 304209756Sbrian } 305147072Sbrooks break; 306147085Sbrooks case RTM_IEEE80211: 307147085Sbrooks ifan = (struct if_announcemsghdr *)rtm; 308147085Sbrooks if (ifan->ifan_index != ifi->index) 309147085Sbrooks break; 310147085Sbrooks switch (ifan->ifan_what) { 311147085Sbrooks case RTM_IEEE80211_ASSOC: 312148373Ssam case RTM_IEEE80211_REASSOC: 313147085Sbrooks /* 314177501Ssam * Use assoc/reassoc event to kick state machine 315177501Ssam * in case we roam. Otherwise fall back to the 316177501Ssam * normal state machine just like a wired network. 317147085Sbrooks */ 318177501Ssam jev = (struct ieee80211_join_event *) &ifan[1]; 319177501Ssam if (memcmp(curbssid, jev->iev_addr, 6)) { 320177501Ssam disassoc(ifi); 321177501Ssam state_reboot(ifi); 322147351Sbrooks } 323177501Ssam memcpy(curbssid, jev->iev_addr, 6); 324147085Sbrooks break; 325147085Sbrooks } 326147085Sbrooks break; 327147072Sbrooks default: 328147072Sbrooks break; 329147072Sbrooks } 330147072Sbrooks return; 331147072Sbrooks 332147072Sbrooksdie: 333147072Sbrooks script_init("FAIL", NULL); 334147072Sbrooks if (ifi->client->alias) 335147072Sbrooks script_write_params("alias_", ifi->client->alias); 336147072Sbrooks script_go(); 337226345Sdes if (pidfile != NULL) 338226345Sdes pidfile_remove(pidfile); 339147072Sbrooks exit(1); 340147072Sbrooks} 341147072Sbrooks 342147072Sbrooksint 343147072Sbrooksmain(int argc, char *argv[]) 344147072Sbrooks{ 345147072Sbrooks extern char *__progname; 346147072Sbrooks int ch, fd, quiet = 0, i = 0; 347147072Sbrooks int pipe_fd[2]; 348147085Sbrooks int immediate_daemon = 0; 349147072Sbrooks struct passwd *pw; 350226345Sdes pid_t otherpid; 351255219Spjd cap_rights_t rights; 352147072Sbrooks 353147072Sbrooks /* Initially, log errors to stderr as well as to syslogd. */ 354147072Sbrooks openlog(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY); 355177500Ssam setlogmask(LOG_UPTO(LOG_DEBUG)); 356147072Sbrooks 357226345Sdes while ((ch = getopt(argc, argv, "bc:dl:p:qu")) != -1) 358147072Sbrooks switch (ch) { 359147085Sbrooks case 'b': 360147085Sbrooks immediate_daemon = 1; 361147085Sbrooks break; 362147072Sbrooks case 'c': 363147072Sbrooks path_dhclient_conf = optarg; 364147072Sbrooks break; 365147072Sbrooks case 'd': 366147072Sbrooks no_daemon = 1; 367147072Sbrooks break; 368147072Sbrooks case 'l': 369147072Sbrooks path_dhclient_db = optarg; 370147072Sbrooks break; 371226345Sdes case 'p': 372226345Sdes path_dhclient_pidfile = optarg; 373226345Sdes break; 374147072Sbrooks case 'q': 375147072Sbrooks quiet = 1; 376147072Sbrooks break; 377147072Sbrooks case 'u': 378147072Sbrooks unknown_ok = 0; 379147072Sbrooks break; 380147072Sbrooks default: 381147072Sbrooks usage(); 382147072Sbrooks } 383147072Sbrooks 384147072Sbrooks argc -= optind; 385147072Sbrooks argv += optind; 386147072Sbrooks 387147072Sbrooks if (argc != 1) 388147072Sbrooks usage(); 389147072Sbrooks 390226345Sdes if (path_dhclient_pidfile == NULL) { 391226345Sdes asprintf(&path_dhclient_pidfile, 392226345Sdes "%sdhclient.%s.pid", _PATH_VARRUN, *argv); 393226345Sdes if (path_dhclient_pidfile == NULL) 394226345Sdes error("asprintf"); 395226345Sdes } 396226345Sdes pidfile = pidfile_open(path_dhclient_pidfile, 0600, &otherpid); 397226345Sdes if (pidfile == NULL) { 398226345Sdes if (errno == EEXIST) 399226345Sdes error("dhclient already running, pid: %d.", otherpid); 400226345Sdes if (errno == EAGAIN) 401226345Sdes error("dhclient already running."); 402226345Sdes warning("Cannot open or create pidfile: %m"); 403226345Sdes } 404226345Sdes 405147072Sbrooks if ((ifi = calloc(1, sizeof(struct interface_info))) == NULL) 406147072Sbrooks error("calloc"); 407147072Sbrooks if (strlcpy(ifi->name, argv[0], IFNAMSIZ) >= IFNAMSIZ) 408147072Sbrooks error("Interface name too long"); 409147072Sbrooks if (path_dhclient_db == NULL && asprintf(&path_dhclient_db, "%s.%s", 410147072Sbrooks _PATH_DHCLIENT_DB, ifi->name) == -1) 411147072Sbrooks error("asprintf"); 412147072Sbrooks 413147072Sbrooks if (quiet) 414147072Sbrooks log_perror = 0; 415147072Sbrooks 416147072Sbrooks tzset(); 417147072Sbrooks time(&cur_time); 418147072Sbrooks 419252616Spjd inaddr_broadcast.s_addr = INADDR_BROADCAST; 420147072Sbrooks inaddr_any.s_addr = INADDR_ANY; 421147072Sbrooks 422147072Sbrooks read_client_conf(); 423147072Sbrooks 424226345Sdes /* The next bit is potentially very time-consuming, so write out 425226345Sdes the pidfile right away. We will write it out again with the 426226345Sdes correct pid after daemonizing. */ 427226345Sdes if (pidfile != NULL) 428226345Sdes pidfile_write(pidfile); 429226345Sdes 430147072Sbrooks if (!interface_link_status(ifi->name)) { 431147072Sbrooks fprintf(stderr, "%s: no link ...", ifi->name); 432147072Sbrooks fflush(stderr); 433147072Sbrooks sleep(1); 434147072Sbrooks while (!interface_link_status(ifi->name)) { 435147072Sbrooks fprintf(stderr, "."); 436147072Sbrooks fflush(stderr); 437147072Sbrooks if (++i > 10) { 438161514Sbrian fprintf(stderr, " giving up\n"); 439161514Sbrian exit(1); 440147072Sbrooks } 441147072Sbrooks sleep(1); 442147072Sbrooks } 443161514Sbrian fprintf(stderr, " got link\n"); 444147072Sbrooks } 445239564Sjhb ifi->linkstat = 1; 446147072Sbrooks 447147072Sbrooks if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) 448147072Sbrooks error("cannot open %s: %m", _PATH_DEVNULL); 449147072Sbrooks 450147072Sbrooks if ((pw = getpwnam("_dhcp")) == NULL) { 451147072Sbrooks warning("no such user: _dhcp, falling back to \"nobody\""); 452147072Sbrooks if ((pw = getpwnam("nobody")) == NULL) 453147072Sbrooks error("no such user: nobody"); 454147072Sbrooks } 455147072Sbrooks 456252623Spjd /* 457252623Spjd * Obtain hostname before entering capability mode - it won't be 458252623Spjd * possible then, as reading kern.hostname is not permitted. 459252623Spjd */ 460252623Spjd if (gethostname(hostname, sizeof(hostname)) < 0) 461252623Spjd hostname[0] = '\0'; 462252623Spjd 463252697Spjd priv_script_init("PREINIT", NULL); 464252697Spjd if (ifi->client->alias) 465252697Spjd priv_script_write_params("alias_", ifi->client->alias); 466252697Spjd priv_script_go(); 467252697Spjd 468252626Spjd /* set up the interface */ 469252626Spjd discover_interfaces(ifi); 470252626Spjd 471147072Sbrooks if (pipe(pipe_fd) == -1) 472147072Sbrooks error("pipe"); 473147072Sbrooks 474147072Sbrooks fork_privchld(pipe_fd[0], pipe_fd[1]); 475147072Sbrooks 476252626Spjd close(ifi->ufdesc); 477252626Spjd ifi->ufdesc = -1; 478252626Spjd close(ifi->wfdesc); 479252626Spjd ifi->wfdesc = -1; 480252626Spjd 481147072Sbrooks close(pipe_fd[0]); 482147072Sbrooks privfd = pipe_fd[1]; 483255219Spjd cap_rights_init(&rights, CAP_READ, CAP_WRITE); 484255219Spjd if (cap_rights_limit(privfd, &rights) < 0 && errno != ENOSYS) 485252629Spjd error("can't limit private descriptor: %m"); 486147072Sbrooks 487147072Sbrooks if ((fd = open(path_dhclient_db, O_RDONLY|O_EXLOCK|O_CREAT, 0)) == -1) 488147072Sbrooks error("can't open and lock %s: %m", path_dhclient_db); 489147072Sbrooks read_client_leases(); 490147072Sbrooks rewrite_client_leases(); 491147072Sbrooks close(fd); 492147072Sbrooks 493147072Sbrooks if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) != -1) 494147072Sbrooks add_protocol("AF_ROUTE", routefd, routehandler, ifi); 495252625Spjd if (shutdown(routefd, SHUT_WR) < 0) 496252625Spjd error("can't shutdown route socket: %m"); 497261828Sbrueffer cap_rights_init(&rights, CAP_EVENT, CAP_READ); 498255219Spjd if (cap_rights_limit(routefd, &rights) < 0 && errno != ENOSYS) 499252630Spjd error("can't limit route socket: %m"); 500147072Sbrooks 501147072Sbrooks if (chroot(_PATH_VAREMPTY) == -1) 502147072Sbrooks error("chroot"); 503147072Sbrooks if (chdir("/") == -1) 504147072Sbrooks error("chdir(\"/\")"); 505147072Sbrooks 506147072Sbrooks if (setgroups(1, &pw->pw_gid) || 507147072Sbrooks setegid(pw->pw_gid) || setgid(pw->pw_gid) || 508147072Sbrooks seteuid(pw->pw_uid) || setuid(pw->pw_uid)) 509147072Sbrooks error("can't drop privileges: %m"); 510147072Sbrooks 511147072Sbrooks endpwent(); 512147072Sbrooks 513147072Sbrooks setproctitle("%s", ifi->name); 514147072Sbrooks 515252634Spjd if (cap_enter() < 0 && errno != ENOSYS) 516252634Spjd error("can't enter capability mode: %m"); 517252634Spjd 518147085Sbrooks if (immediate_daemon) 519147085Sbrooks go_daemon(); 520147085Sbrooks 521147072Sbrooks ifi->client->state = S_INIT; 522147072Sbrooks state_reboot(ifi); 523147072Sbrooks 524147072Sbrooks bootp_packet_handler = do_packet; 525147072Sbrooks 526147072Sbrooks dispatch(); 527147072Sbrooks 528147072Sbrooks /* not reached */ 529147072Sbrooks return (0); 530147072Sbrooks} 531147072Sbrooks 532147072Sbrooksvoid 533147072Sbrooksusage(void) 534147072Sbrooks{ 535147072Sbrooks extern char *__progname; 536147072Sbrooks 537161514Sbrian fprintf(stderr, "usage: %s [-bdqu] ", __progname); 538147072Sbrooks fprintf(stderr, "[-c conffile] [-l leasefile] interface\n"); 539147072Sbrooks exit(1); 540147072Sbrooks} 541147072Sbrooks 542147072Sbrooks/* 543147072Sbrooks * Individual States: 544147072Sbrooks * 545147072Sbrooks * Each routine is called from the dhclient_state_machine() in one of 546147072Sbrooks * these conditions: 547147072Sbrooks * -> entering INIT state 548147072Sbrooks * -> recvpacket_flag == 0: timeout in this state 549147072Sbrooks * -> otherwise: received a packet in this state 550147072Sbrooks * 551147072Sbrooks * Return conditions as handled by dhclient_state_machine(): 552147072Sbrooks * Returns 1, sendpacket_flag = 1: send packet, reset timer. 553147072Sbrooks * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone). 554147072Sbrooks * Returns 0: finish the nap which was interrupted for no good reason. 555147072Sbrooks * 556147072Sbrooks * Several per-interface variables are used to keep track of the process: 557147072Sbrooks * active_lease: the lease that is being used on the interface 558147072Sbrooks * (null pointer if not configured yet). 559147072Sbrooks * offered_leases: leases corresponding to DHCPOFFER messages that have 560147072Sbrooks * been sent to us by DHCP servers. 561147072Sbrooks * acked_leases: leases corresponding to DHCPACK messages that have been 562147072Sbrooks * sent to us by DHCP servers. 563147072Sbrooks * sendpacket: DHCP packet we're trying to send. 564147072Sbrooks * destination: IP address to send sendpacket to 565147072Sbrooks * In addition, there are several relevant per-lease variables. 566147072Sbrooks * T1_expiry, T2_expiry, lease_expiry: lease milestones 567147072Sbrooks * In the active lease, these control the process of renewing the lease; 568147072Sbrooks * In leases on the acked_leases list, this simply determines when we 569147072Sbrooks * can no longer legitimately use the lease. 570147072Sbrooks */ 571147072Sbrooks 572147072Sbrooksvoid 573147072Sbrooksstate_reboot(void *ipp) 574147072Sbrooks{ 575147072Sbrooks struct interface_info *ip = ipp; 576147072Sbrooks 577147072Sbrooks /* If we don't remember an active lease, go straight to INIT. */ 578147072Sbrooks if (!ip->client->active || ip->client->active->is_bootp) { 579147072Sbrooks state_init(ip); 580147072Sbrooks return; 581147072Sbrooks } 582147072Sbrooks 583147072Sbrooks /* We are in the rebooting state. */ 584147072Sbrooks ip->client->state = S_REBOOTING; 585147072Sbrooks 586147072Sbrooks /* make_request doesn't initialize xid because it normally comes 587147072Sbrooks from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER, 588147072Sbrooks so pick an xid now. */ 589147072Sbrooks ip->client->xid = arc4random(); 590147072Sbrooks 591147072Sbrooks /* Make a DHCPREQUEST packet, and set appropriate per-interface 592147072Sbrooks flags. */ 593147072Sbrooks make_request(ip, ip->client->active); 594147072Sbrooks ip->client->destination = iaddr_broadcast; 595147072Sbrooks ip->client->first_sending = cur_time; 596147072Sbrooks ip->client->interval = ip->client->config->initial_interval; 597147072Sbrooks 598147072Sbrooks /* Zap the medium list... */ 599147072Sbrooks ip->client->medium = NULL; 600147072Sbrooks 601147072Sbrooks /* Send out the first DHCPREQUEST packet. */ 602147072Sbrooks send_request(ip); 603147072Sbrooks} 604147072Sbrooks 605147072Sbrooks/* 606147072Sbrooks * Called when a lease has completely expired and we've 607147072Sbrooks * been unable to renew it. 608147072Sbrooks */ 609147072Sbrooksvoid 610147072Sbrooksstate_init(void *ipp) 611147072Sbrooks{ 612147072Sbrooks struct interface_info *ip = ipp; 613147072Sbrooks 614147072Sbrooks ASSERT_STATE(state, S_INIT); 615147072Sbrooks 616147072Sbrooks /* Make a DHCPDISCOVER packet, and set appropriate per-interface 617147072Sbrooks flags. */ 618147072Sbrooks make_discover(ip, ip->client->active); 619147072Sbrooks ip->client->xid = ip->client->packet.xid; 620147072Sbrooks ip->client->destination = iaddr_broadcast; 621147072Sbrooks ip->client->state = S_SELECTING; 622147072Sbrooks ip->client->first_sending = cur_time; 623147072Sbrooks ip->client->interval = ip->client->config->initial_interval; 624147072Sbrooks 625147072Sbrooks /* Add an immediate timeout to cause the first DHCPDISCOVER packet 626147072Sbrooks to go out. */ 627147072Sbrooks send_discover(ip); 628147072Sbrooks} 629147072Sbrooks 630147072Sbrooks/* 631147072Sbrooks * state_selecting is called when one or more DHCPOFFER packets 632147072Sbrooks * have been received and a configurable period of time has passed. 633147072Sbrooks */ 634147072Sbrooksvoid 635147072Sbrooksstate_selecting(void *ipp) 636147072Sbrooks{ 637147072Sbrooks struct interface_info *ip = ipp; 638147072Sbrooks struct client_lease *lp, *next, *picked; 639147072Sbrooks 640147072Sbrooks ASSERT_STATE(state, S_SELECTING); 641147072Sbrooks 642147072Sbrooks /* Cancel state_selecting and send_discover timeouts, since either 643147072Sbrooks one could have got us here. */ 644147072Sbrooks cancel_timeout(state_selecting, ip); 645147072Sbrooks cancel_timeout(send_discover, ip); 646147072Sbrooks 647147072Sbrooks /* We have received one or more DHCPOFFER packets. Currently, 648147072Sbrooks the only criterion by which we judge leases is whether or 649147072Sbrooks not we get a response when we arp for them. */ 650147072Sbrooks picked = NULL; 651147072Sbrooks for (lp = ip->client->offered_leases; lp; lp = next) { 652147072Sbrooks next = lp->next; 653147072Sbrooks 654147072Sbrooks /* Check to see if we got an ARPREPLY for the address 655147072Sbrooks in this particular lease. */ 656147072Sbrooks if (!picked) { 657147072Sbrooks script_init("ARPCHECK", lp->medium); 658147072Sbrooks script_write_params("check_", lp); 659147072Sbrooks 660147072Sbrooks /* If the ARPCHECK code detects another 661147072Sbrooks machine using the offered address, it exits 662147072Sbrooks nonzero. We need to send a DHCPDECLINE and 663147072Sbrooks toss the lease. */ 664147072Sbrooks if (script_go()) { 665147072Sbrooks make_decline(ip, lp); 666147072Sbrooks send_decline(ip); 667147072Sbrooks goto freeit; 668147072Sbrooks } 669147072Sbrooks picked = lp; 670147072Sbrooks picked->next = NULL; 671147072Sbrooks } else { 672147072Sbrooksfreeit: 673147072Sbrooks free_client_lease(lp); 674147072Sbrooks } 675147072Sbrooks } 676147072Sbrooks ip->client->offered_leases = NULL; 677147072Sbrooks 678147072Sbrooks /* If we just tossed all the leases we were offered, go back 679147072Sbrooks to square one. */ 680147072Sbrooks if (!picked) { 681147072Sbrooks ip->client->state = S_INIT; 682147072Sbrooks state_init(ip); 683147072Sbrooks return; 684147072Sbrooks } 685147072Sbrooks 686147072Sbrooks /* If it was a BOOTREPLY, we can just take the address right now. */ 687147072Sbrooks if (!picked->options[DHO_DHCP_MESSAGE_TYPE].len) { 688147072Sbrooks ip->client->new = picked; 689147072Sbrooks 690147072Sbrooks /* Make up some lease expiry times 691147072Sbrooks XXX these should be configurable. */ 692147072Sbrooks ip->client->new->expiry = cur_time + 12000; 693147072Sbrooks ip->client->new->renewal += cur_time + 8000; 694147072Sbrooks ip->client->new->rebind += cur_time + 10000; 695147072Sbrooks 696147072Sbrooks ip->client->state = S_REQUESTING; 697147072Sbrooks 698147072Sbrooks /* Bind to the address we received. */ 699147072Sbrooks bind_lease(ip); 700147072Sbrooks return; 701147072Sbrooks } 702147072Sbrooks 703147072Sbrooks /* Go to the REQUESTING state. */ 704147072Sbrooks ip->client->destination = iaddr_broadcast; 705147072Sbrooks ip->client->state = S_REQUESTING; 706147072Sbrooks ip->client->first_sending = cur_time; 707147072Sbrooks ip->client->interval = ip->client->config->initial_interval; 708147072Sbrooks 709147072Sbrooks /* Make a DHCPREQUEST packet from the lease we picked. */ 710147072Sbrooks make_request(ip, picked); 711147072Sbrooks ip->client->xid = ip->client->packet.xid; 712147072Sbrooks 713147072Sbrooks /* Toss the lease we picked - we'll get it back in a DHCPACK. */ 714147072Sbrooks free_client_lease(picked); 715147072Sbrooks 716147072Sbrooks /* Add an immediate timeout to send the first DHCPREQUEST packet. */ 717147072Sbrooks send_request(ip); 718147072Sbrooks} 719147072Sbrooks 720147072Sbrooks/* state_requesting is called when we receive a DHCPACK message after 721147072Sbrooks having sent out one or more DHCPREQUEST packets. */ 722147072Sbrooks 723147072Sbrooksvoid 724147072Sbrooksdhcpack(struct packet *packet) 725147072Sbrooks{ 726147072Sbrooks struct interface_info *ip = packet->interface; 727147072Sbrooks struct client_lease *lease; 728147072Sbrooks 729147072Sbrooks /* If we're not receptive to an offer right now, or if the offer 730147072Sbrooks has an unrecognizable transaction id, then just drop it. */ 731147072Sbrooks if (packet->interface->client->xid != packet->raw->xid || 732147072Sbrooks (packet->interface->hw_address.hlen != packet->raw->hlen) || 733147072Sbrooks (memcmp(packet->interface->hw_address.haddr, 734147072Sbrooks packet->raw->chaddr, packet->raw->hlen))) 735147072Sbrooks return; 736147072Sbrooks 737147072Sbrooks if (ip->client->state != S_REBOOTING && 738147072Sbrooks ip->client->state != S_REQUESTING && 739147072Sbrooks ip->client->state != S_RENEWING && 740147072Sbrooks ip->client->state != S_REBINDING) 741147072Sbrooks return; 742147072Sbrooks 743147072Sbrooks note("DHCPACK from %s", piaddr(packet->client_addr)); 744147072Sbrooks 745147072Sbrooks lease = packet_to_lease(packet); 746147072Sbrooks if (!lease) { 747147072Sbrooks note("packet_to_lease failed."); 748147072Sbrooks return; 749147072Sbrooks } 750147072Sbrooks 751147072Sbrooks ip->client->new = lease; 752147072Sbrooks 753147072Sbrooks /* Stop resending DHCPREQUEST. */ 754147072Sbrooks cancel_timeout(send_request, ip); 755147072Sbrooks 756147072Sbrooks /* Figure out the lease time. */ 757147072Sbrooks if (ip->client->new->options[DHO_DHCP_LEASE_TIME].data) 758147072Sbrooks ip->client->new->expiry = getULong( 759147072Sbrooks ip->client->new->options[DHO_DHCP_LEASE_TIME].data); 760147072Sbrooks else 761147072Sbrooks ip->client->new->expiry = default_lease_time; 762147072Sbrooks /* A number that looks negative here is really just very large, 763147072Sbrooks because the lease expiry offset is unsigned. */ 764147072Sbrooks if (ip->client->new->expiry < 0) 765147072Sbrooks ip->client->new->expiry = TIME_MAX; 766147072Sbrooks /* XXX should be fixed by resetting the client state */ 767147072Sbrooks if (ip->client->new->expiry < 60) 768147072Sbrooks ip->client->new->expiry = 60; 769147072Sbrooks 770147072Sbrooks /* Take the server-provided renewal time if there is one; 771147072Sbrooks otherwise figure it out according to the spec. */ 772147072Sbrooks if (ip->client->new->options[DHO_DHCP_RENEWAL_TIME].len) 773147072Sbrooks ip->client->new->renewal = getULong( 774147072Sbrooks ip->client->new->options[DHO_DHCP_RENEWAL_TIME].data); 775147072Sbrooks else 776147072Sbrooks ip->client->new->renewal = ip->client->new->expiry / 2; 777147072Sbrooks 778147072Sbrooks /* Same deal with the rebind time. */ 779147072Sbrooks if (ip->client->new->options[DHO_DHCP_REBINDING_TIME].len) 780147072Sbrooks ip->client->new->rebind = getULong( 781147072Sbrooks ip->client->new->options[DHO_DHCP_REBINDING_TIME].data); 782147072Sbrooks else 783147072Sbrooks ip->client->new->rebind = ip->client->new->renewal + 784147072Sbrooks ip->client->new->renewal / 2 + ip->client->new->renewal / 4; 785147072Sbrooks 786147072Sbrooks ip->client->new->expiry += cur_time; 787147072Sbrooks /* Lease lengths can never be negative. */ 788147072Sbrooks if (ip->client->new->expiry < cur_time) 789147072Sbrooks ip->client->new->expiry = TIME_MAX; 790147072Sbrooks ip->client->new->renewal += cur_time; 791147072Sbrooks if (ip->client->new->renewal < cur_time) 792147072Sbrooks ip->client->new->renewal = TIME_MAX; 793147072Sbrooks ip->client->new->rebind += cur_time; 794147072Sbrooks if (ip->client->new->rebind < cur_time) 795147072Sbrooks ip->client->new->rebind = TIME_MAX; 796147072Sbrooks 797147072Sbrooks bind_lease(ip); 798147072Sbrooks} 799147072Sbrooks 800147072Sbrooksvoid 801147072Sbrooksbind_lease(struct interface_info *ip) 802147072Sbrooks{ 803147072Sbrooks /* Remember the medium. */ 804147072Sbrooks ip->client->new->medium = ip->client->medium; 805147072Sbrooks 806147072Sbrooks /* Write out the new lease. */ 807147072Sbrooks write_client_lease(ip, ip->client->new, 0); 808147072Sbrooks 809147072Sbrooks /* Run the client script with the new parameters. */ 810147072Sbrooks script_init((ip->client->state == S_REQUESTING ? "BOUND" : 811147072Sbrooks (ip->client->state == S_RENEWING ? "RENEW" : 812147072Sbrooks (ip->client->state == S_REBOOTING ? "REBOOT" : "REBIND"))), 813147072Sbrooks ip->client->new->medium); 814147072Sbrooks if (ip->client->active && ip->client->state != S_REBOOTING) 815147072Sbrooks script_write_params("old_", ip->client->active); 816147072Sbrooks script_write_params("new_", ip->client->new); 817147072Sbrooks if (ip->client->alias) 818147072Sbrooks script_write_params("alias_", ip->client->alias); 819147072Sbrooks script_go(); 820147072Sbrooks 821147072Sbrooks /* Replace the old active lease with the new one. */ 822147072Sbrooks if (ip->client->active) 823147072Sbrooks free_client_lease(ip->client->active); 824147072Sbrooks ip->client->active = ip->client->new; 825147072Sbrooks ip->client->new = NULL; 826147072Sbrooks 827147072Sbrooks /* Set up a timeout to start the renewal process. */ 828147072Sbrooks add_timeout(ip->client->active->renewal, state_bound, ip); 829147072Sbrooks 830147072Sbrooks note("bound to %s -- renewal in %d seconds.", 831147072Sbrooks piaddr(ip->client->active->address), 832147106Sbrooks (int)(ip->client->active->renewal - cur_time)); 833147072Sbrooks ip->client->state = S_BOUND; 834147072Sbrooks reinitialize_interfaces(); 835147072Sbrooks go_daemon(); 836147072Sbrooks} 837147072Sbrooks 838147072Sbrooks/* 839147072Sbrooks * state_bound is called when we've successfully bound to a particular 840147072Sbrooks * lease, but the renewal time on that lease has expired. We are 841147072Sbrooks * expected to unicast a DHCPREQUEST to the server that gave us our 842147072Sbrooks * original lease. 843147072Sbrooks */ 844147072Sbrooksvoid 845147072Sbrooksstate_bound(void *ipp) 846147072Sbrooks{ 847147072Sbrooks struct interface_info *ip = ipp; 848147072Sbrooks 849147072Sbrooks ASSERT_STATE(state, S_BOUND); 850147072Sbrooks 851147072Sbrooks /* T1 has expired. */ 852147072Sbrooks make_request(ip, ip->client->active); 853147072Sbrooks ip->client->xid = ip->client->packet.xid; 854147072Sbrooks 855147072Sbrooks if (ip->client->active->options[DHO_DHCP_SERVER_IDENTIFIER].len == 4) { 856147072Sbrooks memcpy(ip->client->destination.iabuf, ip->client->active-> 857147072Sbrooks options[DHO_DHCP_SERVER_IDENTIFIER].data, 4); 858147072Sbrooks ip->client->destination.len = 4; 859147072Sbrooks } else 860147072Sbrooks ip->client->destination = iaddr_broadcast; 861147072Sbrooks 862147072Sbrooks ip->client->first_sending = cur_time; 863147072Sbrooks ip->client->interval = ip->client->config->initial_interval; 864147072Sbrooks ip->client->state = S_RENEWING; 865147072Sbrooks 866147072Sbrooks /* Send the first packet immediately. */ 867147072Sbrooks send_request(ip); 868147072Sbrooks} 869147072Sbrooks 870147072Sbrooksvoid 871147072Sbrooksbootp(struct packet *packet) 872147072Sbrooks{ 873147072Sbrooks struct iaddrlist *ap; 874147072Sbrooks 875147072Sbrooks if (packet->raw->op != BOOTREPLY) 876147072Sbrooks return; 877147072Sbrooks 878147072Sbrooks /* If there's a reject list, make sure this packet's sender isn't 879147072Sbrooks on it. */ 880147072Sbrooks for (ap = packet->interface->client->config->reject_list; 881147072Sbrooks ap; ap = ap->next) { 882147072Sbrooks if (addr_eq(packet->client_addr, ap->addr)) { 883147072Sbrooks note("BOOTREPLY from %s rejected.", piaddr(ap->addr)); 884147072Sbrooks return; 885147072Sbrooks } 886147072Sbrooks } 887147072Sbrooks dhcpoffer(packet); 888147072Sbrooks} 889147072Sbrooks 890147072Sbrooksvoid 891147072Sbrooksdhcp(struct packet *packet) 892147072Sbrooks{ 893147072Sbrooks struct iaddrlist *ap; 894147072Sbrooks void (*handler)(struct packet *); 895147072Sbrooks char *type; 896147072Sbrooks 897147072Sbrooks switch (packet->packet_type) { 898147072Sbrooks case DHCPOFFER: 899147072Sbrooks handler = dhcpoffer; 900147072Sbrooks type = "DHCPOFFER"; 901147072Sbrooks break; 902147072Sbrooks case DHCPNAK: 903147072Sbrooks handler = dhcpnak; 904147072Sbrooks type = "DHCPNACK"; 905147072Sbrooks break; 906147072Sbrooks case DHCPACK: 907147072Sbrooks handler = dhcpack; 908147072Sbrooks type = "DHCPACK"; 909147072Sbrooks break; 910147072Sbrooks default: 911147072Sbrooks return; 912147072Sbrooks } 913147072Sbrooks 914147072Sbrooks /* If there's a reject list, make sure this packet's sender isn't 915147072Sbrooks on it. */ 916147072Sbrooks for (ap = packet->interface->client->config->reject_list; 917147072Sbrooks ap; ap = ap->next) { 918147072Sbrooks if (addr_eq(packet->client_addr, ap->addr)) { 919147072Sbrooks note("%s from %s rejected.", type, piaddr(ap->addr)); 920147072Sbrooks return; 921147072Sbrooks } 922147072Sbrooks } 923147072Sbrooks (*handler)(packet); 924147072Sbrooks} 925147072Sbrooks 926147072Sbrooksvoid 927147072Sbrooksdhcpoffer(struct packet *packet) 928147072Sbrooks{ 929147072Sbrooks struct interface_info *ip = packet->interface; 930147072Sbrooks struct client_lease *lease, *lp; 931147072Sbrooks int i; 932147072Sbrooks int arp_timeout_needed, stop_selecting; 933147072Sbrooks char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ? 934147072Sbrooks "DHCPOFFER" : "BOOTREPLY"; 935147072Sbrooks 936147072Sbrooks /* If we're not receptive to an offer right now, or if the offer 937147072Sbrooks has an unrecognizable transaction id, then just drop it. */ 938147072Sbrooks if (ip->client->state != S_SELECTING || 939147072Sbrooks packet->interface->client->xid != packet->raw->xid || 940147072Sbrooks (packet->interface->hw_address.hlen != packet->raw->hlen) || 941147072Sbrooks (memcmp(packet->interface->hw_address.haddr, 942147072Sbrooks packet->raw->chaddr, packet->raw->hlen))) 943147072Sbrooks return; 944147072Sbrooks 945147072Sbrooks note("%s from %s", name, piaddr(packet->client_addr)); 946147072Sbrooks 947147072Sbrooks 948147072Sbrooks /* If this lease doesn't supply the minimum required parameters, 949147072Sbrooks blow it off. */ 950147072Sbrooks for (i = 0; ip->client->config->required_options[i]; i++) { 951147072Sbrooks if (!packet->options[ip->client->config-> 952147072Sbrooks required_options[i]].len) { 953147072Sbrooks note("%s isn't satisfactory.", name); 954147072Sbrooks return; 955147072Sbrooks } 956147072Sbrooks } 957147072Sbrooks 958147072Sbrooks /* If we've already seen this lease, don't record it again. */ 959147072Sbrooks for (lease = ip->client->offered_leases; 960147072Sbrooks lease; lease = lease->next) { 961147072Sbrooks if (lease->address.len == sizeof(packet->raw->yiaddr) && 962147072Sbrooks !memcmp(lease->address.iabuf, 963147072Sbrooks &packet->raw->yiaddr, lease->address.len)) { 964147072Sbrooks debug("%s already seen.", name); 965147072Sbrooks return; 966147072Sbrooks } 967147072Sbrooks } 968147072Sbrooks 969147072Sbrooks lease = packet_to_lease(packet); 970147072Sbrooks if (!lease) { 971147072Sbrooks note("packet_to_lease failed."); 972147072Sbrooks return; 973147072Sbrooks } 974147072Sbrooks 975147072Sbrooks /* If this lease was acquired through a BOOTREPLY, record that 976147072Sbrooks fact. */ 977147072Sbrooks if (!packet->options[DHO_DHCP_MESSAGE_TYPE].len) 978147072Sbrooks lease->is_bootp = 1; 979147072Sbrooks 980147072Sbrooks /* Record the medium under which this lease was offered. */ 981147072Sbrooks lease->medium = ip->client->medium; 982147072Sbrooks 983147072Sbrooks /* Send out an ARP Request for the offered IP address. */ 984147072Sbrooks script_init("ARPSEND", lease->medium); 985147072Sbrooks script_write_params("check_", lease); 986147072Sbrooks /* If the script can't send an ARP request without waiting, 987147072Sbrooks we'll be waiting when we do the ARPCHECK, so don't wait now. */ 988147072Sbrooks if (script_go()) 989147072Sbrooks arp_timeout_needed = 0; 990147072Sbrooks else 991147072Sbrooks arp_timeout_needed = 2; 992147072Sbrooks 993147072Sbrooks /* Figure out when we're supposed to stop selecting. */ 994147072Sbrooks stop_selecting = 995147072Sbrooks ip->client->first_sending + ip->client->config->select_interval; 996147072Sbrooks 997147072Sbrooks /* If this is the lease we asked for, put it at the head of the 998147072Sbrooks list, and don't mess with the arp request timeout. */ 999147072Sbrooks if (lease->address.len == ip->client->requested_address.len && 1000147072Sbrooks !memcmp(lease->address.iabuf, 1001147072Sbrooks ip->client->requested_address.iabuf, 1002147072Sbrooks ip->client->requested_address.len)) { 1003147072Sbrooks lease->next = ip->client->offered_leases; 1004147072Sbrooks ip->client->offered_leases = lease; 1005147072Sbrooks } else { 1006147072Sbrooks /* If we already have an offer, and arping for this 1007147072Sbrooks offer would take us past the selection timeout, 1008147072Sbrooks then don't extend the timeout - just hope for the 1009147072Sbrooks best. */ 1010147072Sbrooks if (ip->client->offered_leases && 1011147072Sbrooks (cur_time + arp_timeout_needed) > stop_selecting) 1012147072Sbrooks arp_timeout_needed = 0; 1013147072Sbrooks 1014147072Sbrooks /* Put the lease at the end of the list. */ 1015147072Sbrooks lease->next = NULL; 1016147072Sbrooks if (!ip->client->offered_leases) 1017147072Sbrooks ip->client->offered_leases = lease; 1018147072Sbrooks else { 1019147072Sbrooks for (lp = ip->client->offered_leases; lp->next; 1020147072Sbrooks lp = lp->next) 1021147072Sbrooks ; /* nothing */ 1022147072Sbrooks lp->next = lease; 1023147072Sbrooks } 1024147072Sbrooks } 1025147072Sbrooks 1026147072Sbrooks /* If we're supposed to stop selecting before we've had time 1027147072Sbrooks to wait for the ARPREPLY, add some delay to wait for 1028147072Sbrooks the ARPREPLY. */ 1029147072Sbrooks if (stop_selecting - cur_time < arp_timeout_needed) 1030147072Sbrooks stop_selecting = cur_time + arp_timeout_needed; 1031147072Sbrooks 1032147072Sbrooks /* If the selecting interval has expired, go immediately to 1033147072Sbrooks state_selecting(). Otherwise, time out into 1034147072Sbrooks state_selecting at the select interval. */ 1035147072Sbrooks if (stop_selecting <= 0) 1036147072Sbrooks state_selecting(ip); 1037147072Sbrooks else { 1038147072Sbrooks add_timeout(stop_selecting, state_selecting, ip); 1039147072Sbrooks cancel_timeout(send_discover, ip); 1040147072Sbrooks } 1041147072Sbrooks} 1042147072Sbrooks 1043147072Sbrooks/* Allocate a client_lease structure and initialize it from the parameters 1044147072Sbrooks in the specified packet. */ 1045147072Sbrooks 1046147072Sbrooksstruct client_lease * 1047147072Sbrookspacket_to_lease(struct packet *packet) 1048147072Sbrooks{ 1049147072Sbrooks struct client_lease *lease; 1050147072Sbrooks int i; 1051147072Sbrooks 1052147072Sbrooks lease = malloc(sizeof(struct client_lease)); 1053147072Sbrooks 1054147072Sbrooks if (!lease) { 1055147072Sbrooks warning("dhcpoffer: no memory to record lease."); 1056147072Sbrooks return (NULL); 1057147072Sbrooks } 1058147072Sbrooks 1059147072Sbrooks memset(lease, 0, sizeof(*lease)); 1060147072Sbrooks 1061147072Sbrooks /* Copy the lease options. */ 1062147072Sbrooks for (i = 0; i < 256; i++) { 1063147072Sbrooks if (packet->options[i].len) { 1064147072Sbrooks lease->options[i].data = 1065147072Sbrooks malloc(packet->options[i].len + 1); 1066147072Sbrooks if (!lease->options[i].data) { 1067147072Sbrooks warning("dhcpoffer: no memory for option %d", i); 1068147072Sbrooks free_client_lease(lease); 1069147072Sbrooks return (NULL); 1070147072Sbrooks } else { 1071147072Sbrooks memcpy(lease->options[i].data, 1072147072Sbrooks packet->options[i].data, 1073147072Sbrooks packet->options[i].len); 1074147072Sbrooks lease->options[i].len = 1075147072Sbrooks packet->options[i].len; 1076147072Sbrooks lease->options[i].data[lease->options[i].len] = 1077147072Sbrooks 0; 1078147072Sbrooks } 1079147072Sbrooks if (!check_option(lease,i)) { 1080147072Sbrooks /* ignore a bogus lease offer */ 1081147072Sbrooks warning("Invalid lease option - ignoring offer"); 1082147072Sbrooks free_client_lease(lease); 1083147072Sbrooks return (NULL); 1084147072Sbrooks } 1085147072Sbrooks } 1086147072Sbrooks } 1087147072Sbrooks 1088147072Sbrooks lease->address.len = sizeof(packet->raw->yiaddr); 1089147072Sbrooks memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len); 1090147072Sbrooks 1091252506Sbms lease->nextserver.len = sizeof(packet->raw->siaddr); 1092252506Sbms memcpy(lease->nextserver.iabuf, &packet->raw->siaddr, lease->nextserver.len); 1093252506Sbms 1094148465Sbrooks /* If the server name was filled out, copy it. 1095148465Sbrooks Do not attempt to validate the server name as a host name. 1096148465Sbrooks RFC 2131 merely states that sname is NUL-terminated (which do 1097148465Sbrooks do not assume) and that it is the server's host name. Since 1098148465Sbrooks the ISC client and server allow arbitrary characters, we do 1099148465Sbrooks as well. */ 1100147072Sbrooks if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len || 1101147072Sbrooks !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) && 1102147072Sbrooks packet->raw->sname[0]) { 1103147072Sbrooks lease->server_name = malloc(DHCP_SNAME_LEN + 1); 1104147072Sbrooks if (!lease->server_name) { 1105147072Sbrooks warning("dhcpoffer: no memory for server name."); 1106147072Sbrooks free_client_lease(lease); 1107147072Sbrooks return (NULL); 1108147072Sbrooks } 1109147072Sbrooks memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN); 1110147072Sbrooks lease->server_name[DHCP_SNAME_LEN]='\0'; 1111147072Sbrooks } 1112147072Sbrooks 1113147072Sbrooks /* Ditto for the filename. */ 1114147072Sbrooks if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len || 1115147072Sbrooks !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) && 1116147072Sbrooks packet->raw->file[0]) { 1117147072Sbrooks /* Don't count on the NUL terminator. */ 1118147072Sbrooks lease->filename = malloc(DHCP_FILE_LEN + 1); 1119147072Sbrooks if (!lease->filename) { 1120147072Sbrooks warning("dhcpoffer: no memory for filename."); 1121147072Sbrooks free_client_lease(lease); 1122147072Sbrooks return (NULL); 1123147072Sbrooks } 1124147072Sbrooks memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN); 1125147072Sbrooks lease->filename[DHCP_FILE_LEN]='\0'; 1126147072Sbrooks } 1127147072Sbrooks return lease; 1128147072Sbrooks} 1129147072Sbrooks 1130147072Sbrooksvoid 1131147072Sbrooksdhcpnak(struct packet *packet) 1132147072Sbrooks{ 1133147072Sbrooks struct interface_info *ip = packet->interface; 1134147072Sbrooks 1135147072Sbrooks /* If we're not receptive to an offer right now, or if the offer 1136147072Sbrooks has an unrecognizable transaction id, then just drop it. */ 1137147072Sbrooks if (packet->interface->client->xid != packet->raw->xid || 1138147072Sbrooks (packet->interface->hw_address.hlen != packet->raw->hlen) || 1139147072Sbrooks (memcmp(packet->interface->hw_address.haddr, 1140147072Sbrooks packet->raw->chaddr, packet->raw->hlen))) 1141147072Sbrooks return; 1142147072Sbrooks 1143147072Sbrooks if (ip->client->state != S_REBOOTING && 1144147072Sbrooks ip->client->state != S_REQUESTING && 1145147072Sbrooks ip->client->state != S_RENEWING && 1146147072Sbrooks ip->client->state != S_REBINDING) 1147147072Sbrooks return; 1148147072Sbrooks 1149147072Sbrooks note("DHCPNAK from %s", piaddr(packet->client_addr)); 1150147072Sbrooks 1151147072Sbrooks if (!ip->client->active) { 1152147072Sbrooks note("DHCPNAK with no active lease.\n"); 1153147072Sbrooks return; 1154147072Sbrooks } 1155147072Sbrooks 1156147072Sbrooks free_client_lease(ip->client->active); 1157147072Sbrooks ip->client->active = NULL; 1158147072Sbrooks 1159147072Sbrooks /* Stop sending DHCPREQUEST packets... */ 1160147072Sbrooks cancel_timeout(send_request, ip); 1161147072Sbrooks 1162147072Sbrooks ip->client->state = S_INIT; 1163147072Sbrooks state_init(ip); 1164147072Sbrooks} 1165147072Sbrooks 1166147072Sbrooks/* Send out a DHCPDISCOVER packet, and set a timeout to send out another 1167147072Sbrooks one after the right interval has expired. If we don't get an offer by 1168147072Sbrooks the time we reach the panic interval, call the panic function. */ 1169147072Sbrooks 1170147072Sbrooksvoid 1171147072Sbrookssend_discover(void *ipp) 1172147072Sbrooks{ 1173147072Sbrooks struct interface_info *ip = ipp; 1174147072Sbrooks int interval, increase = 1; 1175147072Sbrooks 1176147072Sbrooks /* Figure out how long it's been since we started transmitting. */ 1177147072Sbrooks interval = cur_time - ip->client->first_sending; 1178147072Sbrooks 1179147072Sbrooks /* If we're past the panic timeout, call the script and tell it 1180147072Sbrooks we haven't found anything for this interface yet. */ 1181147072Sbrooks if (interval > ip->client->config->timeout) { 1182147072Sbrooks state_panic(ip); 1183147072Sbrooks return; 1184147072Sbrooks } 1185147072Sbrooks 1186147072Sbrooks /* If we're selecting media, try the whole list before doing 1187147072Sbrooks the exponential backoff, but if we've already received an 1188147072Sbrooks offer, stop looping, because we obviously have it right. */ 1189147072Sbrooks if (!ip->client->offered_leases && 1190147072Sbrooks ip->client->config->media) { 1191147072Sbrooks int fail = 0; 1192147072Sbrooksagain: 1193147072Sbrooks if (ip->client->medium) { 1194147072Sbrooks ip->client->medium = ip->client->medium->next; 1195147072Sbrooks increase = 0; 1196147072Sbrooks } 1197147072Sbrooks if (!ip->client->medium) { 1198147072Sbrooks if (fail) 1199147072Sbrooks error("No valid media types for %s!", ip->name); 1200147072Sbrooks ip->client->medium = ip->client->config->media; 1201147072Sbrooks increase = 1; 1202147072Sbrooks } 1203147072Sbrooks 1204147072Sbrooks note("Trying medium \"%s\" %d", ip->client->medium->string, 1205147072Sbrooks increase); 1206147072Sbrooks script_init("MEDIUM", ip->client->medium); 1207147072Sbrooks if (script_go()) 1208147072Sbrooks goto again; 1209147072Sbrooks } 1210147072Sbrooks 1211147072Sbrooks /* 1212147072Sbrooks * If we're supposed to increase the interval, do so. If it's 1213147072Sbrooks * currently zero (i.e., we haven't sent any packets yet), set 1214147072Sbrooks * it to one; otherwise, add to it a random number between zero 1215147072Sbrooks * and two times itself. On average, this means that it will 1216147072Sbrooks * double with every transmission. 1217147072Sbrooks */ 1218147072Sbrooks if (increase) { 1219147072Sbrooks if (!ip->client->interval) 1220147072Sbrooks ip->client->interval = 1221147072Sbrooks ip->client->config->initial_interval; 1222147072Sbrooks else { 1223147072Sbrooks ip->client->interval += (arc4random() >> 2) % 1224147072Sbrooks (2 * ip->client->interval); 1225147072Sbrooks } 1226147072Sbrooks 1227147072Sbrooks /* Don't backoff past cutoff. */ 1228147072Sbrooks if (ip->client->interval > 1229147072Sbrooks ip->client->config->backoff_cutoff) 1230147072Sbrooks ip->client->interval = 1231147072Sbrooks ((ip->client->config->backoff_cutoff / 2) 1232147072Sbrooks + ((arc4random() >> 2) % 1233147072Sbrooks ip->client->config->backoff_cutoff)); 1234147072Sbrooks } else if (!ip->client->interval) 1235147072Sbrooks ip->client->interval = 1236147072Sbrooks ip->client->config->initial_interval; 1237147072Sbrooks 1238147072Sbrooks /* If the backoff would take us to the panic timeout, just use that 1239147072Sbrooks as the interval. */ 1240147072Sbrooks if (cur_time + ip->client->interval > 1241147072Sbrooks ip->client->first_sending + ip->client->config->timeout) 1242147072Sbrooks ip->client->interval = 1243147072Sbrooks (ip->client->first_sending + 1244147072Sbrooks ip->client->config->timeout) - cur_time + 1; 1245147072Sbrooks 1246147072Sbrooks /* Record the number of seconds since we started sending. */ 1247147072Sbrooks if (interval < 65536) 1248147072Sbrooks ip->client->packet.secs = htons(interval); 1249147072Sbrooks else 1250147072Sbrooks ip->client->packet.secs = htons(65535); 1251147072Sbrooks ip->client->secs = ip->client->packet.secs; 1252147072Sbrooks 1253147072Sbrooks note("DHCPDISCOVER on %s to %s port %d interval %d", 1254252616Spjd ip->name, inet_ntoa(inaddr_broadcast), REMOTE_PORT, 1255147106Sbrooks (int)ip->client->interval); 1256147072Sbrooks 1257147072Sbrooks /* Send out a packet. */ 1258252626Spjd send_packet_unpriv(privfd, &ip->client->packet, 1259252626Spjd ip->client->packet_length, inaddr_any, inaddr_broadcast); 1260147072Sbrooks 1261147072Sbrooks add_timeout(cur_time + ip->client->interval, send_discover, ip); 1262147072Sbrooks} 1263147072Sbrooks 1264147072Sbrooks/* 1265147072Sbrooks * state_panic gets called if we haven't received any offers in a preset 1266147072Sbrooks * amount of time. When this happens, we try to use existing leases 1267147072Sbrooks * that haven't yet expired, and failing that, we call the client script 1268147072Sbrooks * and hope it can do something. 1269147072Sbrooks */ 1270147072Sbrooksvoid 1271147072Sbrooksstate_panic(void *ipp) 1272147072Sbrooks{ 1273147072Sbrooks struct interface_info *ip = ipp; 1274147072Sbrooks struct client_lease *loop = ip->client->active; 1275147072Sbrooks struct client_lease *lp; 1276147072Sbrooks 1277147072Sbrooks note("No DHCPOFFERS received."); 1278147072Sbrooks 1279147072Sbrooks /* We may not have an active lease, but we may have some 1280147072Sbrooks predefined leases that we can try. */ 1281147072Sbrooks if (!ip->client->active && ip->client->leases) 1282147072Sbrooks goto activate_next; 1283147072Sbrooks 1284147072Sbrooks /* Run through the list of leases and see if one can be used. */ 1285147072Sbrooks while (ip->client->active) { 1286147072Sbrooks if (ip->client->active->expiry > cur_time) { 1287147072Sbrooks note("Trying recorded lease %s", 1288147072Sbrooks piaddr(ip->client->active->address)); 1289147072Sbrooks /* Run the client script with the existing 1290147072Sbrooks parameters. */ 1291147072Sbrooks script_init("TIMEOUT", 1292147072Sbrooks ip->client->active->medium); 1293147072Sbrooks script_write_params("new_", ip->client->active); 1294147072Sbrooks if (ip->client->alias) 1295147072Sbrooks script_write_params("alias_", 1296147072Sbrooks ip->client->alias); 1297147072Sbrooks 1298147072Sbrooks /* If the old lease is still good and doesn't 1299147072Sbrooks yet need renewal, go into BOUND state and 1300147072Sbrooks timeout at the renewal time. */ 1301147072Sbrooks if (!script_go()) { 1302147072Sbrooks if (cur_time < 1303147072Sbrooks ip->client->active->renewal) { 1304147072Sbrooks ip->client->state = S_BOUND; 1305147072Sbrooks note("bound: renewal in %d seconds.", 1306147106Sbrooks (int)(ip->client->active->renewal - 1307147106Sbrooks cur_time)); 1308147072Sbrooks add_timeout( 1309147072Sbrooks ip->client->active->renewal, 1310147072Sbrooks state_bound, ip); 1311147072Sbrooks } else { 1312147072Sbrooks ip->client->state = S_BOUND; 1313147072Sbrooks note("bound: immediate renewal."); 1314147072Sbrooks state_bound(ip); 1315147072Sbrooks } 1316147072Sbrooks reinitialize_interfaces(); 1317147072Sbrooks go_daemon(); 1318147072Sbrooks return; 1319147072Sbrooks } 1320147072Sbrooks } 1321147072Sbrooks 1322147072Sbrooks /* If there are no other leases, give up. */ 1323147072Sbrooks if (!ip->client->leases) { 1324147072Sbrooks ip->client->leases = ip->client->active; 1325147072Sbrooks ip->client->active = NULL; 1326147072Sbrooks break; 1327147072Sbrooks } 1328147072Sbrooks 1329147072Sbrooksactivate_next: 1330147072Sbrooks /* Otherwise, put the active lease at the end of the 1331147072Sbrooks lease list, and try another lease.. */ 1332147072Sbrooks for (lp = ip->client->leases; lp->next; lp = lp->next) 1333147072Sbrooks ; 1334147072Sbrooks lp->next = ip->client->active; 1335147072Sbrooks if (lp->next) 1336147072Sbrooks lp->next->next = NULL; 1337147072Sbrooks ip->client->active = ip->client->leases; 1338147072Sbrooks ip->client->leases = ip->client->leases->next; 1339147072Sbrooks 1340147072Sbrooks /* If we already tried this lease, we've exhausted the 1341147072Sbrooks set of leases, so we might as well give up for 1342147072Sbrooks now. */ 1343147072Sbrooks if (ip->client->active == loop) 1344147072Sbrooks break; 1345147072Sbrooks else if (!loop) 1346147072Sbrooks loop = ip->client->active; 1347147072Sbrooks } 1348147072Sbrooks 1349147072Sbrooks /* No leases were available, or what was available didn't work, so 1350147072Sbrooks tell the shell script that we failed to allocate an address, 1351147072Sbrooks and try again later. */ 1352147072Sbrooks note("No working leases in persistent database - sleeping.\n"); 1353147072Sbrooks script_init("FAIL", NULL); 1354147072Sbrooks if (ip->client->alias) 1355147072Sbrooks script_write_params("alias_", ip->client->alias); 1356147072Sbrooks script_go(); 1357147072Sbrooks ip->client->state = S_INIT; 1358147072Sbrooks add_timeout(cur_time + ip->client->config->retry_interval, state_init, 1359147072Sbrooks ip); 1360147072Sbrooks go_daemon(); 1361147072Sbrooks} 1362147072Sbrooks 1363147072Sbrooksvoid 1364147072Sbrookssend_request(void *ipp) 1365147072Sbrooks{ 1366147072Sbrooks struct interface_info *ip = ipp; 1367252616Spjd struct in_addr from, to; 1368147072Sbrooks int interval; 1369147072Sbrooks 1370147072Sbrooks /* Figure out how long it's been since we started transmitting. */ 1371147072Sbrooks interval = cur_time - ip->client->first_sending; 1372147072Sbrooks 1373147072Sbrooks /* If we're in the INIT-REBOOT or REQUESTING state and we're 1374147072Sbrooks past the reboot timeout, go to INIT and see if we can 1375147072Sbrooks DISCOVER an address... */ 1376147072Sbrooks /* XXX In the INIT-REBOOT state, if we don't get an ACK, it 1377147072Sbrooks means either that we're on a network with no DHCP server, 1378147072Sbrooks or that our server is down. In the latter case, assuming 1379147072Sbrooks that there is a backup DHCP server, DHCPDISCOVER will get 1380147072Sbrooks us a new address, but we could also have successfully 1381147072Sbrooks reused our old address. In the former case, we're hosed 1382147072Sbrooks anyway. This is not a win-prone situation. */ 1383147072Sbrooks if ((ip->client->state == S_REBOOTING || 1384147072Sbrooks ip->client->state == S_REQUESTING) && 1385147072Sbrooks interval > ip->client->config->reboot_timeout) { 1386147072Sbrookscancel: 1387147072Sbrooks ip->client->state = S_INIT; 1388147072Sbrooks cancel_timeout(send_request, ip); 1389147072Sbrooks state_init(ip); 1390147072Sbrooks return; 1391147072Sbrooks } 1392147072Sbrooks 1393147072Sbrooks /* If we're in the reboot state, make sure the media is set up 1394147072Sbrooks correctly. */ 1395147072Sbrooks if (ip->client->state == S_REBOOTING && 1396147072Sbrooks !ip->client->medium && 1397147072Sbrooks ip->client->active->medium ) { 1398147072Sbrooks script_init("MEDIUM", ip->client->active->medium); 1399147072Sbrooks 1400147072Sbrooks /* If the medium we chose won't fly, go to INIT state. */ 1401147072Sbrooks if (script_go()) 1402147072Sbrooks goto cancel; 1403147072Sbrooks 1404147072Sbrooks /* Record the medium. */ 1405147072Sbrooks ip->client->medium = ip->client->active->medium; 1406147072Sbrooks } 1407147072Sbrooks 1408147072Sbrooks /* If the lease has expired, relinquish the address and go back 1409147072Sbrooks to the INIT state. */ 1410147072Sbrooks if (ip->client->state != S_REQUESTING && 1411147072Sbrooks cur_time > ip->client->active->expiry) { 1412147072Sbrooks /* Run the client script with the new parameters. */ 1413147072Sbrooks script_init("EXPIRE", NULL); 1414147072Sbrooks script_write_params("old_", ip->client->active); 1415147072Sbrooks if (ip->client->alias) 1416147072Sbrooks script_write_params("alias_", ip->client->alias); 1417147072Sbrooks script_go(); 1418147072Sbrooks 1419147072Sbrooks /* Now do a preinit on the interface so that we can 1420147072Sbrooks discover a new address. */ 1421147072Sbrooks script_init("PREINIT", NULL); 1422147072Sbrooks if (ip->client->alias) 1423147072Sbrooks script_write_params("alias_", ip->client->alias); 1424147072Sbrooks script_go(); 1425147072Sbrooks 1426147072Sbrooks ip->client->state = S_INIT; 1427147072Sbrooks state_init(ip); 1428147072Sbrooks return; 1429147072Sbrooks } 1430147072Sbrooks 1431147072Sbrooks /* Do the exponential backoff... */ 1432147072Sbrooks if (!ip->client->interval) 1433147072Sbrooks ip->client->interval = ip->client->config->initial_interval; 1434147072Sbrooks else 1435147072Sbrooks ip->client->interval += ((arc4random() >> 2) % 1436147072Sbrooks (2 * ip->client->interval)); 1437147072Sbrooks 1438147072Sbrooks /* Don't backoff past cutoff. */ 1439147072Sbrooks if (ip->client->interval > 1440147072Sbrooks ip->client->config->backoff_cutoff) 1441147072Sbrooks ip->client->interval = 1442147072Sbrooks ((ip->client->config->backoff_cutoff / 2) + 1443147072Sbrooks ((arc4random() >> 2) % ip->client->interval)); 1444147072Sbrooks 1445147072Sbrooks /* If the backoff would take us to the expiry time, just set the 1446147072Sbrooks timeout to the expiry time. */ 1447147072Sbrooks if (ip->client->state != S_REQUESTING && 1448147072Sbrooks cur_time + ip->client->interval > 1449147072Sbrooks ip->client->active->expiry) 1450147072Sbrooks ip->client->interval = 1451147072Sbrooks ip->client->active->expiry - cur_time + 1; 1452147072Sbrooks 1453147072Sbrooks /* If the lease T2 time has elapsed, or if we're not yet bound, 1454147072Sbrooks broadcast the DHCPREQUEST rather than unicasting. */ 1455147072Sbrooks if (ip->client->state == S_REQUESTING || 1456147072Sbrooks ip->client->state == S_REBOOTING || 1457147072Sbrooks cur_time > ip->client->active->rebind) 1458252616Spjd to.s_addr = INADDR_BROADCAST; 1459147072Sbrooks else 1460252616Spjd memcpy(&to.s_addr, ip->client->destination.iabuf, 1461252616Spjd sizeof(to.s_addr)); 1462147072Sbrooks 1463330693Sdab if (ip->client->state != S_REQUESTING && 1464330693Sdab ip->client->state != S_REBOOTING) 1465147072Sbrooks memcpy(&from, ip->client->active->address.iabuf, 1466147072Sbrooks sizeof(from)); 1467147072Sbrooks else 1468147072Sbrooks from.s_addr = INADDR_ANY; 1469147072Sbrooks 1470147072Sbrooks /* Record the number of seconds since we started sending. */ 1471147072Sbrooks if (ip->client->state == S_REQUESTING) 1472147072Sbrooks ip->client->packet.secs = ip->client->secs; 1473147072Sbrooks else { 1474147072Sbrooks if (interval < 65536) 1475147072Sbrooks ip->client->packet.secs = htons(interval); 1476147072Sbrooks else 1477147072Sbrooks ip->client->packet.secs = htons(65535); 1478147072Sbrooks } 1479147072Sbrooks 1480252616Spjd note("DHCPREQUEST on %s to %s port %d", ip->name, inet_ntoa(to), 1481252616Spjd REMOTE_PORT); 1482147072Sbrooks 1483147072Sbrooks /* Send out a packet. */ 1484252626Spjd send_packet_unpriv(privfd, &ip->client->packet, 1485252626Spjd ip->client->packet_length, from, to); 1486147072Sbrooks 1487147072Sbrooks add_timeout(cur_time + ip->client->interval, send_request, ip); 1488147072Sbrooks} 1489147072Sbrooks 1490147072Sbrooksvoid 1491147072Sbrookssend_decline(void *ipp) 1492147072Sbrooks{ 1493147072Sbrooks struct interface_info *ip = ipp; 1494147072Sbrooks 1495147072Sbrooks note("DHCPDECLINE on %s to %s port %d", ip->name, 1496252616Spjd inet_ntoa(inaddr_broadcast), REMOTE_PORT); 1497147072Sbrooks 1498147072Sbrooks /* Send out a packet. */ 1499252626Spjd send_packet_unpriv(privfd, &ip->client->packet, 1500252626Spjd ip->client->packet_length, inaddr_any, inaddr_broadcast); 1501147072Sbrooks} 1502147072Sbrooks 1503147072Sbrooksvoid 1504147072Sbrooksmake_discover(struct interface_info *ip, struct client_lease *lease) 1505147072Sbrooks{ 1506147072Sbrooks unsigned char discover = DHCPDISCOVER; 1507147072Sbrooks struct tree_cache *options[256]; 1508147072Sbrooks struct tree_cache option_elements[256]; 1509147072Sbrooks int i; 1510147072Sbrooks 1511147072Sbrooks memset(option_elements, 0, sizeof(option_elements)); 1512147072Sbrooks memset(options, 0, sizeof(options)); 1513147072Sbrooks memset(&ip->client->packet, 0, sizeof(ip->client->packet)); 1514147072Sbrooks 1515147072Sbrooks /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */ 1516147072Sbrooks i = DHO_DHCP_MESSAGE_TYPE; 1517147072Sbrooks options[i] = &option_elements[i]; 1518147072Sbrooks options[i]->value = &discover; 1519147072Sbrooks options[i]->len = sizeof(discover); 1520147072Sbrooks options[i]->buf_size = sizeof(discover); 1521147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1522147072Sbrooks 1523147072Sbrooks /* Request the options we want */ 1524147072Sbrooks i = DHO_DHCP_PARAMETER_REQUEST_LIST; 1525147072Sbrooks options[i] = &option_elements[i]; 1526147072Sbrooks options[i]->value = ip->client->config->requested_options; 1527147072Sbrooks options[i]->len = ip->client->config->requested_option_count; 1528147072Sbrooks options[i]->buf_size = 1529147072Sbrooks ip->client->config->requested_option_count; 1530147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1531147072Sbrooks 1532147072Sbrooks /* If we had an address, try to get it again. */ 1533147072Sbrooks if (lease) { 1534147072Sbrooks ip->client->requested_address = lease->address; 1535147072Sbrooks i = DHO_DHCP_REQUESTED_ADDRESS; 1536147072Sbrooks options[i] = &option_elements[i]; 1537147072Sbrooks options[i]->value = lease->address.iabuf; 1538147072Sbrooks options[i]->len = lease->address.len; 1539147072Sbrooks options[i]->buf_size = lease->address.len; 1540147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1541147072Sbrooks } else 1542147072Sbrooks ip->client->requested_address.len = 0; 1543147072Sbrooks 1544147072Sbrooks /* Send any options requested in the config file. */ 1545147072Sbrooks for (i = 0; i < 256; i++) 1546147072Sbrooks if (!options[i] && 1547147072Sbrooks ip->client->config->send_options[i].data) { 1548147072Sbrooks options[i] = &option_elements[i]; 1549147072Sbrooks options[i]->value = 1550147072Sbrooks ip->client->config->send_options[i].data; 1551147072Sbrooks options[i]->len = 1552147072Sbrooks ip->client->config->send_options[i].len; 1553147072Sbrooks options[i]->buf_size = 1554147072Sbrooks ip->client->config->send_options[i].len; 1555147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1556147072Sbrooks } 1557252621Spjd 1558158353Sbrooks /* send host name if not set via config file. */ 1559158353Sbrooks if (!options[DHO_HOST_NAME]) { 1560252623Spjd if (hostname[0] != '\0') { 1561158353Sbrooks size_t len; 1562158353Sbrooks char* posDot = strchr(hostname, '.'); 1563158353Sbrooks if (posDot != NULL) 1564158353Sbrooks len = posDot - hostname; 1565158353Sbrooks else 1566158353Sbrooks len = strlen(hostname); 1567158353Sbrooks options[DHO_HOST_NAME] = &option_elements[DHO_HOST_NAME]; 1568158353Sbrooks options[DHO_HOST_NAME]->value = hostname; 1569158353Sbrooks options[DHO_HOST_NAME]->len = len; 1570158353Sbrooks options[DHO_HOST_NAME]->buf_size = len; 1571158353Sbrooks options[DHO_HOST_NAME]->timeout = 0xFFFFFFFF; 1572158353Sbrooks } 1573158353Sbrooks } 1574147072Sbrooks 1575158353Sbrooks /* set unique client identifier */ 1576158353Sbrooks char client_ident[sizeof(struct hardware)]; 1577158353Sbrooks if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) { 1578158353Sbrooks int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ? 1579158353Sbrooks ip->hw_address.hlen : sizeof(client_ident)-1; 1580158353Sbrooks client_ident[0] = ip->hw_address.htype; 1581252621Spjd memcpy(&client_ident[1], ip->hw_address.haddr, hwlen); 1582158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER]; 1583158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident; 1584158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1; 1585158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->buf_size = hwlen+1; 1586158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->timeout = 0xFFFFFFFF; 1587158353Sbrooks } 1588158353Sbrooks 1589147072Sbrooks /* Set up the option buffer... */ 1590147072Sbrooks ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, 1591147072Sbrooks options, 0, 0, 0, NULL, 0); 1592147072Sbrooks if (ip->client->packet_length < BOOTP_MIN_LEN) 1593147072Sbrooks ip->client->packet_length = BOOTP_MIN_LEN; 1594147072Sbrooks 1595147072Sbrooks ip->client->packet.op = BOOTREQUEST; 1596147072Sbrooks ip->client->packet.htype = ip->hw_address.htype; 1597147072Sbrooks ip->client->packet.hlen = ip->hw_address.hlen; 1598147072Sbrooks ip->client->packet.hops = 0; 1599147072Sbrooks ip->client->packet.xid = arc4random(); 1600147072Sbrooks ip->client->packet.secs = 0; /* filled in by send_discover. */ 1601147072Sbrooks ip->client->packet.flags = 0; 1602147072Sbrooks 1603147072Sbrooks memset(&(ip->client->packet.ciaddr), 1604147072Sbrooks 0, sizeof(ip->client->packet.ciaddr)); 1605147072Sbrooks memset(&(ip->client->packet.yiaddr), 1606147072Sbrooks 0, sizeof(ip->client->packet.yiaddr)); 1607147072Sbrooks memset(&(ip->client->packet.siaddr), 1608147072Sbrooks 0, sizeof(ip->client->packet.siaddr)); 1609147072Sbrooks memset(&(ip->client->packet.giaddr), 1610147072Sbrooks 0, sizeof(ip->client->packet.giaddr)); 1611147072Sbrooks memcpy(ip->client->packet.chaddr, 1612147072Sbrooks ip->hw_address.haddr, ip->hw_address.hlen); 1613147072Sbrooks} 1614147072Sbrooks 1615147072Sbrooks 1616147072Sbrooksvoid 1617147072Sbrooksmake_request(struct interface_info *ip, struct client_lease * lease) 1618147072Sbrooks{ 1619147072Sbrooks unsigned char request = DHCPREQUEST; 1620147072Sbrooks struct tree_cache *options[256]; 1621147072Sbrooks struct tree_cache option_elements[256]; 1622147072Sbrooks int i; 1623147072Sbrooks 1624147072Sbrooks memset(options, 0, sizeof(options)); 1625147072Sbrooks memset(&ip->client->packet, 0, sizeof(ip->client->packet)); 1626147072Sbrooks 1627147072Sbrooks /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */ 1628147072Sbrooks i = DHO_DHCP_MESSAGE_TYPE; 1629147072Sbrooks options[i] = &option_elements[i]; 1630147072Sbrooks options[i]->value = &request; 1631147072Sbrooks options[i]->len = sizeof(request); 1632147072Sbrooks options[i]->buf_size = sizeof(request); 1633147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1634147072Sbrooks 1635147072Sbrooks /* Request the options we want */ 1636147072Sbrooks i = DHO_DHCP_PARAMETER_REQUEST_LIST; 1637147072Sbrooks options[i] = &option_elements[i]; 1638147072Sbrooks options[i]->value = ip->client->config->requested_options; 1639147072Sbrooks options[i]->len = ip->client->config->requested_option_count; 1640147072Sbrooks options[i]->buf_size = 1641147072Sbrooks ip->client->config->requested_option_count; 1642147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1643147072Sbrooks 1644147072Sbrooks /* If we are requesting an address that hasn't yet been assigned 1645147072Sbrooks to us, use the DHCP Requested Address option. */ 1646147072Sbrooks if (ip->client->state == S_REQUESTING) { 1647147072Sbrooks /* Send back the server identifier... */ 1648147072Sbrooks i = DHO_DHCP_SERVER_IDENTIFIER; 1649147072Sbrooks options[i] = &option_elements[i]; 1650147072Sbrooks options[i]->value = lease->options[i].data; 1651147072Sbrooks options[i]->len = lease->options[i].len; 1652147072Sbrooks options[i]->buf_size = lease->options[i].len; 1653147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1654147072Sbrooks } 1655147072Sbrooks if (ip->client->state == S_REQUESTING || 1656147072Sbrooks ip->client->state == S_REBOOTING) { 1657147072Sbrooks ip->client->requested_address = lease->address; 1658147072Sbrooks i = DHO_DHCP_REQUESTED_ADDRESS; 1659147072Sbrooks options[i] = &option_elements[i]; 1660147072Sbrooks options[i]->value = lease->address.iabuf; 1661147072Sbrooks options[i]->len = lease->address.len; 1662147072Sbrooks options[i]->buf_size = lease->address.len; 1663147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1664147072Sbrooks } else 1665147072Sbrooks ip->client->requested_address.len = 0; 1666147072Sbrooks 1667147072Sbrooks /* Send any options requested in the config file. */ 1668147072Sbrooks for (i = 0; i < 256; i++) 1669147072Sbrooks if (!options[i] && 1670147072Sbrooks ip->client->config->send_options[i].data) { 1671147072Sbrooks options[i] = &option_elements[i]; 1672147072Sbrooks options[i]->value = 1673147072Sbrooks ip->client->config->send_options[i].data; 1674147072Sbrooks options[i]->len = 1675147072Sbrooks ip->client->config->send_options[i].len; 1676147072Sbrooks options[i]->buf_size = 1677147072Sbrooks ip->client->config->send_options[i].len; 1678147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1679147072Sbrooks } 1680252621Spjd 1681158353Sbrooks /* send host name if not set via config file. */ 1682158353Sbrooks if (!options[DHO_HOST_NAME]) { 1683252623Spjd if (hostname[0] != '\0') { 1684158353Sbrooks size_t len; 1685158353Sbrooks char* posDot = strchr(hostname, '.'); 1686158353Sbrooks if (posDot != NULL) 1687158353Sbrooks len = posDot - hostname; 1688158353Sbrooks else 1689158353Sbrooks len = strlen(hostname); 1690158353Sbrooks options[DHO_HOST_NAME] = &option_elements[DHO_HOST_NAME]; 1691158353Sbrooks options[DHO_HOST_NAME]->value = hostname; 1692158353Sbrooks options[DHO_HOST_NAME]->len = len; 1693158353Sbrooks options[DHO_HOST_NAME]->buf_size = len; 1694158353Sbrooks options[DHO_HOST_NAME]->timeout = 0xFFFFFFFF; 1695158353Sbrooks } 1696158353Sbrooks } 1697147072Sbrooks 1698158353Sbrooks /* set unique client identifier */ 1699158353Sbrooks char client_ident[sizeof(struct hardware)]; 1700158353Sbrooks if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) { 1701158353Sbrooks int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ? 1702158353Sbrooks ip->hw_address.hlen : sizeof(client_ident)-1; 1703158353Sbrooks client_ident[0] = ip->hw_address.htype; 1704252621Spjd memcpy(&client_ident[1], ip->hw_address.haddr, hwlen); 1705158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER]; 1706158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident; 1707158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1; 1708158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->buf_size = hwlen+1; 1709158353Sbrooks options[DHO_DHCP_CLIENT_IDENTIFIER]->timeout = 0xFFFFFFFF; 1710158353Sbrooks } 1711158353Sbrooks 1712147072Sbrooks /* Set up the option buffer... */ 1713147072Sbrooks ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, 1714147072Sbrooks options, 0, 0, 0, NULL, 0); 1715147072Sbrooks if (ip->client->packet_length < BOOTP_MIN_LEN) 1716147072Sbrooks ip->client->packet_length = BOOTP_MIN_LEN; 1717147072Sbrooks 1718147072Sbrooks ip->client->packet.op = BOOTREQUEST; 1719147072Sbrooks ip->client->packet.htype = ip->hw_address.htype; 1720147072Sbrooks ip->client->packet.hlen = ip->hw_address.hlen; 1721147072Sbrooks ip->client->packet.hops = 0; 1722147072Sbrooks ip->client->packet.xid = ip->client->xid; 1723147072Sbrooks ip->client->packet.secs = 0; /* Filled in by send_request. */ 1724147072Sbrooks 1725147072Sbrooks /* If we own the address we're requesting, put it in ciaddr; 1726147072Sbrooks otherwise set ciaddr to zero. */ 1727147072Sbrooks if (ip->client->state == S_BOUND || 1728147072Sbrooks ip->client->state == S_RENEWING || 1729147072Sbrooks ip->client->state == S_REBINDING) { 1730147072Sbrooks memcpy(&ip->client->packet.ciaddr, 1731147072Sbrooks lease->address.iabuf, lease->address.len); 1732147072Sbrooks ip->client->packet.flags = 0; 1733147072Sbrooks } else { 1734147072Sbrooks memset(&ip->client->packet.ciaddr, 0, 1735147072Sbrooks sizeof(ip->client->packet.ciaddr)); 1736147072Sbrooks ip->client->packet.flags = 0; 1737147072Sbrooks } 1738147072Sbrooks 1739147072Sbrooks memset(&ip->client->packet.yiaddr, 0, 1740147072Sbrooks sizeof(ip->client->packet.yiaddr)); 1741147072Sbrooks memset(&ip->client->packet.siaddr, 0, 1742147072Sbrooks sizeof(ip->client->packet.siaddr)); 1743147072Sbrooks memset(&ip->client->packet.giaddr, 0, 1744147072Sbrooks sizeof(ip->client->packet.giaddr)); 1745147072Sbrooks memcpy(ip->client->packet.chaddr, 1746147072Sbrooks ip->hw_address.haddr, ip->hw_address.hlen); 1747147072Sbrooks} 1748147072Sbrooks 1749147072Sbrooksvoid 1750147072Sbrooksmake_decline(struct interface_info *ip, struct client_lease *lease) 1751147072Sbrooks{ 1752147072Sbrooks struct tree_cache *options[256], message_type_tree; 1753147072Sbrooks struct tree_cache requested_address_tree; 1754147072Sbrooks struct tree_cache server_id_tree, client_id_tree; 1755147072Sbrooks unsigned char decline = DHCPDECLINE; 1756147072Sbrooks int i; 1757147072Sbrooks 1758147072Sbrooks memset(options, 0, sizeof(options)); 1759147072Sbrooks memset(&ip->client->packet, 0, sizeof(ip->client->packet)); 1760147072Sbrooks 1761147072Sbrooks /* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */ 1762147072Sbrooks i = DHO_DHCP_MESSAGE_TYPE; 1763147072Sbrooks options[i] = &message_type_tree; 1764147072Sbrooks options[i]->value = &decline; 1765147072Sbrooks options[i]->len = sizeof(decline); 1766147072Sbrooks options[i]->buf_size = sizeof(decline); 1767147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1768147072Sbrooks 1769147072Sbrooks /* Send back the server identifier... */ 1770147072Sbrooks i = DHO_DHCP_SERVER_IDENTIFIER; 1771147072Sbrooks options[i] = &server_id_tree; 1772147072Sbrooks options[i]->value = lease->options[i].data; 1773147072Sbrooks options[i]->len = lease->options[i].len; 1774147072Sbrooks options[i]->buf_size = lease->options[i].len; 1775147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1776147072Sbrooks 1777147072Sbrooks /* Send back the address we're declining. */ 1778147072Sbrooks i = DHO_DHCP_REQUESTED_ADDRESS; 1779147072Sbrooks options[i] = &requested_address_tree; 1780147072Sbrooks options[i]->value = lease->address.iabuf; 1781147072Sbrooks options[i]->len = lease->address.len; 1782147072Sbrooks options[i]->buf_size = lease->address.len; 1783147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1784147072Sbrooks 1785147072Sbrooks /* Send the uid if the user supplied one. */ 1786147072Sbrooks i = DHO_DHCP_CLIENT_IDENTIFIER; 1787147072Sbrooks if (ip->client->config->send_options[i].len) { 1788147072Sbrooks options[i] = &client_id_tree; 1789147072Sbrooks options[i]->value = ip->client->config->send_options[i].data; 1790147072Sbrooks options[i]->len = ip->client->config->send_options[i].len; 1791147072Sbrooks options[i]->buf_size = ip->client->config->send_options[i].len; 1792147072Sbrooks options[i]->timeout = 0xFFFFFFFF; 1793147072Sbrooks } 1794147072Sbrooks 1795147072Sbrooks 1796147072Sbrooks /* Set up the option buffer... */ 1797147072Sbrooks ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, 1798147072Sbrooks options, 0, 0, 0, NULL, 0); 1799147072Sbrooks if (ip->client->packet_length < BOOTP_MIN_LEN) 1800147072Sbrooks ip->client->packet_length = BOOTP_MIN_LEN; 1801147072Sbrooks 1802147072Sbrooks ip->client->packet.op = BOOTREQUEST; 1803147072Sbrooks ip->client->packet.htype = ip->hw_address.htype; 1804147072Sbrooks ip->client->packet.hlen = ip->hw_address.hlen; 1805147072Sbrooks ip->client->packet.hops = 0; 1806147072Sbrooks ip->client->packet.xid = ip->client->xid; 1807147072Sbrooks ip->client->packet.secs = 0; /* Filled in by send_request. */ 1808147072Sbrooks ip->client->packet.flags = 0; 1809147072Sbrooks 1810147072Sbrooks /* ciaddr must always be zero. */ 1811147072Sbrooks memset(&ip->client->packet.ciaddr, 0, 1812147072Sbrooks sizeof(ip->client->packet.ciaddr)); 1813147072Sbrooks memset(&ip->client->packet.yiaddr, 0, 1814147072Sbrooks sizeof(ip->client->packet.yiaddr)); 1815147072Sbrooks memset(&ip->client->packet.siaddr, 0, 1816147072Sbrooks sizeof(ip->client->packet.siaddr)); 1817147072Sbrooks memset(&ip->client->packet.giaddr, 0, 1818147072Sbrooks sizeof(ip->client->packet.giaddr)); 1819147072Sbrooks memcpy(ip->client->packet.chaddr, 1820147072Sbrooks ip->hw_address.haddr, ip->hw_address.hlen); 1821147072Sbrooks} 1822147072Sbrooks 1823147072Sbrooksvoid 1824147072Sbrooksfree_client_lease(struct client_lease *lease) 1825147072Sbrooks{ 1826147072Sbrooks int i; 1827147072Sbrooks 1828147072Sbrooks if (lease->server_name) 1829147072Sbrooks free(lease->server_name); 1830147072Sbrooks if (lease->filename) 1831147072Sbrooks free(lease->filename); 1832147072Sbrooks for (i = 0; i < 256; i++) { 1833147072Sbrooks if (lease->options[i].len) 1834147072Sbrooks free(lease->options[i].data); 1835147072Sbrooks } 1836147072Sbrooks free(lease); 1837147072Sbrooks} 1838147072Sbrooks 1839147072SbrooksFILE *leaseFile; 1840147072Sbrooks 1841147072Sbrooksvoid 1842147072Sbrooksrewrite_client_leases(void) 1843147072Sbrooks{ 1844147072Sbrooks struct client_lease *lp; 1845255219Spjd cap_rights_t rights; 1846147072Sbrooks 1847147072Sbrooks if (!leaseFile) { 1848147072Sbrooks leaseFile = fopen(path_dhclient_db, "w"); 1849147072Sbrooks if (!leaseFile) 1850147072Sbrooks error("can't create %s: %m", path_dhclient_db); 1851283978Spkelsey cap_rights_init(&rights, CAP_FCNTL, CAP_FSTAT, CAP_FSYNC, 1852283978Spkelsey CAP_FTRUNCATE, CAP_SEEK, CAP_WRITE); 1853255219Spjd if (cap_rights_limit(fileno(leaseFile), &rights) < 0 && 1854252631Spjd errno != ENOSYS) { 1855252631Spjd error("can't limit lease descriptor: %m"); 1856252631Spjd } 1857283978Spkelsey if (cap_fcntls_limit(fileno(leaseFile), CAP_FCNTL_GETFL) < 0 && 1858283978Spkelsey errno != ENOSYS) { 1859283978Spkelsey error("can't limit lease descriptor fcntls: %m"); 1860283978Spkelsey } 1861147072Sbrooks } else { 1862147072Sbrooks fflush(leaseFile); 1863147072Sbrooks rewind(leaseFile); 1864147072Sbrooks } 1865147072Sbrooks 1866147072Sbrooks for (lp = ifi->client->leases; lp; lp = lp->next) 1867147072Sbrooks write_client_lease(ifi, lp, 1); 1868147072Sbrooks if (ifi->client->active) 1869147072Sbrooks write_client_lease(ifi, ifi->client->active, 1); 1870147072Sbrooks 1871147072Sbrooks fflush(leaseFile); 1872147072Sbrooks ftruncate(fileno(leaseFile), ftello(leaseFile)); 1873147072Sbrooks fsync(fileno(leaseFile)); 1874147072Sbrooks} 1875147072Sbrooks 1876147072Sbrooksvoid 1877147072Sbrookswrite_client_lease(struct interface_info *ip, struct client_lease *lease, 1878147072Sbrooks int rewrite) 1879147072Sbrooks{ 1880147072Sbrooks static int leases_written; 1881147072Sbrooks struct tm *t; 1882147072Sbrooks int i; 1883147072Sbrooks 1884147072Sbrooks if (!rewrite) { 1885147072Sbrooks if (leases_written++ > 20) { 1886147072Sbrooks rewrite_client_leases(); 1887147072Sbrooks leases_written = 0; 1888147072Sbrooks } 1889147072Sbrooks } 1890147072Sbrooks 1891147072Sbrooks /* If the lease came from the config file, we don't need to stash 1892147072Sbrooks a copy in the lease database. */ 1893147072Sbrooks if (lease->is_static) 1894147072Sbrooks return; 1895147072Sbrooks 1896147072Sbrooks if (!leaseFile) { /* XXX */ 1897147072Sbrooks leaseFile = fopen(path_dhclient_db, "w"); 1898147072Sbrooks if (!leaseFile) 1899147072Sbrooks error("can't create %s: %m", path_dhclient_db); 1900147072Sbrooks } 1901147072Sbrooks 1902147072Sbrooks fprintf(leaseFile, "lease {\n"); 1903147072Sbrooks if (lease->is_bootp) 1904147072Sbrooks fprintf(leaseFile, " bootp;\n"); 1905147072Sbrooks fprintf(leaseFile, " interface \"%s\";\n", ip->name); 1906147072Sbrooks fprintf(leaseFile, " fixed-address %s;\n", piaddr(lease->address)); 1907252506Sbms if (lease->nextserver.len == sizeof(inaddr_any) && 1908252506Sbms 0 != memcmp(lease->nextserver.iabuf, &inaddr_any, 1909252506Sbms sizeof(inaddr_any))) 1910252506Sbms fprintf(leaseFile, " next-server %s;\n", 1911252506Sbms piaddr(lease->nextserver)); 1912147072Sbrooks if (lease->filename) 1913147072Sbrooks fprintf(leaseFile, " filename \"%s\";\n", lease->filename); 1914147072Sbrooks if (lease->server_name) 1915147072Sbrooks fprintf(leaseFile, " server-name \"%s\";\n", 1916147072Sbrooks lease->server_name); 1917147072Sbrooks if (lease->medium) 1918147072Sbrooks fprintf(leaseFile, " medium \"%s\";\n", lease->medium->string); 1919147072Sbrooks for (i = 0; i < 256; i++) 1920147072Sbrooks if (lease->options[i].len) 1921147072Sbrooks fprintf(leaseFile, " option %s %s;\n", 1922147072Sbrooks dhcp_options[i].name, 1923147072Sbrooks pretty_print_option(i, lease->options[i].data, 1924147072Sbrooks lease->options[i].len, 1, 1)); 1925147072Sbrooks 1926147072Sbrooks t = gmtime(&lease->renewal); 1927147072Sbrooks fprintf(leaseFile, " renew %d %d/%d/%d %02d:%02d:%02d;\n", 1928147072Sbrooks t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, 1929147072Sbrooks t->tm_hour, t->tm_min, t->tm_sec); 1930147072Sbrooks t = gmtime(&lease->rebind); 1931147072Sbrooks fprintf(leaseFile, " rebind %d %d/%d/%d %02d:%02d:%02d;\n", 1932147072Sbrooks t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, 1933147072Sbrooks t->tm_hour, t->tm_min, t->tm_sec); 1934147072Sbrooks t = gmtime(&lease->expiry); 1935147072Sbrooks fprintf(leaseFile, " expire %d %d/%d/%d %02d:%02d:%02d;\n", 1936147072Sbrooks t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, 1937147072Sbrooks t->tm_hour, t->tm_min, t->tm_sec); 1938147072Sbrooks fprintf(leaseFile, "}\n"); 1939147072Sbrooks fflush(leaseFile); 1940147072Sbrooks} 1941147072Sbrooks 1942147072Sbrooksvoid 1943147072Sbrooksscript_init(char *reason, struct string_list *medium) 1944147072Sbrooks{ 1945147072Sbrooks size_t len, mediumlen = 0; 1946147072Sbrooks struct imsg_hdr hdr; 1947147072Sbrooks struct buf *buf; 1948147072Sbrooks int errs; 1949147072Sbrooks 1950147072Sbrooks if (medium != NULL && medium->string != NULL) 1951147072Sbrooks mediumlen = strlen(medium->string); 1952147072Sbrooks 1953147072Sbrooks hdr.code = IMSG_SCRIPT_INIT; 1954147072Sbrooks hdr.len = sizeof(struct imsg_hdr) + 1955147072Sbrooks sizeof(size_t) + mediumlen + 1956147072Sbrooks sizeof(size_t) + strlen(reason); 1957147072Sbrooks 1958147072Sbrooks if ((buf = buf_open(hdr.len)) == NULL) 1959147072Sbrooks error("buf_open: %m"); 1960147072Sbrooks 1961147072Sbrooks errs = 0; 1962147072Sbrooks errs += buf_add(buf, &hdr, sizeof(hdr)); 1963147072Sbrooks errs += buf_add(buf, &mediumlen, sizeof(mediumlen)); 1964147072Sbrooks if (mediumlen > 0) 1965147072Sbrooks errs += buf_add(buf, medium->string, mediumlen); 1966147072Sbrooks len = strlen(reason); 1967147072Sbrooks errs += buf_add(buf, &len, sizeof(len)); 1968147072Sbrooks errs += buf_add(buf, reason, len); 1969147072Sbrooks 1970147072Sbrooks if (errs) 1971147072Sbrooks error("buf_add: %m"); 1972147072Sbrooks 1973147072Sbrooks if (buf_close(privfd, buf) == -1) 1974147072Sbrooks error("buf_close: %m"); 1975147072Sbrooks} 1976147072Sbrooks 1977147072Sbrooksvoid 1978147072Sbrookspriv_script_init(char *reason, char *medium) 1979147072Sbrooks{ 1980147072Sbrooks struct interface_info *ip = ifi; 1981147072Sbrooks 1982147072Sbrooks if (ip) { 1983147072Sbrooks ip->client->scriptEnvsize = 100; 1984147072Sbrooks if (ip->client->scriptEnv == NULL) 1985147072Sbrooks ip->client->scriptEnv = 1986147072Sbrooks malloc(ip->client->scriptEnvsize * sizeof(char *)); 1987147072Sbrooks if (ip->client->scriptEnv == NULL) 1988147072Sbrooks error("script_init: no memory for environment"); 1989147072Sbrooks 1990147072Sbrooks ip->client->scriptEnv[0] = strdup(CLIENT_PATH); 1991147072Sbrooks if (ip->client->scriptEnv[0] == NULL) 1992147072Sbrooks error("script_init: no memory for environment"); 1993147072Sbrooks 1994147072Sbrooks ip->client->scriptEnv[1] = NULL; 1995147072Sbrooks 1996147072Sbrooks script_set_env(ip->client, "", "interface", ip->name); 1997147072Sbrooks 1998147072Sbrooks if (medium) 1999147072Sbrooks script_set_env(ip->client, "", "medium", medium); 2000147072Sbrooks 2001147072Sbrooks script_set_env(ip->client, "", "reason", reason); 2002147072Sbrooks } 2003147072Sbrooks} 2004147072Sbrooks 2005147072Sbrooksvoid 2006147072Sbrookspriv_script_write_params(char *prefix, struct client_lease *lease) 2007147072Sbrooks{ 2008147072Sbrooks struct interface_info *ip = ifi; 2009149727Sbrooks u_int8_t dbuf[1500], *dp = NULL; 2010149727Sbrooks int i, len; 2011147072Sbrooks char tbuf[128]; 2012147072Sbrooks 2013147072Sbrooks script_set_env(ip->client, prefix, "ip_address", 2014147072Sbrooks piaddr(lease->address)); 2015147072Sbrooks 2016149727Sbrooks if (ip->client->config->default_actions[DHO_SUBNET_MASK] == 2017149727Sbrooks ACTION_SUPERSEDE) { 2018149727Sbrooks dp = ip->client->config->defaults[DHO_SUBNET_MASK].data; 2019149727Sbrooks len = ip->client->config->defaults[DHO_SUBNET_MASK].len; 2020149727Sbrooks } else { 2021149727Sbrooks dp = lease->options[DHO_SUBNET_MASK].data; 2022149727Sbrooks len = lease->options[DHO_SUBNET_MASK].len; 2023149727Sbrooks } 2024149727Sbrooks if (len && (len < sizeof(lease->address.iabuf))) { 2025147072Sbrooks struct iaddr netmask, subnet, broadcast; 2026147072Sbrooks 2027149727Sbrooks memcpy(netmask.iabuf, dp, len); 2028149727Sbrooks netmask.len = len; 2029147072Sbrooks subnet = subnet_number(lease->address, netmask); 2030147072Sbrooks if (subnet.len) { 2031147072Sbrooks script_set_env(ip->client, prefix, "network_number", 2032147072Sbrooks piaddr(subnet)); 2033147072Sbrooks if (!lease->options[DHO_BROADCAST_ADDRESS].len) { 2034147072Sbrooks broadcast = broadcast_addr(subnet, netmask); 2035147072Sbrooks if (broadcast.len) 2036147072Sbrooks script_set_env(ip->client, prefix, 2037147072Sbrooks "broadcast_address", 2038147072Sbrooks piaddr(broadcast)); 2039147072Sbrooks } 2040147072Sbrooks } 2041147072Sbrooks } 2042147072Sbrooks 2043147072Sbrooks if (lease->filename) 2044147072Sbrooks script_set_env(ip->client, prefix, "filename", lease->filename); 2045147072Sbrooks if (lease->server_name) 2046147072Sbrooks script_set_env(ip->client, prefix, "server_name", 2047147072Sbrooks lease->server_name); 2048147072Sbrooks for (i = 0; i < 256; i++) { 2049149727Sbrooks len = 0; 2050147072Sbrooks 2051147072Sbrooks if (ip->client->config->defaults[i].len) { 2052147072Sbrooks if (lease->options[i].len) { 2053147072Sbrooks switch ( 2054147072Sbrooks ip->client->config->default_actions[i]) { 2055147072Sbrooks case ACTION_DEFAULT: 2056147072Sbrooks dp = lease->options[i].data; 2057147072Sbrooks len = lease->options[i].len; 2058147072Sbrooks break; 2059147072Sbrooks case ACTION_SUPERSEDE: 2060147072Sbrookssupersede: 2061147072Sbrooks dp = ip->client-> 2062147072Sbrooks config->defaults[i].data; 2063147072Sbrooks len = ip->client-> 2064147072Sbrooks config->defaults[i].len; 2065147072Sbrooks break; 2066147072Sbrooks case ACTION_PREPEND: 2067147072Sbrooks len = ip->client-> 2068147072Sbrooks config->defaults[i].len + 2069147072Sbrooks lease->options[i].len; 2070193765Sbrian if (len >= sizeof(dbuf)) { 2071147072Sbrooks warning("no space to %s %s", 2072147072Sbrooks "prepend option", 2073147072Sbrooks dhcp_options[i].name); 2074147072Sbrooks goto supersede; 2075147072Sbrooks } 2076147072Sbrooks dp = dbuf; 2077147072Sbrooks memcpy(dp, 2078147072Sbrooks ip->client-> 2079147072Sbrooks config->defaults[i].data, 2080147072Sbrooks ip->client-> 2081147072Sbrooks config->defaults[i].len); 2082147072Sbrooks memcpy(dp + ip->client-> 2083147072Sbrooks config->defaults[i].len, 2084147072Sbrooks lease->options[i].data, 2085147072Sbrooks lease->options[i].len); 2086147072Sbrooks dp[len] = '\0'; 2087147072Sbrooks break; 2088147072Sbrooks case ACTION_APPEND: 2089193765Sbrian /* 2090193765Sbrian * When we append, we assume that we're 2091193765Sbrian * appending to text. Some MS servers 2092193765Sbrian * include a NUL byte at the end of 2093193765Sbrian * the search string provided. 2094193765Sbrian */ 2095147072Sbrooks len = ip->client-> 2096147072Sbrooks config->defaults[i].len + 2097147072Sbrooks lease->options[i].len; 2098193765Sbrian if (len >= sizeof(dbuf)) { 2099147072Sbrooks warning("no space to %s %s", 2100147072Sbrooks "append option", 2101147072Sbrooks dhcp_options[i].name); 2102147072Sbrooks goto supersede; 2103147072Sbrooks } 2104193765Sbrian memcpy(dbuf, 2105147072Sbrooks lease->options[i].data, 2106147072Sbrooks lease->options[i].len); 2107193765Sbrian for (dp = dbuf + lease->options[i].len; 2108193765Sbrian dp > dbuf; dp--, len--) 2109193765Sbrian if (dp[-1] != '\0') 2110193765Sbrian break; 2111193765Sbrian memcpy(dp, 2112147072Sbrooks ip->client-> 2113147072Sbrooks config->defaults[i].data, 2114147072Sbrooks ip->client-> 2115147072Sbrooks config->defaults[i].len); 2116193765Sbrian dp = dbuf; 2117147072Sbrooks dp[len] = '\0'; 2118147072Sbrooks } 2119147072Sbrooks } else { 2120147072Sbrooks dp = ip->client-> 2121147072Sbrooks config->defaults[i].data; 2122147072Sbrooks len = ip->client-> 2123147072Sbrooks config->defaults[i].len; 2124147072Sbrooks } 2125147072Sbrooks } else if (lease->options[i].len) { 2126147072Sbrooks len = lease->options[i].len; 2127147072Sbrooks dp = lease->options[i].data; 2128147072Sbrooks } else { 2129147072Sbrooks len = 0; 2130147072Sbrooks } 2131147072Sbrooks if (len) { 2132147072Sbrooks char name[256]; 2133147072Sbrooks 2134147072Sbrooks if (dhcp_option_ev_name(name, sizeof(name), 2135147072Sbrooks &dhcp_options[i])) 2136147072Sbrooks script_set_env(ip->client, prefix, name, 2137147072Sbrooks pretty_print_option(i, dp, len, 0, 0)); 2138147072Sbrooks } 2139147072Sbrooks } 2140147072Sbrooks snprintf(tbuf, sizeof(tbuf), "%d", (int)lease->expiry); 2141147072Sbrooks script_set_env(ip->client, prefix, "expiry", tbuf); 2142147072Sbrooks} 2143147072Sbrooks 2144147072Sbrooksvoid 2145147072Sbrooksscript_write_params(char *prefix, struct client_lease *lease) 2146147072Sbrooks{ 2147147072Sbrooks size_t fn_len = 0, sn_len = 0, pr_len = 0; 2148147072Sbrooks struct imsg_hdr hdr; 2149147072Sbrooks struct buf *buf; 2150147072Sbrooks int errs, i; 2151147072Sbrooks 2152147072Sbrooks if (lease->filename != NULL) 2153147072Sbrooks fn_len = strlen(lease->filename); 2154147072Sbrooks if (lease->server_name != NULL) 2155147072Sbrooks sn_len = strlen(lease->server_name); 2156147072Sbrooks if (prefix != NULL) 2157147072Sbrooks pr_len = strlen(prefix); 2158147072Sbrooks 2159147072Sbrooks hdr.code = IMSG_SCRIPT_WRITE_PARAMS; 2160147072Sbrooks hdr.len = sizeof(hdr) + sizeof(struct client_lease) + 2161147072Sbrooks sizeof(size_t) + fn_len + sizeof(size_t) + sn_len + 2162147072Sbrooks sizeof(size_t) + pr_len; 2163147072Sbrooks 2164147072Sbrooks for (i = 0; i < 256; i++) 2165147072Sbrooks hdr.len += sizeof(int) + lease->options[i].len; 2166147072Sbrooks 2167147072Sbrooks scripttime = time(NULL); 2168147072Sbrooks 2169147072Sbrooks if ((buf = buf_open(hdr.len)) == NULL) 2170147072Sbrooks error("buf_open: %m"); 2171147072Sbrooks 2172147072Sbrooks errs = 0; 2173147072Sbrooks errs += buf_add(buf, &hdr, sizeof(hdr)); 2174147072Sbrooks errs += buf_add(buf, lease, sizeof(struct client_lease)); 2175147072Sbrooks errs += buf_add(buf, &fn_len, sizeof(fn_len)); 2176147072Sbrooks errs += buf_add(buf, lease->filename, fn_len); 2177147072Sbrooks errs += buf_add(buf, &sn_len, sizeof(sn_len)); 2178147072Sbrooks errs += buf_add(buf, lease->server_name, sn_len); 2179147072Sbrooks errs += buf_add(buf, &pr_len, sizeof(pr_len)); 2180147072Sbrooks errs += buf_add(buf, prefix, pr_len); 2181147072Sbrooks 2182147072Sbrooks for (i = 0; i < 256; i++) { 2183147072Sbrooks errs += buf_add(buf, &lease->options[i].len, 2184147072Sbrooks sizeof(lease->options[i].len)); 2185147072Sbrooks errs += buf_add(buf, lease->options[i].data, 2186147072Sbrooks lease->options[i].len); 2187147072Sbrooks } 2188147072Sbrooks 2189147072Sbrooks if (errs) 2190147072Sbrooks error("buf_add: %m"); 2191147072Sbrooks 2192147072Sbrooks if (buf_close(privfd, buf) == -1) 2193147072Sbrooks error("buf_close: %m"); 2194147072Sbrooks} 2195147072Sbrooks 2196147072Sbrooksint 2197147072Sbrooksscript_go(void) 2198147072Sbrooks{ 2199147072Sbrooks struct imsg_hdr hdr; 2200147072Sbrooks struct buf *buf; 2201147072Sbrooks int ret; 2202147072Sbrooks 2203147072Sbrooks hdr.code = IMSG_SCRIPT_GO; 2204147072Sbrooks hdr.len = sizeof(struct imsg_hdr); 2205147072Sbrooks 2206147072Sbrooks if ((buf = buf_open(hdr.len)) == NULL) 2207147072Sbrooks error("buf_open: %m"); 2208147072Sbrooks 2209147072Sbrooks if (buf_add(buf, &hdr, sizeof(hdr))) 2210147072Sbrooks error("buf_add: %m"); 2211147072Sbrooks 2212147072Sbrooks if (buf_close(privfd, buf) == -1) 2213147072Sbrooks error("buf_close: %m"); 2214147072Sbrooks 2215147072Sbrooks bzero(&hdr, sizeof(hdr)); 2216147072Sbrooks buf_read(privfd, &hdr, sizeof(hdr)); 2217147072Sbrooks if (hdr.code != IMSG_SCRIPT_GO_RET) 2218147072Sbrooks error("unexpected msg type %u", hdr.code); 2219147072Sbrooks if (hdr.len != sizeof(hdr) + sizeof(int)) 2220147072Sbrooks error("received corrupted message"); 2221147072Sbrooks buf_read(privfd, &ret, sizeof(ret)); 2222147072Sbrooks 2223209756Sbrian scripttime = time(NULL); 2224209756Sbrian 2225147072Sbrooks return (ret); 2226147072Sbrooks} 2227147072Sbrooks 2228147072Sbrooksint 2229147072Sbrookspriv_script_go(void) 2230147072Sbrooks{ 2231147072Sbrooks char *scriptName, *argv[2], **envp, *epp[3], reason[] = "REASON=NBI"; 2232147072Sbrooks static char client_path[] = CLIENT_PATH; 2233147072Sbrooks struct interface_info *ip = ifi; 2234147072Sbrooks int pid, wpid, wstatus; 2235147072Sbrooks 2236147072Sbrooks scripttime = time(NULL); 2237147072Sbrooks 2238147072Sbrooks if (ip) { 2239147072Sbrooks scriptName = ip->client->config->script_name; 2240147072Sbrooks envp = ip->client->scriptEnv; 2241147072Sbrooks } else { 2242147072Sbrooks scriptName = top_level_config.script_name; 2243147072Sbrooks epp[0] = reason; 2244147072Sbrooks epp[1] = client_path; 2245147072Sbrooks epp[2] = NULL; 2246147072Sbrooks envp = epp; 2247147072Sbrooks } 2248147072Sbrooks 2249147072Sbrooks argv[0] = scriptName; 2250147072Sbrooks argv[1] = NULL; 2251147072Sbrooks 2252147072Sbrooks pid = fork(); 2253147072Sbrooks if (pid < 0) { 2254147072Sbrooks error("fork: %m"); 2255147072Sbrooks wstatus = 0; 2256147072Sbrooks } else if (pid) { 2257147072Sbrooks do { 2258147072Sbrooks wpid = wait(&wstatus); 2259147072Sbrooks } while (wpid != pid && wpid > 0); 2260147072Sbrooks if (wpid < 0) { 2261147072Sbrooks error("wait: %m"); 2262147072Sbrooks wstatus = 0; 2263147072Sbrooks } 2264147072Sbrooks } else { 2265147072Sbrooks execve(scriptName, argv, envp); 2266147072Sbrooks error("execve (%s, ...): %m", scriptName); 2267147072Sbrooks } 2268147072Sbrooks 2269147072Sbrooks if (ip) 2270147072Sbrooks script_flush_env(ip->client); 2271147072Sbrooks 2272147072Sbrooks return (wstatus & 0xff); 2273147072Sbrooks} 2274147072Sbrooks 2275147072Sbrooksvoid 2276147072Sbrooksscript_set_env(struct client_state *client, const char *prefix, 2277147072Sbrooks const char *name, const char *value) 2278147072Sbrooks{ 2279147072Sbrooks int i, j, namelen; 2280147072Sbrooks 2281299156Ssephe /* No `` or $() command substitution allowed in environment values! */ 2282299156Ssephe for (j=0; j < strlen(value); j++) 2283299156Ssephe switch (value[j]) { 2284299156Ssephe case '`': 2285299156Ssephe case '$': 2286299156Ssephe warning("illegal character (%c) in value '%s'", 2287299156Ssephe value[j], value); 2288299156Ssephe /* Ignore this option */ 2289299156Ssephe return; 2290299156Ssephe } 2291299156Ssephe 2292147072Sbrooks namelen = strlen(name); 2293147072Sbrooks 2294147072Sbrooks for (i = 0; client->scriptEnv[i]; i++) 2295147072Sbrooks if (strncmp(client->scriptEnv[i], name, namelen) == 0 && 2296147072Sbrooks client->scriptEnv[i][namelen] == '=') 2297147072Sbrooks break; 2298147072Sbrooks 2299147072Sbrooks if (client->scriptEnv[i]) 2300147072Sbrooks /* Reuse the slot. */ 2301147072Sbrooks free(client->scriptEnv[i]); 2302147072Sbrooks else { 2303147072Sbrooks /* New variable. Expand if necessary. */ 2304147072Sbrooks if (i >= client->scriptEnvsize - 1) { 2305147072Sbrooks char **newscriptEnv; 2306147072Sbrooks int newscriptEnvsize = client->scriptEnvsize + 50; 2307147072Sbrooks 2308147072Sbrooks newscriptEnv = realloc(client->scriptEnv, 2309147072Sbrooks newscriptEnvsize); 2310147072Sbrooks if (newscriptEnv == NULL) { 2311147072Sbrooks free(client->scriptEnv); 2312147072Sbrooks client->scriptEnv = NULL; 2313147072Sbrooks client->scriptEnvsize = 0; 2314147072Sbrooks error("script_set_env: no memory for variable"); 2315147072Sbrooks } 2316147072Sbrooks client->scriptEnv = newscriptEnv; 2317147072Sbrooks client->scriptEnvsize = newscriptEnvsize; 2318147072Sbrooks } 2319147072Sbrooks /* need to set the NULL pointer at end of array beyond 2320147072Sbrooks the new slot. */ 2321147072Sbrooks client->scriptEnv[i + 1] = NULL; 2322147072Sbrooks } 2323147072Sbrooks /* Allocate space and format the variable in the appropriate slot. */ 2324147072Sbrooks client->scriptEnv[i] = malloc(strlen(prefix) + strlen(name) + 1 + 2325147072Sbrooks strlen(value) + 1); 2326147072Sbrooks if (client->scriptEnv[i] == NULL) 2327147072Sbrooks error("script_set_env: no memory for variable assignment"); 2328147072Sbrooks snprintf(client->scriptEnv[i], strlen(prefix) + strlen(name) + 2329147072Sbrooks 1 + strlen(value) + 1, "%s%s=%s", prefix, name, value); 2330147072Sbrooks} 2331147072Sbrooks 2332147072Sbrooksvoid 2333147072Sbrooksscript_flush_env(struct client_state *client) 2334147072Sbrooks{ 2335147072Sbrooks int i; 2336147072Sbrooks 2337147072Sbrooks for (i = 0; client->scriptEnv[i]; i++) { 2338147072Sbrooks free(client->scriptEnv[i]); 2339147072Sbrooks client->scriptEnv[i] = NULL; 2340147072Sbrooks } 2341147072Sbrooks client->scriptEnvsize = 0; 2342147072Sbrooks} 2343147072Sbrooks 2344147072Sbrooksint 2345147072Sbrooksdhcp_option_ev_name(char *buf, size_t buflen, struct option *option) 2346147072Sbrooks{ 2347147072Sbrooks int i; 2348147072Sbrooks 2349147072Sbrooks for (i = 0; option->name[i]; i++) { 2350147072Sbrooks if (i + 1 == buflen) 2351147072Sbrooks return 0; 2352147072Sbrooks if (option->name[i] == '-') 2353147072Sbrooks buf[i] = '_'; 2354147072Sbrooks else 2355147072Sbrooks buf[i] = option->name[i]; 2356147072Sbrooks } 2357147072Sbrooks 2358147072Sbrooks buf[i] = 0; 2359147072Sbrooks return 1; 2360147072Sbrooks} 2361147072Sbrooks 2362147072Sbrooksvoid 2363147072Sbrooksgo_daemon(void) 2364147072Sbrooks{ 2365147072Sbrooks static int state = 0; 2366255219Spjd cap_rights_t rights; 2367147072Sbrooks 2368147072Sbrooks if (no_daemon || state) 2369147072Sbrooks return; 2370147072Sbrooks 2371147072Sbrooks state = 1; 2372147072Sbrooks 2373147072Sbrooks /* Stop logging to stderr... */ 2374147072Sbrooks log_perror = 0; 2375147072Sbrooks 2376147072Sbrooks if (daemon(1, 0) == -1) 2377147072Sbrooks error("daemon"); 2378147072Sbrooks 2379255219Spjd cap_rights_init(&rights); 2380255219Spjd 2381252632Spjd if (pidfile != NULL) { 2382226345Sdes pidfile_write(pidfile); 2383255219Spjd if (cap_rights_limit(pidfile_fileno(pidfile), &rights) < 0 && 2384252632Spjd errno != ENOSYS) { 2385252632Spjd error("can't limit pidfile descriptor: %m"); 2386252632Spjd } 2387252632Spjd } 2388226345Sdes 2389147072Sbrooks /* we are chrooted, daemon(3) fails to open /dev/null */ 2390147072Sbrooks if (nullfd != -1) { 2391147072Sbrooks dup2(nullfd, STDIN_FILENO); 2392147072Sbrooks dup2(nullfd, STDOUT_FILENO); 2393147072Sbrooks dup2(nullfd, STDERR_FILENO); 2394147072Sbrooks close(nullfd); 2395147072Sbrooks nullfd = -1; 2396147072Sbrooks } 2397252633Spjd 2398255219Spjd if (cap_rights_limit(STDIN_FILENO, &rights) < 0 && errno != ENOSYS) 2399252633Spjd error("can't limit stdin: %m"); 2400255219Spjd cap_rights_init(&rights, CAP_WRITE); 2401255219Spjd if (cap_rights_limit(STDOUT_FILENO, &rights) < 0 && errno != ENOSYS) 2402252633Spjd error("can't limit stdout: %m"); 2403255219Spjd if (cap_rights_limit(STDERR_FILENO, &rights) < 0 && errno != ENOSYS) 2404252633Spjd error("can't limit stderr: %m"); 2405147072Sbrooks} 2406147072Sbrooks 2407147072Sbrooksint 2408147072Sbrookscheck_option(struct client_lease *l, int option) 2409147072Sbrooks{ 2410147072Sbrooks char *opbuf; 2411147072Sbrooks char *sbuf; 2412147072Sbrooks 2413147072Sbrooks /* we use this, since this is what gets passed to dhclient-script */ 2414147072Sbrooks 2415147072Sbrooks opbuf = pretty_print_option(option, l->options[option].data, 2416147072Sbrooks l->options[option].len, 0, 0); 2417147072Sbrooks 2418147072Sbrooks sbuf = option_as_string(option, l->options[option].data, 2419147072Sbrooks l->options[option].len); 2420147072Sbrooks 2421147072Sbrooks switch (option) { 2422147072Sbrooks case DHO_SUBNET_MASK: 2423147072Sbrooks case DHO_TIME_SERVERS: 2424147072Sbrooks case DHO_NAME_SERVERS: 2425147072Sbrooks case DHO_ROUTERS: 2426147072Sbrooks case DHO_DOMAIN_NAME_SERVERS: 2427147072Sbrooks case DHO_LOG_SERVERS: 2428147072Sbrooks case DHO_COOKIE_SERVERS: 2429147072Sbrooks case DHO_LPR_SERVERS: 2430147072Sbrooks case DHO_IMPRESS_SERVERS: 2431147072Sbrooks case DHO_RESOURCE_LOCATION_SERVERS: 2432147072Sbrooks case DHO_SWAP_SERVER: 2433147072Sbrooks case DHO_BROADCAST_ADDRESS: 2434147072Sbrooks case DHO_NIS_SERVERS: 2435147072Sbrooks case DHO_NTP_SERVERS: 2436147072Sbrooks case DHO_NETBIOS_NAME_SERVERS: 2437147072Sbrooks case DHO_NETBIOS_DD_SERVER: 2438147072Sbrooks case DHO_FONT_SERVERS: 2439147072Sbrooks case DHO_DHCP_SERVER_IDENTIFIER: 2440183974Sbrooks case DHO_NISPLUS_SERVERS: 2441183974Sbrooks case DHO_MOBILE_IP_HOME_AGENT: 2442147689Sbrooks case DHO_SMTP_SERVER: 2443147689Sbrooks case DHO_POP_SERVER: 2444147689Sbrooks case DHO_NNTP_SERVER: 2445147689Sbrooks case DHO_WWW_SERVER: 2446147689Sbrooks case DHO_FINGER_SERVER: 2447147689Sbrooks case DHO_IRC_SERVER: 2448183974Sbrooks case DHO_STREETTALK_SERVER: 2449183974Sbrooks case DHO_STREETTALK_DA_SERVER: 2450147072Sbrooks if (!ipv4addrs(opbuf)) { 2451147072Sbrooks warning("Invalid IP address in option: %s", opbuf); 2452147072Sbrooks return (0); 2453147072Sbrooks } 2454147072Sbrooks return (1) ; 2455147072Sbrooks case DHO_HOST_NAME: 2456147072Sbrooks case DHO_NIS_DOMAIN: 2457183974Sbrooks case DHO_NISPLUS_DOMAIN: 2458183974Sbrooks case DHO_TFTP_SERVER_NAME: 2459147072Sbrooks if (!res_hnok(sbuf)) { 2460147072Sbrooks warning("Bogus Host Name option %d: %s (%s)", option, 2461147072Sbrooks sbuf, opbuf); 2462153287Sbrooks l->options[option].len = 0; 2463153287Sbrooks free(l->options[option].data); 2464147072Sbrooks } 2465147072Sbrooks return (1); 2466147686Sbrooks case DHO_DOMAIN_NAME: 2467228259Sdumbbell case DHO_DOMAIN_SEARCH: 2468149639Sbrooks if (!res_hnok(sbuf)) { 2469149639Sbrooks if (!check_search(sbuf)) { 2470149639Sbrooks warning("Bogus domain search list %d: %s (%s)", 2471149639Sbrooks option, sbuf, opbuf); 2472153287Sbrooks l->options[option].len = 0; 2473153287Sbrooks free(l->options[option].data); 2474149639Sbrooks } 2475149639Sbrooks } 2476149639Sbrooks return (1); 2477147072Sbrooks case DHO_PAD: 2478147072Sbrooks case DHO_TIME_OFFSET: 2479147072Sbrooks case DHO_BOOT_SIZE: 2480147072Sbrooks case DHO_MERIT_DUMP: 2481147072Sbrooks case DHO_ROOT_PATH: 2482147072Sbrooks case DHO_EXTENSIONS_PATH: 2483147072Sbrooks case DHO_IP_FORWARDING: 2484147072Sbrooks case DHO_NON_LOCAL_SOURCE_ROUTING: 2485147072Sbrooks case DHO_POLICY_FILTER: 2486147072Sbrooks case DHO_MAX_DGRAM_REASSEMBLY: 2487147072Sbrooks case DHO_DEFAULT_IP_TTL: 2488147072Sbrooks case DHO_PATH_MTU_AGING_TIMEOUT: 2489147072Sbrooks case DHO_PATH_MTU_PLATEAU_TABLE: 2490147072Sbrooks case DHO_INTERFACE_MTU: 2491147072Sbrooks case DHO_ALL_SUBNETS_LOCAL: 2492147072Sbrooks case DHO_PERFORM_MASK_DISCOVERY: 2493147072Sbrooks case DHO_MASK_SUPPLIER: 2494147072Sbrooks case DHO_ROUTER_DISCOVERY: 2495147072Sbrooks case DHO_ROUTER_SOLICITATION_ADDRESS: 2496147072Sbrooks case DHO_STATIC_ROUTES: 2497147072Sbrooks case DHO_TRAILER_ENCAPSULATION: 2498147072Sbrooks case DHO_ARP_CACHE_TIMEOUT: 2499147072Sbrooks case DHO_IEEE802_3_ENCAPSULATION: 2500147072Sbrooks case DHO_DEFAULT_TCP_TTL: 2501147072Sbrooks case DHO_TCP_KEEPALIVE_INTERVAL: 2502147072Sbrooks case DHO_TCP_KEEPALIVE_GARBAGE: 2503147072Sbrooks case DHO_VENDOR_ENCAPSULATED_OPTIONS: 2504147072Sbrooks case DHO_NETBIOS_NODE_TYPE: 2505147072Sbrooks case DHO_NETBIOS_SCOPE: 2506147072Sbrooks case DHO_X_DISPLAY_MANAGER: 2507147072Sbrooks case DHO_DHCP_REQUESTED_ADDRESS: 2508147072Sbrooks case DHO_DHCP_LEASE_TIME: 2509147072Sbrooks case DHO_DHCP_OPTION_OVERLOAD: 2510147072Sbrooks case DHO_DHCP_MESSAGE_TYPE: 2511147072Sbrooks case DHO_DHCP_PARAMETER_REQUEST_LIST: 2512147072Sbrooks case DHO_DHCP_MESSAGE: 2513147072Sbrooks case DHO_DHCP_MAX_MESSAGE_SIZE: 2514147072Sbrooks case DHO_DHCP_RENEWAL_TIME: 2515147072Sbrooks case DHO_DHCP_REBINDING_TIME: 2516147072Sbrooks case DHO_DHCP_CLASS_IDENTIFIER: 2517147072Sbrooks case DHO_DHCP_CLIENT_IDENTIFIER: 2518183974Sbrooks case DHO_BOOTFILE_NAME: 2519147072Sbrooks case DHO_DHCP_USER_CLASS_ID: 2520147072Sbrooks case DHO_END: 2521147072Sbrooks return (1); 2522166602Semaste case DHO_CLASSLESS_ROUTES: 2523166602Semaste return (check_classless_option(l->options[option].data, 2524166602Semaste l->options[option].len)); 2525147072Sbrooks default: 2526147072Sbrooks warning("unknown dhcp option value 0x%x", option); 2527147072Sbrooks return (unknown_ok); 2528147072Sbrooks } 2529147072Sbrooks} 2530147072Sbrooks 2531166602Semaste/* RFC 3442 The Classless Static Routes option checks */ 2532147072Sbrooksint 2533166602Semastecheck_classless_option(unsigned char *data, int len) 2534166602Semaste{ 2535166602Semaste int i = 0; 2536166602Semaste unsigned char width; 2537166602Semaste in_addr_t addr, mask; 2538166602Semaste 2539166602Semaste if (len < 5) { 2540166602Semaste warning("Too small length: %d", len); 2541166602Semaste return (0); 2542166602Semaste } 2543166602Semaste while(i < len) { 2544166602Semaste width = data[i++]; 2545166602Semaste if (width == 0) { 2546166602Semaste i += 4; 2547166602Semaste continue; 2548166602Semaste } else if (width < 9) { 2549252621Spjd addr = (in_addr_t)(data[i] << 24); 2550166602Semaste i += 1; 2551166602Semaste } else if (width < 17) { 2552252621Spjd addr = (in_addr_t)(data[i] << 24) + 2553166602Semaste (in_addr_t)(data[i + 1] << 16); 2554166602Semaste i += 2; 2555166602Semaste } else if (width < 25) { 2556252621Spjd addr = (in_addr_t)(data[i] << 24) + 2557166602Semaste (in_addr_t)(data[i + 1] << 16) + 2558166602Semaste (in_addr_t)(data[i + 2] << 8); 2559166602Semaste i += 3; 2560166602Semaste } else if (width < 33) { 2561252621Spjd addr = (in_addr_t)(data[i] << 24) + 2562166602Semaste (in_addr_t)(data[i + 1] << 16) + 2563166602Semaste (in_addr_t)(data[i + 2] << 8) + 2564166602Semaste data[i + 3]; 2565166602Semaste i += 4; 2566166602Semaste } else { 2567166602Semaste warning("Incorrect subnet width: %d", width); 2568166602Semaste return (0); 2569166602Semaste } 2570166602Semaste mask = (in_addr_t)(~0) << (32 - width); 2571166602Semaste addr = ntohl(addr); 2572166602Semaste mask = ntohl(mask); 2573166602Semaste 2574166602Semaste /* 2575166602Semaste * From RFC 3442: 2576166602Semaste * ... After deriving a subnet number and subnet mask 2577166602Semaste * from each destination descriptor, the DHCP client 2578166602Semaste * MUST zero any bits in the subnet number where the 2579166602Semaste * corresponding bit in the mask is zero... 2580166602Semaste */ 2581166602Semaste if ((addr & mask) != addr) { 2582166602Semaste addr &= mask; 2583166602Semaste data[i - 1] = (unsigned char)( 2584166602Semaste (addr >> (((32 - width)/8)*8)) & 0xFF); 2585252621Spjd } 2586166602Semaste i += 4; 2587166602Semaste } 2588166602Semaste if (i > len) { 2589166602Semaste warning("Incorrect data length: %d (must be %d)", len, i); 2590166602Semaste return (0); 2591166602Semaste } 2592166602Semaste return (1); 2593166602Semaste} 2594166602Semaste 2595166602Semasteint 2596147072Sbrooksres_hnok(const char *dn) 2597147072Sbrooks{ 2598147072Sbrooks int pch = PERIOD, ch = *dn++; 2599147072Sbrooks 2600147072Sbrooks while (ch != '\0') { 2601147072Sbrooks int nch = *dn++; 2602147072Sbrooks 2603147072Sbrooks if (periodchar(ch)) { 2604147072Sbrooks ; 2605147072Sbrooks } else if (periodchar(pch)) { 2606147072Sbrooks if (!borderchar(ch)) 2607147072Sbrooks return (0); 2608147072Sbrooks } else if (periodchar(nch) || nch == '\0') { 2609147072Sbrooks if (!borderchar(ch)) 2610147072Sbrooks return (0); 2611147072Sbrooks } else { 2612147072Sbrooks if (!middlechar(ch)) 2613147072Sbrooks return (0); 2614147072Sbrooks } 2615147072Sbrooks pch = ch, ch = nch; 2616147072Sbrooks } 2617147072Sbrooks return (1); 2618147072Sbrooks} 2619147072Sbrooks 2620149639Sbrooksint 2621149639Sbrookscheck_search(const char *srch) 2622149639Sbrooks{ 2623149639Sbrooks int pch = PERIOD, ch = *srch++; 2624149639Sbrooks int domains = 1; 2625149639Sbrooks 2626149639Sbrooks /* 256 char limit re resolv.conf(5) */ 2627149639Sbrooks if (strlen(srch) > 256) 2628149639Sbrooks return (0); 2629149639Sbrooks 2630149639Sbrooks while (whitechar(ch)) 2631149639Sbrooks ch = *srch++; 2632149639Sbrooks 2633149639Sbrooks while (ch != '\0') { 2634149639Sbrooks int nch = *srch++; 2635149639Sbrooks 2636149639Sbrooks if (periodchar(ch) || whitechar(ch)) { 2637149639Sbrooks ; 2638149639Sbrooks } else if (periodchar(pch)) { 2639149639Sbrooks if (!borderchar(ch)) 2640149639Sbrooks return (0); 2641149639Sbrooks } else if (periodchar(nch) || nch == '\0') { 2642149639Sbrooks if (!borderchar(ch)) 2643149639Sbrooks return (0); 2644149639Sbrooks } else { 2645149639Sbrooks if (!middlechar(ch)) 2646149639Sbrooks return (0); 2647149639Sbrooks } 2648149639Sbrooks if (!whitechar(ch)) { 2649149639Sbrooks pch = ch; 2650149639Sbrooks } else { 2651149639Sbrooks while (whitechar(nch)) { 2652149639Sbrooks nch = *srch++; 2653149639Sbrooks } 2654149639Sbrooks if (nch != '\0') 2655149639Sbrooks domains++; 2656149639Sbrooks pch = PERIOD; 2657149639Sbrooks } 2658149639Sbrooks ch = nch; 2659149639Sbrooks } 2660149639Sbrooks /* 6 domain limit re resolv.conf(5) */ 2661149639Sbrooks if (domains > 6) 2662149639Sbrooks return (0); 2663149639Sbrooks return (1); 2664149639Sbrooks} 2665149639Sbrooks 2666147072Sbrooks/* Does buf consist only of dotted decimal ipv4 addrs? 2667147072Sbrooks * return how many if so, 2668147072Sbrooks * otherwise, return 0 2669147072Sbrooks */ 2670147072Sbrooksint 2671147072Sbrooksipv4addrs(char * buf) 2672147072Sbrooks{ 2673147072Sbrooks struct in_addr jnk; 2674147072Sbrooks int count = 0; 2675147072Sbrooks 2676147072Sbrooks while (inet_aton(buf, &jnk) == 1){ 2677147072Sbrooks count++; 2678147072Sbrooks while (periodchar(*buf) || digitchar(*buf)) 2679147072Sbrooks buf++; 2680147072Sbrooks if (*buf == '\0') 2681147072Sbrooks return (count); 2682147072Sbrooks while (*buf == ' ') 2683147072Sbrooks buf++; 2684147072Sbrooks } 2685147072Sbrooks return (0); 2686147072Sbrooks} 2687147072Sbrooks 2688147072Sbrooks 2689147072Sbrookschar * 2690147072Sbrooksoption_as_string(unsigned int code, unsigned char *data, int len) 2691147072Sbrooks{ 2692147072Sbrooks static char optbuf[32768]; /* XXX */ 2693147072Sbrooks char *op = optbuf; 2694147072Sbrooks int opleft = sizeof(optbuf); 2695147072Sbrooks unsigned char *dp = data; 2696147072Sbrooks 2697147072Sbrooks if (code > 255) 2698147072Sbrooks error("option_as_string: bad code %d", code); 2699147072Sbrooks 2700147072Sbrooks for (; dp < data + len; dp++) { 2701147072Sbrooks if (!isascii(*dp) || !isprint(*dp)) { 2702147072Sbrooks if (dp + 1 != data + len || *dp != 0) { 2703147072Sbrooks snprintf(op, opleft, "\\%03o", *dp); 2704147072Sbrooks op += 4; 2705147072Sbrooks opleft -= 4; 2706147072Sbrooks } 2707147072Sbrooks } else if (*dp == '"' || *dp == '\'' || *dp == '$' || 2708147072Sbrooks *dp == '`' || *dp == '\\') { 2709147072Sbrooks *op++ = '\\'; 2710147072Sbrooks *op++ = *dp; 2711147072Sbrooks opleft -= 2; 2712147072Sbrooks } else { 2713147072Sbrooks *op++ = *dp; 2714147072Sbrooks opleft--; 2715147072Sbrooks } 2716147072Sbrooks } 2717147072Sbrooks if (opleft < 1) 2718147072Sbrooks goto toobig; 2719147072Sbrooks *op = 0; 2720147072Sbrooks return optbuf; 2721147072Sbrookstoobig: 2722147072Sbrooks warning("dhcp option too large"); 2723147072Sbrooks return "<error>"; 2724147072Sbrooks} 2725147072Sbrooks 2726147072Sbrooksint 2727147072Sbrooksfork_privchld(int fd, int fd2) 2728147072Sbrooks{ 2729147072Sbrooks struct pollfd pfd[1]; 2730147072Sbrooks int nfds; 2731147072Sbrooks 2732147072Sbrooks switch (fork()) { 2733147072Sbrooks case -1: 2734147072Sbrooks error("cannot fork"); 2735147072Sbrooks case 0: 2736147072Sbrooks break; 2737147072Sbrooks default: 2738147072Sbrooks return (0); 2739147072Sbrooks } 2740147072Sbrooks 2741147072Sbrooks setproctitle("%s [priv]", ifi->name); 2742147072Sbrooks 2743180130Sed setsid(); 2744147072Sbrooks dup2(nullfd, STDIN_FILENO); 2745147072Sbrooks dup2(nullfd, STDOUT_FILENO); 2746147072Sbrooks dup2(nullfd, STDERR_FILENO); 2747147072Sbrooks close(nullfd); 2748147072Sbrooks close(fd2); 2749252626Spjd close(ifi->rfdesc); 2750252626Spjd ifi->rfdesc = -1; 2751147072Sbrooks 2752147072Sbrooks for (;;) { 2753147072Sbrooks pfd[0].fd = fd; 2754147072Sbrooks pfd[0].events = POLLIN; 2755147072Sbrooks if ((nfds = poll(pfd, 1, INFTIM)) == -1) 2756147072Sbrooks if (errno != EINTR) 2757147072Sbrooks error("poll error"); 2758147072Sbrooks 2759147072Sbrooks if (nfds == 0 || !(pfd[0].revents & POLLIN)) 2760147072Sbrooks continue; 2761147072Sbrooks 2762252626Spjd dispatch_imsg(ifi, fd); 2763147072Sbrooks } 2764147072Sbrooks} 2765