From 87b9c279868f954a77caa1918eaa35f39e4cfab1 Mon Sep 17 00:00:00 2001
From: DannyAAM <danny@saru.moe>
Date: Mon, 15 Jan 2018 15:14:20 +0800
Subject: [PATCH 1/3] add support of prefix delegation

Signed-off-by: DannyAAM <danny@saru.moe>
---
 networking/udhcp/d6_common.h |   1 +
 networking/udhcp/d6_dhcpc.c  | 210 ++++++++++++++++++++++++++++++-------------
 2 files changed, 147 insertions(+), 64 deletions(-)

diff --git a/networking/udhcp/d6_common.h b/networking/udhcp/d6_common.h
index 310550371..e9c0397ae 100644
--- a/networking/udhcp/d6_common.h
+++ b/networking/udhcp/d6_common.h
@@ -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 */
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 37ffd064d..6d65e75fa 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -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"
+	"prefixdelegation\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,)
@@ -277,11 +279,11 @@ static void option_to_env(uint8_t *option, uint8_t *option_end)
  * |               |
  * +-+-+-+-+-+-+-+-+
  */
-			//move_from_unaligned32(v32, option + 4 + 4);
-			//*new_env() = xasprintf("lease=%u", (unsigned)v32);
+			move_from_unaligned32(v32, option + 4 + 4);
+			*new_env() = xasprintf("ipv6prefix_lease=%u", (unsigned)v32);
 
-			sprint_nip6(ipv6str, option + 4 + 4 + 1);
-			*new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4]));
+			sprint_nip6(ipv6str, option + 4 + 4 + 4 + 1);
+			*new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4 + 4]));
 			break;
 #if ENABLE_FEATURE_UDHCPC6_RFC3646
 		case D6_OPT_DNS_SERVERS: {
@@ -550,7 +552,7 @@ static int d6_mcast_from_client_config_ifindex(struct d6_packet *packet, uint8_t
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  */
 /* NOINLINE: limit stack usage in caller */
-static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ipv6)
+static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ipv6, int request_address, int request_prefix)
 {
 	struct d6_packet packet;
 	uint8_t *opt_ptr;
@@ -561,18 +563,35 @@ 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);
+	if (request_address) {
+		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);
+	} else {
+		client6_data.ia_na = NULL;
+	}
+
+	/* IA_PD */
+	free(client6_data.ia_pd);
+	if (request_prefix) {
+		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);
+	} else {
+		client6_data.ia_pd = NULL;
 	}
-	opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, len);
 
 	/* Add options:
 	 * "param req" option according to -O, options specified with -x
@@ -625,7 +644,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 +717,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 +752,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 +1059,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 disable)"
+//usage:     "\n	-d,--prefixdelegation	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 +1094,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 disable)"
+//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"
@@ -1091,6 +1124,8 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
 	struct in6_addr srv6_buf;
 	struct in6_addr ipv6_buf;
 	struct in6_addr *requested_ipv6;
+	int request_address = 1;
+	int request_prefix = 0;
 	uint32_t xid = 0;
 	int packet_num;
 	int timeout; /* must be signed */
@@ -1109,7 +1144,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:")
@@ -1126,10 +1161,15 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
 	);
 	requested_ipv6 = NULL;
 	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) {
+			request_address = 0;
+		} 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;
+		}
 	}
