sd-dhcp6-client.c revision 2c1ab8ca9b5dec48c66eb25dd8af71731e70e517
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering This file is part of systemd.
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering systemd is free software; you can redistribute it and/or modify it
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering under the terms of the GNU Lesser General Public License as published by
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering (at your option) any later version.
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering systemd is distributed in the hope that it will be useful, but
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering Lesser General Public License for more details.
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering You should have received a copy of the GNU Lesser General Public License
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringstatic const uint16_t default_req_opts[] = {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringconst char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart PoetteringDEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringconst char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart PoetteringDEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering#define DHCP6_CLIENT_DONT_DESTROY(client) \
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringstatic int client_start(sd_dhcp6_client *client, enum DHCP6State state);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringint sd_dhcp6_client_set_callback(sd_dhcp6_client *client, sd_dhcp6_client_cb_t cb, void *userdata) {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringint sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering assert_return(interface_index >= -1, -EINVAL);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringint sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address) {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
507f22bd0172bff5e5d98145b1419bd472a2c57fZbigniew Jędrzejewski-Szmek assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
507f22bd0172bff5e5d98145b1419bd472a2c57fZbigniew Jędrzejewski-Szmek assert_return(arp_type > 0, -EINVAL);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering assert_return(addr_len == ETH_ALEN, -EINVAL);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering memcmp(&client->mac_addr, addr, addr_len) == 0)
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering memcpy(&client->mac_addr, addr, addr_len);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringstatic int client_ensure_duid(sd_dhcp6_client *client) {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering if (duid_len <= sizeof(client->duid.llt))
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering if (duid_len != sizeof(client->duid.uuid))
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering /* accept unknown type in order to be forward compatible */
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering memcpy(&client->duid.raw.data, duid, duid_len);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering client->duid_len = duid_len + sizeof(client->duid.type);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringint sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringint sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringint sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
4b94f3b8f7693f076e5c85bc2c02cf028192d8deZbigniew Jędrzejewski-Szmek for (t = 0; t < client->req_opts_len; t++)
4b94f3b8f7693f076e5c85bc2c02cf028192d8deZbigniew Jędrzejewski-Szmek if (client->req_opts[t] == htobe16(option))
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering client->req_opts[client->req_opts_len++] = htobe16(option);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringint sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringstatic void client_notify(sd_dhcp6_client *client, int event) {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering client->cb(client, event, client->userdata);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringstatic void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering dhcp6_lease_clear_timers(&client->lease->ia);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringstatic int client_reset(sd_dhcp6_client *client) {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering sd_event_source_unref(client->receive_message);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering sd_event_source_unref(client->ia_na.timeout_t1);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering sd_event_source_unref(client->ia_na.timeout_t2);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering client->timeout_resend = sd_event_source_unref(client->timeout_resend);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering sd_event_source_unref(client->timeout_resend_expire);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringstatic void client_stop(sd_dhcp6_client *client, int error) {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringstatic int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering _cleanup_free_ DHCP6Message *message = NULL;
f9a810bedacf1da7c505c1786a2416d592665926Lennart Poettering IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering message->transaction_id = client->transaction_id;
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering message->type = DHCP6_INFORMATION_REQUEST;
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
f9a810bedacf1da7c505c1786a2416d592665926Lennart Poettering if (client->state == DHCP6_STATE_REQUEST)
f9a810bedacf1da7c505c1786a2416d592665926Lennart Poettering r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
f9a810bedacf1da7c505c1786a2416d592665926Lennart Poettering r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
f9a810bedacf1da7c505c1786a2416d592665926Lennart Poettering r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
f9a810bedacf1da7c505c1786a2416d592665926Lennart Poettering elapsed_usec = time_now - client->transaction_start;
f9a810bedacf1da7c505c1786a2416d592665926Lennart Poettering if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
c0f71f469fef3f3a0822e0021085e6d165df2b46Lennart Poettering elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
a69f4254a82765cd0c7f155d5dc86e0768ea0ef3Lennart Poettering r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
c0f71f469fef3f3a0822e0021085e6d165df2b46Lennart Poettering dhcp6_message_type_to_string(message->type));
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringstatic int client_timeout_t2(sd_event_source *s, uint64_t usec,
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering sd_event_source_unref(client->lease->ia.timeout_t2);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering client_start(client, DHCP6_STATE_REBIND);
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poetteringstatic int client_timeout_t1(sd_event_source *s, uint64_t usec,
ef63833d532dd86bdba63211e6a1363cbb3ef61dLennart Poettering sd_event_source_unref(client->lease->ia.timeout_t1);
void *userdata) {
assert(s);
void *userdata) {
assert(s);
case DHCP6_STATE_SOLICITATION:
case DHCP6_STATE_REQUEST:
case DHCP6_STATE_RENEW:
case DHCP6_STATE_REBIND:
&expire);
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_BOUND:
if (max_retransmit_count &&
goto error;
if (max_retransmit_time &&
client);
goto error;
goto error;
goto error;
goto error;
goto error;
goto error;
r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
bool clientid = false;
&optval)) >= 0) {
switch (optcode) {
case SD_DHCP6_OPTION_CLIENTID:
if (clientid) {
return -EINVAL;
return -EINVAL;
clientid = true;
case SD_DHCP6_OPTION_SERVERID:
if (r >= 0 && id) {
return -EINVAL;
return -EINVAL;
return -EINVAL;
if (status) {
return -EINVAL;
case SD_DHCP6_OPTION_IA_NA:
if (r < 0 && r != -ENOMSG)
return -EINVAL;
if (r == -ENOMSG)
if (r < 0 || !clientid) {
return -EINVAL;
bool rapid_commit;
return -ENOMEM;
if (!rapid_commit)
return DHCP6_STATE_BOUND;
r = DHCP6_STATE_REQUEST;
assert(s);
return -errno;
else if (buflen < 0)
return -EIO;
if (!message)
return -ENOMEM;
if (len < 0) {
return -errno;
case DHCP6_SOLICIT:
case DHCP6_REQUEST:
case DHCP6_CONFIRM:
case DHCP6_RENEW:
case DHCP6_REBIND:
case DHCP6_RELEASE:
case DHCP6_DECLINE:
case DHCP6_RELAY_FORW:
case DHCP6_RELAY_REPL:
case DHCP6_ADVERTISE:
case DHCP6_REPLY:
case DHCP6_RECONFIGURE:
case DHCP6_STATE_SOLICITATION:
if (r == DHCP6_STATE_REQUEST) {
case DHCP6_STATE_REQUEST:
case DHCP6_STATE_RENEW:
case DHCP6_STATE_REBIND:
if (r == DHCP6_STATE_BOUND) {
case DHCP6_STATE_BOUND:
case DHCP6_STATE_STOPPED:
switch (state) {
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_SOLICITATION:
case DHCP6_STATE_REQUEST:
case DHCP6_STATE_RENEW:
case DHCP6_STATE_REBIND:
case DHCP6_STATE_BOUND:
client);
client);
client);
assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
return -EBUSY;
client);
goto error;
goto error;
goto error;
if (event)
if (!client)
return NULL;
if (!client)
return NULL;
return client;
if (!client)
return NULL;
return NULL;
return NULL;
size_t t;
if (!client)
return -ENOMEM;
return -ENOMEM;