dhclient - Have only one timeout at once.
[dragonfly.git] / sbin / dhclient / dhclient.c
index 79baeec..159e956 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: src/sbin/dhclient/dhclient.c,v 1.137 2010/10/15 09:51:15 jsg Exp $    */
+/*     $OpenBSD: src/sbin/dhclient/dhclient.c,v 1.146 2012/07/09 16:21:21 krw Exp $    */
 
 /*
  * Copyright 2004 Henning Brauer <henning@openbsd.org>
@@ -205,8 +205,7 @@ routehandler(void)
                        if (addr_eq(a, l->address))
                                break;
 
-               if (l != NULL || (client->alias &&
-                   addr_eq(a, client->alias->address)))
+               if (l != NULL)
                        /* new addr is the one we set */
                        break;
                snprintf(buf, sizeof(buf), "%s: %s",
@@ -241,9 +240,9 @@ routehandler(void)
                            ifi->linkstat ? "up" : "down",
                            linkstat ? "up" : "down");
 #endif
-                       ifi->linkstat = interface_link_status(ifi->name);
+                       ifi->linkstat = interface_status(ifi->name);
                        if (ifi->linkstat) {
-                               client->state = S_INIT;
+                               client->state = S_REBOOTING;
                                state_reboot();
                        }
                }
@@ -262,9 +261,7 @@ routehandler(void)
        return;
 
 die:
-       script_init("FAIL", NULL);
-       if (client->alias)
-               script_write_params("alias_", client->alias);
+       script_init("FAIL");
        script_go();
        error("routehandler: %s", errmsg);
 }
@@ -343,7 +340,7 @@ main(int argc, char *argv[])
        } else
                i = 0;
 
