diff options
-rw-r--r-- | networking/udhcp/d6_dhcpc.c | 80 | ||||
-rw-r--r-- | networking/udhcp/dhcpc.c | 127 |
2 files changed, 89 insertions, 118 deletions
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index 76b087b..592f5b1 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -1220,7 +1220,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) uint32_t xid = 0; int packet_num; int timeout; /* must be signed */ - unsigned already_waited_sec; + int lease_remaining; /* must be signed */ unsigned opt; int retval; @@ -1348,19 +1348,16 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) change_listen_mode(LISTEN_RAW); packet_num = 0; timeout = 0; - already_waited_sec = 0; + lease_remaining = 0; /* Main event loop. select() waits on signal pipe and possibly * on sockfd. * "continue" statements in code below jump to the top of the loop. */ for (;;) { - int tv; struct pollfd pfds[2]; struct d6_packet packet; uint8_t *packet_end; - /* silence "uninitialized!" warning */ - unsigned timestamp_before_wait = timestamp_before_wait; //bb_error_msg("sockfd:%d, listen_mode:%d", client_data.sockfd, client_data.listen_mode); @@ -1373,17 +1370,24 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) udhcp_sp_fd_set(pfds, client_data.sockfd); - tv = timeout - already_waited_sec; retval = 0; /* If we already timed out, fall through with retval = 0, else... */ - if (tv > 0) { - log1("waiting %u seconds", tv); - timestamp_before_wait = (unsigned)monotonic_sec(); - retval = poll(pfds, 2, tv < INT_MAX/1000 ? tv * 1000 : INT_MAX); + if (timeout > 0) { + unsigned diff; + + if (timeout > INT_MAX/1000) + timeout = INT_MAX/1000; + log1("waiting %u seconds", timeout); + diff = (unsigned)monotonic_sec(); + retval = poll(pfds, 2, timeout * 1000); if (retval < 0) { /* EINTR? A signal was caught, don't panic */ if (errno == EINTR) { - already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait; + diff = (unsigned)monotonic_sec() - diff; + lease_remaining -= diff; + if (lease_remaining < 0) + lease_remaining = 0; + timeout -= diff; continue; } /* Else: an error occured, panic! */ @@ -1410,9 +1414,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) memcpy(clientid_mac_ptr, client_data.client_mac, 6); - /* We will restart the wait in any case */ - already_waited_sec = 0; - switch (client_data.state) { case INIT_SELECTING: if (!discover_retries || packet_num < discover_retries) { @@ -1477,7 +1478,8 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) case RENEW_REQUESTED: /* manual (SIGUSR1) renew */ case_RENEW_REQUESTED: case RENEWING: - if (timeout >= 60) { + if (packet_num < 3) { + packet_num++; /* send an unicast renew request */ /* Sometimes observed to fail (EADDRNOTAVAIL) to bind * a new UDP socket for sending inside send_renew. @@ -1491,7 +1493,8 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) send_d6_info_request(xid); else send_d6_renew(xid, &srv6_buf, requested_ipv6); - timeout >>= 1; + timeout = discover_timeout; + /* ^^^ used to be = lease_remaining / 2 - WAY too long */ continue; } /* Timed out, enter rebinding state */ @@ -1503,12 +1506,12 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) change_listen_mode(LISTEN_RAW); /* Lease is *really* about to run out, * try to find DHCP server using broadcast */ - if (timeout > 0) { + if (lease_remaining > 0) { 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; + timeout = discover_timeout; continue; } /* Timed out, enter init state */ @@ -1516,7 +1519,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) d6_run_script_no_option("deconfig"); client_data.state = INIT_SELECTING; client_data.first_secs = 0; /* make secs field count from 0 */ - /*timeout = 0; - already is */ + timeout = 0; packet_num = 0; continue; /* case RELEASED: */ @@ -1532,21 +1535,9 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) switch (udhcp_sp_read()) { case SIGUSR1: client_data.first_secs = 0; /* make secs field count from 0 */ - already_waited_sec = 0; perform_renew(); - if (client_data.state == RENEW_REQUESTED) { - /* We might be either on the same network - * (in which case renew might work), - * or we might be on a completely different one - * (in which case renew won't ever succeed). - * For the second case, must make sure timeout - * is not too big, or else we can send - * futile renew requests for hours. - */ - if (timeout > 60) - timeout = 60; + if (client_data.state == RENEW_REQUESTED) goto case_RENEW_REQUESTED; - } /* Start things over */ packet_num = 0; /* Kill any timeouts, user wants this to hurry along */ @@ -1579,10 +1570,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) sleep(discover_timeout); /* 3 seconds by default */ change_listen_mode(client_data.listen_mode); /* just close and reopen */ } - /* If this packet will turn out to be unrelated/bogus, - * we will go back and wait for next one. - * Be sure timeout is properly decreased. */ - already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait; if (len < 0) continue; packet_end = (uint8_t*)&packet + len; @@ -1609,6 +1596,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) case RENEW_REQUESTED: case REBINDING: if (packet.d6_msg_type == D6_MSG_REPLY) { + unsigned start; uint32_t lease_seconds; struct d6_option *option; unsigned address_timeout; @@ -1631,7 +1619,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) requested_ipv6 = NULL; timeout = 0; packet_num = 0; - already_waited_sec = 0; continue; } option = d6_copy_option(packet.d6_options, packet_end, D6_OPT_SERVERID); @@ -1650,7 +1637,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) client_data.state = REQUESTING; timeout = 0; packet_num = 0; - already_waited_sec = 0; continue; } /* It's a D6_MSG_REPLY */ @@ -1814,21 +1800,26 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) address_timeout = prefix_timeout; if (!prefix_timeout) 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; + lease_remaining = (prefix_timeout < address_timeout ? prefix_timeout : address_timeout); + if (lease_remaining < 0) /* signed overflow? */ + lease_remaining = INT_MAX; 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; + lease_remaining = 24 * 60 * 60; } /* paranoia: must not be too small */ - /* timeout > 60 - ensures at least one unicast renew attempt */ - if (timeout < 61) - timeout = 61; + if (lease_remaining < 30) + lease_remaining = 30; + /* enter bound state */ + start = monotonic_sec(); d6_run_script(packet.d6_options, packet_end, (client_data.state == REQUESTING ? "bound" : "renew")); + timeout = (unsigned)lease_remaining / 2; + timeout -= (unsigned)monotonic_sec() - start; + packet_num = 0; client_data.state = BOUND; change_listen_mode(LISTEN_NONE); @@ -1844,7 +1835,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) opt = ((opt & ~OPT_b) | OPT_f); } #endif - already_waited_sec = 0; continue; /* back to main loop */ } continue; diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index bbcbd1f..a818c18 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -1266,7 +1266,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) uint32_t xid = xid; /* for compiler */ int packet_num; int timeout; /* must be signed */ - unsigned already_waited_sec; + int lease_remaining; /* must be signed */ unsigned opt; IF_FEATURE_UDHCPC_ARPING(unsigned arpping_ms;) int retval; @@ -1411,18 +1411,15 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) change_listen_mode(LISTEN_RAW); packet_num = 0; timeout = 0; - already_waited_sec = 0; + lease_remaining = 0; /* Main event loop. select() waits on signal pipe and possibly * on sockfd. * "continue" statements in code below jump to the top of the loop. */ for (;;) { - int tv; struct pollfd pfds[2]; struct dhcp_packet packet; - /* silence "uninitialized!" warning */ - unsigned timestamp_before_wait = timestamp_before_wait; //bb_error_msg("sockfd:%d, listen_mode:%d", client_data.sockfd, client_data.listen_mode); @@ -1435,17 +1432,24 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) udhcp_sp_fd_set(pfds, client_data.sockfd); - tv = timeout - already_waited_sec; retval = 0; /* If we already timed out, fall through with retval = 0, else... */ - if (tv > 0) { - log1("waiting %u seconds", tv); - timestamp_before_wait = (unsigned)monotonic_sec(); - retval = poll(pfds, 2, tv < INT_MAX/1000 ? tv * 1000 : INT_MAX); + if (timeout > 0) { + unsigned diff; + + if (timeout > INT_MAX/1000) + timeout = INT_MAX/1000; + log1("waiting %u seconds", timeout); + diff = (unsigned)monotonic_sec(); + retval = poll(pfds, 2, timeout * 1000); if (retval < 0) { /* EINTR? A signal was caught, don't panic */ if (errno == EINTR) { - already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait; + diff = (unsigned)monotonic_sec() - diff; + lease_remaining -= diff; + if (lease_remaining < 0) + lease_remaining = 0; + timeout -= diff; continue; } /* Else: an error occurred, panic! */ @@ -1472,9 +1476,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) if (clientid_mac_ptr) memcpy(clientid_mac_ptr, client_data.client_mac, 6); - /* We will restart the wait in any case */ - already_waited_sec = 0; - switch (client_data.state) { case INIT_SELECTING: if (!discover_retries || packet_num < discover_retries) { @@ -1536,7 +1537,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) case RENEW_REQUESTED: /* manual (SIGUSR1) renew */ case_RENEW_REQUESTED: case RENEWING: - if (timeout >= 60) { + if (packet_num < 3) { + packet_num++; /* send an unicast renew request */ /* Sometimes observed to fail (EADDRNOTAVAIL) to bind * a new UDP socket for sending inside send_renew. @@ -1547,14 +1549,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) * into INIT_SELECTING state. */ if (send_renew(xid, server_addr, requested_ip) >= 0) { - timeout >>= 1; -//TODO: the timeout to receive an answer for our renew should not be selected -//with "timeout = lease_seconds / 2; ...; timeout = timeout / 2": it is often huge. -//Waiting e.g. 4*3600 seconds for a reply does not make sense -//(if reply isn't coming, we keep an open socket for hours), -//it should be something like 10 seconds. -//Also, it's probably best to try sending renew in kernel mode a few (3-5) times -//and fall back to raw mode if it does not work. + timeout = discover_timeout; + /* ^^^ used to be = lease_remaining / 2 - WAY too long */ continue; } /* else: error sending. @@ -1563,6 +1559,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) * which wasn't reachable (and probably did not exist). */ } +//TODO: if 3 renew's failed (no reply) but remaining lease is large, +//it might make sense to make a large pause (~1 hour?) and try later? /* Timed out or error, enter rebinding state */ log1s("entering rebinding state"); client_data.state = REBINDING; @@ -1572,10 +1570,10 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) change_listen_mode(LISTEN_RAW); /* Lease is *really* about to run out, * try to find DHCP server using broadcast */ - if (timeout > 0) { + if (lease_remaining > 0) { /* send a broadcast renew request */ send_renew(xid, 0 /*INADDR_ANY*/, requested_ip); - timeout >>= 1; + timeout = discover_timeout; continue; } /* Timed out, enter init state */ @@ -1583,7 +1581,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) udhcp_run_script(NULL, "deconfig"); client_data.state = INIT_SELECTING; client_data.first_secs = 0; /* make secs field count from 0 */ - /*timeout = 0; - already is */ + timeout = 0; packet_num = 0; continue; /* case RELEASED: */ @@ -1599,21 +1597,9 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) switch (udhcp_sp_read()) { case SIGUSR1: client_data.first_secs = 0; /* make secs field count from 0 */ - already_waited_sec = 0; perform_renew(); - if (client_data.state == RENEW_REQUESTED) { - /* We might be either on the same network - * (in which case renew might work), - * or we might be on a completely different one - * (in which case renew won't ever succeed). - * For the second case, must make sure timeout - * is not too big, or else we can send - * futile renew requests for hours. - */ - if (timeout > 60) - timeout = 60; + if (client_data.state == RENEW_REQUESTED) goto case_RENEW_REQUESTED; - } /* Start things over */ packet_num = 0; /* Kill any timeouts, user wants this to hurry along */ @@ -1646,10 +1632,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) sleep(discover_timeout); /* 3 seconds by default */ change_listen_mode(client_data.listen_mode); /* just close and reopen */ } - /* If this packet will turn out to be unrelated/bogus, - * we will go back and wait for next one. - * Be sure timeout is properly decreased. */ - already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait; if (len < 0) continue; } @@ -1722,7 +1704,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) client_data.state = REQUESTING; timeout = 0; packet_num = 0; - already_waited_sec = 0; } continue; case REQUESTING: @@ -1731,28 +1712,38 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) case REBINDING: if (*message == DHCPACK) { unsigned start; - uint32_t lease_seconds; struct in_addr temp_addr; char server_str[sizeof("255.255.255.255")]; uint8_t *temp; + temp_addr.s_addr = server_addr; + strcpy(server_str, inet_ntoa(temp_addr)); + temp_addr.s_addr = packet.yiaddr; + temp = udhcp_get_option32(&packet, DHCP_LEASE_TIME); if (!temp) { - bb_simple_info_msg("no lease time with ACK, using 1 hour lease"); - lease_seconds = 60 * 60; + lease_remaining = 60 * 60; } else { + uint32_t lease; /* it IS unaligned sometimes, don't "optimize" */ - move_from_unaligned32(lease_seconds, temp); - lease_seconds = ntohl(lease_seconds); - /* paranoia: must not be too small and not prone to overflows */ - /* timeout > 60 - ensures at least one unicast renew attempt */ - if (lease_seconds < 2 * 61) - lease_seconds = 2 * 61; - //if (lease_seconds > 0x7fffffff) - // lease_seconds = 0x7fffffff; - //^^^not necessary since "timeout = lease_seconds / 2" - //does not overflow even for 0xffffffff. + move_from_unaligned32(lease, temp); + lease_remaining = ntohl(lease); } + /* Log message _before_ we sanitize lease */ + bb_info_msg("lease of %s obtained from %s, lease time %u%s", + inet_ntoa(temp_addr), server_str, (unsigned)lease_remaining, + temp ? "" : " (default)" + ); + /* paranoia: must not be too small and not prone to overflows */ + /* NB: 60s leases _are_ used in real world + * (temporary IPs while ISP modem initializes) + * do not break this case by bumplit it up. + */ + if (lease_remaining < 0) /* signed overflow? */ + lease_remaining = INT_MAX; + if (lease_remaining < 30) + lease_remaining = 30; + requested_ip = packet.yiaddr; #if ENABLE_FEATURE_UDHCPC_ARPING if (opt & OPT_a) { /* RFC 2131 3.1 paragraph 5: @@ -1764,7 +1755,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(requested_ip, NULL, (uint32_t) 0, client_data.client_mac, @@ -1783,27 +1774,18 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) requested_ip = 0; timeout = tryagain_timeout; packet_num = 0; - already_waited_sec = 0; continue; /* back to main loop */ } } #endif - /* enter bound state */ - temp_addr.s_addr = server_addr; - strcpy(server_str, inet_ntoa(temp_addr)); - temp_addr.s_addr = packet.yiaddr; - bb_info_msg("lease of %s obtained from %s, lease time %u", - inet_ntoa(temp_addr), server_str, (unsigned)lease_seconds); - requested_ip = packet.yiaddr; + /* enter bound state */ start = monotonic_sec(); udhcp_run_script(&packet, client_data.state == REQUESTING ? "bound" : "renew"); - already_waited_sec = (unsigned)monotonic_sec() - start; - timeout = lease_seconds / 2; - if ((unsigned)timeout < already_waited_sec) { - /* Something went wrong. Back to discover state */ - timeout = already_waited_sec = 0; - } + timeout = (unsigned)lease_remaining / 2; +//TODO: why / 2? + timeout -= (unsigned)monotonic_sec() - start; + packet_num = 0; client_data.state = BOUND; change_listen_mode(LISTEN_NONE); @@ -1856,7 +1838,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) requested_ip = 0; timeout = 0; packet_num = 0; - already_waited_sec = 0; } continue; /* case BOUND: - ignore all packets */ |