+	request_prefix = opt & OPT_d;
 #if ENABLE_FEATURE_UDHCP_PORT
 	if (opt & OPT_P) {
 		CLIENT_PORT6 = xatou16(str_P);
@@ -1287,7 +1327,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
 					if (packet_num == 0)
 						xid = random_xid();
 					/* multicast */
-					send_d6_discover(xid, requested_ipv6);
+					send_d6_discover(xid, requested_ipv6, request_address, request_prefix);
 					timeout = discover_timeout;
 					packet_num++;
 					continue;
@@ -1465,7 +1505,9 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
 		case REBINDING:
 			if (packet.d6_msg_type == D6_MSG_REPLY) {
 				uint32_t lease_seconds;
-				struct d6_option *option, *iaaddr;
+				struct d6_option *option, *iaaddr, *iaprefix;
+				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,44 +1631,84 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
  * .                                                               .
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  */
-				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) {
-					bb_error_msg("no %s option, ignoring packet", "IA_NA");
-					continue;
-				}
-				if (client6_data.ia_na->len < (4 + 4 + 4) + (2 + 2 + 16 + 4 + 4)) {
-					bb_error_msg("IA_NA option is too short:%d bytes", client6_data.ia_na->len);
-					continue;
-				}
-				iaaddr = d6_find_option(client6_data.ia_na->data + 4 + 4 + 4,
-						client6_data.ia_na->data + client6_data.ia_na->len,
-						D6_OPT_IAADDR
-				);
-				if (!iaaddr) {
-					bb_error_msg("no %s option, ignoring packet", "IAADDR");
-					continue;
-				}
-				if (iaaddr->len < (16 + 4 + 4)) {
-					bb_error_msg("IAADDR option is too short:%d bytes", iaaddr->len);
-					continue;
+				if (request_address) {
+					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) {
+						bb_error_msg("no %s option, ignoring packet", "IA_NA");
+						continue;
+					}
+					if (client6_data.ia_na->len < (4 + 4 + 4) + (2 + 2 + 16 + 4 + 4)) {
+						bb_error_msg("IA_NA option is too short:%d bytes", client6_data.ia_na->len);
+						continue;
+					}
+					iaaddr = d6_find_option(client6_data.ia_na->data + 4 + 4 + 4,
+							client6_data.ia_na->data + client6_data.ia_na->len,
+							D6_OPT_IAADDR
+					);
+					if (!iaaddr) {
+						bb_error_msg("no %s option, ignoring packet", "IAADDR");
+						continue;
+					}
+					if (iaaddr->len < (16 + 4 + 4)) {
+						bb_error_msg("IAADDR option is too short:%d bytes", iaaddr->len);
+						continue;
+					}
+					/* Note: the address is sufficiently aligned for cast:
+					 * we _copied_ IA-NA, and copy is always well-aligned.
+					 */
+					requested_ipv6 = (struct in6_addr*) iaaddr->data;
+					move_from_unaligned32(lease_seconds, iaaddr->data + 16 + 4);
+					lease_seconds = ntohl(lease_seconds);
+					/* paranoia: must not be too small and not prone to overflows */
+					if (lease_seconds < 0x10)
+						lease_seconds = 0x10;
+/// TODO: check for 0 lease time?
+					if (lease_seconds > 0x7fffffff / 1000)
+						lease_seconds = 0x7fffffff / 1000;
+					/* enter bound state */
+					address_timeout = lease_seconds / 2;
+					bb_error_msg("lease obtained, lease time %u",
+						/*inet_ntoa(temp_addr),*/ (unsigned)lease_seconds);
 				}
-				/* Note: the address is sufficiently aligned for cast:
-				 * we _copied_ IA-NA, and copy is always well-aligned.
-				 */
-				requested_ipv6 = (struct in6_addr*) iaaddr->data;
-				move_from_unaligned32(lease_seconds, iaaddr->data + 16 + 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 (request_prefix) {
+					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;
+					}
+					// ??? = (struct ???*) iaprefix->data;
+					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;
 /// TODO: check for 0 lease time?
-				if (lease_seconds > 0x7fffffff / 1000)
-					lease_seconds = 0x7fffffff / 1000;
-				/* enter bound state */
-				timeout = lease_seconds / 2;
-				bb_error_msg("lease obtained, lease time %u",
-					/*inet_ntoa(temp_addr),*/ (unsigned)lease_seconds);
+					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;
-- 
2.15.1


From 63aa9471db23339082f72dfb2272706c24f45aab Mon Sep 17 00:00:00 2001
From: DannyAAM <danny@saru.moe>
Date: Mon, 15 Jan 2018 15:11:30 +0800
Subject: [PATCH 2/3] add support of PPPoE link

Signed-off-by: DannyAAM <danny@saru.moe>
---
 networking/udhcp/d6_dhcpc.c  | 43 +++++++++++++++++++++++++++++++++----------
 networking/udhcp/d6_socket.c | 38 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 69 insertions(+), 12 deletions(-)

diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 6d65e75fa..583aae542 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -1131,7 +1131,8 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
 	int timeout; /* must be signed */
 	unsigned already_waited_sec;
 	unsigned opt;
-	int retval;
+	int retval, retval2;
+	int rand_mac = 0;
 
 	setup_common_bufsiz();
 
@@ -1205,15 +1206,15 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
 			*colon = ':'; /* restore it for NOMMU reexec */
 	}
 
-	if (d6_read_interface(client_config.interface,
+	retval2 = d6_read_interface(client_config.interface,
 			&client_config.ifindex,
 			&client6_data.ll_ip6,
-			client_config.client_mac)
-	) {
+			client_config.client_mac);
+	if (retval2 != 0 && retval2 != 1) { /* no error, or just lack of MAC is fine */
 		return 1;
 	}
 
-	/* Create client ID based on mac, set clientid_mac_ptr */
+	/* Create client ID based on mac (or random, if we don't have it), set clientid_mac_ptr */
 	{
 		struct d6_option *clientid;
 		clientid = xzalloc(2+2+2+2+6);
@@ -1222,7 +1223,12 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
 		clientid->data[1] = 3; /* DUID-LL */
 		clientid->data[3] = 1; /* ethernet */
 		clientid_mac_ptr = clientid->data + 2+2;
-		memcpy(clientid_mac_ptr, client_config.client_mac, 6);
+		if (retval2 == 1) { /* no MAC */
+			rand_mac = 1;
+			/* generate random MAC later (after srand) */
+		} else {
+			memcpy(clientid_mac_ptr, client_config.client_mac, 6);
+		}
 		client_config.clientid = (void*)clientid;
 	}
 
@@ -1258,6 +1264,12 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
 	timeout = 0;
 	already_waited_sec = 0;
 
+	/* Generate random MAC here (after srand) */
+	if (rand_mac) {
+		*(uint32_t*)clientid_mac_ptr = rand();
+		*(uint32_t*)(clientid_mac_ptr+2) = rand();
+	}
+
 	/* Main event loop. select() waits on signal pipe and possibly
 	 * on sockfd.
 	 * "continue" statements in code below jump to the top of the loop.
@@ -1308,15 +1320,26 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
 			 * or if the status of the bridge changed).
 			 * Refresh ifindex and client_mac:
 			 */
-			if (d6_read_interface(client_config.interface,
+			retval2 = d6_read_interface(client_config.interface,
 					&client_config.ifindex,
 					&client6_data.ll_ip6,
-					client_config.client_mac)
-			) {
+					client_config.client_mac);
+			if (retval2 != 0 && retval2 != 1) { /* no error, or just lack of MAC is fine */
 				goto ret0; /* iface is gone? */
 			}
 
-			memcpy(clientid_mac_ptr, client_config.client_mac, 6);
+			if (retval2 == 1) { /* no MAC */
+				if (rand_mac) {
+					/* keep original random MAC */
+				} else {
+					rand_mac = 1;
+					*(uint32_t*)clientid_mac_ptr = rand();
+					*(uint32_t*)(clientid_mac_ptr+2) = rand();
+				}
+			} else {
+				rand_mac = 0;
+				memcpy(clientid_mac_ptr, client_config.client_mac, 6);
+			}
 
 			/* We will restart the wait in any case */
 			already_waited_sec = 0;
diff --git a/networking/udhcp/d6_socket.c b/networking/udhcp/d6_socket.c
index d00c217d6..a7e388bf3 100644
--- a/networking/udhcp/d6_socket.c
+++ b/networking/udhcp/d6_socket.c
@@ -12,8 +12,11 @@
 
 int FAST_FUNC d6_read_interface(const char *interface, int *ifindex, struct in6_addr *nip6, uint8_t *mac)
 {
-	int retval = 3;
+	int retval = 7;
 	struct ifaddrs *ifap, *ifa;
+	struct ifreq ifr;
+	size_t if_name_len;
+	int fd;
 
 	getifaddrs(&ifap);
 
@@ -31,7 +34,8 @@ int FAST_FUNC d6_read_interface(const char *interface, int *ifindex, struct in6_
 			log2("MAC %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
 			log2("ifindex %d", sll->sll_ifindex);
 			*ifindex = sll->sll_ifindex;
-			retval &= (0xf - (1<<0));
+			retval &= (0xf - (1<<0)); /* no MAC */
+            retval &= (0xf - (1<<2)); /* no ifindex */
 		}
 #if 0
 		if (ifa->ifa_addr->sa_family == AF_INET) {
@@ -59,13 +63,43 @@ int FAST_FUNC d6_read_interface(const char *interface, int *ifindex, struct in6_
 	}
 
 	freeifaddrs(ifap);
+
+	if (retval & (1<<2)) { /* no ifindex, maybe link is ppp link, try to get ifindex through xioctl */
+		if_name_len = strlen(interface);
+		if (if_name_len < sizeof(ifr.ifr_name)) {
+			memcpy(ifr.ifr_name, interface, if_name_len);
+			ifr.ifr_name[if_name_len] = 0;
+		} else {
+			bb_error_msg("interface name is too long");
+			return -1;
+		}
+
+		fd = xsocket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); /* temporary socket */
+		if (fd == -1) {
+			bb_error_msg("temporary socket creation failed");
+			return -1;
+		}
+		if (xioctl(fd, SIOCGIFINDEX, &ifr) == -1) {
+			bb_error_msg("failed to get ifindex: %s", strerror(errno));
+			return -1;
+		}
+		close(fd);
+
+		*ifindex = ifr.ifr_ifindex;
+		retval &= (0xf - (1<<2));
+	}
+
 	if (retval == 0)
 		return retval;
+	if (retval == (1<<0)) /* seems not very critical for just lack of MAC */
+		return retval;
 
 	if (retval & (1<<0))
 		bb_error_msg("can't get %s", "MAC");
 	if (retval & (1<<1))
 		bb_error_msg("can't get %s", "link-local IPv6 address");
+	if (retval & (1<<2))
+		bb_error_msg("can't get %s", "ifindex");
 	return -1;
 }
 
-- 
2.15.1


From 69c432aba1cc9bf3db4df19c8db0b54aeac757fa Mon Sep 17 00:00:00 2001
From: DannyAAM <danny@saru.moe>
Date: Mon, 15 Jan 2018 15:42:02 +0800
Subject: [PATCH 3/3] fix always 0s lease time

Signed-off-by: DannyAAM <danny@saru.moe>
---
 networking/udhcp/d6_dhcpc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 583aae542..0a9d141f3 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -1529,8 +1529,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, *iaprefix;
-				int address_timeout = 0;
-				int prefix_timeout = 0;
+				int address_timeout = 0x7fffffff / 1000;
+				int prefix_timeout = 0x7fffffff / 1000;
  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) {
-- 
2.15.1