-       while (!(ifi->linkstat = interface_link_status(ifi->name))) {
+       while (!(ifi->linkstat = interface_status(ifi->name))) {
                if (i == 0)
                        fprintf(stderr, "%s: no link ...", ifi->name);
                else if (i > 0)
@@ -381,11 +378,6 @@ main(int argc, char *argv[])
        rewrite_client_leases();
        close(fd);
 
-       priv_script_init("PREINIT", NULL);
-       if (client->alias)
-               priv_script_write_params("alias_", client->alias);
-       priv_script_go();
-
        if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
                error("socket(PF_ROUTE, SOCK_RAW): %m");
 
@@ -409,7 +401,7 @@ main(int argc, char *argv[])
        setproctitle("%s", ifi->name);
 
        if (ifi->linkstat) {
-               client->state = S_INIT;
+               client->state = S_REBOOTING;
                state_reboot();
        } else
                go_daemon();
@@ -460,15 +452,17 @@ usage(void)
 void
 state_reboot(void)
 {
+       /* Cancel all timeouts, since a link state change gets us here
+          and can happen anytime. */
+       cancel_timeout();
+
        /* If we don't remember an active lease, go straight to INIT. */
        if (!client->active || client->active->is_bootp) {
+               client->state = S_INIT;
                state_init();
                return;
        }
 
-       /* We are in the rebooting state. */
-       client->state = S_REBOOTING;
-
        /* make_request doesn't initialize xid because it normally comes
           from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER,
           so pick an xid now. */
@@ -479,10 +473,7 @@ state_reboot(void)
        make_request(client->active);
        client->destination = iaddr_broadcast;
        client->first_sending = cur_time;
-       client->interval = config->initial_interval;
-
-       /* Zap the medium list... */
-       client->medium = NULL;
+       client->interval = 0;
 
        /* Send out the first DHCPREQUEST packet. */
        send_request();
@@ -502,7 +493,7 @@ state_init(void)
        client->destination = iaddr_broadcast;
        client->state = S_SELECTING;
        client->first_sending = cur_time;
-       client->interval = config->initial_interval;
+       client->interval = 0;
 
        /* Add an immediate timeout to cause the first DHCPDISCOVER packet
           to go out. */
@@ -520,8 +511,7 @@ state_selecting(void)
 
        /* Cancel state_selecting and send_discover timeouts, since either
           one could have got us here. */
-       cancel_timeout(state_selecting);
-       cancel_timeout(send_discover);
+       cancel_timeout();
 
        /* We have received one or more DHCPOFFER packets.   Currently,
           the only criterion by which we judge leases is whether or
@@ -530,25 +520,11 @@ state_selecting(void)
        for (lp = client->offered_leases; lp; lp = next) {
                next = lp->next;
 
-               /* Check to see if we got an ARPREPLY for the address
-                  in this particular lease. */
                if (!picked) {
-                       script_init("ARPCHECK", lp->medium);
-                       script_write_params("check_", lp);
-
-                       /* If the ARPCHECK code detects another
-                          machine using the offered address, it exits
-                          nonzero.  We need to send a DHCPDECLINE and
-                          toss the lease. */
-                       if (script_go()) {
-                               make_decline(lp);
-                               send_decline();
-                               goto freeit;
-                       }
                        picked = lp;
-                       picked->next = NULL;
                } else {
-freeit:
+                       make_decline(lp);
+                       send_decline();
                        free_client_lease(lp);
                }
        }
@@ -561,6 +537,7 @@ freeit:
                state_init();
                return;
        }
+       picked->next = NULL;
 
        /* If it was a BOOTREPLY, we can just take the address right now. */
        if (!picked->options[DHO_DHCP_MESSAGE_TYPE].len) {
@@ -583,7 +560,7 @@ freeit:
        client->destination = iaddr_broadcast;
        client->state = S_REQUESTING;
        client->first_sending = cur_time;
-       client->interval = config->initial_interval;
+       client->interval = 0;
 
        /* Make a DHCPREQUEST packet from the lease we picked. */
        make_request(picked);
@@ -618,7 +595,7 @@ dhcpack(struct iaddr client_addr, struct option_data *options)
        client->new = lease;
 
        /* Stop resending DHCPREQUEST. */
-       cancel_timeout(send_request);
+       cancel_timeout();
 
        /* Figure out the lease time. */
        if (client->new->options[DHO_DHCP_LEASE_TIME].data)
@@ -667,22 +644,13 @@ dhcpack(struct iaddr client_addr, struct option_data *options)
 void
 bind_lease(void)
 {
-       /* Remember the medium. */
-       client->new->medium = client->medium;
-
-       /* Write out the new lease. */
-       write_client_lease(client->new, 0);
-
        /* Run the client script with the new parameters. */
        script_init((client->state == S_REQUESTING ? "BOUND" :
            (client->state == S_RENEWING ? "RENEW" :
-           (client->state == S_REBOOTING ? "REBOOT" : "REBIND"))),
-           client->new->medium);
+               (client->state == S_REBOOTING ? "REBOOT" : "REBIND"))));
        if (client->active && client->state != S_REBOOTING)
                script_write_params("old_", client->active);
        script_write_params("new_", client->new);
-       if (client->alias)
-               script_write_params("alias_", client->alias);
        script_go();
 
        /* Replace the old active lease with the new one. */
@@ -691,8 +659,11 @@ bind_lease(void)
        client->active = client->new;
        client->new = NULL;
 
