udhcpc6: support stateless DHCPv6
authorEivind Versvik <versvikeivind@gmail.com>
Sat, 24 Aug 2019 15:23:48 +0000 (17:23 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sat, 24 Aug 2019 15:24:40 +0000 (17:24 +0200)
-l will send Information-Request to request configuration parameters

function                                             old     new   delta
packed_usage                                       33114   33180     +66
send_d6_info_request                                   -      62     +62
udhcpc6_main                                        2534    2593     +59
udhcpc6_longopts                                     199     211     +12
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/0 up/down: 199/0)             Total: 199 bytes

Signed-off-by: Eivind Versvik <versvikeivind@gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
networking/udhcp/d6_dhcpc.c

index 9d8e17c51a71c25e226e1db8ae00b8c276e9a285..a426b9933e2d988a9335e66299fbca120d8bd2ae 100644 (file)
@@ -123,6 +123,7 @@ static const char udhcpc6_longopts[] ALIGN1 =
        "request-option\0" Required_argument "O"
        "no-default-options\0" No_argument   "o"
        "foreground\0"     No_argument       "f"
+       "stateless\0"      No_argument       "l"
        USE_FOR_MMU(
        "background\0"     No_argument       "b"
        )
@@ -147,9 +148,10 @@ enum {
        OPT_o = 1 << 12,
        OPT_x = 1 << 13,
        OPT_f = 1 << 14,
-       OPT_d = 1 << 15,
+       OPT_l = 1 << 15,
+       OPT_d = 1 << 16,
 /* The rest has variable bit positions, need to be clever */
-       OPTBIT_d = 15,
+       OPTBIT_d = 16,
        USE_FOR_MMU(             OPTBIT_b,)
        ///IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
        IF_FEATURE_UDHCP_PORT(   OPTBIT_P,)
@@ -544,6 +546,46 @@ static int d6_mcast_from_client_data_ifindex(struct d6_packet *packet, uint8_t *
        );
 }
 
+/* RFC 3315 18.1.5. Creation and Transmission of Information-request Messages
+ *
+ * The client uses an Information-request message to obtain
+ * configuration information without having addresses assigned to it.
+ *
+ * The client sets the "msg-type" field to INFORMATION-REQUEST.  The
+ * client generates a transaction ID and inserts this value in the
+ * "transaction-id" field.
+ *
+ * The client SHOULD include a Client Identifier option to identify
+ * itself to the server.  If the client does not include a Client
+ * Identifier option, the server will not be able to return any client-
+ * specific options to the client, or the server may choose not to
+ * respond to the message at all.  The client MUST include a Client
+ * Identifier option if the Information-Request message will be
+ * authenticated.
+ *
+ * The client MUST include an Option Request option (see section 22.7)
+ * to indicate the options the client is interested in receiving.  The
+ * client MAY include options with data values as hints to the server
+ * about parameter values the client would like to have returned.
+ */
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE int send_d6_info_request(uint32_t xid)
+{
+       struct d6_packet packet;
+       uint8_t *opt_ptr;
+
+       /* Fill in: msg type, client id */
+       opt_ptr = init_d6_packet(&packet, D6_MSG_INFORMATION_REQUEST, xid);
+
+       /* Add options:
+        * "param req" option according to -O, options specified with -x
+        */
+       opt_ptr = add_d6_client_options(opt_ptr);
+
+       bb_error_msg("sending %s", "info request");
+       return d6_mcast_from_client_data_ifindex(&packet, opt_ptr);
+}
+
 /* Milticast a DHCPv6 Solicit packet to the network, with an optionally requested IP.
  *
  * RFC 3315 17.1.1. Creation of Solicit Messages
@@ -1129,6 +1171,8 @@ static void client_background(void)
 //usage:     "\n       -o              Don't request any options (unless -O is given)"
 //usage:     "\n       -r IPv6         Request this address ('no' to not request any IP)"
 //usage:     "\n       -d              Request prefix"
+//usage:     "\n       -l              Send 'information request' instead of 'solicit'"
+//usage:     "\n                       (used for servers which do not assign IPv6 addresses)"
 //usage:     "\n       -x OPT:VAL      Include option OPT in sent packets (cumulative)"
 //usage:     "\n                       Examples of string, numeric, and hex byte opts:"
 //usage:     "\n                       -x hostname:bbox - option 12"
@@ -1181,7 +1225,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
        /* Parse command line */
        opt = getopt32long(argv, "^"
                /* O,x: list; -T,-t,-A take numeric param */
-               "i:np:qRr:s:T:+t:+SA:+O:*ox:*fd"
+               "i:np:qRr:s:T:+t:+SA:+O:*ox:*fld"
                USE_FOR_MMU("b")
                ///IF_FEATURE_UDHCPC_ARPING("a")
                IF_FEATURE_UDHCP_PORT("P:")
@@ -1198,15 +1242,20 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
        );
        requested_ipv6 = NULL;
        option_mask32 |= OPT_r;
