udhcpc6: add support of prefix delegation
Based on patch by DannyAAM <danny@saru.moe> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
@@ -133,6 +133,7 @@ struct d6_option {
|
|||||||
struct client6_data_t {
|
struct client6_data_t {
|
||||||
struct d6_option *server_id;
|
struct d6_option *server_id;
|
||||||
struct d6_option *ia_na;
|
struct d6_option *ia_na;
|
||||||
|
struct d6_option *ia_pd;
|
||||||
char **env_ptr;
|
char **env_ptr;
|
||||||
unsigned env_idx;
|
unsigned env_idx;
|
||||||
/* link-local IPv6 address */
|
/* link-local IPv6 address */
|
||||||
|
@@ -96,6 +96,7 @@ static const char udhcpc6_longopts[] ALIGN1 =
|
|||||||
"quit\0" No_argument "q"
|
"quit\0" No_argument "q"
|
||||||
"release\0" No_argument "R"
|
"release\0" No_argument "R"
|
||||||
"request\0" Required_argument "r"
|
"request\0" Required_argument "r"
|
||||||
|
"requestprefix\0" No_argument "d"
|
||||||
"script\0" Required_argument "s"
|
"script\0" Required_argument "s"
|
||||||
"timeout\0" Required_argument "T"
|
"timeout\0" Required_argument "T"
|
||||||
"retries\0" Required_argument "t"
|
"retries\0" Required_argument "t"
|
||||||
@@ -128,8 +129,9 @@ enum {
|
|||||||
OPT_o = 1 << 12,
|
OPT_o = 1 << 12,
|
||||||
OPT_x = 1 << 13,
|
OPT_x = 1 << 13,
|
||||||
OPT_f = 1 << 14,
|
OPT_f = 1 << 14,
|
||||||
|
OPT_d = 1 << 15,
|
||||||
/* The rest has variable bit positions, need to be clever */
|
/* The rest has variable bit positions, need to be clever */
|
||||||
OPTBIT_f = 14,
|
OPTBIT_d = 15,
|
||||||
USE_FOR_MMU( OPTBIT_b,)
|
USE_FOR_MMU( OPTBIT_b,)
|
||||||
///IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
|
///IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
|
||||||
IF_FEATURE_UDHCP_PORT( OPTBIT_P,)
|
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 */
|
/* Create new IA_NA, optionally with included IAADDR with requested IP */
|
||||||
free(client6_data.ia_na);
|
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 = NULL;
|
||||||
client6_data.ia_na = xzalloc(len);
|
if (option_mask32 & OPT_r) {
|
||||||
client6_data.ia_na->code = D6_OPT_IA_NA;
|
len = requested_ipv6 ? 2+2+4+4+4 + 2+2+16+4+4 : 2+2+4+4+4;
|
||||||
client6_data.ia_na->len = len - 4;
|
client6_data.ia_na = xzalloc(len);
|
||||||
*(uint32_t*)client6_data.ia_na->data = rand(); /* IAID */
|
client6_data.ia_na->code = D6_OPT_IA_NA;
|
||||||
if (requested_ipv6) {
|
client6_data.ia_na->len = len - 4;
|
||||||
struct d6_option *iaaddr = (void*)(client6_data.ia_na->data + 4+4+4);
|
*(uint32_t*)client6_data.ia_na->data = rand(); /* IAID */
|
||||||
iaaddr->code = D6_OPT_IAADDR;
|
if (requested_ipv6) {
|
||||||
iaaddr->len = 16+4+4;
|
struct d6_option *iaaddr = (void*)(client6_data.ia_na->data + 4+4+4);
|
||||||
memcpy(iaaddr->data, requested_ipv6, 16);
|
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:
|
/* Add options:
|
||||||
* "param req" option according to -O, options specified with -x
|
* "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 */
|
/* server id */
|
||||||
opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
|
opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
|
||||||
/* IA NA (contains requested IP) */
|
/* 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:
|
/* Add options:
|
||||||
* "param req" option according to -O, options specified with -x
|
* "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 */
|
/* server id */
|
||||||
opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
|
opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
|
||||||
/* IA NA (contains requested IP) */
|
/* 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:
|
/* Add options:
|
||||||
* "param req" option according to -O, options specified with -x
|
* "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 */
|
/* server id */
|
||||||
opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
|
opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
|
||||||
/* IA NA (contains our current IP) */
|
/* 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");
|
bb_error_msg("sending %s", "release");
|
||||||
return d6_send_kernel_packet(
|
return d6_send_kernel_packet(
|
||||||
@@ -1028,7 +1057,8 @@ static void client_background(void)
|
|||||||
////usage: )
|
////usage: )
|
||||||
//usage: "\n -O,--request-option OPT Request option OPT from server (cumulative)"
|
//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 -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 -x OPT:VAL Include option OPT in sent packets (cumulative)"
|
||||||
//usage: "\n Examples of string, numeric, and hex byte opts:"
|
//usage: "\n Examples of string, numeric, and hex byte opts:"
|
||||||
//usage: "\n -x hostname:bbox - option 12"
|
//usage: "\n -x hostname:bbox - option 12"
|
||||||
@@ -1062,7 +1092,8 @@ static void client_background(void)
|
|||||||
////usage: )
|
////usage: )
|
||||||
//usage: "\n -O OPT Request option OPT from server (cumulative)"
|
//usage: "\n -O OPT Request option OPT from server (cumulative)"
|
||||||
//usage: "\n -o Don't request any options (unless -O is given)"
|
//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 -x OPT:VAL Include option OPT in sent packets (cumulative)"
|
||||||
//usage: "\n Examples of string, numeric, and hex byte opts:"
|
//usage: "\n Examples of string, numeric, and hex byte opts:"
|
||||||
//usage: "\n -x hostname:bbox - option 12"
|
//usage: "\n -x hostname:bbox - option 12"
|
||||||
@@ -1109,7 +1140,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
/* Parse command line */
|
/* Parse command line */
|
||||||
opt = getopt32long(argv, "^"
|
opt = getopt32long(argv, "^"
|
||||||
/* O,x: list; -T,-t,-A take numeric param */
|
/* 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")
|
USE_FOR_MMU("b")
|
||||||
///IF_FEATURE_UDHCPC_ARPING("a")
|
///IF_FEATURE_UDHCPC_ARPING("a")
|
||||||
IF_FEATURE_UDHCP_PORT("P:")
|
IF_FEATURE_UDHCP_PORT("P:")
|
||||||
@@ -1125,10 +1156,15 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
IF_UDHCP_VERBOSE(, &dhcp_verbose)
|
IF_UDHCP_VERBOSE(, &dhcp_verbose)
|
||||||
);
|
);
|
||||||
requested_ipv6 = NULL;
|
requested_ipv6 = NULL;
|
||||||
|
option_mask32 |= OPT_r;
|
||||||
if (opt & OPT_r) {
|
if (opt & OPT_r) {
|
||||||
if (inet_pton(AF_INET6, str_r, &ipv6_buf) <= 0)
|
if (strcmp(str_r, "no") == 0) {
|
||||||
bb_error_msg_and_die("bad IPv6 address '%s'", str_r);
|
option_mask32 -= OPT_r;
|
||||||
requested_ipv6 = &ipv6_buf;
|
} 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 ENABLE_FEATURE_UDHCP_PORT
|
||||||
if (opt & OPT_P) {
|
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) {
|
if (packet.d6_msg_type == D6_MSG_REPLY) {
|
||||||
uint32_t lease_seconds;
|
uint32_t lease_seconds;
|
||||||
struct d6_option *option, *iaaddr;
|
struct d6_option *option, *iaaddr;
|
||||||
|
int address_timeout = 0;
|
||||||
|
int prefix_timeout = 0;
|
||||||
type_is_ok:
|
type_is_ok:
|
||||||
option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
|
option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
|
||||||
if (option && (option->data[0] | option->data[1]) != 0) {
|
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);
|
free(client6_data.ia_na);
|
||||||
client6_data.ia_na = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_NA);
|
client6_data.ia_na = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_NA);
|
||||||
if (!client6_data.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)
|
if (lease_seconds > 0x7fffffff / 1000)
|
||||||
lease_seconds = 0x7fffffff / 1000;
|
lease_seconds = 0x7fffffff / 1000;
|
||||||
/* enter bound state */
|
/* enter bound state */
|
||||||
timeout = lease_seconds / 2;
|
address_timeout = lease_seconds / 2;
|
||||||
bb_error_msg("lease obtained, lease time %u",
|
bb_error_msg("lease obtained, lease time %u",
|
||||||
/*inet_ntoa(temp_addr),*/ (unsigned)lease_seconds);
|
/*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");
|
d6_run_script(&packet, state == REQUESTING ? "bound" : "renew");
|
||||||
|
|
||||||
state = BOUND;
|
state = BOUND;
|
||||||
|
Reference in New Issue
Block a user