-       /* Set up a timeout to start the renewal process. */
-       add_timeout(client->active->renewal, state_bound);
+       /* Write out new leases file. */
+       rewrite_client_leases();
+
+       /* Set timeout to start the renewal process. */
+       set_timeout(client->active->renewal, state_bound);
 
        note("bound to %s -- renewal in %ld seconds.",
            piaddr(client->active->address),
@@ -724,7 +695,7 @@ state_bound(void)
                client->destination = iaddr_broadcast;
 
        client->first_sending = cur_time;
-       client->interval = config->initial_interval;
+       client->interval = 0;
        client->state = S_RENEWING;
 
        /* Send the first packet immediately. */
@@ -736,7 +707,7 @@ dhcpoffer(struct iaddr client_addr, struct option_data *options)
 {
        struct client_lease *lease, *lp;
        int i;
-       int arp_timeout_needed, stop_selecting;
+       int stop_selecting;
        char *name = options[DHO_DHCP_MESSAGE_TYPE].len ? "DHCPOFFER" :
            "BOOTREPLY";
 
@@ -778,19 +749,6 @@ dhcpoffer(struct iaddr client_addr, struct option_data *options)
        if (!options[DHO_DHCP_MESSAGE_TYPE].len)
                lease->is_bootp = 1;
 
-       /* Record the medium under which this lease was offered. */
-       lease->medium = client->medium;
-
-       /* Send out an ARP Request for the offered IP address. */
-       script_init("ARPSEND", lease->medium);
-       script_write_params("check_", lease);
-       /* If the script can't send an ARP request without waiting,
-          we'll be waiting when we do the ARPCHECK, so don't wait now. */
-       if (script_go())
-               arp_timeout_needed = 0;
-       else
-               arp_timeout_needed = 2;
-
        /* Figure out when we're supposed to stop selecting. */
        stop_selecting = client->first_sending + config->select_interval;
 
@@ -800,14 +758,6 @@ dhcpoffer(struct iaddr client_addr, struct option_data *options)
                lease->next = client->offered_leases;
                client->offered_leases = lease;
        } else {
-               /* If we already have an offer, and arping for this
-                  offer would take us past the selection timeout,
-                  then don't extend the timeout - just hope for the
-                  best. */
-               if (client->offered_leases &&
-                   (cur_time + arp_timeout_needed) > stop_selecting)
-                       arp_timeout_needed = 0;
-
                /* Put the lease at the end of the list. */
                lease->next = NULL;
                if (!client->offered_leases)
@@ -820,20 +770,13 @@ dhcpoffer(struct iaddr client_addr, struct option_data *options)
                }
        }
 
-       /* If we're supposed to stop selecting before we've had time
-          to wait for the ARPREPLY, add some delay to wait for
-          the ARPREPLY. */
-       if (stop_selecting - cur_time < arp_timeout_needed)
-               stop_selecting = cur_time + arp_timeout_needed;
-
        /* If the selecting interval has expired, go immediately to
           state_selecting().  Otherwise, time out into
           state_selecting at the select interval. */
-       if (stop_selecting <= 0)
+       if (stop_selecting <= cur_time)
                state_selecting();
        else {
-               add_timeout(stop_selecting, state_selecting);
-               cancel_timeout(send_discover);
+               set_timeout(stop_selecting, state_selecting);
        }
 }
 
@@ -931,7 +874,7 @@ dhcpnak(struct iaddr client_addr, struct option_data *options)
        client->active = NULL;
 
        /* Stop sending DHCPREQUEST packets... */
-       cancel_timeout(send_request);
+       cancel_timeout();
 
        client->state = S_INIT;
        state_init();
@@ -957,30 +900,6 @@ send_discover(void)
                return;
        }
 
-       /* If we're selecting media, try the whole list before doing
-          the exponential backoff, but if we've already received an
-          offer, stop looping, because we obviously have it right. */
-       if (!client->offered_leases && config->media) {
-               int fail = 0;
-again:
-               if (client->medium) {
-                       client->medium = client->medium->next;
-                       increase = 0;
-               }
-               if (!client->medium) {
-                       if (fail)
-                               error("No valid media types for %s!", ifi->name);
-                       client->medium = config->media;
-                       increase = 1;
-               }
-
-               note("Trying medium \"%s\" %d", client->medium->string,
-                   increase);
-               script_init("MEDIUM", client->medium);
-               if (script_go())
-                       goto again;
-       }
-
        /*
         * If we're supposed to increase the interval, do so.  If it's
         * currently zero (i.e., we haven't sent any packets yet), set
@@ -1025,7 +944,7 @@ again:
        /* Send out a packet. */
        send_packet(inaddr_any, &sockaddr_broadcast, NULL);
 
-       add_timeout(cur_time + client->interval, send_discover);
+       set_timeout(cur_time + client->interval, send_discover);
 }
 
 /*
@@ -1054,12 +973,8 @@ state_panic(void)
                            piaddr(client->active->address));
                        /* Run the client script with the existing
                           parameters. */
