]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
merge 17621
authorFrancis Dupont <fdupont@isc.org>
Fri, 23 May 2008 13:22:23 +0000 (13:22 +0000)
committerFrancis Dupont <fdupont@isc.org>
Fri, 23 May 2008 13:22:23 +0000 (13:22 +0000)
client/clparse.c
client/dhc6.c
client/dhclient.c
includes/dhcp6.h
includes/dhcpd.h

index e5f4e81ce3bc42a83d45d7f46d972f0e4baf3687..52a7db251f71e8fc84af83169d0a71562feb62c2 100644 (file)
@@ -1439,8 +1439,7 @@ parse_client6_lease_statement(struct parse *cfile)
 
        if (iface == NULL)
                parse_warn(cfile, "Lease has no interface designation.");
-
-       if (!has_name && (client == NULL)) {
+       else if (!has_name && (client == NULL)) {
                for (client = iface->client ; client != NULL ;
                     client = client->next) {
                        if (client->name == NULL)
index 19917e838f5479c8848ef0d109880f3b80a30f32..00f30e6158262bbff471ff5e3808efa475f5172f 100644 (file)
@@ -39,6 +39,7 @@ struct option *ia_pd_option = NULL;
 struct option *iaaddr_option = NULL;
 struct option *iaprefix_option = NULL;
 struct option *oro_option = NULL;
+struct option *irt_option = NULL;
 
 static struct dhc6_lease *dhc6_dup_lease(struct dhc6_lease *lease,
                                         const char *file, int line);
@@ -66,8 +67,10 @@ static struct dhc6_ia *find_ia_na(struct dhc6_ia *head, const char *id);
 static struct dhc6_addr *find_addr(struct dhc6_addr *head,
                                   struct iaddr *address);
 void init_handler(struct packet *packet, struct client_state *client);
+void info_request_handler(struct packet *packet, struct client_state *client);
 void rapid_commit_handler(struct packet *packet, struct client_state *client);
 void do_init6(void *input);
+void do_info_request6(void *input);
 void do_confirm6(void *input);
 void reply_handler(struct packet *packet, struct client_state *client);
 static isc_result_t dhc6_add_ia_na(struct client_state *client,
@@ -80,6 +83,8 @@ void do_select6(void *input);
 void do_refresh6(void *input);
 static void do_release6(void *input);
 static void start_bound(struct client_state *client);
+static void start_informed(struct client_state *client);
+void informed_handler(struct packet *packet, struct client_state *client);
 void bound_handler(struct packet *packet, struct client_state *client);
 void start_renew6(void *input);
 void start_rebind6(void *input);
@@ -92,10 +97,12 @@ static void script_write_params6(struct client_state *client,
                                 const char *prefix,
                                 struct option_state *options);
 
+extern int stateless;
+
 /* The "best" default DUID, since we cannot predict any information
  * about the system (such as whether or not the hardware addresses are
  * integrated into the motherboard or similar), is the "LLT", link local
- * plus time, DUID.
+ * plus time, DUID. For real stateless "LL" is better.
  *
  * Once generated, this duid is stored into the state database, and
  * retained across restarts.
@@ -123,22 +130,31 @@ form_duid(struct data_string *duid, const char *file, int line)
 
        /* 2 bytes for the 'duid type' field.
         * 2 bytes for the 'htype' field.
-        * 4 bytes for the 'current time'.
+        * (not stateless) 4 bytes for the 'current time'.
         * enough bytes for the hardware address (note that hw_address has
         * the 'htype' on byte zero).
         */
-       len = 8 + (ip->hw_address.hlen - 1);
+       len = 4 + (ip->hw_address.hlen - 1);
+       if (!stateless)
+               len += 4;
        if (!buffer_allocate(&duid->buffer, len, MDL))
                log_fatal("no memory for default DUID!");
        duid->data = duid->buffer->data;
        duid->len = len;
 
        /* Basic Link Local Address type of DUID. */
-       putUShort(duid->buffer->data, DUID_LLT);
-       putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]);
-       putULong(duid->buffer->data + 4, cur_time - DUID_TIME_EPOCH);
-       memcpy(duid->buffer->data + 8, ip->hw_address.hbuf + 1,
-              ip->hw_address.hlen - 1);
+       if (!stateless) {
+               putUShort(duid->buffer->data, DUID_LLT);
+               putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]);
+               putULong(duid->buffer->data + 4, cur_time - DUID_TIME_EPOCH);
+               memcpy(duid->buffer->data + 8, ip->hw_address.hbuf + 1,
+                      ip->hw_address.hlen - 1);
+       } else {
+               putUShort(duid->buffer->data, DUID_LL);
+               putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]);
+               memcpy(duid->buffer->data + 4, ip->hw_address.hbuf + 1,
+                      ip->hw_address.hlen - 1);
+       }
 }
 
 /* Assign DHCPv6 port numbers as a client.
@@ -217,6 +233,11 @@ dhcpv6_client_assignments(void)
                                     &code, 0, MDL))
                log_fatal("Unable to find the ORO option definition.");
 
+       code = D6O_INFORMATION_REFRESH_TIME;
+       if (!option_code_hash_lookup(&irt_option, dhcpv6_universe.code_hash,
+                                    &code, 0, MDL))
+               log_fatal("Unable to find the IRT option definition.");
+
 #ifndef __CYGWIN32__ /* XXX */
        endservent();
 #endif