-       if (opt & OPT_r) {
+       if (opt & OPT_l) {
+               /* for -l, do not require IPv6 assignment from server */
+               option_mask32 &= ~OPT_r;
+       } else if (opt & OPT_r) {
+               /* explicit "-r ARG" given */
                if (strcmp(str_r, "no") == 0) {
-                       option_mask32 -= OPT_r;
+                       option_mask32 &= ~OPT_r;
                } else {
                        if (inet_pton(AF_INET6, str_r, &ipv6_buf) <= 0)
                                bb_error_msg_and_die("bad IPv6 address '%s'", str_r);
                        requested_ipv6 = &ipv6_buf;
                }
        }
+
 #if ENABLE_FEATURE_UDHCP_PORT
        if (opt & OPT_P) {
                CLIENT_PORT6 = xatou16(str_P);
@@ -1353,7 +1402,10 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
                                        if (packet_num == 0)
                                                xid = random_xid();
                                        /* multicast */
-                                       send_d6_discover(xid, requested_ipv6);
+                                       if (opt & OPT_l)
+                                               send_d6_info_request(xid);
+                                       else
+                                               send_d6_discover(xid, requested_ipv6);
                                        timeout = discover_timeout;
                                        packet_num++;
                                        continue;
@@ -1418,7 +1470,10 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
                         * Anyway, it does recover by eventually failing through
                         * into INIT_SELECTING state.
                         */
-                                       send_d6_renew(xid, &srv6_buf, requested_ipv6);
+                                       if (opt & OPT_l)
+                                               send_d6_info_request(xid);
+                                       else
+                                               send_d6_renew(xid, &srv6_buf, requested_ipv6);
                                        timeout >>= 1;
                                        continue;
                                }
@@ -1432,8 +1487,10 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
                                /* Lease is *really* about to run out,
                                 * try to find DHCP server using broadcast */
                                if (timeout > 0) {
-                                       /* send a broadcast renew request */
-                                       send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6);
+                                       if (opt & OPT_l)
+                                               send_d6_info_request(xid);
+                                       else /* send a broadcast renew request */
+                                               send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6);
                                        timeout >>= 1;
                                        continue;
                                }
@@ -1740,6 +1797,12 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
                                        prefix_timeout = address_timeout;
                                /* note: "int timeout" will not overflow even with 0xffffffff inputs here: */
                                timeout = (prefix_timeout < address_timeout ? prefix_timeout : address_timeout) / 2;
+                               if (opt & OPT_l) {
+                                       /* TODO: request OPTION_INFORMATION_REFRESH_TIME (32)
+                                        * and use its value instead of the default 1 day.
+                                        */
+                                       timeout = 24 * 60 * 60;
+                               }
                                /* paranoia: must not be too small */
                                /* timeout > 60 - ensures at least one unicast renew attempt */
                                if (timeout < 61)