udhcpc6: add support of prefix delegation
authorDenys Vlasenko <vda.linux@googlemail.com>
Tue, 16 Jan 2018 20:39:14 +0000 (21:39 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Tue, 16 Jan 2018 20:39:14 +0000 (21:39 +0100)
Based on patch by DannyAAM <danny@saru.moe>

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
networking/udhcp/d6_common.h
networking/udhcp/d6_dhcpc.c

index 310550371713bb4c7712584d6cd7a4e40cd134d6..e9c0397ae474ac05633071bb24ed650011881abd 100644 (file)
@@ -133,6 +133,7 @@ struct d6_option {
 struct client6_data_t {
        struct d6_option *server_id;
        struct d6_option *ia_na;
+       struct d6_option *ia_pd;
        char **env_ptr;
        unsigned env_idx;
        /* link-local IPv6 address */
index 57ad968cdc44be3879393999e40952f8a6dfa508..cfb2dcc5af599db78ea9401d8b2b3dad0c24bff6 100644 (file)
@@ -96,6 +96,7 @@ static const char udhcpc6_longopts[] ALIGN1 =
        "quit\0"           No_argument       "q"
        "release\0"        No_argument       "R"
        "request\0"        Required_argument "r"
+       "requestprefix\0"  No_argument       "d"
        "script\0"         Required_argument "s"
        "timeout\0"        Required_argument "T"
        "retries\0"        Required_argument "t"
@@ -128,8 +129,9 @@ enum {
        OPT_o = 1 << 12,
        OPT_x = 1 << 13,
        OPT_f = 1 << 14,
+       OPT_d = 1 << 15,
 /* The rest has variable bit positions, need to be clever */
-       OPTBIT_f = 14,
+       OPTBIT_d = 15,
        USE_FOR_MMU(             OPTBIT_b,)
        ///IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
        IF_FEATURE_UDHCP_PORT(   OPTBIT_P,)
@@ -561,18 +563,33 @@ static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ip
 
        /* Create new IA_NA, optionally with included IAADDR with requested IP */
        free(client6_data.ia_na);
-       len = requested_ipv6 ? 2+2+4+4+4 + 2+2+16+4+4 : 2+2+4+4+4;
-       client6_data.ia_na = xzalloc(len);
-       client6_data.ia_na->code = D6_OPT_IA_NA;
-       client6_data.ia_na->len = len - 4;
-       *(uint32_t*)client6_data.ia_na->data = rand(); /* IAID */
-       if (requested_ipv6) {
-               struct d6_option *iaaddr = (void*)(client6_data.ia_na->data + 4+4+4);
-               iaaddr->code = D6_OPT_IAADDR;
-               iaaddr->len = 16+4+4;
-               memcpy(iaaddr->data, requested_ipv6, 16);
+       client6_data.ia_na = NULL;
+       if (option_mask32 & OPT_r) {
+               len = requested_ipv6 ? 2+2+4+4+4 + 2+2+16+4+4 : 2+2+4+4+4;
+               client6_data.ia_na = xzalloc(len);
+               client6_data.ia_na->code = D6_OPT_IA_NA;
+               client6_data.ia_na->len = len - 4;
+               *(uint32_t*)client6_data.ia_na->data = rand(); /* IAID */
+               if (requested_ipv6) {
+                       struct d6_option *iaaddr = (void*)(client6_data.ia_na->data + 4+4+4);
+                       iaaddr->code = D6_OPT_IAADDR;
+                       iaaddr->len = 16+4+4;
+                       memcpy(iaaddr->data, requested_ipv6, 16);
+               }
+               opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, len);
+       }
+
+       /* IA_PD */
+       free(client6_data.ia_pd);
+       client6_data.ia_pd = NULL;
+       if (option_mask32 & OPT_d) {
+               len = 2+2+4+4+4;
+               client6_data.ia_pd = xzalloc(len);
+               client6_data.ia_pd->code = D6_OPT_IA_PD;
+               client6_data.ia_pd->len = len - 4;
+               *(uint32_t*)client6_data.ia_pd->data = rand(); /* IAID */
+               opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, len);
        }
-       opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, len);
 
        /* Add options:
         * "param req" option according to -O, options specified with -x
@@ -625,7 +642,11 @@ static NOINLINE int send_d6_select(uint32_t xid)
        /* server id */
        opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
        /* IA NA (contains requested IP) */
-       opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
+       if (client6_data.ia_na)
+               opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
+       /* IA PD */
+       if (client6_data.ia_pd)
+               opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
 
        /* Add options:
         * "param req" option according to -O, options specified with -x
@@ -694,7 +715,11 @@ static NOINLINE int send_d6_renew(uint32_t xid, struct in6_addr *server_ipv6, st
        /* server id */
        opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
        /* IA NA (contains requested IP) */
-       opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
+       if (client6_data.ia_na)
+               opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
+       /* IA PD */
+       if (client6_data.ia_pd)
+               opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
 
        /* Add options:
         * "param req" option according to -O, options specified with -x
@@ -725,7 +750,11 @@ static int send_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cu
        /* server id */
        opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
        /* IA NA (contains our current IP) */
-       opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
+       if (client6_data.ia_na)
+               opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
+       /* IA PD */
+       if (client6_data.ia_pd)
+               opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
 
        bb_error_msg("sending %s", "release");
        return d6_send_kernel_packet(
@@ -1028,7 +1057,8 @@ static void client_background(void)
 ////usage:     )
 //usage:     "\n       -O,--request-option OPT Request option OPT from server (cumulative)"
 //usage:     "\n       -o,--no-default-options Don't request any options (unless -O is given)"
-//usage:     "\n       -r,--request IP         Request this IP address"
+//usage:     "\n       -r,--request IP         Request this IP address ('no' to not request any IP)"
+//usage:     "\n       -d,--requestprefix      Request prefix"
 //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"
@@ -1062,7 +1092,8 @@ static void client_background(void)
 ////usage:     )
 //usage:     "\n       -O OPT          Request option OPT from server (cumulative)"
 //usage:     "\n       -o              Don't request any options (unless -O is given)"
-//usage:     "\n       -r IP           Request this IP address"
+//usage:     "\n       -r IP           Request this IP address ('no' to not request any IP)"
+//usage:     "\n       -d              Request prefix"
 //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"
@@ -1109,7 +1140,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:*f"
+               "i:np:qRr:s:T:+t:+SA:+O:*ox:*fd"
                USE_FOR_MMU("b")
                ///IF_FEATURE_UDHCPC_ARPING("a")
                IF_FEATURE_UDHCP_PORT("P:")
@@ -1125,10 +1156,15 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
                IF_UDHCP_VERBOSE(, &dhcp_verbose)
        );
        requested_ipv6 = NULL;
+       option_mask32 |= OPT_r;
        if (opt & OPT_r) {
-               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 (strcmp(str_r, "no") == 0) {
+                       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) {
@@ -1466,6 +1502,8 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
                        if (packet.d6_msg_type == D6_MSG_REPLY) {
                                uint32_t lease_seconds;
                                struct d6_option *option, *iaaddr;
+                               int address_timeout = 0;
+                               int prefix_timeout = 0;
  type_is_ok:
                                option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
                                if (option && (option->data[0] | option->data[1]) != 0) {
@@ -1589,6 +1627,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
  * .                                                               .
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  */
+                               if (option_mask32 & OPT_r) {
                                        free(client6_data.ia_na);
                                        client6_data.ia_na = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_NA);
                                        if (!client6_data.ia_na) {
@@ -1624,9 +1663,48 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
                                        if (lease_seconds > 0x7fffffff / 1000)
                                                lease_seconds = 0x7fffffff / 1000;
                                        /* enter bound state */
-                                       timeout = lease_seconds / 2;
+                                       address_timeout = lease_seconds / 2;
                                        bb_error_msg("lease obtained, lease time %u",
                                                /*inet_ntoa(temp_addr),*/ (unsigned)lease_seconds);
+                               }
+                               if (option_mask32 & OPT_d) {
+                                       struct d6_option *iaprefix;
+
+                                       free(client6_data.ia_pd);
+                                       client6_data.ia_pd = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_PD);
+                                       if (!client6_data.ia_pd) {
+                                               bb_error_msg("no %s option, ignoring packet", "IA_PD");
+                                               continue;
+                                       }
+                                       if (client6_data.ia_pd->len < (4 + 4 + 4) + (2 + 2 + 4 + 4 + 1 + 16)) {
+                                               bb_error_msg("IA_PD option is too short:%d bytes", client6_data.ia_pd->len);
+                                               continue;
+                                       }
+                                       iaprefix = d6_find_option(client6_data.ia_pd->data + 4 + 4 + 4,
+                                                       client6_data.ia_pd->data + client6_data.ia_pd->len,
+                                                       D6_OPT_IAPREFIX
+                                       );
+                                       if (!iaprefix) {
+                                               bb_error_msg("no %s option, ignoring packet", "IAPREFIX");
+                                               continue;
+                                       }
+                                       if (iaprefix->len < (4 + 4 + 1 + 16)) {
+                                               bb_error_msg("IAPREFIX option is too short:%d bytes", iaprefix->len);
+                                               continue;
+                                       }
+                                       move_from_unaligned32(lease_seconds, iaprefix->data + 4);
+                                       lease_seconds = ntohl(lease_seconds);
+                                       /* paranoia: must not be too small and not prone to overflows */
+                                       if (lease_seconds < 0x10)
+                                               lease_seconds = 0x10;
+                                       if (lease_seconds > 0x7fffffff / 1000)
+                                               lease_seconds = 0x7fffffff / 1000;
+                                       /* enter bound state */
+                                       prefix_timeout = lease_seconds / 2;
+                                       bb_error_msg("prefix obtained, lease time %u",
+                                               /*inet_ntoa(temp_addr),*/ (unsigned)lease_seconds);
+                               }
+                               timeout = address_timeout > prefix_timeout ? prefix_timeout : address_timeout;
                                d6_run_script(&packet, state == REQUESTING ? "bound" : "renew");
 
                                state = BOUND;