summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdhcp/client.c48
-rw-r--r--gdhcp/gdhcp.h4
-rw-r--r--src/dhcpv6.c107
3 files changed, 150 insertions, 9 deletions
diff --git a/gdhcp/client.c b/gdhcp/client.c
index a1f03097..cd428cff 100644
--- a/gdhcp/client.c
+++ b/gdhcp/client.c
@@ -73,6 +73,7 @@ typedef enum _dhcp_client_state {
SOLICITATION,
REQUEST,
RENEW,
+ REBIND,
} ClientState;
struct _GDHCPClient {
@@ -123,6 +124,8 @@ struct _GDHCPClient {
gpointer request_data;
GDHCPClientEventFunc renew_cb;
gpointer renew_data;
+ GDHCPClientEventFunc rebind_cb;
+ gpointer rebind_data;
char *last_address;
unsigned char *duid;
int duid_len;
@@ -134,6 +137,7 @@ struct _GDHCPClient {
struct in6_addr ia_na;
struct in6_addr ia_ta;
time_t last_renew;
+ time_t last_rebind;
};
static inline void debug(GDHCPClient *client, const char *format, ...)
@@ -622,7 +626,8 @@ 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, time_t *last_renew)
+ uint32_t *T1, uint32_t *T2,
+ time_t *last_renew, time_t *last_rebind)
{
if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
return -EINVAL;
@@ -636,6 +641,9 @@ int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
if (last_renew != NULL)
*last_renew = dhcp_client->last_renew;
+ if (last_rebind != NULL)
+ *last_rebind = dhcp_client->last_rebind;
+
return 0;
}
@@ -799,6 +807,11 @@ static int send_dhcpv6_renew(GDHCPClient *dhcp_client)
return send_dhcpv6_msg(dhcp_client, DHCPV6_RENEW, "renew");
}
+static int send_dhcpv6_rebind(GDHCPClient *dhcp_client)
+{
+ return send_dhcpv6_msg(dhcp_client, DHCPV6_REBIND, "rebind");
+}
+
static int send_information_req(GDHCPClient *dhcp_client)
{
return send_dhcpv6_msg(dhcp_client, DHCPV6_INFORMATION_REQ,
@@ -870,7 +883,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);
+ dhcp_client->last_renew = dhcp_client->last_rebind = time(0);
*error = G_DHCP_CLIENT_ERROR_NONE;
@@ -1980,6 +1993,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
case INFORMATION_REQ:
case REQUEST:
case RENEW:
+ case REBIND:
if (dhcp_client->type != G_DHCP_IPV6)
return TRUE;
@@ -2026,6 +2040,11 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
dhcp_client->renew_data);
return TRUE;
}
+ if (dhcp_client->rebind_cb != NULL) {
+ dhcp_client->rebind_cb(dhcp_client,
+ dhcp_client->rebind_data);
+ return TRUE;
+ }
break;
default:
break;
@@ -2159,6 +2178,16 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
return re;
}
send_dhcpv6_renew(dhcp_client);
+
+ } else if (dhcp_client->rebind_cb) {
+ dhcp_client->state = REBIND;
+ 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_rebind(dhcp_client);
}
return 0;
@@ -2303,6 +2332,12 @@ void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
dhcp_client->renew_cb = func;
dhcp_client->renew_data = data;
return;
+ case G_DHCP_CLIENT_EVENT_REBIND:
+ if (dhcp_client->type == G_DHCP_IPV4)
+ return;
+ dhcp_client->rebind_cb = func;
+ dhcp_client->rebind_data = data;
+ return;
}
}
@@ -2342,6 +2377,7 @@ char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
case SOLICITATION:
case REQUEST:
case RENEW:
+ case REBIND:
break;
}
return NULL;
@@ -2446,6 +2482,14 @@ void g_dhcpv6_client_reset_renew(GDHCPClient *dhcp_client)
dhcp_client->last_renew = time(0);
}
+void g_dhcpv6_client_reset_rebind(GDHCPClient *dhcp_client)
+{
+ if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+ return;
+
+ dhcp_client->last_rebind = time(0);
+}
+
uint16_t g_dhcpv6_client_get_status(GDHCPClient *dhcp_client)
{
if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
diff --git a/gdhcp/gdhcp.h b/gdhcp/gdhcp.h
index 3982e3ef..29eea566 100644
--- a/gdhcp/gdhcp.h
+++ b/gdhcp/gdhcp.h
@@ -57,6 +57,7 @@ typedef enum {
G_DHCP_CLIENT_EVENT_ADVERTISE,
G_DHCP_CLIENT_EVENT_REQUEST,
G_DHCP_CLIENT_EVENT_RENEW,
+ G_DHCP_CLIENT_EVENT_REBIND,
} GDHCPClientEvent;
typedef enum {
@@ -143,11 +144,12 @@ 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,
- time_t *last_renew);
+ time_t *last_renew, time_t *last_rebind);
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);
+void g_dhcpv6_client_reset_rebind(GDHCPClient *dhcp_client);
/* DHCP Server */
typedef enum {
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index b5b91727..fcd5d9cb 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -48,6 +48,8 @@
#define REQ_MAX_RC 10
#define REN_TIMEOUT (10 * 1000)
#define REN_MAX_RT (600 * 1000)
+#define REB_TIMEOUT (10 * 1000)
+#define REB_MAX_RT (600 * 1000)
struct connman_dhcpv6 {
@@ -230,6 +232,10 @@ static void clear_callbacks(GDHCPClient *dhcp_client)
NULL, NULL);
g_dhcp_client_register_event(dhcp_client,
+ G_DHCP_CLIENT_EVENT_REBIND,
+ NULL, NULL);
+
+ g_dhcp_client_register_event(dhcp_client,
G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
NULL, NULL);
}
@@ -531,6 +537,78 @@ static void re_cb(GDHCPClient *dhcp_client, gpointer user_data)
}
}
+static void rebind_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+ DBG("");
+
+ g_dhcpv6_client_reset_rebind(dhcp_client);
+ g_dhcpv6_client_reset_renew(dhcp_client);
+
+ re_cb(dhcp_client, user_data);
+}
+
+static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp)
+{
+ GDHCPClient *dhcp_client;
+
+ 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_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_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);
+
+ clear_callbacks(dhcp_client);
+
+ g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
+ rebind_cb, dhcp);
+
+ dhcp->dhcp_client = dhcp_client;
+
+ return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static gboolean timeout_rebind(gpointer user_data)
+{
+ struct connman_dhcpv6 *dhcp = user_data;
+
+ dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
+
+ DBG("rebind RT timeout %d msec", dhcp->RT);
+
+ dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
+
+ g_dhcp_client_start(dhcp->dhcp_client, NULL);
+
+ return FALSE;
+}
+
+static gboolean start_rebind(gpointer user_data)
+{
+ struct connman_dhcpv6 *dhcp = user_data;
+
+ dhcp->RT = REB_TIMEOUT * (1 + get_random());
+
+ DBG("rebind initial RT timeout %d msec", dhcp->RT);
+
+ dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
+
+ dhcpv6_rebind(dhcp);
+
+ return FALSE;
+}
+
static void request_cb(GDHCPClient *dhcp_client, gpointer user_data)
{
DBG("");
@@ -558,7 +636,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, NULL);
+ g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, 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,
@@ -625,7 +703,7 @@ static int dhcpv6_renew(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, NULL);
+ g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, 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,
@@ -676,7 +754,7 @@ int __connman_dhcpv6_start_renew(struct connman_network *network,
{
struct connman_dhcpv6 *dhcp;
uint32_t T1, T2;
- time_t last_renew, current;
+ time_t last_renew, last_rebind, current;
DBG("");
@@ -690,7 +768,7 @@ int __connman_dhcpv6_start_renew(struct connman_network *network,
}
g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
- &last_renew);
+ &last_renew, &last_rebind);
DBG("T1 %u T2 %u", T1, T2);
if (T1 == 0xffffffff)
@@ -707,10 +785,27 @@ int __connman_dhcpv6_start_renew(struct connman_network *network,
dhcp->callback = callback;
- DBG("renew after %d secs", T1);
+ if (T2 != 0xffffffff && T2 > 0 &&
+ (unsigned)current > (unsigned)last_rebind + T2) {
+ /* RFC 3315, chapter 18.1.3, start rebind */
+ int timeout = 0;
- dhcp->timeout = g_timeout_add_seconds(T1, start_renew, dhcp);
+ if ((unsigned)current > (unsigned)last_renew + T1)
+ timeout = 0;
+ else
+ timeout = last_renew - current + T1;
+ /*
+ * If we just did a renew, do not restart the rebind
+ * immediately.
+ */
+ dhcp->timeout = g_timeout_add_seconds(timeout, start_rebind,
+ dhcp);
+ } else {
+ DBG("renew after %d secs", T1);
+
+ dhcp->timeout = g_timeout_add_seconds(T1, start_renew, dhcp);
+ }
return 0;
}