@@ -1253,8 +1274,9 @@ start_init6(struct client_state *client)
        client->MRD = 0;
 
        dhc6_retrans_init(client);
-       /* RFC3315 section 17.1.2 goes out of its way:
-        *
+
+       /*
+         * RFC3315 section 17.1.2 goes out of its way:
         * Also, the first RT MUST be selected to be strictly greater than IRT
         * by choosing RAND to be strictly greater than 0.
         */
@@ -1284,7 +1306,45 @@ start_init6(struct client_state *client)
                go_daemon();
 }
 
-/* start_init6() kicks off an "init-reboot" version of the process, at
+/* start_info_request6() kicks off the process, transmitting an info
+ * request packet and scheduling a retransmission event.
+ */
+void
+start_info_request6(struct client_state *client)
+{
+       struct timeval tv;
+
+       log_debug("PRC: Requesting information (INIT).");
+       client->state = S_INIT;
+
+       /* Initialize timers, RFC3315 section 18.1.5. */
+       client->IRT = INF_TIMEOUT * 100;
+       client->MRT = INF_MAX_RT * 100;
+       client->MRC = 0;
+       client->MRD = 0;
+
+       dhc6_retrans_init(client);
+
+       client->v6_handler = info_request_handler;
+
+       /* RFC3315 section 18.1.5 says we MUST start the first packet
+        * between 0 and INF_MAX_DELAY seconds.  The good news is
+        * INF_MAX_DELAY is 1.
+        */
+       tv.tv_sec = cur_tv.tv_sec;
+       tv.tv_usec = cur_tv.tv_usec;
+       tv.tv_usec += (random() % (INF_MAX_DELAY * 100)) * 10000;
+       if (tv.tv_usec >= 1000000) {
+               tv.tv_sec += 1;
+               tv.tv_usec -= 1000000;
+       }
+       add_timeout(&tv, do_info_request6, client, NULL, NULL);
+
+       if (nowait)
+               go_daemon();
+}
+
+/* start_confirm6() kicks off an "init-reboot" version of the process, at
  * startup to find out if old bindings are 'fair' and at runtime whenever
  * a link cycles state we'll eventually want to do this.
  */
@@ -1529,6 +1589,107 @@ do_init6(void *input)
        dhc6_retrans_advance(client);
 }
 
+/* do_info_request6() marshals and transmits an information-request. */
+void
+do_info_request6(void *input)
+{
+       struct client_state *client;
+       struct data_string ds;
+       struct timeval elapsed, tv;
+       int send_ret;
+
+       client = input;
+
+       if ((client->MRC != 0) && (client->txcount > client->MRC)) {
+               log_info("Max retransmission count exceeded.");
+               return;
+       }
+
+       /*
+        * Start_time starts at the first transmission.
+        */
+       if (client->txcount == 0) {
+               client->start_time.tv_sec = cur_tv.tv_sec;
+               client->start_time.tv_usec = cur_tv.tv_usec;
+       }
+
+       /* elapsed = cur - start */
+       elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec;
+       elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec;
+       if (elapsed.tv_usec < 0) {
+               elapsed.tv_sec -= 1;
+               elapsed.tv_usec += 1000000;
+       }
+       if ((client->MRD != 0) && (elapsed.tv_sec > client->MRD)) {
+               log_info("Max retransmission duration exceeded.");
+               return;
+       }
+
+       memset(&ds, 0, sizeof(ds));
+       if (!buffer_allocate(&ds.buffer, 4, MDL)) {
+               log_error("Unable to allocate memory for INFO-REQUEST.");
+               return;
+       }
+       ds.data = ds.buffer->data;
+       ds.len = 4;
+
+       ds.buffer->data[0] = DHCPV6_INFORMATION_REQUEST;
+       memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3);
+
+       /* Form an elapsed option. */
+       /* Maximum value is 65535 1/100s coded as 0xffff. */
+       if ((elapsed.tv_sec < 0) || (elapsed.tv_sec > 655) ||
+           ((elapsed.tv_sec == 655) && (elapsed.tv_usec > 350000))) {
+               client->elapsed = 0xffff;
+       } else {
+               client->elapsed = elapsed.tv_sec * 100;
+               client->elapsed += elapsed.tv_usec / 10000;
+       }
+
+       if (client->elapsed == 0)
+               log_debug("XMT: Forming Info-Request, 0 ms elapsed.");
+       else
+               log_debug("XMT: Forming Info-Request, %u0 ms elapsed.",
+                         (unsigned)client->elapsed);
+
+       client->elapsed = htons(client->elapsed);
+
+       make_client6_options(client, &client->sent_options, NULL,
+                            DHCPV6_INFORMATION_REQUEST);
+
+       /* Fetch any configured 'sent' options (includes DUID) in wire format.
+        */
+       dhcpv6_universe.encapsulate(&ds, NULL, NULL, client,
+                                   NULL, client->sent_options, &global_scope,
+                                   &dhcpv6_universe);
+
+       /* Transmit and wait. */
+
+       log_info("XMT: Info-Request on %s, interval %ld0ms.",
+                client->name ? client->name : client->interface->name,
+                (long int)client->RT);
+
+       send_ret = send_packet6(client->interface,
+                               ds.data, ds.len, &DHCPv6DestAddr);
+       if (send_ret != ds.len) {
+               log_error("dhc6: send_packet6() sent %d of %d bytes",
+                         send_ret, ds.len);
+       }
+
+       data_string_forget(&ds, MDL);
+
+       /* Wait RT */
+       tv.tv_sec = cur_tv.tv_sec + client->RT / 100;
+       tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000;
+       if (tv.tv_usec >= 1000000) {
+               tv.tv_sec += 1;
+               tv.tv_usec -= 1000000;
+       }
+       add_timeout(&tv, do_info_request6, client, NULL, NULL);
+
+       dhc6_retrans_advance(client);
+}
+
 /* do_confirm6() creates a Confirm packet and transmits it.  This function
  * is called on every timeout to (re)transmit.
  */
