sd-dhcp6-client.c revision 3f0c075f8ef3344da5a6bda524540201f9204e61
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering This file is part of systemd.
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering Copyright (C) 2014 Intel Corporation. All rights reserved.
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering systemd is free software; you can redistribute it and/or modify it
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering under the terms of the GNU Lesser General Public License as published by
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering (at your option) any later version.
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering systemd is distributed in the hope that it will be useful, but
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering Lesser General Public License for more details.
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering You should have received a copy of the GNU Lesser General Public License
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringconst char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart PoetteringDEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringconst char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart PoetteringDEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart PoetteringDEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#define DHCP6_CLIENT_DONT_DESTROY(client) \
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
634af5665fda8776d22624d947c8de830e30a874Lennart Poetteringstatic int client_start(sd_dhcp6_client *client, enum DHCP6State state);
634af5665fda8776d22624d947c8de830e30a874Lennart Poetteringint sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringint sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert_return(interface_index >= -1, -EINVAL);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringint sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringint sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering *ret = sd_dhcp6_lease_ref(client->lease);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic void client_notify(sd_dhcp6_client *client, int event) {
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering client->cb(client, event, client->userdata);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic int client_reset(sd_dhcp6_client *client) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_event_source_unref(client->receive_message);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_event_source_unref(client->ia_na.timeout_t1);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_event_source_unref(client->ia_na.timeout_t2);
555bd6e95bf2b469306d9cd86e126c0122d0895bLennart Poettering client->timeout_resend = sd_event_source_unref(client->timeout_resend);
9d485985338a46b8cb1acdf1af6c1eb2e88acfeeLennart Poettering sd_event_source_unref(client->timeout_resend_expire);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic void client_stop(sd_dhcp6_client *client, int error) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic int client_send_message(sd_dhcp6_client *client) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering _cleanup_free_ DHCP6Message *message = NULL;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
e70df46b9721a3d025e7a0b4ffb5893cbde5e55dLennart Poettering message->transaction_id = client->transaction_id;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering dhcp6_message_type_to_string(message->type));
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic int client_timeout_t2(sd_event_source *s, uint64_t usec,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_event_source_unref(client->lease->ia.timeout_t2);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic int client_timeout_t1(sd_event_source *s, uint64_t usec,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_event_source_unref(client->lease->ia.timeout_t1);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic usec_t client_timeout_compute_random(usec_t val) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic int client_timeout_resend(sd_event_source *s, uint64_t usec,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering usec_t time_now, init_retransmit_time, max_retransmit_time;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->timeout_resend = sd_event_source_unref(client->timeout_resend);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (client->retransmit_count && client->lease) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client_start(client, DHCP6_STATE_REQUEST);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering init_retransmit_time = DHCP6_SOL_TIMEOUT;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering init_retransmit_time = DHCP6_REQ_TIMEOUT;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->retransmit_count >= max_retransmit_count) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client_stop(client, DHCP6_EVENT_RETRANS_MAX);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client_timeout_compute_random(init_retransmit_time);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (client->state == DHCP6_STATE_SOLICITATION)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->retransmit_time += init_retransmit_time / 10;
e70df46b9721a3d025e7a0b4ffb5893cbde5e55dLennart Poettering client->retransmit_time > max_retransmit_time / 2)
e70df46b9721a3d025e7a0b4ffb5893cbde5e55dLennart Poettering client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering log_dhcp6_client(client, "Next retransmission in %s",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering format_timespan(time_string, FORMAT_TIMESPAN_MAX,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = sd_event_add_time(client->event, &client->timeout_resend,
client);
goto error;
goto error;
goto error;
goto error;
if (!udev)
return -ENOMEM;
if (!device)
return -errno;
return -EBUSY;
if (name)
bool clientid = false;
&optval)) >= 0) {
switch (optcode) {
case DHCP6_OPTION_CLIENTID:
if (clientid) {
return -EINVAL;
return -EINVAL;
clientid = true;
case DHCP6_OPTION_SERVERID:
if (r >= 0 && id) {
return -EINVAL;
case DHCP6_OPTION_PREFERENCE:
return -EINVAL;
case DHCP6_OPTION_STATUS_CODE:
return -EINVAL;
if (status) {
return -EINVAL;
case DHCP6_OPTION_IA_NA:
if (r < 0 && r != -ENOMSG)
return -EINVAL;
return -EINVAL;
return -EINVAL;
return -ENOMEM;
return DHCP6_STATE_BOUND;
return -EINVAL;
r = DHCP6_STATE_REQUEST;
void *userdata) {
assert(s);
if (r < 0 || buflen <= 0)
if (!message)
return -ENOMEM;
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:
if (r == DHCP6_STATE_BOUND) {
case DHCP6_STATE_BOUND:
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_RS:
switch (state) {
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_RS:
case DHCP6_STATE_SOLICITATION:
client);
case DHCP6_STATE_REQUEST:
case DHCP6_STATE_BOUND:
timeout, 0));
client);
timeout, 0));
client);
client);
int priority)
if (event)
if (!client)
return NULL;
if (client)
return client;
return NULL;
return client;
if (!client)
return -ENOMEM;