summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJukka Rissanen <jukka.rissanen@linux.intel.com>2012-01-05 13:38:10 +0200
committerDaniel Wagner <daniel.wagner@bmw-carit.de>2012-01-05 13:17:26 +0100
commit3fd016f68a3d021d89fa7a1be1fa712e27e4913b (patch)
tree81f085800797c124b2aa9146041f5bd4061e4cdf
parent195007a210055b96f42f29348cbce02915dea242 (diff)
downloadconnman-3fd016f68a3d021d89fa7a1be1fa712e27e4913b.tar.gz
connman-3fd016f68a3d021d89fa7a1be1fa712e27e4913b.tar.bz2
connman-3fd016f68a3d021d89fa7a1be1fa712e27e4913b.zip
dhcpv6: Request message implemented.
-rw-r--r--gdhcp/client.c36
-rw-r--r--gdhcp/gdhcp.h8
-rw-r--r--src/dhcpv6.c123
3 files changed, 166 insertions, 1 deletions
diff --git a/gdhcp/client.c b/gdhcp/client.c
index 425f1cbf..a8fedb1f 100644
--- a/gdhcp/client.c
+++ b/gdhcp/client.c
@@ -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;
diff --git a/gdhcp/gdhcp.h b/gdhcp/gdhcp.h
index 56848549..22a28a54 100644
--- a/gdhcp/gdhcp.h
+++ b/gdhcp/gdhcp.h
@@ -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,
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index a19eee06..34395fe0 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -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)