@@ -2467,6 +2628,62 @@ init_handler(struct packet *packet, struct client_state *client)
                log_debug("RCV:  Advertisement recorded.");
 }
 
+/* info_request_handler() accepts a Reply to an Info-request.
+ */
+void
+info_request_handler(struct packet *packet, struct client_state *client)
+{
+       isc_result_t check_status;
+       unsigned code;
+
+       if (packet->dhcpv6_msg_type != DHCPV6_REPLY)
+               return;
+
+       /* RFC3315 section 15.10 validation (same as 15.3 since we
+        * always include a client id).
+        */
+       if (!valid_reply(packet, client)) {
+               log_error("Invalid Reply - rejecting.");
+               return;
+       }
+
+       check_status = dhc6_check_status(ISC_R_SUCCESS, packet->options,
+                                        "message", &code);
+       if (check_status != ISC_R_SUCCESS) {
+               /* If no action was taken, but there is an error, then
+                * we wait for a retransmission.
+                */
+               if (check_status != ISC_R_CANCELED)
+                       return;
+       }
+
+       /* We're done retransmitting at this point. */
+       cancel_timeout(do_info_request6, client);
+
+       /* Action was taken, so now that we've torn down our scheduled
+        * retransmissions, return.
+        */
+       if (check_status == ISC_R_CANCELED)
+               return;
+
+       /* Cleanup if a previous attempt to go bound failed. */
+       if (client->old_lease != NULL) {
+               dhc6_lease_destroy(&client->old_lease, MDL);
+               client->old_lease = NULL;
+       }
+
+       /* Cache options in the active_lease. */
+       if (client->active_lease != NULL)
+               client->old_lease = client->active_lease;
+       client->active_lease = dmalloc(sizeof(struct dhc6_lease), MDL);
+       if (client->active_lease == NULL)
+               log_fatal("Out of memory for v6 lease structure.");
+       option_state_reference(&client->active_lease->options,
+                              packet->options, MDL);
+
+       start_informed(client);
+}
+
 /* Specific version of init_handler() for rapid-commit.
  */
 void
@@ -3499,7 +3716,7 @@ dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst)
  * to inform it about the new values, and then lay in wait for the next
  * event.
  */
-void
+static void
 start_bound(struct client_state *client)
 {
        struct dhc6_ia *ia, *oldia;
@@ -3974,6 +4191,7 @@ do_expire(void *input)
  * Run client script to unconfigure interface.
  * Called with reason STOP6 when dhclient -x is run, or with reason
  * RELEASE6 when server has replied to a Release message.
+ * Stateless is a special case.
  */
 void
 unconfigure6(struct client_state *client, const char *reason)
@@ -3981,6 +4199,15 @@ unconfigure6(struct client_state *client, const char *reason)
        struct dhc6_ia *ia;
        struct dhc6_addr *addr;
 
+       if (stateless) {
+               script_init(client, reason, NULL);
+               if (client->active_lease != NULL)
+                       script_write_params6(client, "old_",
+                                            client->active_lease->options);
+               script_go(client);
+               return;
+       }
+
        if (client->active_lease == NULL)
                return;
 
@@ -4000,6 +4227,109 @@ unconfigure6(struct client_state *client, const char *reason)
        }
 }
 
