summaryrefslogtreecommitdiff
path: root/gdhcp
diff options
context:
space:
mode:
authorJukka Rissanen <jukka.rissanen@linux.intel.com>2013-05-06 13:06:34 +0300
committerPatrik Flykt <patrik.flykt@linux.intel.com>2013-05-06 14:34:20 +0300
commitfd415b40a2219cfc99dca8789496ec9df8e116e6 (patch)
tree3be2c38177c6de3a2f3b896fb9050fc913319c56 /gdhcp
parent68ce7d3019b847c655bac4764b2b0a7d4e32ce69 (diff)
downloadconnman-fd415b40a2219cfc99dca8789496ec9df8e116e6.tar.gz
connman-fd415b40a2219cfc99dca8789496ec9df8e116e6.tar.bz2
connman-fd415b40a2219cfc99dca8789496ec9df8e116e6.zip
dhcpv6: Implement CONFIRM message support
See RFC 3315 Chapter 18.1.2. Creation and Transmission of Confirm Messages for details
Diffstat (limited to 'gdhcp')
-rw-r--r--gdhcp/client.c70
-rw-r--r--gdhcp/gdhcp.h4
2 files changed, 71 insertions, 3 deletions
diff --git a/gdhcp/client.c b/gdhcp/client.c
index 305533f0..fca5fff7 100644
--- a/gdhcp/client.c
+++ b/gdhcp/client.c
@@ -73,6 +73,7 @@ typedef enum _dhcp_client_state {
INFORMATION_REQ,
SOLICITATION,
REQUEST,
+ CONFIRM,
RENEW,
REBIND,
RELEASE,
@@ -131,6 +132,8 @@ struct _GDHCPClient {
gpointer rebind_data;
GDHCPClientEventFunc release_cb;
gpointer release_data;
+ GDHCPClientEventFunc confirm_cb;
+ gpointer confirm_data;
char *last_address;
unsigned char *duid;
int duid_len;
@@ -759,7 +762,7 @@ static void put_iaid(GDHCPClient *dhcp_client, int index, uint8_t *buf)
int g_dhcpv6_client_set_ia(GDHCPClient *dhcp_client, int index,
int code, uint32_t *T1, uint32_t *T2,
- gboolean add_iaaddr)
+ gboolean add_iaaddr, const char *ia_na)
{
if (code == G_DHCPV6_IA_TA) {
uint8_t ia_options[4];
@@ -771,13 +774,29 @@ int g_dhcpv6_client_set_ia(GDHCPClient *dhcp_client, int index,
ia_options, sizeof(ia_options));
} else if (code == G_DHCPV6_IA_NA) {
+ struct in6_addr addr;
g_dhcp_client_set_request(dhcp_client, G_DHCPV6_IA_NA);
- if (add_iaaddr == TRUE) {
+ /*
+ * If caller has specified the IPv6 address it wishes to
+ * to use (ia_na != NULL and address is valid), then send
+ * the address to server.
+ * If caller did not specify the address (ia_na == NULL) and
+ * if the current address is not set, then we should not send
+ * the address sub-option.
+ */
+ if (add_iaaddr == TRUE && ((ia_na == NULL &&
+ IN6_IS_ADDR_UNSPECIFIED(&dhcp_client->ia_na) == FALSE)
+ || (ia_na != NULL &&
+ inet_pton(AF_INET6, ia_na, &addr) == 1))) {
#define IAADDR_LEN (16+4+4)
uint8_t ia_options[4+4+4+2+2+IAADDR_LEN];
+ if (ia_na != NULL)
+ memcpy(&dhcp_client->ia_na, &addr,
+ sizeof(struct in6_addr));
+
put_iaid(dhcp_client, index, ia_options);
if (T1 != NULL) {
@@ -895,6 +914,11 @@ static int send_dhcpv6_request(GDHCPClient *dhcp_client)
return send_dhcpv6_msg(dhcp_client, DHCPV6_REQUEST, "request");
}
+static int send_dhcpv6_confirm(GDHCPClient *dhcp_client)
+{
+ return send_dhcpv6_msg(dhcp_client, DHCPV6_CONFIRM, "confirm");
+}
+
static int send_dhcpv6_renew(GDHCPClient *dhcp_client)
{
return send_dhcpv6_msg(dhcp_client, DHCPV6_RENEW, "renew");
@@ -2109,6 +2133,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
case RENEW:
case REBIND:
case RELEASE:
+ case CONFIRM:
if (dhcp_client->type != G_DHCP_IPV6)
return TRUE;
@@ -2164,6 +2189,30 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
dhcp_client->release_data);
return TRUE;
}
+ if (dhcp_client->confirm_cb != NULL) {
+ count = 0;
+ server_id = dhcpv6_get_option(packet6, pkt_len,
+ G_DHCPV6_SERVERID, &option_len,
+ &count);
+ if (server_id == NULL || count != 1 ||
+ option_len == 0) {
+ /* RFC 3315, 15.10 */
+ debug(dhcp_client,
+ "confirm server duid error, "
+ "discarding msg %p/%d/%d",
+ server_id, option_len, count);
+ return TRUE;
+ }
+ dhcp_client->server_duid = g_try_malloc(option_len);
+ if (dhcp_client->server_duid == NULL)
+ return TRUE;
+ memcpy(dhcp_client->server_duid, server_id, option_len);
+ dhcp_client->server_duid_len = option_len;
+
+ dhcp_client->confirm_cb(dhcp_client,
+ dhcp_client->confirm_data);
+ return TRUE;
+ }
break;
default:
break;
@@ -2288,6 +2337,16 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
}
send_dhcpv6_request(dhcp_client);
+ } else if (dhcp_client->confirm_cb) {
+ dhcp_client->state = CONFIRM;
+ 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_confirm(dhcp_client);
+
} else if (dhcp_client->renew_cb) {
dhcp_client->state = RENEW;
re = switch_listening_mode(dhcp_client, L3);
@@ -2474,6 +2533,12 @@ void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
dhcp_client->release_cb = func;
dhcp_client->release_data = data;
return;
+ case G_DHCP_CLIENT_EVENT_CONFIRM:
+ if (dhcp_client->type == G_DHCP_IPV4)
+ return;
+ dhcp_client->confirm_cb = func;
+ dhcp_client->confirm_data = data;
+ return;
}
}
@@ -2512,6 +2577,7 @@ char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
case INFORMATION_REQ:
case SOLICITATION:
case REQUEST:
+ case CONFIRM:
case RENEW:
case REBIND:
case RELEASE:
diff --git a/gdhcp/gdhcp.h b/gdhcp/gdhcp.h
index 0820cdd5..ba47eaff 100644
--- a/gdhcp/gdhcp.h
+++ b/gdhcp/gdhcp.h
@@ -59,6 +59,7 @@ typedef enum {
G_DHCP_CLIENT_EVENT_RENEW,
G_DHCP_CLIENT_EVENT_REBIND,
G_DHCP_CLIENT_EVENT_RELEASE,
+ G_DHCP_CLIENT_EVENT_CONFIRM,
} GDHCPClientEvent;
typedef enum {
@@ -152,7 +153,8 @@ int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
time_t *expire);
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);
+ int code, uint32_t *T1, uint32_t *T2,
+ gboolean add_addresses, const char *address);
void g_dhcpv6_client_reset_renew(GDHCPClient *dhcp_client);
void g_dhcpv6_client_reset_rebind(GDHCPClient *dhcp_client);
void g_dhcpv6_client_set_expire(GDHCPClient *dhcp_client, uint32_t timeout);