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",
DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
+#define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
+
+#define DHCP6_CLIENT_DONT_DESTROY(client) \
+ _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
+
static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
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);
return 0;
}
-static sd_dhcp6_client *client_notify(sd_dhcp6_client *client, int event) {
- if (client->cb) {
- client = sd_dhcp6_client_ref(client);
+static void client_notify(sd_dhcp6_client *client, int event) {
+ if (client->cb)
client->cb(client, event, client->userdata);
- client = sd_dhcp6_client_unref(client);
- }
-
- return client;
}
static int client_reset(sd_dhcp6_client *client) {
return 0;
}
-static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) {
- assert_return(client, NULL);
+static void client_stop(sd_dhcp6_client *client, int error) {
+ DHCP6_CLIENT_DONT_DESTROY(client);
- client = client_notify(client, error);
- if (client)
- client_reset(client);
+ assert(client);
- return client;
+ client_notify(client, error);
+
+ client_reset(client);
}
static int client_send_message(sd_dhcp6_client *client) {
case DHCP6_STATE_SOLICITATION:
message->type = DHCP6_SOLICIT;
+ r = dhcp6_option_append(&opt, &optlen,
+ DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
+
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;
}
usec_t max_retransmit_duration;
uint8_t max_retransmit_count = 0;
char time_string[FORMAT_TIMESPAN_MAX];
+ uint32_t expire = 0;
assert(s);
assert(client);
break;
+ case DHCP6_STATE_RENEW:
+ init_retransmit_time = DHCP6_REN_TIMEOUT;
+ max_retransmit_time = DHCP6_REN_MAX_RT;
+ max_retransmit_count = 0;
+
+ /* 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 */
+ max_retransmit_duration = 0;
+
+ break;
+
+ case DHCP6_STATE_REBIND:
+ init_retransmit_time = DHCP6_REB_TIMEOUT;
+ max_retransmit_time = DHCP6_REB_MAX_RT;
+ max_retransmit_count = 0;
+
+ max_retransmit_duration = 0;
+
+ 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:
}
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;
+ 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;
+ }
+
dhcp6_lease_clear_timers(&client->lease->ia);
client->lease = sd_dhcp6_lease_unref(client->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)
static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
void *userdata) {
sd_dhcp6_client *client = userdata;
+ DHCP6_CLIENT_DONT_DESTROY(client);
_cleanup_free_ DHCP6Message *message;
int r, buflen, len;
len = read(fd, message, buflen);
if ((size_t)len < sizeof(DHCP6Message)) {
- log_dhcp6_client(client, "could not receive message from UDP socket: %s", strerror(errno));
+ log_dhcp6_client(client, "could not receive message from UDP socket: %m");
return 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;
return 0;
}
- client = client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
- if (!client)
- return 0;
+ client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
}
break;
break;
case DHCP6_STATE_REQUEST:
+ case DHCP6_STATE_RENEW:
+ case DHCP6_STATE_REBIND:
client->state = state;
if (r < 0)
return r;
+ client->state = state;
+
return 0;
}
sd_dhcp6_client_detach_event(client);
+ free(client->req_opts);
free(client);
return NULL;
return client;
}
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
-#define _cleanup_dhcp6_client_free_ _cleanup_(sd_dhcp6_client_unrefp)
-
int sd_dhcp6_client_new(sd_dhcp6_client **ret)
{
- _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = 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;