+void
+refresh_info_request6(void *input)
+{
+       struct client_state *client;
+
+       client = (struct client_state *)input;
+       start_info_request6(client);
+}
+
+/* Timeout for Information-Request (using the IRT option).
+ */
+static void
+dhc6_check_irt(struct client_state *client)
+{
+       struct option **req;
+       struct option_cache *oc;
+       TIME expire = MAX_TIME;
+       struct timeval tv;
+       int i;
+       isc_boolean_t found = ISC_FALSE;
+
+       cancel_timeout(refresh_info_request6, client);
+
+       req = client->config->requested_options;
+       for (i = 0; req[i] != NULL; i++) {
+               if (req[i] == irt_option) {
+                       found = ISC_TRUE;
+                       break;
+               }
+       }
+       /* Simply return gives a endless loop waiting for nothing. */
+       if (!found)
+               exit(0);
+
+       oc = lookup_option(&dhcpv6_universe, client->active_lease->options,
+                          D6O_INFORMATION_REFRESH_TIME);
+       if (oc != NULL) {
+               struct data_string irt;
+
+               memset(&irt, 0, sizeof(irt));
+               if (!evaluate_option_cache(&irt, NULL, NULL, client,
+                                          client->active_lease->options,
+                                          NULL, &global_scope, oc, MDL) ||
+                   (irt.len < 4)) {
+                       log_error("Can't evaluate IRT.");
+               } else {
+                       expire = getULong(irt.data);
+                       if (expire < IRT_MINIMUM)
+                               expire = IRT_MINIMUM;
+                       if (expire == 0xffffffff)
+                               expire = MAX_TIME;
+               }
+               data_string_forget(&irt, MDL);
+       } else
+               expire = IRT_DEFAULT;
+
+       if (expire != MAX_TIME) {
+               log_debug("PRC: Refresh event scheduled in %u seconds.",
+                         (unsigned) expire);
+               tv.tv_sec = cur_time + expire;
+               tv.tv_usec = 0;
+               add_timeout(&tv, refresh_info_request6, client, NULL, NULL);
+       }
+}
+
+/* We got a Reply. Give dhclient-script a tickle to inform it about
+ * the new values, and then lay in wait for the next event.
+ */
+static void
+start_informed(struct client_state *client)
+{
+       client->v6_handler = informed_handler;
+
+       log_debug("PRC: Done.");
+
+       client->state = S_BOUND;
+
+       script_init(client, "RENEW6", NULL);
+       if (client->old_lease != NULL)
+               script_write_params6(client, "old_",
+                                    client->old_lease->options);
+       script_write_params6(client, "new_", client->active_lease->options);
+       script_go(client);
+
+       go_daemon();
+
+       if (client->old_lease != NULL) {
+               dhc6_lease_destroy(&client->old_lease, MDL);
+               client->old_lease = NULL;
+       }
+
+       /* Schedule events. */
+       dhc6_check_irt(client);
+}
+
+/* While informed, ignore packets.
+ */
+void
+informed_handler(struct packet *packet, struct client_state *client)
+{
+       log_debug("RCV: Input packets are ignored once bound.");
+}
+
 /* make_client6_options() fetches option caches relevant to the client's
  * scope and places them into the sent_options cache.  This cache is later
  * used to populate DHCPv6 output packets with options.
index 1fb1da83c028b8b3abcad255d54bca834d8f59fd..d4bc2c907f9490aad884e7c71ca304150163aef3 100644 (file)
@@ -75,13 +75,16 @@ int client_env_count=0;
 int onetry=0;
 int quiet=1;
 int nowait=0;
+int stateless=0;
 char *mockup_relay = NULL;
 
+void run_stateless(int exit_mode);
+
 static void usage PROTO ((void));
 
 static isc_result_t write_duid(struct data_string *duid);
 
-int 
+int
 main(int argc, char **argv) {
        int fd;
        int i;
@@ -90,8 +93,8 @@ main(int argc, char **argv) {
        unsigned seed;
        char *server = (char *)0;
        isc_result_t status;
-       int exit_mode = 0;
-       int release_mode = 0;
+       int exit_mode = 0;
+       int release_mode = 0;
        struct timeval tv;
        omapi_object_t *listener;
        isc_result_t result;
@@ -108,24 +111,24 @@ main(int argc, char **argv) {
        /* Initialize client globals. */
        memset(&default_duid, 0, sizeof(default_duid));
 
-        /* Make sure that file descriptors 0 (stdin), 1, (stdout), and
-           2 (stderr) are open. To do this, we assume that when we
-           open a file the lowest available file descriptor is used. */
-        fd = open("/dev/null", O_RDWR);
-        if (fd == 0)
-                fd = open("/dev/null", O_RDWR);
-        if (fd == 1)
-                fd = open("/dev/null", O_RDWR);
-        if (fd == 2)
-                log_perror = 0; /* No sense logging to /dev/null. */
-        else if (fd != -1)
-                close(fd);
+       /* Make sure that file descriptors 0 (stdin), 1, (stdout), and
+          2 (stderr) are open. To do this, we assume that when we
+          open a file the lowest available file descriptor is used. */
+       fd = open("/dev/null", O_RDWR);
+       if (fd == 0)
+               fd = open("/dev/null", O_RDWR);
+       if (fd == 1)
+               fd = open("/dev/null", O_RDWR);
+       if (fd == 2)
+               log_perror = 0; /* No sense logging to /dev/null. */
+       else if (fd != -1)
+               close(fd);
 
        openlog ("dhclient", LOG_NDELAY, LOG_DAEMON);
 
 #if !(defined (DEBUG) || defined (__CYGWIN32__))
        setlogmask (LOG_UPTO (LOG_INFO));
-#endif 
+#endif
 
        /* Set up the OMAPI. */
        status = omapi_init ();
