dhcpv6: Request message implemented.
authorJukka Rissanen <jukka.rissanen@linux.intel.com>
Thu, 5 Jan 2012 11:38:10 +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/dhcpv6.c

index 425f1cb..a8fedb1 100644 (file)
@@ -71,6 +71,7 @@ typedef enum _dhcp_client_state {
        IPV4LL_DEFEND,
        INFORMATION_REQ,
        SOLICITATION,
+       REQUEST,
 } ClientState;
 
 struct _GDHCPClient {
@@ -117,6 +118,8 @@ struct _GDHCPClient {
        gpointer solicitation_data;
        GDHCPClientEventFunc advertise_cb;
        gpointer advertise_data;
+       GDHCPClientEventFunc request_cb;
+       gpointer request_data;
        char *last_address;
        unsigned char *duid;
        int duid_len;
@@ -779,6 +782,11 @@ static int send_solicitation(GDHCPClient *dhcp_client)
        return send_dhcpv6_msg(dhcp_client, DHCPV6_SOLICIT, "solicit");
 }
 
+static int send_dhcpv6_request(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_REQUEST, "request");
+}
+
 static int send_information_req(GDHCPClient *dhcp_client)
 {
        return send_dhcpv6_msg(dhcp_client, DHCPV6_INFORMATION_REQ,
@@ -1957,6 +1965,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                }
                break;
        case INFORMATION_REQ:
+       case REQUEST:
                if (dhcp_client->type != G_DHCP_IPV6)
                        return TRUE;
 
@@ -1967,7 +1976,10 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                option_len = 0;
                server_id = dhcpv6_get_option(packet6, pkt_len,
                                G_DHCPV6_SERVERID, &option_len, &count);
-               if (server_id == NULL || count != 1 || option_len == 0) {
+               if (server_id == NULL || count != 1 || option_len == 0 ||
+                               (dhcp_client->server_duid_len > 0 &&
+                               memcmp(dhcp_client->server_duid, server_id,
+                                       dhcp_client->server_duid_len) != 0)) {
                        /* RFC 3315, 15.10 */
                        debug(dhcp_client,
                                "server duid error, discarding msg %p/%d/%d",
@@ -1990,6 +2002,11 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                                        dhcp_client->information_req_data);
                        return TRUE;
                }
+               if (dhcp_client->request_cb != NULL) {
+                       dhcp_client->request_cb(dhcp_client,
+                                       dhcp_client->request_data);
+                       return TRUE;
+               }
                break;
        default:
                break;
@@ -2103,6 +2120,16 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
                                return re;
                        }
                        send_solicitation(dhcp_client);
+
+               } else if (dhcp_client->request_cb) {
+                       dhcp_client->state = REQUEST;
+                       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_request(dhcp_client);
                }
 
                return 0;
@@ -2235,6 +2262,12 @@ void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
                dhcp_client->advertise_cb = func;
                dhcp_client->advertise_data = data;
                return;
+       case G_DHCP_CLIENT_EVENT_REQUEST:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->request_cb = func;
+               dhcp_client->request_data = data;
+               return;
        }
 }
 
@@ -2272,6 +2305,7 @@ char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
        case IPV4LL_ANNOUNCE:
        case INFORMATION_REQ:
        case SOLICITATION:
+       case REQUEST:
                break;
        }
        return NULL;