-                       script_init("TIMEOUT",
-                           client->active->medium);
+                       script_init("TIMEOUT");
                        script_write_params("new_", client->active);
-                       if (client->alias)
-                               script_write_params("alias_",
-                                   client->alias);
 
                        /* If the old lease is still good and doesn't
                           yet need renewal, go into BOUND state and
@@ -1071,7 +986,7 @@ state_panic(void)
                                        note("bound: renewal in %ld seconds.",
                                            client->active->renewal -
                                            cur_time);
-                                       add_timeout(client->active->renewal,
+                                       set_timeout(client->active->renewal,
                                            state_bound);
                                } else {
                                        client->state = S_BOUND;
@@ -1115,12 +1030,10 @@ activate_next:
           tell the shell script that we failed to allocate an address,
           and try again later. */
        note("No working leases in persistent database - sleeping.");
-       script_init("FAIL", NULL);
-       if (client->alias)
-               script_write_params("alias_", client->alias);
+       script_init("FAIL");
        script_go();
        client->state = S_INIT;
-       add_timeout(cur_time + config->retry_interval, state_init);
+       set_timeout(cur_time + config->retry_interval, state_init);
        go_daemon();
 }
 
@@ -1147,44 +1060,19 @@ send_request(void)
        if ((client->state == S_REBOOTING ||
            client->state == S_REQUESTING) &&
            interval > config->reboot_timeout) {
-cancel:
                client->state = S_INIT;
-               cancel_timeout(send_request);
+               cancel_timeout();
                state_init();
                return;
        }
 
-       /* If we're in the reboot state, make sure the media is set up
-          correctly. */
-       if (client->state == S_REBOOTING &&
-           !client->medium &&
-           client->active->medium) {
-               script_init("MEDIUM", client->active->medium);
-
-               /* If the medium we chose won't fly, go to INIT state. */
-               if (script_go())
-                       goto cancel;
-
-               /* Record the medium. */
-               client->medium = client->active->medium;
-       }
-
        /* If the lease has expired, relinquish the address and go back
           to the INIT state. */
        if (client->state != S_REQUESTING &&
            cur_time > client->active->expiry) {
                /* Run the client script with the new parameters. */
-               script_init("EXPIRE", NULL);
+               script_init("EXPIRE");
                script_write_params("old_", client->active);
-               if (client->alias)
-                       script_write_params("alias_", client->alias);
-               script_go();
-
-               /* Now do a preinit on the interface so that we can
-                  discover a new address. */
-               script_init("PREINIT", NULL);
-               if (client->alias)
-                       script_write_params("alias_", client->alias);
                script_go();
 
                client->state = S_INIT;
@@ -1245,7 +1133,7 @@ cancel:
        /* Send out a packet. */
        send_packet(from, &destination, NULL);
 
-       add_timeout(cur_time + client->interval, send_request);
+       set_timeout(cur_time + client->interval, send_request);
 }
 
 void
@@ -1297,8 +1185,7 @@ make_discover(struct client_lease *lease)
                }
 
        /* Set up the option buffer to fit in a minimal UDP packet. */
-       i = cons_options(client->packet.options, 576 - DHCP_FIXED_LEN,
-           options);
+       i = cons_options(options);
        if (i == -1 || client->packet.options[i] != DHO_END)
                error("options do not fit in DHCPDISCOVER packet.");
        client->packet_length = DHCP_FIXED_NON_UDP+i+1;
@@ -1366,8 +1253,7 @@ make_request(struct client_lease * lease)
                }
 
        /* Set up the option buffer to fit in a minimal UDP packet. */
-       i = cons_options(client->packet.options, 576 - DHCP_FIXED_LEN,
-           options);
+       i = cons_options(options);
        if (i == -1 || client->packet.options[i] != DHO_END)
                error("options do not fit in DHCPREQUEST packet.");
        client->packet_length = DHCP_FIXED_NON_UDP+i+1;
@@ -1434,8 +1320,7 @@ make_decline(struct client_lease *lease)
        }
 
        /* Set up the option buffer to fit in a minimal UDP packet. */
