be32_t transaction_id;
struct sd_dhcp6_lease *lease;
int fd;
+ be16_t *req_opts;
+ size_t req_opts_allocated;
+ size_t req_opts_len;
sd_event_source *receive_message;
usec_t retransmit_time;
uint8_t retransmit_count;
} _packed_ duid;
};
+static const uint16_t default_req_opts[] = {
+ DHCP6_OPTION_DNS_SERVERS,
+ DHCP6_OPTION_DOMAIN_LIST,
+ DHCP6_OPTION_NTP_SERVER,
+};
+
const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
[DHCP6_SOLICIT] = "SOLICIT",
[DHCP6_ADVERTISE] = "ADVERTISE",
return 0;
}
+int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
+ uint16_t option) {
+ size_t t;
+
+ assert_return(client, -EINVAL);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
+
+ switch(option) {
+ case DHCP6_OPTION_DNS_SERVERS:
+ case DHCP6_OPTION_DOMAIN_LIST:
+ case DHCP6_OPTION_SNTP_SERVERS:
+ case DHCP6_OPTION_NTP_SERVER:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ for (t = 0; t < client->req_opts_len; t++)
+ if (client->req_opts[t] == htobe16(option))
+ return -EEXIST;
+
+ if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
+ client->req_opts_len + 1))
+ return -ENOMEM;
+
+ client->req_opts[client->req_opts_len++] = htobe16(option);
+
+ return 0;
+}
+
int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
assert_return(client, -EINVAL);
assert_return(ret, -EINVAL);
case DHCP6_STATE_SOLICITATION:
message->type = DHCP6_SOLICIT;
+ r = dhcp6_option_append(&opt, &optlen,
+ DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
+ if (r < 0)
+ return r;
+
r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
if (r < 0)
return r;
break;
case DHCP6_STATE_REQUEST:
- message->type = DHCP6_REQUEST;
+ case DHCP6_STATE_RENEW:
+
+ if (client->state == DHCP6_STATE_REQUEST)
+ message->type = DHCP6_REQUEST;
+ else
+ message->type = DHCP6_RENEW;
r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
client->lease->serverid_len,
break;
+ case DHCP6_STATE_REBIND:
+ message->type = DHCP6_REBIND;
+
+ r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
+ if (r < 0)
+ return r;
+
+ break;
+
case DHCP6_STATE_STOPPED:
- case DHCP6_STATE_RS:
case DHCP6_STATE_BOUND:
return -EINVAL;
}
+ r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
+ client->req_opts_len * sizeof(be16_t),
+ client->req_opts);
+ if (r < 0)
+ return r;
+
r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
sizeof(client->duid), &client->duid);
if (r < 0)
log_dhcp6_client(client, "Timeout T2");
+ client_start(client, DHCP6_STATE_REBIND);
+
return 0;
}
log_dhcp6_client(client, "Timeout T1");
+ client_start(client, DHCP6_STATE_RENEW);
+
return 0;
}
static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
void *userdata) {
sd_dhcp6_client *client = userdata;
+ DHCP6_CLIENT_DONT_DESTROY(client);
+ enum DHCP6State state;
assert(s);
assert(client);
assert(client->event);
+ state = client->state;
+
client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
+ /* RFC 3315, section 18.1.4., says that "...the client may choose to
+ use a Solicit message to locate a new DHCP server..." */
+ if (state == DHCP6_STATE_REBIND)
+ client_start(client, DHCP6_STATE_SOLICITATION);
+
return 0;
}
void *userdata) {
int r = 0;
sd_dhcp6_client *client = userdata;
- usec_t time_now, init_retransmit_time, max_retransmit_time;
- usec_t max_retransmit_duration;
+ usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
+ usec_t max_retransmit_duration = 0;
uint8_t max_retransmit_count = 0;
char time_string[FORMAT_TIMESPAN_MAX];
+ uint32_t expire = 0;
assert(s);
assert(client);
init_retransmit_time = DHCP6_SOL_TIMEOUT;
max_retransmit_time = DHCP6_SOL_MAX_RT;
- max_retransmit_count = 0;
- max_retransmit_duration = 0;
break;
init_retransmit_time = DHCP6_REQ_TIMEOUT;
max_retransmit_time = DHCP6_REQ_MAX_RT;
max_retransmit_count = DHCP6_REQ_MAX_RC;
- max_retransmit_duration = 0;
+
+ break;
+
+ case DHCP6_STATE_RENEW:
+ init_retransmit_time = DHCP6_REN_TIMEOUT;
+ max_retransmit_time = DHCP6_REN_MAX_RT;
+
+ /* RFC 3315, section 18.1.3. says max retransmit duration will
+ be the remaining time until T2. Instead of setting MRD,
+ wait for T2 to trigger with the same end result */
+
+ break;
+
+ case DHCP6_STATE_REBIND:
+ init_retransmit_time = DHCP6_REB_TIMEOUT;
+ max_retransmit_time = DHCP6_REB_MAX_RT;
+
+ if (!client->timeout_resend_expire) {
+ r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
+ &expire);
+ if (r < 0) {
+ client_stop(client, r);
+ return 0;
+ }
+ max_retransmit_duration = expire * USEC_PER_SEC;
+ }
break;
case DHCP6_STATE_STOPPED:
- case DHCP6_STATE_RS:
case DHCP6_STATE_BOUND:
return 0;
}
client->retransmit_count++;
- r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
+ r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
goto error;
client->retransmit_time, 0));
r = sd_event_add_time(client->event, &client->timeout_resend,
- CLOCK_MONOTONIC,
+ clock_boottime_or_monotonic(),
time_now + client->retransmit_time,
10 * USEC_PER_MSEC, client_timeout_resend,
client);
r = sd_event_add_time(client->event,
&client->timeout_resend_expire,
- CLOCK_MONOTONIC,
+ clock_boottime_or_monotonic(),
time_now + max_retransmit_duration,
USEC_PER_SEC,
client_timeout_resend_expire, client);
}
static int client_ensure_iaid(sd_dhcp6_client *client) {
+ /* name is a pointer to memory in the udev_device struct, so must
+ have the same scope */
+ _cleanup_udev_device_unref_ struct udev_device *device = NULL;
const char *name = NULL;
uint64_t id;
if (detect_container(NULL) <= 0) {
/* not in a container, udev will be around */
_cleanup_udev_unref_ struct udev *udev;
- _cleanup_udev_device_unref_ struct udev_device *device = NULL;
char ifindex_str[2 + DECIMAL_STR_MAX(int)];
udev = udev_new();
}
break;
+
+ case DHCP6_OPTION_RAPID_COMMIT:
+ r = dhcp6_lease_set_rapid_commit(lease);
+ if (r < 0)
+ return r;
+
+ break;
}
}
{
int r;
_cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+ bool rapid_commit;
if (reply->type != DHCP6_REPLY)
- return -EINVAL;
+ return 0;
r = dhcp6_lease_new(&lease);
if (r < 0)
if (r < 0)
return r;
- dhcp6_lease_clear_timers(&client->lease->ia);
+ if (client->state == DHCP6_STATE_SOLICITATION) {
+ r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
+ if (r < 0)
+ return r;
+
+ if (!rapid_commit)
+ return 0;
+ }
+
+ if (client->lease)
+ dhcp6_lease_clear_timers(&client->lease->ia);
client->lease = sd_dhcp6_lease_unref(client->lease);
client->lease = lease;
uint8_t pref_advertise = 0, pref_lease = 0;
if (advertise->type != DHCP6_ADVERTISE)
- return -EINVAL;
+ return 0;
r = dhcp6_lease_new(&lease);
if (r < 0)
case DHCP6_STATE_SOLICITATION:
r = client_receive_advertise(client, message, len);
- if (r == DHCP6_STATE_REQUEST)
+ if (r == DHCP6_STATE_REQUEST) {
client_start(client, r);
- break;
+ break;
+ }
+ /* fall through for Soliciation Rapid Commit option check */
case DHCP6_STATE_REQUEST:
+ case DHCP6_STATE_RENEW:
+ case DHCP6_STATE_REBIND:
+
r = client_receive_reply(client, message, len);
if (r < 0)
return 0;
break;
case DHCP6_STATE_STOPPED:
- case DHCP6_STATE_RS:
return 0;
}
switch (state) {
case DHCP6_STATE_STOPPED:
- case DHCP6_STATE_RS:
case DHCP6_STATE_SOLICITATION:
r = client_ensure_iaid(client);
break;
case DHCP6_STATE_REQUEST:
+ case DHCP6_STATE_RENEW:
+ case DHCP6_STATE_REBIND:
client->state = state;
case DHCP6_STATE_BOUND:
- r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
+ r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
return r;
r = sd_event_add_time(client->event,
&client->lease->ia.timeout_t1,
- CLOCK_MONOTONIC, time_now + timeout,
+ clock_boottime_or_monotonic(), time_now + timeout,
10 * USEC_PER_SEC, client_timeout_t1,
client);
if (r < 0)
r = sd_event_add_time(client->event,
&client->lease->ia.timeout_t2,
- CLOCK_MONOTONIC, time_now + timeout,
+ clock_boottime_or_monotonic(), time_now + timeout,
10 * USEC_PER_SEC, client_timeout_t2,
client);
if (r < 0)
if (r < 0)
return r;
+ client->state = state;
+
return 0;
}
client->transaction_id = random_u32() & htobe32(0x00ffffff);
r = sd_event_add_time(client->event, &client->timeout_resend,
- CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
+ clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
client);
if (r < 0)
return r;
sd_dhcp6_client_detach_event(client);
+ free(client->req_opts);
free(client);
return NULL;
_cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
sd_id128_t machine_id;
int r;
+ size_t t;
assert_return(ret, -EINVAL);
siphash24(client->duid.id, &machine_id, sizeof(machine_id),
HASH_KEY.bytes);
+ client->req_opts_len = ELEMENTSOF(default_req_opts);
+
+ client->req_opts = new0(be16_t, client->req_opts_len);
+ if (!client->req_opts)
+ return -ENOMEM;
+
+ for (t = 0; t < client->req_opts_len; t++)
+ client->req_opts[t] = htobe16(default_req_opts[t]);
+
*ret = client;
client = NULL;