sd-dhcp6-client: Implement Information Request message

Implement Information Request message according to RFC 3315, section
18.1.5. with the excepion that the first message is not delayed by a
random amount. Instead systemd-networkd is supposed to take care of
desynchronizing between other clients.

Initialize the DHCPv6 client structure in sd_dhcp6_client_start()
as this allows toggling between information request and normal
DHCPv6 address aquisition modes.
This commit is contained in:
Patrik Flykt 2014-12-10 16:17:32 +02:00 committed by Tom Gundersen
parent fab15fec24
commit bbfa43ca37
3 changed files with 118 additions and 36 deletions

View file

@ -51,6 +51,8 @@ enum {
DHCP6_PORT_CLIENT = 546,
};
#define DHCP6_INF_TIMEOUT 1 * USEC_PER_SEC
#define DHCP6_INF_MAX_RT 120 * USEC_PER_SEC
#define DHCP6_SOL_MAX_DELAY 1 * USEC_PER_SEC
#define DHCP6_SOL_TIMEOUT 1 * USEC_PER_SEC
#define DHCP6_SOL_MAX_RT 120 * USEC_PER_SEC
@ -71,6 +73,7 @@ enum {
enum DHCP6State {
DHCP6_STATE_STOPPED = 0,
DHCP6_STATE_INFORMATION_REQUEST = 1,
DHCP6_STATE_SOLICITATION = 2,
DHCP6_STATE_REQUEST = 3,
DHCP6_STATE_BOUND = 4,

View file

@ -62,6 +62,7 @@ struct sd_dhcp6_client {
usec_t transaction_start;
struct sd_dhcp6_lease *lease;
int fd;
bool information_request;
be16_t *req_opts;
size_t req_opts_allocated;
size_t req_opts_len;
@ -227,6 +228,25 @@ int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *du
return 0;
}
int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
bool enabled) {
assert_return(client, -EINVAL);
client->information_request = enabled;
return 0;
}
int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
bool *enabled) {
assert_return(client, -EINVAL);
assert_return(enabled, -EINVAL);
*enabled = client->information_request;
return 0;
}
int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
uint16_t option) {
size_t t;
@ -333,6 +353,11 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
message->transaction_id = client->transaction_id;
switch(client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
message->type = DHCP6_INFORMATION_REQUEST;
break;
case DHCP6_STATE_SOLICITATION:
message->type = DHCP6_SOLICIT;
@ -494,6 +519,12 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
switch (client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
init_retransmit_time = DHCP6_INF_TIMEOUT;
max_retransmit_time = DHCP6_INF_MAX_RT;
break;
case DHCP6_STATE_SOLICITATION:
if (client->retransmit_count && client->lease) {
@ -744,6 +775,12 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
case DHCP6_OPTION_IA_NA:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
log_dhcp6_client(client, "Information request ignoring IA NA option");
break;
}
r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
&lease->ia);
if (r < 0 && r != -ENOMSG)
@ -779,10 +816,12 @@ static int client_parse_message(sd_dhcp6_client *client,
return -EINVAL;
}
r = dhcp6_lease_get_serverid(lease, &id, &id_len);
if (r < 0)
log_dhcp6_client(client, "%s has no server id",
dhcp6_message_type_to_string(message->type));
if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
r = dhcp6_lease_get_serverid(lease, &id, &id_len);
if (r < 0)
log_dhcp6_client(client, "%s has no server id",
dhcp6_message_type_to_string(message->type));
}
return r;
}
@ -814,12 +853,15 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
return 0;
}
if (client->lease)
if (client->lease) {
dhcp6_lease_clear_timers(&client->lease->ia);
client->lease = sd_dhcp6_lease_unref(client->lease);
}
client->lease = sd_dhcp6_lease_unref(client->lease);
client->lease = lease;
lease = NULL;
if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
client->lease = lease;
lease = NULL;
}
return DHCP6_STATE_BOUND;
}
@ -846,7 +888,8 @@ static int client_receive_advertise(sd_dhcp6_client *client,
return r;
r = dhcp6_lease_get_preference(client->lease, &pref_lease);
if (!client->lease || r < 0 || pref_advertise > pref_lease) {
if (r < 0 || pref_advertise > pref_lease) {
sd_dhcp6_lease_unref(client->lease);
client->lease = lease;
lease = NULL;
@ -913,6 +956,17 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
return 0;
switch (client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
r = client_receive_reply(client, message, len);
if (r < 0)
return 0;
client_notify(client, DHCP6_EVENT_INFORMATION_REQUEST);
client_start(client, DHCP6_STATE_STOPPED);
break;
case DHCP6_STATE_SOLICITATION:
r = client_receive_advertise(client, message, len);
@ -988,37 +1042,19 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
switch (state) {
case DHCP6_STATE_STOPPED:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
client->state = DHCP6_STATE_STOPPED;
return 0;
}
/* fall through */
case DHCP6_STATE_SOLICITATION:
r = client_ensure_iaid(client);
if (r < 0)
return r;
r = dhcp6_network_bind_udp_socket(client->index, NULL);
if (r < 0)
return r;
client->fd = r;
r = sd_event_add_io(client->event, &client->receive_message,
client->fd, EPOLLIN, client_receive_message,
client);
if (r < 0)
return r;
r = sd_event_source_set_priority(client->receive_message,
client->event_priority);
if (r < 0)
return r;
r = sd_event_source_set_description(client->receive_message, "dhcp6-receive-message");
if (r < 0)
return r;
client->state = DHCP6_STATE_SOLICITATION;
break;
case DHCP6_STATE_INFORMATION_REQUEST:
case DHCP6_STATE_REQUEST:
case DHCP6_STATE_RENEW:
case DHCP6_STATE_REBIND:
@ -1123,6 +1159,7 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client)
int sd_dhcp6_client_start(sd_dhcp6_client *client)
{
int r = 0;
enum DHCP6State state = DHCP6_STATE_SOLICITATION;
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
@ -1132,7 +1169,44 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client)
if (r < 0)
return r;
return client_start(client, DHCP6_STATE_SOLICITATION);
r = client_ensure_iaid(client);
if (r < 0)
return r;
r = dhcp6_network_bind_udp_socket(client->index, NULL);
if (r < 0)
return r;
client->fd = r;
r = sd_event_add_io(client->event, &client->receive_message,
client->fd, EPOLLIN, client_receive_message,
client);
if (r < 0)
goto error;
r = sd_event_source_set_priority(client->receive_message,
client->event_priority);
if (r < 0)
goto error;
r = sd_event_source_set_description(client->receive_message,
"dhcp6-receive-message");
if (r < 0)
goto error;
if (client->information_request)
state = DHCP6_STATE_INFORMATION_REQUEST;
log_dhcp6_client(client, "Started in %s mode",
client->information_request? "Information request":
"Managed");
return client_start(client, state);
error:
client_reset(client);
return r;
}
int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,

View file

@ -33,6 +33,7 @@ enum {
DHCP6_EVENT_RESEND_EXPIRE = 10,
DHCP6_EVENT_RETRANS_MAX = 11,
DHCP6_EVENT_IP_ACQUIRE = 12,
DHCP6_EVENT_INFORMATION_REQUEST = 13,
};
typedef struct sd_dhcp6_client sd_dhcp6_client;
@ -47,6 +48,10 @@ int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
size_t addr_len, uint16_t arp_type);
int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
size_t duid_len);
int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
bool enabled);
int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
bool *enabled);
int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
uint16_t option);