dhcpv6: Renew message implemented.
authorJukka Rissanen <jukka.rissanen@linux.intel.com>
Thu, 5 Jan 2012 11:38:11 +0000 (13:38 +0200)
committerDaniel Wagner <daniel.wagner@bmw-carit.de>
Thu, 5 Jan 2012 12:17:26 +0000 (13:17 +0100)
gdhcp/client.c
gdhcp/gdhcp.h
src/connman.h
src/dhcpv6.c
src/network.c

index a8fedb1..a1f0309 100644 (file)
@@ -72,6 +72,7 @@ typedef enum _dhcp_client_state {
        INFORMATION_REQ,
        SOLICITATION,
        REQUEST,
+       RENEW,
 } ClientState;
 
 struct _GDHCPClient {
@@ -120,6 +121,8 @@ struct _GDHCPClient {
        gpointer advertise_data;
        GDHCPClientEventFunc request_cb;
        gpointer request_data;
+       GDHCPClientEventFunc renew_cb;
+       gpointer renew_data;
        char *last_address;
        unsigned char *duid;
        int duid_len;
@@ -130,6 +133,7 @@ struct _GDHCPClient {
        uint32_t T1, T2;
        struct in6_addr ia_na;
        struct in6_addr ia_ta;
+       time_t last_renew;
 };
 
 static inline void debug(GDHCPClient *client, const char *format, ...)
@@ -618,7 +622,7 @@ void g_dhcpv6_client_create_iaid(GDHCPClient *dhcp_client, int index,
 }
 
 int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
-                               uint32_t *T1, uint32_t *T2)
+                               uint32_t *T1, uint32_t *T2, time_t *last_renew)
 {
        if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
                return -EINVAL;
@@ -629,6 +633,9 @@ int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
        if (T2 != NULL)
                *T2 = dhcp_client->T2;
 
+       if (last_renew != NULL)
+               *last_renew = dhcp_client->last_renew;
+
        return 0;
 }
 
@@ -787,6 +794,11 @@ static int send_dhcpv6_request(GDHCPClient *dhcp_client)
        return send_dhcpv6_msg(dhcp_client, DHCPV6_REQUEST, "request");
 }
 
+static int send_dhcpv6_renew(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_RENEW, "renew");
+}
+
 static int send_information_req(GDHCPClient *dhcp_client)
 {
        return send_dhcpv6_msg(dhcp_client, DHCPV6_INFORMATION_REQ,
@@ -858,6 +870,7 @@ GDHCPClient *g_dhcp_client_new(GDHCPType type,
        dhcp_client->require_list = NULL;
        dhcp_client->duid = NULL;
        dhcp_client->duid_len = 0;
+       dhcp_client->last_renew = time(0);
 
        *error = G_DHCP_CLIENT_ERROR_NONE;
 
@@ -1966,6 +1979,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                break;
        case INFORMATION_REQ:
        case REQUEST:
+       case RENEW:
                if (dhcp_client->type != G_DHCP_IPV6)
                        return TRUE;
 
@@ -2007,6 +2021,11 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                                        dhcp_client->request_data);
                        return TRUE;
                }
+               if (dhcp_client->renew_cb != NULL) {
+                       dhcp_client->renew_cb(dhcp_client,
+                                       dhcp_client->renew_data);
+                       return TRUE;
+               }
                break;
        default:
                break;
@@ -2130,6 +2149,16 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
                                return re;
                        }
                        send_dhcpv6_request(dhcp_client);
+
+               } else if (dhcp_client->renew_cb) {
+                       dhcp_client->state = RENEW;
+                       re = switch_listening_mode(dhcp_client, L3);
+                       if (re != 0) {
+                               switch_listening_mode(dhcp_client, L_NONE);
+                               dhcp_client->state = 0;
+                               return re;
+                       }
+                       send_dhcpv6_renew(dhcp_client);
                }
 
                return 0;
@@ -2268,6 +2297,12 @@ void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
                dhcp_client->request_cb = func;
                dhcp_client->request_data = data;
                return;
+       case G_DHCP_CLIENT_EVENT_RENEW:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->renew_cb = func;
+               dhcp_client->renew_data = data;
+               return;
        }
 }
 
@@ -2306,6 +2341,7 @@ char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
        case INFORMATION_REQ:
        case SOLICITATION:
        case REQUEST:
+       case RENEW:
                break;
        }
        return NULL;
@@ -2402,6 +2438,14 @@ void g_dhcpv6_client_set_send(GDHCPClient *dhcp_client,
        }
 }
 