@@ -142,7 +145,7 @@ main(int argc, char **argv) {
        dhcp_interface_startup_hook = dhclient_interface_startup_hook;
 
        for (i = 1; i < argc; i++) {
-                if (!strcmp(argv[i], "-r")) {
+               if (!strcmp(argv[i], "-r")) {
                        release_mode = 1;
                        no_daemon = 1;
 #ifdef DHCPv6
@@ -159,10 +162,10 @@ main(int argc, char **argv) {
                        local_family_set = 1;
                        local_family = AF_INET6;
 #endif /* DHCPv6 */
-                } else if (!strcmp (argv [i], "-x")) { /* eXit, no release */
-                        release_mode = 0;
-                        no_daemon = 0;
-                        exit_mode = 1;
+               } else if (!strcmp (argv [i], "-x")) { /* eXit, no release */
+                       release_mode = 0;
+                       no_daemon = 0;
+                       exit_mode = 1;
                } else if (!strcmp (argv [i], "-p")) {
                        if (++i == argc)
                                usage ();
@@ -172,25 +175,25 @@ main(int argc, char **argv) {
                } else if (!strcmp (argv [i], "-d")) {
                        no_daemon = 1;
                        quiet = 0;
-                } else if (!strcmp (argv [i], "-pf")) {
-                        if (++i == argc)
-                                usage ();
-                        path_dhclient_pid = argv [i];
+               } else if (!strcmp (argv [i], "-pf")) {
+                       if (++i == argc)
+                               usage ();
+                       path_dhclient_pid = argv [i];
                        no_dhclient_pid = 1;
-                } else if (!strcmp (argv [i], "-cf")) {
-                        if (++i == argc)
-                                usage ();
-                        path_dhclient_conf = argv [i];
+               } else if (!strcmp (argv [i], "-cf")) {
+                       if (++i == argc)
+                               usage ();
+                       path_dhclient_conf = argv [i];
                        no_dhclient_conf = 1;
-                } else if (!strcmp (argv [i], "-lf")) {
-                        if (++i == argc)
-                                usage ();
-                        path_dhclient_db = argv [i];
+               } else if (!strcmp (argv [i], "-lf")) {
+                       if (++i == argc)
+                               usage ();
+                       path_dhclient_db = argv [i];
                        no_dhclient_db = 1;
                } else if (!strcmp (argv [i], "-sf")) {
                        if (++i == argc)
                                usage ();
-                        path_dhclient_script = argv [i];
+                       path_dhclient_script = argv [i];
                        no_dhclient_script = 1;
                } else if (!strcmp (argv [i], "-1")) {
                        onetry = 1;
@@ -208,7 +211,7 @@ main(int argc, char **argv) {
                        nowait = 1;
                } else if (!strcmp (argv [i], "-n")) {
                        /* do not start up any interfaces */
-                       interfaces_requested = 1;
+                       interfaces_requested = -1;
                } else if (!strcmp (argv [i], "-w")) {
                        /* do not exit if there are no broadcast interfaces. */
                        persist = 1;
@@ -223,18 +226,27 @@ main(int argc, char **argv) {
                        tmp -> next = client_env;
                        client_env = tmp;
                        client_env_count++;
+               } else if (!strcmp(argv[i], "-S")) {
+                       if (local_family_set && (local_family == AF_INET)) {
+                               usage ();
+                       }
+                       local_family_set = 1;
+                       local_family = AF_INET6;
+                       stateless = 1;
                } else if (!strcmp(argv[i], "-v")) {
                        quiet = 0;
                } else if (!strcmp (argv [i], "--version")) {
                        log_info ("isc-dhclient-%s", PACKAGE_VERSION);
                        exit (0);
-               } else if (argv [i][0] == '-') {
-                   usage ();
+               } else if (argv [i][0] == '-') {
+                   usage ();
+               } else if (interfaces_requested < 0) {
+                   usage ();
                } else {
-                   struct interface_info *tmp = (struct interface_info *)0;
+                   struct interface_info *tmp = (struct interface_info *)0;
                    status = interface_allocate (&tmp, MDL);
-                   if (status != ISC_R_SUCCESS)
-                       log_fatal ("Can't record interface %s:%s",
+                   if (status != ISC_R_SUCCESS)
+                       log_fatal ("Can't record interface %s:%s",
                                   argv [i], isc_result_totext (status));
                    if (strlen(argv[i]) >= sizeof(tmp->name))
                            log_fatal("%s: interface name too long (is %ld)",
@@ -246,9 +258,9 @@ main(int argc, char **argv) {
                            interface_dereference (&interfaces, MDL);
                    }
                    interface_reference (&interfaces, tmp, MDL);
-                   tmp -> flags = INTERFACE_REQUESTED;
-                   interfaces_requested = 1;
-               }
+                   tmp -> flags = INTERFACE_REQUESTED;
+                   interfaces_requested++;
+               }
        }
 
        if (!no_dhclient_conf && (s = getenv ("PATH_DHCLIENT_CONF"))) {
@@ -289,7 +301,7 @@ main(int argc, char **argv) {
                if (path_dhclient_db == NULL)
                        log_fatal("%s: %s", path, strerror(errno));
        }
+
        if (path_dhclient_script[0] != '/') {
                char *path = dmalloc(PATH_MAX, MDL);
                if (path == NULL)
@@ -298,7 +310,7 @@ main(int argc, char **argv) {
                if (path_dhclient_script == NULL)
                        log_fatal("%s: %s", path, strerror(errno));
        }
+
        /* first kill off any currently running client */
        if (release_mode || exit_mode) {
                FILE *pidfd;
@@ -368,6 +380,15 @@ main(int argc, char **argv) {
 
        inaddr_any.s_addr = INADDR_ANY;
 
+       /* Stateless special case. */
+       if (stateless) {
+               if (release_mode || (interfaces_requested != 1)) {
+                       usage ();
+               }
+               run_stateless (exit_mode);
+               return 0;
+       }
+
        /* Discover all the network interfaces. */
        discover_interfaces (DISCOVER_UNCONFIGURED);
 
@@ -404,7 +425,7 @@ main(int argc, char **argv) {
                for (ip = interfaces; ip; ip = ip -> next) {
                        /* If interfaces were specified, don't configure
                           interfaces that weren't specified! */
-                       if (interfaces_requested &&
+                       if ((interfaces_requested > 0) &&
                            ((ip -> flags & (INTERFACE_REQUESTED |
                                             INTERFACE_AUTOMATIC)) !=
                             INTERFACE_REQUESTED))
@@ -427,7 +448,7 @@ main(int argc, char **argv) {
           are relevant should be running, so now we once again call
           discover_interfaces(), and this time ask it to actually set
           up the interfaces. */
-       discover_interfaces (interfaces_requested
+       discover_interfaces (interfaces_requested != 0
                             ? DISCOVER_REQUESTED
                             : DISCOVER_RUNNING);
 
@@ -463,13 +484,13 @@ main(int argc, char **argv) {
                for (ip = interfaces ; ip != NULL ; ip = ip->next) {
                        for (client = ip->client ; client != NULL ;
                             client = client->next) {
-                                if (release_mode) {
-                                        start_release6(client);
-                                        continue;
-                                } else if (exit_mode) {
-                                        unconfigure6(client, "STOP6");
-                                        continue;
-                                }
+                               if (release_mode) {
+                                       start_release6(client);
+                                       continue;
+                               } else if (exit_mode) {
+                                       unconfigure6(client, "STOP6");
+                                       continue;
+                               }
 
                                /* If we have a previous binding, Confirm
                                 * that we can (or can't) still use it.
@@ -481,16 +502,16 @@ main(int argc, char **argv) {
                                        start_init6(client);
                        }
                }
-       } else 
+       } else
 #endif /* DHCPv6 */
        {
                for (ip = interfaces ; ip ; ip = ip->next) {
                        ip->flags |= INTERFACE_RUNNING;
                        for (client = ip->client ; client ;
                             client = client->next) {
-                                if (exit_mode)
-                                        state_stop(client);
-                                else if (release_mode)
+                               if (exit_mode)
+                                       state_stop(client);
+                               else if (release_mode)
                                        do_release(client);
                                else {
                                        client->state = S_INIT;
@@ -575,9 +596,9 @@ static void usage ()
 
        log_error ("Usage: dhclient %s %s",
 #ifdef DHCPv6
-                   "[-4|-6] [-1dvrx] [-nw] [-p <port>]",
+                  "[-4|-6] [-S1dvrx] [-nw] [-p <port>]",
 #else /* DHCPv6 */
-                   "[-1dvrx] [-nw] [-p <port>]",
+                  "[-1dvrx] [-nw] [-p <port>]",
 #endif /* DHCPv6 */
                   "[-s server]");
        log_error ("                [-cf config-file] [-lf lease-file]%s",
@@ -585,6 +606,88 @@ static void usage ()
        log_fatal ("                [-sf script-file] [interface]");
 }
 
+void run_stateless(int exit_mode)
+{
+       struct client_state *client;
+       omapi_object_t *listener;
+       isc_result_t result;
+
+       /* Discover the network interface. */
+       discover_interfaces(DISCOVER_REQUESTED);
+
+       if (!interfaces)
+               usage();
+
+       /* Parse the dhclient.conf file. */
+       read_client_conf();
+
+       /* Parse the lease database. */
+       read_client_leases();
+
+       /* Establish a default DUID. */
+       if (default_duid.len == 0) {
+               if (default_duid.buffer != NULL)
+                       data_string_forget(&default_duid, MDL);
+
+               form_duid(&default_duid, MDL);
+       }
+
+       /* Start a configuration state machine. */
+       for (client = interfaces->client ;
+            client != NULL ;
+            client = client->next) {
+               if (exit_mode) {
+                       unconfigure6(client, "STOP6");
+                       continue;
+               }
+               start_info_request6(client);
+       }
+       if (exit_mode)
+               return;
+
+       /* Start up a listener for the object management API protocol. */
+       if (top_level_config.omapi_port != -1) {
+               listener = (omapi_object_t *)0;
+               result = omapi_generic_new(&listener, MDL);
+               if (result != ISC_R_SUCCESS)
+                       log_fatal("Can't allocate new generic object: %s\n",
+                                 isc_result_totext(result));
+               result = omapi_protocol_listen(listener,
+                                              (unsigned)
+                                              top_level_config.omapi_port,
+                                              1);
+               if (result != ISC_R_SUCCESS)
+                       log_fatal("Can't start OMAPI protocol: %s",
+                                 isc_result_totext(result));
+       }
+
+       /* Set up the packet handler... */
+       dhcpv6_packet_handler = do_packet6;
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
+               defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+       dmalloc_cutoff_generation = dmalloc_generation;
+       dmalloc_longterm = dmalloc_outstanding;
+       dmalloc_outstanding = 0;
+#endif
+
+       /* If we're not supposed to wait before getting the address,
+          don't. */
+       if (nowait)
+               go_daemon();
+
+       /* If we're not going to daemonize, write the pid file
+          now. */
+       if (no_daemon || nowait)
+               write_client_pid_file();
+
+       /* Start dispatching packets and timeouts... */
+       dispatch();
+
+       /*NOTREACHED*/
+       return;
+}
+
 isc_result_t find_class (struct class **c,
                const char *s, const char *file, int line)
 {
@@ -619,7 +722,7 @@ int find_subnet (struct subnet **sp,
 }
 
 /* Individual States:
- * 
+ *
  * Each routine is called from the dhclient_state_machine() in one of
  * these conditions:
  * -> entering INIT state
@@ -781,7 +884,7 @@ void state_selecting (cpp)
 
        /* Add an immediate timeout to send the first DHCPREQUEST packet. */
        send_request (client);
-}  
+}
 
 /* state_requesting is called when we receive a DHCPACK message after
    having sent out one or more DHCPREQUEST packets. */
@@ -794,7 +897,7 @@ void dhcpack (packet)
        struct client_lease *lease;
        struct option_cache *oc;
        struct data_string ds;
-       
+
        /* If we're not receptive to an offer right now, or if the offer
           has an unrecognizable transaction id, then just drop it. */
        for (client = ip -> client; client; client = client -> next) {
@@ -854,7 +957,7 @@ void dhcpack (packet)
        if (!client -> new -> expiry) {
                log_error ("no expiry time on offered lease.");
                /* XXX this is going to be bad - if this _does_
-                  XXX happen, we should probably dynamically 
+                  XXX happen, we should probably dynamically
                   XXX disqualify the DHCP server that gave us the
                   XXX bad packet from future selections and
                   XXX then go back into the init state. */
@@ -999,7 +1102,7 @@ void bind_lease (client)
        if (client->config->do_forward_update)
                dhclient_schedule_updates(client, &client->active->address,
                                          1);
-}  
+}
 
 /* state_bound is called when we've successfully bound to a particular
    lease, but the renewal time on that lease has expired.   We are
@@ -1043,7 +1146,7 @@ void state_bound (cpp)
 
        /* Send the first packet immediately. */
        send_request (client);
-}  
+}
 
 /* state_stop is called when we've been told to shut down.   We unconfigure
    the interfaces, and then stop operating until told otherwise. */
@@ -1067,7 +1170,7 @@ void state_stop (cpp)
                        script_write_params(client, "alias_", client->alias);
                script_go(client);
        }
-}  
+}
 
 int commit_leases ()
 {
@@ -1119,7 +1222,7 @@ void bootp (packet)
                        return;
                }
        }
-       
+
        dhcpoffer (packet);
 
 }
@@ -1175,7 +1278,7 @@ void dhcp (packet)
 }
 
 #ifdef DHCPv6
-void 
+void
 dhcpv6(struct packet *packet) {
        struct iaddrmatchlist *ap;
        struct client_state *client;
@@ -1201,8 +1304,11 @@ dhcpv6(struct packet *packet) {
        /* Screen out nonsensical messages. */
        switch(packet->dhcpv6_msg_type) {
              case DHCPV6_ADVERTISE:
-             case DHCPV6_REPLY:
              case DHCPV6_RECONFIGURE:
+               if (stateless)
+                 return;
+             /* Falls through */
+             case DHCPV6_REPLY:
                log_info("RCV: %s message on %s from %s.",
                         dhcpv6_type_names[packet->dhcpv6_msg_type],
                         packet->interface->name, piaddr(packet->client_addr));
@@ -1239,10 +1345,10 @@ void dhcpoffer (packet)
        const char *name = packet -> packet_type ? "DHCPOFFER" : "BOOTREPLY";
        char obuf [1024];
        struct timeval tv;
-       
+
 #ifdef DEBUG_PACKET
        dump_packet (packet);
-#endif 
+#endif
 
        /* Find a client state that matches the xid... */
        for (client = ip -> client; client; client = client -> next)
@@ -1482,7 +1588,7 @@ struct client_lease *packet_to_lease (packet, client)
                                     (struct group *)0);
 
        return lease;
-}      
+}
 
 void dhcpnak (packet)
        struct packet *packet;
@@ -1592,7 +1698,7 @@ void send_discover (cpp)
                if (client -> medium) {
                        client -> medium = client -> medium -> next;
                        increase = 0;
-               } 
+               }
                if (!client -> medium) {
                        if (fail)
                                log_fatal ("No valid media types for %s!",
@@ -1601,7 +1707,7 @@ void send_discover (cpp)
                                client -> config -> media;
                        increase = 1;
                }
-                       
+
                log_info ("Trying medium \"%s\" %d",
                          client -> medium -> string, increase);
                script_init (client, "MEDIUM", client -> medium);
@@ -1628,7 +1734,7 @@ void send_discover (cpp)
                                 + (random() % client->config->backoff_cutoff);
        } else if (!client->interval)
                client->interval = client->config->initial_interval;
-               
+
        /* If the backoff would take us to the panic timeout, just use that
           as the interval. */
        if (cur_time + client -> interval >
@@ -1853,7 +1959,7 @@ void send_request (cpp)
                client -> interval += ((random () >> 2) %
                                       (2 * client -> interval));
        }
-       
+
        /* Don't backoff past cutoff. */
        if (client -> interval >
            client -> config -> backoff_cutoff)
@@ -2191,7 +2297,7 @@ void make_request (client, lease)
                              (struct lease *)0, client,
                              /* maximum packet size */1500,
                              (struct option_state *)0,
-                             client -> sent_options, 
+                             client -> sent_options,
                              /* scope */ &global_scope,
                              /* overload */ 0,
                              /* terminate */0,
@@ -2467,7 +2573,7 @@ write_options(struct client_state *client, struct option_state *options,
 
        for (i = 0; i < options->universe_count; i++) {
                option_space_foreach(NULL, NULL, client, NULL, options,
-                                    &global_scope, universes[i], 
+                                    &global_scope, universes[i],
                                     (char *)preamble, write_lease_option);
        }
 }
@@ -2794,7 +2900,7 @@ void script_init (client, reason, medium)
                }
                client -> env = (struct string_list *)0;
                client -> envc = 0;
-               
+
                if (client -> interface) {
                        client_envadd (client, "", "interface", "%s",
                                       client -> interface -> name);
@@ -3048,7 +3154,7 @@ int dhcp_option_ev_name (buf, buflen, option)
        if (option -> universe != &dhcp_universe) {
                s = option -> universe -> name;
                i = 0;
-       } else { 
+       } else {
                s = option -> name;
                i = 1;
        }
@@ -3104,9 +3210,9 @@ void go_daemon ()
        pid = setsid ();
 
        /* Close standard I/O descriptors. */
-        close(0);
-        close(1);
-        close(2);
+       close(0);
+       close(1);
+       close(2);
 
        /* Reopen them on /dev/null. */
        open("/dev/null", O_RDWR);
@@ -3115,7 +3221,7 @@ void go_daemon ()
 
        write_client_pid_file ();
 
-        chdir("/");
+       chdir("/");
 }
 
 void write_client_pid_file ()
@@ -3172,7 +3278,7 @@ void client_location_changed ()
        }
 }
 
-void do_release(client) 
+void do_release(client)
        struct client_state *client;
 {
        struct data_string ds;
@@ -3209,10 +3315,10 @@ void do_release(client)
                        client -> destination = iaddr_broadcast;
                client -> first_sending = cur_time;
                client -> interval = client -> config -> initial_interval;
-       
+
                /* Zap the medium list... */
                client -> medium = (struct string_list *)0;
-       
+
                /* Send out the first and only DHCPRELEASE packet. */
                send_release (client);
 
@@ -3318,8 +3424,8 @@ isc_result_t dhclient_interface_startup_hook (struct interface_info *interface)
                                             ip -> client -> alias);
                script_go (ip -> client);
        }
-       
-       discover_interfaces (interfaces_requested
+
+       discover_interfaces (interfaces_requested != 0
                             ? DISCOVER_REQUESTED
                             : DISCOVER_RUNNING);
 
@@ -3501,7 +3607,7 @@ isc_result_t client_dns_update (struct client_state *client, int addp,
                                           (struct option_state *)0,
                                           &global_scope, oc, MDL))
                return ISC_R_SUCCESS;
-       
+
        /* If we set the "server, please update" flag, or didn't set it
           to false, don't do the update. */
        if (!(oc = lookup_option (&fqdn_universe, client -> sent_options,
@@ -3512,12 +3618,12 @@ isc_result_t client_dns_update (struct client_state *client, int addp,
                                           (struct option_state *)0,
                                           &global_scope, oc, MDL))
                return ISC_R_SUCCESS;
-       
+
        /* If no FQDN option was supplied, don't do the update. */
        memset (&ddns_fwd_name, 0, sizeof ddns_fwd_name);
        if (!(oc = lookup_option (&fqdn_universe, client -> sent_options,
                                  FQDN_FQDN)) ||
-           !evaluate_option_cache (&ddns_fwd_name, (struct packet *)0, 
+           !evaluate_option_cache (&ddns_fwd_name, (struct packet *)0,
                                    (struct lease *)0, client,
                                    client -> sent_options,
                                    (struct option_state *)0,
@@ -3592,7 +3698,7 @@ isc_result_t client_dns_update (struct client_state *client, int addp,
                                                &ddns_dhcid);
        } else
                rcode = ISC_R_FAILURE;
-       
+
        data_string_forget (&ddns_fwd_name, MDL);
        data_string_forget (&ddns_dhcid, MDL);
        return rcode;
index 4b526b05073d8baedb18812319efb7c3da6bab4f..7cf7f3aa540beb1bb3e3622c1dab758cb000234e 100644 (file)
@@ -205,3 +205,9 @@ struct dhcpv6_relay_packet {
  * when the Unix epoch began.
  */
 #define DUID_TIME_EPOCH 946684800
+
+/* Information-Request Time option (RFC 4242) */
+
+#define IRT_DEFAULT    86400
+#define IRT_MINIMUM    600
+
index 85046d047b2d3b0910ba0bb950752655635ce065..7a0741c753d24f863977d30fc2b7d4d55c81b752 100644 (file)
@@ -2525,6 +2525,7 @@ void dhcpv6_client_assignments(void);
 void form_duid(struct data_string *duid, const char *file, int line);
 void dhc6_lease_destroy(struct dhc6_lease **src, const char *file, int line);
 void start_init6(struct client_state *client);
+void start_info_request6(struct client_state *client);
 void start_confirm6(struct client_state *client);
 void start_release6(struct client_state *client);
 void start_selecting6(struct client_state *client);