
 function                                             old     new   delta
 udhcpc_main                                         2544    2752    +208
 .rodata                                           139665  139710     +45
 packed_usage                                       28561   28579     +18
 udhcpc_longopts                                      248     265     +17
 ------------------------------------------------------------------------------
 (add/remove: 0/0 grow/shrink: 4/0 up/down: 288/0)             Total: 288 bytes
    text    data     bss     dec     hex filename
    752120    2091    9068  763279   ba58f busybox_old
    752373    2091    9068  763532   ba68c busybox_unstripped

diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index 4d755e6..fb6514d 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -49,6 +49,7 @@ static const char udhcpc_longopts[] ALIGN1 =
 	"version\0"        No_argument       "v"
 	"retries\0"        Required_argument "t"
 	"tryagain\0"       Required_argument "A"
+	"select-timeout\0" Required_argument "L"
 	"syslog\0"         No_argument       "S"
 	"request-option\0" Required_argument "O"
 	"no-default-options\0" No_argument   "o"
@@ -776,20 +777,24 @@ static int sockfd = -1;
 #define LISTEN_RAW    2
 static smallint listen_mode;
 
+enum {
 /* initial state: (re)start DHCP negotiation */
-#define INIT_SELECTING  0
-/* discover was sent, DHCPOFFER reply received */
-#define REQUESTING      1
+	INIT_SELECTING,
+/* discover was sent, first DHCPOFFER reply received */
+	SELECTING,
+/* one or more DHCPOFFER reply received */
+	REQUESTING,
 /* select/renew was sent, DHCPACK reply received */
-#define BOUND           2
+	BOUND,
 /* half of lease passed, want to renew it by sending unicast renew requests */
-#define RENEWING        3
+	RENEWING,
 /* renew requests were not answered, lease is almost over, send broadcast renew */
-#define REBINDING       4
+	REBINDING,
 /* manually requested renew (SIGUSR1) */
-#define RENEW_REQUESTED 5
+	RENEW_REQUESTED,
 /* release, possibly manually requested (SIGUSR2) */
-#define RELEASED        6
+	RELEASED
+};
 static smallint state;
 
 static int udhcp_raw_socket(int ifindex)
@@ -905,6 +910,7 @@ static void perform_renew(void)
 		change_listen_mode(LISTEN_RAW);
 		state = INIT_SELECTING;
 		break;
+	case SELECTING:
 	case INIT_SELECTING:
 		break;
 	}
@@ -969,6 +975,7 @@ static void client_background(void)
 //usage:     "\n	-t,--retries N		Send up to N discover packets"
 //usage:     "\n	-T,--timeout N		Pause between packets (default 3 seconds)"
 //usage:     "\n	-A,--tryagain N		Wait N seconds after failure (default 20)"
+//usage:     "\n	-L,--select-timeout N	Wait N seconds for offers from servers (default 0 seconds)"
 //usage:     "\n	-f,--foreground		Run in foreground"
 //usage:	USE_FOR_MMU(
 //usage:     "\n	-b,--background		Background if lease is not obtained"
@@ -1007,6 +1014,7 @@ static void client_background(void)
 //usage:     "\n	-t N		Send up to N discover packets"
 //usage:     "\n	-T N		Pause between packets (default 3 seconds)"
 //usage:     "\n	-A N		Wait N seconds (default 20) after failure"
+//usage:     "\n	-L N		Wait N seconds for offers from servers (default 0 seconds)"
 //usage:     "\n	-f		Run in foreground"
 //usage:	USE_FOR_MMU(
 //usage:     "\n	-b		Background if lease is not obtained"
@@ -1054,6 +1062,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 	int tryagain_timeout = 20;
 	int discover_timeout = 3;
 	int discover_retries = 3;
+	int select_timeout = 0;
+	llist_t *offers = NULL;
 	uint32_t server_addr = server_addr; /* for compiler */
 	uint32_t requested_ip = 0;
 	uint32_t xid = 0;
@@ -1065,7 +1075,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 	int max_fd;
 	int retval;
 	struct timeval tv;
-	struct dhcp_packet packet;
+	struct dhcp_packet *packet = NULL;
 	fd_set rfds;
 
 	/* Default options */
