sd-dhcp6-client.c revision 513a6fa8679510ea1b55967bdb482dd5f8a39f21
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering This file is part of systemd.
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering Copyright (C) 2014 Intel Corporation. All rights reserved.
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering systemd is free software; you can redistribute it and/or modify it
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering under the terms of the GNU Lesser General Public License as published by
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering (at your option) any later version.
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering systemd is distributed in the hope that it will be useful, but
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering Lesser General Public License for more details.
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering You should have received a copy of the GNU Lesser General Public License
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringconst char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart PoetteringDEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringconst char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart PoetteringDEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringstatic int client_start(sd_dhcp6_client *client, enum DHCP6State state);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringint sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sieversint sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sieversint sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sieversint sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringstatic sd_dhcp6_client *client_notify(sd_dhcp6_client *client, int event) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering client->cb(client, event, client->userdata);
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sieversstatic int client_reset(sd_dhcp6_client *client) {
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers sd_event_source_unref(client->ia_na.timeout_t1);
e1636421f46db6d06fbd028ef20a3113fa3e11f8Lennart Poettering sd_event_source_unref(client->ia_na.timeout_t2);
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers client->timeout_resend = sd_event_source_unref(client->timeout_resend);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering sd_event_source_unref(client->timeout_resend_expire);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringstatic sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringstatic int client_send_message(sd_dhcp6_client *client) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering _cleanup_free_ DHCP6Message *message = NULL;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering message->transaction_id = client->transaction_id;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering dhcp6_message_type_to_string(message->type));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringstatic int client_timeout_t2(sd_event_source *s, uint64_t usec,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering sd_event_source_unref(client->lease->ia.timeout_t2);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringstatic int client_timeout_t1(sd_event_source *s, uint64_t usec,
ef42202ac8ed27e7ff1fc90ef8bc2590046dff25Zbigniew Jędrzejewski-Szmek assert_return(client->lease, -EINVAL);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering sd_event_source_unref(client->lease->ia.timeout_t1);
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagnastatic int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagna client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagnastatic usec_t client_timeout_compute_random(usec_t val) {
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagna (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagnastatic int client_timeout_resend(sd_event_source *s, uint64_t usec,
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagna usec_t time_now, init_retransmit_time, max_retransmit_time;
ef42202ac8ed27e7ff1fc90ef8bc2590046dff25Zbigniew Jędrzejewski-Szmek client->timeout_resend = sd_event_source_unref(client->timeout_resend);
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagna if (client->retransmit_count && client->lease) {
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagna client_start(client, DHCP6_STATE_REQUEST);
bac3c8eefe23a820caac930d41629cebafbfc7b2Zbigniew Jędrzejewski-Szmek max_retransmit_time = DHCP6_SOL_MAX_RT;
bac3c8eefe23a820caac930d41629cebafbfc7b2Zbigniew Jędrzejewski-Szmek init_retransmit_time = DHCP6_REQ_TIMEOUT;
bac3c8eefe23a820caac930d41629cebafbfc7b2Zbigniew Jędrzejewski-Szmek client->retransmit_count >= max_retransmit_count) {
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagna client_stop(client, DHCP6_EVENT_RETRANS_MAX);
7c2d80944afb4196f2eff614e8da1450dffcbeaaThomas Hindoe Paaboel Andersen r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers client_timeout_compute_random(init_retransmit_time);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (client->state == DHCP6_STATE_SOLICITATION)
e1636421f46db6d06fbd028ef20a3113fa3e11f8Lennart Poettering client->retransmit_time += init_retransmit_time / 10;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering client->retransmit_time > max_retransmit_time / 2)
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_dhcp6_client(client, "Next retransmission in %s",
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering format_timespan(time_string, FORMAT_TIMESPAN_MAX,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = sd_event_add_time(client->event, &client->timeout_resend,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering 10 * USEC_PER_MSEC, client_timeout_resend,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = sd_event_source_set_priority(client->timeout_resend,
e1636421f46db6d06fbd028ef20a3113fa3e11f8Lennart Poettering if (max_retransmit_duration && !client->timeout_resend_expire) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = sd_event_source_set_priority(client->timeout_resend_expire,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringstatic int client_ensure_iaid(sd_dhcp6_client *client) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* not in a container, udev will be around */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering _cleanup_udev_device_unref_ struct udev_device *device = NULL;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering char ifindex_str[2 + DECIMAL_STR_MAX(int)];
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering sprintf(ifindex_str, "n%d", client->index);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering device = udev_device_new_from_device_id(udev, ifindex_str);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (udev_device_get_is_initialized(device) <= 0)
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* not yet ready */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* fall back to mac address if no predictable name available */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* fold into 32 bits */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
7c2d80944afb4196f2eff614e8da1450dffcbeaaThomas Hindoe Paaboel Andersenstatic int client_parse_message(sd_dhcp6_client *client,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers bool clientid = false;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_dhcp6_client(client, "%s contains multiple clientids",
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering dhcp6_message_type_to_string(message->type));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering memcmp(&client->duid, optval, optlen) != 0) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_dhcp6_client(client, "%s DUID does not match",
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering dhcp6_message_type_to_string(message->type));
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers r = dhcp6_lease_get_serverid(lease, &id, &id_len);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (r >= 0 && id) {
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers log_dhcp6_client(client, "%s contains multiple serverids",
e1636421f46db6d06fbd028ef20a3113fa3e11f8Lennart Poettering r = dhcp6_lease_set_serverid(lease, optval, optlen);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering r = dhcp6_lease_set_preference(lease, *optval);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering dhcp6_message_type_to_string(message->type),
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering if (r < 0 && r != -ENOMSG)
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering r = dhcp6_lease_get_iaid(lease, &iaid_lease);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering log_dhcp6_client(client, "%s has wrong IAID",
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering dhcp6_message_type_to_string(message->type));
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering if ((r < 0 && r != -ENOMSG) || !clientid) {
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering log_dhcp6_client(client, "%s has incomplete options",
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering dhcp6_message_type_to_string(message->type));
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering r = dhcp6_lease_get_serverid(lease, &id, &id_len);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering log_dhcp6_client(client, "%s has no server id",
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering dhcp6_message_type_to_string(message->type));
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poetteringstatic int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering r = client_parse_message(client, reply, len, lease);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering dhcp6_lease_clear_timers(&client->lease->ia);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering client->lease = sd_dhcp6_lease_unref(client->lease);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poetteringstatic int client_receive_advertise(sd_dhcp6_client *client,
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering uint8_t pref_advertise = 0, pref_lease = 0;
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering r = client_parse_message(client, advertise, len, lease);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = dhcp6_lease_get_preference(lease, &pref_advertise);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering r = dhcp6_lease_get_preference(client->lease, &pref_lease);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering if (!client->lease || r < 0 || pref_advertise > pref_lease) {
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering if (pref_advertise == 255 || client->retransmit_count > 1)
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poetteringstatic int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (r < 0 || buflen <= 0)
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if ((size_t)len < sizeof(DHCP6Message)) {
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers log_dhcp6_client(client, "could not receive message from UDP socket: %s", strerror(errno));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_dhcp6_client(client, "unknown message type %d",
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (client->transaction_id != (message->transaction_id &
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = client_receive_advertise(client, message, len);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering client = client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers if (r >= 0) {
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering dhcp6_message_type_to_string(message->type));
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poetteringstatic int client_start(sd_dhcp6_client *client, enum DHCP6State state)
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering assert_return(client->index > 0, -EINVAL);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering assert_return(client->state != state, -EINVAL);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering sd_event_source_unref(client->timeout_resend_expire);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering client->timeout_resend = sd_event_source_unref(client->timeout_resend);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = dhcp6_network_bind_udp_socket(client->index, NULL);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = sd_event_add_io(client->event, &client->receive_message,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering client->fd, EPOLLIN, client_receive_message,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = sd_event_source_set_priority(client->receive_message,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering client->state = DHCP6_STATE_SOLICITATION;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (client->lease->ia.lifetime_t1 == 0xffffffff ||
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering client->lease->ia.lifetime_t2 == 0xffffffff) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_dhcp6_client(client, "T1 expires in %s",
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
84f6181c2ac99a0514ca5e0c8fc8c8e284caf789Lennart Poettering timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
timeout, 0));
client);
client);
int priority)
if (event)
if (!client)
return NULL;
if (client)
return client;
return NULL;
return client;
if (!client)
return -ENOMEM;