Fix more problems with DHCP timers

- Number of retries could overflow. If we retry something more than 255
  times and it fails, just stop, instead of flooding the network with
  bogus requests
- The timeout could also overflow. It was stored in microseconds in a
  time_t, which allows a bit more than an hour. This is fine for the
  initial timeout (which starts at a few seconds and will go up to 64
  seconds), but after that we switch to a slower rate driven by the
  "state time". In particular, this can be the lease time, which DHCP
  servers may set to several days, or at least easily more than an hour.
- The computaiton of the timeout in the "slow lease" case attempted to
  do "not less than a minute", but missed a conversion from microseconds
  to seconds so it ended up doing "not less than 60 microseconds"

The combination of all these things means we can end up with a negative
timeout, and we will send a burst of requests without ever stopping,
flooding the network.

Change-Id: I0eb811c90f4a4dd8c9d92bff728bc2bbb52fbd56
Reviewed-on: https://review.haiku-os.org/c/haiku/+/4826
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Fredrik Holmqvist <[email protected]>
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Adrien Destugues
2021-12-21 18:51:25 +00:00
committed by waddlesplash
parent 50a4c18678
commit a8b90daa8a
+13 -8
View File
@@ -163,7 +163,7 @@ struct socket_timeout {
UpdateSocket(socket);
}
time_t timeout; // in micro secs
bigtime_t timeout; // in micro secs
uint8 tries;
bool Shift(int socket, bigtime_t stateMaxTime, const char* device);
@@ -448,17 +448,22 @@ socket_timeout::UpdateSocket(int socket) const
bool
socket_timeout::Shift(int socket, bigtime_t stateMaxTime, const char* device)
{
if (tries == UINT8_MAX)
return false;
tries++;
timeout += timeout;
if (timeout > AS_USECS(MAX_TIMEOUT))
timeout = AS_USECS(MAX_TIMEOUT);
if (tries > MAX_RETRIES) {
if (stateMaxTime == -1)
bigtime_t now = system_time();
if (stateMaxTime == -1 || stateMaxTime < now)
return false;
bigtime_t remaining = (stateMaxTime - system_time()) / 2 + 1;
timeout = std::max(remaining, bigtime_t(60));
}
bigtime_t remaining = (stateMaxTime - now) / 2 + 1;
timeout = std::max(remaining, bigtime_t(AS_USECS(60)));
} else
timeout += timeout;
if (timeout > AS_USECS(MAX_TIMEOUT))
timeout = AS_USECS(MAX_TIMEOUT);
syslog(LOG_DEBUG, "%s: Timeout shift: %" B_PRIdTIME " msecs (try %" B_PRIu8 ")\n",
device, timeout / 1000, tries);