Hi,

On Mon, 2014-02-10 at 16:29 -0800, Andrew LeCain wrote:
> The existing renew/rebind mechanism doesn't correctly follow the
> procedure as indicated in rfc2131 section 4.4.5 dictating the
> resending of renewal and rebinding packets. This change sets up
> three seperate timers for T1, T2, and expiration and correctly
> sends retries for renew and rebind with a logarithmic backoff.
> ---
>  gdhcp/client.c |  205 
> +++++++++++++++++++++++++++++++++-----------------------
>  1 file changed, 123 insertions(+), 82 deletions(-)
> 
> diff --git a/gdhcp/client.c b/gdhcp/client.c
> index b24d19d..3b4ec9c 100644
> --- a/gdhcp/client.c
> +++ b/gdhcp/client.c
> @@ -100,6 +100,9 @@ struct _GDHCPClient {
>       uint8_t ack_retry_times;
>       uint8_t conflicts;
>       guint timeout;
> +     guint t1_timeout;
> +     guint t2_timeout;
> +     guint lease_timeout;
>       guint listener_watch;
>       GIOChannel *listener_channel;
>       GList *require_list;
> @@ -271,6 +274,26 @@ static int32_t get_time_diff(struct timeval *tv)
>       return hsec;
>  }
>  
> +static void remove_timeouts(GDHCPClient *dhcp_client)
> +{
> +
> +     if (dhcp_client->timeout > 0)
> +             g_source_remove(dhcp_client->timeout);
> +     if (dhcp_client->t1_timeout > 0)
> +             g_source_remove(dhcp_client->t1_timeout);
> +     if (dhcp_client->t2_timeout > 0)
> +             g_source_remove(dhcp_client->t2_timeout);
> +     if (dhcp_client->lease_timeout > 0)
> +             g_source_remove(dhcp_client->lease_timeout);
> +
> +     dhcp_client->timeout = 0;
> +     dhcp_client->t1_timeout = 0;
> +     dhcp_client->t2_timeout = 0;
> +     dhcp_client->lease_timeout = 0;
> +
> +

Nitpick: unnecessary empty line here.

> +}
> +
>  static void add_dhcpv6_request_options(GDHCPClient *dhcp_client,
>                               struct dhcpv6_packet *packet,
>                               unsigned char *buf, int max_buf,
> @@ -574,9 +597,7 @@ static gboolean send_announce_packet(gpointer dhcp_data)
>                               dhcp_client->requested_ip,
>                               dhcp_client->ifindex);
>  
> -     if (dhcp_client->timeout > 0)
> -             g_source_remove(dhcp_client->timeout);
> -     dhcp_client->timeout = 0;
> +     remove_timeouts(dhcp_client);
>  
>       if (dhcp_client->state == IPV4LL_DEFEND) {
>               dhcp_client->timeout =
> @@ -1352,10 +1373,7 @@ static void ipv4ll_start(GDHCPClient *dhcp_client)
>       guint timeout;
>       int seed;
>  
> -     if (dhcp_client->timeout > 0) {
> -             g_source_remove(dhcp_client->timeout);
> -             dhcp_client->timeout = 0;
> -     }
> +     remove_timeouts(dhcp_client);
>  
>       switch_listening_mode(dhcp_client, L_NONE);
>       dhcp_client->retry_times = 0;
> @@ -1381,8 +1399,7 @@ static void ipv4ll_stop(GDHCPClient *dhcp_client)
>  
>       switch_listening_mode(dhcp_client, L_NONE);
>  
> -     if (dhcp_client->timeout > 0)
> -             g_source_remove(dhcp_client->timeout);
> +     remove_timeouts(dhcp_client);
>  
>       if (dhcp_client->listener_watch > 0) {
>               g_source_remove(dhcp_client->listener_watch);
> @@ -1633,10 +1650,7 @@ static void restart_dhcp(GDHCPClient *dhcp_client, int 
> retry_times)
>  {
>       debug(dhcp_client, "restart DHCP (retries %d)", retry_times);
>  
> -     if (dhcp_client->timeout > 0) {
> -             g_source_remove(dhcp_client->timeout);
> -             dhcp_client->timeout = 0;
> -     }
> +     remove_timeouts(dhcp_client);
>  
>       dhcp_client->retry_times = retry_times;
>       dhcp_client->requested_ip = 0;
> @@ -1646,91 +1660,106 @@ static void restart_dhcp(GDHCPClient *dhcp_client, 
> int retry_times)
>       g_dhcp_client_start(dhcp_client, dhcp_client->last_address);
>  }
>  
> -static gboolean start_rebound_timeout(gpointer user_data)
> +static gboolean start_expire(gpointer user_data)
>  {
>       GDHCPClient *dhcp_client = user_data;
>  
> -     debug(dhcp_client, "start rebound timeout");
> +     debug(dhcp_client, "lease expired");
>  
> -     switch_listening_mode(dhcp_client, L2);
> +     //remove any timeouts if they are set

Nitpick: use /* */ for comments.
 
> -     dhcp_client->lease_seconds >>= 1;
> +     remove_timeouts(dhcp_client);
>  
> -     /* We need to have enough time to receive ACK package*/
> -     if (dhcp_client->lease_seconds <= 6) {
>  
> -             /* ip need to be cleared */
> -             if (dhcp_client->lease_lost_cb)
> -                     dhcp_client->lease_lost_cb(dhcp_client,
> -                                     dhcp_client->lease_lost_data);
> +     /* ip need to be cleared */
> +     if (dhcp_client->lease_lost_cb)
> +             dhcp_client->lease_lost_cb(dhcp_client,
> +                             dhcp_client->lease_lost_data);
>  
> -             restart_dhcp(dhcp_client, 0);
> -     } else {
> -             send_rebound(dhcp_client);
> +     restart_dhcp(dhcp_client, 0);
>  
> -             dhcp_client->timeout =
> -                             g_timeout_add_seconds_full(G_PRIORITY_HIGH,
> -                                             dhcp_client->lease_seconds >> 1,
> -                                                     start_rebound_timeout,
> -                                                             dhcp_client,
> -                                                             NULL);
> +     return false;
> +}
> +
> +static gboolean continue_rebound(gpointer user_data)
> +{
> +     GDHCPClient *dhcp_client = user_data;
> +
> +     switch_listening_mode(dhcp_client, L2);
> +     send_rebound(dhcp_client);
> +
> +     if (dhcp_client->t2_timeout> 0)
> +             g_source_remove(dhcp_client->t2_timeout);
> +
> +     //recalculate remaining rebind time
> +     dhcp_client->T2 >>= 1;
> +     if (dhcp_client->T2 > 60)
> +     {
> +             dhcp_client->t2_timeout =
> +                     g_timeout_add_seconds_full(G_PRIORITY_HIGH,
> +                                     dhcp_client->T2,
> +                                     continue_rebound,
> +                                     dhcp_client,
> +                                     NULL);

It's not useful to call _add_seconds_full here, a network full of
ConnMan DHCP clients need to distribute the load, especially as they use
NTP and thus have synchronized clocks as well. Use g_timeout_add_full
with a +-1s time window, see section 4.1 of that RFC.

>       }
>  
>       return FALSE;
>  }
> -
> -static void start_rebound(GDHCPClient *dhcp_client)

Nitpick: add empty line before function starts.

> +static gboolean start_rebound(gpointer user_data)
>  {
> -     debug(dhcp_client, "start rebound");
> +     GDHCPClient *dhcp_client = user_data;
>  
> +     //remove renew timer
> +     if (dhcp_client->t1_timeout > 0)
> +             g_source_remove(dhcp_client->t1_timeout);
> +
> +     debug(dhcp_client, "start rebound");
>       dhcp_client->state = REBINDING;
>  
> -     dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
> -                                             dhcp_client->lease_seconds >> 1,
> -                                                     start_rebound_timeout,
> -                                                             dhcp_client,
> -                                                             NULL);
> +     //calculate total rebind time
> +     dhcp_client->T2 = dhcp_client->expire - dhcp_client->T2;

Half the remaining time here, see 4.4.5.

> +
> +     //send the first rebound and reschedule
> +     continue_rebound(user_data);
> +
> +     return FALSE;
>  }
>  
> -static gboolean start_renew_request_timeout(gpointer user_data)
> +static gboolean continue_renew (gpointer user_data)
>  {
>       GDHCPClient *dhcp_client = user_data;
>  
> -     debug(dhcp_client, "renew request timeout");
> +     switch_listening_mode(dhcp_client, L3);
> +     send_renew(dhcp_client);
>  
> -     if (dhcp_client->no_lease_cb)
> -                     dhcp_client->no_lease_cb(dhcp_client,
> -                                             dhcp_client->no_lease_data);
> +     if (dhcp_client->t1_timeout > 0)
> +             g_source_remove(dhcp_client->t1_timeout);
>  
> -     return false;
> -}
> +     dhcp_client->T1 >>= 1;
>  
> -static gboolean start_renew_timeout(gpointer user_data)
> +     if (dhcp_client->T1 > 60)
> +     {
> +             dhcp_client->t1_timeout = 
> g_timeout_add_seconds_full(G_PRIORITY_HIGH,
> +                             dhcp_client->T1,
> +                             continue_renew,
> +                             dhcp_client,
> +                             NULL);
> +     }

As above.

> +
> +     return FALSE;
> +}
> +static gboolean start_renew(gpointer user_data)
>  {
>       GDHCPClient *dhcp_client = user_data;
>  
> -     debug(dhcp_client, "start renew timeout");
> -
> +     debug(dhcp_client, "start renew");
>       dhcp_client->state = RENEWING;
>  
> -     dhcp_client->lease_seconds >>= 1;
> -
> -     switch_listening_mode(dhcp_client, L3);
> -     if (dhcp_client->lease_seconds <= 60)
> -             start_rebound(dhcp_client);
> -     else {
> -             send_renew(dhcp_client);
> -
> -             if (dhcp_client->timeout > 0)
> -                     g_source_remove(dhcp_client->timeout);
> +     //calculate total renew period
> +     dhcp_client->T1 = dhcp_client->T2 - dhcp_client->T1;

Half the remaining time as above.
 
> -             dhcp_client->timeout =
> -                             g_timeout_add_seconds_full(G_PRIORITY_HIGH,
> -                                             REQUEST_TIMEOUT,
> -                                             start_renew_request_timeout,
> -                                             dhcp_client,
> -                                             NULL);
> -     }
> +     //send first renew and reschedule for half the remaining time.
> +     continue_renew(user_data);
>  
>       return FALSE;
>  }
> @@ -1741,12 +1770,30 @@ static void start_bound(GDHCPClient *dhcp_client)
>  
>       dhcp_client->state = BOUND;
>  
> -     if (dhcp_client->timeout > 0)
> -             g_source_remove(dhcp_client->timeout);
> +     remove_timeouts(dhcp_client);
>  
> -     dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
> -                                     dhcp_client->lease_seconds >> 1,
> -                                     start_renew_timeout, dhcp_client,
> +     /*
> +      *TODO: T1 and T2 should be set through options instead of
> +      * defaults as they are here.
> +      */
> +
> +     dhcp_client->T1 = dhcp_client->lease_seconds >> 1;
> +     dhcp_client->T2 = dhcp_client->lease_seconds * 0.875;
> +     dhcp_client->expire = dhcp_client->lease_seconds;
> +
> +     dhcp_client->t1_timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
> +                                     dhcp_client->T1,
> +                                     start_renew, dhcp_client,
> +                                                     NULL);
> +
> +     dhcp_client->t2_timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
> +                                     dhcp_client->T2,
> +                                     start_rebound, dhcp_client,
> +                                                     NULL);
> +
> +     dhcp_client->lease_timeout= g_timeout_add_seconds_full(G_PRIORITY_HIGH,
> +                                     dhcp_client->expire,
> +                                     start_expire, dhcp_client,
>                                                       NULL);
>  }
>  
> @@ -2317,7 +2364,7 @@ static gboolean listener_event(GIOChannel *channel, 
> GIOCondition condition,
>               if (*message_type != DHCPOFFER)
>                       return TRUE;
>  
> -             g_source_remove(dhcp_client->timeout);
> +             remove_timeouts(dhcp_client);
>               dhcp_client->timeout = 0;
>               dhcp_client->retry_times = 0;
>  
> @@ -2336,9 +2383,7 @@ static gboolean listener_event(GIOChannel *channel, 
> GIOCondition condition,
>               if (*message_type == DHCPACK) {
>                       dhcp_client->retry_times = 0;
>  
> -                     if (dhcp_client->timeout > 0)
> -                             g_source_remove(dhcp_client->timeout);
> -                     dhcp_client->timeout = 0;
> +                     remove_timeouts(dhcp_client);
>  
>                       dhcp_client->lease_seconds = get_lease(&packet);
>  
> @@ -2358,8 +2403,7 @@ static gboolean listener_event(GIOChannel *channel, 
> GIOCondition condition,
>               } else if (*message_type == DHCPNAK) {
>                       dhcp_client->retry_times = 0;
>  
> -                     if (dhcp_client->timeout > 0)
> -                             g_source_remove(dhcp_client->timeout);
> +                     remove_timeouts(dhcp_client);
>  
>                       dhcp_client->timeout = g_timeout_add_seconds_full(
>                                                       G_PRIORITY_HIGH, 3,
> @@ -2773,10 +2817,7 @@ void g_dhcp_client_stop(GDHCPClient *dhcp_client)
>               send_release(dhcp_client, dhcp_client->server_ip,
>                                       dhcp_client->requested_ip);
>  
> -     if (dhcp_client->timeout > 0) {
> -             g_source_remove(dhcp_client->timeout);
> -             dhcp_client->timeout = 0;
> -     }
> +     remove_timeouts(dhcp_client);
>  
>       if (dhcp_client->listener_watch > 0) {
>               g_source_remove(dhcp_client->listener_watch);

And remember to rebase to latest upstream, some refactoring around
send_* happened recently.

Cheers,

        Patrik


_______________________________________________
connman mailing list
[email protected]
https://lists.connman.net/mailman/listinfo/connman

Reply via email to