2014-06-19 14:38:50 +02:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
2015-04-02 09:50:16 +02:00
|
|
|
Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
|
2014-06-19 14:38:50 +02:00
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
|
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
systemd is distributed in the hope that it will be useful, but
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
2014-06-19 14:39:42 +02:00
|
|
|
#include <sys/ioctl.h>
|
2014-10-08 21:15:45 +02:00
|
|
|
#include <linux/if_infiniband.h>
|
2014-06-19 14:38:50 +02:00
|
|
|
|
|
|
|
#include "sd-dhcp6-client.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "dhcp-identifier.h"
|
2014-06-19 14:39:08 +02:00
|
|
|
#include "dhcp6-internal.h"
|
2014-06-19 14:39:42 +02:00
|
|
|
#include "dhcp6-lease-internal.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "dhcp6-protocol.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2015-11-16 17:43:08 +01:00
|
|
|
#include "in-addr-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "network-internal.h"
|
|
|
|
#include "random-util.h"
|
2016-02-15 22:50:01 +01:00
|
|
|
#include "socket-util.h"
|
2015-10-26 22:31:05 +01:00
|
|
|
#include "string-table.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "util.h"
|
2014-06-19 14:38:50 +02:00
|
|
|
|
2014-10-08 21:15:45 +02:00
|
|
|
#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
|
|
|
|
|
2014-06-19 14:38:50 +02:00
|
|
|
struct sd_dhcp6_client {
|
2015-08-26 21:05:53 +02:00
|
|
|
unsigned n_ref;
|
2014-06-19 14:38:50 +02:00
|
|
|
|
|
|
|
enum DHCP6State state;
|
|
|
|
sd_event *event;
|
|
|
|
int event_priority;
|
2016-05-23 16:13:18 +02:00
|
|
|
int ifindex;
|
2015-11-16 17:43:08 +01:00
|
|
|
struct in6_addr local_address;
|
2014-10-08 21:15:45 +02:00
|
|
|
uint8_t mac_addr[MAX_MAC_ADDR_LEN];
|
|
|
|
size_t mac_addr_len;
|
|
|
|
uint16_t arp_type;
|
2014-06-19 14:39:08 +02:00
|
|
|
DHCP6IA ia_na;
|
2014-06-19 14:39:27 +02:00
|
|
|
be32_t transaction_id;
|
2014-09-01 12:21:33 +02:00
|
|
|
usec_t transaction_start;
|
2014-06-19 14:39:42 +02:00
|
|
|
struct sd_dhcp6_lease *lease;
|
2014-06-19 14:39:27 +02:00
|
|
|
int fd;
|
2014-12-10 15:17:32 +01:00
|
|
|
bool information_request;
|
2014-06-24 15:20:32 +02:00
|
|
|
be16_t *req_opts;
|
|
|
|
size_t req_opts_allocated;
|
|
|
|
size_t req_opts_len;
|
2014-06-19 14:39:27 +02:00
|
|
|
sd_event_source *receive_message;
|
2014-06-19 14:39:15 +02:00
|
|
|
usec_t retransmit_time;
|
|
|
|
uint8_t retransmit_count;
|
|
|
|
sd_event_source *timeout_resend;
|
|
|
|
sd_event_source *timeout_resend_expire;
|
2016-05-23 16:48:56 +02:00
|
|
|
sd_dhcp6_client_callback_t callback;
|
2014-06-19 14:38:50 +02:00
|
|
|
void *userdata;
|
2015-01-21 21:23:47 +01:00
|
|
|
struct duid duid;
|
2014-09-26 22:12:36 +02:00
|
|
|
size_t duid_len;
|
2014-06-19 14:38:50 +02:00
|
|
|
};
|
|
|
|
|
2014-06-24 15:20:32 +02:00
|
|
|
static const uint16_t default_req_opts[] = {
|
2016-01-20 14:44:28 +01:00
|
|
|
SD_DHCP6_OPTION_DNS_SERVERS,
|
|
|
|
SD_DHCP6_OPTION_DOMAIN_LIST,
|
|
|
|
SD_DHCP6_OPTION_NTP_SERVER,
|
|
|
|
SD_DHCP6_OPTION_SNTP_SERVERS,
|
2014-06-24 15:20:32 +02:00
|
|
|
};
|
|
|
|
|
2014-06-19 14:39:27 +02:00
|
|
|
const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
|
|
|
|
[DHCP6_SOLICIT] = "SOLICIT",
|
|
|
|
[DHCP6_ADVERTISE] = "ADVERTISE",
|
|
|
|
[DHCP6_REQUEST] = "REQUEST",
|
|
|
|
[DHCP6_CONFIRM] = "CONFIRM",
|
|
|
|
[DHCP6_RENEW] = "RENEW",
|
|
|
|
[DHCP6_REBIND] = "REBIND",
|
|
|
|
[DHCP6_REPLY] = "REPLY",
|
|
|
|
[DHCP6_RELEASE] = "RELEASE",
|
|
|
|
[DHCP6_DECLINE] = "DECLINE",
|
|
|
|
[DHCP6_RECONFIGURE] = "RECONFIGURE",
|
|
|
|
[DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
|
|
|
|
[DHCP6_RELAY_FORW] = "RELAY-FORW",
|
|
|
|
[DHCP6_RELAY_REPL] = "RELAY-REPL",
|
|
|
|
};
|
|
|
|
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
|
|
|
|
|
2014-06-19 14:39:42 +02:00
|
|
|
const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
|
|
|
|
[DHCP6_STATUS_SUCCESS] = "Success",
|
|
|
|
[DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
|
|
|
|
[DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
|
|
|
|
[DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
|
|
|
|
[DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
|
|
|
|
[DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
|
|
|
|
};
|
|
|
|
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
|
|
|
|
|
2014-06-25 09:36:56 +02:00
|
|
|
#define DHCP6_CLIENT_DONT_DESTROY(client) \
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
|
2014-06-25 09:36:56 +02:00
|
|
|
|
2014-06-19 14:39:49 +02:00
|
|
|
static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
int sd_dhcp6_client_set_callback(
|
|
|
|
sd_dhcp6_client *client,
|
|
|
|
sd_dhcp6_client_callback_t cb,
|
|
|
|
void *userdata) {
|
2016-05-23 16:48:56 +02:00
|
|
|
|
2014-06-19 14:38:50 +02:00
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
2016-05-23 16:48:56 +02:00
|
|
|
client->callback = cb;
|
2014-06-19 14:38:50 +02:00
|
|
|
client->userdata = userdata;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-23 16:13:18 +02:00
|
|
|
int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
|
2014-06-19 14:38:50 +02:00
|
|
|
|
2016-05-23 16:13:18 +02:00
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
assert_return(ifindex >= -1, -EINVAL);
|
2015-09-23 12:51:53 +02:00
|
|
|
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
|
|
|
|
2016-05-23 16:13:18 +02:00
|
|
|
client->ifindex = ifindex;
|
2014-06-19 14:38:50 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
int sd_dhcp6_client_set_local_address(
|
|
|
|
sd_dhcp6_client *client,
|
|
|
|
const struct in6_addr *local_address) {
|
|
|
|
|
2015-11-16 17:43:08 +01:00
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
assert_return(local_address, -EINVAL);
|
|
|
|
assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL);
|
|
|
|
|
|
|
|
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
|
|
|
|
|
|
|
client->local_address = *local_address;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-26 21:09:00 +02:00
|
|
|
int sd_dhcp6_client_set_mac(
|
|
|
|
sd_dhcp6_client *client,
|
|
|
|
const uint8_t *addr, size_t addr_len,
|
|
|
|
uint16_t arp_type) {
|
|
|
|
|
2014-06-19 14:38:50 +02:00
|
|
|
assert_return(client, -EINVAL);
|
2014-10-08 21:15:45 +02:00
|
|
|
assert_return(addr, -EINVAL);
|
|
|
|
assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
|
|
|
|
assert_return(arp_type > 0, -EINVAL);
|
|
|
|
|
2015-09-23 12:51:53 +02:00
|
|
|
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
|
|
|
|
2014-10-08 21:15:45 +02:00
|
|
|
if (arp_type == ARPHRD_ETHER)
|
|
|
|
assert_return(addr_len == ETH_ALEN, -EINVAL);
|
|
|
|
else if (arp_type == ARPHRD_INFINIBAND)
|
|
|
|
assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
|
2014-06-19 14:38:50 +02:00
|
|
|
else
|
2014-10-08 21:15:45 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (client->mac_addr_len == addr_len &&
|
|
|
|
memcmp(&client->mac_addr, addr, addr_len) == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
memcpy(&client->mac_addr, addr, addr_len);
|
|
|
|
client->mac_addr_len = addr_len;
|
|
|
|
client->arp_type = arp_type;
|
2014-06-19 14:38:50 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-26 21:09:00 +02:00
|
|
|
static int client_ensure_duid(sd_dhcp6_client *client) {
|
2015-03-03 21:06:29 +01:00
|
|
|
if (client->duid_len != 0)
|
|
|
|
return 0;
|
2015-08-26 21:09:00 +02:00
|
|
|
|
2015-03-03 21:06:29 +01:00
|
|
|
return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
|
|
|
|
}
|
|
|
|
|
2016-04-30 03:18:11 +02:00
|
|
|
/**
|
|
|
|
* Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
|
|
|
|
* without further modification. Otherwise, if duid_type is supported, DUID
|
|
|
|
* is set based on that type. Otherwise, an error is returned.
|
|
|
|
*/
|
2016-05-03 17:52:44 +02:00
|
|
|
int sd_dhcp6_client_set_duid(
|
|
|
|
sd_dhcp6_client *client,
|
|
|
|
uint16_t duid_type,
|
2016-05-03 18:08:56 +02:00
|
|
|
const void *duid,
|
2016-05-03 17:52:44 +02:00
|
|
|
size_t duid_len) {
|
2016-05-03 18:08:56 +02:00
|
|
|
|
2016-03-31 01:33:55 +02:00
|
|
|
int r;
|
2014-09-26 22:12:36 +02:00
|
|
|
assert_return(client, -EINVAL);
|
2016-04-30 03:18:11 +02:00
|
|
|
assert_return(duid_len == 0 || duid != NULL, -EINVAL);
|
2016-03-21 23:24:24 +01:00
|
|
|
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
2016-03-10 06:58:44 +01:00
|
|
|
|
2016-04-30 03:18:11 +02:00
|
|
|
if (duid != NULL) {
|
2016-03-31 01:33:55 +02:00
|
|
|
r = dhcp_validate_duid_len(duid_type, duid_len);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2016-04-30 03:18:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (duid != NULL) {
|
2016-03-31 01:33:55 +02:00
|
|
|
client->duid.type = htobe16(duid_type);
|
|
|
|
memcpy(&client->duid.raw.data, duid, duid_len);
|
2016-04-30 03:18:11 +02:00
|
|
|
client->duid_len = sizeof(client->duid.type) + duid_len;
|
|
|
|
} else if (duid_type == DUID_TYPE_EN) {
|
|
|
|
r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
} else
|
|
|
|
return -EOPNOTSUPP;
|
2014-10-02 16:25:08 +02:00
|
|
|
|
2016-03-31 01:33:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
|
|
|
|
|
|
|
client->ia_na.id = htobe32(iaid);
|
2014-09-26 22:12:36 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-24 23:42:56 +02:00
|
|
|
int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
|
2014-12-10 15:17:32 +01:00
|
|
|
assert_return(client, -EINVAL);
|
2015-09-23 12:51:53 +02:00
|
|
|
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
|
|
|
|
2014-12-10 15:17:32 +01:00
|
|
|
client->information_request = enabled;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-24 23:42:56 +02:00
|
|
|
int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) {
|
2014-12-10 15:17:32 +01:00
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
assert_return(enabled, -EINVAL);
|
|
|
|
|
|
|
|
*enabled = client->information_request;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-26 21:09:00 +02:00
|
|
|
int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) {
|
2014-06-24 15:20:32 +02:00
|
|
|
size_t t;
|
|
|
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
|
|
|
|
|
|
|
|
switch(option) {
|
2016-05-23 16:27:05 +02:00
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_DNS_SERVERS:
|
|
|
|
case SD_DHCP6_OPTION_DOMAIN_LIST:
|
|
|
|
case SD_DHCP6_OPTION_SNTP_SERVERS:
|
|
|
|
case SD_DHCP6_OPTION_NTP_SERVER:
|
2014-06-24 15:20:32 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (t = 0; t < client->req_opts_len; t++)
|
|
|
|
if (client->req_opts[t] == htobe16(option))
|
|
|
|
return -EEXIST;
|
|
|
|
|
|
|
|
if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
|
|
|
|
client->req_opts_len + 1))
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
client->req_opts[client->req_opts_len++] = htobe16(option);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-19 14:39:45 +02:00
|
|
|
int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
|
|
|
if (!client->lease)
|
|
|
|
return -ENOMSG;
|
|
|
|
|
2015-11-13 14:35:39 +01:00
|
|
|
if (ret)
|
|
|
|
*ret = client->lease;
|
2014-06-19 14:39:45 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-25 09:36:56 +02:00
|
|
|
static void client_notify(sd_dhcp6_client *client, int event) {
|
2016-05-23 16:48:56 +02:00
|
|
|
assert(client);
|
|
|
|
|
|
|
|
if (client->callback)
|
|
|
|
client->callback(client, event, client->userdata);
|
2014-06-19 14:38:50 +02:00
|
|
|
}
|
|
|
|
|
2015-09-22 14:17:32 +02:00
|
|
|
static void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) {
|
2016-05-23 16:27:05 +02:00
|
|
|
assert(client);
|
|
|
|
|
2015-07-10 10:25:21 +02:00
|
|
|
if (client->lease) {
|
|
|
|
dhcp6_lease_clear_timers(&client->lease->ia);
|
2015-09-22 14:17:32 +02:00
|
|
|
sd_dhcp6_lease_unref(client->lease);
|
2015-07-10 10:25:21 +02:00
|
|
|
}
|
2016-05-23 16:27:05 +02:00
|
|
|
|
2015-09-22 14:17:32 +02:00
|
|
|
client->lease = lease;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int client_reset(sd_dhcp6_client *client) {
|
2016-05-23 16:27:05 +02:00
|
|
|
assert(client);
|
2015-09-22 14:17:32 +02:00
|
|
|
|
|
|
|
client_set_lease(client, NULL);
|
2015-07-10 10:25:21 +02:00
|
|
|
|
2014-06-19 14:39:27 +02:00
|
|
|
client->receive_message =
|
|
|
|
sd_event_source_unref(client->receive_message);
|
|
|
|
|
2014-06-19 15:08:37 +02:00
|
|
|
client->fd = safe_close(client->fd);
|
2014-06-19 14:39:27 +02:00
|
|
|
|
2014-06-19 14:39:49 +02:00
|
|
|
client->transaction_id = 0;
|
2014-09-01 12:21:33 +02:00
|
|
|
client->transaction_start = 0;
|
2014-06-19 14:39:27 +02:00
|
|
|
|
2014-06-19 14:39:08 +02:00
|
|
|
client->ia_na.timeout_t1 =
|
|
|
|
sd_event_source_unref(client->ia_na.timeout_t1);
|
|
|
|
client->ia_na.timeout_t2 =
|
|
|
|
sd_event_source_unref(client->ia_na.timeout_t2);
|
|
|
|
|
2014-06-19 14:39:15 +02:00
|
|
|
client->retransmit_time = 0;
|
|
|
|
client->retransmit_count = 0;
|
|
|
|
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
|
|
|
|
client->timeout_resend_expire =
|
|
|
|
sd_event_source_unref(client->timeout_resend_expire);
|
|
|
|
|
2014-06-19 14:38:50 +02:00
|
|
|
client->state = DHCP6_STATE_STOPPED;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-25 09:36:56 +02:00
|
|
|
static void client_stop(sd_dhcp6_client *client, int error) {
|
|
|
|
DHCP6_CLIENT_DONT_DESTROY(client);
|
2014-06-19 14:38:50 +02:00
|
|
|
|
2014-06-25 09:36:56 +02:00
|
|
|
assert(client);
|
2014-06-19 14:38:50 +02:00
|
|
|
|
2014-06-25 09:36:56 +02:00
|
|
|
client_notify(client, error);
|
|
|
|
|
|
|
|
client_reset(client);
|
2014-06-19 14:38:50 +02:00
|
|
|
}
|
|
|
|
|
2014-09-01 12:21:33 +02:00
|
|
|
static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
|
2014-06-19 14:39:27 +02:00
|
|
|
_cleanup_free_ DHCP6Message *message = NULL;
|
|
|
|
struct in6_addr all_servers =
|
|
|
|
IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
|
|
|
|
size_t len, optlen = 512;
|
|
|
|
uint8_t *opt;
|
|
|
|
int r;
|
2014-09-01 12:21:33 +02:00
|
|
|
usec_t elapsed_usec;
|
|
|
|
be16_t elapsed_time;
|
2014-06-19 14:39:27 +02:00
|
|
|
|
2016-05-23 16:27:05 +02:00
|
|
|
assert(client);
|
|
|
|
|
2014-06-19 14:39:27 +02:00
|
|
|
len = sizeof(DHCP6Message) + optlen;
|
|
|
|
|
|
|
|
message = malloc0(len);
|
|
|
|
if (!message)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
opt = (uint8_t *)(message + 1);
|
|
|
|
|
|
|
|
message->transaction_id = client->transaction_id;
|
|
|
|
|
|
|
|
switch(client->state) {
|
2014-12-10 15:17:32 +01:00
|
|
|
case DHCP6_STATE_INFORMATION_REQUEST:
|
|
|
|
message->type = DHCP6_INFORMATION_REQUEST;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2014-06-19 14:39:27 +02:00
|
|
|
case DHCP6_STATE_SOLICITATION:
|
|
|
|
message->type = DHCP6_SOLICIT;
|
|
|
|
|
2014-06-25 15:54:30 +02:00
|
|
|
r = dhcp6_option_append(&opt, &optlen,
|
2016-01-20 14:44:28 +01:00
|
|
|
SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
|
2014-07-01 22:56:31 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-06-25 15:54:30 +02:00
|
|
|
|
2014-06-19 14:39:53 +02:00
|
|
|
r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
|
2014-06-19 14:39:27 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-06-19 14:39:53 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DHCP6_STATE_REQUEST:
|
2014-06-25 13:06:02 +02:00
|
|
|
case DHCP6_STATE_RENEW:
|
|
|
|
|
|
|
|
if (client->state == DHCP6_STATE_REQUEST)
|
|
|
|
message->type = DHCP6_REQUEST;
|
|
|
|
else
|
|
|
|
message->type = DHCP6_RENEW;
|
2014-06-19 14:39:53 +02:00
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
|
2014-06-19 14:39:53 +02:00
|
|
|
client->lease->serverid_len,
|
|
|
|
client->lease->serverid);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
|
2014-06-19 14:39:27 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2014-06-25 13:06:02 +02:00
|
|
|
case DHCP6_STATE_REBIND:
|
|
|
|
message->type = DHCP6_REBIND;
|
|
|
|
|
|
|
|
r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2014-06-19 14:39:27 +02:00
|
|
|
case DHCP6_STATE_STOPPED:
|
2014-06-19 14:39:57 +02:00
|
|
|
case DHCP6_STATE_BOUND:
|
2014-06-19 14:39:27 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
|
2014-06-24 15:20:32 +02:00
|
|
|
client->req_opts_len * sizeof(be16_t),
|
|
|
|
client->req_opts);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-03-03 21:06:29 +01:00
|
|
|
assert (client->duid_len);
|
2016-01-20 14:44:28 +01:00
|
|
|
r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
|
2014-09-26 22:12:36 +02:00
|
|
|
client->duid_len, &client->duid);
|
2014-06-19 14:39:53 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-09-01 12:21:33 +02:00
|
|
|
elapsed_usec = time_now - client->transaction_start;
|
|
|
|
if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
|
|
|
|
elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
|
|
|
|
else
|
|
|
|
elapsed_time = 0xffff;
|
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
|
2014-09-01 12:21:33 +02:00
|
|
|
sizeof(elapsed_time), &elapsed_time);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-06-19 14:39:27 +02:00
|
|
|
r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
|
|
|
|
len - optlen);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
log_dhcp6_client(client, "Sent %s",
|
|
|
|
dhcp6_message_type_to_string(message->type));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
|
2014-06-19 14:39:57 +02:00
|
|
|
sd_dhcp6_client *client = userdata;
|
|
|
|
|
2016-05-23 16:27:05 +02:00
|
|
|
assert(s);
|
|
|
|
assert(client);
|
|
|
|
assert(client->lease);
|
2014-06-19 14:39:57 +02:00
|
|
|
|
|
|
|
client->lease->ia.timeout_t2 =
|
|
|
|
sd_event_source_unref(client->lease->ia.timeout_t2);
|
|
|
|
|
|
|
|
log_dhcp6_client(client, "Timeout T2");
|
|
|
|
|
2014-06-25 13:06:02 +02:00
|
|
|
client_start(client, DHCP6_STATE_REBIND);
|
|
|
|
|
2014-06-19 14:39:57 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
|
2014-06-19 14:39:57 +02:00
|
|
|
sd_dhcp6_client *client = userdata;
|
|
|
|
|
2016-05-23 16:27:05 +02:00
|
|
|
assert(s);
|
|
|
|
assert(client);
|
|
|
|
assert(client->lease);
|
2014-06-19 14:39:57 +02:00
|
|
|
|
|
|
|
client->lease->ia.timeout_t1 =
|
|
|
|
sd_event_source_unref(client->lease->ia.timeout_t1);
|
|
|
|
|
|
|
|
log_dhcp6_client(client, "Timeout T1");
|
|
|
|
|
2014-06-25 13:06:02 +02:00
|
|
|
client_start(client, DHCP6_STATE_RENEW);
|
|
|
|
|
2014-06-19 14:39:57 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
|
2014-06-19 14:39:15 +02:00
|
|
|
sd_dhcp6_client *client = userdata;
|
2014-06-25 13:06:02 +02:00
|
|
|
DHCP6_CLIENT_DONT_DESTROY(client);
|
|
|
|
enum DHCP6State state;
|
2014-06-19 14:39:15 +02:00
|
|
|
|
|
|
|
assert(s);
|
|
|
|
assert(client);
|
|
|
|
assert(client->event);
|
|
|
|
|
2014-06-25 13:06:02 +02:00
|
|
|
state = client->state;
|
|
|
|
|
2015-09-22 14:52:23 +02:00
|
|
|
client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
|
2014-06-19 14:39:15 +02:00
|
|
|
|
2014-06-25 13:06:02 +02:00
|
|
|
/* RFC 3315, section 18.1.4., says that "...the client may choose to
|
|
|
|
use a Solicit message to locate a new DHCP server..." */
|
|
|
|
if (state == DHCP6_STATE_REBIND)
|
|
|
|
client_start(client, DHCP6_STATE_SOLICITATION);
|
|
|
|
|
2014-06-19 14:39:15 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static usec_t client_timeout_compute_random(usec_t val) {
|
|
|
|
return val - val / 10 +
|
|
|
|
(random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
|
|
|
|
}
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
|
2014-06-19 14:39:15 +02:00
|
|
|
int r = 0;
|
|
|
|
sd_dhcp6_client *client = userdata;
|
2014-07-12 23:07:33 +02:00
|
|
|
usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
|
2014-07-02 15:53:41 +02:00
|
|
|
usec_t max_retransmit_duration = 0;
|
2014-06-19 19:44:03 +02:00
|
|
|
uint8_t max_retransmit_count = 0;
|
2014-06-19 14:39:15 +02:00
|
|
|
char time_string[FORMAT_TIMESPAN_MAX];
|
2014-06-25 13:06:02 +02:00
|
|
|
uint32_t expire = 0;
|
2014-06-19 14:39:15 +02:00
|
|
|
|
|
|
|
assert(s);
|
|
|
|
assert(client);
|
|
|
|
assert(client->event);
|
|
|
|
|
|
|
|
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
|
|
|
|
|
|
|
|
switch (client->state) {
|
2014-12-10 15:17:32 +01:00
|
|
|
case DHCP6_STATE_INFORMATION_REQUEST:
|
|
|
|
init_retransmit_time = DHCP6_INF_TIMEOUT;
|
|
|
|
max_retransmit_time = DHCP6_INF_MAX_RT;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2014-06-19 14:39:15 +02:00
|
|
|
case DHCP6_STATE_SOLICITATION:
|
2014-06-19 14:39:53 +02:00
|
|
|
|
|
|
|
if (client->retransmit_count && client->lease) {
|
|
|
|
client_start(client, DHCP6_STATE_REQUEST);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-19 14:39:15 +02:00
|
|
|
init_retransmit_time = DHCP6_SOL_TIMEOUT;
|
|
|
|
max_retransmit_time = DHCP6_SOL_MAX_RT;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2014-06-19 14:39:53 +02:00
|
|
|
case DHCP6_STATE_REQUEST:
|
|
|
|
init_retransmit_time = DHCP6_REQ_TIMEOUT;
|
|
|
|
max_retransmit_time = DHCP6_REQ_MAX_RT;
|
|
|
|
max_retransmit_count = DHCP6_REQ_MAX_RC;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2014-06-25 13:06:02 +02:00
|
|
|
case DHCP6_STATE_RENEW:
|
|
|
|
init_retransmit_time = DHCP6_REN_TIMEOUT;
|
|
|
|
max_retransmit_time = DHCP6_REN_MAX_RT;
|
|
|
|
|
|
|
|
/* RFC 3315, section 18.1.3. says max retransmit duration will
|
|
|
|
be the remaining time until T2. Instead of setting MRD,
|
|
|
|
wait for T2 to trigger with the same end result */
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DHCP6_STATE_REBIND:
|
|
|
|
init_retransmit_time = DHCP6_REB_TIMEOUT;
|
|
|
|
max_retransmit_time = DHCP6_REB_MAX_RT;
|
|
|
|
|
|
|
|
if (!client->timeout_resend_expire) {
|
|
|
|
r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
|
|
|
|
&expire);
|
|
|
|
if (r < 0) {
|
|
|
|
client_stop(client, r);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
max_retransmit_duration = expire * USEC_PER_SEC;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2014-06-19 14:39:15 +02:00
|
|
|
case DHCP6_STATE_STOPPED:
|
2014-06-19 14:39:57 +02:00
|
|
|
case DHCP6_STATE_BOUND:
|
2014-06-19 14:39:15 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (max_retransmit_count &&
|
|
|
|
client->retransmit_count >= max_retransmit_count) {
|
2015-09-22 14:52:23 +02:00
|
|
|
client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
|
2014-06-19 14:39:15 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-07-24 18:53:01 +02:00
|
|
|
r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
|
2014-06-19 14:39:15 +02:00
|
|
|
if (r < 0)
|
|
|
|
goto error;
|
|
|
|
|
2014-09-01 12:21:33 +02:00
|
|
|
r = client_send_message(client, time_now);
|
|
|
|
if (r >= 0)
|
|
|
|
client->retransmit_count++;
|
|
|
|
|
2014-06-19 14:39:15 +02:00
|
|
|
if (!client->retransmit_time) {
|
|
|
|
client->retransmit_time =
|
|
|
|
client_timeout_compute_random(init_retransmit_time);
|
2014-06-19 14:39:27 +02:00
|
|
|
|
|
|
|
if (client->state == DHCP6_STATE_SOLICITATION)
|
|
|
|
client->retransmit_time += init_retransmit_time / 10;
|
|
|
|
|
2014-06-19 14:39:15 +02:00
|
|
|
} else {
|
|
|
|
if (max_retransmit_time &&
|
|
|
|
client->retransmit_time > max_retransmit_time / 2)
|
|
|
|
client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
|
|
|
|
else
|
|
|
|
client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
|
|
|
|
}
|
|
|
|
|
|
|
|
log_dhcp6_client(client, "Next retransmission in %s",
|
2015-09-25 17:41:09 +02:00
|
|
|
format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC));
|
2014-06-19 14:39:15 +02:00
|
|
|
|
|
|
|
r = sd_event_add_time(client->event, &client->timeout_resend,
|
2014-07-24 18:53:01 +02:00
|
|
|
clock_boottime_or_monotonic(),
|
2014-06-19 14:39:15 +02:00
|
|
|
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;
|
|
|
|
|
2014-11-04 16:27:05 +01:00
|
|
|
r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer");
|
2014-08-28 15:46:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
goto error;
|
|
|
|
|
2014-06-19 14:39:15 +02:00
|
|
|
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,
|
2014-07-24 18:53:01 +02:00
|
|
|
clock_boottime_or_monotonic(),
|
2014-06-19 14:39:15 +02:00
|
|
|
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;
|
2014-08-28 15:46:29 +02:00
|
|
|
|
2014-11-04 16:27:05 +01:00
|
|
|
r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer");
|
2014-08-28 15:46:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
goto error;
|
2014-06-19 14:39:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (r < 0)
|
|
|
|
client_stop(client, r);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-19 14:39:08 +02:00
|
|
|
static int client_ensure_iaid(sd_dhcp6_client *client) {
|
2015-01-21 22:25:20 +01:00
|
|
|
int r;
|
2014-06-19 14:39:08 +02:00
|
|
|
|
|
|
|
assert(client);
|
|
|
|
|
|
|
|
if (client->ia_na.id)
|
|
|
|
return 0;
|
|
|
|
|
2016-05-23 16:13:18 +02:00
|
|
|
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
|
2015-01-21 22:25:20 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-06-19 14:39:08 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
static int client_parse_message(
|
|
|
|
sd_dhcp6_client *client,
|
|
|
|
DHCP6Message *message,
|
|
|
|
size_t len,
|
|
|
|
sd_dhcp6_lease *lease) {
|
2014-06-19 14:39:42 +02:00
|
|
|
int r;
|
2014-08-31 00:31:21 +02:00
|
|
|
uint8_t *optval, *option, *id = NULL;
|
2014-06-19 14:39:42 +02:00
|
|
|
uint16_t optcode, status;
|
|
|
|
size_t optlen, id_len;
|
|
|
|
bool clientid = false;
|
|
|
|
be32_t iaid_lease;
|
|
|
|
|
2016-05-23 16:27:05 +02:00
|
|
|
assert(client);
|
|
|
|
assert(message);
|
|
|
|
assert(len >= sizeof(DHCP6Message));
|
|
|
|
assert(lease);
|
|
|
|
|
2014-08-31 00:31:21 +02:00
|
|
|
option = (uint8_t *)message + sizeof(DHCP6Message);
|
|
|
|
len -= sizeof(DHCP6Message);
|
|
|
|
|
2014-06-19 14:39:42 +02:00
|
|
|
while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
|
|
|
|
&optval)) >= 0) {
|
|
|
|
switch (optcode) {
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_CLIENTID:
|
2014-06-19 14:39:42 +02:00
|
|
|
if (clientid) {
|
|
|
|
log_dhcp6_client(client, "%s contains multiple clientids",
|
|
|
|
dhcp6_message_type_to_string(message->type));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2014-09-26 22:12:36 +02:00
|
|
|
if (optlen != client->duid_len ||
|
2014-06-19 14:39:42 +02:00
|
|
|
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;
|
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_SERVERID:
|
2014-06-19 14:39:42 +02:00
|
|
|
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;
|
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_PREFERENCE:
|
2014-06-19 14:39:42 +02:00
|
|
|
if (optlen != 1)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
r = dhcp6_lease_set_preference(lease, *optval);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_STATUS_CODE:
|
2014-06-19 14:39:42 +02:00
|
|
|
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;
|
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_IA_NA:
|
2014-12-10 15:17:32 +01:00
|
|
|
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
|
|
|
log_dhcp6_client(client, "Information request ignoring IA NA option");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-06-19 14:39:42 +02:00
|
|
|
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;
|
2014-06-25 15:54:30 +02:00
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_RAPID_COMMIT:
|
2014-06-25 15:54:30 +02:00
|
|
|
r = dhcp6_lease_set_rapid_commit(lease);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
break;
|
2015-04-02 09:50:16 +02:00
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_DNS_SERVERS:
|
2015-04-02 09:50:16 +02:00
|
|
|
r = dhcp6_lease_set_dns(lease, optval, optlen);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
break;
|
2015-04-02 14:34:12 +02:00
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_DOMAIN_LIST:
|
2015-04-02 14:34:12 +02:00
|
|
|
r = dhcp6_lease_set_domains(lease, optval, optlen);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_NTP_SERVER:
|
2015-04-10 14:59:00 +02:00
|
|
|
r = dhcp6_lease_set_ntp(lease, optval, optlen);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
break;
|
2015-04-10 15:17:22 +02:00
|
|
|
|
2016-01-20 14:44:28 +01:00
|
|
|
case SD_DHCP6_OPTION_SNTP_SERVERS:
|
2015-04-10 15:17:22 +02:00
|
|
|
r = dhcp6_lease_set_sntp(lease, optval, optlen);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
break;
|
2014-06-19 14:39:42 +02:00
|
|
|
}
|
2015-04-10 14:59:00 +02:00
|
|
|
|
2014-06-19 14:39:42 +02:00
|
|
|
}
|
|
|
|
|
2014-12-10 15:17:30 +01:00
|
|
|
if (r == -ENOMSG)
|
|
|
|
r = 0;
|
|
|
|
|
|
|
|
if (r < 0 || !clientid) {
|
2014-06-19 14:39:42 +02:00
|
|
|
log_dhcp6_client(client, "%s has incomplete options",
|
|
|
|
dhcp6_message_type_to_string(message->type));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2014-12-10 15:17:32 +01:00
|
|
|
if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
|
|
|
|
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));
|
|
|
|
}
|
2014-06-19 14:39:42 +02:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2015-08-26 21:09:00 +02:00
|
|
|
static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
|
2014-06-25 15:54:30 +02:00
|
|
|
bool rapid_commit;
|
2016-05-23 16:27:05 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(client);
|
|
|
|
assert(reply);
|
2014-06-19 14:39:57 +02:00
|
|
|
|
|
|
|
if (reply->type != DHCP6_REPLY)
|
2014-06-25 15:54:30 +02:00
|
|
|
return 0;
|
2014-06-19 14:39:57 +02:00
|
|
|
|
|
|
|
r = dhcp6_lease_new(&lease);
|
|
|
|
if (r < 0)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
r = client_parse_message(client, reply, len, lease);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-06-25 15:54:30 +02:00
|
|
|
if (client->state == DHCP6_STATE_SOLICITATION) {
|
|
|
|
r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (!rapid_commit)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-22 14:17:32 +02:00
|
|
|
client_set_lease(client, lease);
|
2015-07-10 10:31:50 +02:00
|
|
|
lease = NULL;
|
2014-06-19 14:39:57 +02:00
|
|
|
|
|
|
|
return DHCP6_STATE_BOUND;
|
|
|
|
}
|
|
|
|
|
2015-08-26 21:09:00 +02:00
|
|
|
static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
|
2014-06-19 14:39:42 +02:00
|
|
|
uint8_t pref_advertise = 0, pref_lease = 0;
|
2016-05-23 16:27:05 +02:00
|
|
|
int r;
|
2014-06-19 14:39:42 +02:00
|
|
|
|
|
|
|
if (advertise->type != DHCP6_ADVERTISE)
|
2014-06-25 15:54:30 +02:00
|
|
|
return 0;
|
2014-06-19 14:39:42 +02:00
|
|
|
|
|
|
|
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);
|
2014-12-10 15:17:32 +01:00
|
|
|
|
|
|
|
if (r < 0 || pref_advertise > pref_lease) {
|
2015-09-22 14:17:32 +02:00
|
|
|
client_set_lease(client, lease);
|
2014-06-19 14:39:42 +02:00
|
|
|
lease = NULL;
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
2014-06-19 14:39:53 +02:00
|
|
|
if (pref_advertise == 255 || client->retransmit_count > 1)
|
|
|
|
r = DHCP6_STATE_REQUEST;
|
|
|
|
|
2014-06-19 14:39:42 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2016-05-23 15:56:01 +02:00
|
|
|
static int client_receive_message(
|
|
|
|
sd_event_source *s,
|
|
|
|
int fd, uint32_t
|
|
|
|
revents,
|
|
|
|
void *userdata) {
|
|
|
|
|
2014-06-19 14:39:42 +02:00
|
|
|
sd_dhcp6_client *client = userdata;
|
2014-06-25 09:36:56 +02:00
|
|
|
DHCP6_CLIENT_DONT_DESTROY(client);
|
2015-11-24 18:25:52 +01:00
|
|
|
_cleanup_free_ DHCP6Message *message = NULL;
|
2016-02-15 22:50:01 +01:00
|
|
|
ssize_t buflen, len;
|
|
|
|
int r = 0;
|
2014-06-19 14:39:42 +02:00
|
|
|
|
|
|
|
assert(s);
|
|
|
|
assert(client);
|
|
|
|
assert(client->event);
|
|
|
|
|
2016-02-15 22:50:01 +01:00
|
|
|
buflen = next_datagram_size_fd(fd);
|
|
|
|
if (buflen < 0)
|
|
|
|
return buflen;
|
2014-06-19 14:39:42 +02:00
|
|
|
|
2015-11-24 18:25:52 +01:00
|
|
|
message = malloc(buflen);
|
2014-06-19 14:39:42 +02:00
|
|
|
if (!message)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2016-05-21 23:00:32 +02:00
|
|
|
len = recv(fd, message, buflen, 0);
|
2015-11-24 18:25:52 +01:00
|
|
|
if (len < 0) {
|
2017-10-04 16:01:32 +02:00
|
|
|
if (IN_SET(errno, EAGAIN, EINTR))
|
2015-11-24 18:25:52 +01:00
|
|
|
return 0;
|
|
|
|
|
2016-05-15 16:46:17 +02:00
|
|
|
return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
|
2015-11-24 18:25:52 +01:00
|
|
|
|
2016-05-23 15:56:01 +02:00
|
|
|
}
|
|
|
|
if ((size_t) len < sizeof(DHCP6Message)) {
|
|
|
|
log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
|
2014-06-19 14:39:42 +02:00
|
|
|
return 0;
|
2016-05-23 15:56:01 +02:00
|
|
|
}
|
2014-06-19 14:39:42 +02:00
|
|
|
|
|
|
|
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:
|
2016-05-15 16:46:17 +02:00
|
|
|
log_dhcp6_client(client, "Unknown message type %d", message->type);
|
2014-06-19 14:39:42 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (client->transaction_id != (message->transaction_id &
|
|
|
|
htobe32(0x00ffffff)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (client->state) {
|
2014-12-10 15:17:32 +01:00
|
|
|
case DHCP6_STATE_INFORMATION_REQUEST:
|
|
|
|
r = client_receive_reply(client, message, len);
|
|
|
|
if (r < 0)
|
|
|
|
return 0;
|
|
|
|
|
2015-09-22 14:52:23 +02:00
|
|
|
client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
|
2014-12-10 15:17:32 +01:00
|
|
|
|
|
|
|
client_start(client, DHCP6_STATE_STOPPED);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2014-06-19 14:39:42 +02:00
|
|
|
case DHCP6_STATE_SOLICITATION:
|
|
|
|
r = client_receive_advertise(client, message, len);
|
|
|
|
|
2014-06-25 15:54:30 +02:00
|
|
|
if (r == DHCP6_STATE_REQUEST) {
|
2014-06-19 14:39:53 +02:00
|
|
|
client_start(client, r);
|
|
|
|
|
2014-06-25 15:54:30 +02:00
|
|
|
break;
|
|
|
|
}
|
2014-06-19 14:39:42 +02:00
|
|
|
|
2017-01-27 06:50:10 +01:00
|
|
|
/* fall through */ /* for Soliciation Rapid Commit option check */
|
2014-06-19 14:39:53 +02:00
|
|
|
case DHCP6_STATE_REQUEST:
|
2014-06-25 13:06:02 +02:00
|
|
|
case DHCP6_STATE_RENEW:
|
|
|
|
case DHCP6_STATE_REBIND:
|
|
|
|
|
2014-06-19 14:39:57 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-09-22 14:52:23 +02:00
|
|
|
client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
|
2014-06-19 14:39:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DHCP6_STATE_BOUND:
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2014-06-19 14:39:42 +02:00
|
|
|
case DHCP6_STATE_STOPPED:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-15 16:46:17 +02:00
|
|
|
if (r >= 0)
|
2014-06-19 14:39:42 +02:00
|
|
|
log_dhcp6_client(client, "Recv %s",
|
|
|
|
dhcp6_message_type_to_string(message->type));
|
|
|
|
|
2014-06-19 14:39:27 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-26 21:09:00 +02:00
|
|
|
static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
|
2014-06-19 14:39:08 +02:00
|
|
|
int r;
|
2014-06-19 14:39:57 +02:00
|
|
|
usec_t timeout, time_now;
|
|
|
|
char time_string[FORMAT_TIMESPAN_MAX];
|
2014-06-19 14:39:08 +02:00
|
|
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
assert_return(client->event, -EINVAL);
|
2016-05-23 16:13:18 +02:00
|
|
|
assert_return(client->ifindex > 0, -EINVAL);
|
2014-06-19 14:39:49 +02:00
|
|
|
assert_return(client->state != state, -EINVAL);
|
2014-06-19 14:39:08 +02:00
|
|
|
|
2014-06-19 14:39:49 +02:00
|
|
|
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;
|
2014-06-19 14:39:08 +02:00
|
|
|
|
sd-event: make sure sd_event_now() cannot fail
Previously, if the event loop never ran before sd_event_now() would
fail. With this change it will instead fall back to invoking now(). This
way, the function cannot fail anymore, except for programming error when
invoking it with wrong parameters.
This takes into account the fact that many callers did not handle the
error condition correctly, and if the callers did, then they kept simply
invoking now() as fall back on their own. Hence let's shorten the code
using this call, and make things more robust, and let's just fall back
to now() internally.
Whether now() is used or the cache timestamp may still be detected via
the return value of sd_event_now(). If > 0 is returned, then the fall
back to now() was used, if == 0 is returned, then the cached value was
returned.
This patch also simplifies many of the invocations of sd_event_now():
the manual fall back to now() can be removed. Also, in cases where the
call is invoked withing void functions we can now protect the invocation
via assert_se(), acknowledging the fact that the call cannot fail
anymore except for programming errors with the parameters.
This change is inspired by #841.
2015-08-03 17:29:09 +02:00
|
|
|
r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-09-01 12:21:33 +02:00
|
|
|
|
2014-06-19 14:39:49 +02:00
|
|
|
switch (state) {
|
|
|
|
case DHCP6_STATE_STOPPED:
|
2014-12-10 15:17:32 +01:00
|
|
|
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
|
|
|
client->state = DHCP6_STATE_STOPPED;
|
2014-06-19 14:39:27 +02:00
|
|
|
|
2014-12-10 15:17:32 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-08-28 15:46:29 +02:00
|
|
|
|
2014-12-10 15:17:32 +01:00
|
|
|
/* fall through */
|
|
|
|
case DHCP6_STATE_SOLICITATION:
|
2014-06-19 14:39:49 +02:00
|
|
|
client->state = DHCP6_STATE_SOLICITATION;
|
|
|
|
|
2014-06-19 14:39:53 +02:00
|
|
|
break;
|
|
|
|
|
2014-12-10 15:17:32 +01:00
|
|
|
case DHCP6_STATE_INFORMATION_REQUEST:
|
2014-06-19 14:39:53 +02:00
|
|
|
case DHCP6_STATE_REQUEST:
|
2014-06-25 13:06:02 +02:00
|
|
|
case DHCP6_STATE_RENEW:
|
|
|
|
case DHCP6_STATE_REBIND:
|
2014-06-19 14:39:57 +02:00
|
|
|
|
2014-06-19 14:39:53 +02:00
|
|
|
client->state = state;
|
|
|
|
|
2014-06-19 14:39:49 +02:00
|
|
|
break;
|
2014-06-19 14:39:57 +02:00
|
|
|
|
|
|
|
case DHCP6_STATE_BOUND:
|
|
|
|
|
|
|
|
if (client->lease->ia.lifetime_t1 == 0xffffffff ||
|
|
|
|
client->lease->ia.lifetime_t2 == 0xffffffff) {
|
|
|
|
|
2016-05-15 16:46:17 +02:00
|
|
|
log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
|
2014-06-19 14:39:57 +02:00
|
|
|
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",
|
2015-09-25 17:41:09 +02:00
|
|
|
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
|
2014-06-19 14:39:57 +02:00
|
|
|
|
|
|
|
r = sd_event_add_time(client->event,
|
|
|
|
&client->lease->ia.timeout_t1,
|
2014-07-24 18:53:01 +02:00
|
|
|
clock_boottime_or_monotonic(), time_now + timeout,
|
2014-06-19 14:39:57 +02:00
|
|
|
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;
|
|
|
|
|
2014-11-04 16:27:05 +01:00
|
|
|
r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
|
2014-08-28 15:46:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-06-19 14:39:57 +02:00
|
|
|
timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
|
|
|
|
|
|
|
|
log_dhcp6_client(client, "T2 expires in %s",
|
2015-09-25 17:41:09 +02:00
|
|
|
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
|
2014-06-19 14:39:57 +02:00
|
|
|
|
|
|
|
r = sd_event_add_time(client->event,
|
|
|
|
&client->lease->ia.timeout_t2,
|
2014-07-24 18:53:01 +02:00
|
|
|
clock_boottime_or_monotonic(), time_now + timeout,
|
2014-06-19 14:39:57 +02:00
|
|
|
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;
|
|
|
|
|
2014-11-04 16:27:05 +01:00
|
|
|
r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
|
2014-08-28 15:46:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-06-25 13:06:02 +02:00
|
|
|
client->state = state;
|
|
|
|
|
2014-06-19 14:39:57 +02:00
|
|
|
return 0;
|
2014-06-19 14:39:49 +02:00
|
|
|
}
|
2014-06-19 14:39:27 +02:00
|
|
|
|
2014-06-19 14:39:49 +02:00
|
|
|
client->transaction_id = random_u32() & htobe32(0x00ffffff);
|
2014-09-01 12:21:33 +02:00
|
|
|
client->transaction_start = time_now;
|
2014-06-19 14:39:15 +02:00
|
|
|
|
|
|
|
r = sd_event_add_time(client->event, &client->timeout_resend,
|
2014-07-24 18:53:01 +02:00
|
|
|
clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
|
2014-06-19 14:39:15 +02:00
|
|
|
client);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_event_source_set_priority(client->timeout_resend,
|
|
|
|
client->event_priority);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-11-04 16:27:05 +01:00
|
|
|
r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
|
2014-08-28 15:46:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-06-19 14:39:08 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-26 21:09:00 +02:00
|
|
|
int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
|
2015-11-10 15:41:30 +01:00
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
2015-09-22 14:52:23 +02:00
|
|
|
client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
|
2014-06-19 14:38:50 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-10 15:41:30 +01:00
|
|
|
int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
|
|
|
return client->state != DHCP6_STATE_STOPPED;
|
|
|
|
}
|
|
|
|
|
2015-08-26 21:09:00 +02:00
|
|
|
int sd_dhcp6_client_start(sd_dhcp6_client *client) {
|
2014-12-10 15:17:32 +01:00
|
|
|
enum DHCP6State state = DHCP6_STATE_SOLICITATION;
|
2016-05-23 16:13:18 +02:00
|
|
|
int r = 0;
|
2014-06-19 14:38:50 +02:00
|
|
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
assert_return(client->event, -EINVAL);
|
2016-05-23 16:13:18 +02:00
|
|
|
assert_return(client->ifindex > 0, -EINVAL);
|
2015-11-16 17:43:08 +01:00
|
|
|
assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
|
2014-06-19 14:38:50 +02:00
|
|
|
|
2015-09-23 12:51:53 +02:00
|
|
|
if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
|
2015-11-16 16:46:14 +01:00
|
|
|
return -EBUSY;
|
2015-09-23 12:51:53 +02:00
|
|
|
|
2014-06-19 15:08:37 +02:00
|
|
|
r = client_reset(client);
|
2014-06-19 14:39:08 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-12-10 15:17:32 +01:00
|
|
|
r = client_ensure_iaid(client);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-03-03 21:06:29 +01:00
|
|
|
r = client_ensure_duid(client);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2016-05-23 16:13:18 +02:00
|
|
|
r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
|
2016-05-15 16:46:17 +02:00
|
|
|
if (r < 0) {
|
|
|
|
_cleanup_free_ char *p = NULL;
|
|
|
|
|
|
|
|
(void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
|
|
|
|
return log_dhcp6_client_errno(client, r,
|
|
|
|
"Failed to bind to UDP socket at address %s: %m", strna(p));
|
|
|
|
}
|
2014-12-10 15:17:32 +01:00
|
|
|
|
|
|
|
client->fd = r;
|
|
|
|
|
|
|
|
r = sd_event_add_io(client->event, &client->receive_message,
|
|
|
|
client->fd, EPOLLIN, client_receive_message,
|
|
|
|
client);
|
|
|
|
if (r < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
r = sd_event_source_set_priority(client->receive_message,
|
|
|
|
client->event_priority);
|
|
|
|
if (r < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
r = sd_event_source_set_description(client->receive_message,
|
2016-05-15 16:22:40 +02:00
|
|
|
"dhcp6-receive-message");
|
2014-12-10 15:17:32 +01:00
|
|
|
if (r < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (client->information_request)
|
|
|
|
state = DHCP6_STATE_INFORMATION_REQUEST;
|
|
|
|
|
|
|
|
log_dhcp6_client(client, "Started in %s mode",
|
2016-05-15 16:22:40 +02:00
|
|
|
client->information_request? "Information request":
|
|
|
|
"Managed");
|
2014-12-10 15:17:32 +01:00
|
|
|
|
|
|
|
return client_start(client, state);
|
|
|
|
|
|
|
|
error:
|
|
|
|
client_reset(client);
|
|
|
|
return r;
|
2014-06-19 14:38:50 +02:00
|
|
|
}
|
|
|
|
|
2016-02-16 19:33:36 +01:00
|
|
|
int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
|
2014-06-19 14:38:50 +02:00
|
|
|
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) {
|
2016-05-23 16:27:05 +02:00
|
|
|
assert_return(client, NULL);
|
2014-06-19 14:38:50 +02:00
|
|
|
|
|
|
|
return client->event;
|
|
|
|
}
|
|
|
|
|
|
|
|
sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
|
2015-08-26 21:05:53 +02:00
|
|
|
|
|
|
|
if (!client)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
assert(client->n_ref >= 1);
|
|
|
|
client->n_ref++;
|
2014-06-19 14:38:50 +02:00
|
|
|
|
|
|
|
return client;
|
|
|
|
}
|
|
|
|
|
|
|
|
sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
|
|
|
|
|
2015-08-26 21:05:53 +02:00
|
|
|
if (!client)
|
|
|
|
return NULL;
|
2014-06-19 14:38:50 +02:00
|
|
|
|
2015-08-26 21:05:53 +02:00
|
|
|
assert(client->n_ref >= 1);
|
|
|
|
client->n_ref--;
|
2014-06-19 14:38:50 +02:00
|
|
|
|
2015-08-26 21:05:53 +02:00
|
|
|
if (client->n_ref > 0)
|
2014-06-19 14:38:50 +02:00
|
|
|
return NULL;
|
|
|
|
|
2015-08-26 21:05:53 +02:00
|
|
|
client_reset(client);
|
|
|
|
|
|
|
|
sd_dhcp6_client_detach_event(client);
|
|
|
|
|
|
|
|
free(client->req_opts);
|
2016-10-17 00:28:30 +02:00
|
|
|
return mfree(client);
|
2014-06-19 14:38:50 +02:00
|
|
|
}
|
|
|
|
|
2015-08-26 21:09:00 +02:00
|
|
|
int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
|
2014-06-24 15:20:32 +02:00
|
|
|
size_t t;
|
2014-06-19 14:38:50 +02:00
|
|
|
|
|
|
|
assert_return(ret, -EINVAL);
|
|
|
|
|
|
|
|
client = new0(sd_dhcp6_client, 1);
|
|
|
|
if (!client)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2015-08-26 21:05:53 +02:00
|
|
|
client->n_ref = 1;
|
2016-01-20 14:44:28 +01:00
|
|
|
client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
|
2016-05-23 16:13:18 +02:00
|
|
|
client->ifindex = -1;
|
2014-06-19 15:08:37 +02:00
|
|
|
client->fd = -1;
|
|
|
|
|
2014-06-24 15:20:32 +02:00
|
|
|
client->req_opts_len = ELEMENTSOF(default_req_opts);
|
|
|
|
client->req_opts = new0(be16_t, client->req_opts_len);
|
|
|
|
if (!client->req_opts)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
for (t = 0; t < client->req_opts_len; t++)
|
|
|
|
client->req_opts[t] = htobe16(default_req_opts[t]);
|
|
|
|
|
2014-06-19 14:38:50 +02:00
|
|
|
*ret = client;
|
|
|
|
client = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|