@@ -1076,14 +1086,14 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 	str_V = "udhcp "BB_VER;
 
 	/* Parse command line */
-	/* O,x: list; -T,-t,-A take numeric param */
-	opt_complementary = "O::x::T+:t+:A+"
+	/* O,x: list; -T,-t,-A,-L take numeric param */
+	opt_complementary = "O::x::T+:t+:A+:L+:"
 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
 		":vv"
 #endif
 		;
 	IF_LONG_OPTS(applet_long_options = udhcpc_longopts;)
-	opt = getopt32(argv, "CV:H:h:F:i:np:qRr:s:T:t:SA:O:ox:fB"
+	opt = getopt32(argv, "CV:H:h:F:i:np:qRr:s:T:t:SA:L:O:ox:fB"
 		USE_FOR_MMU("b")
 		IF_FEATURE_UDHCPC_ARPING("a")
 		IF_FEATURE_UDHCP_PORT("P:")
@@ -1092,6 +1102,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 		, &client_config.interface, &client_config.pidfile, &str_r /* i,p */
 		, &client_config.script /* s */
 		, &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */
+		, &select_timeout /* L */
 		, &list_O
 		, &list_x
 		IF_FEATURE_UDHCP_PORT(, &str_P)
@@ -1285,6 +1296,9 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 				timeout = tryagain_timeout;
 				packet_num = 0;
 				continue;
+			case SELECTING:
+				/* Timed out, deal with collected packets */
+				goto case_SELECTING;
 			case REQUESTING:
 				if (packet_num < discover_retries) {
 					/* send broadcast select packet */
@@ -1387,11 +1401,14 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 		{
 			int len;
 
+			if (packet == NULL)
+				packet = xmalloc(sizeof(struct dhcp_packet));
+
 			/* A packet is ready, read it */
 			if (listen_mode == LISTEN_KERNEL)
-				len = udhcp_recv_kernel_packet(&packet, sockfd);
+				len = udhcp_recv_kernel_packet(packet, sockfd);
 			else
-				len = udhcp_recv_raw_packet(&packet, sockfd);
+				len = udhcp_recv_raw_packet(packet, sockfd);
 			if (len == -1) {
 				/* Error is severe, reopen socket */
 				bb_info_msg("Read error: %s, reopening socket", strerror(errno));
@@ -1406,22 +1423,22 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 				continue;
 		}
 
-		if (packet.xid != xid) {
+		if (packet->xid != xid) {
 			log1("xid %x (our is %x), ignoring packet",
-				(unsigned)packet.xid, (unsigned)xid);
+				(unsigned)packet->xid, (unsigned)xid);
 			continue;
 		}
 
 		/* Ignore packets that aren't for us */
-		if (packet.hlen != 6
-		 || memcmp(packet.chaddr, client_config.client_mac, 6) != 0
+		if (packet->hlen != 6
+		 || memcmp(packet->chaddr, client_config.client_mac, 6) != 0
 		) {
 //FIXME: need to also check that last 10 bytes are zero
 			log1("chaddr does not match, ignoring packet"); // log2?
 			continue;
 		}
 
-		message = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
+		message = udhcp_get_option(packet, DHCP_MESSAGE_TYPE);
 		if (message == NULL) {
 			bb_error_msg("no message type option, ignoring packet");
 			continue;
@@ -1429,19 +1446,63 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 
 		switch (state) {
 		case INIT_SELECTING:
+		case SELECTING:
 			/* Must be a DHCPOFFER to one of our xid's */
 			if (*message == DHCPOFFER) {
 		/* TODO: why we don't just fetch server's IP from IP header? */
-				temp = udhcp_get_option(&packet, DHCP_SERVER_ID);
+				temp = udhcp_get_option(packet, DHCP_SERVER_ID);
 				if (!temp) {
 					bb_error_msg("no server ID, ignoring packet");
 					continue;
 					/* still selecting - this server looks bad */
 				}
+
+				/* Keep received packet */
+				llist_add_to_end(&offers, packet);
+				packet = NULL;
+
+				/* Still selecting */
+				if (state == SELECTING)
+					continue;
+
+				/* Enter selecting state */
+				state = SELECTING;
+				timeout = select_timeout;
+				already_waited_sec = 0;
+			}
+			continue;
+		case_SELECTING:
+			{
+				lease_seconds = -1;
+
+				while (offers) {
+					struct dhcp_packet *temp_packet;
+					uint32_t temp_lease_seconds;
+
+					temp_packet = llist_pop(&offers);
+					temp = udhcp_get_option(temp_packet, DHCP_LEASE_TIME);
+
+					/* it IS unaligned sometimes, don't "optimize" */
+					move_from_unaligned32(temp_lease_seconds, temp);
+					lease_seconds = ntohl(temp_lease_seconds);
+					lease_seconds &= 0x0fffffff; /* paranoia: must not be prone to overflows */
+
+					if (temp_lease_seconds > lease_seconds) {
+						struct dhcp_packet *save_packet = packet;
+
+						lease_seconds = temp_lease_seconds;
+						packet = temp_packet;
+						temp_packet = save_packet;
+					}
+					free(temp_packet);
+				}
+
+				temp = udhcp_get_option(packet, DHCP_SERVER_ID);
+
 				/* it IS unaligned sometimes, don't "optimize" */
 				move_from_unaligned32(server_addr, temp);
-				/*xid = packet.xid; - already is */
-				requested_ip = packet.yiaddr;
+				/*xid = packet->xid; - already is */
+				requested_ip = packet->yiaddr;
 
 				/* enter requesting state */
 				state = REQUESTING;
@@ -1455,7 +1516,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 		case RENEW_REQUESTED:
 		case REBINDING:
 			if (*message == DHCPACK) {
-				temp = udhcp_get_option(&packet, DHCP_LEASE_TIME);
+				temp = udhcp_get_option(packet, DHCP_LEASE_TIME);
 				if (!temp) {
 					bb_error_msg("no lease time with ACK, using 1 hour lease");
 					lease_seconds = 60 * 60;
@@ -1478,7 +1539,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
  * address is already in use (e.g., through the use of ARP),
  * the client MUST send a DHCPDECLINE message to the server and restarts
  * the configuration process..." */
-					if (!arpping(packet.yiaddr,
+					if (!arpping(packet->yiaddr,
 							NULL,
 							(uint32_t) 0,
 							client_config.client_mac,
@@ -1486,7 +1547,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 					) {
 						bb_info_msg("Offered address is in use "
 							"(got ARP reply), declining");
-						send_decline(xid, server_addr, packet.yiaddr);
+						send_decline(xid, server_addr, packet->yiaddr);
 
 						if (state != REQUESTING)
 							udhcp_run_script(NULL, "deconfig");
@@ -1505,12 +1566,12 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 				timeout = lease_seconds / 2;
 				{
 					struct in_addr temp_addr;
-					temp_addr.s_addr = packet.yiaddr;
+					temp_addr.s_addr = packet->yiaddr;
 					bb_info_msg("Lease of %s obtained, lease time %u",
 						inet_ntoa(temp_addr), (unsigned)lease_seconds);
 				}
-				requested_ip = packet.yiaddr;
-				udhcp_run_script(&packet, state == REQUESTING ? "bound" : "renew");
+				requested_ip = packet->yiaddr;
+				udhcp_run_script(packet, state == REQUESTING ? "bound" : "renew");
 
 				state = BOUND;
 				change_listen_mode(LISTEN_NONE);
@@ -1534,7 +1595,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 			if (*message == DHCPNAK) {
 				/* return to init state */
 				bb_info_msg("Received DHCP NAK");
-				udhcp_run_script(&packet, "nak");
+				udhcp_run_script(packet, "nak");
 				if (state != REQUESTING)
 					udhcp_run_script(NULL, "deconfig");
 				change_listen_mode(LISTEN_RAW);
@@ -1556,6 +1617,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
  ret0:
 	retval = 0;
  ret:
+	free(packet);
 	/*if (client_config.pidfile) - remove_pidfile has its own check */
 		remove_pidfile(client_config.pidfile);
 	return retval;