+void g_dhcpv6_client_reset_renew(GDHCPClient *dhcp_client)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return;
+
+       dhcp_client->last_renew = time(0);
+}
+
 uint16_t g_dhcpv6_client_get_status(GDHCPClient *dhcp_client)
 {
        if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
index 22a28a5..3982e3e 100644 (file)
@@ -56,6 +56,7 @@ typedef enum {
        G_DHCP_CLIENT_EVENT_SOLICITATION,
        G_DHCP_CLIENT_EVENT_ADVERTISE,
        G_DHCP_CLIENT_EVENT_REQUEST,
+       G_DHCP_CLIENT_EVENT_RENEW,
 } GDHCPClientEvent;
 
 typedef enum {
@@ -141,10 +142,12 @@ int g_dhcpv6_client_set_oro(GDHCPClient *dhcp_client, int args, ...);
 void g_dhcpv6_client_create_iaid(GDHCPClient *dhcp_client, int index,
                                unsigned char *iaid);
 int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
-                               uint32_t *T1, uint32_t *T2);
+                               uint32_t *T1, uint32_t *T2,
+                               time_t *last_renew);
 uint32_t g_dhcpv6_client_get_iaid(GDHCPClient *dhcp_client);
 int g_dhcpv6_client_set_ia(GDHCPClient *dhcp_client, int index,
                int code, uint32_t *T1, uint32_t *T2, gboolean add_iaaddr);
+void g_dhcpv6_client_reset_renew(GDHCPClient *dhcp_client);
 
 /* DHCP Server */
 typedef enum {
index f48f922..d99d4b4 100644 (file)
@@ -284,6 +284,8 @@ int __connman_dhcpv6_start_info(struct connman_network *network,
 void __connman_dhcpv6_stop(struct connman_network *network);
 int __connman_dhcpv6_start(struct connman_network *network,
                                GSList *prefixes, dhcp_cb callback);
+int __connman_dhcpv6_start_renew(struct connman_network *network,
+                               dhcp_cb callback);
 
 int __connman_ipv4_init(void);
 void __connman_ipv4_cleanup(void);
index 34395fe..b5b9172 100644 (file)
@@ -46,6 +46,8 @@
 #define REQ_TIMEOUT    (1 * 1000)
 #define REQ_MAX_RT     (30 * 1000)
 #define REQ_MAX_RC     10
+#define REN_TIMEOUT     (10 * 1000)
+#define REN_MAX_RT      (600 * 1000)
 
 
 struct connman_dhcpv6 {
@@ -224,6 +226,10 @@ static void clear_callbacks(GDHCPClient *dhcp_client)
                                NULL, NULL);
 
        g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_RENEW,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
                                G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
                                NULL, NULL);
 }
@@ -552,7 +558,7 @@ static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
        g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
                                G_DHCPV6_SNTP_SERVERS);
 
-       g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2);
+       g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL);
        g_dhcpv6_client_set_ia(dhcp_client,
                        connman_network_get_index(dhcp->network),
                        dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
@@ -591,6 +597,123 @@ static gboolean timeout_request(gpointer user_data)
        return FALSE;
 }
 
+static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       DBG("");
+
+       g_dhcpv6_client_reset_renew(dhcp_client);
+
+       re_cb(dhcp_client, user_data);
+}
+
+static int dhcpv6_renew(struct connman_dhcpv6 *dhcp)
+{
+       GDHCPClient *dhcp_client;
+       uint32_t T1, T2;
+
+       DBG("dhcp %p", dhcp);
+
+       dhcp_client = dhcp->dhcp_client;
+
+       g_dhcp_client_clear_requests(dhcp_client);
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
+                               G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL);
+       g_dhcpv6_client_set_ia(dhcp_client,
+                       connman_network_get_index(dhcp->network),
+                       dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
+                       &T1, &T2, TRUE);
+
+       clear_callbacks(dhcp_client);
+
+       g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
+                                       renew_cb, dhcp);
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static gboolean timeout_renew(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
+
+       DBG("renew RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
+
+       g_dhcp_client_start(dhcp->dhcp_client, NULL);
+
+       return FALSE;
+}
+
+static gboolean start_renew(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       dhcp->RT = REN_TIMEOUT * (1 + get_random());
+
+       DBG("renew initial RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
+
+       dhcpv6_renew(dhcp);
+
+       return FALSE;
+}
+
+int __connman_dhcpv6_start_renew(struct connman_network *network,
+                                                       dhcp_cb callback)
+{
+       struct connman_dhcpv6 *dhcp;
+       uint32_t T1, T2;
+       time_t last_renew, current;
+
+       DBG("");
+
+       dhcp = g_hash_table_lookup(network_table, network);
+       if (dhcp == NULL)
+               return -ENOENT;
+
+       if (dhcp->timeout > 0) {
+               g_source_remove(dhcp->timeout);
+               dhcp->timeout = 0;
+       }
+
+       g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
+                               &last_renew);
+       DBG("T1 %u T2 %u", T1, T2);
+
+       if (T1 == 0xffffffff)
+               /* RFC 3315, 22.4 */
+               return 0;
+
+       if (T1 == 0)
+               /* RFC 3315, 22.4
+                * Client can choose the timeout.
+                */
+               T1 = 1800;
+
+       current = time(0);
+
+       dhcp->callback = callback;
+
+       DBG("renew after %d secs", T1);
+
+       dhcp->timeout = g_timeout_add_seconds(T1, start_renew, dhcp);
+
+       return 0;
+}
+
 static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
 {
        DBG("dhcp %p", dhcp);
index f9aed49..1790fba 100644 (file)
@@ -1026,6 +1026,8 @@ static void dhcpv6_callback(struct connman_network *network,
                        return;
                }
 
+               __connman_dhcpv6_start_renew(network, dhcpv6_callback);
+
                return;
        } else
                stop_dhcpv6(network);