Annotation of embedaddon/dhcp/client/dhclient.c, revision 1.1
1.1 ! misho 1: /* dhclient.c
! 2:
! 3: DHCP Client. */
! 4:
! 5: /*
! 6: * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
! 7: * Copyright (c) 1995-2003 by Internet Software Consortium
! 8: *
! 9: * Permission to use, copy, modify, and distribute this software for any
! 10: * purpose with or without fee is hereby granted, provided that the above
! 11: * copyright notice and this permission notice appear in all copies.
! 12: *
! 13: * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
! 14: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 15: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
! 16: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 17: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 18: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
! 19: * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 20: *
! 21: * Internet Systems Consortium, Inc.
! 22: * 950 Charter Street
! 23: * Redwood City, CA 94063
! 24: * <info@isc.org>
! 25: * https://www.isc.org/
! 26: *
! 27: * This code is based on the original client state machine that was
! 28: * written by Elliot Poger. The code has been extensively hacked on
! 29: * by Ted Lemon since then, so any mistakes you find are probably his
! 30: * fault and not Elliot's.
! 31: */
! 32:
! 33: #include "dhcpd.h"
! 34: #include <syslog.h>
! 35: #include <signal.h>
! 36: #include <errno.h>
! 37: #include <sys/time.h>
! 38: #include <sys/wait.h>
! 39: #include <limits.h>
! 40:
! 41: TIME default_lease_time = 43200; /* 12 hours... */
! 42: TIME max_lease_time = 86400; /* 24 hours... */
! 43:
! 44: const char *path_dhclient_conf = _PATH_DHCLIENT_CONF;
! 45: const char *path_dhclient_db = NULL;
! 46: const char *path_dhclient_pid = NULL;
! 47: static char path_dhclient_script_array[] = _PATH_DHCLIENT_SCRIPT;
! 48: char *path_dhclient_script = path_dhclient_script_array;
! 49:
! 50: /* False (default) => we write and use a pid file */
! 51: isc_boolean_t no_pid_file = ISC_FALSE;
! 52:
! 53: int dhcp_max_agent_option_packet_length = 0;
! 54:
! 55: int interfaces_requested = 0;
! 56:
! 57: struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } };
! 58: struct iaddr iaddr_any = { 4, { 0, 0, 0, 0 } };
! 59: struct in_addr inaddr_any;
! 60: struct sockaddr_in sockaddr_broadcast;
! 61: struct in_addr giaddr;
! 62: struct data_string default_duid;
! 63:
! 64: /* ASSERT_STATE() does nothing now; it used to be
! 65: assert (state_is == state_shouldbe). */
! 66: #define ASSERT_STATE(state_is, state_shouldbe) {}
! 67:
! 68: static const char copyright[] =
! 69: "Copyright 2004-2011 Internet Systems Consortium.";
! 70: static const char arr [] = "All rights reserved.";
! 71: static const char message [] = "Internet Systems Consortium DHCP Client";
! 72: static const char url [] =
! 73: "For info, please visit https://www.isc.org/software/dhcp/";
! 74:
! 75: u_int16_t local_port = 0;
! 76: u_int16_t remote_port = 0;
! 77: int no_daemon = 0;
! 78: struct string_list *client_env = NULL;
! 79: int client_env_count = 0;
! 80: int onetry = 0;
! 81: int quiet = 1;
! 82: int nowait = 0;
! 83: int stateless = 0;
! 84: int wanted_ia_na = -1; /* the absolute value is the real one. */
! 85: int wanted_ia_ta = 0;
! 86: int wanted_ia_pd = 0;
! 87: char *mockup_relay = NULL;
! 88:
! 89: void run_stateless(int exit_mode);
! 90:
! 91: static void usage(void);
! 92:
! 93: static isc_result_t write_duid(struct data_string *duid);
! 94: static void add_reject(struct packet *packet);
! 95:
! 96: static int check_domain_name(const char *ptr, size_t len, int dots);
! 97: static int check_domain_name_list(const char *ptr, size_t len, int dots);
! 98: static int check_option_values(struct universe *universe, unsigned int opt,
! 99: const char *ptr, size_t len);
! 100:
! 101: int
! 102: main(int argc, char **argv) {
! 103: int fd;
! 104: int i;
! 105: struct interface_info *ip;
! 106: struct client_state *client;
! 107: unsigned seed;
! 108: char *server = NULL;
! 109: isc_result_t status;
! 110: int exit_mode = 0;
! 111: int release_mode = 0;
! 112: struct timeval tv;
! 113: omapi_object_t *listener;
! 114: isc_result_t result;
! 115: int persist = 0;
! 116: int no_dhclient_conf = 0;
! 117: int no_dhclient_db = 0;
! 118: int no_dhclient_pid = 0;
! 119: int no_dhclient_script = 0;
! 120: #ifdef DHCPv6
! 121: int local_family_set = 0;
! 122: #endif /* DHCPv6 */
! 123: char *s;
! 124:
! 125: /* Initialize client globals. */
! 126: memset(&default_duid, 0, sizeof(default_duid));
! 127:
! 128: /* Make sure that file descriptors 0 (stdin), 1, (stdout), and
! 129: 2 (stderr) are open. To do this, we assume that when we
! 130: open a file the lowest available file descriptor is used. */
! 131: fd = open("/dev/null", O_RDWR);
! 132: if (fd == 0)
! 133: fd = open("/dev/null", O_RDWR);
! 134: if (fd == 1)
! 135: fd = open("/dev/null", O_RDWR);
! 136: if (fd == 2)
! 137: log_perror = 0; /* No sense logging to /dev/null. */
! 138: else if (fd != -1)
! 139: close(fd);
! 140:
! 141: openlog("dhclient", LOG_NDELAY, LOG_DAEMON);
! 142:
! 143: #if !(defined(DEBUG) || defined(__CYGWIN32__))
! 144: setlogmask(LOG_UPTO(LOG_INFO));
! 145: #endif
! 146:
! 147: /*
! 148: * Set up the signal handlers, currently we only
! 149: * have one to ignore sigpipe.
! 150: */
! 151: if (dhcp_handle_signal(SIGPIPE, SIG_IGN) != ISC_R_SUCCESS) {
! 152: log_fatal("Can't set up signal handler");
! 153: }
! 154:
! 155: /* Set up the OMAPI. */
! 156: status = omapi_init();
! 157: if (status != ISC_R_SUCCESS)
! 158: log_fatal("Can't initialize OMAPI: %s",
! 159: isc_result_totext(status));
! 160:
! 161: /* Set up the OMAPI wrappers for various server database internal
! 162: objects. */
! 163: dhcp_common_objects_setup();
! 164:
! 165: dhcp_interface_discovery_hook = dhclient_interface_discovery_hook;
! 166: dhcp_interface_shutdown_hook = dhclient_interface_shutdown_hook;
! 167: dhcp_interface_startup_hook = dhclient_interface_startup_hook;
! 168:
! 169: for (i = 1; i < argc; i++) {
! 170: if (!strcmp(argv[i], "-r")) {
! 171: release_mode = 1;
! 172: no_daemon = 1;
! 173: #ifdef DHCPv6
! 174: } else if (!strcmp(argv[i], "-4")) {
! 175: if (local_family_set && local_family != AF_INET)
! 176: log_fatal("Client can only do v4 or v6, not "
! 177: "both.");
! 178: local_family_set = 1;
! 179: local_family = AF_INET;
! 180: } else if (!strcmp(argv[i], "-6")) {
! 181: if (local_family_set && local_family != AF_INET6)
! 182: log_fatal("Client can only do v4 or v6, not "
! 183: "both.");
! 184: local_family_set = 1;
! 185: local_family = AF_INET6;
! 186: #endif /* DHCPv6 */
! 187: } else if (!strcmp(argv[i], "-x")) { /* eXit, no release */
! 188: release_mode = 0;
! 189: no_daemon = 0;
! 190: exit_mode = 1;
! 191: } else if (!strcmp(argv[i], "-p")) {
! 192: if (++i == argc)
! 193: usage();
! 194: local_port = validate_port(argv[i]);
! 195: log_debug("binding to user-specified port %d",
! 196: ntohs(local_port));
! 197: } else if (!strcmp(argv[i], "-d")) {
! 198: no_daemon = 1;
! 199: quiet = 0;
! 200: } else if (!strcmp(argv[i], "-pf")) {
! 201: if (++i == argc)
! 202: usage();
! 203: path_dhclient_pid = argv[i];
! 204: no_dhclient_pid = 1;
! 205: } else if (!strcmp(argv[i], "--no-pid")) {
! 206: no_pid_file = ISC_TRUE;
! 207: } else if (!strcmp(argv[i], "-cf")) {
! 208: if (++i == argc)
! 209: usage();
! 210: path_dhclient_conf = argv[i];
! 211: no_dhclient_conf = 1;
! 212: } else if (!strcmp(argv[i], "-lf")) {
! 213: if (++i == argc)
! 214: usage();
! 215: path_dhclient_db = argv[i];
! 216: no_dhclient_db = 1;
! 217: } else if (!strcmp(argv[i], "-sf")) {
! 218: if (++i == argc)
! 219: usage();
! 220: path_dhclient_script = argv[i];
! 221: no_dhclient_script = 1;
! 222: } else if (!strcmp(argv[i], "-1")) {
! 223: onetry = 1;
! 224: } else if (!strcmp(argv[i], "-q")) {
! 225: quiet = 1;
! 226: } else if (!strcmp(argv[i], "-s")) {
! 227: if (++i == argc)
! 228: usage();
! 229: server = argv[i];
! 230: } else if (!strcmp(argv[i], "-g")) {
! 231: if (++i == argc)
! 232: usage();
! 233: mockup_relay = argv[i];
! 234: } else if (!strcmp(argv[i], "-nw")) {
! 235: nowait = 1;
! 236: } else if (!strcmp(argv[i], "-n")) {
! 237: /* do not start up any interfaces */
! 238: interfaces_requested = -1;
! 239: } else if (!strcmp(argv[i], "-w")) {
! 240: /* do not exit if there are no broadcast interfaces. */
! 241: persist = 1;
! 242: } else if (!strcmp(argv[i], "-e")) {
! 243: struct string_list *tmp;
! 244: if (++i == argc)
! 245: usage();
! 246: tmp = dmalloc(strlen(argv[i]) + sizeof *tmp, MDL);
! 247: if (!tmp)
! 248: log_fatal("No memory for %s", argv[i]);
! 249: strcpy(tmp->string, argv[i]);
! 250: tmp->next = client_env;
! 251: client_env = tmp;
! 252: client_env_count++;
! 253: #ifdef DHCPv6
! 254: } else if (!strcmp(argv[i], "-S")) {
! 255: if (local_family_set && (local_family == AF_INET)) {
! 256: usage();
! 257: }
! 258: local_family_set = 1;
! 259: local_family = AF_INET6;
! 260: wanted_ia_na = 0;
! 261: stateless = 1;
! 262: } else if (!strcmp(argv[i], "-N")) {
! 263: if (local_family_set && (local_family == AF_INET)) {
! 264: usage();
! 265: }
! 266: local_family_set = 1;
! 267: local_family = AF_INET6;
! 268: if (wanted_ia_na < 0) {
! 269: wanted_ia_na = 0;
! 270: }
! 271: wanted_ia_na++;
! 272: } else if (!strcmp(argv[i], "-T")) {
! 273: if (local_family_set && (local_family == AF_INET)) {
! 274: usage();
! 275: }
! 276: local_family_set = 1;
! 277: local_family = AF_INET6;
! 278: if (wanted_ia_na < 0) {
! 279: wanted_ia_na = 0;
! 280: }
! 281: wanted_ia_ta++;
! 282: } else if (!strcmp(argv[i], "-P")) {
! 283: if (local_family_set && (local_family == AF_INET)) {
! 284: usage();
! 285: }
! 286: local_family_set = 1;
! 287: local_family = AF_INET6;
! 288: if (wanted_ia_na < 0) {
! 289: wanted_ia_na = 0;
! 290: }
! 291: wanted_ia_pd++;
! 292: #endif /* DHCPv6 */
! 293: } else if (!strcmp(argv[i], "-v")) {
! 294: quiet = 0;
! 295: } else if (!strcmp(argv[i], "--version")) {
! 296: log_info("isc-dhclient-%s", PACKAGE_VERSION);
! 297: exit(0);
! 298: } else if (argv[i][0] == '-') {
! 299: usage();
! 300: } else if (interfaces_requested < 0) {
! 301: usage();
! 302: } else {
! 303: struct interface_info *tmp = NULL;
! 304:
! 305: status = interface_allocate(&tmp, MDL);
! 306: if (status != ISC_R_SUCCESS)
! 307: log_fatal("Can't record interface %s:%s",
! 308: argv[i], isc_result_totext(status));
! 309: if (strlen(argv[i]) >= sizeof(tmp->name))
! 310: log_fatal("%s: interface name too long (is %ld)",
! 311: argv[i], (long)strlen(argv[i]));
! 312: strcpy(tmp->name, argv[i]);
! 313: if (interfaces) {
! 314: interface_reference(&tmp->next,
! 315: interfaces, MDL);
! 316: interface_dereference(&interfaces, MDL);
! 317: }
! 318: interface_reference(&interfaces, tmp, MDL);
! 319: tmp->flags = INTERFACE_REQUESTED;
! 320: interfaces_requested++;
! 321: }
! 322: }
! 323:
! 324: if (wanted_ia_na < 0) {
! 325: wanted_ia_na = 1;
! 326: }
! 327:
! 328: /* Support only one (requested) interface for Prefix Delegation. */
! 329: if (wanted_ia_pd && (interfaces_requested != 1)) {
! 330: usage();
! 331: }
! 332:
! 333: if (!no_dhclient_conf && (s = getenv("PATH_DHCLIENT_CONF"))) {
! 334: path_dhclient_conf = s;
! 335: }
! 336: if (!no_dhclient_db && (s = getenv("PATH_DHCLIENT_DB"))) {
! 337: path_dhclient_db = s;
! 338: }
! 339: if (!no_dhclient_pid && (s = getenv("PATH_DHCLIENT_PID"))) {
! 340: path_dhclient_pid = s;
! 341: }
! 342: if (!no_dhclient_script && (s = getenv("PATH_DHCLIENT_SCRIPT"))) {
! 343: path_dhclient_script = s;
! 344: }
! 345:
! 346: /* Set up the initial dhcp option universe. */
! 347: initialize_common_option_spaces();
! 348:
! 349: /* Assign v4 or v6 specific running parameters. */
! 350: if (local_family == AF_INET)
! 351: dhcpv4_client_assignments();
! 352: #ifdef DHCPv6
! 353: else if (local_family == AF_INET6)
! 354: dhcpv6_client_assignments();
! 355: #endif /* DHCPv6 */
! 356: else
! 357: log_fatal("Impossible condition at %s:%d.", MDL);
! 358:
! 359: /*
! 360: * convert relative path names to absolute, for files that need
! 361: * to be reopened after chdir() has been called
! 362: */
! 363: if (path_dhclient_db[0] != '/') {
! 364: char *path = dmalloc(PATH_MAX, MDL);
! 365: if (path == NULL)
! 366: log_fatal("No memory for filename\n");
! 367: path_dhclient_db = realpath(path_dhclient_db, path);
! 368: if (path_dhclient_db == NULL)
! 369: log_fatal("%s: %s", path, strerror(errno));
! 370: }
! 371:
! 372: if (path_dhclient_script[0] != '/') {
! 373: char *path = dmalloc(PATH_MAX, MDL);
! 374: if (path == NULL)
! 375: log_fatal("No memory for filename\n");
! 376: path_dhclient_script = realpath(path_dhclient_script, path);
! 377: if (path_dhclient_script == NULL)
! 378: log_fatal("%s: %s", path, strerror(errno));
! 379: }
! 380:
! 381: /*
! 382: * See if we should kill off any currently running client
! 383: * we don't try to kill it off if the user told us not
! 384: * to write a pid file - we assume they are controlling
! 385: * the process in some other fashion.
! 386: */
! 387: if ((release_mode || exit_mode) && (no_pid_file == ISC_FALSE)) {
! 388: FILE *pidfd;
! 389: pid_t oldpid;
! 390: long temp;
! 391: int e;
! 392:
! 393: oldpid = 0;
! 394: if ((pidfd = fopen(path_dhclient_pid, "r")) != NULL) {
! 395: e = fscanf(pidfd, "%ld\n", &temp);
! 396: oldpid = (pid_t)temp;
! 397:
! 398: if (e != 0 && e != EOF) {
! 399: if (oldpid)
! 400: kill(oldpid, SIGTERM);
! 401: }
! 402: fclose(pidfd);
! 403: }
! 404: }
! 405:
! 406: if (!quiet) {
! 407: log_info("%s %s", message, PACKAGE_VERSION);
! 408: log_info(copyright);
! 409: log_info(arr);
! 410: log_info(url);
! 411: log_info("%s", "");
! 412: } else {
! 413: log_perror = 0;
! 414: quiet_interface_discovery = 1;
! 415: }
! 416:
! 417: /* If we're given a relay agent address to insert, for testing
! 418: purposes, figure out what it is. */
! 419: if (mockup_relay) {
! 420: if (!inet_aton(mockup_relay, &giaddr)) {
! 421: struct hostent *he;
! 422: he = gethostbyname(mockup_relay);
! 423: if (he) {
! 424: memcpy(&giaddr, he->h_addr_list[0],
! 425: sizeof giaddr);
! 426: } else {
! 427: log_fatal("%s: no such host", mockup_relay);
! 428: }
! 429: }
! 430: }
! 431:
! 432: /* Get the current time... */
! 433: gettimeofday(&cur_tv, NULL);
! 434:
! 435: sockaddr_broadcast.sin_family = AF_INET;
! 436: sockaddr_broadcast.sin_port = remote_port;
! 437: if (server) {
! 438: if (!inet_aton(server, &sockaddr_broadcast.sin_addr)) {
! 439: struct hostent *he;
! 440: he = gethostbyname(server);
! 441: if (he) {
! 442: memcpy(&sockaddr_broadcast.sin_addr,
! 443: he->h_addr_list[0],
! 444: sizeof sockaddr_broadcast.sin_addr);
! 445: } else
! 446: sockaddr_broadcast.sin_addr.s_addr =
! 447: INADDR_BROADCAST;
! 448: }
! 449: } else {
! 450: sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST;
! 451: }
! 452:
! 453: inaddr_any.s_addr = INADDR_ANY;
! 454:
! 455: /* Stateless special case. */
! 456: if (stateless) {
! 457: if (release_mode || (wanted_ia_na > 0) ||
! 458: wanted_ia_ta || wanted_ia_pd ||
! 459: (interfaces_requested != 1)) {
! 460: usage();
! 461: }
! 462: run_stateless(exit_mode);
! 463: return 0;
! 464: }
! 465:
! 466: /* Discover all the network interfaces. */
! 467: discover_interfaces(DISCOVER_UNCONFIGURED);
! 468:
! 469: /* Parse the dhclient.conf file. */
! 470: read_client_conf();
! 471:
! 472: /* Parse the lease database. */
! 473: read_client_leases();
! 474:
! 475: /* Rewrite the lease database... */
! 476: rewrite_client_leases();
! 477:
! 478: /* XXX */
! 479: /* config_counter(&snd_counter, &rcv_counter); */
! 480:
! 481: /*
! 482: * If no broadcast interfaces were discovered, call the script
! 483: * and tell it so.
! 484: */
! 485: if (!interfaces) {
! 486: /*
! 487: * Call dhclient-script with the NBI flag,
! 488: * in case somebody cares.
! 489: */
! 490: script_init(NULL, "NBI", NULL);
! 491: script_go(NULL);
! 492:
! 493: /*
! 494: * If we haven't been asked to persist, waiting for new
! 495: * interfaces, then just exit.
! 496: */
! 497: if (!persist) {
! 498: /* Nothing more to do. */
! 499: log_info("No broadcast interfaces found - exiting.");
! 500: exit(0);
! 501: }
! 502: } else if (!release_mode && !exit_mode) {
! 503: /* Call the script with the list of interfaces. */
! 504: for (ip = interfaces; ip; ip = ip->next) {
! 505: /*
! 506: * If interfaces were specified, don't configure
! 507: * interfaces that weren't specified!
! 508: */
! 509: if ((interfaces_requested > 0) &&
! 510: ((ip->flags & (INTERFACE_REQUESTED |
! 511: INTERFACE_AUTOMATIC)) !=
! 512: INTERFACE_REQUESTED))
! 513: continue;
! 514:
! 515: if (local_family == AF_INET6) {
! 516: script_init(ip->client, "PREINIT6", NULL);
! 517: } else {
! 518: script_init(ip->client, "PREINIT", NULL);
! 519: if (ip->client->alias != NULL)
! 520: script_write_params(ip->client,
! 521: "alias_",
! 522: ip->client->alias);
! 523: }
! 524: script_go(ip->client);
! 525: }
! 526: }
! 527:
! 528: /* At this point, all the interfaces that the script thinks
! 529: are relevant should be running, so now we once again call
! 530: discover_interfaces(), and this time ask it to actually set
! 531: up the interfaces. */
! 532: discover_interfaces(interfaces_requested != 0
! 533: ? DISCOVER_REQUESTED
! 534: : DISCOVER_RUNNING);
! 535:
! 536: /* Make up a seed for the random number generator from current
! 537: time plus the sum of the last four bytes of each
! 538: interface's hardware address interpreted as an integer.
! 539: Not much entropy, but we're booting, so we're not likely to
! 540: find anything better. */
! 541: seed = 0;
! 542: for (ip = interfaces; ip; ip = ip->next) {
! 543: int junk;
! 544: memcpy(&junk,
! 545: &ip->hw_address.hbuf[ip->hw_address.hlen -
! 546: sizeof seed], sizeof seed);
! 547: seed += junk;
! 548: }
! 549: srandom(seed + cur_time + (unsigned)getpid());
! 550:
! 551: /* Start a configuration state machine for each interface. */
! 552: #ifdef DHCPv6
! 553: if (local_family == AF_INET6) {
! 554: /* Establish a default DUID. This may be moved to the
! 555: * DHCPv4 area later.
! 556: */
! 557: if (default_duid.len == 0) {
! 558: if (default_duid.buffer != NULL)
! 559: data_string_forget(&default_duid, MDL);
! 560:
! 561: form_duid(&default_duid, MDL);
! 562: write_duid(&default_duid);
! 563: }
! 564:
! 565: for (ip = interfaces ; ip != NULL ; ip = ip->next) {
! 566: for (client = ip->client ; client != NULL ;
! 567: client = client->next) {
! 568: if (release_mode) {
! 569: start_release6(client);
! 570: continue;
! 571: } else if (exit_mode) {
! 572: unconfigure6(client, "STOP6");
! 573: continue;
! 574: }
! 575:
! 576: /* If we have a previous binding, Confirm
! 577: * that we can (or can't) still use it.
! 578: */
! 579: if ((client->active_lease != NULL) &&
! 580: !client->active_lease->released)
! 581: start_confirm6(client);
! 582: else
! 583: start_init6(client);
! 584: }
! 585: }
! 586: } else
! 587: #endif /* DHCPv6 */
! 588: {
! 589: for (ip = interfaces ; ip ; ip = ip->next) {
! 590: ip->flags |= INTERFACE_RUNNING;
! 591: for (client = ip->client ; client ;
! 592: client = client->next) {
! 593: if (exit_mode)
! 594: state_stop(client);
! 595: else if (release_mode)
! 596: do_release(client);
! 597: else {
! 598: client->state = S_INIT;
! 599:
! 600: if (top_level_config.initial_delay>0)
! 601: {
! 602: tv.tv_sec = 0;
! 603: if (top_level_config.
! 604: initial_delay>1)
! 605: tv.tv_sec = cur_time
! 606: + random()
! 607: % (top_level_config.
! 608: initial_delay-1);
! 609: tv.tv_usec = random()
! 610: % 1000000;
! 611: /*
! 612: * this gives better
! 613: * distribution than just
! 614: *whole seconds
! 615: */
! 616: add_timeout(&tv, state_reboot,
! 617: client, 0, 0);
! 618: } else {
! 619: state_reboot(client);
! 620: }
! 621: }
! 622: }
! 623: }
! 624: }
! 625:
! 626: if (exit_mode)
! 627: return 0;
! 628: if (release_mode) {
! 629: #ifndef DHCPv6
! 630: return 0;
! 631: #else
! 632: if (local_family == AF_INET6) {
! 633: if (onetry)
! 634: return 0;
! 635: } else
! 636: return 0;
! 637: #endif /* DHCPv6 */
! 638: }
! 639:
! 640: /* Start up a listener for the object management API protocol. */
! 641: if (top_level_config.omapi_port != -1) {
! 642: listener = NULL;
! 643: result = omapi_generic_new(&listener, MDL);
! 644: if (result != ISC_R_SUCCESS)
! 645: log_fatal("Can't allocate new generic object: %s\n",
! 646: isc_result_totext(result));
! 647: result = omapi_protocol_listen(listener,
! 648: (unsigned)
! 649: top_level_config.omapi_port,
! 650: 1);
! 651: if (result != ISC_R_SUCCESS)
! 652: log_fatal("Can't start OMAPI protocol: %s",
! 653: isc_result_totext (result));
! 654: }
! 655:
! 656: /* Set up the bootp packet handler... */
! 657: bootp_packet_handler = do_packet;
! 658: #ifdef DHCPv6
! 659: dhcpv6_packet_handler = do_packet6;
! 660: #endif /* DHCPv6 */
! 661:
! 662: #if defined(DEBUG_MEMORY_LEAKAGE) || defined(DEBUG_MALLOC_POOL) || \
! 663: defined(DEBUG_MEMORY_LEAKAGE_ON_EXIT)
! 664: dmalloc_cutoff_generation = dmalloc_generation;
! 665: dmalloc_longterm = dmalloc_outstanding;
! 666: dmalloc_outstanding = 0;
! 667: #endif
! 668:
! 669: /* If we're not supposed to wait before getting the address,
! 670: don't. */
! 671: if (nowait)
! 672: go_daemon();
! 673:
! 674: /* If we're not going to daemonize, write the pid file
! 675: now. */
! 676: if (no_daemon || nowait)
! 677: write_client_pid_file();
! 678:
! 679: /* Start dispatching packets and timeouts... */
! 680: dispatch();
! 681:
! 682: /*NOTREACHED*/
! 683: return 0;
! 684: }
! 685:
! 686: static void usage()
! 687: {
! 688: log_info("%s %s", message, PACKAGE_VERSION);
! 689: log_info(copyright);
! 690: log_info(arr);
! 691: log_info(url);
! 692:
! 693:
! 694: log_fatal("Usage: dhclient "
! 695: #ifdef DHCPv6
! 696: "[-4|-6] [-SNTP1dvrx] [-nw] [-p <port>]\n"
! 697: #else /* DHCPv6 */
! 698: "[-1dvrx] [-nw] [-p <port>]\n"
! 699: #endif /* DHCPv6 */
! 700: " [-s server-addr] [-cf config-file] "
! 701: "[-lf lease-file]\n"
! 702: " [-pf pid-file] [--no-pid] [-e VAR=val]\n"
! 703: " [-sf script-file] [interface]");
! 704: }
! 705:
! 706: void run_stateless(int exit_mode)
! 707: {
! 708: #ifdef DHCPv6
! 709: struct client_state *client;
! 710: omapi_object_t *listener;
! 711: isc_result_t result;
! 712:
! 713: /* Discover the network interface. */
! 714: discover_interfaces(DISCOVER_REQUESTED);
! 715:
! 716: if (!interfaces)
! 717: usage();
! 718:
! 719: /* Parse the dhclient.conf file. */
! 720: read_client_conf();
! 721:
! 722: /* Parse the lease database. */
! 723: read_client_leases();
! 724:
! 725: /* Establish a default DUID. */
! 726: if (default_duid.len == 0) {
! 727: if (default_duid.buffer != NULL)
! 728: data_string_forget(&default_duid, MDL);
! 729:
! 730: form_duid(&default_duid, MDL);
! 731: }
! 732:
! 733: /* Start a configuration state machine. */
! 734: for (client = interfaces->client ;
! 735: client != NULL ;
! 736: client = client->next) {
! 737: if (exit_mode) {
! 738: unconfigure6(client, "STOP6");
! 739: continue;
! 740: }
! 741: start_info_request6(client);
! 742: }
! 743: if (exit_mode)
! 744: return;
! 745:
! 746: /* Start up a listener for the object management API protocol. */
! 747: if (top_level_config.omapi_port != -1) {
! 748: listener = NULL;
! 749: result = omapi_generic_new(&listener, MDL);
! 750: if (result != ISC_R_SUCCESS)
! 751: log_fatal("Can't allocate new generic object: %s\n",
! 752: isc_result_totext(result));
! 753: result = omapi_protocol_listen(listener,
! 754: (unsigned)
! 755: top_level_config.omapi_port,
! 756: 1);
! 757: if (result != ISC_R_SUCCESS)
! 758: log_fatal("Can't start OMAPI protocol: %s",
! 759: isc_result_totext(result));
! 760: }
! 761:
! 762: /* Set up the packet handler... */
! 763: dhcpv6_packet_handler = do_packet6;
! 764:
! 765: #if defined(DEBUG_MEMORY_LEAKAGE) || defined(DEBUG_MALLOC_POOL) || \
! 766: defined(DEBUG_MEMORY_LEAKAGE_ON_EXIT)
! 767: dmalloc_cutoff_generation = dmalloc_generation;
! 768: dmalloc_longterm = dmalloc_outstanding;
! 769: dmalloc_outstanding = 0;
! 770: #endif
! 771:
! 772: /* If we're not supposed to wait before getting the address,
! 773: don't. */
! 774: if (nowait)
! 775: go_daemon();
! 776:
! 777: /* If we're not going to daemonize, write the pid file
! 778: now. */
! 779: if (no_daemon || nowait)
! 780: write_client_pid_file();
! 781:
! 782: /* Start dispatching packets and timeouts... */
! 783: dispatch();
! 784:
! 785: /*NOTREACHED*/
! 786: #endif /* DHCPv6 */
! 787: return;
! 788: }
! 789:
! 790: isc_result_t find_class (struct class **c,
! 791: const char *s, const char *file, int line)
! 792: {
! 793: return 0;
! 794: }
! 795:
! 796: int check_collection (packet, lease, collection)
! 797: struct packet *packet;
! 798: struct lease *lease;
! 799: struct collection *collection;
! 800: {
! 801: return 0;
! 802: }
! 803:
! 804: void classify (packet, class)
! 805: struct packet *packet;
! 806: struct class *class;
! 807: {
! 808: }
! 809:
! 810: int unbill_class (lease, class)
! 811: struct lease *lease;
! 812: struct class *class;
! 813: {
! 814: return 0;
! 815: }
! 816:
! 817: int find_subnet (struct subnet **sp,
! 818: struct iaddr addr, const char *file, int line)
! 819: {
! 820: return 0;
! 821: }
! 822:
! 823: /* Individual States:
! 824: *
! 825: * Each routine is called from the dhclient_state_machine() in one of
! 826: * these conditions:
! 827: * -> entering INIT state
! 828: * -> recvpacket_flag == 0: timeout in this state
! 829: * -> otherwise: received a packet in this state
! 830: *
! 831: * Return conditions as handled by dhclient_state_machine():
! 832: * Returns 1, sendpacket_flag = 1: send packet, reset timer.
! 833: * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone).
! 834: * Returns 0: finish the nap which was interrupted for no good reason.
! 835: *
! 836: * Several per-interface variables are used to keep track of the process:
! 837: * active_lease: the lease that is being used on the interface
! 838: * (null pointer if not configured yet).
! 839: * offered_leases: leases corresponding to DHCPOFFER messages that have
! 840: * been sent to us by DHCP servers.
! 841: * acked_leases: leases corresponding to DHCPACK messages that have been
! 842: * sent to us by DHCP servers.
! 843: * sendpacket: DHCP packet we're trying to send.
! 844: * destination: IP address to send sendpacket to
! 845: * In addition, there are several relevant per-lease variables.
! 846: * T1_expiry, T2_expiry, lease_expiry: lease milestones
! 847: * In the active lease, these control the process of renewing the lease;
! 848: * In leases on the acked_leases list, this simply determines when we
! 849: * can no longer legitimately use the lease.
! 850: */
! 851:
! 852: void state_reboot (cpp)
! 853: void *cpp;
! 854: {
! 855: struct client_state *client = cpp;
! 856:
! 857: /* If we don't remember an active lease, go straight to INIT. */
! 858: if (!client -> active ||
! 859: client -> active -> is_bootp ||
! 860: client -> active -> expiry <= cur_time) {
! 861: state_init (client);
! 862: return;
! 863: }
! 864:
! 865: /* We are in the rebooting state. */
! 866: client -> state = S_REBOOTING;
! 867:
! 868: /*
! 869: * make_request doesn't initialize xid because it normally comes
! 870: * from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER,
! 871: * so pick an xid now.
! 872: */
! 873: client -> xid = random ();
! 874:
! 875: /*
! 876: * Make a DHCPREQUEST packet, and set
! 877: * appropriate per-interface flags.
! 878: */
! 879: make_request (client, client -> active);
! 880: client -> destination = iaddr_broadcast;
! 881: client -> first_sending = cur_time;
! 882: client -> interval = client -> config -> initial_interval;
! 883:
! 884: /* Zap the medium list... */
! 885: client -> medium = NULL;
! 886:
! 887: /* Send out the first DHCPREQUEST packet. */
! 888: send_request (client);
! 889: }
! 890:
! 891: /* Called when a lease has completely expired and we've been unable to
! 892: renew it. */
! 893:
! 894: void state_init (cpp)
! 895: void *cpp;
! 896: {
! 897: struct client_state *client = cpp;
! 898:
! 899: ASSERT_STATE(state, S_INIT);
! 900:
! 901: /* Make a DHCPDISCOVER packet, and set appropriate per-interface
! 902: flags. */
! 903: make_discover (client, client -> active);
! 904: client -> xid = client -> packet.xid;
! 905: client -> destination = iaddr_broadcast;
! 906: client -> state = S_SELECTING;
! 907: client -> first_sending = cur_time;
! 908: client -> interval = client -> config -> initial_interval;
! 909:
! 910: /* Add an immediate timeout to cause the first DHCPDISCOVER packet
! 911: to go out. */
! 912: send_discover (client);
! 913: }
! 914:
! 915: /*
! 916: * state_selecting is called when one or more DHCPOFFER packets have been
! 917: * received and a configurable period of time has passed.
! 918: */
! 919:
! 920: void state_selecting (cpp)
! 921: void *cpp;
! 922: {
! 923: struct client_state *client = cpp;
! 924: struct client_lease *lp, *next, *picked;
! 925:
! 926:
! 927: ASSERT_STATE(state, S_SELECTING);
! 928:
! 929: /*
! 930: * Cancel state_selecting and send_discover timeouts, since either
! 931: * one could have got us here.
! 932: */
! 933: cancel_timeout (state_selecting, client);
! 934: cancel_timeout (send_discover, client);
! 935:
! 936: /*
! 937: * We have received one or more DHCPOFFER packets. Currently,
! 938: * the only criterion by which we judge leases is whether or
! 939: * not we get a response when we arp for them.
! 940: */
! 941: picked = NULL;
! 942: for (lp = client -> offered_leases; lp; lp = next) {
! 943: next = lp -> next;
! 944:
! 945: /*
! 946: * Check to see if we got an ARPREPLY for the address
! 947: * in this particular lease.
! 948: */
! 949: if (!picked) {
! 950: picked = lp;
! 951: picked -> next = NULL;
! 952: } else {
! 953: destroy_client_lease (lp);
! 954: }
! 955: }
! 956: client -> offered_leases = NULL;
! 957:
! 958: /*
! 959: * If we just tossed all the leases we were offered, go back
! 960: * to square one.
! 961: */
! 962: if (!picked) {
! 963: client -> state = S_INIT;
! 964: state_init (client);
! 965: return;
! 966: }
! 967:
! 968: /* If it was a BOOTREPLY, we can just take the address right now. */
! 969: if (picked -> is_bootp) {
! 970: client -> new = picked;
! 971:
! 972: /* Make up some lease expiry times
! 973: XXX these should be configurable. */
! 974: client -> new -> expiry = cur_time + 12000;
! 975: client -> new -> renewal += cur_time + 8000;
! 976: client -> new -> rebind += cur_time + 10000;
! 977:
! 978: client -> state = S_REQUESTING;
! 979:
! 980: /* Bind to the address we received. */
! 981: bind_lease (client);
! 982: return;
! 983: }
! 984:
! 985: /* Go to the REQUESTING state. */
! 986: client -> destination = iaddr_broadcast;
! 987: client -> state = S_REQUESTING;
! 988: client -> first_sending = cur_time;
! 989: client -> interval = client -> config -> initial_interval;
! 990:
! 991: /* Make a DHCPREQUEST packet from the lease we picked. */
! 992: make_request (client, picked);
! 993: client -> xid = client -> packet.xid;
! 994:
! 995: /* Toss the lease we picked - we'll get it back in a DHCPACK. */
! 996: destroy_client_lease (picked);
! 997:
! 998: /* Add an immediate timeout to send the first DHCPREQUEST packet. */
! 999: send_request (client);
! 1000: }
! 1001:
! 1002: /* state_requesting is called when we receive a DHCPACK message after
! 1003: having sent out one or more DHCPREQUEST packets. */
! 1004:
! 1005: void dhcpack (packet)
! 1006: struct packet *packet;
! 1007: {
! 1008: struct interface_info *ip = packet -> interface;
! 1009: struct client_state *client;
! 1010: struct client_lease *lease;
! 1011: struct option_cache *oc;
! 1012: struct data_string ds;
! 1013:
! 1014: /* If we're not receptive to an offer right now, or if the offer
! 1015: has an unrecognizable transaction id, then just drop it. */
! 1016: for (client = ip -> client; client; client = client -> next) {
! 1017: if (client -> xid == packet -> raw -> xid)
! 1018: break;
! 1019: }
! 1020: if (!client ||
! 1021: (packet -> interface -> hw_address.hlen - 1 !=
! 1022: packet -> raw -> hlen) ||
! 1023: (memcmp (&packet -> interface -> hw_address.hbuf [1],
! 1024: packet -> raw -> chaddr, packet -> raw -> hlen))) {
! 1025: #if defined (DEBUG)
! 1026: log_debug ("DHCPACK in wrong transaction.");
! 1027: #endif
! 1028: return;
! 1029: }
! 1030:
! 1031: if (client -> state != S_REBOOTING &&
! 1032: client -> state != S_REQUESTING &&
! 1033: client -> state != S_RENEWING &&
! 1034: client -> state != S_REBINDING) {
! 1035: #if defined (DEBUG)
! 1036: log_debug ("DHCPACK in wrong state.");
! 1037: #endif
! 1038: return;
! 1039: }
! 1040:
! 1041: log_info ("DHCPACK from %s", piaddr (packet -> client_addr));
! 1042:
! 1043: lease = packet_to_lease (packet, client);
! 1044: if (!lease) {
! 1045: log_info ("packet_to_lease failed.");
! 1046: return;
! 1047: }
! 1048:
! 1049: client -> new = lease;
! 1050:
! 1051: /* Stop resending DHCPREQUEST. */
! 1052: cancel_timeout (send_request, client);
! 1053:
! 1054: /* Figure out the lease time. */
! 1055: oc = lookup_option (&dhcp_universe, client -> new -> options,
! 1056: DHO_DHCP_LEASE_TIME);
! 1057: memset (&ds, 0, sizeof ds);
! 1058: if (oc &&
! 1059: evaluate_option_cache (&ds, packet, (struct lease *)0, client,
! 1060: packet -> options, client -> new -> options,
! 1061: &global_scope, oc, MDL)) {
! 1062: if (ds.len > 3)
! 1063: client -> new -> expiry = getULong (ds.data);
! 1064: else
! 1065: client -> new -> expiry = 0;
! 1066: data_string_forget (&ds, MDL);
! 1067: } else
! 1068: client -> new -> expiry = 0;
! 1069:
! 1070: if (client->new->expiry == 0) {
! 1071: struct timeval tv;
! 1072:
! 1073: log_error ("no expiry time on offered lease.");
! 1074:
! 1075: /* Quench this (broken) server. Return to INIT to reselect. */
! 1076: add_reject(packet);
! 1077:
! 1078: /* 1/2 second delay to restart at INIT. */
! 1079: tv.tv_sec = cur_tv.tv_sec;
! 1080: tv.tv_usec = cur_tv.tv_usec + 500000;
! 1081:
! 1082: if (tv.tv_usec >= 1000000) {
! 1083: tv.tv_sec++;
! 1084: tv.tv_usec -= 1000000;
! 1085: }
! 1086:
! 1087: add_timeout(&tv, state_init, client, 0, 0);
! 1088: return;
! 1089: }
! 1090:
! 1091: /*
! 1092: * A number that looks negative here is really just very large,
! 1093: * because the lease expiry offset is unsigned.
! 1094: */
! 1095: if (client->new->expiry < 0)
! 1096: client->new->expiry = TIME_MAX;
! 1097:
! 1098: /* Take the server-provided renewal time if there is one. */
! 1099: oc = lookup_option (&dhcp_universe, client -> new -> options,
! 1100: DHO_DHCP_RENEWAL_TIME);
! 1101: if (oc &&
! 1102: evaluate_option_cache (&ds, packet, (struct lease *)0, client,
! 1103: packet -> options, client -> new -> options,
! 1104: &global_scope, oc, MDL)) {
! 1105: if (ds.len > 3)
! 1106: client -> new -> renewal = getULong (ds.data);
! 1107: else
! 1108: client -> new -> renewal = 0;
! 1109: data_string_forget (&ds, MDL);
! 1110: } else
! 1111: client -> new -> renewal = 0;
! 1112:
! 1113: /* If it wasn't specified by the server, calculate it. */
! 1114: if (!client -> new -> renewal)
! 1115: client -> new -> renewal = client -> new -> expiry / 2 + 1;
! 1116:
! 1117: if (client -> new -> renewal <= 0)
! 1118: client -> new -> renewal = TIME_MAX;
! 1119:
! 1120: /* Now introduce some randomness to the renewal time: */
! 1121: if (client->new->renewal <= ((TIME_MAX / 3) - 3))
! 1122: client->new->renewal = (((client->new->renewal * 3) + 3) / 4) +
! 1123: (((random() % client->new->renewal) + 3) / 4);
! 1124:
! 1125: /* Same deal with the rebind time. */
! 1126: oc = lookup_option (&dhcp_universe, client -> new -> options,
! 1127: DHO_DHCP_REBINDING_TIME);
! 1128: if (oc &&
! 1129: evaluate_option_cache (&ds, packet, (struct lease *)0, client,
! 1130: packet -> options, client -> new -> options,
! 1131: &global_scope, oc, MDL)) {
! 1132: if (ds.len > 3)
! 1133: client -> new -> rebind = getULong (ds.data);
! 1134: else
! 1135: client -> new -> rebind = 0;
! 1136: data_string_forget (&ds, MDL);
! 1137: } else
! 1138: client -> new -> rebind = 0;
! 1139:
! 1140: if (client -> new -> rebind <= 0) {
! 1141: if (client -> new -> expiry <= TIME_MAX / 7)
! 1142: client -> new -> rebind =
! 1143: client -> new -> expiry * 7 / 8;
! 1144: else
! 1145: client -> new -> rebind =
! 1146: client -> new -> expiry / 8 * 7;
! 1147: }
! 1148:
! 1149: /* Make sure our randomness didn't run the renewal time past the
! 1150: rebind time. */
! 1151: if (client -> new -> renewal > client -> new -> rebind) {
! 1152: if (client -> new -> rebind <= TIME_MAX / 3)
! 1153: client -> new -> renewal =
! 1154: client -> new -> rebind * 3 / 4;
! 1155: else
! 1156: client -> new -> renewal =
! 1157: client -> new -> rebind / 4 * 3;
! 1158: }
! 1159:
! 1160: client -> new -> expiry += cur_time;
! 1161: /* Lease lengths can never be negative. */
! 1162: if (client -> new -> expiry < cur_time)
! 1163: client -> new -> expiry = TIME_MAX;
! 1164: client -> new -> renewal += cur_time;
! 1165: if (client -> new -> renewal < cur_time)
! 1166: client -> new -> renewal = TIME_MAX;
! 1167: client -> new -> rebind += cur_time;
! 1168: if (client -> new -> rebind < cur_time)
! 1169: client -> new -> rebind = TIME_MAX;
! 1170:
! 1171: bind_lease (client);
! 1172: }
! 1173:
! 1174: void bind_lease (client)
! 1175: struct client_state *client;
! 1176: {
! 1177: struct timeval tv;
! 1178:
! 1179: /* Remember the medium. */
! 1180: client -> new -> medium = client -> medium;
! 1181:
! 1182: /* Run the client script with the new parameters. */
! 1183: script_init (client, (client -> state == S_REQUESTING
! 1184: ? "BOUND"
! 1185: : (client -> state == S_RENEWING
! 1186: ? "RENEW"
! 1187: : (client -> state == S_REBOOTING
! 1188: ? "REBOOT" : "REBIND"))),
! 1189: client -> new -> medium);
! 1190: if (client -> active && client -> state != S_REBOOTING)
! 1191: script_write_params (client, "old_", client -> active);
! 1192: script_write_params (client, "new_", client -> new);
! 1193: if (client -> alias)
! 1194: script_write_params (client, "alias_", client -> alias);
! 1195:
! 1196: /* If the BOUND/RENEW code detects another machine using the
! 1197: offered address, it exits nonzero. We need to send a
! 1198: DHCPDECLINE and toss the lease. */
! 1199: if (script_go (client)) {
! 1200: make_decline (client, client -> new);
! 1201: send_decline (client);
! 1202: destroy_client_lease (client -> new);
! 1203: client -> new = (struct client_lease *)0;
! 1204: state_init (client);
! 1205: return;
! 1206: }
! 1207:
! 1208: /* Write out the new lease if it has been long enough. */
! 1209: if (!client->last_write ||
! 1210: (cur_time - client->last_write) >= MIN_LEASE_WRITE)
! 1211: write_client_lease(client, client->new, 0, 0);
! 1212:
! 1213: /* Replace the old active lease with the new one. */
! 1214: if (client -> active)
! 1215: destroy_client_lease (client -> active);
! 1216: client -> active = client -> new;
! 1217: client -> new = (struct client_lease *)0;
! 1218:
! 1219: /* Set up a timeout to start the renewal process. */
! 1220: tv.tv_sec = client->active->renewal;
! 1221: tv.tv_usec = ((client->active->renewal - cur_tv.tv_sec) > 1) ?
! 1222: random() % 1000000 : cur_tv.tv_usec;
! 1223: add_timeout(&tv, state_bound, client, 0, 0);
! 1224:
! 1225: log_info ("bound to %s -- renewal in %ld seconds.",
! 1226: piaddr (client -> active -> address),
! 1227: (long)(client -> active -> renewal - cur_time));
! 1228: client -> state = S_BOUND;
! 1229: reinitialize_interfaces ();
! 1230: go_daemon ();
! 1231: if (client->config->do_forward_update)
! 1232: dhclient_schedule_updates(client, &client->active->address,
! 1233: 1);
! 1234: }
! 1235:
! 1236: /* state_bound is called when we've successfully bound to a particular
! 1237: lease, but the renewal time on that lease has expired. We are
! 1238: expected to unicast a DHCPREQUEST to the server that gave us our
! 1239: original lease. */
! 1240:
! 1241: void state_bound (cpp)
! 1242: void *cpp;
! 1243: {
! 1244: struct client_state *client = cpp;
! 1245: struct option_cache *oc;
! 1246: struct data_string ds;
! 1247:
! 1248: ASSERT_STATE(state, S_BOUND);
! 1249:
! 1250: /* T1 has expired. */
! 1251: make_request (client, client -> active);
! 1252: client -> xid = client -> packet.xid;
! 1253:
! 1254: memset (&ds, 0, sizeof ds);
! 1255: oc = lookup_option (&dhcp_universe, client -> active -> options,
! 1256: DHO_DHCP_SERVER_IDENTIFIER);
! 1257: if (oc &&
! 1258: evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
! 1259: client, (struct option_state *)0,
! 1260: client -> active -> options,
! 1261: &global_scope, oc, MDL)) {
! 1262: if (ds.len > 3) {
! 1263: memcpy (client -> destination.iabuf, ds.data, 4);
! 1264: client -> destination.len = 4;
! 1265: } else
! 1266: client -> destination = iaddr_broadcast;
! 1267:
! 1268: data_string_forget (&ds, MDL);
! 1269: } else
! 1270: client -> destination = iaddr_broadcast;
! 1271:
! 1272: client -> first_sending = cur_time;
! 1273: client -> interval = client -> config -> initial_interval;
! 1274: client -> state = S_RENEWING;
! 1275:
! 1276: /* Send the first packet immediately. */
! 1277: send_request (client);
! 1278: }
! 1279:
! 1280: /* state_stop is called when we've been told to shut down. We unconfigure
! 1281: the interfaces, and then stop operating until told otherwise. */
! 1282:
! 1283: void state_stop (cpp)
! 1284: void *cpp;
! 1285: {
! 1286: struct client_state *client = cpp;
! 1287:
! 1288: /* Cancel all timeouts. */
! 1289: cancel_timeout(state_selecting, client);
! 1290: cancel_timeout(send_discover, client);
! 1291: cancel_timeout(send_request, client);
! 1292: cancel_timeout(state_bound, client);
! 1293:
! 1294: /* If we have an address, unconfigure it. */
! 1295: if (client->active) {
! 1296: script_init(client, "STOP", client->active->medium);
! 1297: script_write_params(client, "old_", client->active);
! 1298: if (client->alias)
! 1299: script_write_params(client, "alias_", client->alias);
! 1300: script_go(client);
! 1301: }
! 1302: }
! 1303:
! 1304: int commit_leases ()
! 1305: {
! 1306: return 0;
! 1307: }
! 1308:
! 1309: int write_lease (lease)
! 1310: struct lease *lease;
! 1311: {
! 1312: return 0;
! 1313: }
! 1314:
! 1315: int write_host (host)
! 1316: struct host_decl *host;
! 1317: {
! 1318: return 0;
! 1319: }
! 1320:
! 1321: void db_startup (testp)
! 1322: int testp;
! 1323: {
! 1324: }
! 1325:
! 1326: void bootp (packet)
! 1327: struct packet *packet;
! 1328: {
! 1329: struct iaddrmatchlist *ap;
! 1330: char addrbuf[4*16];
! 1331: char maskbuf[4*16];
! 1332:
! 1333: if (packet -> raw -> op != BOOTREPLY)
! 1334: return;
! 1335:
! 1336: /* If there's a reject list, make sure this packet's sender isn't
! 1337: on it. */
! 1338: for (ap = packet -> interface -> client -> config -> reject_list;
! 1339: ap; ap = ap -> next) {
! 1340: if (addr_match(&packet->client_addr, &ap->match)) {
! 1341:
! 1342: /* piaddr() returns its result in a static
! 1343: buffer sized 4*16 (see common/inet.c). */
! 1344:
! 1345: strcpy(addrbuf, piaddr(ap->match.addr));
! 1346: strcpy(maskbuf, piaddr(ap->match.mask));
! 1347:
! 1348: log_info("BOOTREPLY from %s rejected by rule %s "
! 1349: "mask %s.", piaddr(packet->client_addr),
! 1350: addrbuf, maskbuf);
! 1351: return;
! 1352: }
! 1353: }
! 1354:
! 1355: dhcpoffer (packet);
! 1356:
! 1357: }
! 1358:
! 1359: void dhcp (packet)
! 1360: struct packet *packet;
! 1361: {
! 1362: struct iaddrmatchlist *ap;
! 1363: void (*handler) (struct packet *);
! 1364: const char *type;
! 1365: char addrbuf[4*16];
! 1366: char maskbuf[4*16];
! 1367:
! 1368: switch (packet -> packet_type) {
! 1369: case DHCPOFFER:
! 1370: handler = dhcpoffer;
! 1371: type = "DHCPOFFER";
! 1372: break;
! 1373:
! 1374: case DHCPNAK:
! 1375: handler = dhcpnak;
! 1376: type = "DHCPNACK";
! 1377: break;
! 1378:
! 1379: case DHCPACK:
! 1380: handler = dhcpack;
! 1381: type = "DHCPACK";
! 1382: break;
! 1383:
! 1384: default:
! 1385: return;
! 1386: }
! 1387:
! 1388: /* If there's a reject list, make sure this packet's sender isn't
! 1389: on it. */
! 1390: for (ap = packet -> interface -> client -> config -> reject_list;
! 1391: ap; ap = ap -> next) {
! 1392: if (addr_match(&packet->client_addr, &ap->match)) {
! 1393:
! 1394: /* piaddr() returns its result in a static
! 1395: buffer sized 4*16 (see common/inet.c). */
! 1396:
! 1397: strcpy(addrbuf, piaddr(ap->match.addr));
! 1398: strcpy(maskbuf, piaddr(ap->match.mask));
! 1399:
! 1400: log_info("%s from %s rejected by rule %s mask %s.",
! 1401: type, piaddr(packet->client_addr),
! 1402: addrbuf, maskbuf);
! 1403: return;
! 1404: }
! 1405: }
! 1406: (*handler) (packet);
! 1407: }
! 1408:
! 1409: #ifdef DHCPv6
! 1410: void
! 1411: dhcpv6(struct packet *packet) {
! 1412: struct iaddrmatchlist *ap;
! 1413: struct client_state *client;
! 1414: char addrbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")];
! 1415:
! 1416: /* Silently drop bogus messages. */
! 1417: if (packet->dhcpv6_msg_type >= dhcpv6_type_name_max)
! 1418: return;
! 1419:
! 1420: /* Discard, with log, packets from quenched sources. */
! 1421: for (ap = packet->interface->client->config->reject_list ;
! 1422: ap ; ap = ap->next) {
! 1423: if (addr_match(&packet->client_addr, &ap->match)) {
! 1424: strcpy(addrbuf, piaddr(packet->client_addr));
! 1425: log_info("%s from %s rejected by rule %s",
! 1426: dhcpv6_type_names[packet->dhcpv6_msg_type],
! 1427: addrbuf,
! 1428: piaddrmask(&ap->match.addr, &ap->match.mask));
! 1429: return;
! 1430: }
! 1431: }
! 1432:
! 1433: /* Screen out nonsensical messages. */
! 1434: switch(packet->dhcpv6_msg_type) {
! 1435: case DHCPV6_ADVERTISE:
! 1436: case DHCPV6_RECONFIGURE:
! 1437: if (stateless)
! 1438: return;
! 1439: /* Falls through */
! 1440: case DHCPV6_REPLY:
! 1441: log_info("RCV: %s message on %s from %s.",
! 1442: dhcpv6_type_names[packet->dhcpv6_msg_type],
! 1443: packet->interface->name, piaddr(packet->client_addr));
! 1444: break;
! 1445:
! 1446: default:
! 1447: return;
! 1448: }
! 1449:
! 1450: /* Find a client state that matches the incoming XID. */
! 1451: for (client = packet->interface->client ; client ;
! 1452: client = client->next) {
! 1453: if (memcmp(&client->dhcpv6_transaction_id,
! 1454: packet->dhcpv6_transaction_id, 3) == 0) {
! 1455: client->v6_handler(packet, client);
! 1456: return;
! 1457: }
! 1458: }
! 1459:
! 1460: /* XXX: temporary log for debugging */
! 1461: log_info("Packet received, but nothing done with it.");
! 1462: }
! 1463: #endif /* DHCPv6 */
! 1464:
! 1465: void dhcpoffer (packet)
! 1466: struct packet *packet;
! 1467: {
! 1468: struct interface_info *ip = packet -> interface;
! 1469: struct client_state *client;
! 1470: struct client_lease *lease, *lp;
! 1471: struct option **req;
! 1472: int i;
! 1473: int stop_selecting;
! 1474: const char *name = packet -> packet_type ? "DHCPOFFER" : "BOOTREPLY";
! 1475: char obuf [1024];
! 1476: struct timeval tv;
! 1477:
! 1478: #ifdef DEBUG_PACKET
! 1479: dump_packet (packet);
! 1480: #endif
! 1481:
! 1482: /* Find a client state that matches the xid... */
! 1483: for (client = ip -> client; client; client = client -> next)
! 1484: if (client -> xid == packet -> raw -> xid)
! 1485: break;
! 1486:
! 1487: /* If we're not receptive to an offer right now, or if the offer
! 1488: has an unrecognizable transaction id, then just drop it. */
! 1489: if (!client ||
! 1490: client -> state != S_SELECTING ||
! 1491: (packet -> interface -> hw_address.hlen - 1 !=
! 1492: packet -> raw -> hlen) ||
! 1493: (memcmp (&packet -> interface -> hw_address.hbuf [1],
! 1494: packet -> raw -> chaddr, packet -> raw -> hlen))) {
! 1495: #if defined (DEBUG)
! 1496: log_debug ("%s in wrong transaction.", name);
! 1497: #endif
! 1498: return;
! 1499: }
! 1500:
! 1501: sprintf (obuf, "%s from %s", name, piaddr (packet -> client_addr));
! 1502:
! 1503:
! 1504: /* If this lease doesn't supply the minimum required DHCPv4 parameters,
! 1505: * ignore it.
! 1506: */
! 1507: req = client->config->required_options;
! 1508: if (req != NULL) {
! 1509: for (i = 0 ; req[i] != NULL ; i++) {
! 1510: if ((req[i]->universe == &dhcp_universe) &&
! 1511: !lookup_option(&dhcp_universe, packet->options,
! 1512: req[i]->code)) {
! 1513: struct option *option = NULL;
! 1514: unsigned code = req[i]->code;
! 1515:
! 1516: option_code_hash_lookup(&option,
! 1517: dhcp_universe.code_hash,
! 1518: &code, 0, MDL);
! 1519:
! 1520: if (option)
! 1521: log_info("%s: no %s option.", obuf,
! 1522: option->name);
! 1523: else
! 1524: log_info("%s: no unknown-%u option.",
! 1525: obuf, code);
! 1526:
! 1527: option_dereference(&option, MDL);
! 1528:
! 1529: return;
! 1530: }
! 1531: }
! 1532: }
! 1533:
! 1534: /* If we've already seen this lease, don't record it again. */
! 1535: for (lease = client -> offered_leases; lease; lease = lease -> next) {
! 1536: if (lease -> address.len == sizeof packet -> raw -> yiaddr &&
! 1537: !memcmp (lease -> address.iabuf,
! 1538: &packet -> raw -> yiaddr, lease -> address.len)) {
! 1539: log_debug ("%s: already seen.", obuf);
! 1540: return;
! 1541: }
! 1542: }
! 1543:
! 1544: lease = packet_to_lease (packet, client);
! 1545: if (!lease) {
! 1546: log_info ("%s: packet_to_lease failed.", obuf);
! 1547: return;
! 1548: }
! 1549:
! 1550: /* If this lease was acquired through a BOOTREPLY, record that
! 1551: fact. */
! 1552: if (!packet -> options_valid || !packet -> packet_type)
! 1553: lease -> is_bootp = 1;
! 1554:
! 1555: /* Record the medium under which this lease was offered. */
! 1556: lease -> medium = client -> medium;
! 1557:
! 1558: /* Figure out when we're supposed to stop selecting. */
! 1559: stop_selecting = (client -> first_sending +
! 1560: client -> config -> select_interval);
! 1561:
! 1562: /* If this is the lease we asked for, put it at the head of the
! 1563: list, and don't mess with the arp request timeout. */
! 1564: if (lease -> address.len == client -> requested_address.len &&
! 1565: !memcmp (lease -> address.iabuf,
! 1566: client -> requested_address.iabuf,
! 1567: client -> requested_address.len)) {
! 1568: lease -> next = client -> offered_leases;
! 1569: client -> offered_leases = lease;
! 1570: } else {
! 1571: /* Put the lease at the end of the list. */
! 1572: lease -> next = (struct client_lease *)0;
! 1573: if (!client -> offered_leases)
! 1574: client -> offered_leases = lease;
! 1575: else {
! 1576: for (lp = client -> offered_leases; lp -> next;
! 1577: lp = lp -> next)
! 1578: ;
! 1579: lp -> next = lease;
! 1580: }
! 1581: }
! 1582:
! 1583: /* If the selecting interval has expired, go immediately to
! 1584: state_selecting(). Otherwise, time out into
! 1585: state_selecting at the select interval. */
! 1586: if (stop_selecting <= cur_tv.tv_sec)
! 1587: state_selecting (client);
! 1588: else {
! 1589: tv.tv_sec = stop_selecting;
! 1590: tv.tv_usec = cur_tv.tv_usec;
! 1591: add_timeout(&tv, state_selecting, client, 0, 0);
! 1592: cancel_timeout(send_discover, client);
! 1593: }
! 1594: log_info("%s", obuf);
! 1595: }
! 1596:
! 1597: /* Allocate a client_lease structure and initialize it from the parameters
! 1598: in the specified packet. */
! 1599:
! 1600: struct client_lease *packet_to_lease (packet, client)
! 1601: struct packet *packet;
! 1602: struct client_state *client;
! 1603: {
! 1604: struct client_lease *lease;
! 1605: unsigned i;
! 1606: struct option_cache *oc;
! 1607: struct option *option = NULL;
! 1608: struct data_string data;
! 1609:
! 1610: lease = (struct client_lease *)new_client_lease (MDL);
! 1611:
! 1612: if (!lease) {
! 1613: log_error ("packet_to_lease: no memory to record lease.\n");
! 1614: return (struct client_lease *)0;
! 1615: }
! 1616:
! 1617: memset (lease, 0, sizeof *lease);
! 1618:
! 1619: /* Copy the lease options. */
! 1620: option_state_reference (&lease -> options, packet -> options, MDL);
! 1621:
! 1622: lease -> address.len = sizeof (packet -> raw -> yiaddr);
! 1623: memcpy (lease -> address.iabuf, &packet -> raw -> yiaddr,
! 1624: lease -> address.len);
! 1625:
! 1626: memset (&data, 0, sizeof data);
! 1627:
! 1628: if (client -> config -> vendor_space_name) {
! 1629: i = DHO_VENDOR_ENCAPSULATED_OPTIONS;
! 1630:
! 1631: /* See if there was a vendor encapsulation option. */
! 1632: oc = lookup_option (&dhcp_universe, lease -> options, i);
! 1633: if (oc &&
! 1634: client -> config -> vendor_space_name &&
! 1635: evaluate_option_cache (&data, packet,
! 1636: (struct lease *)0, client,
! 1637: packet -> options, lease -> options,
! 1638: &global_scope, oc, MDL)) {
! 1639: if (data.len) {
! 1640: if (!option_code_hash_lookup(&option,
! 1641: dhcp_universe.code_hash,
! 1642: &i, 0, MDL))
! 1643: log_fatal("Unable to find VENDOR "
! 1644: "option (%s:%d).", MDL);
! 1645: parse_encapsulated_suboptions
! 1646: (packet -> options, option,
! 1647: data.data, data.len, &dhcp_universe,
! 1648: client -> config -> vendor_space_name
! 1649: );
! 1650:
! 1651: option_dereference(&option, MDL);
! 1652: }
! 1653: data_string_forget (&data, MDL);
! 1654: }
! 1655: } else
! 1656: i = 0;
! 1657:
! 1658: /* Figure out the overload flag. */
! 1659: oc = lookup_option (&dhcp_universe, lease -> options,
! 1660: DHO_DHCP_OPTION_OVERLOAD);
! 1661: if (oc &&
! 1662: evaluate_option_cache (&data, packet, (struct lease *)0, client,
! 1663: packet -> options, lease -> options,
! 1664: &global_scope, oc, MDL)) {
! 1665: if (data.len > 0)
! 1666: i = data.data [0];
! 1667: else
! 1668: i = 0;
! 1669: data_string_forget (&data, MDL);
! 1670: } else
! 1671: i = 0;
! 1672:
! 1673: /* If the server name was filled out, copy it. */
! 1674: if (!(i & 2) && packet -> raw -> sname [0]) {
! 1675: unsigned len;
! 1676: /* Don't count on the NUL terminator. */
! 1677: for (len = 0; len < DHCP_SNAME_LEN; len++)
! 1678: if (!packet -> raw -> sname [len])
! 1679: break;
! 1680: lease -> server_name = dmalloc (len + 1, MDL);
! 1681: if (!lease -> server_name) {
! 1682: log_error ("dhcpoffer: no memory for server name.\n");
! 1683: destroy_client_lease (lease);
! 1684: return (struct client_lease *)0;
! 1685: } else {
! 1686: memcpy (lease -> server_name,
! 1687: packet -> raw -> sname, len);
! 1688: lease -> server_name [len] = 0;
! 1689: }
! 1690: }
! 1691:
! 1692: /* Ditto for the filename. */
! 1693: if (!(i & 1) && packet -> raw -> file [0]) {
! 1694: unsigned len;
! 1695: /* Don't count on the NUL terminator. */
! 1696: for (len = 0; len < DHCP_FILE_LEN; len++)
! 1697: if (!packet -> raw -> file [len])
! 1698: break;
! 1699: lease -> filename = dmalloc (len + 1, MDL);
! 1700: if (!lease -> filename) {
! 1701: log_error ("dhcpoffer: no memory for filename.\n");
! 1702: destroy_client_lease (lease);
! 1703: return (struct client_lease *)0;
! 1704: } else {
! 1705: memcpy (lease -> filename,
! 1706: packet -> raw -> file, len);
! 1707: lease -> filename [len] = 0;
! 1708: }
! 1709: }
! 1710:
! 1711: execute_statements_in_scope ((struct binding_value **)0,
! 1712: (struct packet *)packet,
! 1713: (struct lease *)0, client,
! 1714: lease -> options, lease -> options,
! 1715: &global_scope,
! 1716: client -> config -> on_receipt,
! 1717: (struct group *)0);
! 1718:
! 1719: return lease;
! 1720: }
! 1721:
! 1722: void dhcpnak (packet)
! 1723: struct packet *packet;
! 1724: {
! 1725: struct interface_info *ip = packet -> interface;
! 1726: struct client_state *client;
! 1727:
! 1728: /* Find a client state that matches the xid... */
! 1729: for (client = ip -> client; client; client = client -> next)
! 1730: if (client -> xid == packet -> raw -> xid)
! 1731: break;
! 1732:
! 1733: /* If we're not receptive to an offer right now, or if the offer
! 1734: has an unrecognizable transaction id, then just drop it. */
! 1735: if (!client ||
! 1736: (packet -> interface -> hw_address.hlen - 1 !=
! 1737: packet -> raw -> hlen) ||
! 1738: (memcmp (&packet -> interface -> hw_address.hbuf [1],
! 1739: packet -> raw -> chaddr, packet -> raw -> hlen))) {
! 1740: #if defined (DEBUG)
! 1741: log_debug ("DHCPNAK in wrong transaction.");
! 1742: #endif
! 1743: return;
! 1744: }
! 1745:
! 1746: if (client -> state != S_REBOOTING &&
! 1747: client -> state != S_REQUESTING &&
! 1748: client -> state != S_RENEWING &&
! 1749: client -> state != S_REBINDING) {
! 1750: #if defined (DEBUG)
! 1751: log_debug ("DHCPNAK in wrong state.");
! 1752: #endif
! 1753: return;
! 1754: }
! 1755:
! 1756: log_info ("DHCPNAK from %s", piaddr (packet -> client_addr));
! 1757:
! 1758: if (!client -> active) {
! 1759: #if defined (DEBUG)
! 1760: log_info ("DHCPNAK with no active lease.\n");
! 1761: #endif
! 1762: return;
! 1763: }
! 1764:
! 1765: /* If we get a DHCPNAK, we use the EXPIRE dhclient-script state
! 1766: * to indicate that we want all old bindings to be removed. (It
! 1767: * is possible that we may get a NAK while in the RENEW state,
! 1768: * so we might have bindings active at that time)
! 1769: */
! 1770: script_init(client, "EXPIRE", NULL);
! 1771: script_write_params(client, "old_", client->active);
! 1772: if (client->alias)
! 1773: script_write_params(client, "alias_", client->alias);
! 1774: script_go(client);
! 1775:
! 1776: destroy_client_lease (client -> active);
! 1777: client -> active = (struct client_lease *)0;
! 1778:
! 1779: /* Stop sending DHCPREQUEST packets... */
! 1780: cancel_timeout (send_request, client);
! 1781:
! 1782: /* On some scripts, 'EXPIRE' causes the interface to be ifconfig'd
! 1783: * down (this expunges any routes and arp cache). This makes the
! 1784: * interface unusable by state_init(), which we call next. So, we
! 1785: * need to 'PREINIT' the interface to bring it back up.
! 1786: */
! 1787: script_init(client, "PREINIT", NULL);
! 1788: if (client->alias)
! 1789: script_write_params(client, "alias_", client->alias);
! 1790: script_go(client);
! 1791:
! 1792: client -> state = S_INIT;
! 1793: state_init (client);
! 1794: }
! 1795:
! 1796: /* Send out a DHCPDISCOVER packet, and set a timeout to send out another
! 1797: one after the right interval has expired. If we don't get an offer by
! 1798: the time we reach the panic interval, call the panic function. */
! 1799:
! 1800: void send_discover (cpp)
! 1801: void *cpp;
! 1802: {
! 1803: struct client_state *client = cpp;
! 1804:
! 1805: int result;
! 1806: int interval;
! 1807: int increase = 1;
! 1808: struct timeval tv;
! 1809:
! 1810: /* Figure out how long it's been since we started transmitting. */
! 1811: interval = cur_time - client -> first_sending;
! 1812:
! 1813: /* If we're past the panic timeout, call the script and tell it
! 1814: we haven't found anything for this interface yet. */
! 1815: if (interval > client -> config -> timeout) {
! 1816: state_panic (client);
! 1817: return;
! 1818: }
! 1819:
! 1820: /* If we're selecting media, try the whole list before doing
! 1821: the exponential backoff, but if we've already received an
! 1822: offer, stop looping, because we obviously have it right. */
! 1823: if (!client -> offered_leases &&
! 1824: client -> config -> media) {
! 1825: int fail = 0;
! 1826: again:
! 1827: if (client -> medium) {
! 1828: client -> medium = client -> medium -> next;
! 1829: increase = 0;
! 1830: }
! 1831: if (!client -> medium) {
! 1832: if (fail)
! 1833: log_fatal ("No valid media types for %s!",
! 1834: client -> interface -> name);
! 1835: client -> medium =
! 1836: client -> config -> media;
! 1837: increase = 1;
! 1838: }
! 1839:
! 1840: log_info ("Trying medium \"%s\" %d",
! 1841: client -> medium -> string, increase);
! 1842: script_init (client, "MEDIUM", client -> medium);
! 1843: if (script_go (client)) {
! 1844: fail = 1;
! 1845: goto again;
! 1846: }
! 1847: }
! 1848:
! 1849: /* If we're supposed to increase the interval, do so. If it's
! 1850: currently zero (i.e., we haven't sent any packets yet), set
! 1851: it to initial_interval; otherwise, add to it a random number
! 1852: between zero and two times itself. On average, this means
! 1853: that it will double with every transmission. */
! 1854: if (increase) {
! 1855: if (!client->interval)
! 1856: client->interval = client->config->initial_interval;
! 1857: else
! 1858: client->interval += random() % (2 * client->interval);
! 1859:
! 1860: /* Don't backoff past cutoff. */
! 1861: if (client->interval > client->config->backoff_cutoff)
! 1862: client->interval = (client->config->backoff_cutoff / 2)
! 1863: + (random() % client->config->backoff_cutoff);
! 1864: } else if (!client->interval)
! 1865: client->interval = client->config->initial_interval;
! 1866:
! 1867: /* If the backoff would take us to the panic timeout, just use that
! 1868: as the interval. */
! 1869: if (cur_time + client -> interval >
! 1870: client -> first_sending + client -> config -> timeout)
! 1871: client -> interval =
! 1872: (client -> first_sending +
! 1873: client -> config -> timeout) - cur_time + 1;
! 1874:
! 1875: /* Record the number of seconds since we started sending. */
! 1876: if (interval < 65536)
! 1877: client -> packet.secs = htons (interval);
! 1878: else
! 1879: client -> packet.secs = htons (65535);
! 1880: client -> secs = client -> packet.secs;
! 1881:
! 1882: log_info ("DHCPDISCOVER on %s to %s port %d interval %ld",
! 1883: client -> name ? client -> name : client -> interface -> name,
! 1884: inet_ntoa (sockaddr_broadcast.sin_addr),
! 1885: ntohs (sockaddr_broadcast.sin_port), (long)(client -> interval));
! 1886:
! 1887: /* Send out a packet. */
! 1888: result = send_packet (client -> interface, (struct packet *)0,
! 1889: &client -> packet,
! 1890: client -> packet_length,
! 1891: inaddr_any, &sockaddr_broadcast,
! 1892: (struct hardware *)0);
! 1893:
! 1894: /*
! 1895: * If we used 0 microseconds here, and there were other clients on the
! 1896: * same network with a synchronized local clock (ntp), and a similar
! 1897: * zero-microsecond-scheduler behavior, then we could be participating
! 1898: * in a sub-second DOS ttck.
! 1899: */
! 1900: tv.tv_sec = cur_tv.tv_sec + client->interval;
! 1901: tv.tv_usec = client->interval > 1 ? random() % 1000000 : cur_tv.tv_usec;
! 1902: add_timeout(&tv, send_discover, client, 0, 0);
! 1903: }
! 1904:
! 1905: /* state_panic gets called if we haven't received any offers in a preset
! 1906: amount of time. When this happens, we try to use existing leases that
! 1907: haven't yet expired, and failing that, we call the client script and
! 1908: hope it can do something. */
! 1909:
! 1910: void state_panic (cpp)
! 1911: void *cpp;
! 1912: {
! 1913: struct client_state *client = cpp;
! 1914: struct client_lease *loop;
! 1915: struct client_lease *lp;
! 1916: struct timeval tv;
! 1917:
! 1918: loop = lp = client -> active;
! 1919:
! 1920: log_info ("No DHCPOFFERS received.");
! 1921:
! 1922: /* We may not have an active lease, but we may have some
! 1923: predefined leases that we can try. */
! 1924: if (!client -> active && client -> leases)
! 1925: goto activate_next;
! 1926:
! 1927: /* Run through the list of leases and see if one can be used. */
! 1928: while (client -> active) {
! 1929: if (client -> active -> expiry > cur_time) {
! 1930: log_info ("Trying recorded lease %s",
! 1931: piaddr (client -> active -> address));
! 1932: /* Run the client script with the existing
! 1933: parameters. */
! 1934: script_init (client, "TIMEOUT",
! 1935: client -> active -> medium);
! 1936: script_write_params (client, "new_", client -> active);
! 1937: if (client -> alias)
! 1938: script_write_params (client, "alias_",
! 1939: client -> alias);
! 1940:
! 1941: /* If the old lease is still good and doesn't
! 1942: yet need renewal, go into BOUND state and
! 1943: timeout at the renewal time. */
! 1944: if (!script_go (client)) {
! 1945: if (cur_time < client -> active -> renewal) {
! 1946: client -> state = S_BOUND;
! 1947: log_info ("bound: renewal in %ld %s.",
! 1948: (long)(client -> active -> renewal -
! 1949: cur_time), "seconds");
! 1950: tv.tv_sec = client->active->renewal;
! 1951: tv.tv_usec = ((client->active->renewal -
! 1952: cur_time) > 1) ?
! 1953: random() % 1000000 :
! 1954: cur_tv.tv_usec;
! 1955: add_timeout(&tv, state_bound, client, 0, 0);
! 1956: } else {
! 1957: client -> state = S_BOUND;
! 1958: log_info ("bound: immediate renewal.");
! 1959: state_bound (client);
! 1960: }
! 1961: reinitialize_interfaces ();
! 1962: go_daemon ();
! 1963: return;
! 1964: }
! 1965: }
! 1966:
! 1967: /* If there are no other leases, give up. */
! 1968: if (!client -> leases) {
! 1969: client -> leases = client -> active;
! 1970: client -> active = (struct client_lease *)0;
! 1971: break;
! 1972: }
! 1973:
! 1974: activate_next:
! 1975: /* Otherwise, put the active lease at the end of the
! 1976: lease list, and try another lease.. */
! 1977: for (lp = client -> leases; lp -> next; lp = lp -> next)
! 1978: ;
! 1979: lp -> next = client -> active;
! 1980: if (lp -> next) {
! 1981: lp -> next -> next = (struct client_lease *)0;
! 1982: }
! 1983: client -> active = client -> leases;
! 1984: client -> leases = client -> leases -> next;
! 1985:
! 1986: /* If we already tried this lease, we've exhausted the
! 1987: set of leases, so we might as well give up for
! 1988: now. */
! 1989: if (client -> active == loop)
! 1990: break;
! 1991: else if (!loop)
! 1992: loop = client -> active;
! 1993: }
! 1994:
! 1995: /* No leases were available, or what was available didn't work, so
! 1996: tell the shell script that we failed to allocate an address,
! 1997: and try again later. */
! 1998: if (onetry) {
! 1999: if (!quiet)
! 2000: log_info ("Unable to obtain a lease on first try.%s",
! 2001: " Exiting.");
! 2002: exit (2);
! 2003: }
! 2004:
! 2005: log_info ("No working leases in persistent database - sleeping.");
! 2006: script_init (client, "FAIL", (struct string_list *)0);
! 2007: if (client -> alias)
! 2008: script_write_params (client, "alias_", client -> alias);
! 2009: script_go (client);
! 2010: client -> state = S_INIT;
! 2011: tv.tv_sec = cur_tv.tv_sec + ((client->config->retry_interval + 1) / 2 +
! 2012: (random() % client->config->retry_interval));
! 2013: tv.tv_usec = ((tv.tv_sec - cur_tv.tv_sec) > 1) ?
! 2014: random() % 1000000 : cur_tv.tv_usec;
! 2015: add_timeout(&tv, state_init, client, 0, 0);
! 2016: go_daemon ();
! 2017: }
! 2018:
! 2019: void send_request (cpp)
! 2020: void *cpp;
! 2021: {
! 2022: struct client_state *client = cpp;
! 2023:
! 2024: int result;
! 2025: int interval;
! 2026: struct sockaddr_in destination;
! 2027: struct in_addr from;
! 2028: struct timeval tv;
! 2029:
! 2030: /* Figure out how long it's been since we started transmitting. */
! 2031: interval = cur_time - client -> first_sending;
! 2032:
! 2033: /* If we're in the INIT-REBOOT or REQUESTING state and we're
! 2034: past the reboot timeout, go to INIT and see if we can
! 2035: DISCOVER an address... */
! 2036: /* XXX In the INIT-REBOOT state, if we don't get an ACK, it
! 2037: means either that we're on a network with no DHCP server,
! 2038: or that our server is down. In the latter case, assuming
! 2039: that there is a backup DHCP server, DHCPDISCOVER will get
! 2040: us a new address, but we could also have successfully
! 2041: reused our old address. In the former case, we're hosed
! 2042: anyway. This is not a win-prone situation. */
! 2043: if ((client -> state == S_REBOOTING ||
! 2044: client -> state == S_REQUESTING) &&
! 2045: interval > client -> config -> reboot_timeout) {
! 2046: cancel:
! 2047: client -> state = S_INIT;
! 2048: cancel_timeout (send_request, client);
! 2049: state_init (client);
! 2050: return;
! 2051: }
! 2052:
! 2053: /* If we're in the reboot state, make sure the media is set up
! 2054: correctly. */
! 2055: if (client -> state == S_REBOOTING &&
! 2056: !client -> medium &&
! 2057: client -> active -> medium ) {
! 2058: script_init (client, "MEDIUM", client -> active -> medium);
! 2059:
! 2060: /* If the medium we chose won't fly, go to INIT state. */
! 2061: if (script_go (client))
! 2062: goto cancel;
! 2063:
! 2064: /* Record the medium. */
! 2065: client -> medium = client -> active -> medium;
! 2066: }
! 2067:
! 2068: /* If the lease has expired, relinquish the address and go back
! 2069: to the INIT state. */
! 2070: if (client -> state != S_REQUESTING &&
! 2071: cur_time > client -> active -> expiry) {
! 2072: /* Run the client script with the new parameters. */
! 2073: script_init (client, "EXPIRE", (struct string_list *)0);
! 2074: script_write_params (client, "old_", client -> active);
! 2075: if (client -> alias)
! 2076: script_write_params (client, "alias_",
! 2077: client -> alias);
! 2078: script_go (client);
! 2079:
! 2080: /* Now do a preinit on the interface so that we can
! 2081: discover a new address. */
! 2082: script_init (client, "PREINIT", (struct string_list *)0);
! 2083: if (client -> alias)
! 2084: script_write_params (client, "alias_",
! 2085: client -> alias);
! 2086: script_go (client);
! 2087:
! 2088: client -> state = S_INIT;
! 2089: state_init (client);
! 2090: return;
! 2091: }
! 2092:
! 2093: /* Do the exponential backoff... */
! 2094: if (!client -> interval)
! 2095: client -> interval = client -> config -> initial_interval;
! 2096: else {
! 2097: client -> interval += ((random () >> 2) %
! 2098: (2 * client -> interval));
! 2099: }
! 2100:
! 2101: /* Don't backoff past cutoff. */
! 2102: if (client -> interval >
! 2103: client -> config -> backoff_cutoff)
! 2104: client -> interval =
! 2105: ((client -> config -> backoff_cutoff / 2)
! 2106: + ((random () >> 2) %
! 2107: client -> config -> backoff_cutoff));
! 2108:
! 2109: /* If the backoff would take us to the expiry time, just set the
! 2110: timeout to the expiry time. */
! 2111: if (client -> state != S_REQUESTING &&
! 2112: cur_time + client -> interval > client -> active -> expiry)
! 2113: client -> interval =
! 2114: client -> active -> expiry - cur_time + 1;
! 2115:
! 2116: /* If the lease T2 time has elapsed, or if we're not yet bound,
! 2117: broadcast the DHCPREQUEST rather than unicasting. */
! 2118: if (client -> state == S_REQUESTING ||
! 2119: client -> state == S_REBOOTING ||
! 2120: cur_time > client -> active -> rebind)
! 2121: destination.sin_addr = sockaddr_broadcast.sin_addr;
! 2122: else
! 2123: memcpy (&destination.sin_addr.s_addr,
! 2124: client -> destination.iabuf,
! 2125: sizeof destination.sin_addr.s_addr);
! 2126: destination.sin_port = remote_port;
! 2127: destination.sin_family = AF_INET;
! 2128: #ifdef HAVE_SA_LEN
! 2129: destination.sin_len = sizeof destination;
! 2130: #endif
! 2131:
! 2132: if (client -> state == S_RENEWING ||
! 2133: client -> state == S_REBINDING)
! 2134: memcpy (&from, client -> active -> address.iabuf,
! 2135: sizeof from);
! 2136: else
! 2137: from.s_addr = INADDR_ANY;
! 2138:
! 2139: /* Record the number of seconds since we started sending. */
! 2140: if (client -> state == S_REQUESTING)
! 2141: client -> packet.secs = client -> secs;
! 2142: else {
! 2143: if (interval < 65536)
! 2144: client -> packet.secs = htons (interval);
! 2145: else
! 2146: client -> packet.secs = htons (65535);
! 2147: }
! 2148:
! 2149: log_info ("DHCPREQUEST on %s to %s port %d",
! 2150: client -> name ? client -> name : client -> interface -> name,
! 2151: inet_ntoa (destination.sin_addr),
! 2152: ntohs (destination.sin_port));
! 2153:
! 2154: if (destination.sin_addr.s_addr != INADDR_BROADCAST &&
! 2155: fallback_interface)
! 2156: result = send_packet (fallback_interface,
! 2157: (struct packet *)0,
! 2158: &client -> packet,
! 2159: client -> packet_length,
! 2160: from, &destination,
! 2161: (struct hardware *)0);
! 2162: else
! 2163: /* Send out a packet. */
! 2164: result = send_packet (client -> interface, (struct packet *)0,
! 2165: &client -> packet,
! 2166: client -> packet_length,
! 2167: from, &destination,
! 2168: (struct hardware *)0);
! 2169:
! 2170: tv.tv_sec = cur_tv.tv_sec + client->interval;
! 2171: tv.tv_usec = ((tv.tv_sec - cur_tv.tv_sec) > 1) ?
! 2172: random() % 1000000 : cur_tv.tv_usec;
! 2173: add_timeout(&tv, send_request, client, 0, 0);
! 2174: }
! 2175:
! 2176: void send_decline (cpp)
! 2177: void *cpp;
! 2178: {
! 2179: struct client_state *client = cpp;
! 2180:
! 2181: int result;
! 2182:
! 2183: log_info ("DHCPDECLINE on %s to %s port %d",
! 2184: client -> name ? client -> name : client -> interface -> name,
! 2185: inet_ntoa (sockaddr_broadcast.sin_addr),
! 2186: ntohs (sockaddr_broadcast.sin_port));
! 2187:
! 2188: /* Send out a packet. */
! 2189: result = send_packet (client -> interface, (struct packet *)0,
! 2190: &client -> packet,
! 2191: client -> packet_length,
! 2192: inaddr_any, &sockaddr_broadcast,
! 2193: (struct hardware *)0);
! 2194: }
! 2195:
! 2196: void send_release (cpp)
! 2197: void *cpp;
! 2198: {
! 2199: struct client_state *client = cpp;
! 2200:
! 2201: int result;
! 2202: struct sockaddr_in destination;
! 2203: struct in_addr from;
! 2204:
! 2205: memcpy (&from, client -> active -> address.iabuf,
! 2206: sizeof from);
! 2207: memcpy (&destination.sin_addr.s_addr,
! 2208: client -> destination.iabuf,
! 2209: sizeof destination.sin_addr.s_addr);
! 2210: destination.sin_port = remote_port;
! 2211: destination.sin_family = AF_INET;
! 2212: #ifdef HAVE_SA_LEN
! 2213: destination.sin_len = sizeof destination;
! 2214: #endif
! 2215:
! 2216: /* Set the lease to end now, so that we don't accidentally
! 2217: reuse it if we restart before the old expiry time. */
! 2218: client -> active -> expiry =
! 2219: client -> active -> renewal =
! 2220: client -> active -> rebind = cur_time;
! 2221: if (!write_client_lease (client, client -> active, 1, 1)) {
! 2222: log_error ("Can't release lease: lease write failed.");
! 2223: return;
! 2224: }
! 2225:
! 2226: log_info ("DHCPRELEASE on %s to %s port %d",
! 2227: client -> name ? client -> name : client -> interface -> name,
! 2228: inet_ntoa (destination.sin_addr),
! 2229: ntohs (destination.sin_port));
! 2230:
! 2231: if (fallback_interface)
! 2232: result = send_packet (fallback_interface,
! 2233: (struct packet *)0,
! 2234: &client -> packet,
! 2235: client -> packet_length,
! 2236: from, &destination,
! 2237: (struct hardware *)0);
! 2238: else
! 2239: /* Send out a packet. */
! 2240: result = send_packet (client -> interface, (struct packet *)0,
! 2241: &client -> packet,
! 2242: client -> packet_length,
! 2243: from, &destination,
! 2244: (struct hardware *)0);
! 2245: }
! 2246:
! 2247: void
! 2248: make_client_options(struct client_state *client, struct client_lease *lease,
! 2249: u_int8_t *type, struct option_cache *sid,
! 2250: struct iaddr *rip, struct option **prl,
! 2251: struct option_state **op)
! 2252: {
! 2253: unsigned i;
! 2254: struct option_cache *oc;
! 2255: struct option *option = NULL;
! 2256: struct buffer *bp = (struct buffer *)0;
! 2257:
! 2258: /* If there are any leftover options, get rid of them. */
! 2259: if (*op)
! 2260: option_state_dereference (op, MDL);
! 2261:
! 2262: /* Allocate space for options. */
! 2263: option_state_allocate (op, MDL);
! 2264:
! 2265: /* Send the server identifier if provided. */
! 2266: if (sid)
! 2267: save_option (&dhcp_universe, *op, sid);
! 2268:
! 2269: oc = (struct option_cache *)0;
! 2270:
! 2271: /* Send the requested address if provided. */
! 2272: if (rip) {
! 2273: client -> requested_address = *rip;
! 2274: i = DHO_DHCP_REQUESTED_ADDRESS;
! 2275: if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash,
! 2276: &i, 0, MDL) &&
! 2277: make_const_option_cache(&oc, NULL, rip->iabuf, rip->len,
! 2278: option, MDL)))
! 2279: log_error ("can't make requested address cache.");
! 2280: else {
! 2281: save_option (&dhcp_universe, *op, oc);
! 2282: option_cache_dereference (&oc, MDL);
! 2283: }
! 2284: option_dereference(&option, MDL);
! 2285: } else {
! 2286: client -> requested_address.len = 0;
! 2287: }
! 2288:
! 2289: i = DHO_DHCP_MESSAGE_TYPE;
! 2290: if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash, &i, 0,
! 2291: MDL) &&
! 2292: make_const_option_cache(&oc, NULL, type, 1, option, MDL)))
! 2293: log_error ("can't make message type.");
! 2294: else {
! 2295: save_option (&dhcp_universe, *op, oc);
! 2296: option_cache_dereference (&oc, MDL);
! 2297: }
! 2298: option_dereference(&option, MDL);
! 2299:
! 2300: if (prl) {
! 2301: int len;
! 2302:
! 2303: /* Probe the length of the list. */
! 2304: len = 0;
! 2305: for (i = 0 ; prl[i] != NULL ; i++)
! 2306: if (prl[i]->universe == &dhcp_universe)
! 2307: len++;
! 2308:
! 2309: if (!buffer_allocate (&bp, len, MDL))
! 2310: log_error ("can't make parameter list buffer.");
! 2311: else {
! 2312: unsigned code = DHO_DHCP_PARAMETER_REQUEST_LIST;
! 2313:
! 2314: len = 0;
! 2315: for (i = 0 ; prl[i] != NULL ; i++)
! 2316: if (prl[i]->universe == &dhcp_universe)
! 2317: bp->data[len++] = prl[i]->code;
! 2318:
! 2319: if (!(option_code_hash_lookup(&option,
! 2320: dhcp_universe.code_hash,
! 2321: &code, 0, MDL) &&
! 2322: make_const_option_cache(&oc, &bp, NULL, len,
! 2323: option, MDL)))
! 2324: log_error ("can't make option cache");
! 2325: else {
! 2326: save_option (&dhcp_universe, *op, oc);
! 2327: option_cache_dereference (&oc, MDL);
! 2328: }
! 2329: option_dereference(&option, MDL);
! 2330: }
! 2331: }
! 2332:
! 2333: /* Run statements that need to be run on transmission. */
! 2334: if (client -> config -> on_transmission)
! 2335: execute_statements_in_scope
! 2336: ((struct binding_value **)0,
! 2337: (struct packet *)0, (struct lease *)0, client,
! 2338: (lease ? lease -> options : (struct option_state *)0),
! 2339: *op, &global_scope,
! 2340: client -> config -> on_transmission,
! 2341: (struct group *)0);
! 2342: }
! 2343:
! 2344: void make_discover (client, lease)
! 2345: struct client_state *client;
! 2346: struct client_lease *lease;
! 2347: {
! 2348: unsigned char discover = DHCPDISCOVER;
! 2349: struct option_state *options = (struct option_state *)0;
! 2350:
! 2351: memset (&client -> packet, 0, sizeof (client -> packet));
! 2352:
! 2353: make_client_options (client,
! 2354: lease, &discover, (struct option_cache *)0,
! 2355: lease ? &lease -> address : (struct iaddr *)0,
! 2356: client -> config -> requested_options,
! 2357: &options);
! 2358:
! 2359: /* Set up the option buffer... */
! 2360: client -> packet_length =
! 2361: cons_options ((struct packet *)0, &client -> packet,
! 2362: (struct lease *)0, client,
! 2363: /* maximum packet size */1500,
! 2364: (struct option_state *)0,
! 2365: options,
! 2366: /* scope */ &global_scope,
! 2367: /* overload */ 0,
! 2368: /* terminate */0,
! 2369: /* bootpp */0,
! 2370: (struct data_string *)0,
! 2371: client -> config -> vendor_space_name);
! 2372:
! 2373: option_state_dereference (&options, MDL);
! 2374: if (client -> packet_length < BOOTP_MIN_LEN)
! 2375: client -> packet_length = BOOTP_MIN_LEN;
! 2376:
! 2377: client -> packet.op = BOOTREQUEST;
! 2378: client -> packet.htype = client -> interface -> hw_address.hbuf [0];
! 2379: client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
! 2380: client -> packet.hops = 0;
! 2381: client -> packet.xid = random ();
! 2382: client -> packet.secs = 0; /* filled in by send_discover. */
! 2383:
! 2384: if (can_receive_unicast_unconfigured (client -> interface))
! 2385: client -> packet.flags = 0;
! 2386: else
! 2387: client -> packet.flags = htons (BOOTP_BROADCAST);
! 2388:
! 2389: memset (&(client -> packet.ciaddr),
! 2390: 0, sizeof client -> packet.ciaddr);
! 2391: memset (&(client -> packet.yiaddr),
! 2392: 0, sizeof client -> packet.yiaddr);
! 2393: memset (&(client -> packet.siaddr),
! 2394: 0, sizeof client -> packet.siaddr);
! 2395: client -> packet.giaddr = giaddr;
! 2396: if (client -> interface -> hw_address.hlen > 0)
! 2397: memcpy (client -> packet.chaddr,
! 2398: &client -> interface -> hw_address.hbuf [1],
! 2399: (unsigned)(client -> interface -> hw_address.hlen - 1));
! 2400:
! 2401: #ifdef DEBUG_PACKET
! 2402: dump_raw ((unsigned char *)&client -> packet, client -> packet_length);
! 2403: #endif
! 2404: }
! 2405:
! 2406:
! 2407: void make_request (client, lease)
! 2408: struct client_state *client;
! 2409: struct client_lease *lease;
! 2410: {
! 2411: unsigned char request = DHCPREQUEST;
! 2412: struct option_cache *oc;
! 2413:
! 2414: memset (&client -> packet, 0, sizeof (client -> packet));
! 2415:
! 2416: if (client -> state == S_REQUESTING)
! 2417: oc = lookup_option (&dhcp_universe, lease -> options,
! 2418: DHO_DHCP_SERVER_IDENTIFIER);
! 2419: else
! 2420: oc = (struct option_cache *)0;
! 2421:
! 2422: if (client -> sent_options)
! 2423: option_state_dereference (&client -> sent_options, MDL);
! 2424:
! 2425: make_client_options (client, lease, &request, oc,
! 2426: ((client -> state == S_REQUESTING ||
! 2427: client -> state == S_REBOOTING)
! 2428: ? &lease -> address
! 2429: : (struct iaddr *)0),
! 2430: client -> config -> requested_options,
! 2431: &client -> sent_options);
! 2432:
! 2433: /* Set up the option buffer... */
! 2434: client -> packet_length =
! 2435: cons_options ((struct packet *)0, &client -> packet,
! 2436: (struct lease *)0, client,
! 2437: /* maximum packet size */1500,
! 2438: (struct option_state *)0,
! 2439: client -> sent_options,
! 2440: /* scope */ &global_scope,
! 2441: /* overload */ 0,
! 2442: /* terminate */0,
! 2443: /* bootpp */0,
! 2444: (struct data_string *)0,
! 2445: client -> config -> vendor_space_name);
! 2446:
! 2447: if (client -> packet_length < BOOTP_MIN_LEN)
! 2448: client -> packet_length = BOOTP_MIN_LEN;
! 2449:
! 2450: client -> packet.op = BOOTREQUEST;
! 2451: client -> packet.htype = client -> interface -> hw_address.hbuf [0];
! 2452: client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
! 2453: client -> packet.hops = 0;
! 2454: client -> packet.xid = client -> xid;
! 2455: client -> packet.secs = 0; /* Filled in by send_request. */
! 2456:
! 2457: /* If we own the address we're requesting, put it in ciaddr;
! 2458: otherwise set ciaddr to zero. */
! 2459: if (client -> state == S_BOUND ||
! 2460: client -> state == S_RENEWING ||
! 2461: client -> state == S_REBINDING) {
! 2462: memcpy (&client -> packet.ciaddr,
! 2463: lease -> address.iabuf, lease -> address.len);
! 2464: client -> packet.flags = 0;
! 2465: } else {
! 2466: memset (&client -> packet.ciaddr, 0,
! 2467: sizeof client -> packet.ciaddr);
! 2468: if (can_receive_unicast_unconfigured (client -> interface))
! 2469: client -> packet.flags = 0;
! 2470: else
! 2471: client -> packet.flags = htons (BOOTP_BROADCAST);
! 2472: }
! 2473:
! 2474: memset (&client -> packet.yiaddr, 0,
! 2475: sizeof client -> packet.yiaddr);
! 2476: memset (&client -> packet.siaddr, 0,
! 2477: sizeof client -> packet.siaddr);
! 2478: if (client -> state != S_BOUND &&
! 2479: client -> state != S_RENEWING)
! 2480: client -> packet.giaddr = giaddr;
! 2481: else
! 2482: memset (&client -> packet.giaddr, 0,
! 2483: sizeof client -> packet.giaddr);
! 2484: if (client -> interface -> hw_address.hlen > 0)
! 2485: memcpy (client -> packet.chaddr,
! 2486: &client -> interface -> hw_address.hbuf [1],
! 2487: (unsigned)(client -> interface -> hw_address.hlen - 1));
! 2488:
! 2489: #ifdef DEBUG_PACKET
! 2490: dump_raw ((unsigned char *)&client -> packet, client -> packet_length);
! 2491: #endif
! 2492: }
! 2493:
! 2494: void make_decline (client, lease)
! 2495: struct client_state *client;
! 2496: struct client_lease *lease;
! 2497: {
! 2498: unsigned char decline = DHCPDECLINE;
! 2499: struct option_cache *oc;
! 2500:
! 2501: struct option_state *options = (struct option_state *)0;
! 2502:
! 2503: /* Create the options cache. */
! 2504: oc = lookup_option (&dhcp_universe, lease -> options,
! 2505: DHO_DHCP_SERVER_IDENTIFIER);
! 2506: make_client_options(client, lease, &decline, oc, &lease->address,
! 2507: NULL, &options);
! 2508:
! 2509: /* Consume the options cache into the option buffer. */
! 2510: memset (&client -> packet, 0, sizeof (client -> packet));
! 2511: client -> packet_length =
! 2512: cons_options ((struct packet *)0, &client -> packet,
! 2513: (struct lease *)0, client, 0,
! 2514: (struct option_state *)0, options,
! 2515: &global_scope, 0, 0, 0, (struct data_string *)0,
! 2516: client -> config -> vendor_space_name);
! 2517:
! 2518: /* Destroy the options cache. */
! 2519: option_state_dereference (&options, MDL);
! 2520:
! 2521: if (client -> packet_length < BOOTP_MIN_LEN)
! 2522: client -> packet_length = BOOTP_MIN_LEN;
! 2523:
! 2524: client -> packet.op = BOOTREQUEST;
! 2525: client -> packet.htype = client -> interface -> hw_address.hbuf [0];
! 2526: client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
! 2527: client -> packet.hops = 0;
! 2528: client -> packet.xid = client -> xid;
! 2529: client -> packet.secs = 0; /* Filled in by send_request. */
! 2530: if (can_receive_unicast_unconfigured (client -> interface))
! 2531: client -> packet.flags = 0;
! 2532: else
! 2533: client -> packet.flags = htons (BOOTP_BROADCAST);
! 2534:
! 2535: /* ciaddr must always be zero. */
! 2536: memset (&client -> packet.ciaddr, 0,
! 2537: sizeof client -> packet.ciaddr);
! 2538: memset (&client -> packet.yiaddr, 0,
! 2539: sizeof client -> packet.yiaddr);
! 2540: memset (&client -> packet.siaddr, 0,
! 2541: sizeof client -> packet.siaddr);
! 2542: client -> packet.giaddr = giaddr;
! 2543: memcpy (client -> packet.chaddr,
! 2544: &client -> interface -> hw_address.hbuf [1],
! 2545: client -> interface -> hw_address.hlen);
! 2546:
! 2547: #ifdef DEBUG_PACKET
! 2548: dump_raw ((unsigned char *)&client -> packet, client -> packet_length);
! 2549: #endif
! 2550: }
! 2551:
! 2552: void make_release (client, lease)
! 2553: struct client_state *client;
! 2554: struct client_lease *lease;
! 2555: {
! 2556: unsigned char request = DHCPRELEASE;
! 2557: struct option_cache *oc;
! 2558:
! 2559: struct option_state *options = (struct option_state *)0;
! 2560:
! 2561: memset (&client -> packet, 0, sizeof (client -> packet));
! 2562:
! 2563: oc = lookup_option (&dhcp_universe, lease -> options,
! 2564: DHO_DHCP_SERVER_IDENTIFIER);
! 2565: make_client_options(client, lease, &request, oc, NULL, NULL, &options);
! 2566:
! 2567: /* Set up the option buffer... */
! 2568: client -> packet_length =
! 2569: cons_options ((struct packet *)0, &client -> packet,
! 2570: (struct lease *)0, client,
! 2571: /* maximum packet size */1500,
! 2572: (struct option_state *)0,
! 2573: options,
! 2574: /* scope */ &global_scope,
! 2575: /* overload */ 0,
! 2576: /* terminate */0,
! 2577: /* bootpp */0,
! 2578: (struct data_string *)0,
! 2579: client -> config -> vendor_space_name);
! 2580:
! 2581: if (client -> packet_length < BOOTP_MIN_LEN)
! 2582: client -> packet_length = BOOTP_MIN_LEN;
! 2583: option_state_dereference (&options, MDL);
! 2584:
! 2585: client -> packet.op = BOOTREQUEST;
! 2586: client -> packet.htype = client -> interface -> hw_address.hbuf [0];
! 2587: client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
! 2588: client -> packet.hops = 0;
! 2589: client -> packet.xid = random ();
! 2590: client -> packet.secs = 0;
! 2591: client -> packet.flags = 0;
! 2592: memcpy (&client -> packet.ciaddr,
! 2593: lease -> address.iabuf, lease -> address.len);
! 2594: memset (&client -> packet.yiaddr, 0,
! 2595: sizeof client -> packet.yiaddr);
! 2596: memset (&client -> packet.siaddr, 0,
! 2597: sizeof client -> packet.siaddr);
! 2598: client -> packet.giaddr = giaddr;
! 2599: memcpy (client -> packet.chaddr,
! 2600: &client -> interface -> hw_address.hbuf [1],
! 2601: client -> interface -> hw_address.hlen);
! 2602:
! 2603: #ifdef DEBUG_PACKET
! 2604: dump_raw ((unsigned char *)&client -> packet, client -> packet_length);
! 2605: #endif
! 2606: }
! 2607:
! 2608: void destroy_client_lease (lease)
! 2609: struct client_lease *lease;
! 2610: {
! 2611: if (lease -> server_name)
! 2612: dfree (lease -> server_name, MDL);
! 2613: if (lease -> filename)
! 2614: dfree (lease -> filename, MDL);
! 2615: option_state_dereference (&lease -> options, MDL);
! 2616: free_client_lease (lease, MDL);
! 2617: }
! 2618:
! 2619: FILE *leaseFile = NULL;
! 2620: int leases_written = 0;
! 2621:
! 2622: void rewrite_client_leases ()
! 2623: {
! 2624: struct interface_info *ip;
! 2625: struct client_state *client;
! 2626: struct client_lease *lp;
! 2627:
! 2628: if (leaseFile != NULL)
! 2629: fclose (leaseFile);
! 2630: leaseFile = fopen (path_dhclient_db, "w");
! 2631: if (leaseFile == NULL) {
! 2632: log_error ("can't create %s: %m", path_dhclient_db);
! 2633: return;
! 2634: }
! 2635:
! 2636: /* If there is a default duid, write it out. */
! 2637: if (default_duid.len != 0)
! 2638: write_duid(&default_duid);
! 2639:
! 2640: /* Write out all the leases attached to configured interfaces that
! 2641: we know about. */
! 2642: for (ip = interfaces; ip; ip = ip -> next) {
! 2643: for (client = ip -> client; client; client = client -> next) {
! 2644: for (lp = client -> leases; lp; lp = lp -> next) {
! 2645: write_client_lease (client, lp, 1, 0);
! 2646: }
! 2647: if (client -> active)
! 2648: write_client_lease (client,
! 2649: client -> active, 1, 0);
! 2650:
! 2651: if (client->active_lease != NULL)
! 2652: write_client6_lease(client,
! 2653: client->active_lease,
! 2654: 1, 0);
! 2655:
! 2656: /* Reset last_write after rewrites. */
! 2657: client->last_write = 0;
! 2658: }
! 2659: }
! 2660:
! 2661: /* Write out any leases that are attached to interfaces that aren't
! 2662: currently configured. */
! 2663: for (ip = dummy_interfaces; ip; ip = ip -> next) {
! 2664: for (client = ip -> client; client; client = client -> next) {
! 2665: for (lp = client -> leases; lp; lp = lp -> next) {
! 2666: write_client_lease (client, lp, 1, 0);
! 2667: }
! 2668: if (client -> active)
! 2669: write_client_lease (client,
! 2670: client -> active, 1, 0);
! 2671:
! 2672: if (client->active_lease != NULL)
! 2673: write_client6_lease(client,
! 2674: client->active_lease,
! 2675: 1, 0);
! 2676:
! 2677: /* Reset last_write after rewrites. */
! 2678: client->last_write = 0;
! 2679: }
! 2680: }
! 2681: fflush (leaseFile);
! 2682: }
! 2683:
! 2684: void write_lease_option (struct option_cache *oc,
! 2685: struct packet *packet, struct lease *lease,
! 2686: struct client_state *client_state,
! 2687: struct option_state *in_options,
! 2688: struct option_state *cfg_options,
! 2689: struct binding_scope **scope,
! 2690: struct universe *u, void *stuff)
! 2691: {
! 2692: const char *name, *dot;
! 2693: struct data_string ds;
! 2694: char *preamble = stuff;
! 2695:
! 2696: memset (&ds, 0, sizeof ds);
! 2697:
! 2698: if (u != &dhcp_universe) {
! 2699: name = u -> name;
! 2700: dot = ".";
! 2701: } else {
! 2702: name = "";
! 2703: dot = "";
! 2704: }
! 2705: if (evaluate_option_cache (&ds, packet, lease, client_state,
! 2706: in_options, cfg_options, scope, oc, MDL)) {
! 2707: fprintf(leaseFile, "%soption %s%s%s %s;\n", preamble,
! 2708: name, dot, oc->option->name,
! 2709: pretty_print_option(oc->option, ds.data, ds.len,
! 2710: 1, 1));
! 2711: data_string_forget (&ds, MDL);
! 2712: }
! 2713: }
! 2714:
! 2715: /* Write an option cache to the lease store. */
! 2716: static void
! 2717: write_options(struct client_state *client, struct option_state *options,
! 2718: const char *preamble)
! 2719: {
! 2720: int i;
! 2721:
! 2722: for (i = 0; i < options->universe_count; i++) {
! 2723: option_space_foreach(NULL, NULL, client, NULL, options,
! 2724: &global_scope, universes[i],
! 2725: (char *)preamble, write_lease_option);
! 2726: }
! 2727: }
! 2728:
! 2729: /* Write the default DUID to the lease store. */
! 2730: static isc_result_t
! 2731: write_duid(struct data_string *duid)
! 2732: {
! 2733: char *str;
! 2734: int stat;
! 2735:
! 2736: if ((duid == NULL) || (duid->len <= 2))
! 2737: return ISC_R_INVALIDARG;
! 2738:
! 2739: if (leaseFile == NULL) { /* XXX? */
! 2740: leaseFile = fopen(path_dhclient_db, "w");
! 2741: if (leaseFile == NULL) {
! 2742: log_error("can't create %s: %m", path_dhclient_db);
! 2743: return ISC_R_IOERROR;
! 2744: }
! 2745: }
! 2746:
! 2747: /* It would make more sense to write this as a hex string,
! 2748: * but our function to do that (print_hex_n) uses a fixed
! 2749: * length buffer...and we can't guarantee a duid would be
! 2750: * less than the fixed length.
! 2751: */
! 2752: str = quotify_buf(duid->data, duid->len, MDL);
! 2753: if (str == NULL)
! 2754: return ISC_R_NOMEMORY;
! 2755:
! 2756: stat = fprintf(leaseFile, "default-duid \"%s\";\n", str);
! 2757: dfree(str, MDL);
! 2758: if (stat <= 0)
! 2759: return ISC_R_IOERROR;
! 2760:
! 2761: if (fflush(leaseFile) != 0)
! 2762: return ISC_R_IOERROR;
! 2763:
! 2764: return ISC_R_SUCCESS;
! 2765: }
! 2766:
! 2767: /* Write a DHCPv6 lease to the store. */
! 2768: isc_result_t
! 2769: write_client6_lease(struct client_state *client, struct dhc6_lease *lease,
! 2770: int rewrite, int sync)
! 2771: {
! 2772: struct dhc6_ia *ia;
! 2773: struct dhc6_addr *addr;
! 2774: int stat;
! 2775: const char *ianame;
! 2776:
! 2777: /* This should include the current lease. */
! 2778: if (!rewrite && (leases_written++ > 20)) {
! 2779: rewrite_client_leases();
! 2780: leases_written = 0;
! 2781: return ISC_R_SUCCESS;
! 2782: }
! 2783:
! 2784: if (client == NULL || lease == NULL)
! 2785: return ISC_R_INVALIDARG;
! 2786:
! 2787: if (leaseFile == NULL) { /* XXX? */
! 2788: leaseFile = fopen(path_dhclient_db, "w");
! 2789: if (leaseFile == NULL) {
! 2790: log_error("can't create %s: %m", path_dhclient_db);
! 2791: return ISC_R_IOERROR;
! 2792: }
! 2793: }
! 2794:
! 2795: stat = fprintf(leaseFile, "lease6 {\n");
! 2796: if (stat <= 0)
! 2797: return ISC_R_IOERROR;
! 2798:
! 2799: stat = fprintf(leaseFile, " interface \"%s\";\n",
! 2800: client->interface->name);
! 2801: if (stat <= 0)
! 2802: return ISC_R_IOERROR;
! 2803:
! 2804: for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
! 2805: switch (ia->ia_type) {
! 2806: case D6O_IA_NA:
! 2807: default:
! 2808: ianame = "ia-na";
! 2809: break;
! 2810: case D6O_IA_TA:
! 2811: ianame = "ia-ta";
! 2812: break;
! 2813: case D6O_IA_PD:
! 2814: ianame = "ia-pd";
! 2815: break;
! 2816: }
! 2817: stat = fprintf(leaseFile, " %s %s {\n",
! 2818: ianame, print_hex_1(4, ia->iaid, 12));
! 2819: if (stat <= 0)
! 2820: return ISC_R_IOERROR;
! 2821:
! 2822: if (ia->ia_type != D6O_IA_TA)
! 2823: stat = fprintf(leaseFile, " starts %d;\n"
! 2824: " renew %u;\n"
! 2825: " rebind %u;\n",
! 2826: (int)ia->starts, ia->renew, ia->rebind);
! 2827: else
! 2828: stat = fprintf(leaseFile, " starts %d;\n",
! 2829: (int)ia->starts);
! 2830: if (stat <= 0)
! 2831: return ISC_R_IOERROR;
! 2832:
! 2833: for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
! 2834: if (ia->ia_type != D6O_IA_PD)
! 2835: stat = fprintf(leaseFile,
! 2836: " iaaddr %s {\n",
! 2837: piaddr(addr->address));
! 2838: else
! 2839: stat = fprintf(leaseFile,
! 2840: " iaprefix %s/%d {\n",
! 2841: piaddr(addr->address),
! 2842: (int)addr->plen);
! 2843: if (stat <= 0)
! 2844: return ISC_R_IOERROR;
! 2845:
! 2846: stat = fprintf(leaseFile, " starts %d;\n"
! 2847: " preferred-life %u;\n"
! 2848: " max-life %u;\n",
! 2849: (int)addr->starts, addr->preferred_life,
! 2850: addr->max_life);
! 2851: if (stat <= 0)
! 2852: return ISC_R_IOERROR;
! 2853:
! 2854: if (addr->options != NULL)
! 2855: write_options(client, addr->options, " ");
! 2856:
! 2857: stat = fprintf(leaseFile, " }\n");
! 2858: if (stat <= 0)
! 2859: return ISC_R_IOERROR;
! 2860: }
! 2861:
! 2862: if (ia->options != NULL)
! 2863: write_options(client, ia->options, " ");
! 2864:
! 2865: stat = fprintf(leaseFile, " }\n");
! 2866: if (stat <= 0)
! 2867: return ISC_R_IOERROR;
! 2868: }
! 2869:
! 2870: if (lease->released) {
! 2871: stat = fprintf(leaseFile, " released;\n");
! 2872: if (stat <= 0)
! 2873: return ISC_R_IOERROR;
! 2874: }
! 2875:
! 2876: if (lease->options != NULL)
! 2877: write_options(client, lease->options, " ");
! 2878:
! 2879: stat = fprintf(leaseFile, "}\n");
! 2880: if (stat <= 0)
! 2881: return ISC_R_IOERROR;
! 2882:
! 2883: if (fflush(leaseFile) != 0)
! 2884: return ISC_R_IOERROR;
! 2885:
! 2886: if (sync) {
! 2887: if (fsync(fileno(leaseFile)) < 0) {
! 2888: log_error("write_client_lease: fsync(): %m");
! 2889: return ISC_R_IOERROR;
! 2890: }
! 2891: }
! 2892:
! 2893: return ISC_R_SUCCESS;
! 2894: }
! 2895:
! 2896: int write_client_lease (client, lease, rewrite, makesure)
! 2897: struct client_state *client;
! 2898: struct client_lease *lease;
! 2899: int rewrite;
! 2900: int makesure;
! 2901: {
! 2902: struct data_string ds;
! 2903: int errors = 0;
! 2904: char *s;
! 2905: const char *tval;
! 2906:
! 2907: if (!rewrite) {
! 2908: if (leases_written++ > 20) {
! 2909: rewrite_client_leases ();
! 2910: leases_written = 0;
! 2911: }
! 2912: }
! 2913:
! 2914: /* If the lease came from the config file, we don't need to stash
! 2915: a copy in the lease database. */
! 2916: if (lease -> is_static)
! 2917: return 1;
! 2918:
! 2919: if (leaseFile == NULL) { /* XXX */
! 2920: leaseFile = fopen (path_dhclient_db, "w");
! 2921: if (leaseFile == NULL) {
! 2922: log_error ("can't create %s: %m", path_dhclient_db);
! 2923: return 0;
! 2924: }
! 2925: }
! 2926:
! 2927: errno = 0;
! 2928: fprintf (leaseFile, "lease {\n");
! 2929: if (lease -> is_bootp) {
! 2930: fprintf (leaseFile, " bootp;\n");
! 2931: if (errno) {
! 2932: ++errors;
! 2933: errno = 0;
! 2934: }
! 2935: }
! 2936: fprintf (leaseFile, " interface \"%s\";\n",
! 2937: client -> interface -> name);
! 2938: if (errno) {
! 2939: ++errors;
! 2940: errno = 0;
! 2941: }
! 2942: if (client -> name) {
! 2943: fprintf (leaseFile, " name \"%s\";\n", client -> name);
! 2944: if (errno) {
! 2945: ++errors;
! 2946: errno = 0;
! 2947: }
! 2948: }
! 2949: fprintf (leaseFile, " fixed-address %s;\n",
! 2950: piaddr (lease -> address));
! 2951: if (errno) {
! 2952: ++errors;
! 2953: errno = 0;
! 2954: }
! 2955: if (lease -> filename) {
! 2956: s = quotify_string (lease -> filename, MDL);
! 2957: if (s) {
! 2958: fprintf (leaseFile, " filename \"%s\";\n", s);
! 2959: if (errno) {
! 2960: ++errors;
! 2961: errno = 0;
! 2962: }
! 2963: dfree (s, MDL);
! 2964: } else
! 2965: errors++;
! 2966:
! 2967: }
! 2968: if (lease->server_name != NULL) {
! 2969: s = quotify_string(lease->server_name, MDL);
! 2970: if (s != NULL) {
! 2971: fprintf(leaseFile, " server-name \"%s\";\n", s);
! 2972: if (errno) {
! 2973: ++errors;
! 2974: errno = 0;
! 2975: }
! 2976: dfree(s, MDL);
! 2977: } else
! 2978: ++errors;
! 2979: }
! 2980: if (lease -> medium) {
! 2981: s = quotify_string (lease -> medium -> string, MDL);
! 2982: if (s) {
! 2983: fprintf (leaseFile, " medium \"%s\";\n", s);
! 2984: if (errno) {
! 2985: ++errors;
! 2986: errno = 0;
! 2987: }
! 2988: dfree (s, MDL);
! 2989: } else
! 2990: errors++;
! 2991: }
! 2992: if (errno != 0) {
! 2993: errors++;
! 2994: errno = 0;
! 2995: }
! 2996:
! 2997: memset (&ds, 0, sizeof ds);
! 2998:
! 2999: write_options(client, lease->options, " ");
! 3000:
! 3001: tval = print_time(lease->renewal);
! 3002: if (tval == NULL ||
! 3003: fprintf(leaseFile, " renew %s\n", tval) < 0)
! 3004: errors++;
! 3005:
! 3006: tval = print_time(lease->rebind);
! 3007: if (tval == NULL ||
! 3008: fprintf(leaseFile, " rebind %s\n", tval) < 0)
! 3009: errors++;
! 3010:
! 3011: tval = print_time(lease->expiry);
! 3012: if (tval == NULL ||
! 3013: fprintf(leaseFile, " expire %s\n", tval) < 0)
! 3014: errors++;
! 3015:
! 3016: if (fprintf(leaseFile, "}\n") < 0)
! 3017: errors++;
! 3018:
! 3019: if (fflush(leaseFile) != 0)
! 3020: errors++;
! 3021:
! 3022: client->last_write = cur_time;
! 3023:
! 3024: if (!errors && makesure) {
! 3025: if (fsync (fileno (leaseFile)) < 0) {
! 3026: log_info ("write_client_lease: %m");
! 3027: return 0;
! 3028: }
! 3029: }
! 3030:
! 3031: return errors ? 0 : 1;
! 3032: }
! 3033:
! 3034: /* Variables holding name of script and file pointer for writing to
! 3035: script. Needless to say, this is not reentrant - only one script
! 3036: can be invoked at a time. */
! 3037: char scriptName [256];
! 3038: FILE *scriptFile;
! 3039:
! 3040: void script_init (client, reason, medium)
! 3041: struct client_state *client;
! 3042: const char *reason;
! 3043: struct string_list *medium;
! 3044: {
! 3045: struct string_list *sl, *next;
! 3046:
! 3047: if (client) {
! 3048: for (sl = client -> env; sl; sl = next) {
! 3049: next = sl -> next;
! 3050: dfree (sl, MDL);
! 3051: }
! 3052: client -> env = (struct string_list *)0;
! 3053: client -> envc = 0;
! 3054:
! 3055: if (client -> interface) {
! 3056: client_envadd (client, "", "interface", "%s",
! 3057: client -> interface -> name);
! 3058: }
! 3059: if (client -> name)
! 3060: client_envadd (client,
! 3061: "", "client", "%s", client -> name);
! 3062: if (medium)
! 3063: client_envadd (client,
! 3064: "", "medium", "%s", medium -> string);
! 3065:
! 3066: client_envadd (client, "", "reason", "%s", reason);
! 3067: client_envadd (client, "", "pid", "%ld", (long int)getpid ());
! 3068: }
! 3069: }
! 3070:
! 3071: void client_option_envadd (struct option_cache *oc,
! 3072: struct packet *packet, struct lease *lease,
! 3073: struct client_state *client_state,
! 3074: struct option_state *in_options,
! 3075: struct option_state *cfg_options,
! 3076: struct binding_scope **scope,
! 3077: struct universe *u, void *stuff)
! 3078: {
! 3079: struct envadd_state *es = stuff;
! 3080: struct data_string data;
! 3081: memset (&data, 0, sizeof data);
! 3082:
! 3083: if (evaluate_option_cache (&data, packet, lease, client_state,
! 3084: in_options, cfg_options, scope, oc, MDL)) {
! 3085: if (data.len) {
! 3086: char name [256];
! 3087: if (dhcp_option_ev_name (name, sizeof name,
! 3088: oc->option)) {
! 3089: const char *value;
! 3090: value = pretty_print_option(oc->option,
! 3091: data.data,
! 3092: data.len, 0, 0);
! 3093: size_t length = strlen(value);
! 3094:
! 3095: if (check_option_values(oc->option->universe,
! 3096: oc->option->code,
! 3097: value, length) == 0) {
! 3098: client_envadd(es->client, es->prefix,
! 3099: name, "%s", value);
! 3100: } else {
! 3101: log_error("suspect value in %s "
! 3102: "option - discarded",
! 3103: name);
! 3104: }
! 3105: data_string_forget (&data, MDL);
! 3106: }
! 3107: }
! 3108: }
! 3109: }
! 3110:
! 3111: void script_write_params (client, prefix, lease)
! 3112: struct client_state *client;
! 3113: const char *prefix;
! 3114: struct client_lease *lease;
! 3115: {
! 3116: int i;
! 3117: struct data_string data;
! 3118: struct option_cache *oc;
! 3119: struct envadd_state es;
! 3120:
! 3121: es.client = client;
! 3122: es.prefix = prefix;
! 3123:
! 3124: client_envadd (client,
! 3125: prefix, "ip_address", "%s", piaddr (lease -> address));
! 3126:
! 3127: /* For the benefit of Linux (and operating systems which may
! 3128: have similar needs), compute the network address based on
! 3129: the supplied ip address and netmask, if provided. Also
! 3130: compute the broadcast address (the host address all ones
! 3131: broadcast address, not the host address all zeroes
! 3132: broadcast address). */
! 3133:
! 3134: memset (&data, 0, sizeof data);
! 3135: oc = lookup_option (&dhcp_universe, lease -> options, DHO_SUBNET_MASK);
! 3136: if (oc && evaluate_option_cache (&data, (struct packet *)0,
! 3137: (struct lease *)0, client,
! 3138: (struct option_state *)0,
! 3139: lease -> options,
! 3140: &global_scope, oc, MDL)) {
! 3141: if (data.len > 3) {
! 3142: struct iaddr netmask, subnet, broadcast;
! 3143:
! 3144: /*
! 3145: * No matter the length of the subnet-mask option,
! 3146: * use only the first four octets. Note that
! 3147: * subnet-mask options longer than 4 octets are not
! 3148: * in conformance with RFC 2132, but servers with this
! 3149: * flaw do exist.
! 3150: */
! 3151: memcpy(netmask.iabuf, data.data, 4);
! 3152: netmask.len = 4;
! 3153: data_string_forget (&data, MDL);
! 3154:
! 3155: subnet = subnet_number (lease -> address, netmask);
! 3156: if (subnet.len) {
! 3157: client_envadd (client, prefix, "network_number",
! 3158: "%s", piaddr (subnet));
! 3159:
! 3160: oc = lookup_option (&dhcp_universe,
! 3161: lease -> options,
! 3162: DHO_BROADCAST_ADDRESS);
! 3163: if (!oc ||
! 3164: !(evaluate_option_cache
! 3165: (&data, (struct packet *)0,
! 3166: (struct lease *)0, client,
! 3167: (struct option_state *)0,
! 3168: lease -> options,
! 3169: &global_scope, oc, MDL))) {
! 3170: broadcast = broadcast_addr (subnet, netmask);
! 3171: if (broadcast.len) {
! 3172: client_envadd (client,
! 3173: prefix, "broadcast_address",
! 3174: "%s", piaddr (broadcast));
! 3175: }
! 3176: }
! 3177: }
! 3178: }
! 3179: data_string_forget (&data, MDL);
! 3180: }
! 3181:
! 3182: if (lease->filename) {
! 3183: if (check_option_values(NULL, DHO_ROOT_PATH,
! 3184: lease->filename,
! 3185: strlen(lease->filename)) == 0) {
! 3186: client_envadd(client, prefix, "filename",
! 3187: "%s", lease->filename);
! 3188: } else {
! 3189: log_error("suspect value in %s "
! 3190: "option - discarded",
! 3191: lease->filename);
! 3192: }
! 3193: }
! 3194:
! 3195: if (lease->server_name) {
! 3196: if (check_option_values(NULL, DHO_HOST_NAME,
! 3197: lease->server_name,
! 3198: strlen(lease->server_name)) == 0 ) {
! 3199: client_envadd (client, prefix, "server_name",
! 3200: "%s", lease->server_name);
! 3201: } else {
! 3202: log_error("suspect value in %s "
! 3203: "option - discarded",
! 3204: lease->server_name);
! 3205: }
! 3206: }
! 3207:
! 3208:
! 3209: for (i = 0; i < lease -> options -> universe_count; i++) {
! 3210: option_space_foreach ((struct packet *)0, (struct lease *)0,
! 3211: client, (struct option_state *)0,
! 3212: lease -> options, &global_scope,
! 3213: universes [i],
! 3214: &es, client_option_envadd);
! 3215: }
! 3216: client_envadd (client, prefix, "expiry", "%d", (int)(lease -> expiry));
! 3217: }
! 3218:
! 3219: int script_go (client)
! 3220: struct client_state *client;
! 3221: {
! 3222: char *scriptName;
! 3223: char *argv [2];
! 3224: char **envp;
! 3225: char reason [] = "REASON=NBI";
! 3226: static char client_path [] = CLIENT_PATH;
! 3227: int i;
! 3228: struct string_list *sp, *next;
! 3229: int pid, wpid, wstatus;
! 3230:
! 3231: if (client)
! 3232: scriptName = client -> config -> script_name;
! 3233: else
! 3234: scriptName = top_level_config.script_name;
! 3235:
! 3236: envp = dmalloc (((client ? client -> envc : 2) +
! 3237: client_env_count + 2) * sizeof (char *), MDL);
! 3238: if (!envp) {
! 3239: log_error ("No memory for client script environment.");
! 3240: return 0;
! 3241: }
! 3242: i = 0;
! 3243: /* Copy out the environment specified on the command line,
! 3244: if any. */
! 3245: for (sp = client_env; sp; sp = sp -> next) {
! 3246: envp [i++] = sp -> string;
! 3247: }
! 3248: /* Copy out the environment specified by dhclient. */
! 3249: if (client) {
! 3250: for (sp = client -> env; sp; sp = sp -> next) {
! 3251: envp [i++] = sp -> string;
! 3252: }
! 3253: } else {
! 3254: envp [i++] = reason;
! 3255: }
! 3256: /* Set $PATH. */
! 3257: envp [i++] = client_path;
! 3258: envp [i] = (char *)0;
! 3259:
! 3260: argv [0] = scriptName;
! 3261: argv [1] = (char *)0;
! 3262:
! 3263: pid = fork ();
! 3264: if (pid < 0) {
! 3265: log_error ("fork: %m");
! 3266: wstatus = 0;
! 3267: } else if (pid) {
! 3268: do {
! 3269: wpid = wait (&wstatus);
! 3270: } while (wpid != pid && wpid > 0);
! 3271: if (wpid < 0) {
! 3272: log_error ("wait: %m");
! 3273: wstatus = 0;
! 3274: }
! 3275: } else {
! 3276: /* We don't want to pass an open file descriptor for
! 3277: * dhclient.leases when executing dhclient-script.
! 3278: */
! 3279: if (leaseFile != NULL)
! 3280: fclose(leaseFile);
! 3281: execve (scriptName, argv, envp);
! 3282: log_error ("execve (%s, ...): %m", scriptName);
! 3283: exit (0);
! 3284: }
! 3285:
! 3286: if (client) {
! 3287: for (sp = client -> env; sp; sp = next) {
! 3288: next = sp -> next;
! 3289: dfree (sp, MDL);
! 3290: }
! 3291: client -> env = (struct string_list *)0;
! 3292: client -> envc = 0;
! 3293: }
! 3294: dfree (envp, MDL);
! 3295: gettimeofday(&cur_tv, NULL);
! 3296: return (WIFEXITED (wstatus) ?
! 3297: WEXITSTATUS (wstatus) : -WTERMSIG (wstatus));
! 3298: }
! 3299:
! 3300: void client_envadd (struct client_state *client,
! 3301: const char *prefix, const char *name, const char *fmt, ...)
! 3302: {
! 3303: char spbuf [1024];
! 3304: char *s;
! 3305: unsigned len;
! 3306: struct string_list *val;
! 3307: va_list list;
! 3308:
! 3309: va_start (list, fmt);
! 3310: len = vsnprintf (spbuf, sizeof spbuf, fmt, list);
! 3311: va_end (list);
! 3312:
! 3313: val = dmalloc (strlen (prefix) + strlen (name) + 1 /* = */ +
! 3314: len + sizeof *val, MDL);
! 3315: if (!val)
! 3316: return;
! 3317: s = val -> string;
! 3318: strcpy (s, prefix);
! 3319: strcat (s, name);
! 3320: s += strlen (s);
! 3321: *s++ = '=';
! 3322: if (len >= sizeof spbuf) {
! 3323: va_start (list, fmt);
! 3324: vsnprintf (s, len + 1, fmt, list);
! 3325: va_end (list);
! 3326: } else
! 3327: strcpy (s, spbuf);
! 3328: val -> next = client -> env;
! 3329: client -> env = val;
! 3330: client -> envc++;
! 3331: }
! 3332:
! 3333: int dhcp_option_ev_name (buf, buflen, option)
! 3334: char *buf;
! 3335: size_t buflen;
! 3336: struct option *option;
! 3337: {
! 3338: int i, j;
! 3339: const char *s;
! 3340:
! 3341: j = 0;
! 3342: if (option -> universe != &dhcp_universe) {
! 3343: s = option -> universe -> name;
! 3344: i = 0;
! 3345: } else {
! 3346: s = option -> name;
! 3347: i = 1;
! 3348: }
! 3349:
! 3350: do {
! 3351: while (*s) {
! 3352: if (j + 1 == buflen)
! 3353: return 0;
! 3354: if (*s == '-')
! 3355: buf [j++] = '_';
! 3356: else
! 3357: buf [j++] = *s;
! 3358: ++s;
! 3359: }
! 3360: if (!i) {
! 3361: s = option -> name;
! 3362: if (j + 1 == buflen)
! 3363: return 0;
! 3364: buf [j++] = '_';
! 3365: }
! 3366: ++i;
! 3367: } while (i != 2);
! 3368:
! 3369: buf [j] = 0;
! 3370: return 1;
! 3371: }
! 3372:
! 3373: void go_daemon ()
! 3374: {
! 3375: static int state = 0;
! 3376: int pid;
! 3377:
! 3378: /* Don't become a daemon if the user requested otherwise. */
! 3379: if (no_daemon) {
! 3380: write_client_pid_file ();
! 3381: return;
! 3382: }
! 3383:
! 3384: /* Only do it once. */
! 3385: if (state)
! 3386: return;
! 3387: state = 1;
! 3388:
! 3389: /* Stop logging to stderr... */
! 3390: log_perror = 0;
! 3391:
! 3392: /* Become a daemon... */
! 3393: if ((pid = fork ()) < 0)
! 3394: log_fatal ("Can't fork daemon: %m");
! 3395: else if (pid)
! 3396: exit (0);
! 3397: /* Become session leader and get pid... */
! 3398: pid = setsid ();
! 3399:
! 3400: /* Close standard I/O descriptors. */
! 3401: close(0);
! 3402: close(1);
! 3403: close(2);
! 3404:
! 3405: /* Reopen them on /dev/null. */
! 3406: open("/dev/null", O_RDWR);
! 3407: open("/dev/null", O_RDWR);
! 3408: open("/dev/null", O_RDWR);
! 3409:
! 3410: write_client_pid_file ();
! 3411:
! 3412: IGNORE_RET (chdir("/"));
! 3413: }
! 3414:
! 3415: void write_client_pid_file ()
! 3416: {
! 3417: FILE *pf;
! 3418: int pfdesc;
! 3419:
! 3420: /* nothing to do if the user doesn't want a pid file */
! 3421: if (no_pid_file == ISC_TRUE) {
! 3422: return;
! 3423: }
! 3424:
! 3425: pfdesc = open (path_dhclient_pid, O_CREAT | O_TRUNC | O_WRONLY, 0644);
! 3426:
! 3427: if (pfdesc < 0) {
! 3428: log_error ("Can't create %s: %m", path_dhclient_pid);
! 3429: return;
! 3430: }
! 3431:
! 3432: pf = fdopen (pfdesc, "w");
! 3433: if (!pf) {
! 3434: close(pfdesc);
! 3435: log_error ("Can't fdopen %s: %m", path_dhclient_pid);
! 3436: } else {
! 3437: fprintf (pf, "%ld\n", (long)getpid ());
! 3438: fclose (pf);
! 3439: }
! 3440: }
! 3441:
! 3442: void client_location_changed ()
! 3443: {
! 3444: struct interface_info *ip;
! 3445: struct client_state *client;
! 3446:
! 3447: for (ip = interfaces; ip; ip = ip -> next) {
! 3448: for (client = ip -> client; client; client = client -> next) {
! 3449: switch (client -> state) {
! 3450: case S_SELECTING:
! 3451: cancel_timeout (send_discover, client);
! 3452: break;
! 3453:
! 3454: case S_BOUND:
! 3455: cancel_timeout (state_bound, client);
! 3456: break;
! 3457:
! 3458: case S_REBOOTING:
! 3459: case S_REQUESTING:
! 3460: case S_RENEWING:
! 3461: cancel_timeout (send_request, client);
! 3462: break;
! 3463:
! 3464: case S_INIT:
! 3465: case S_REBINDING:
! 3466: case S_STOPPED:
! 3467: break;
! 3468: }
! 3469: client -> state = S_INIT;
! 3470: state_reboot (client);
! 3471: }
! 3472: }
! 3473: }
! 3474:
! 3475: void do_release(client)
! 3476: struct client_state *client;
! 3477: {
! 3478: struct data_string ds;
! 3479: struct option_cache *oc;
! 3480:
! 3481: /* Pick a random xid. */
! 3482: client -> xid = random ();
! 3483:
! 3484: /* is there even a lease to release? */
! 3485: if (client -> active) {
! 3486: /* Make a DHCPRELEASE packet, and set appropriate per-interface
! 3487: flags. */
! 3488: make_release (client, client -> active);
! 3489:
! 3490: memset (&ds, 0, sizeof ds);
! 3491: oc = lookup_option (&dhcp_universe,
! 3492: client -> active -> options,
! 3493: DHO_DHCP_SERVER_IDENTIFIER);
! 3494: if (oc &&
! 3495: evaluate_option_cache (&ds, (struct packet *)0,
! 3496: (struct lease *)0, client,
! 3497: (struct option_state *)0,
! 3498: client -> active -> options,
! 3499: &global_scope, oc, MDL)) {
! 3500: if (ds.len > 3) {
! 3501: memcpy (client -> destination.iabuf,
! 3502: ds.data, 4);
! 3503: client -> destination.len = 4;
! 3504: } else
! 3505: client -> destination = iaddr_broadcast;
! 3506:
! 3507: data_string_forget (&ds, MDL);
! 3508: } else
! 3509: client -> destination = iaddr_broadcast;
! 3510: client -> first_sending = cur_time;
! 3511: client -> interval = client -> config -> initial_interval;
! 3512:
! 3513: /* Zap the medium list... */
! 3514: client -> medium = (struct string_list *)0;
! 3515:
! 3516: /* Send out the first and only DHCPRELEASE packet. */
! 3517: send_release (client);
! 3518:
! 3519: /* Do the client script RELEASE operation. */
! 3520: script_init (client,
! 3521: "RELEASE", (struct string_list *)0);
! 3522: if (client -> alias)
! 3523: script_write_params (client, "alias_",
! 3524: client -> alias);
! 3525: script_write_params (client, "old_", client -> active);
! 3526: script_go (client);
! 3527: }
! 3528:
! 3529: /* Cancel any timeouts. */
! 3530: cancel_timeout (state_bound, client);
! 3531: cancel_timeout (send_discover, client);
! 3532: cancel_timeout (state_init, client);
! 3533: cancel_timeout (send_request, client);
! 3534: cancel_timeout (state_reboot, client);
! 3535: client -> state = S_STOPPED;
! 3536: }
! 3537:
! 3538: int dhclient_interface_shutdown_hook (struct interface_info *interface)
! 3539: {
! 3540: do_release (interface -> client);
! 3541:
! 3542: return 1;
! 3543: }
! 3544:
! 3545: int dhclient_interface_discovery_hook (struct interface_info *tmp)
! 3546: {
! 3547: struct interface_info *last, *ip;
! 3548: /* See if we can find the client from dummy_interfaces */
! 3549: last = 0;
! 3550: for (ip = dummy_interfaces; ip; ip = ip -> next) {
! 3551: if (!strcmp (ip -> name, tmp -> name)) {
! 3552: /* Remove from dummy_interfaces */
! 3553: if (last) {
! 3554: ip = (struct interface_info *)0;
! 3555: interface_reference (&ip, last -> next, MDL);
! 3556: interface_dereference (&last -> next, MDL);
! 3557: if (ip -> next) {
! 3558: interface_reference (&last -> next,
! 3559: ip -> next, MDL);
! 3560: interface_dereference (&ip -> next,
! 3561: MDL);
! 3562: }
! 3563: } else {
! 3564: ip = (struct interface_info *)0;
! 3565: interface_reference (&ip,
! 3566: dummy_interfaces, MDL);
! 3567: interface_dereference (&dummy_interfaces, MDL);
! 3568: if (ip -> next) {
! 3569: interface_reference (&dummy_interfaces,
! 3570: ip -> next, MDL);
! 3571: interface_dereference (&ip -> next,
! 3572: MDL);
! 3573: }
! 3574: }
! 3575: /* Copy "client" to tmp */
! 3576: if (ip -> client) {
! 3577: tmp -> client = ip -> client;
! 3578: tmp -> client -> interface = tmp;
! 3579: }
! 3580: interface_dereference (&ip, MDL);
! 3581: break;
! 3582: }
! 3583: last = ip;
! 3584: }
! 3585: return 1;
! 3586: }
! 3587:
! 3588: isc_result_t dhclient_interface_startup_hook (struct interface_info *interface)
! 3589: {
! 3590: struct interface_info *ip;
! 3591: struct client_state *client;
! 3592:
! 3593: /* This code needs some rethinking. It doesn't test against
! 3594: a signal name, and it just kind of bulls into doing something
! 3595: that may or may not be appropriate. */
! 3596:
! 3597: if (interfaces) {
! 3598: interface_reference (&interface -> next, interfaces, MDL);
! 3599: interface_dereference (&interfaces, MDL);
! 3600: }
! 3601: interface_reference (&interfaces, interface, MDL);
! 3602:
! 3603: discover_interfaces (DISCOVER_UNCONFIGURED);
! 3604:
! 3605: for (ip = interfaces; ip; ip = ip -> next) {
! 3606: /* If interfaces were specified, don't configure
! 3607: interfaces that weren't specified! */
! 3608: if (ip -> flags & INTERFACE_RUNNING ||
! 3609: (ip -> flags & (INTERFACE_REQUESTED |
! 3610: INTERFACE_AUTOMATIC)) !=
! 3611: INTERFACE_REQUESTED)
! 3612: continue;
! 3613: script_init (ip -> client,
! 3614: "PREINIT", (struct string_list *)0);
! 3615: if (ip -> client -> alias)
! 3616: script_write_params (ip -> client, "alias_",
! 3617: ip -> client -> alias);
! 3618: script_go (ip -> client);
! 3619: }
! 3620:
! 3621: discover_interfaces (interfaces_requested != 0
! 3622: ? DISCOVER_REQUESTED
! 3623: : DISCOVER_RUNNING);
! 3624:
! 3625: for (ip = interfaces; ip; ip = ip -> next) {
! 3626: if (ip -> flags & INTERFACE_RUNNING)
! 3627: continue;
! 3628: ip -> flags |= INTERFACE_RUNNING;
! 3629: for (client = ip->client ; client ; client = client->next) {
! 3630: client->state = S_INIT;
! 3631: state_reboot(client);
! 3632: }
! 3633: }
! 3634: return ISC_R_SUCCESS;
! 3635: }
! 3636:
! 3637: /* The client should never receive a relay agent information option,
! 3638: so if it does, log it and discard it. */
! 3639:
! 3640: int parse_agent_information_option (packet, len, data)
! 3641: struct packet *packet;
! 3642: int len;
! 3643: u_int8_t *data;
! 3644: {
! 3645: return 1;
! 3646: }
! 3647:
! 3648: /* The client never sends relay agent information options. */
! 3649:
! 3650: unsigned cons_agent_information_options (cfg_options, outpacket,
! 3651: agentix, length)
! 3652: struct option_state *cfg_options;
! 3653: struct dhcp_packet *outpacket;
! 3654: unsigned agentix;
! 3655: unsigned length;
! 3656: {
! 3657: return length;
! 3658: }
! 3659:
! 3660: static void shutdown_exit (void *foo)
! 3661: {
! 3662: exit (0);
! 3663: }
! 3664:
! 3665: isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
! 3666: control_object_state_t newstate)
! 3667: {
! 3668: struct interface_info *ip;
! 3669: struct client_state *client;
! 3670: struct timeval tv;
! 3671:
! 3672: /* Do the right thing for each interface. */
! 3673: for (ip = interfaces; ip; ip = ip -> next) {
! 3674: for (client = ip -> client; client; client = client -> next) {
! 3675: switch (newstate) {
! 3676: case server_startup:
! 3677: return ISC_R_SUCCESS;
! 3678:
! 3679: case server_running:
! 3680: return ISC_R_SUCCESS;
! 3681:
! 3682: case server_shutdown:
! 3683: if (client -> active &&
! 3684: client -> active -> expiry > cur_time) {
! 3685: if (client -> config -> do_forward_update)
! 3686: client_dns_update(client, 0, 0,
! 3687: &client->active->address);
! 3688: do_release (client);
! 3689: }
! 3690: break;
! 3691:
! 3692: case server_hibernate:
! 3693: state_stop (client);
! 3694: break;
! 3695:
! 3696: case server_awaken:
! 3697: state_reboot (client);
! 3698: break;
! 3699: }
! 3700: }
! 3701: }
! 3702:
! 3703: if (newstate == server_shutdown) {
! 3704: tv.tv_sec = cur_tv.tv_sec;
! 3705: tv.tv_usec = cur_tv.tv_usec + 1;
! 3706: add_timeout(&tv, shutdown_exit, 0, 0, 0);
! 3707: }
! 3708: return ISC_R_SUCCESS;
! 3709: }
! 3710:
! 3711: /* Schedule updates to retry occasionally until it no longer times out.
! 3712: */
! 3713: void
! 3714: dhclient_schedule_updates(struct client_state *client, struct iaddr *addr,
! 3715: int offset)
! 3716: {
! 3717: struct dns_update_state *ustate;
! 3718: struct timeval tv;
! 3719:
! 3720: if (!client->config->do_forward_update)
! 3721: return;
! 3722:
! 3723: ustate = dmalloc(sizeof(*ustate), MDL);
! 3724:
! 3725: if (ustate != NULL) {
! 3726: ustate->client = client;
! 3727: ustate->address = *addr;
! 3728: ustate->dns_update_timeout = 1;
! 3729:
! 3730: tv.tv_sec = cur_tv.tv_sec + offset;
! 3731: tv.tv_usec = cur_tv.tv_usec;
! 3732: add_timeout(&tv, client_dns_update_timeout,
! 3733: ustate, NULL, NULL);
! 3734: } else {
! 3735: log_error("Unable to allocate dns update state for %s.",
! 3736: piaddr(*addr));
! 3737: }
! 3738: }
! 3739:
! 3740: /* Called after a timeout if the DNS update failed on the previous try.
! 3741: Retries the update, and if it times out, schedules a retry after
! 3742: ten times as long of a wait. */
! 3743:
! 3744: void client_dns_update_timeout (void *cp)
! 3745: {
! 3746: struct dns_update_state *ustate = cp;
! 3747: isc_result_t status = ISC_R_FAILURE;
! 3748: struct timeval tv;
! 3749:
! 3750: /* XXX: DNS TTL is a problem we need to solve properly. Until
! 3751: * that time, 300 is a placeholder default for something that is
! 3752: * less insane than a value scaled by lease timeout.
! 3753: */
! 3754: if ((ustate->client->active != NULL) ||
! 3755: (ustate->client->active_lease != NULL))
! 3756: status = client_dns_update(ustate->client, 1, 300,
! 3757: &ustate->address);
! 3758:
! 3759: if (status == ISC_R_TIMEDOUT) {
! 3760: if (ustate->dns_update_timeout < 3600)
! 3761: ustate->dns_update_timeout *= 10;
! 3762: tv.tv_sec = cur_tv.tv_sec + ustate->dns_update_timeout;
! 3763: tv.tv_usec = cur_tv.tv_usec;
! 3764: add_timeout(&tv, client_dns_update_timeout,
! 3765: ustate, NULL, NULL);
! 3766: } else
! 3767: dfree(ustate, MDL);
! 3768: }
! 3769:
! 3770: /* See if we should do a DNS update, and if so, do it. */
! 3771:
! 3772: isc_result_t client_dns_update (struct client_state *client, int addp,
! 3773: int ttl, struct iaddr *address)
! 3774: {
! 3775: struct data_string ddns_fwd_name, ddns_dhcid, client_identifier;
! 3776: struct option_cache *oc;
! 3777: int ignorep;
! 3778: int result;
! 3779: isc_result_t rcode;
! 3780:
! 3781: /* If we didn't send an FQDN option, we certainly aren't going to
! 3782: be doing an update. */
! 3783: if (!client -> sent_options)
! 3784: return ISC_R_SUCCESS;
! 3785:
! 3786: /* If we don't have a lease, we can't do an update. */
! 3787: if ((client->active == NULL) && (client->active_lease == NULL))
! 3788: return ISC_R_SUCCESS;
! 3789:
! 3790: /* If we set the no client update flag, don't do the update. */
! 3791: if ((oc = lookup_option (&fqdn_universe, client -> sent_options,
! 3792: FQDN_NO_CLIENT_UPDATE)) &&
! 3793: evaluate_boolean_option_cache (&ignorep, (struct packet *)0,
! 3794: (struct lease *)0, client,
! 3795: client -> sent_options,
! 3796: (struct option_state *)0,
! 3797: &global_scope, oc, MDL))
! 3798: return ISC_R_SUCCESS;
! 3799:
! 3800: /* If we set the "server, please update" flag, or didn't set it
! 3801: to false, don't do the update. */
! 3802: if (!(oc = lookup_option (&fqdn_universe, client -> sent_options,
! 3803: FQDN_SERVER_UPDATE)) ||
! 3804: evaluate_boolean_option_cache (&ignorep, (struct packet *)0,
! 3805: (struct lease *)0, client,
! 3806: client -> sent_options,
! 3807: (struct option_state *)0,
! 3808: &global_scope, oc, MDL))
! 3809: return ISC_R_SUCCESS;
! 3810:
! 3811: /* If no FQDN option was supplied, don't do the update. */
! 3812: memset (&ddns_fwd_name, 0, sizeof ddns_fwd_name);
! 3813: if (!(oc = lookup_option (&fqdn_universe, client -> sent_options,
! 3814: FQDN_FQDN)) ||
! 3815: !evaluate_option_cache (&ddns_fwd_name, (struct packet *)0,
! 3816: (struct lease *)0, client,
! 3817: client -> sent_options,
! 3818: (struct option_state *)0,
! 3819: &global_scope, oc, MDL))
! 3820: return ISC_R_SUCCESS;
! 3821:
! 3822: /* If this is a DHCPv6 client update, make a dhcid string out of
! 3823: * the DUID. If this is a DHCPv4 client update, choose either
! 3824: * the client identifier, if there is one, or the interface's
! 3825: * MAC address.
! 3826: */
! 3827: memset (&ddns_dhcid, 0, sizeof ddns_dhcid);
! 3828:
! 3829: result = 0;
! 3830: memset(&client_identifier, 0, sizeof(client_identifier));
! 3831: if (client->active_lease != NULL) {
! 3832: if (((oc =
! 3833: lookup_option(&dhcpv6_universe, client->sent_options,
! 3834: D6O_CLIENTID)) != NULL) &&
! 3835: evaluate_option_cache(&client_identifier, NULL, NULL,
! 3836: client, client->sent_options, NULL,
! 3837: &global_scope, oc, MDL)) {
! 3838: /* RFC4701 defines type '2' as being for the DUID
! 3839: * field. We aren't using RFC4701 DHCID RR's yet,
! 3840: * but this is as good a value as any.
! 3841: */
! 3842: result = get_dhcid(&ddns_dhcid, 2,
! 3843: client_identifier.data,
! 3844: client_identifier.len);
! 3845: data_string_forget(&client_identifier, MDL);
! 3846: } else
! 3847: log_fatal("Impossible condition at %s:%d.", MDL);
! 3848: } else {
! 3849: if (((oc =
! 3850: lookup_option(&dhcp_universe, client->sent_options,
! 3851: DHO_DHCP_CLIENT_IDENTIFIER)) != NULL) &&
! 3852: evaluate_option_cache(&client_identifier, NULL, NULL,
! 3853: client, client->sent_options, NULL,
! 3854: &global_scope, oc, MDL)) {
! 3855: result = get_dhcid(&ddns_dhcid,
! 3856: DHO_DHCP_CLIENT_IDENTIFIER,
! 3857: client_identifier.data,
! 3858: client_identifier.len);
! 3859: data_string_forget(&client_identifier, MDL);
! 3860: } else
! 3861: result = get_dhcid(&ddns_dhcid, 0,
! 3862: client->interface->hw_address.hbuf,
! 3863: client->interface->hw_address.hlen);
! 3864: }
! 3865: if (!result) {
! 3866: data_string_forget(&ddns_fwd_name, MDL);
! 3867: return ISC_R_SUCCESS;
! 3868: }
! 3869:
! 3870: /* Start the resolver, if necessary. */
! 3871: if (!resolver_inited) {
! 3872: minires_ninit (&resolver_state);
! 3873: resolver_inited = 1;
! 3874: resolver_state.retrans = 1;
! 3875: resolver_state.retry = 1;
! 3876: }
! 3877:
! 3878: /*
! 3879: * Perform updates.
! 3880: */
! 3881: if (ddns_fwd_name.len && ddns_dhcid.len) {
! 3882: if (addp)
! 3883: rcode = ddns_update_fwd(&ddns_fwd_name, *address,
! 3884: &ddns_dhcid, ttl, 1, 1);
! 3885: else
! 3886: rcode = ddns_remove_fwd(&ddns_fwd_name, *address,
! 3887: &ddns_dhcid);
! 3888: } else
! 3889: rcode = ISC_R_FAILURE;
! 3890:
! 3891: data_string_forget (&ddns_fwd_name, MDL);
! 3892: data_string_forget (&ddns_dhcid, MDL);
! 3893: return rcode;
! 3894: }
! 3895:
! 3896: void
! 3897: dhcpv4_client_assignments(void)
! 3898: {
! 3899: struct servent *ent;
! 3900:
! 3901: if (path_dhclient_pid == NULL)
! 3902: path_dhclient_pid = _PATH_DHCLIENT_PID;
! 3903: if (path_dhclient_db == NULL)
! 3904: path_dhclient_db = _PATH_DHCLIENT_DB;
! 3905:
! 3906: /* Default to the DHCP/BOOTP port. */
! 3907: if (!local_port) {
! 3908: /* If we're faking a relay agent, and we're not using loopback,
! 3909: use the server port, not the client port. */
! 3910: if (mockup_relay && giaddr.s_addr != htonl (INADDR_LOOPBACK)) {
! 3911: local_port = htons(67);
! 3912: } else {
! 3913: ent = getservbyname ("dhcpc", "udp");
! 3914: if (!ent)
! 3915: local_port = htons (68);
! 3916: else
! 3917: local_port = ent -> s_port;
! 3918: #ifndef __CYGWIN32__
! 3919: endservent ();
! 3920: #endif
! 3921: }
! 3922: }
! 3923:
! 3924: /* If we're faking a relay agent, and we're not using loopback,
! 3925: we're using the server port, not the client port. */
! 3926: if (mockup_relay && giaddr.s_addr != htonl (INADDR_LOOPBACK)) {
! 3927: remote_port = local_port;
! 3928: } else
! 3929: remote_port = htons (ntohs (local_port) - 1); /* XXX */
! 3930: }
! 3931:
! 3932: /*
! 3933: * The following routines are used to check that certain
! 3934: * strings are reasonable before we pass them to the scripts.
! 3935: * This avoids some problems with scripts treating the strings
! 3936: * as commands - see ticket 23722
! 3937: * The domain checking code should be done as part of assembling
! 3938: * the string but we are doing it here for now due to time
! 3939: * constraints.
! 3940: */
! 3941:
! 3942: static int check_domain_name(const char *ptr, size_t len, int dots)
! 3943: {
! 3944: const char *p;
! 3945:
! 3946: /* not empty or complete length not over 255 characters */
! 3947: if ((len == 0) || (len > 256))
! 3948: return(-1);
! 3949:
! 3950: /* consists of [[:alnum:]-]+ labels separated by [.] */
! 3951: /* a [_] is against RFC but seems to be "widely used"... */
! 3952: for (p=ptr; (*p != 0) && (len-- > 0); p++) {
! 3953: if ((*p == '-') || (*p == '_')) {
! 3954: /* not allowed at begin or end of a label */
! 3955: if (((p - ptr) == 0) || (len == 0) || (p[1] == '.'))
! 3956: return(-1);
! 3957: } else if (*p == '.') {
! 3958: /* each label has to be 1-63 characters;
! 3959: we allow [.] at the end ('foo.bar.') */
! 3960: size_t d = p - ptr;
! 3961: if ((d <= 0) || (d >= 64))
! 3962: return(-1);
! 3963: ptr = p + 1; /* jump to the next label */
! 3964: if ((dots > 0) && (len > 0))
! 3965: dots--;
! 3966: } else if (isalnum((unsigned char)*p) == 0) {
! 3967: /* also numbers at the begin are fine */
! 3968: return(-1);
! 3969: }
! 3970: }
! 3971: return(dots ? -1 : 0);
! 3972: }
! 3973:
! 3974: static int check_domain_name_list(const char *ptr, size_t len, int dots)
! 3975: {
! 3976: const char *p;
! 3977: int ret = -1; /* at least one needed */
! 3978:
! 3979: if ((ptr == NULL) || (len == 0))
! 3980: return(-1);
! 3981:
! 3982: for (p=ptr; (*p != 0) && (len > 0); p++, len--) {
! 3983: if (*p != ' ')
! 3984: continue;
! 3985: if (p > ptr) {
! 3986: if (check_domain_name(ptr, p - ptr, dots) != 0)
! 3987: return(-1);
! 3988: ret = 0;
! 3989: }
! 3990: ptr = p + 1;
! 3991: }
! 3992: if (p > ptr)
! 3993: return(check_domain_name(ptr, p - ptr, dots));
! 3994: else
! 3995: return(ret);
! 3996: }
! 3997:
! 3998: static int check_option_values(struct universe *universe,
! 3999: unsigned int opt,
! 4000: const char *ptr,
! 4001: size_t len)
! 4002: {
! 4003: if (ptr == NULL)
! 4004: return(-1);
! 4005:
! 4006: /* just reject options we want to protect, will be escaped anyway */
! 4007: if ((universe == NULL) || (universe == &dhcp_universe)) {
! 4008: switch(opt) {
! 4009: case DHO_DOMAIN_NAME:
! 4010: #ifdef ACCEPT_LIST_IN_DOMAIN_NAME
! 4011: return check_domain_name_list(ptr, len, 0);
! 4012: #else
! 4013: return check_domain_name(ptr, len, 0);
! 4014: #endif
! 4015: case DHO_HOST_NAME:
! 4016: case DHO_NIS_DOMAIN:
! 4017: case DHO_NETBIOS_SCOPE:
! 4018: return check_domain_name(ptr, len, 0);
! 4019: break;
! 4020: case DHO_DOMAIN_SEARCH:
! 4021: return check_domain_name_list(ptr, len, 0);
! 4022: break;
! 4023: case DHO_ROOT_PATH:
! 4024: if (len == 0)
! 4025: return(-1);
! 4026: for (; (*ptr != 0) && (len-- > 0); ptr++) {
! 4027: if(!(isalnum((unsigned char)*ptr) ||
! 4028: *ptr == '#' || *ptr == '%' ||
! 4029: *ptr == '+' || *ptr == '-' ||
! 4030: *ptr == '_' || *ptr == ':' ||
! 4031: *ptr == '.' || *ptr == ',' ||
! 4032: *ptr == '@' || *ptr == '~' ||
! 4033: *ptr == '\\' || *ptr == '/' ||
! 4034: *ptr == '[' || *ptr == ']' ||
! 4035: *ptr == '=' || *ptr == ' '))
! 4036: return(-1);
! 4037: }
! 4038: return(0);
! 4039: break;
! 4040: }
! 4041: }
! 4042:
! 4043: #ifdef DHCPv6
! 4044: if (universe == &dhcpv6_universe) {
! 4045: switch(opt) {
! 4046: case D6O_SIP_SERVERS_DNS:
! 4047: case D6O_DOMAIN_SEARCH:
! 4048: case D6O_NIS_DOMAIN_NAME:
! 4049: case D6O_NISP_DOMAIN_NAME:
! 4050: return check_domain_name_list(ptr, len, 0);
! 4051: break;
! 4052: }
! 4053: }
! 4054: #endif
! 4055:
! 4056: return(0);
! 4057: }
! 4058:
! 4059: static void
! 4060: add_reject(struct packet *packet) {
! 4061: struct iaddrmatchlist *list;
! 4062:
! 4063: list = dmalloc(sizeof(struct iaddrmatchlist), MDL);
! 4064: if (!list)
! 4065: log_fatal ("no memory for reject list!");
! 4066:
! 4067: /*
! 4068: * client_addr is misleading - it is set to source address in common
! 4069: * code.
! 4070: */
! 4071: list->match.addr = packet->client_addr;
! 4072: /* Set mask to indicate host address. */
! 4073: list->match.mask.len = list->match.addr.len;
! 4074: memset(list->match.mask.iabuf, 0xff, sizeof(list->match.mask.iabuf));
! 4075:
! 4076: /* Append to reject list for the source interface. */
! 4077: list->next = packet->interface->client->config->reject_list;
! 4078: packet->interface->client->config->reject_list = list;
! 4079:
! 4080: /*
! 4081: * We should inform user that we won't be accepting this server
! 4082: * anymore.
! 4083: */
! 4084: log_info("Server added to list of rejected servers.");
! 4085: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>