Source code for dhcpcanon.timers
# -*- coding: utf-8 -*-
# vim:ts=4:sw=4:expandtab 2
# Copyright 2016, 2017 juga (juga at riseup dot net), MIT license.
"""Timers for the DHCP client implementation of the Anonymity Profile
([:rfc:`7844`])."""
from __future__ import absolute_import
import logging
import random
from datetime import datetime, timedelta
from .constants import (DT_PRINT_FORMAT, MAX_DELAY_SELECTING, REBIND_PERC,
RENEW_PERC)
logger = logging.getLogger(__name__)
[docs]def future_dt_str(dt, td):
"""."""
if isinstance(td, str):
td = float(td)
td = timedelta(seconds=td)
future_dt = dt + td
return future_dt.strftime(DT_PRINT_FORMAT)
[docs]def nowutc():
"""."""
# NOTE: Not using UTC, as all the timers are set in reference to local time
# now = datetime.utcnow().replace(tzinfo=utc)
now = datetime.now()
return now
[docs]def gen_delay_selecting():
"""Generate the delay in seconds in which the DISCOVER will be sent.
[:rfc:`2131#section-4.4.1`]::
The client SHOULD wait a random time between one and ten seconds to
desynchronize the use of DHCP at startup.
"""
delay = float(random.randint(0, MAX_DELAY_SELECTING))
logger.debug('Delay to enter in SELECTING %s.', delay)
logger.debug('SELECTING will happen on %s',
future_dt_str(nowutc(), delay))
return delay
[docs]def gen_timeout_resend(attempts):
"""Generate the time in seconds in which DHCPDISCOVER wil be retransmited.
[:rfc:`2131#section-3.1`]::
might retransmit the
DHCPREQUEST message four times, for a total delay of 60 seconds
[:rfc:`2131#section-4.1`]::
For example, in a 10Mb/sec Ethernet
internetwork, the delay before the first retransmission SHOULD be 4
seconds randomized by the value of a uniform random number chosen
from the range -1 to +1. Clients with clocks that provide resolution
granularity of less than one second may choose a non-integer
randomization value. The delay before the next retransmission SHOULD
be 8 seconds randomized by the value of a uniform number chosen from
the range -1 to +1. The retransmission delay SHOULD be doubled with
subsequent retransmissions up to a maximum of 64 seconds.
"""
timeout = 2 ** (attempts + 1) + random.uniform(-1, +1)
logger.debug('next timeout resending will happen on %s',
future_dt_str(nowutc(), timeout))
return timeout
[docs]def gen_timeout_request_renew(lease):
"""Generate time in seconds to retransmit DHCPREQUEST.
[:rfc:`2131#section-4..4.5`]::
In both RENEWING and REBINDING states,
if the client receives no response to its DHCPREQUEST
message, the client SHOULD wait one-half of the remaining
time until T2 (in RENEWING state) and one-half of the
remaining lease time (in REBINDING state), down to a
minimum of 60 seconds, before retransmitting the
DHCPREQUEST message.
"""
time_left = (lease.rebinding_time - lease.renewing_time) * RENEW_PERC
if time_left < 60:
time_left = 60
logger.debug('Next request in renew will happen on %s',
future_dt_str(nowutc(), time_left))
return time_left
[docs]def gen_timeout_request_rebind(lease):
"""."""
time_left = (lease.lease_time - lease.rebinding_time) * RENEW_PERC
if time_left < 60:
time_left = 60
logger.debug('Next request on rebinding will happen on %s',
future_dt_str(nowutc(), time_left))
return time_left
[docs]def gen_renewing_time(lease_time, elapsed=0):
"""Generate RENEWING time.
[:rfc:`2131#section-4.4.5`]::
T1
defaults to (0.5 * duration_of_lease). T2 defaults to (0.875 *
duration_of_lease). Times T1 and T2 SHOULD be chosen with some
random "fuzz" around a fixed value, to avoid synchronization of
client reacquisition.
"""
renewing_time = int(lease_time) * RENEW_PERC - elapsed
# FIXME:80 [:rfc:`2131#section-4.4.5`]: the chosen "fuzz" could fingerprint
# the implementation
# NOTE: here using same "fuzz" as systemd?
range_fuzz = int(lease_time) * REBIND_PERC - renewing_time
logger.debug('rebinding fuzz range %s', range_fuzz)
fuzz = random.uniform(-(range_fuzz),
+(range_fuzz))
renewing_time += fuzz
logger.debug('Renewing time %s.', renewing_time)
return renewing_time
[docs]def gen_rebinding_time(lease_time, elapsed=0):
"""."""
rebinding_time = int(lease_time) * REBIND_PERC - elapsed
# FIXME:90 [:rfc:`2131#section-4.4.5`]: the chosen "fuzz" could fingerprint
# the implementation
# NOTE: here using same "fuzz" as systemd?
range_fuzz = int(lease_time) - rebinding_time
logger.debug('rebinding fuzz range %s', range_fuzz)
fuzz = random.uniform(-(range_fuzz),
+(range_fuzz))
rebinding_time += fuzz
logger.debug('Rebinding time %s.', rebinding_time)
return rebinding_time