sd-dhcp6-client.c revision 3f0c075f8ef3344da5a6bda524540201f9204e61
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering/***
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering This file is part of systemd.
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering Copyright (C) 2014 Intel Corporation. All rights reserved.
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
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
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
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***/
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#include <errno.h>
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#include <string.h>
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#include <sys/ioctl.h>
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#include "udev.h"
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#include "udev-util.h"
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#include "virt.h"
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#include "siphash24.h"
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#include "util.h"
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#include "refcnt.h"
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#include "network-internal.h"
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#include "sd-dhcp6-client.h"
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering#include "dhcp6-protocol.h"
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#include "dhcp6-internal.h"
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#include "dhcp6-lease-internal.h"
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering#define SYSTEMD_PEN 43793
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 Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstruct sd_dhcp6_client {
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering RefCount n_ref;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering enum DHCP6State state;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_event *event;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering int event_priority;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering int index;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering struct ether_addr mac_addr;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering DHCP6IA ia_na;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering be32_t transaction_id;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering struct sd_dhcp6_lease *lease;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering int fd;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_event_source *receive_message;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering usec_t retransmit_time;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering uint8_t retransmit_count;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_event_source *timeout_resend;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_event_source *timeout_resend_expire;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_dhcp6_client_cb_t cb;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering void *userdata;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering struct duid_en {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering uint16_t type; /* DHCP6_DUID_EN */
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering uint32_t pen;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering uint8_t id[8];
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering } _packed_ duid;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering};
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringconst char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_SOLICIT] = "SOLICIT",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_ADVERTISE] = "ADVERTISE",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_REQUEST] = "REQUEST",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_CONFIRM] = "CONFIRM",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_RENEW] = "RENEW",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_REBIND] = "REBIND",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_REPLY] = "REPLY",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_RELEASE] = "RELEASE",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_DECLINE] = "DECLINE",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_RECONFIGURE] = "RECONFIGURE",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_RELAY_FORW] = "RELAY-FORW",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering [DHCP6_RELAY_REPL] = "RELAY-REPL",
634af5665fda8776d22624d947c8de830e30a874Lennart Poettering};
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart PoetteringDEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringconst char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
634af5665fda8776d22624d947c8de830e30a874Lennart Poettering [DHCP6_STATUS_SUCCESS] = "Success",
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 Poettering};
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart PoetteringDEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
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
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)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
634af5665fda8776d22624d947c8de830e30a874Lennart Poetteringstatic int client_start(sd_dhcp6_client *client, enum DHCP6State state);
634af5665fda8776d22624d947c8de830e30a874Lennart Poettering
634af5665fda8776d22624d947c8de830e30a874Lennart Poetteringint sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_dhcp6_client_cb_t cb, void *userdata)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering{
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert_return(client, -EINVAL);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->cb = cb;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->userdata = userdata;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering}
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringint sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering{
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert_return(client, -EINVAL);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert_return(interface_index >= -1, -EINVAL);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->index = interface_index;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return 0;
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering}
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringint sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering const struct ether_addr *mac_addr)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering{
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert_return(client, -EINVAL);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (mac_addr)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
555bd6e95bf2b469306d9cd86e126c0122d0895bLennart Poettering else
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering}
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringint sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert_return(client, -EINVAL);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert_return(ret, -EINVAL);
555bd6e95bf2b469306d9cd86e126c0122d0895bLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (!client->lease)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return -ENOMSG;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering *ret = sd_dhcp6_lease_ref(client->lease);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering}
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic void client_notify(sd_dhcp6_client *client, int event) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (client->cb)
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering client->cb(client, event, client->userdata);
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering}
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic int client_reset(sd_dhcp6_client *client) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert_return(client, -EINVAL);
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->receive_message =
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_event_source_unref(client->receive_message);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->fd = safe_close(client->fd);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->transaction_id = 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->ia_na.timeout_t1 =
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_event_source_unref(client->ia_na.timeout_t1);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->ia_na.timeout_t2 =
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_event_source_unref(client->ia_na.timeout_t2);
555bd6e95bf2b469306d9cd86e126c0122d0895bLennart Poettering
555bd6e95bf2b469306d9cd86e126c0122d0895bLennart Poettering client->retransmit_time = 0;
555bd6e95bf2b469306d9cd86e126c0122d0895bLennart Poettering client->retransmit_count = 0;
555bd6e95bf2b469306d9cd86e126c0122d0895bLennart Poettering client->timeout_resend = sd_event_source_unref(client->timeout_resend);
555bd6e95bf2b469306d9cd86e126c0122d0895bLennart Poettering client->timeout_resend_expire =
9d485985338a46b8cb1acdf1af6c1eb2e88acfeeLennart Poettering sd_event_source_unref(client->timeout_resend_expire);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->state = DHCP6_STATE_STOPPED;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering}
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic void client_stop(sd_dhcp6_client *client, int error) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering DHCP6_CLIENT_DONT_DESTROY(client);
634af5665fda8776d22624d947c8de830e30a874Lennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert(client);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client_notify(client, error);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client_reset(client);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering}
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic int client_send_message(sd_dhcp6_client *client) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering _cleanup_free_ DHCP6Message *message = NULL;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering struct in6_addr all_servers =
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering size_t len, optlen = 512;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering uint8_t *opt;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering int r;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering len = sizeof(DHCP6Message) + optlen;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering message = malloc0(len);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (!message)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return -ENOMEM;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering opt = (uint8_t *)(message + 1);
e70df46b9721a3d025e7a0b4ffb5893cbde5e55dLennart Poettering
e70df46b9721a3d025e7a0b4ffb5893cbde5e55dLennart Poettering message->transaction_id = client->transaction_id;
e70df46b9721a3d025e7a0b4ffb5893cbde5e55dLennart Poettering
e70df46b9721a3d025e7a0b4ffb5893cbde5e55dLennart Poettering switch(client->state) {
e70df46b9721a3d025e7a0b4ffb5893cbde5e55dLennart Poettering case DHCP6_STATE_SOLICITATION:
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering message->type = DHCP6_SOLICIT;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (r < 0)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return r;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering break;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering case DHCP6_STATE_REQUEST:
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering message->type = DHCP6_REQUEST;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->lease->serverid_len,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->lease->serverid);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (r < 0)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return r;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (r < 0)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return r;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering break;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering case DHCP6_STATE_STOPPED:
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering case DHCP6_STATE_RS:
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering case DHCP6_STATE_BOUND:
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return -EINVAL;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering }
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sizeof(client->duid), &client->duid);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (r < 0)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return r;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering len - optlen);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (r < 0)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return r;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering log_dhcp6_client(client, "Sent %s",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering dhcp6_message_type_to_string(message->type));
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering}
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic int client_timeout_t2(sd_event_source *s, uint64_t usec,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering void *userdata) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_dhcp6_client *client = userdata;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert_return(s, -EINVAL);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert_return(client, -EINVAL);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert_return(client->lease, -EINVAL);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->lease->ia.timeout_t2 =
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_event_source_unref(client->lease->ia.timeout_t2);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering log_dhcp6_client(client, "Timeout T2");
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering}
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic int client_timeout_t1(sd_event_source *s, uint64_t usec,
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering void *userdata) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_dhcp6_client *client = userdata;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert_return(s, -EINVAL);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert_return(client, -EINVAL);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert_return(client->lease, -EINVAL);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->lease->ia.timeout_t1 =
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_event_source_unref(client->lease->ia.timeout_t1);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering log_dhcp6_client(client, "Timeout T1");
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering}
9d485985338a46b8cb1acdf1af6c1eb2e88acfeeLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering void *userdata) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_dhcp6_client *client = userdata;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert(s);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert(client);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert(client->event);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering}
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic usec_t client_timeout_compute_random(usec_t val) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return val - val / 10 +
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering}
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poetteringstatic int client_timeout_resend(sd_event_source *s, uint64_t usec,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering void *userdata) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering int r = 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering sd_dhcp6_client *client = userdata;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering usec_t time_now, init_retransmit_time, max_retransmit_time;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering usec_t max_retransmit_duration;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering uint8_t max_retransmit_count = 0;
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering char time_string[FORMAT_TIMESPAN_MAX];
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert(s);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert(client);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering assert(client->event);
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->timeout_resend = sd_event_source_unref(client->timeout_resend);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering switch (client->state) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering case DHCP6_STATE_SOLICITATION:
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (client->retransmit_count && client->lease) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client_start(client, DHCP6_STATE_REQUEST);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering }
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering init_retransmit_time = DHCP6_SOL_TIMEOUT;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering max_retransmit_time = DHCP6_SOL_MAX_RT;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering max_retransmit_count = 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering max_retransmit_duration = 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering break;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering case DHCP6_STATE_REQUEST:
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering init_retransmit_time = DHCP6_REQ_TIMEOUT;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering max_retransmit_time = DHCP6_REQ_MAX_RT;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering max_retransmit_count = DHCP6_REQ_MAX_RC;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering max_retransmit_duration = 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering break;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering case DHCP6_STATE_STOPPED:
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering case DHCP6_STATE_RS:
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering case DHCP6_STATE_BOUND:
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering }
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (max_retransmit_count &&
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->retransmit_count >= max_retransmit_count) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client_stop(client, DHCP6_EVENT_RETRANS_MAX);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering return 0;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering }
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = client_send_message(client);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (r >= 0)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->retransmit_count++;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (r < 0)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering goto error;
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (!client->retransmit_time) {
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->retransmit_time =
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client_timeout_compute_random(init_retransmit_time);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering if (client->state == DHCP6_STATE_SOLICITATION)
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->retransmit_time += init_retransmit_time / 10;
e70df46b9721a3d025e7a0b4ffb5893cbde5e55dLennart Poettering
e70df46b9721a3d025e7a0b4ffb5893cbde5e55dLennart Poettering } else {
e70df46b9721a3d025e7a0b4ffb5893cbde5e55dLennart Poettering if (max_retransmit_time &&
e70df46b9721a3d025e7a0b4ffb5893cbde5e55dLennart Poettering client->retransmit_time > max_retransmit_time / 2)
e70df46b9721a3d025e7a0b4ffb5893cbde5e55dLennart Poettering client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering else
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering }
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering log_dhcp6_client(client, "Next retransmission in %s",
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering format_timespan(time_string, FORMAT_TIMESPAN_MAX,
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering client->retransmit_time, 0));
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering
cabb0bc6b1a4ec57e108dc99364687d7c4f9670fLennart Poettering r = sd_event_add_time(client->event, &client->timeout_resend,
CLOCK_MONOTONIC,
time_now + client->retransmit_time,
10 * USEC_PER_MSEC, client_timeout_resend,
client);
if (r < 0)
goto error;
r = sd_event_source_set_priority(client->timeout_resend,
client->event_priority);
if (r < 0)
goto error;
if (max_retransmit_duration && !client->timeout_resend_expire) {
log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
max_retransmit_duration / USEC_PER_SEC);
r = sd_event_add_time(client->event,
&client->timeout_resend_expire,
CLOCK_MONOTONIC,
time_now + max_retransmit_duration,
USEC_PER_SEC,
client_timeout_resend_expire, client);
if (r < 0)
goto error;
r = sd_event_source_set_priority(client->timeout_resend_expire,
client->event_priority);
if (r < 0)
goto error;
}
error:
if (r < 0)
client_stop(client, r);
return 0;
}
static int client_ensure_iaid(sd_dhcp6_client *client) {
const char *name = NULL;
uint64_t id;
assert(client);
if (client->ia_na.id)
return 0;
if (detect_container(NULL) <= 0) {
/* not in a container, udev will be around */
_cleanup_udev_unref_ struct udev *udev;
_cleanup_udev_device_unref_ struct udev_device *device = NULL;
char ifindex_str[2 + DECIMAL_STR_MAX(int)];
udev = udev_new();
if (!udev)
return -ENOMEM;
sprintf(ifindex_str, "n%d", client->index);
device = udev_device_new_from_device_id(udev, ifindex_str);
if (!device)
return -errno;
if (udev_device_get_is_initialized(device) <= 0)
/* not yet ready */
return -EBUSY;
name = net_get_name(device);
}
if (name)
siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
else
/* fall back to mac address if no predictable name available */
siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
HASH_KEY.bytes);
/* fold into 32 bits */
client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
return 0;
}
static int client_parse_message(sd_dhcp6_client *client,
DHCP6Message *message, size_t len,
sd_dhcp6_lease *lease) {
int r;
uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
uint16_t optcode, status;
size_t optlen, id_len;
bool clientid = false;
be32_t iaid_lease;
while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
&optval)) >= 0) {
switch (optcode) {
case DHCP6_OPTION_CLIENTID:
if (clientid) {
log_dhcp6_client(client, "%s contains multiple clientids",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}
if (optlen != sizeof(client->duid) ||
memcmp(&client->duid, optval, optlen) != 0) {
log_dhcp6_client(client, "%s DUID does not match",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}
clientid = true;
break;
case DHCP6_OPTION_SERVERID:
r = dhcp6_lease_get_serverid(lease, &id, &id_len);
if (r >= 0 && id) {
log_dhcp6_client(client, "%s contains multiple serverids",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}
r = dhcp6_lease_set_serverid(lease, optval, optlen);
if (r < 0)
return r;
break;
case DHCP6_OPTION_PREFERENCE:
if (optlen != 1)
return -EINVAL;
r = dhcp6_lease_set_preference(lease, *optval);
if (r < 0)
return r;
break;
case DHCP6_OPTION_STATUS_CODE:
if (optlen < 2)
return -EINVAL;
status = optval[0] << 8 | optval[1];
if (status) {
log_dhcp6_client(client, "%s Status %s",
dhcp6_message_type_to_string(message->type),
dhcp6_message_status_to_string(status));
return -EINVAL;
}
break;
case DHCP6_OPTION_IA_NA:
r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
&lease->ia);
if (r < 0 && r != -ENOMSG)
return r;
r = dhcp6_lease_get_iaid(lease, &iaid_lease);
if (r < 0)
return r;
if (client->ia_na.id != iaid_lease) {
log_dhcp6_client(client, "%s has wrong IAID",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}
break;
}
}
if ((r < 0 && r != -ENOMSG) || !clientid) {
log_dhcp6_client(client, "%s has incomplete options",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}
r = dhcp6_lease_get_serverid(lease, &id, &id_len);
if (r < 0)
log_dhcp6_client(client, "%s has no server id",
dhcp6_message_type_to_string(message->type));
return r;
}
static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
size_t len)
{
int r;
_cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
if (reply->type != DHCP6_REPLY)
return -EINVAL;
r = dhcp6_lease_new(&lease);
if (r < 0)
return -ENOMEM;
r = client_parse_message(client, reply, len, lease);
if (r < 0)
return r;
dhcp6_lease_clear_timers(&client->lease->ia);
client->lease = sd_dhcp6_lease_unref(client->lease);
client->lease = lease;
lease = NULL;
return DHCP6_STATE_BOUND;
}
static int client_receive_advertise(sd_dhcp6_client *client,
DHCP6Message *advertise, size_t len) {
int r;
_cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
uint8_t pref_advertise = 0, pref_lease = 0;
if (advertise->type != DHCP6_ADVERTISE)
return -EINVAL;
r = dhcp6_lease_new(&lease);
if (r < 0)
return r;
r = client_parse_message(client, advertise, len, lease);
if (r < 0)
return r;
r = dhcp6_lease_get_preference(lease, &pref_advertise);
if (r < 0)
return r;
r = dhcp6_lease_get_preference(client->lease, &pref_lease);
if (!client->lease || r < 0 || pref_advertise > pref_lease) {
sd_dhcp6_lease_unref(client->lease);
client->lease = lease;
lease = NULL;
r = 0;
}
if (pref_advertise == 255 || client->retransmit_count > 1)
r = DHCP6_STATE_REQUEST;
return r;
}
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;
assert(s);
assert(client);
assert(client->event);
r = ioctl(fd, FIONREAD, &buflen);
if (r < 0 || buflen <= 0)
buflen = DHCP6_MIN_OPTIONS_SIZE;
message = malloc0(buflen);
if (!message)
return -ENOMEM;
len = read(fd, message, buflen);
if ((size_t)len < sizeof(DHCP6Message)) {
log_dhcp6_client(client, "could not receive message from UDP socket: %m");
return 0;
}
switch(message->type) {
case DHCP6_SOLICIT:
case DHCP6_REQUEST:
case DHCP6_CONFIRM:
case DHCP6_RENEW:
case DHCP6_REBIND:
case DHCP6_RELEASE:
case DHCP6_DECLINE:
case DHCP6_INFORMATION_REQUEST:
case DHCP6_RELAY_FORW:
case DHCP6_RELAY_REPL:
return 0;
case DHCP6_ADVERTISE:
case DHCP6_REPLY:
case DHCP6_RECONFIGURE:
break;
default:
log_dhcp6_client(client, "unknown message type %d",
message->type);
return 0;
}
if (client->transaction_id != (message->transaction_id &
htobe32(0x00ffffff)))
return 0;
switch (client->state) {
case DHCP6_STATE_SOLICITATION:
r = client_receive_advertise(client, message, len);
if (r == DHCP6_STATE_REQUEST)
client_start(client, r);
break;
case DHCP6_STATE_REQUEST:
r = client_receive_reply(client, message, len);
if (r < 0)
return 0;
if (r == DHCP6_STATE_BOUND) {
r = client_start(client, DHCP6_STATE_BOUND);
if (r < 0) {
client_stop(client, r);
return 0;
}
client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
}
break;
case DHCP6_STATE_BOUND:
break;
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_RS:
return 0;
}
if (r >= 0) {
log_dhcp6_client(client, "Recv %s",
dhcp6_message_type_to_string(message->type));
}
return 0;
}
static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
{
int r;
usec_t timeout, time_now;
char time_string[FORMAT_TIMESPAN_MAX];
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
assert_return(client->index > 0, -EINVAL);
assert_return(client->state != state, -EINVAL);
client->timeout_resend_expire =
sd_event_source_unref(client->timeout_resend_expire);
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
client->retransmit_time = 0;
client->retransmit_count = 0;
switch (state) {
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_RS:
case DHCP6_STATE_SOLICITATION:
r = client_ensure_iaid(client);
if (r < 0)
return r;
r = dhcp6_network_bind_udp_socket(client->index, NULL);
if (r < 0)
return r;
client->fd = r;
r = sd_event_add_io(client->event, &client->receive_message,
client->fd, EPOLLIN, client_receive_message,
client);
if (r < 0)
return r;
r = sd_event_source_set_priority(client->receive_message,
client->event_priority);
if (r < 0)
return r;
client->state = DHCP6_STATE_SOLICITATION;
break;
case DHCP6_STATE_REQUEST:
client->state = state;
break;
case DHCP6_STATE_BOUND:
r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
if (r < 0)
return r;
if (client->lease->ia.lifetime_t1 == 0xffffffff ||
client->lease->ia.lifetime_t2 == 0xffffffff) {
log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
be32toh(client->lease->ia.lifetime_t1),
be32toh(client->lease->ia.lifetime_t2));
return 0;
}
timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
log_dhcp6_client(client, "T1 expires in %s",
format_timespan(time_string,
FORMAT_TIMESPAN_MAX,
timeout, 0));
r = sd_event_add_time(client->event,
&client->lease->ia.timeout_t1,
CLOCK_MONOTONIC, time_now + timeout,
10 * USEC_PER_SEC, client_timeout_t1,
client);
if (r < 0)
return r;
r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
client->event_priority);
if (r < 0)
return r;
timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
log_dhcp6_client(client, "T2 expires in %s",
format_timespan(time_string,
FORMAT_TIMESPAN_MAX,
timeout, 0));
r = sd_event_add_time(client->event,
&client->lease->ia.timeout_t2,
CLOCK_MONOTONIC, time_now + timeout,
10 * USEC_PER_SEC, client_timeout_t2,
client);
if (r < 0)
return r;
r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
client->event_priority);
if (r < 0)
return r;
return 0;
}
client->transaction_id = random_u32() & htobe32(0x00ffffff);
r = sd_event_add_time(client->event, &client->timeout_resend,
CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
client);
if (r < 0)
return r;
r = sd_event_source_set_priority(client->timeout_resend,
client->event_priority);
if (r < 0)
return r;
return 0;
}
int sd_dhcp6_client_stop(sd_dhcp6_client *client)
{
client_stop(client, DHCP6_EVENT_STOP);
return 0;
}
int sd_dhcp6_client_start(sd_dhcp6_client *client)
{
int r = 0;
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
assert_return(client->index > 0, -EINVAL);
r = client_reset(client);
if (r < 0)
return r;
return client_start(client, DHCP6_STATE_SOLICITATION);
}
int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
int priority)
{
int r;
assert_return(client, -EINVAL);
assert_return(!client->event, -EBUSY);
if (event)
client->event = sd_event_ref(event);
else {
r = sd_event_default(&client->event);
if (r < 0)
return 0;
}
client->event_priority = priority;
return 0;
}
int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
assert_return(client, -EINVAL);
client->event = sd_event_unref(client->event);
return 0;
}
sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
if (!client)
return NULL;
return client->event;
}
sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
if (client)
assert_se(REFCNT_INC(client->n_ref) >= 2);
return client;
}
sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
if (client && REFCNT_DEC(client->n_ref) <= 0) {
client_reset(client);
sd_dhcp6_client_detach_event(client);
free(client);
return NULL;
}
return client;
}
int sd_dhcp6_client_new(sd_dhcp6_client **ret)
{
_cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
sd_id128_t machine_id;
int r;
assert_return(ret, -EINVAL);
client = new0(sd_dhcp6_client, 1);
if (!client)
return -ENOMEM;
client->n_ref = REFCNT_INIT;
client->ia_na.type = DHCP6_OPTION_IA_NA;
client->index = -1;
client->fd = -1;
/* initialize DUID */
client->duid.type = htobe16(DHCP6_DUID_EN);
client->duid.pen = htobe32(SYSTEMD_PEN);
r = sd_id128_get_machine(&machine_id);
if (r < 0)
return r;
/* a bit of snake-oil perhaps, but no need to expose the machine-id
directly */
siphash24(client->duid.id, &machine_id, sizeof(machine_id),
HASH_KEY.bytes);
*ret = client;
client = NULL;
return 0;
}