-       i = cons_options(client->packet.options, 576 - DHCP_FIXED_LEN,
-           options);
+       i = cons_options(options);
        if (i == -1 || client->packet.options[i] != DHO_END)
                error("options do not fit in DHCPDECLINE packet.");
        client->packet_length = DHCP_FIXED_NON_UDP+i+1;
@@ -1486,10 +1371,15 @@ rewrite_client_leases(void)
        fflush(leaseFile);
        rewind(leaseFile);
 
-       for (lp = client->leases; lp; lp = lp->next)
-               write_client_lease(lp, 1);
+       for (lp = client->leases; lp; lp = lp->next) {
+               if (client->active && addr_eq(lp->address,
+                       client->active->address))
+                       continue;
+               write_client_lease(lp);
+       }
+
        if (client->active)
-               write_client_lease(client->active, 1);
+               write_client_lease(client->active);
 
        fflush(leaseFile);
        ftruncate(fileno(leaseFile), ftello(leaseFile));
@@ -1497,19 +1387,11 @@ rewrite_client_leases(void)
 }
 
 void
-write_client_lease(struct client_lease *lease, int rewrite)
+write_client_lease(struct client_lease *lease)
 {
-       static int leases_written;
        struct tm *t;
        int i;
 
-       if (!rewrite) {
-               if (leases_written++ > 20) {
-                       rewrite_client_leases();
-                       leases_written = 0;
-               }
-       }
-
        /* If the lease came from the config file, we don't need to stash
           a copy in the lease database. */
        if (lease->is_static)
@@ -1528,8 +1410,6 @@ write_client_lease(struct client_lease *lease, int rewrite)
        if (lease->server_name)
                fprintf(leaseFile, "  server-name \"%s\";\n",
                    lease->server_name);
-       if (lease->medium)
-               fprintf(leaseFile, "  medium \"%s\";\n", lease->medium->string);
        for (i = 0; i < 256; i++)
                if (lease->options[i].len)
                        fprintf(leaseFile, "  option %s %s;\n",
@@ -1554,26 +1434,17 @@ write_client_lease(struct client_lease *lease, int rewrite)
 }
 
 void
-script_init(char *reason, struct string_list *medium)
+script_init(char *reason)
 {
-       size_t           len, mediumlen = 0;
+       size_t           len;
        struct imsg_hdr  hdr;
        struct buf      *buf;
 
-       if (medium != NULL && medium->string != NULL)
-               mediumlen = strlen(medium->string);
-
        hdr.code = IMSG_SCRIPT_INIT;
-       hdr.len = sizeof(struct imsg_hdr) +
-           sizeof(size_t) + mediumlen +
-           sizeof(size_t) + strlen(reason);
-
+       hdr.len = sizeof(struct imsg_hdr) + sizeof(size_t) + strlen(reason);
        buf = buf_open(hdr.len);
 
        buf_add(buf, &hdr, sizeof(hdr));
-       buf_add(buf, &mediumlen, sizeof(mediumlen));
-       if (mediumlen > 0)
-               buf_add(buf, medium->string, mediumlen);
        len = strlen(reason);
        buf_add(buf, &len, sizeof(len));
        buf_add(buf, reason, len);
@@ -1582,7 +1453,7 @@ script_init(char *reason, struct string_list *medium)
 }
 
 void
-priv_script_init(char *reason, char *medium)
+priv_script_init(char *reason)
 {
        client->scriptEnvsize = 100;
        if (client->scriptEnv == NULL)
@@ -1599,9 +1470,6 @@ priv_script_init(char *reason, char *medium)
 
        script_set_env("", "interface", ifi->name);
 
-       if (medium)
-               script_set_env("", "medium", medium);
-
        script_set_env("", "reason", reason);
 }
 
@@ -2045,7 +1913,8 @@ check_option(struct client_lease *l, int option)
        case DHO_END:
                return (1);
        default:
-               warning("unknown dhcp option value 0x%x", option);
+               if (!unknown_ok)
+                       warning("unknown dhcp option value 0x%x", option);
                return (unknown_ok);
        }
 }