index 5684854..22a28a5 100644 (file)
@@ -55,6 +55,7 @@ typedef enum {
        G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
        G_DHCP_CLIENT_EVENT_SOLICITATION,
        G_DHCP_CLIENT_EVENT_ADVERTISE,
+       G_DHCP_CLIENT_EVENT_REQUEST,
 } GDHCPClientEvent;
 
 typedef enum {
@@ -82,6 +83,13 @@ typedef enum {
 #define G_DHCPV6_DNS_SERVERS   23
 #define G_DHCPV6_SNTP_SERVERS  31
 
+#define G_DHCPV6_ERROR_SUCCESS 0
+#define G_DHCPV6_ERROR_FAILURE 1
+#define G_DHCPV6_ERROR_NO_ADDR 2
+#define G_DHCPV6_ERROR_BINDING 3
+#define G_DHCPV6_ERROR_LINK    4
+#define G_DHCPV6_ERROR_MCAST   5
+
 typedef enum {
        G_DHCPV6_DUID_LLT = 1,
        G_DHCPV6_DUID_EN  = 2,
index a19eee0..34395fe 100644 (file)
@@ -43,6 +43,9 @@
 #define SOL_MAX_DELAY   (1 * 1000)
 #define SOL_TIMEOUT     (1 * 1000)
 #define SOL_MAX_RT      (120 * 1000)
+#define REQ_TIMEOUT    (1 * 1000)
+#define REQ_MAX_RT     (30 * 1000)
+#define REQ_MAX_RC     10
 
 
 struct connman_dhcpv6 {
@@ -58,10 +61,13 @@ struct connman_dhcpv6 {
        guint RT;               /* in msec */
        gboolean use_ta;        /* set to TRUE if IPv6 privacy is enabled */
        GSList *prefixes;       /* network prefixes from radvd */
+       int request_count;      /* how many times REQUEST have been sent */
 };
 
 static GHashTable *network_table;
 
+static int dhcpv6_request(struct connman_dhcpv6 *dhcp, gboolean add_addresses);
+
 static inline float get_random()
 {
        return (rand() % 200 - 100) / 1000.0;
@@ -214,6 +220,10 @@ static void clear_callbacks(GDHCPClient *dhcp_client)
                                NULL, NULL);
 
        g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_REQUEST,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
                                G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
                                NULL, NULL);
 }
@@ -487,6 +497,100 @@ static int set_addresses(GDHCPClient *dhcp_client,
        return 0;
 }
 
+static void re_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+       uint16_t status;
+       int ret;
+
+       ret = set_addresses(dhcp_client, dhcp);
+
+       status = g_dhcpv6_client_get_status(dhcp_client);
+
+       DBG("dhcpv6 cb msg %p ret %d status %d", dhcp, ret, status);
+
+       if (ret < 0) {
+               if (dhcp->callback != NULL)
+                       dhcp->callback(dhcp->network, FALSE);
+               return;
+       }
+
+       if (status  == G_DHCPV6_ERROR_BINDING) {
+               /* RFC 3315, 18.1.8 */
+               dhcpv6_request(dhcp, FALSE);
+       } else {
+               if (dhcp->callback != NULL)
+                       dhcp->callback(dhcp->network,
+                                               status == 0 ? TRUE : FALSE);
+       }
+}
+
+static void request_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       DBG("");
+
+       re_cb(dhcp_client, user_data);
+}
+
+static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
+                       gboolean add_addresses)
+{
+       GDHCPClient *dhcp_client;
+       uint32_t T1, T2;
+
+       DBG("dhcp %p add %d", dhcp, add_addresses);
+
+       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);
+       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, add_addresses);
+
+       clear_callbacks(dhcp_client);
+
+       g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
+                                       request_cb, dhcp);
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static gboolean timeout_request(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       if (dhcp->request_count >= REQ_MAX_RC) {
+               DBG("max request retry attempts %d", dhcp->request_count);
+               dhcp->request_count = 0;
+               if (dhcp->callback != NULL)
+                       dhcp->callback(dhcp->network, FALSE);
+               return FALSE;
+       }
+
+       dhcp->request_count++;
+
+       dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
+       DBG("request RT timeout %d msec", dhcp->RT);
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
+
+       g_dhcp_client_start(dhcp->dhcp_client, NULL);
+
+       return FALSE;
+}
+
 static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
 {
        DBG("dhcp %p", dhcp);
@@ -583,6 +687,25 @@ static void advertise_cb(GDHCPClient *dhcp_client, gpointer user_data)
        struct connman_dhcpv6 *dhcp = user_data;
 
        DBG("dhcpv6 advertise msg %p", dhcp);
+
+       if (dhcp->timeout > 0) {
+               g_source_remove(dhcp->timeout);
+               dhcp->timeout = 0;
+       }
+
+       if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
+               if (dhcp->callback != NULL)
+                       dhcp->callback(dhcp->network, FALSE);
+               return;
+       }
+
+       dhcp->RT = REQ_TIMEOUT * (1 + get_random());
+       DBG("request initial RT timeout %d msec", dhcp->RT);
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
+
+       dhcp->request_count = 1;
+
+       dhcpv6_request(dhcp, TRUE);
 }
 
 static void solicitation_cb(GDHCPClient *dhcp_client, gpointer user_data)