diff options
author | Jukka Rissanen <jukka.rissanen@linux.intel.com> | 2013-05-06 13:06:34 +0300 |
---|---|---|
committer | Patrik Flykt <patrik.flykt@linux.intel.com> | 2013-05-06 14:34:20 +0300 |
commit | fd415b40a2219cfc99dca8789496ec9df8e116e6 (patch) | |
tree | 3be2c38177c6de3a2f3b896fb9050fc913319c56 /src | |
parent | 68ce7d3019b847c655bac4764b2b0a7d4e32ce69 (diff) | |
download | connman-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 'src')
-rw-r--r-- | src/dhcpv6.c | 181 |
1 files changed, 175 insertions, 6 deletions
diff --git a/src/dhcpv6.c b/src/dhcpv6.c index 0ac7f4ce..703bedbf 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -50,6 +50,10 @@ #define REN_MAX_RT (600 * 1000) #define REB_TIMEOUT (10 * 1000) #define REB_MAX_RT (600 * 1000) +#define CNF_MAX_DELAY (1 * 1000) +#define CNF_TIMEOUT (1 * 1000) +#define CNF_MAX_RT (4 * 1000) +#define CNF_MAX_RD (10 * 1000) struct connman_dhcpv6 { @@ -245,6 +249,10 @@ static void clear_callbacks(GDHCPClient *dhcp_client) NULL, NULL); g_dhcp_client_register_event(dhcp_client, + G_DHCP_CLIENT_EVENT_CONFIRM, + NULL, NULL); + + g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW, NULL, NULL); @@ -608,7 +616,7 @@ static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp) 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, - NULL, NULL, FALSE); + NULL, NULL, FALSE, NULL); clear_callbacks(dhcp_client); @@ -721,7 +729,7 @@ static int dhcpv6_request(struct connman_dhcpv6 *dhcp, 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); + &T1, &T2, add_addresses, NULL); clear_callbacks(dhcp_client); @@ -791,7 +799,7 @@ static int dhcpv6_renew(struct connman_dhcpv6 *dhcp) 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); + &T1, &T2, TRUE, NULL); clear_callbacks(dhcp_client); @@ -945,7 +953,7 @@ int __connman_dhcpv6_start_release(struct connman_network *network, 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, - NULL, NULL, TRUE); + NULL, NULL, TRUE, NULL); clear_callbacks(dhcp_client); @@ -1166,7 +1174,7 @@ static int dhcpv6_solicitation(struct connman_dhcpv6 *dhcp) g_dhcpv6_client_set_ia(dhcp_client, index, dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA, - NULL, NULL, FALSE); + NULL, NULL, FALSE, NULL); clear_callbacks(dhcp_client); @@ -1199,10 +1207,150 @@ static gboolean start_solicitation(gpointer user_data) return FALSE; } +static void confirm_cb(GDHCPClient *dhcp_client, gpointer user_data) +{ + struct connman_dhcpv6 *dhcp = user_data; + int status = g_dhcpv6_client_get_status(dhcp_client); + + DBG("dhcpv6 confirm msg %p status %d", dhcp, status); + + clear_timer(dhcp); + + set_addresses(dhcp_client, dhcp); + + g_dhcpv6_client_clear_retransmit(dhcp_client); + + /* + * If confirm fails, start from scratch. + */ + if (status != 0) { + g_dhcp_client_unref(dhcp->dhcp_client); + start_solicitation(dhcp); + } else if (dhcp->callback != NULL) + dhcp->callback(dhcp->network, TRUE); +} + +static int dhcpv6_confirm(struct connman_dhcpv6 *dhcp) +{ + GDHCPClient *dhcp_client; + GDHCPClientError error; + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv6; + int index, ret; + + DBG("dhcp %p", dhcp); + + index = connman_network_get_index(dhcp->network); + + dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error); + if (error != G_DHCP_CLIENT_ERROR_NONE) { + clear_timer(dhcp); + return -EINVAL; + } + + if (getenv("CONNMAN_DHCPV6_DEBUG")) + g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6"); + + service = connman_service_lookup_from_network(dhcp->network); + if (service == NULL) { + clear_timer(dhcp); + g_dhcp_client_unref(dhcp_client); + return -EINVAL; + } + + ret = set_duid(service, dhcp->network, dhcp_client, index); + if (ret < 0) { + clear_timer(dhcp); + g_dhcp_client_unref(dhcp_client); + return ret; + } + + g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID); + g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT); + 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); + + ipconfig_ipv6 = __connman_service_get_ip6config(service); + dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6); + + g_dhcpv6_client_set_ia(dhcp_client, index, + dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA, + NULL, NULL, TRUE, + __connman_ipconfig_get_dhcp_address(ipconfig_ipv6)); + + clear_callbacks(dhcp_client); + + g_dhcp_client_register_event(dhcp_client, + G_DHCP_CLIENT_EVENT_CONFIRM, + confirm_cb, dhcp); + + dhcp->dhcp_client = dhcp_client; + + return g_dhcp_client_start(dhcp_client, NULL); +} + +static gboolean timeout_confirm(gpointer user_data) +{ + struct connman_dhcpv6 *dhcp = user_data; + + dhcp->RT = calc_delay(dhcp->RT, CNF_MAX_RT); + + DBG("confirm RT timeout %d msec", dhcp->RT); + + dhcp->timeout = g_timeout_add(dhcp->RT, timeout_confirm, dhcp); + + g_dhcpv6_client_set_retransmit(dhcp->dhcp_client); + + g_dhcp_client_start(dhcp->dhcp_client, NULL); + + return FALSE; +} + +static gboolean timeout_max_confirm(gpointer user_data) +{ + struct connman_dhcpv6 *dhcp = user_data; + + dhcp->MRD = 0; + + clear_timer(dhcp); + + DBG("confirm max retransmit duration timeout"); + + g_dhcpv6_client_clear_retransmit(dhcp->dhcp_client); + + if (dhcp->callback != NULL) + dhcp->callback(dhcp->network, FALSE); + + return FALSE; +} + +static gboolean start_confirm(gpointer user_data) +{ + struct connman_dhcpv6 *dhcp = user_data; + + /* Set the confirm timeout, RFC 3315 chapter 14 */ + dhcp->RT = CNF_TIMEOUT * (1 + get_random()); + + DBG("confirm initial RT timeout %d msec", dhcp->RT); + + dhcp->timeout = g_timeout_add(dhcp->RT, timeout_confirm, dhcp); + dhcp->MRD = g_timeout_add(CNF_MAX_RD, timeout_max_confirm, dhcp); + + dhcpv6_confirm(dhcp); + + return FALSE; +} + int __connman_dhcpv6_start(struct connman_network *network, GSList *prefixes, dhcp_cb callback) { + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv6; struct connman_dhcpv6 *dhcp; + char *last_address; int delay; DBG(""); @@ -1213,6 +1361,10 @@ int __connman_dhcpv6_start(struct connman_network *network, return -EBUSY; } + service = connman_service_lookup_from_network(network); + if (service == NULL) + return -EINVAL; + dhcp = g_try_new0(struct connman_dhcpv6, 1); if (dhcp == NULL) return -ENOMEM; @@ -1231,7 +1383,24 @@ int __connman_dhcpv6_start(struct connman_network *network, /* Initial timeout, RFC 3315, 17.1.2 */ delay = rand() % 1000; - dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp); + ipconfig_ipv6 = __connman_service_get_ip6config(service); + last_address = __connman_ipconfig_get_dhcp_address(ipconfig_ipv6); + + if (prefixes != NULL && last_address != NULL && + check_ipv6_addr_prefix(prefixes, + last_address) != 128) { + /* + * So we are in the same subnet + * RFC 3315, chapter 18.1.2 Confirm message + */ + dhcp->timeout = g_timeout_add(delay, start_confirm, dhcp); + } else { + /* + * Start from scratch. + * RFC 3315, chapter 17.1.2 Solicitation message + */ + dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp); + } return 0; } |