sd-dhcp6-client.c revision 76253e73f9c9c24fec755e485516f3b55d0707b4
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer This file is part of systemd.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer Copyright (C) 2014 Intel Corporation. All rights reserved.
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer systemd is free software; you can redistribute it and/or modify it
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer under the terms of the GNU Lesser General Public License as published by
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer the Free Software Foundation; either version 2.1 of the License, or
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer (at your option) any later version.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer systemd is distributed in the hope that it will be useful, but
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer WITHOUT ANY WARRANTY; without even the implied warranty of
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer Lesser General Public License for more details.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer You should have received a copy of the GNU Lesser General Public License
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer along with systemd; If not, see <http://www.gnu.org/licenses/>.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer/* RFC 3315 section 9.1:
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * A DUID can be no more than 128 octets long (not including the type code).
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerconst char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald HoyerDEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerconst char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald HoyerDEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald HoyerDEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer#define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_start(sd_dhcp6_client *client, enum DHCP6State state);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer memcmp(&client->mac_addr, addr, addr_len) == 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* accept unknown type in order to be forward compatible */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer memcpy(&client->duid.raw.data, duid, duid_len);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer client->req_opts[client->req_opts_len++] = htobe16(option);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic void client_notify(sd_dhcp6_client *client, int event) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_reset(sd_dhcp6_client *client) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer sd_event_source_unref(client->receive_message);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer sd_event_source_unref(client->ia_na.timeout_t1);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer sd_event_source_unref(client->ia_na.timeout_t2);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer client->timeout_resend = sd_event_source_unref(client->timeout_resend);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer sd_event_source_unref(client->timeout_resend_expire);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic void client_stop(sd_dhcp6_client *client, int error) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer message->transaction_id = client->transaction_id;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer elapsed_usec = time_now - client->transaction_start;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_timeout_t2(sd_event_source *s, uint64_t usec,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer sd_event_source_unref(client->lease->ia.timeout_t2);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_timeout_t1(sd_event_source *s, uint64_t usec,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer sd_event_source_unref(client->lease->ia.timeout_t1);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* RFC 3315, section 18.1.4., says that "...the client may choose to
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer use a Solicit message to locate a new DHCP server..." */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer client_start(client, DHCP6_STATE_SOLICITATION);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic usec_t client_timeout_compute_random(usec_t val) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_timeout_resend(sd_event_source *s, uint64_t usec,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer client->timeout_resend = sd_event_source_unref(client->timeout_resend);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (client->retransmit_count && client->lease) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* RFC 3315, section 18.1.3. says max retransmit duration will
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer be the remaining time until T2. Instead of setting MRD,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer wait for T2 to trigger with the same end result */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer max_retransmit_duration = expire * USEC_PER_SEC;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer client->retransmit_count >= max_retransmit_count) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer client_timeout_compute_random(init_retransmit_time);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer client->retransmit_time += init_retransmit_time / 10;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer client->retransmit_time > max_retransmit_time / 2)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer log_dhcp6_client(client, "Next retransmission in %s",
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer format_timespan(time_string, FORMAT_TIMESPAN_MAX,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = sd_event_add_time(client->event, &client->timeout_resend,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = sd_event_source_set_priority(client->timeout_resend,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = sd_event_source_set_name(client->timeout_resend,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer "dhcp6-resend-timer");
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (max_retransmit_duration && !client->timeout_resend_expire) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = sd_event_source_set_priority(client->timeout_resend_expire,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = sd_event_source_set_name(client->timeout_resend_expire,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer "dhcp6-resend-expire-timer");
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerstatic int client_ensure_iaid(sd_dhcp6_client *client) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* name is a pointer to memory in the udev_device struct, so must
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer have the same scope */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer _cleanup_udev_device_unref_ struct udev_device *device = NULL;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* not in a container, udev will be around */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer device = udev_device_new_from_device_id(udev, ifindex_str);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (udev_device_get_is_initialized(device) <= 0)
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* not yet ready */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* fall back to mac address if no predictable name available */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* fold into 32 bits */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerstatic int client_parse_message(sd_dhcp6_client *client,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer bool clientid = false;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer option = (uint8_t *)message + sizeof(DHCP6Message);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer log_dhcp6_client(client, "%s contains multiple clientids",
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer log_dhcp6_client(client, "%s DUID does not match",
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = dhcp6_lease_get_serverid(lease, &id, &id_len);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (r >= 0 && id) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer log_dhcp6_client(client, "%s contains multiple serverids",
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = dhcp6_lease_set_serverid(lease, optval, optlen);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = dhcp6_lease_set_preference(lease, *optval);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (r < 0 && r != -ENOMSG)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_dhcp6_client(client, "%s has incomplete options",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp6_lease_get_serverid(lease, &id, &id_len);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_dhcp6_client(client, "%s has no server id",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
1ecf6a2b4960229ad1d06c591b4776ddf065e834Harald Hoyer _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
33a5e20ffaa2cbb2853f14265566bac66a7f9026Harald Hoyer r = client_parse_message(client, reply, len, lease);
33a5e20ffaa2cbb2853f14265566bac66a7f9026Harald Hoyer if (client->state == DHCP6_STATE_SOLICITATION) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer client->lease = sd_dhcp6_lease_unref(client->lease);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_receive_advertise(sd_dhcp6_client *client,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = client_parse_message(client, advertise, len, lease);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp6_lease_get_preference(lease, &pref_advertise);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp6_lease_get_preference(client->lease, &pref_lease);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (!client->lease || r < 0 || pref_advertise > pref_lease) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (pref_advertise == 255 || client->retransmit_count > 1)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
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:
case DHCP6_STATE_RENEW:
case DHCP6_STATE_REBIND:
if (r == DHCP6_STATE_BOUND) {
case DHCP6_STATE_BOUND:
case DHCP6_STATE_STOPPED:
&time_now);
switch (state) {
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_SOLICITATION:
client);
case DHCP6_STATE_REQUEST:
case DHCP6_STATE_RENEW:
case DHCP6_STATE_REBIND:
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;
size_t t;
if (!client)
return -ENOMEM;
return -ENOMEM;