2020-11-09 05:23:58 +01:00
|
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2013-12-09 22:43:08 +01:00
|
|
|
|
/***
|
2018-06-12 17:15:23 +02:00
|
|
|
|
Copyright © 2013 Intel Corporation. All rights reserved.
|
2013-12-09 22:43:08 +01:00
|
|
|
|
***/
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
2013-12-09 22:43:19 +01:00
|
|
|
|
#include <net/ethernet.h>
|
2014-04-06 14:05:32 +02:00
|
|
|
|
#include <net/if_arp.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
2014-02-23 17:30:13 +01:00
|
|
|
|
#include <sys/ioctl.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
|
#include <linux/if_infiniband.h>
|
2013-12-09 22:43:08 +01:00
|
|
|
|
|
2015-10-24 22:58:24 +02:00
|
|
|
|
#include "sd-dhcp-client.h"
|
2013-12-09 22:43:08 +01: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"
|
2013-12-09 22:43:19 +01:00
|
|
|
|
#include "dhcp-internal.h"
|
2014-02-27 01:24:05 +01:00
|
|
|
|
#include "dhcp-lease-internal.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
|
#include "dhcp-protocol.h"
|
2015-11-16 10:17:48 +01:00
|
|
|
|
#include "dns-domain.h"
|
2018-11-13 05:50:08 +01:00
|
|
|
|
#include "event-util.h"
|
2020-10-13 14:11:36 +02:00
|
|
|
|
#include "fd-util.h"
|
2015-11-16 10:17:48 +01:00
|
|
|
|
#include "hostname-util.h"
|
2018-11-27 10:34:32 +01:00
|
|
|
|
#include "io-util.h"
|
2019-03-13 12:02:21 +01:00
|
|
|
|
#include "memory-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
|
#include "random-util.h"
|
2020-10-14 05:47:58 +02:00
|
|
|
|
#include "set.h"
|
|
|
|
|
#include "sort-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
|
#include "string-util.h"
|
2018-05-07 14:21:02 +02:00
|
|
|
|
#include "strv.h"
|
2020-12-08 21:33:29 +01:00
|
|
|
|
#include "time-util.h"
|
2020-05-22 11:30:49 +02:00
|
|
|
|
#include "utf8.h"
|
2020-03-30 16:42:48 +02:00
|
|
|
|
#include "web-util.h"
|
2013-12-09 22:43:08 +01:00
|
|
|
|
|
2015-01-22 00:53:16 +01:00
|
|
|
|
#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
|
2015-04-12 20:15:08 +02:00
|
|
|
|
#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
|
2014-10-08 21:15:45 +02:00
|
|
|
|
|
2016-01-27 11:21:23 +01:00
|
|
|
|
#define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC)
|
|
|
|
|
#define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE)
|
|
|
|
|
|
2020-10-08 16:51:25 +02:00
|
|
|
|
#define TRANSIENT_FAILURE_ATTEMPTS 3 /* Arbitrary limit: how many attempts are considered enough to report
|
|
|
|
|
* transient failure. */
|
|
|
|
|
|
2020-05-22 11:27:49 +02:00
|
|
|
|
typedef struct sd_dhcp_client_id {
|
|
|
|
|
uint8_t type;
|
|
|
|
|
union {
|
|
|
|
|
struct {
|
|
|
|
|
/* 0: Generic (non-LL) (RFC 2132) */
|
|
|
|
|
uint8_t data[MAX_CLIENT_ID_LEN];
|
|
|
|
|
} _packed_ gen;
|
|
|
|
|
struct {
|
|
|
|
|
/* 1: Ethernet Link-Layer (RFC 2132) */
|
|
|
|
|
uint8_t haddr[ETH_ALEN];
|
|
|
|
|
} _packed_ eth;
|
|
|
|
|
struct {
|
|
|
|
|
/* 2 - 254: ARP/Link-Layer (RFC 2132) */
|
|
|
|
|
uint8_t haddr[0];
|
|
|
|
|
} _packed_ ll;
|
|
|
|
|
struct {
|
|
|
|
|
/* 255: Node-specific (RFC 4361) */
|
|
|
|
|
be32_t iaid;
|
|
|
|
|
struct duid duid;
|
|
|
|
|
} _packed_ ns;
|
|
|
|
|
struct {
|
|
|
|
|
uint8_t data[MAX_CLIENT_ID_LEN];
|
|
|
|
|
} _packed_ raw;
|
|
|
|
|
};
|
|
|
|
|
} _packed_ sd_dhcp_client_id;
|
|
|
|
|
|
2013-12-09 22:43:08 +01:00
|
|
|
|
struct sd_dhcp_client {
|
2015-08-26 21:05:53 +02:00
|
|
|
|
unsigned n_ref;
|
2014-04-09 12:12:07 +02:00
|
|
|
|
|
2013-12-09 22:43:08 +01:00
|
|
|
|
DHCPState state;
|
2013-12-09 22:43:25 +01:00
|
|
|
|
sd_event *event;
|
2014-01-18 19:32:45 +01:00
|
|
|
|
int event_priority;
|
2013-12-09 22:43:25 +01:00
|
|
|
|
sd_event_source *timeout_resend;
|
2016-05-23 16:13:18 +02:00
|
|
|
|
int ifindex;
|
2013-12-09 22:43:26 +01:00
|
|
|
|
int fd;
|
2016-11-11 00:34:19 +01:00
|
|
|
|
uint16_t port;
|
2013-12-09 22:43:26 +01:00
|
|
|
|
union sockaddr_union link;
|
|
|
|
|
sd_event_source *receive_message;
|
2014-07-15 18:55:31 +02:00
|
|
|
|
bool request_broadcast;
|
2020-10-14 05:47:58 +02:00
|
|
|
|
Set *req_opts;
|
2017-08-03 03:32:46 +02:00
|
|
|
|
bool anonymize;
|
2013-12-20 16:16:10 +01:00
|
|
|
|
be32_t last_addr;
|
2014-10-08 21:15:45 +02:00
|
|
|
|
uint8_t mac_addr[MAX_MAC_ADDR_LEN];
|
|
|
|
|
size_t mac_addr_len;
|
2020-10-26 14:09:13 +01:00
|
|
|
|
uint8_t bcast_addr[MAX_MAC_ADDR_LEN];
|
|
|
|
|
size_t bcast_addr_len;
|
2014-10-08 21:15:45 +02:00
|
|
|
|
uint16_t arp_type;
|
2020-05-22 11:27:49 +02:00
|
|
|
|
sd_dhcp_client_id client_id;
|
2014-11-19 00:01:20 +01:00
|
|
|
|
size_t client_id_len;
|
2014-07-01 20:58:49 +02:00
|
|
|
|
char *hostname;
|
2014-07-14 10:04:18 +02:00
|
|
|
|
char *vendor_class_identifier;
|
2020-03-30 16:42:48 +02:00
|
|
|
|
char *mudurl;
|
2018-05-07 14:21:02 +02:00
|
|
|
|
char **user_class;
|
2014-08-01 16:10:13 +02:00
|
|
|
|
uint32_t mtu;
|
2020-05-20 06:30:05 +02:00
|
|
|
|
uint32_t fallback_lease_lifetime;
|
2013-12-09 22:43:19 +01:00
|
|
|
|
uint32_t xid;
|
2013-12-09 22:43:25 +01:00
|
|
|
|
usec_t start_time;
|
2020-12-08 20:37:59 +01:00
|
|
|
|
usec_t t1_time;
|
|
|
|
|
usec_t t2_time;
|
|
|
|
|
usec_t expire_time;
|
2019-05-04 13:40:54 +02:00
|
|
|
|
uint64_t attempt;
|
|
|
|
|
uint64_t max_attempts;
|
2020-02-28 19:28:49 +01:00
|
|
|
|
OrderedHashmap *extra_options;
|
|
|
|
|
OrderedHashmap *vendor_options;
|
2013-12-09 22:43:30 +01:00
|
|
|
|
usec_t request_sent;
|
|
|
|
|
sd_event_source *timeout_t1;
|
|
|
|
|
sd_event_source *timeout_t2;
|
|
|
|
|
sd_event_source *timeout_expire;
|
2016-05-23 16:48:56 +02:00
|
|
|
|
sd_dhcp_client_callback_t callback;
|
2013-12-09 22:43:31 +01:00
|
|
|
|
void *userdata;
|
2014-02-04 23:13:52 +01:00
|
|
|
|
sd_dhcp_lease *lease;
|
2016-01-27 11:21:23 +01:00
|
|
|
|
usec_t start_delay;
|
2019-09-23 13:25:21 +02:00
|
|
|
|
int ip_service_type;
|
2013-12-09 22:43:08 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const uint8_t default_req_opts[] = {
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_SUBNET_MASK,
|
|
|
|
|
SD_DHCP_OPTION_ROUTER,
|
|
|
|
|
SD_DHCP_OPTION_HOST_NAME,
|
|
|
|
|
SD_DHCP_OPTION_DOMAIN_NAME,
|
|
|
|
|
SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
|
2013-12-09 22:43:08 +01:00
|
|
|
|
};
|
|
|
|
|
|
2017-08-03 03:32:46 +02:00
|
|
|
|
/* RFC7844 section 3:
|
|
|
|
|
MAY contain the Parameter Request List option.
|
|
|
|
|
RFC7844 section 3.6:
|
|
|
|
|
The client intending to protect its privacy SHOULD only request a
|
|
|
|
|
minimal number of options in the PRL and SHOULD also randomly shuffle
|
|
|
|
|
the ordering of option codes in the PRL. If this random ordering
|
|
|
|
|
cannot be implemented, the client MAY order the option codes in the
|
|
|
|
|
PRL by option code number (lowest to highest).
|
|
|
|
|
*/
|
|
|
|
|
/* NOTE: using PRL options that Windows 10 RFC7844 implementation uses */
|
|
|
|
|
static const uint8_t default_req_opts_anonymize[] = {
|
2018-12-22 23:57:28 +01:00
|
|
|
|
SD_DHCP_OPTION_SUBNET_MASK, /* 1 */
|
|
|
|
|
SD_DHCP_OPTION_ROUTER, /* 3 */
|
|
|
|
|
SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */
|
|
|
|
|
SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */
|
|
|
|
|
SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */
|
|
|
|
|
SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */
|
|
|
|
|
SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */
|
|
|
|
|
SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */
|
|
|
|
|
SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */
|
|
|
|
|
SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */
|
|
|
|
|
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */
|
|
|
|
|
SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */
|
|
|
|
|
SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, /* 252 */
|
2017-08-03 03:32:46 +02:00
|
|
|
|
};
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int client_receive_message_raw(
|
|
|
|
|
sd_event_source *s,
|
|
|
|
|
int fd,
|
|
|
|
|
uint32_t revents,
|
|
|
|
|
void *userdata);
|
|
|
|
|
static int client_receive_message_udp(
|
|
|
|
|
sd_event_source *s,
|
|
|
|
|
int fd,
|
|
|
|
|
uint32_t revents,
|
|
|
|
|
void *userdata);
|
2014-05-22 15:18:28 +02:00
|
|
|
|
static void client_stop(sd_dhcp_client *client, int error);
|
2013-12-20 16:16:18 +01:00
|
|
|
|
|
2020-05-22 11:30:49 +02:00
|
|
|
|
int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) {
|
|
|
|
|
const sd_dhcp_client_id *client_id = data;
|
|
|
|
|
_cleanup_free_ char *t = NULL;
|
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
|
|
assert_return(data, -EINVAL);
|
|
|
|
|
assert_return(len >= 1, -EINVAL);
|
|
|
|
|
assert_return(ret, -EINVAL);
|
|
|
|
|
|
|
|
|
|
len -= 1;
|
|
|
|
|
if (len > MAX_CLIENT_ID_LEN)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
switch (client_id->type) {
|
|
|
|
|
case 0:
|
|
|
|
|
if (utf8_is_printable((char *) client_id->gen.data, len))
|
|
|
|
|
r = asprintf(&t, "%.*s", (int) len, client_id->gen.data);
|
|
|
|
|
else
|
|
|
|
|
r = asprintf(&t, "DATA");
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
if (len != sizeof_field(sd_dhcp_client_id, eth))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
r = asprintf(&t, "%x:%x:%x:%x:%x:%x",
|
|
|
|
|
client_id->eth.haddr[0],
|
|
|
|
|
client_id->eth.haddr[1],
|
|
|
|
|
client_id->eth.haddr[2],
|
|
|
|
|
client_id->eth.haddr[3],
|
|
|
|
|
client_id->eth.haddr[4],
|
|
|
|
|
client_id->eth.haddr[5]);
|
|
|
|
|
break;
|
|
|
|
|
case 2 ... 254:
|
|
|
|
|
r = asprintf(&t, "ARP/LL");
|
|
|
|
|
break;
|
|
|
|
|
case 255:
|
|
|
|
|
if (len < 6)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
uint32_t iaid = be32toh(client_id->ns.iaid);
|
|
|
|
|
uint16_t duid_type = be16toh(client_id->ns.duid.type);
|
|
|
|
|
if (dhcp_validate_duid_len(duid_type, len - 6, true) < 0)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
r = asprintf(&t, "IAID:0x%x/DUID", iaid);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
*ret = TAKE_PTR(t);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
int sd_dhcp_client_set_callback(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
sd_dhcp_client_callback_t cb,
|
|
|
|
|
void *userdata) {
|
2016-05-23 16:48:56 +02:00
|
|
|
|
|
2013-12-09 22:43:31 +01:00
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
|
2016-05-23 16:48:56 +02:00
|
|
|
|
client->callback = cb;
|
2013-12-09 22:43:31 +01:00
|
|
|
|
client->userdata = userdata;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-15 18:55:31 +02:00
|
|
|
|
int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
|
|
|
|
|
client->request_broadcast = !!broadcast;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-16 19:55:25 +01:00
|
|
|
|
int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
|
2013-12-09 22:43:08 +01:00
|
|
|
|
assert_return(client, -EINVAL);
|
2016-05-23 16:27:05 +02:00
|
|
|
|
assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
|
2013-12-09 22:43:08 +01:00
|
|
|
|
|
|
|
|
|
switch(option) {
|
2016-05-23 16:27:05 +02:00
|
|
|
|
|
2016-01-20 14:44:24 +01:00
|
|
|
|
case SD_DHCP_OPTION_PAD:
|
|
|
|
|
case SD_DHCP_OPTION_OVERLOAD:
|
|
|
|
|
case SD_DHCP_OPTION_MESSAGE_TYPE:
|
|
|
|
|
case SD_DHCP_OPTION_PARAMETER_REQUEST_LIST:
|
|
|
|
|
case SD_DHCP_OPTION_END:
|
2013-12-09 22:43:08 +01:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-14 05:47:58 +02:00
|
|
|
|
return set_ensure_put(&client->req_opts, NULL, UINT8_TO_PTR(option));
|
2013-12-09 22:43:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
int sd_dhcp_client_set_request_address(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
const struct in_addr *last_addr) {
|
2016-05-23 16:27:05 +02:00
|
|
|
|
|
2013-12-09 22:43:08 +01:00
|
|
|
|
assert_return(client, -EINVAL);
|
2016-05-23 16:27:05 +02:00
|
|
|
|
assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
|
2013-12-09 22:43:08 +01:00
|
|
|
|
|
|
|
|
|
if (last_addr)
|
|
|
|
|
client->last_addr = last_addr->s_addr;
|
|
|
|
|
else
|
|
|
|
|
client->last_addr = INADDR_ANY;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-23 16:13:18 +02:00
|
|
|
|
int sd_dhcp_client_set_ifindex(sd_dhcp_client *client, int ifindex) {
|
2013-12-09 22:43:08 +01:00
|
|
|
|
|
2016-05-23 16:13:18 +02:00
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
|
|
|
|
|
assert_return(ifindex > 0, -EINVAL);
|
2013-12-09 22:43:08 +01:00
|
|
|
|
|
2016-05-23 16:13:18 +02:00
|
|
|
|
client->ifindex = ifindex;
|
2013-12-09 22:43:08 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
int sd_dhcp_client_set_mac(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
const uint8_t *addr,
|
2020-10-26 14:09:13 +01:00
|
|
|
|
const uint8_t *bcast_addr,
|
2016-05-03 17:52:44 +02:00
|
|
|
|
size_t addr_len,
|
|
|
|
|
uint16_t arp_type) {
|
|
|
|
|
|
2014-05-22 15:18:28 +02:00
|
|
|
|
DHCP_CLIENT_DONT_DESTROY(client);
|
2014-03-21 18:36:32 +01:00
|
|
|
|
bool need_restart = false;
|
2019-11-17 15:52:46 +01:00
|
|
|
|
int r;
|
2014-03-21 18:36:32 +01:00
|
|
|
|
|
2013-12-09 22:43:19 +01:00
|
|
|
|
assert_return(client, -EINVAL);
|
2014-03-21 18:36:32 +01:00
|
|
|
|
assert_return(addr, -EINVAL);
|
2014-10-08 21:15:45 +02:00
|
|
|
|
assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
|
|
|
|
|
assert_return(arp_type > 0, -EINVAL);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
else
|
|
|
|
|
return -EINVAL;
|
2013-12-09 22:43:19 +01:00
|
|
|
|
|
2014-10-08 21:15:45 +02:00
|
|
|
|
if (client->mac_addr_len == addr_len &&
|
2020-10-26 14:09:13 +01:00
|
|
|
|
memcmp(&client->mac_addr, addr, addr_len) == 0 &&
|
|
|
|
|
(client->bcast_addr_len > 0) == !!bcast_addr &&
|
|
|
|
|
(!bcast_addr || memcmp(&client->bcast_addr, bcast_addr, addr_len) == 0))
|
2014-03-21 18:36:32 +01:00
|
|
|
|
return 0;
|
|
|
|
|
|
2014-04-09 12:12:08 +02:00
|
|
|
|
if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
|
2016-05-23 16:27:05 +02:00
|
|
|
|
log_dhcp_client(client, "Changing MAC address on running DHCP client, restarting");
|
2014-03-21 18:36:32 +01:00
|
|
|
|
need_restart = true;
|
2015-09-22 14:46:21 +02:00
|
|
|
|
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
|
2014-03-21 18:36:32 +01:00
|
|
|
|
}
|
2014-03-05 11:07:15 +01:00
|
|
|
|
|
2014-10-08 21:15:45 +02:00
|
|
|
|
memcpy(&client->mac_addr, addr, addr_len);
|
|
|
|
|
client->mac_addr_len = addr_len;
|
|
|
|
|
client->arp_type = arp_type;
|
2020-10-26 14:09:13 +01:00
|
|
|
|
client->bcast_addr_len = 0;
|
|
|
|
|
|
|
|
|
|
if (bcast_addr) {
|
|
|
|
|
memcpy(&client->bcast_addr, bcast_addr, addr_len);
|
|
|
|
|
client->bcast_addr_len = addr_len;
|
|
|
|
|
}
|
2014-10-08 21:15:45 +02:00
|
|
|
|
|
2019-11-17 15:52:46 +01:00
|
|
|
|
if (need_restart && client->state != DHCP_STATE_STOPPED) {
|
|
|
|
|
r = sd_dhcp_client_start(client);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
|
|
|
|
|
}
|
2014-11-19 00:01:20 +01:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
int sd_dhcp_client_get_client_id(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
uint8_t *type,
|
|
|
|
|
const uint8_t **data,
|
|
|
|
|
size_t *data_len) {
|
2014-11-19 00:01:20 +01:00
|
|
|
|
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
assert_return(type, -EINVAL);
|
|
|
|
|
assert_return(data, -EINVAL);
|
|
|
|
|
assert_return(data_len, -EINVAL);
|
|
|
|
|
|
|
|
|
|
*type = 0;
|
|
|
|
|
*data = NULL;
|
|
|
|
|
*data_len = 0;
|
|
|
|
|
if (client->client_id_len) {
|
2015-01-22 00:53:16 +01:00
|
|
|
|
*type = client->client_id.type;
|
2014-11-19 00:01:20 +01:00
|
|
|
|
*data = client->client_id.raw.data;
|
2015-01-22 00:53:16 +01:00
|
|
|
|
*data_len = client->client_id_len - sizeof(client->client_id.type);
|
2014-11-19 00:01:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
int sd_dhcp_client_set_client_id(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
uint8_t type,
|
|
|
|
|
const uint8_t *data,
|
|
|
|
|
size_t data_len) {
|
|
|
|
|
|
2014-11-19 00:01:20 +01:00
|
|
|
|
DHCP_CLIENT_DONT_DESTROY(client);
|
|
|
|
|
bool need_restart = false;
|
2019-11-17 15:52:46 +01:00
|
|
|
|
int r;
|
2014-11-19 00:01:20 +01:00
|
|
|
|
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
assert_return(data, -EINVAL);
|
|
|
|
|
assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
|
|
|
|
|
|
2015-01-22 00:53:16 +01:00
|
|
|
|
if (client->client_id_len == data_len + sizeof(client->client_id.type) &&
|
|
|
|
|
client->client_id.type == type &&
|
2014-11-19 00:01:20 +01:00
|
|
|
|
memcmp(&client->client_id.raw.data, data, data_len) == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
dhcp: don't enforce hardware address length for sd_dhcp_client_set_client_id()
sd_dhcp_client_set_client_id() is the only API for setting a raw client-id.
All other setters are more restricted and only allow to set a type 255 DUID.
Also, dhcp4_set_client_identifier() is the only caller, which already
does:
r = sd_dhcp_client_set_client_id(link->dhcp_client,
ARPHRD_ETHER,
(const uint8_t *) &link->mac,
sizeof(link->mac));
and hence ensures that the data length is indeed ETH_ALEN.
Drop additional input validation from sd_dhcp_client_set_client_id(). The client-id
is an opaque blob, and if a caller wishes to set type 1 (ethernet) or type 32
(infiniband) with unexpected address length, it should be allowed. The actual
client-id is not relevant to the DHCP client, and it's the responsibility of the
caller to generate a suitable client-id.
For example, in NetworkManager you can configure all the bytes of the
client-id, including such _invalid_ settings. I think it makes sense,
to allow the user to fully configure the identifier. Even if such configuration
would be rejected, it would be the responsibility of the higher layers (including
a sensible error message to the user) and not fail later during
sd_dhcp_client_set_client_id().
Still log a debug message if the length is unexpected.
2018-12-19 10:05:37 +01:00
|
|
|
|
/* For hardware types, log debug message about unexpected data length.
|
|
|
|
|
*
|
|
|
|
|
* Note that infiniband's INFINIBAND_ALEN is 20 bytes long, but only
|
2020-07-04 10:37:01 +02:00
|
|
|
|
* the last 8 bytes of the address are stable and suitable to put into
|
dhcp: don't enforce hardware address length for sd_dhcp_client_set_client_id()
sd_dhcp_client_set_client_id() is the only API for setting a raw client-id.
All other setters are more restricted and only allow to set a type 255 DUID.
Also, dhcp4_set_client_identifier() is the only caller, which already
does:
r = sd_dhcp_client_set_client_id(link->dhcp_client,
ARPHRD_ETHER,
(const uint8_t *) &link->mac,
sizeof(link->mac));
and hence ensures that the data length is indeed ETH_ALEN.
Drop additional input validation from sd_dhcp_client_set_client_id(). The client-id
is an opaque blob, and if a caller wishes to set type 1 (ethernet) or type 32
(infiniband) with unexpected address length, it should be allowed. The actual
client-id is not relevant to the DHCP client, and it's the responsibility of the
caller to generate a suitable client-id.
For example, in NetworkManager you can configure all the bytes of the
client-id, including such _invalid_ settings. I think it makes sense,
to allow the user to fully configure the identifier. Even if such configuration
would be rejected, it would be the responsibility of the higher layers (including
a sensible error message to the user) and not fail later during
sd_dhcp_client_set_client_id().
Still log a debug message if the length is unexpected.
2018-12-19 10:05:37 +01:00
|
|
|
|
* the client-id. The caller is advised to account for that. */
|
|
|
|
|
if ((type == ARPHRD_ETHER && data_len != ETH_ALEN) ||
|
|
|
|
|
(type == ARPHRD_INFINIBAND && data_len != 8))
|
|
|
|
|
log_dhcp_client(client, "Changing client ID to hardware type %u with "
|
|
|
|
|
"unexpected address length %zu",
|
|
|
|
|
type, data_len);
|
|
|
|
|
|
2014-11-19 00:01:20 +01:00
|
|
|
|
if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
|
|
|
|
|
log_dhcp_client(client, "Changing client ID on running DHCP "
|
|
|
|
|
"client, restarting");
|
|
|
|
|
need_restart = true;
|
2015-09-22 14:46:21 +02:00
|
|
|
|
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
|
2014-11-19 00:01:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-01-22 00:53:16 +01:00
|
|
|
|
client->client_id.type = type;
|
2014-11-19 00:01:20 +01:00
|
|
|
|
memcpy(&client->client_id.raw.data, data, data_len);
|
2015-01-22 00:53:16 +01:00
|
|
|
|
client->client_id_len = data_len + sizeof (client->client_id.type);
|
2013-12-09 22:43:19 +01:00
|
|
|
|
|
2019-11-17 15:52:46 +01:00
|
|
|
|
if (need_restart && client->state != DHCP_STATE_STOPPED) {
|
|
|
|
|
r = sd_dhcp_client_start(client);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
|
|
|
|
|
}
|
2014-03-21 18:36:32 +01:00
|
|
|
|
|
2013-12-09 22:43:19 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-30 03:18:11 +02:00
|
|
|
|
/**
|
|
|
|
|
* Sets IAID and 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.
|
|
|
|
|
*/
|
2018-08-07 06:55:38 +02:00
|
|
|
|
static int dhcp_client_set_iaid_duid_internal(
|
2016-05-03 17:52:44 +02:00
|
|
|
|
sd_dhcp_client *client,
|
2018-11-23 22:19:26 +01:00
|
|
|
|
bool iaid_append,
|
|
|
|
|
bool iaid_set,
|
2016-05-03 17:52:44 +02:00
|
|
|
|
uint32_t iaid,
|
|
|
|
|
uint16_t duid_type,
|
2016-05-03 18:08:56 +02:00
|
|
|
|
const void *duid,
|
2018-08-07 06:55:38 +02:00
|
|
|
|
size_t duid_len,
|
|
|
|
|
usec_t llt_time) {
|
2016-05-03 17:52:44 +02:00
|
|
|
|
|
2016-03-31 01:33:55 +02:00
|
|
|
|
DHCP_CLIENT_DONT_DESTROY(client);
|
|
|
|
|
int r;
|
2016-04-30 03:18:11 +02:00
|
|
|
|
size_t len;
|
|
|
|
|
|
2016-03-31 01:33:55 +02:00
|
|
|
|
assert_return(client, -EINVAL);
|
2018-12-22 23:58:58 +01:00
|
|
|
|
assert_return(duid_len == 0 || duid, -EINVAL);
|
2016-04-30 03:18:11 +02:00
|
|
|
|
|
2018-12-22 23:58:58 +01:00
|
|
|
|
if (duid) {
|
dhcp6: don't enforce DUID content for sd_dhcp6_client_set_duid()
There are various functions to set the DUID of a DHCPv6 client.
However, none of them allows to set arbitrary data. The closest is
sd_dhcp6_client_set_duid(), which would still do validation of the
DUID's content via dhcp_validate_duid_len().
Relax the validation and only log a debug message if the DUID
does not validate.
Note that dhcp_validate_duid_len() already is not very strict. For example
with DUID_TYPE_LLT it only ensures that the length is suitable to contain
hwtype and time. It does not further check that the length of hwaddr is non-zero
or suitable for hwtype. Also, non-well-known DUID types are accepted for
extensibility. Why reject certain DUIDs but allowing clearly wrong formats
otherwise?
The validation and failure should happen earlier, when accepting the
unsuitable DUID. At that point, there is more context of what is wrong,
and a better failure reason (or warning) can be reported to the user. Rejecting
the DUID when setting up the DHCPv6 client seems not optimal, in particular
because the DHCPv6 client does not care about actual content of the
DUID and treats it as opaque blob.
Also, NetworkManager (which uses this code) allows to configure the entire
binary DUID in binary. It intentionally does not validate the binary
content any further. Hence, it needs to be able to set _invalid_ DUIDs,
provided that some basic constraints are satisfied (like the maximum length).
sd_dhcp6_client_set_duid() has two callers: both set the DUID obtained
from link_get_duid(), which comes from configuration.
`man networkd.conf` says: "The configured DHCP DUID should conform to
the specification in RFC 3315, RFC 6355.". It does not not state that
it MUST conform.
Note that dhcp_validate_duid_len() has another caller: DHCPv4's
dhcp_client_set_iaid_duid_internal(). In this case, continue with
strict validation, as the callers are more controlled. Also, there is
already sd_dhcp_client_set_client_id() which can be used to bypass
this check and set arbitrary client identifiers.
2018-12-20 11:56:02 +01:00
|
|
|
|
r = dhcp_validate_duid_len(duid_type, duid_len, true);
|
2016-04-30 03:18:11 +02:00
|
|
|
|
if (r < 0)
|
2019-11-17 15:48:46 +01:00
|
|
|
|
return log_dhcp_client_errno(client, r, "Failed to validate length of DUID: %m");
|
2016-04-30 03:18:11 +02:00
|
|
|
|
}
|
2016-03-31 01:33:55 +02:00
|
|
|
|
|
2016-04-30 03:18:11 +02:00
|
|
|
|
zero(client->client_id);
|
2016-03-31 01:33:55 +02:00
|
|
|
|
client->client_id.type = 255;
|
|
|
|
|
|
2018-11-23 22:19:26 +01:00
|
|
|
|
if (iaid_append) {
|
|
|
|
|
if (iaid_set)
|
|
|
|
|
client->client_id.ns.iaid = htobe32(iaid);
|
|
|
|
|
else {
|
2018-03-12 17:18:07 +01:00
|
|
|
|
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
|
|
|
|
|
client->mac_addr_len,
|
2018-11-01 14:43:11 +01:00
|
|
|
|
true,
|
2018-03-12 17:18:07 +01:00
|
|
|
|
&client->client_id.ns.iaid);
|
|
|
|
|
if (r < 0)
|
2019-11-17 15:48:46 +01:00
|
|
|
|
return log_dhcp_client_errno(client, r, "Failed to set IAID: %m");
|
2018-11-23 22:19:26 +01:00
|
|
|
|
}
|
2018-03-12 17:18:07 +01:00
|
|
|
|
}
|
2016-03-31 01:33:55 +02:00
|
|
|
|
|
2018-12-22 23:58:58 +01:00
|
|
|
|
if (duid) {
|
2016-03-31 01:33:55 +02:00
|
|
|
|
client->client_id.ns.duid.type = htobe16(duid_type);
|
|
|
|
|
memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len);
|
2016-04-30 03:18:11 +02:00
|
|
|
|
len = sizeof(client->client_id.ns.duid.type) + duid_len;
|
|
|
|
|
} else
|
2018-06-25 11:23:13 +02:00
|
|
|
|
switch (duid_type) {
|
2018-08-07 06:35:58 +02:00
|
|
|
|
case DUID_TYPE_LLT:
|
2018-08-24 04:14:32 +02:00
|
|
|
|
if (client->mac_addr_len == 0)
|
2019-11-17 15:48:46 +01:00
|
|
|
|
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set.");
|
2018-08-07 06:35:58 +02:00
|
|
|
|
|
2018-08-07 06:55:38 +02:00
|
|
|
|
r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
|
2018-08-07 06:35:58 +02:00
|
|
|
|
if (r < 0)
|
2019-11-17 15:48:46 +01:00
|
|
|
|
return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m");
|
2018-08-07 06:35:58 +02:00
|
|
|
|
break;
|
2018-06-25 11:23:13 +02:00
|
|
|
|
case DUID_TYPE_EN:
|
|
|
|
|
r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
|
|
|
|
|
if (r < 0)
|
2019-11-17 15:48:46 +01:00
|
|
|
|
return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m");
|
2018-06-25 11:23:13 +02:00
|
|
|
|
break;
|
2018-08-07 06:35:58 +02:00
|
|
|
|
case DUID_TYPE_LL:
|
2018-08-24 04:14:32 +02:00
|
|
|
|
if (client->mac_addr_len == 0)
|
2019-11-17 15:48:46 +01:00
|
|
|
|
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set.");
|
2018-08-07 06:35:58 +02:00
|
|
|
|
|
|
|
|
|
r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
|
|
|
|
|
if (r < 0)
|
2019-11-17 15:48:46 +01:00
|
|
|
|
return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m");
|
2018-08-07 06:35:58 +02:00
|
|
|
|
break;
|
2018-06-25 11:23:13 +02:00
|
|
|
|
case DUID_TYPE_UUID:
|
|
|
|
|
r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len);
|
|
|
|
|
if (r < 0)
|
2019-11-17 15:48:46 +01:00
|
|
|
|
return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m");
|
2018-06-25 11:23:13 +02:00
|
|
|
|
break;
|
|
|
|
|
default:
|
2019-11-17 15:48:46 +01:00
|
|
|
|
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type");
|
2018-06-25 11:23:13 +02:00
|
|
|
|
}
|
2016-03-31 01:33:55 +02:00
|
|
|
|
|
2016-04-30 03:18:11 +02:00
|
|
|
|
client->client_id_len = sizeof(client->client_id.type) + len +
|
2018-11-23 22:19:26 +01:00
|
|
|
|
(iaid_append ? sizeof(client->client_id.ns.iaid) : 0);
|
2016-03-31 01:33:55 +02:00
|
|
|
|
|
|
|
|
|
if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
|
2018-11-23 22:19:26 +01:00
|
|
|
|
log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : "");
|
2016-03-31 01:33:55 +02:00
|
|
|
|
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
|
2019-11-17 15:52:46 +01:00
|
|
|
|
r = sd_dhcp_client_start(client);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
|
2016-03-31 01:33:55 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-12 17:18:07 +01:00
|
|
|
|
int sd_dhcp_client_set_iaid_duid(
|
|
|
|
|
sd_dhcp_client *client,
|
2018-11-23 22:19:26 +01:00
|
|
|
|
bool iaid_set,
|
2018-03-12 17:18:07 +01:00
|
|
|
|
uint32_t iaid,
|
|
|
|
|
uint16_t duid_type,
|
|
|
|
|
const void *duid,
|
|
|
|
|
size_t duid_len) {
|
2018-11-23 22:19:26 +01:00
|
|
|
|
return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, duid_type, duid, duid_len, 0);
|
2018-08-07 06:55:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sd_dhcp_client_set_iaid_duid_llt(
|
|
|
|
|
sd_dhcp_client *client,
|
2018-11-23 22:19:26 +01:00
|
|
|
|
bool iaid_set,
|
2018-08-07 06:55:38 +02:00
|
|
|
|
uint32_t iaid,
|
|
|
|
|
usec_t llt_time) {
|
2018-11-23 22:19:26 +01:00
|
|
|
|
return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, DUID_TYPE_LLT, NULL, 0, llt_time);
|
2018-03-12 17:18:07 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sd_dhcp_client_set_duid(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
uint16_t duid_type,
|
|
|
|
|
const void *duid,
|
|
|
|
|
size_t duid_len) {
|
2018-11-23 22:19:26 +01:00
|
|
|
|
return dhcp_client_set_iaid_duid_internal(client, false, false, 0, duid_type, duid, duid_len, 0);
|
2018-08-07 06:55:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sd_dhcp_client_set_duid_llt(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
usec_t llt_time) {
|
2018-11-23 22:19:26 +01:00
|
|
|
|
return dhcp_client_set_iaid_duid_internal(client, false, false, 0, DUID_TYPE_LLT, NULL, 0, llt_time);
|
2018-03-12 17:18:07 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
int sd_dhcp_client_set_hostname(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
const char *hostname) {
|
|
|
|
|
|
2014-07-01 20:58:49 +02:00
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
|
2017-11-16 10:05:44 +01:00
|
|
|
|
/* Make sure hostnames qualify as DNS and as Linux hostnames */
|
2016-11-19 18:53:29 +01:00
|
|
|
|
if (hostname &&
|
2017-11-16 10:05:44 +01:00
|
|
|
|
!(hostname_is_valid(hostname, false) && dns_name_is_valid(hostname) > 0))
|
2015-11-16 10:17:48 +01:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
2016-11-19 18:53:29 +01:00
|
|
|
|
return free_and_strdup(&client->hostname, hostname);
|
2014-07-01 20:58:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
int sd_dhcp_client_set_vendor_class_identifier(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
const char *vci) {
|
|
|
|
|
|
2014-07-14 10:04:18 +02:00
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
|
2016-11-19 18:53:29 +01:00
|
|
|
|
return free_and_strdup(&client->vendor_class_identifier, vci);
|
2014-07-14 10:04:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-30 16:42:48 +02:00
|
|
|
|
int sd_dhcp_client_set_mud_url(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
const char *mudurl) {
|
|
|
|
|
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
assert_return(mudurl, -EINVAL);
|
|
|
|
|
assert_return(strlen(mudurl) <= 255, -EINVAL);
|
|
|
|
|
assert_return(http_url_is_valid(mudurl), -EINVAL);
|
|
|
|
|
|
|
|
|
|
return free_and_strdup(&client->mudurl, mudurl);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-07 14:21:02 +02:00
|
|
|
|
int sd_dhcp_client_set_user_class(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
const char* const* user_class) {
|
|
|
|
|
|
|
|
|
|
_cleanup_strv_free_ char **s = NULL;
|
|
|
|
|
char **p;
|
|
|
|
|
|
|
|
|
|
STRV_FOREACH(p, (char **) user_class)
|
|
|
|
|
if (strlen(*p) > 255)
|
|
|
|
|
return -ENAMETOOLONG;
|
|
|
|
|
|
|
|
|
|
s = strv_copy((char **) user_class);
|
|
|
|
|
if (!s)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
client->user_class = TAKE_PTR(s);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-11 00:34:19 +01:00
|
|
|
|
int sd_dhcp_client_set_client_port(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
uint16_t port) {
|
|
|
|
|
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
|
|
|
|
|
client->port = port;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-01 16:10:13 +02:00
|
|
|
|
int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
|
|
|
|
|
|
|
|
|
|
client->mtu = mtu;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-04 13:40:54 +02:00
|
|
|
|
int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempts) {
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
|
|
|
|
|
client->max_attempts = max_attempts;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-28 19:28:49 +01:00
|
|
|
|
int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v) {
|
2019-09-26 20:06:02 +02:00
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
assert_return(v, -EINVAL);
|
|
|
|
|
|
2020-02-28 19:28:49 +01:00
|
|
|
|
r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp_option_hash_ops);
|
2019-09-26 20:06:02 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
2020-02-28 19:28:49 +01:00
|
|
|
|
r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v);
|
2019-09-26 20:06:02 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
sd_dhcp_option_ref(v);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-28 19:28:49 +01:00
|
|
|
|
int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v) {
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
assert_return(v, -EINVAL);
|
|
|
|
|
|
|
|
|
|
r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp_option_hash_ops);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
r = ordered_hashmap_put(client->vendor_options, v, v);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
sd_dhcp_option_ref(v);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-04 23:13:52 +01:00
|
|
|
|
int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
|
2013-12-09 22:43:31 +01:00
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
|
2019-05-01 00:47:41 +02:00
|
|
|
|
if (!IN_SET(client->state, DHCP_STATE_SELECTING, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING))
|
2014-02-04 23:13:52 +01:00
|
|
|
|
return -EADDRNOTAVAIL;
|
2013-12-09 22:43:31 +01:00
|
|
|
|
|
2016-05-23 16:27:05 +02:00
|
|
|
|
if (ret)
|
|
|
|
|
*ret = client->lease;
|
2014-01-13 23:07:59 +01:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-23 13:25:21 +02:00
|
|
|
|
int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) {
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
|
|
|
|
|
client->ip_service_type = type;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-20 06:30:05 +02:00
|
|
|
|
int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint32_t fallback_lease_lifetime) {
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
assert_return(fallback_lease_lifetime > 0, -EINVAL);
|
|
|
|
|
|
|
|
|
|
client->fallback_lease_lifetime = fallback_lease_lifetime;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-01 00:47:41 +02:00
|
|
|
|
static int client_notify(sd_dhcp_client *client, int event) {
|
2016-05-23 16:48:56 +02:00
|
|
|
|
assert(client);
|
|
|
|
|
|
|
|
|
|
if (client->callback)
|
2019-05-01 00:47:41 +02:00
|
|
|
|
return client->callback(client, event, client->userdata);
|
|
|
|
|
|
|
|
|
|
return 0;
|
2013-12-09 22:43:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-12 10:46:40 +01:00
|
|
|
|
static int client_initialize(sd_dhcp_client *client) {
|
2013-12-09 22:43:23 +01:00
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
|
2016-05-23 16:27:05 +02:00
|
|
|
|
client->receive_message = sd_event_source_unref(client->receive_message);
|
2013-12-09 22:43:26 +01:00
|
|
|
|
|
2020-10-13 14:11:36 +02:00
|
|
|
|
client->fd = safe_close(client->fd);
|
2013-12-09 22:43:26 +01:00
|
|
|
|
|
2018-11-13 05:50:08 +01:00
|
|
|
|
(void) event_source_disable(client->timeout_resend);
|
|
|
|
|
(void) event_source_disable(client->timeout_t1);
|
|
|
|
|
(void) event_source_disable(client->timeout_t2);
|
|
|
|
|
(void) event_source_disable(client->timeout_expire);
|
2013-12-09 22:43:30 +01:00
|
|
|
|
|
2019-05-05 17:10:22 +02:00
|
|
|
|
client->attempt = 0;
|
2013-12-09 22:43:27 +01:00
|
|
|
|
|
2014-01-31 10:31:24 +01:00
|
|
|
|
client->state = DHCP_STATE_INIT;
|
2014-03-12 10:46:40 +01:00
|
|
|
|
client->xid = 0;
|
2013-12-09 22:43:23 +01:00
|
|
|
|
|
dhcp: clean up dhcp4 lease object
a) drop handling of obsolete or unused DHCP options time_offset,
mtu_aging_timeout, policy filter, mdr, ttl, ip forwarding settings.
Should this become useful one day we can readd support for this.
b) For subnet mask and broadcast it is not always clear whether 0 or
255.255.255.255 might be valid, hence maintain a boolean indicating
validity next to it.
c) serialize/deserialize broadcast address, lifetime, T1 and T2 together
with the rest of the fields in dhcp_lease_save() and
dhcp_lease_load().
d) consistently return ENODATA from getter functions for data that is
missing in the lease.
e) add missing getter calls for broadcast, lifetime, T1, T2.
f) when decoding DHCP options, generate debug messages on parse
failures, but try to proceed if possible.
g) Similar, when deserializing a lease in dhcp_lease_load(), make sure
we deal nicely with unparsable fields, to provide upgrade compat.
h) fix some memory allocations
2015-08-27 01:05:13 +02:00
|
|
|
|
client->lease = sd_dhcp_lease_unref(client->lease);
|
2013-12-09 22:43:26 +01:00
|
|
|
|
|
2014-03-12 10:46:40 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-22 15:18:28 +02:00
|
|
|
|
static void client_stop(sd_dhcp_client *client, int error) {
|
|
|
|
|
assert(client);
|
2014-03-12 10:46:40 +01:00
|
|
|
|
|
2014-04-27 22:01:42 +02:00
|
|
|
|
if (error < 0)
|
2019-07-03 16:55:45 +02:00
|
|
|
|
log_dhcp_client_errno(client, error, "STOPPED: %m");
|
2015-09-22 14:46:21 +02:00
|
|
|
|
else if (error == SD_DHCP_CLIENT_EVENT_STOP)
|
2014-05-17 21:23:20 +02:00
|
|
|
|
log_dhcp_client(client, "STOPPED");
|
|
|
|
|
else
|
|
|
|
|
log_dhcp_client(client, "STOPPED: Unknown event");
|
2014-03-12 10:46:40 +01:00
|
|
|
|
|
2014-05-22 15:18:28 +02:00
|
|
|
|
client_notify(client, error);
|
2014-02-22 19:53:45 +01:00
|
|
|
|
|
2014-05-22 15:18:28 +02:00
|
|
|
|
client_initialize(client);
|
2013-12-09 22:43:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-08 21:33:29 +01:00
|
|
|
|
/* RFC2131 section 4.1:
|
|
|
|
|
* retransmission delays should include -1 to +1 sec of random 'fuzz'. */
|
|
|
|
|
#define RFC2131_RANDOM_FUZZ \
|
|
|
|
|
((int64_t)(random_u64() % (2 * USEC_PER_SEC)) - (int64_t)USEC_PER_SEC)
|
|
|
|
|
|
|
|
|
|
/* RFC2131 section 4.1:
|
|
|
|
|
* for retransmission delays, timeout should start at 4s then double
|
|
|
|
|
* each attempt with max of 64s, with -1 to +1 sec of random 'fuzz' added.
|
|
|
|
|
* This assumes the first call will be using attempt 1. */
|
|
|
|
|
static usec_t client_compute_request_timeout(usec_t now, uint64_t attempt) {
|
|
|
|
|
usec_t timeout = (UINT64_C(1) << MIN(attempt + 1, UINT64_C(6))) * USEC_PER_SEC;
|
|
|
|
|
|
|
|
|
|
return usec_sub_signed(usec_add(now, timeout), RFC2131_RANDOM_FUZZ);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* RFC2131 section 4.4.5:
|
|
|
|
|
* T1 defaults to (0.5 * duration_of_lease).
|
|
|
|
|
* T2 defaults to (0.875 * duration_of_lease). */
|
|
|
|
|
#define T1_DEFAULT(lifetime) ((lifetime) / 2)
|
|
|
|
|
#define T2_DEFAULT(lifetime) (((lifetime) * 7) / 8)
|
|
|
|
|
|
|
|
|
|
/* RFC2131 section 4.4.5:
|
|
|
|
|
* the client SHOULD wait one-half of the remaining time until T2 (in RENEWING state)
|
|
|
|
|
* and one-half of the remaining lease time (in REBINDING state), down to a minimum
|
|
|
|
|
* of 60 seconds.
|
|
|
|
|
* Note that while the default T1/T2 initial times do have random 'fuzz' applied,
|
|
|
|
|
* the RFC sec 4.4.5 does not mention adding any fuzz to retries. */
|
|
|
|
|
static usec_t client_compute_reacquisition_timeout(usec_t now, usec_t expire) {
|
|
|
|
|
return MAX(usec_sub_unsigned(expire, now) / 2, 60 * USEC_PER_SEC);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-14 05:47:58 +02:00
|
|
|
|
static int cmp_uint8(const uint8_t *a, const uint8_t *b) {
|
|
|
|
|
return CMP(*a, *b);
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int client_message_init(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
DHCPPacket **ret,
|
|
|
|
|
uint8_t type,
|
|
|
|
|
size_t *_optlen,
|
|
|
|
|
size_t *_optoffset) {
|
|
|
|
|
|
2016-02-27 13:40:50 +01:00
|
|
|
|
_cleanup_free_ DHCPPacket *packet = NULL;
|
2014-05-21 16:46:14 +02:00
|
|
|
|
size_t optlen, optoffset, size;
|
2014-04-11 19:54:04 +02:00
|
|
|
|
be16_t max_size;
|
2014-11-04 18:20:43 +01:00
|
|
|
|
usec_t time_now;
|
|
|
|
|
uint16_t secs;
|
2014-02-11 13:11:18 +01:00
|
|
|
|
int r;
|
2013-12-09 22:43:19 +01:00
|
|
|
|
|
2014-03-19 14:45:35 +01:00
|
|
|
|
assert(client);
|
2014-11-04 18:20:43 +01:00
|
|
|
|
assert(client->start_time);
|
2014-05-21 16:46:14 +02:00
|
|
|
|
assert(ret);
|
|
|
|
|
assert(_optlen);
|
|
|
|
|
assert(_optoffset);
|
2019-12-20 14:13:18 +01:00
|
|
|
|
assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST, DHCP_RELEASE, DHCP_DECLINE));
|
2014-02-23 22:07:07 +01:00
|
|
|
|
|
2014-05-21 16:46:14 +02:00
|
|
|
|
optlen = DHCP_MIN_OPTIONS_SIZE;
|
|
|
|
|
size = sizeof(DHCPPacket) + optlen;
|
|
|
|
|
|
|
|
|
|
packet = malloc0(size);
|
|
|
|
|
if (!packet)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
|
2014-10-08 21:15:45 +02:00
|
|
|
|
client->arp_type, optlen, &optoffset);
|
2014-02-11 13:11:18 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2013-12-09 22:43:19 +01:00
|
|
|
|
|
2014-02-23 22:07:07 +01:00
|
|
|
|
/* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
|
|
|
|
|
refuse to issue an DHCP lease if 'secs' is set to zero */
|
2014-11-04 18:20:43 +01:00
|
|
|
|
r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
assert(time_now >= client->start_time);
|
|
|
|
|
|
|
|
|
|
/* seconds between sending first and last DISCOVER
|
|
|
|
|
* must always be strictly positive to deal with broken servers */
|
|
|
|
|
secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
|
|
|
|
|
packet->dhcp.secs = htobe16(secs);
|
2014-02-23 22:07:07 +01:00
|
|
|
|
|
2014-05-28 20:43:37 +02:00
|
|
|
|
/* RFC2132 section 4.1
|
|
|
|
|
A client that cannot receive unicast IP datagrams until its protocol
|
|
|
|
|
software has been configured with an IP address SHOULD set the
|
|
|
|
|
BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
|
|
|
|
|
DHCPREQUEST messages that client sends. The BROADCAST bit will
|
|
|
|
|
provide a hint to the DHCP server and BOOTP relay agent to broadcast
|
2014-07-15 18:55:31 +02:00
|
|
|
|
any messages to the client on the client's subnet.
|
|
|
|
|
|
|
|
|
|
Note: some interfaces needs this to be enabled, but some networks
|
|
|
|
|
needs this to be disabled as broadcasts are filteretd, so this
|
|
|
|
|
needs to be configurable */
|
2014-10-08 21:15:45 +02:00
|
|
|
|
if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
|
2014-07-15 18:55:31 +02:00
|
|
|
|
packet->dhcp.flags = htobe16(0x8000);
|
2014-05-28 20:43:37 +02:00
|
|
|
|
|
2014-04-11 21:02:16 +02:00
|
|
|
|
/* RFC2132 section 4.1.1:
|
|
|
|
|
The client MUST include its hardware address in the ’chaddr’ field, if
|
2014-10-08 21:15:45 +02:00
|
|
|
|
necessary for delivery of DHCP reply messages. Non-Ethernet
|
|
|
|
|
interfaces will leave 'chaddr' empty and use the client identifier
|
|
|
|
|
instead (eg, RFC 4390 section 2.1).
|
2014-04-11 21:02:16 +02:00
|
|
|
|
*/
|
2014-10-08 21:15:45 +02:00
|
|
|
|
if (client->arp_type == ARPHRD_ETHER)
|
|
|
|
|
memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
|
2013-12-09 22:43:22 +01:00
|
|
|
|
|
2016-03-21 23:24:24 +01:00
|
|
|
|
/* If no client identifier exists, construct an RFC 4361-compliant one */
|
2015-01-22 00:53:16 +01:00
|
|
|
|
if (client->client_id_len == 0) {
|
|
|
|
|
size_t duid_len;
|
|
|
|
|
|
|
|
|
|
client->client_id.type = 255;
|
|
|
|
|
|
2018-11-01 14:43:11 +01:00
|
|
|
|
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len,
|
|
|
|
|
true, &client->client_id.ns.iaid);
|
2015-01-22 00:53:16 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len;
|
2014-11-19 00:01:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-02-22 19:53:45 +01:00
|
|
|
|
/* Some DHCP servers will refuse to issue an DHCP lease if the Client
|
2013-12-09 22:43:19 +01:00
|
|
|
|
Identifier option is not set */
|
2014-11-19 00:01:20 +01:00
|
|
|
|
if (client->client_id_len) {
|
|
|
|
|
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_CLIENT_IDENTIFIER,
|
2014-11-19 00:01:20 +01:00
|
|
|
|
client->client_id_len,
|
2015-01-22 00:53:16 +01:00
|
|
|
|
&client->client_id);
|
2014-11-19 00:01:20 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
2014-04-11 21:02:16 +02:00
|
|
|
|
|
|
|
|
|
/* RFC2131 section 3.5:
|
|
|
|
|
in its initial DHCPDISCOVER or DHCPREQUEST message, a
|
|
|
|
|
client may provide the server with a list of specific
|
|
|
|
|
parameters the client is interested in. If the client
|
|
|
|
|
includes a list of parameters in a DHCPDISCOVER message,
|
|
|
|
|
it MUST include that list in any subsequent DHCPREQUEST
|
|
|
|
|
messages.
|
|
|
|
|
*/
|
2017-08-03 19:19:51 +02:00
|
|
|
|
|
|
|
|
|
/* RFC7844 section 3:
|
|
|
|
|
MAY contain the Parameter Request List option. */
|
|
|
|
|
/* NOTE: in case that there would be an option to do not send
|
|
|
|
|
* any PRL at all, the size should be checked before sending */
|
2020-10-14 05:47:58 +02:00
|
|
|
|
if (!set_isempty(client->req_opts) && type != DHCP_RELEASE) {
|
|
|
|
|
_cleanup_free_ uint8_t *opts = NULL;
|
|
|
|
|
size_t n_opts, i = 0;
|
|
|
|
|
void *val;
|
|
|
|
|
|
|
|
|
|
n_opts = set_size(client->req_opts);
|
|
|
|
|
opts = new(uint8_t, n_opts);
|
|
|
|
|
if (!opts)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
SET_FOREACH(val, client->req_opts)
|
|
|
|
|
opts[i++] = PTR_TO_UINT8(val);
|
|
|
|
|
assert(i == n_opts);
|
|
|
|
|
|
|
|
|
|
/* For anonymizing the request, let's sort the options. */
|
|
|
|
|
typesafe_qsort(opts, n_opts, cmp_uint8);
|
|
|
|
|
|
2017-08-03 19:19:51 +02:00
|
|
|
|
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
|
|
|
|
|
SD_DHCP_OPTION_PARAMETER_REQUEST_LIST,
|
2020-10-14 05:47:58 +02:00
|
|
|
|
n_opts, opts);
|
2017-08-03 19:19:51 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
2014-04-11 19:54:04 +02:00
|
|
|
|
|
2014-04-11 21:02:16 +02:00
|
|
|
|
/* RFC2131 section 3.5:
|
|
|
|
|
The client SHOULD include the ’maximum DHCP message size’ option to
|
|
|
|
|
let the server know how large the server may make its DHCP messages.
|
|
|
|
|
|
|
|
|
|
Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
|
2018-09-30 21:20:08 +02:00
|
|
|
|
than the defined default size unless the Maximum Message Size option
|
2014-12-29 10:45:58 +01:00
|
|
|
|
is explicitly set
|
2014-08-01 16:10:13 +02:00
|
|
|
|
|
|
|
|
|
RFC3442 "Requirements to Avoid Sizing Constraints":
|
|
|
|
|
Because a full routing table can be quite large, the standard 576
|
|
|
|
|
octet maximum size for a DHCP message may be too short to contain
|
|
|
|
|
some legitimate Classless Static Route options. Because of this,
|
|
|
|
|
clients implementing the Classless Static Route option SHOULD send a
|
|
|
|
|
Maximum DHCP Message Size [4] option if the DHCP client's TCP/IP
|
|
|
|
|
stack is capable of receiving larger IP datagrams. In this case, the
|
|
|
|
|
client SHOULD set the value of this option to at least the MTU of the
|
|
|
|
|
interface that the client is configuring. The client MAY set the
|
|
|
|
|
value of this option higher, up to the size of the largest UDP packet
|
|
|
|
|
it is prepared to accept. (Note that the value specified in the
|
|
|
|
|
Maximum DHCP Message Size option is the total maximum packet size,
|
|
|
|
|
including IP and UDP headers.)
|
2014-04-11 21:02:16 +02:00
|
|
|
|
*/
|
2017-08-04 03:08:41 +02:00
|
|
|
|
/* RFC7844 section 3:
|
|
|
|
|
SHOULD NOT contain any other option. */
|
2019-05-13 16:30:28 +02:00
|
|
|
|
if (!client->anonymize && type != DHCP_RELEASE) {
|
2017-08-04 03:08:41 +02:00
|
|
|
|
max_size = htobe16(size);
|
|
|
|
|
r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
|
|
|
|
|
SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
|
|
|
|
|
2, &max_size);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
2013-12-09 22:43:19 +01:00
|
|
|
|
|
2014-05-21 16:46:14 +02:00
|
|
|
|
*_optlen = optlen;
|
|
|
|
|
*_optoffset = optoffset;
|
2018-04-05 07:26:26 +02:00
|
|
|
|
*ret = TAKE_PTR(packet);
|
2014-05-21 16:46:14 +02:00
|
|
|
|
|
2013-12-09 22:43:19 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int client_append_fqdn_option(
|
|
|
|
|
DHCPMessage *message,
|
|
|
|
|
size_t optlen,
|
|
|
|
|
size_t *optoffset,
|
|
|
|
|
const char *fqdn) {
|
|
|
|
|
|
2015-11-16 10:17:48 +01:00
|
|
|
|
uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH];
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */
|
|
|
|
|
DHCP_FQDN_FLAG_E; /* Canonical wire format */
|
|
|
|
|
buffer[1] = 0; /* RCODE1 (deprecated) */
|
|
|
|
|
buffer[2] = 0; /* RCODE2 (deprecated) */
|
|
|
|
|
|
2015-12-02 20:47:11 +01:00
|
|
|
|
r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3, false);
|
2015-11-16 10:17:48 +01:00
|
|
|
|
if (r > 0)
|
|
|
|
|
r = dhcp_option_append(message, optlen, optoffset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_FQDN, 3 + r, buffer);
|
2015-11-16 10:17:48 +01:00
|
|
|
|
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int dhcp_client_send_raw(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
DHCPPacket *packet,
|
|
|
|
|
size_t len) {
|
|
|
|
|
|
2016-11-11 00:34:19 +01:00
|
|
|
|
dhcp_packet_append_ip_headers(packet, INADDR_ANY, client->port,
|
2019-09-23 13:25:21 +02:00
|
|
|
|
INADDR_BROADCAST, DHCP_PORT_SERVER, len, client->ip_service_type);
|
2014-03-09 22:51:07 +01:00
|
|
|
|
|
|
|
|
|
return dhcp_network_send_raw_socket(client->fd, &client->link,
|
|
|
|
|
packet, len);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-20 01:34:36 +02:00
|
|
|
|
static int client_append_common_discover_request_options(sd_dhcp_client *client, DHCPPacket *packet, size_t *optoffset, size_t optlen) {
|
2019-09-26 20:06:02 +02:00
|
|
|
|
sd_dhcp_option *j;
|
2014-03-19 14:45:35 +01:00
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(client);
|
2013-12-09 22:43:19 +01:00
|
|
|
|
|
2014-07-01 20:58:49 +02:00
|
|
|
|
if (client->hostname) {
|
2015-11-16 10:17:48 +01:00
|
|
|
|
/* According to RFC 4702 "clients that send the Client FQDN option in
|
|
|
|
|
their messages MUST NOT also send the Host Name option". Just send
|
|
|
|
|
one of the two depending on the hostname type.
|
|
|
|
|
*/
|
2015-11-25 21:07:17 +01:00
|
|
|
|
if (dns_name_is_single_label(client->hostname)) {
|
2015-11-16 10:17:48 +01:00
|
|
|
|
/* it is unclear from RFC 2131 if client should send hostname in
|
|
|
|
|
DHCPDISCOVER but dhclient does and so we do as well
|
|
|
|
|
*/
|
2020-04-20 01:34:36 +02:00
|
|
|
|
r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_HOST_NAME,
|
2015-11-16 10:17:48 +01:00
|
|
|
|
strlen(client->hostname), client->hostname);
|
|
|
|
|
} else
|
2020-04-20 01:34:36 +02:00
|
|
|
|
r = client_append_fqdn_option(&packet->dhcp, optlen, optoffset,
|
2015-11-16 10:17:48 +01:00
|
|
|
|
client->hostname);
|
2014-07-01 20:58:49 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-14 10:04:18 +02:00
|
|
|
|
if (client->vendor_class_identifier) {
|
2020-04-20 01:34:36 +02:00
|
|
|
|
r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
|
2014-07-14 10:04:18 +02:00
|
|
|
|
strlen(client->vendor_class_identifier),
|
|
|
|
|
client->vendor_class_identifier);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-30 16:42:48 +02:00
|
|
|
|
if (client->mudurl) {
|
2020-04-20 01:34:36 +02:00
|
|
|
|
r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
|
2020-03-30 16:42:48 +02:00
|
|
|
|
SD_DHCP_OPTION_MUD_URL,
|
|
|
|
|
strlen(client->mudurl),
|
|
|
|
|
client->mudurl);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-07 14:21:02 +02:00
|
|
|
|
if (client->user_class) {
|
2020-04-20 01:34:36 +02:00
|
|
|
|
r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
|
2018-05-07 14:21:02 +02:00
|
|
|
|
SD_DHCP_OPTION_USER_CLASS,
|
|
|
|
|
strv_length(client->user_class),
|
|
|
|
|
client->user_class);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-08 11:58:29 +02:00
|
|
|
|
ORDERED_HASHMAP_FOREACH(j, client->extra_options) {
|
2020-04-20 01:34:36 +02:00
|
|
|
|
r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
|
2019-09-26 20:06:02 +02:00
|
|
|
|
j->option, j->length, j->data);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-28 19:28:49 +01:00
|
|
|
|
if (!ordered_hashmap_isempty(client->vendor_options)) {
|
|
|
|
|
r = dhcp_option_append(
|
2020-04-20 01:34:36 +02:00
|
|
|
|
&packet->dhcp, optlen, optoffset, 0,
|
2020-02-28 19:28:49 +01:00
|
|
|
|
SD_DHCP_OPTION_VENDOR_SPECIFIC,
|
|
|
|
|
ordered_hashmap_size(client->vendor_options), client->vendor_options);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-20 01:34:36 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int client_send_discover(sd_dhcp_client *client) {
|
|
|
|
|
_cleanup_free_ DHCPPacket *discover = NULL;
|
|
|
|
|
size_t optoffset, optlen;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(client);
|
|
|
|
|
assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING));
|
|
|
|
|
|
|
|
|
|
r = client_message_init(client, &discover, DHCP_DISCOVER,
|
|
|
|
|
&optlen, &optoffset);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
/* the client may suggest values for the network address
|
|
|
|
|
and lease time in the DHCPDISCOVER message. The client may include
|
|
|
|
|
the ’requested IP address’ option to suggest that a particular IP
|
|
|
|
|
address be assigned, and may include the ’IP address lease time’
|
|
|
|
|
option to suggest the lease time it would like.
|
|
|
|
|
*/
|
|
|
|
|
/* RFC7844 section 3:
|
|
|
|
|
SHOULD NOT contain any other option. */
|
|
|
|
|
if (!client->anonymize && client->last_addr != INADDR_ANY) {
|
|
|
|
|
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
|
|
|
|
|
SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
|
|
|
|
|
4, &client->last_addr);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r = client_append_common_discover_request_options(client, discover, &optoffset, optlen);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
2014-05-21 15:55:02 +02:00
|
|
|
|
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_END, 0, NULL);
|
2014-06-18 20:26:54 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2013-12-09 22:43:19 +01:00
|
|
|
|
|
2014-04-11 21:02:16 +02:00
|
|
|
|
/* We currently ignore:
|
|
|
|
|
The client SHOULD wait a random time between one and ten seconds to
|
|
|
|
|
desynchronize the use of DHCP at startup.
|
|
|
|
|
*/
|
2014-05-20 11:04:50 +02:00
|
|
|
|
r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
|
2014-03-19 14:45:35 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2013-12-09 22:43:27 +01:00
|
|
|
|
|
2014-02-22 19:53:45 +01:00
|
|
|
|
log_dhcp_client(client, "DISCOVER");
|
|
|
|
|
|
2014-03-09 22:51:07 +01:00
|
|
|
|
return 0;
|
2013-12-09 22:43:27 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-19 14:45:35 +01:00
|
|
|
|
static int client_send_request(sd_dhcp_client *client) {
|
2014-06-13 18:48:20 +02:00
|
|
|
|
_cleanup_free_ DHCPPacket *request = NULL;
|
2014-05-21 16:46:14 +02:00
|
|
|
|
size_t optoffset, optlen;
|
2014-03-19 14:45:35 +01:00
|
|
|
|
int r;
|
2013-12-09 22:43:27 +01:00
|
|
|
|
|
2016-05-23 16:27:05 +02:00
|
|
|
|
assert(client);
|
|
|
|
|
|
|
|
|
|
r = client_message_init(client, &request, DHCP_REQUEST, &optlen, &optoffset);
|
2014-03-19 14:45:35 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2013-12-09 22:43:19 +01:00
|
|
|
|
|
2014-01-31 10:31:22 +01:00
|
|
|
|
switch (client->state) {
|
2014-04-11 21:02:16 +02:00
|
|
|
|
/* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
|
|
|
|
|
SELECTING should be REQUESTING)
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
case DHCP_STATE_REQUESTING:
|
|
|
|
|
/* Client inserts the address of the selected server in ’server
|
|
|
|
|
identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
|
|
|
|
|
filled in with the yiaddr value from the chosen DHCPOFFER.
|
|
|
|
|
*/
|
2014-01-31 10:31:22 +01:00
|
|
|
|
|
2014-05-21 15:55:02 +02:00
|
|
|
|
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_SERVER_IDENTIFIER,
|
2014-04-11 21:02:16 +02:00
|
|
|
|
4, &client->lease->server_address);
|
2014-03-19 14:45:35 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2014-01-31 10:31:22 +01:00
|
|
|
|
|
2014-05-21 15:55:02 +02:00
|
|
|
|
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
|
2014-03-19 14:45:35 +01:00
|
|
|
|
4, &client->lease->address);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
2014-04-11 21:02:16 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DHCP_STATE_INIT_REBOOT:
|
|
|
|
|
/* ’server identifier’ MUST NOT be filled in, ’requested IP address’
|
|
|
|
|
option MUST be filled in with client’s notion of its previously
|
|
|
|
|
assigned address. ’ciaddr’ MUST be zero.
|
|
|
|
|
*/
|
2014-05-21 15:55:02 +02:00
|
|
|
|
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
|
2014-04-11 21:02:16 +02:00
|
|
|
|
4, &client->last_addr);
|
2014-03-19 14:45:35 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2014-01-31 10:31:22 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DHCP_STATE_RENEWING:
|
2014-04-11 21:02:16 +02:00
|
|
|
|
/* ’server identifier’ MUST NOT be filled in, ’requested IP address’
|
|
|
|
|
option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
|
|
|
|
|
client’s IP address.
|
|
|
|
|
*/
|
|
|
|
|
|
2014-01-31 10:31:22 +01:00
|
|
|
|
case DHCP_STATE_REBINDING:
|
2014-04-11 21:02:16 +02:00
|
|
|
|
/* ’server identifier’ MUST NOT be filled in, ’requested IP address’
|
|
|
|
|
option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
|
|
|
|
|
client’s IP address.
|
|
|
|
|
|
|
|
|
|
This message MUST be broadcast to the 0xffffffff IP broadcast address.
|
|
|
|
|
*/
|
|
|
|
|
request->dhcp.ciaddr = client->lease->address;
|
2014-01-31 10:31:22 +01:00
|
|
|
|
|
|
|
|
|
break;
|
2014-04-09 12:12:08 +02:00
|
|
|
|
|
2014-04-11 21:02:16 +02:00
|
|
|
|
case DHCP_STATE_INIT:
|
|
|
|
|
case DHCP_STATE_SELECTING:
|
|
|
|
|
case DHCP_STATE_REBOOTING:
|
|
|
|
|
case DHCP_STATE_BOUND:
|
2014-04-09 12:12:08 +02:00
|
|
|
|
case DHCP_STATE_STOPPED:
|
|
|
|
|
return -EINVAL;
|
2013-12-09 22:43:27 +01:00
|
|
|
|
}
|
2013-12-09 22:43:19 +01:00
|
|
|
|
|
2020-04-20 01:34:36 +02:00
|
|
|
|
r = client_append_common_discover_request_options(client, request, &optoffset, optlen);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2020-03-30 16:42:48 +02:00
|
|
|
|
|
2014-05-21 15:55:02 +02:00
|
|
|
|
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_END, 0, NULL);
|
2014-03-19 14:45:35 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2013-12-09 22:43:19 +01:00
|
|
|
|
|
2019-03-04 09:19:48 +01:00
|
|
|
|
if (client->state == DHCP_STATE_RENEWING)
|
2014-03-19 14:45:35 +01:00
|
|
|
|
r = dhcp_network_send_udp_socket(client->fd,
|
|
|
|
|
client->lease->server_address,
|
|
|
|
|
DHCP_PORT_SERVER,
|
|
|
|
|
&request->dhcp,
|
2014-05-20 11:04:50 +02:00
|
|
|
|
sizeof(DHCPMessage) + optoffset);
|
2019-03-04 09:19:48 +01:00
|
|
|
|
else
|
2014-05-20 11:04:50 +02:00
|
|
|
|
r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
|
2014-03-19 14:45:35 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2013-12-09 22:43:19 +01:00
|
|
|
|
|
2014-04-12 01:01:13 +02:00
|
|
|
|
switch (client->state) {
|
2016-05-23 16:27:05 +02:00
|
|
|
|
|
2014-04-12 01:01:13 +02:00
|
|
|
|
case DHCP_STATE_REQUESTING:
|
|
|
|
|
log_dhcp_client(client, "REQUEST (requesting)");
|
|
|
|
|
break;
|
2016-05-23 16:27:05 +02:00
|
|
|
|
|
2014-04-12 01:01:13 +02:00
|
|
|
|
case DHCP_STATE_INIT_REBOOT:
|
|
|
|
|
log_dhcp_client(client, "REQUEST (init-reboot)");
|
|
|
|
|
break;
|
2016-05-23 16:27:05 +02:00
|
|
|
|
|
2014-04-12 01:01:13 +02:00
|
|
|
|
case DHCP_STATE_RENEWING:
|
|
|
|
|
log_dhcp_client(client, "REQUEST (renewing)");
|
|
|
|
|
break;
|
2016-05-23 16:27:05 +02:00
|
|
|
|
|
2014-04-12 01:01:13 +02:00
|
|
|
|
case DHCP_STATE_REBINDING:
|
|
|
|
|
log_dhcp_client(client, "REQUEST (rebinding)");
|
|
|
|
|
break;
|
2016-05-23 16:27:05 +02:00
|
|
|
|
|
2014-04-12 01:01:13 +02:00
|
|
|
|
default:
|
|
|
|
|
log_dhcp_client(client, "REQUEST (invalid)");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2014-02-22 19:53:45 +01:00
|
|
|
|
|
2014-03-09 22:51:07 +01:00
|
|
|
|
return 0;
|
2013-12-09 22:43:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-04-12 00:34:05 +02:00
|
|
|
|
static int client_start(sd_dhcp_client *client);
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int client_timeout_resend(
|
|
|
|
|
sd_event_source *s,
|
|
|
|
|
uint64_t usec,
|
|
|
|
|
void *userdata) {
|
|
|
|
|
|
2013-12-09 22:43:25 +01:00
|
|
|
|
sd_dhcp_client *client = userdata;
|
2014-05-22 15:18:28 +02:00
|
|
|
|
DHCP_CLIENT_DONT_DESTROY(client);
|
2020-10-07 14:14:09 +02:00
|
|
|
|
usec_t next_timeout;
|
2014-03-18 14:13:01 +01:00
|
|
|
|
uint64_t time_now;
|
|
|
|
|
int r;
|
2014-01-18 19:32:45 +01:00
|
|
|
|
|
|
|
|
|
assert(s);
|
|
|
|
|
assert(client);
|
|
|
|
|
assert(client->event);
|
2013-12-09 22:43:25 +01:00
|
|
|
|
|
2014-07-24 18:53:01 +02:00
|
|
|
|
r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
|
2014-03-18 14:13:01 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
goto error;
|
|
|
|
|
|
2013-12-20 16:16:18 +01:00
|
|
|
|
switch (client->state) {
|
2016-05-23 16:27:05 +02:00
|
|
|
|
|
2013-12-20 16:16:18 +01:00
|
|
|
|
case DHCP_STATE_RENEWING:
|
2020-12-08 21:40:10 +01:00
|
|
|
|
next_timeout = client_compute_reacquisition_timeout(time_now, client->t2_time);
|
2013-12-20 16:16:18 +01:00
|
|
|
|
break;
|
|
|
|
|
|
2013-12-20 16:16:20 +01:00
|
|
|
|
case DHCP_STATE_REBINDING:
|
2020-12-08 21:40:10 +01:00
|
|
|
|
next_timeout = client_compute_reacquisition_timeout(time_now, client->expire_time);
|
2013-12-20 16:16:20 +01:00
|
|
|
|
break;
|
|
|
|
|
|
2014-01-31 10:31:22 +01:00
|
|
|
|
case DHCP_STATE_REBOOTING:
|
|
|
|
|
/* start over as we did not receive a timely ack or nak */
|
2014-04-12 00:34:05 +02:00
|
|
|
|
r = client_initialize(client);
|
2014-04-11 18:02:54 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
goto error;
|
2014-01-31 10:31:22 +01:00
|
|
|
|
|
2014-04-12 01:01:13 +02:00
|
|
|
|
r = client_start(client);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
goto error;
|
2020-10-07 14:14:09 +02:00
|
|
|
|
|
|
|
|
|
log_dhcp_client(client, "REBOOTED");
|
|
|
|
|
return 0;
|
2014-04-12 00:34:05 +02:00
|
|
|
|
|
2013-12-20 16:16:18 +01:00
|
|
|
|
case DHCP_STATE_INIT:
|
|
|
|
|
case DHCP_STATE_INIT_REBOOT:
|
|
|
|
|
case DHCP_STATE_SELECTING:
|
|
|
|
|
case DHCP_STATE_REQUESTING:
|
|
|
|
|
case DHCP_STATE_BOUND:
|
2020-10-07 14:14:09 +02:00
|
|
|
|
if (client->attempt >= client->max_attempts)
|
2019-05-01 08:43:23 +02:00
|
|
|
|
goto error;
|
2013-12-20 16:16:18 +01:00
|
|
|
|
|
2020-10-07 14:14:09 +02:00
|
|
|
|
client->attempt++;
|
sd-dhcp-client: correct retransmission timeout to match RFC
This changes the retransmission timeout algorithm for requests
other than RENEW and REBIND. Previously, the retransmission timeout
started at 2 seconds, then doubling each retransmission up to a max
of 64 seconds. This is changed to match what RFC2131 section 4.1 describes,
which skips the initial 2 second timeout and starts with a 4 second timeout
instead. Note that -1 to +1 seconds of random 'fuzz' is added to each
timeout, in previous and current behavior.
This change is therefore slightly slower than the previous behavior in
attempting retransmissions when no server response is received, since the
first transmission times out in 4 seconds instead of 2.
Since TRANSIENT_FAILURE_ATTEMPTS is set to 3, the previous length of time
before a transient failure was reported back to systemd-networkd was
2 + 4 + 8 = 14 seconds, plus, on average, 3 seconds of random 'fuzz' for
a transient failure timeout between 11 and 17 seconds. Now, since the
first timeout starts at 4, the transient failure will be reported at
4 + 8 + 16 = 28 seconds, again plus 3 random seconds for a transient
failure timeout between 25 and 31 seconds.
Additionally, if MaxAttempts= is set, it will take slightly longer to
reach than with previous behavior.
2020-12-09 20:32:06 +01:00
|
|
|
|
next_timeout = client_compute_request_timeout(time_now, client->attempt);
|
2013-12-20 16:16:18 +01:00
|
|
|
|
break;
|
2014-04-09 12:12:08 +02:00
|
|
|
|
|
|
|
|
|
case DHCP_STATE_STOPPED:
|
|
|
|
|
r = -EINVAL;
|
|
|
|
|
goto error;
|
2013-12-20 16:16:18 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-13 05:50:08 +01:00
|
|
|
|
r = event_reset_time(client->event, &client->timeout_resend,
|
|
|
|
|
clock_boottime_or_monotonic(),
|
|
|
|
|
next_timeout, 10 * USEC_PER_MSEC,
|
|
|
|
|
client_timeout_resend, client,
|
|
|
|
|
client->event_priority, "dhcp4-resend-timer", true);
|
2014-08-28 15:46:29 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
goto error;
|
|
|
|
|
|
2013-12-09 22:43:27 +01:00
|
|
|
|
switch (client->state) {
|
|
|
|
|
case DHCP_STATE_INIT:
|
2014-03-19 14:45:35 +01:00
|
|
|
|
r = client_send_discover(client);
|
2014-01-18 19:32:45 +01:00
|
|
|
|
if (r >= 0) {
|
2013-12-09 22:43:27 +01:00
|
|
|
|
client->state = DHCP_STATE_SELECTING;
|
2019-05-05 17:10:22 +02:00
|
|
|
|
client->attempt = 0;
|
2019-05-04 13:40:54 +02:00
|
|
|
|
} else if (client->attempt >= client->max_attempts)
|
|
|
|
|
goto error;
|
2013-12-09 22:43:27 +01:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DHCP_STATE_SELECTING:
|
2014-03-19 14:45:35 +01:00
|
|
|
|
r = client_send_discover(client);
|
2019-05-04 13:40:54 +02:00
|
|
|
|
if (r < 0 && client->attempt >= client->max_attempts)
|
2013-12-09 22:43:25 +01:00
|
|
|
|
goto error;
|
|
|
|
|
|
2013-12-09 22:43:27 +01:00
|
|
|
|
break;
|
|
|
|
|
|
2014-01-31 10:31:22 +01:00
|
|
|
|
case DHCP_STATE_INIT_REBOOT:
|
2013-12-09 22:43:27 +01:00
|
|
|
|
case DHCP_STATE_REQUESTING:
|
2013-12-20 16:16:18 +01:00
|
|
|
|
case DHCP_STATE_RENEWING:
|
2013-12-20 16:16:20 +01:00
|
|
|
|
case DHCP_STATE_REBINDING:
|
2014-03-19 14:45:35 +01:00
|
|
|
|
r = client_send_request(client);
|
2019-05-04 13:40:54 +02:00
|
|
|
|
if (r < 0 && client->attempt >= client->max_attempts)
|
2013-12-09 22:43:27 +01:00
|
|
|
|
goto error;
|
2013-12-09 22:43:25 +01:00
|
|
|
|
|
2014-01-31 10:31:22 +01:00
|
|
|
|
if (client->state == DHCP_STATE_INIT_REBOOT)
|
|
|
|
|
client->state = DHCP_STATE_REBOOTING;
|
|
|
|
|
|
2014-03-18 14:13:01 +01:00
|
|
|
|
client->request_sent = time_now;
|
2013-12-09 22:43:25 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DHCP_STATE_REBOOTING:
|
|
|
|
|
case DHCP_STATE_BOUND:
|
|
|
|
|
break;
|
2014-04-09 12:12:08 +02:00
|
|
|
|
|
|
|
|
|
case DHCP_STATE_STOPPED:
|
|
|
|
|
r = -EINVAL;
|
|
|
|
|
goto error;
|
2013-12-09 22:43:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-08 16:51:25 +02:00
|
|
|
|
if (client->attempt >= TRANSIENT_FAILURE_ATTEMPTS)
|
|
|
|
|
client_notify(client, SD_DHCP_CLIENT_EVENT_TRANSIENT_FAILURE);
|
|
|
|
|
|
2013-12-09 22:43:25 +01:00
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
error:
|
2014-01-18 19:32:45 +01:00
|
|
|
|
client_stop(client, r);
|
2013-12-09 22:43:25 +01:00
|
|
|
|
|
|
|
|
|
/* Errors were dealt with when stopping the client, don't spill
|
|
|
|
|
errors into the event loop handler */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int client_initialize_io_events(
|
|
|
|
|
sd_dhcp_client *client,
|
|
|
|
|
sd_event_io_handler_t io_callback) {
|
|
|
|
|
|
2013-12-20 16:16:19 +01:00
|
|
|
|
int r;
|
|
|
|
|
|
2014-01-18 19:32:45 +01:00
|
|
|
|
assert(client);
|
|
|
|
|
assert(client->event);
|
|
|
|
|
|
2014-02-19 23:54:58 +01:00
|
|
|
|
r = sd_event_add_io(client->event, &client->receive_message,
|
|
|
|
|
client->fd, EPOLLIN, io_callback,
|
|
|
|
|
client);
|
2013-12-20 16:16:19 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
goto error;
|
|
|
|
|
|
2014-02-13 20:56:16 +01:00
|
|
|
|
r = sd_event_source_set_priority(client->receive_message,
|
|
|
|
|
client->event_priority);
|
2014-01-18 19:32:45 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
goto error;
|
|
|
|
|
|
2014-11-04 16:27:05 +01:00
|
|
|
|
r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message");
|
2014-08-28 15:46:29 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
goto error;
|
|
|
|
|
|
2014-07-25 14:44:12 +02:00
|
|
|
|
error:
|
|
|
|
|
if (r < 0)
|
|
|
|
|
client_stop(client, r);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int client_initialize_time_events(sd_dhcp_client *client) {
|
2016-01-27 11:21:23 +01:00
|
|
|
|
uint64_t usec = 0;
|
2014-07-25 14:44:12 +02:00
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(client);
|
|
|
|
|
assert(client->event);
|
|
|
|
|
|
2019-03-04 09:19:48 +01:00
|
|
|
|
if (client->start_delay > 0) {
|
2016-02-15 16:11:51 +01:00
|
|
|
|
assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0);
|
2016-01-27 11:21:23 +01:00
|
|
|
|
usec += client->start_delay;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-13 05:50:08 +01:00
|
|
|
|
r = event_reset_time(client->event, &client->timeout_resend,
|
|
|
|
|
clock_boottime_or_monotonic(),
|
|
|
|
|
usec, 0,
|
|
|
|
|
client_timeout_resend, client,
|
|
|
|
|
client->event_priority, "dhcp4-resend-timer", true);
|
2013-12-20 16:16:19 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
client_stop(client, r);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int client_initialize_events(sd_dhcp_client *client, sd_event_io_handler_t io_callback) {
|
2014-07-25 14:44:12 +02:00
|
|
|
|
client_initialize_io_events(client, io_callback);
|
|
|
|
|
client_initialize_time_events(client);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-27 11:21:23 +01:00
|
|
|
|
static int client_start_delayed(sd_dhcp_client *client) {
|
2014-03-12 10:46:40 +01:00
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
assert_return(client->event, -EINVAL);
|
2016-05-23 16:13:18 +02:00
|
|
|
|
assert_return(client->ifindex > 0, -EINVAL);
|
2014-03-12 10:46:40 +01:00
|
|
|
|
assert_return(client->fd < 0, -EBUSY);
|
|
|
|
|
assert_return(client->xid == 0, -EINVAL);
|
2016-05-23 16:27:05 +02:00
|
|
|
|
assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT), -EBUSY);
|
2014-03-12 10:46:40 +01:00
|
|
|
|
|
|
|
|
|
client->xid = random_u32();
|
|
|
|
|
|
2020-10-26 14:09:13 +01:00
|
|
|
|
r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid,
|
|
|
|
|
client->mac_addr, client->mac_addr_len,
|
|
|
|
|
client->bcast_addr, client->bcast_addr_len,
|
|
|
|
|
client->arp_type, client->port);
|
2014-03-12 10:46:40 +01:00
|
|
|
|
if (r < 0) {
|
|
|
|
|
client_stop(client, r);
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
client->fd = r;
|
2014-03-20 16:21:43 +01:00
|
|
|
|
|
2017-09-29 00:37:23 +02:00
|
|
|
|
if (IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT))
|
2014-07-24 18:53:01 +02:00
|
|
|
|
client->start_time = now(clock_boottime_or_monotonic());
|
2014-03-12 10:46:40 +01:00
|
|
|
|
|
|
|
|
|
return client_initialize_events(client, client_receive_message_raw);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-27 11:21:23 +01:00
|
|
|
|
static int client_start(sd_dhcp_client *client) {
|
|
|
|
|
client->start_delay = 0;
|
|
|
|
|
return client_start_delayed(client);
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) {
|
2013-12-09 22:43:31 +01:00
|
|
|
|
sd_dhcp_client *client = userdata;
|
2014-05-22 15:18:28 +02:00
|
|
|
|
DHCP_CLIENT_DONT_DESTROY(client);
|
2013-12-09 22:43:31 +01:00
|
|
|
|
|
2014-02-22 19:53:45 +01:00
|
|
|
|
log_dhcp_client(client, "EXPIRED");
|
|
|
|
|
|
2015-09-22 14:46:21 +02:00
|
|
|
|
client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
|
2014-03-12 10:46:40 +01:00
|
|
|
|
|
2014-04-09 12:12:08 +02:00
|
|
|
|
/* lease was lost, start over if not freed or stopped in callback */
|
2014-05-22 15:18:28 +02:00
|
|
|
|
if (client->state != DHCP_STATE_STOPPED) {
|
2014-04-09 12:12:07 +02:00
|
|
|
|
client_initialize(client);
|
|
|
|
|
client_start(client);
|
|
|
|
|
}
|
2013-12-09 22:43:31 +01:00
|
|
|
|
|
2013-12-09 22:43:30 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-16 19:55:25 +01:00
|
|
|
|
static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
|
2013-12-20 16:16:20 +01:00
|
|
|
|
sd_dhcp_client *client = userdata;
|
2014-05-22 15:18:28 +02:00
|
|
|
|
DHCP_CLIENT_DONT_DESTROY(client);
|
2013-12-20 16:16:20 +01:00
|
|
|
|
int r;
|
|
|
|
|
|
2016-05-23 16:27:05 +02:00
|
|
|
|
assert(client);
|
|
|
|
|
|
2014-03-18 19:22:43 +01:00
|
|
|
|
client->receive_message = sd_event_source_unref(client->receive_message);
|
2020-10-13 14:11:36 +02:00
|
|
|
|
client->fd = safe_close(client->fd);
|
2013-12-20 16:16:20 +01:00
|
|
|
|
|
|
|
|
|
client->state = DHCP_STATE_REBINDING;
|
2019-05-05 17:10:22 +02:00
|
|
|
|
client->attempt = 0;
|
2013-12-20 16:16:20 +01:00
|
|
|
|
|
2020-10-26 14:09:13 +01:00
|
|
|
|
r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid,
|
|
|
|
|
client->mac_addr, client->mac_addr_len,
|
|
|
|
|
client->bcast_addr, client->bcast_addr_len,
|
|
|
|
|
client->arp_type, client->port);
|
2013-12-20 16:16:20 +01:00
|
|
|
|
if (r < 0) {
|
|
|
|
|
client_stop(client, r);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
client->fd = r;
|
|
|
|
|
|
2014-03-18 14:13:01 +01:00
|
|
|
|
return client_initialize_events(client, client_receive_message_raw);
|
2013-12-09 22:43:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
|
2013-12-20 16:16:18 +01:00
|
|
|
|
sd_dhcp_client *client = userdata;
|
2014-05-22 15:18:28 +02:00
|
|
|
|
DHCP_CLIENT_DONT_DESTROY(client);
|
2013-12-20 16:16:18 +01:00
|
|
|
|
|
2020-07-07 03:04:42 +02:00
|
|
|
|
if (client->lease)
|
|
|
|
|
client->state = DHCP_STATE_RENEWING;
|
|
|
|
|
else if (client->state != DHCP_STATE_INIT)
|
|
|
|
|
client->state = DHCP_STATE_INIT_REBOOT;
|
2019-05-05 17:10:22 +02:00
|
|
|
|
client->attempt = 0;
|
2013-12-20 16:16:18 +01:00
|
|
|
|
|
2014-07-25 14:44:12 +02:00
|
|
|
|
return client_initialize_time_events(client);
|
2013-12-09 22:43:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, 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_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
|
2014-01-16 19:55:25 +01:00
|
|
|
|
int r;
|
2013-12-09 22:43:29 +01:00
|
|
|
|
|
2014-02-04 23:13:52 +01:00
|
|
|
|
r = dhcp_lease_new(&lease);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2013-12-09 22:43:26 +01:00
|
|
|
|
|
2014-11-19 00:13:12 +01:00
|
|
|
|
if (client->client_id_len) {
|
|
|
|
|
r = dhcp_lease_set_client_id(lease,
|
2015-01-22 00:53:16 +01:00
|
|
|
|
(uint8_t *) &client->client_id,
|
2014-11-19 00:13:12 +01:00
|
|
|
|
client->client_id_len);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-25 17:29:30 +01:00
|
|
|
|
r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease, NULL);
|
2014-03-19 16:05:44 +01:00
|
|
|
|
if (r != DHCP_OFFER) {
|
2014-08-29 13:28:04 +02:00
|
|
|
|
log_dhcp_client(client, "received message was not an OFFER, ignoring");
|
2014-01-16 19:55:25 +01:00
|
|
|
|
return -ENOMSG;
|
2014-03-19 16:05:44 +01:00
|
|
|
|
}
|
2013-12-09 22:43:26 +01:00
|
|
|
|
|
2014-03-03 17:13:59 +01:00
|
|
|
|
lease->next_server = offer->siaddr;
|
2014-02-13 20:56:16 +01:00
|
|
|
|
lease->address = offer->yiaddr;
|
2013-12-09 22:43:26 +01:00
|
|
|
|
|
2020-05-20 06:30:05 +02:00
|
|
|
|
if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0)
|
|
|
|
|
lease->lifetime = client->fallback_lease_lifetime;
|
|
|
|
|
|
dhcp: clean up dhcp4 lease object
a) drop handling of obsolete or unused DHCP options time_offset,
mtu_aging_timeout, policy filter, mdr, ttl, ip forwarding settings.
Should this become useful one day we can readd support for this.
b) For subnet mask and broadcast it is not always clear whether 0 or
255.255.255.255 might be valid, hence maintain a boolean indicating
validity next to it.
c) serialize/deserialize broadcast address, lifetime, T1 and T2 together
with the rest of the fields in dhcp_lease_save() and
dhcp_lease_load().
d) consistently return ENODATA from getter functions for data that is
missing in the lease.
e) add missing getter calls for broadcast, lifetime, T1, T2.
f) when decoding DHCP options, generate debug messages on parse
failures, but try to proceed if possible.
g) Similar, when deserializing a lease in dhcp_lease_load(), make sure
we deal nicely with unparsable fields, to provide upgrade compat.
h) fix some memory allocations
2015-08-27 01:05:13 +02:00
|
|
|
|
if (lease->address == 0 ||
|
|
|
|
|
lease->server_address == 0 ||
|
2014-03-19 16:05:44 +01:00
|
|
|
|
lease->lifetime == 0) {
|
dhcp: clean up dhcp4 lease object
a) drop handling of obsolete or unused DHCP options time_offset,
mtu_aging_timeout, policy filter, mdr, ttl, ip forwarding settings.
Should this become useful one day we can readd support for this.
b) For subnet mask and broadcast it is not always clear whether 0 or
255.255.255.255 might be valid, hence maintain a boolean indicating
validity next to it.
c) serialize/deserialize broadcast address, lifetime, T1 and T2 together
with the rest of the fields in dhcp_lease_save() and
dhcp_lease_load().
d) consistently return ENODATA from getter functions for data that is
missing in the lease.
e) add missing getter calls for broadcast, lifetime, T1, T2.
f) when decoding DHCP options, generate debug messages on parse
failures, but try to proceed if possible.
g) Similar, when deserializing a lease in dhcp_lease_load(), make sure
we deal nicely with unparsable fields, to provide upgrade compat.
h) fix some memory allocations
2015-08-27 01:05:13 +02:00
|
|
|
|
log_dhcp_client(client, "received lease lacks address, server address or lease lifetime, ignoring");
|
2014-01-16 19:55:25 +01:00
|
|
|
|
return -ENOMSG;
|
2014-03-19 16:05:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
dhcp: clean up dhcp4 lease object
a) drop handling of obsolete or unused DHCP options time_offset,
mtu_aging_timeout, policy filter, mdr, ttl, ip forwarding settings.
Should this become useful one day we can readd support for this.
b) For subnet mask and broadcast it is not always clear whether 0 or
255.255.255.255 might be valid, hence maintain a boolean indicating
validity next to it.
c) serialize/deserialize broadcast address, lifetime, T1 and T2 together
with the rest of the fields in dhcp_lease_save() and
dhcp_lease_load().
d) consistently return ENODATA from getter functions for data that is
missing in the lease.
e) add missing getter calls for broadcast, lifetime, T1, T2.
f) when decoding DHCP options, generate debug messages on parse
failures, but try to proceed if possible.
g) Similar, when deserializing a lease in dhcp_lease_load(), make sure
we deal nicely with unparsable fields, to provide upgrade compat.
h) fix some memory allocations
2015-08-27 01:05:13 +02:00
|
|
|
|
if (!lease->have_subnet_mask) {
|
2014-03-19 16:05:44 +01:00
|
|
|
|
r = dhcp_lease_set_default_subnet_mask(lease);
|
|
|
|
|
if (r < 0) {
|
2018-02-08 10:34:52 +01:00
|
|
|
|
log_dhcp_client(client,
|
|
|
|
|
"received lease lacks subnet mask, "
|
|
|
|
|
"and a fallback one cannot be generated, ignoring");
|
2014-03-19 16:05:44 +01:00
|
|
|
|
return -ENOMSG;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-12-09 22:43:26 +01:00
|
|
|
|
|
2014-04-11 05:45:46 +02:00
|
|
|
|
sd_dhcp_lease_unref(client->lease);
|
2018-04-05 07:26:26 +02:00
|
|
|
|
client->lease = TAKE_PTR(lease);
|
2013-12-09 22:43:26 +01:00
|
|
|
|
|
2019-05-01 00:47:41 +02:00
|
|
|
|
if (client_notify(client, SD_DHCP_CLIENT_EVENT_SELECTING) < 0)
|
|
|
|
|
return -ENOMSG;
|
|
|
|
|
|
2014-02-22 19:53:45 +01:00
|
|
|
|
log_dhcp_client(client, "OFFER");
|
|
|
|
|
|
2013-12-09 22:43:26 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, size_t len) {
|
2014-05-16 00:50:44 +02:00
|
|
|
|
int r;
|
|
|
|
|
|
2015-11-25 17:29:30 +01:00
|
|
|
|
r = dhcp_option_parse(force, len, NULL, NULL, NULL);
|
2014-05-16 00:50:44 +02:00
|
|
|
|
if (r != DHCP_FORCERENEW)
|
|
|
|
|
return -ENOMSG;
|
|
|
|
|
|
|
|
|
|
log_dhcp_client(client, "FORCERENEW");
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-18 04:18:46 +02:00
|
|
|
|
static bool lease_equal(const sd_dhcp_lease *a, const sd_dhcp_lease *b) {
|
|
|
|
|
if (a->address != b->address)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (a->subnet_mask != b->subnet_mask)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (a->router_size != b->router_size)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < a->router_size; i++)
|
|
|
|
|
if (a->router[i].s_addr != b->router[i].s_addr)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, 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_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
|
2015-11-25 17:29:30 +01:00
|
|
|
|
_cleanup_free_ char *error_message = NULL;
|
2014-01-16 19:55:25 +01:00
|
|
|
|
int r;
|
2013-12-09 22:43:29 +01:00
|
|
|
|
|
2014-02-04 23:13:52 +01:00
|
|
|
|
r = dhcp_lease_new(&lease);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2013-12-09 22:43:29 +01:00
|
|
|
|
|
2014-11-19 00:13:12 +01:00
|
|
|
|
if (client->client_id_len) {
|
|
|
|
|
r = dhcp_lease_set_client_id(lease,
|
2015-01-22 00:53:16 +01:00
|
|
|
|
(uint8_t *) &client->client_id,
|
2014-11-19 00:13:12 +01:00
|
|
|
|
client->client_id_len);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-25 17:29:30 +01:00
|
|
|
|
r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease, &error_message);
|
2014-02-22 19:53:45 +01:00
|
|
|
|
if (r == DHCP_NAK) {
|
2015-11-25 17:29:30 +01:00
|
|
|
|
log_dhcp_client(client, "NAK: %s", strna(error_message));
|
2014-05-17 21:23:20 +02:00
|
|
|
|
return -EADDRNOTAVAIL;
|
2014-02-22 19:53:45 +01:00
|
|
|
|
}
|
2013-12-09 22:43:29 +01:00
|
|
|
|
|
2014-03-19 16:05:44 +01:00
|
|
|
|
if (r != DHCP_ACK) {
|
2014-08-29 13:28:04 +02:00
|
|
|
|
log_dhcp_client(client, "received message was not an ACK, ignoring");
|
2014-01-16 19:55:25 +01:00
|
|
|
|
return -ENOMSG;
|
2014-03-19 16:05:44 +01:00
|
|
|
|
}
|
2013-12-09 22:43:29 +01:00
|
|
|
|
|
2014-03-03 17:13:59 +01:00
|
|
|
|
lease->next_server = ack->siaddr;
|
|
|
|
|
|
2014-02-13 20:56:16 +01:00
|
|
|
|
lease->address = ack->yiaddr;
|
2013-12-09 22:43:29 +01:00
|
|
|
|
|
|
|
|
|
if (lease->address == INADDR_ANY ||
|
|
|
|
|
lease->server_address == INADDR_ANY ||
|
2014-03-19 16:05:44 +01:00
|
|
|
|
lease->lifetime == 0) {
|
2014-08-29 13:28:04 +02:00
|
|
|
|
log_dhcp_client(client, "received lease lacks address, server "
|
2014-03-19 16:05:44 +01:00
|
|
|
|
"address or lease lifetime, ignoring");
|
2014-01-16 19:55:25 +01:00
|
|
|
|
return -ENOMSG;
|
2014-03-19 16:05:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lease->subnet_mask == INADDR_ANY) {
|
|
|
|
|
r = dhcp_lease_set_default_subnet_mask(lease);
|
|
|
|
|
if (r < 0) {
|
2018-02-08 10:34:52 +01:00
|
|
|
|
log_dhcp_client(client,
|
|
|
|
|
"received lease lacks subnet mask, "
|
|
|
|
|
"and a fallback one cannot be generated, ignoring");
|
2014-03-19 16:05:44 +01:00
|
|
|
|
return -ENOMSG;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-12-09 22:43:29 +01:00
|
|
|
|
|
2015-09-22 14:46:21 +02:00
|
|
|
|
r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
|
2013-12-09 22:43:29 +01:00
|
|
|
|
if (client->lease) {
|
2019-06-18 04:18:46 +02:00
|
|
|
|
if (lease_equal(client->lease, lease))
|
2015-09-22 14:46:21 +02:00
|
|
|
|
r = SD_DHCP_CLIENT_EVENT_RENEW;
|
2019-06-18 04:18:46 +02:00
|
|
|
|
else
|
|
|
|
|
r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
|
2013-12-09 22:43:29 +01:00
|
|
|
|
|
2014-02-04 23:13:52 +01:00
|
|
|
|
client->lease = sd_dhcp_lease_unref(client->lease);
|
2013-12-09 22:43:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-05 07:26:26 +02:00
|
|
|
|
client->lease = TAKE_PTR(lease);
|
2013-12-09 22:43:29 +01:00
|
|
|
|
|
2014-02-22 19:53:45 +01:00
|
|
|
|
log_dhcp_client(client, "ACK");
|
|
|
|
|
|
2013-12-09 22:43:29 +01:00
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-19 17:19:22 +01:00
|
|
|
|
static int client_set_lease_timeouts(sd_dhcp_client *client) {
|
|
|
|
|
usec_t time_now;
|
|
|
|
|
char time_string[FORMAT_TIMESPAN_MAX];
|
2014-01-16 19:55:25 +01:00
|
|
|
|
int r;
|
2013-12-09 22:43:30 +01:00
|
|
|
|
|
2014-01-18 19:32:45 +01:00
|
|
|
|
assert(client);
|
|
|
|
|
assert(client->event);
|
2014-03-19 17:19:22 +01:00
|
|
|
|
assert(client->lease);
|
|
|
|
|
assert(client->lease->lifetime);
|
2013-12-09 22:43:30 +01:00
|
|
|
|
|
2014-03-19 17:19:22 +01:00
|
|
|
|
/* don't set timers for infinite leases */
|
2018-11-13 05:50:08 +01:00
|
|
|
|
if (client->lease->lifetime == 0xffffffff) {
|
|
|
|
|
(void) event_source_disable(client->timeout_t1);
|
|
|
|
|
(void) event_source_disable(client->timeout_t2);
|
|
|
|
|
(void) event_source_disable(client->timeout_expire);
|
|
|
|
|
|
2014-03-19 17:19:22 +01:00
|
|
|
|
return 0;
|
2018-11-13 05:50:08 +01:00
|
|
|
|
}
|
2013-12-09 22:43:30 +01:00
|
|
|
|
|
2014-07-24 18:53:01 +02:00
|
|
|
|
r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
|
2014-03-19 17:19:22 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
assert(client->request_sent <= time_now);
|
|
|
|
|
|
sd-dhcp-client: simplify dhcp4 t1/t2 parsing
The parsing of the dhcpv4 lease lifetime, as well as the t1/t2
times, is simplified by this commit.
This differs from previous behavior; previously, the lease lifetime and
t1/t2 values were modified by random 'fuzz' by subtracting 3, then adding
a random number between 0 and (slightly over) 2 seconds. The resulting
values were therefore always between 1-3 seconds shorter than the value
provided by the server (or the default, in case of t1/t2). Now, as
described in RFC2131, the random 'fuzz' is between -1 and +1 seconds,
meaning the actual t1 and t2 value will be up to 1 second earlier or
later than the server-provided (or default) t1/t2 value.
This also differs in handling the lease lifetime, as described above it
previously was adjusted by the random 'fuzz', but the RFC does not state
that the lease expiration time should be adjusted, so now the code uses
exactly the lease lifetime as provided by the server with no adjustment.
2020-12-08 21:36:19 +01:00
|
|
|
|
/* verify that 0 < t2 < lifetime */
|
|
|
|
|
if (client->lease->t2 == 0 || client->lease->t2 >= client->lease->lifetime)
|
|
|
|
|
client->lease->t2 = T2_DEFAULT(client->lease->lifetime);
|
|
|
|
|
/* verify that 0 < t1 < lifetime */
|
|
|
|
|
if (client->lease->t1 == 0 || client->lease->t1 >= client->lease->t2)
|
|
|
|
|
client->lease->t1 = T1_DEFAULT(client->lease->lifetime);
|
|
|
|
|
/* now, if t1 >= t2, t1 *must* be T1_DEFAULT, since the previous check
|
|
|
|
|
* could not evalate to false if t1 >= t2; so setting t2 to T2_DEFAULT
|
|
|
|
|
* guarantees t1 < t2. */
|
|
|
|
|
if (client->lease->t1 >= client->lease->t2)
|
|
|
|
|
client->lease->t2 = T2_DEFAULT(client->lease->lifetime);
|
|
|
|
|
|
|
|
|
|
client->expire_time = client->request_sent + client->lease->lifetime * USEC_PER_SEC;
|
|
|
|
|
client->t1_time = client->request_sent + client->lease->t1 * USEC_PER_SEC;
|
|
|
|
|
client->t2_time = client->request_sent + client->lease->t2 * USEC_PER_SEC;
|
|
|
|
|
|
|
|
|
|
/* RFC2131 section 4.4.5:
|
|
|
|
|
* Times T1 and T2 SHOULD be chosen with some random "fuzz".
|
|
|
|
|
* Since the RFC doesn't specify here the exact 'fuzz' to use,
|
|
|
|
|
* we use the range from section 4.1: -1 to +1 sec. */
|
|
|
|
|
client->t1_time = usec_sub_signed(client->t1_time, RFC2131_RANDOM_FUZZ);
|
|
|
|
|
client->t2_time = usec_sub_signed(client->t2_time, RFC2131_RANDOM_FUZZ);
|
|
|
|
|
|
|
|
|
|
/* after fuzzing, ensure t2 is still >= t1 */
|
|
|
|
|
client->t2_time = MAX(client->t1_time, client->t2_time);
|
2013-12-09 22:43:30 +01:00
|
|
|
|
|
2014-03-19 17:19:22 +01:00
|
|
|
|
/* arm lifetime timeout */
|
2018-11-13 05:50:08 +01:00
|
|
|
|
r = event_reset_time(client->event, &client->timeout_expire,
|
|
|
|
|
clock_boottime_or_monotonic(),
|
2020-12-08 20:37:59 +01:00
|
|
|
|
client->expire_time, 10 * USEC_PER_MSEC,
|
2018-11-13 05:50:08 +01:00
|
|
|
|
client_timeout_expire, client,
|
|
|
|
|
client->event_priority, "dhcp4-lifetime", true);
|
2014-08-28 15:46:29 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
2014-03-19 17:19:22 +01:00
|
|
|
|
/* don't arm earlier timeouts if this has already expired */
|
2020-12-08 20:37:59 +01:00
|
|
|
|
if (client->expire_time <= time_now)
|
2014-03-19 17:19:22 +01:00
|
|
|
|
return 0;
|
2013-12-09 22:43:30 +01:00
|
|
|
|
|
2020-12-14 21:50:11 +01:00
|
|
|
|
log_dhcp_client(client, "lease expires in %s",
|
2020-12-08 20:37:59 +01:00
|
|
|
|
format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->expire_time - time_now, USEC_PER_SEC));
|
2020-12-14 21:50:11 +01:00
|
|
|
|
|
2014-03-19 17:19:22 +01:00
|
|
|
|
/* arm T2 timeout */
|
2018-11-13 05:50:08 +01:00
|
|
|
|
r = event_reset_time(client->event, &client->timeout_t2,
|
|
|
|
|
clock_boottime_or_monotonic(),
|
2020-12-08 20:37:59 +01:00
|
|
|
|
client->t2_time, 10 * USEC_PER_MSEC,
|
2018-11-13 05:50:08 +01:00
|
|
|
|
client_timeout_t2, client,
|
|
|
|
|
client->event_priority, "dhcp4-t2-timeout", true);
|
2014-08-28 15:46:29 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
2014-03-19 17:19:22 +01:00
|
|
|
|
/* don't arm earlier timeout if this has already expired */
|
2020-12-08 20:37:59 +01:00
|
|
|
|
if (client->t2_time <= time_now)
|
2014-03-19 17:19:22 +01:00
|
|
|
|
return 0;
|
2013-12-09 22:43:30 +01:00
|
|
|
|
|
2020-12-14 21:50:11 +01:00
|
|
|
|
log_dhcp_client(client, "T2 expires in %s",
|
2020-12-08 20:37:59 +01:00
|
|
|
|
format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->t2_time - time_now, USEC_PER_SEC));
|
2020-12-14 21:50:11 +01:00
|
|
|
|
|
2014-03-19 17:19:22 +01:00
|
|
|
|
/* arm T1 timeout */
|
2018-11-13 05:50:08 +01:00
|
|
|
|
r = event_reset_time(client->event, &client->timeout_t1,
|
|
|
|
|
clock_boottime_or_monotonic(),
|
2020-12-08 20:37:59 +01:00
|
|
|
|
client->t1_time, 10 * USEC_PER_MSEC,
|
2018-11-13 05:50:08 +01:00
|
|
|
|
client_timeout_t1, client,
|
|
|
|
|
client->event_priority, "dhcp4-t1-timer", true);
|
2014-08-28 15:46:29 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
2020-12-08 20:37:59 +01:00
|
|
|
|
if (client->t1_time > time_now)
|
2020-12-14 21:50:11 +01:00
|
|
|
|
log_dhcp_client(client, "T1 expires in %s",
|
2020-12-08 20:37:59 +01:00
|
|
|
|
format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->t1_time - time_now, USEC_PER_SEC));
|
2014-03-19 17:19:22 +01:00
|
|
|
|
|
2013-12-09 22:43:30 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) {
|
2014-05-22 15:18:28 +02:00
|
|
|
|
DHCP_CLIENT_DONT_DESTROY(client);
|
2016-01-27 11:21:23 +01:00
|
|
|
|
char time_string[FORMAT_TIMESPAN_MAX];
|
2014-02-13 20:56:16 +01:00
|
|
|
|
int r = 0, notify_event = 0;
|
2013-12-09 22:43:26 +01:00
|
|
|
|
|
2014-01-18 19:32:45 +01:00
|
|
|
|
assert(client);
|
|
|
|
|
assert(client->event);
|
2014-02-13 20:56:16 +01:00
|
|
|
|
assert(message);
|
2014-01-18 19:32:45 +01:00
|
|
|
|
|
2013-12-09 22:43:26 +01:00
|
|
|
|
switch (client->state) {
|
|
|
|
|
case DHCP_STATE_SELECTING:
|
|
|
|
|
|
2014-02-13 20:56:16 +01:00
|
|
|
|
r = client_handle_offer(client, message, len);
|
|
|
|
|
if (r >= 0) {
|
2013-12-09 22:43:26 +01:00
|
|
|
|
|
|
|
|
|
client->state = DHCP_STATE_REQUESTING;
|
2019-05-05 17:10:22 +02:00
|
|
|
|
client->attempt = 0;
|
2013-12-09 22:43:27 +01:00
|
|
|
|
|
2018-11-13 05:50:08 +01:00
|
|
|
|
r = event_reset_time(client->event, &client->timeout_resend,
|
|
|
|
|
clock_boottime_or_monotonic(),
|
|
|
|
|
0, 0,
|
|
|
|
|
client_timeout_resend, client,
|
|
|
|
|
client->event_priority, "dhcp4-resend-timer", true);
|
2014-08-28 15:46:29 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
goto error;
|
2014-03-19 16:05:44 +01:00
|
|
|
|
} else if (r == -ENOMSG)
|
|
|
|
|
/* invalid message, let's ignore it */
|
|
|
|
|
return 0;
|
2013-12-09 22:43:26 +01:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2014-01-31 10:31:22 +01:00
|
|
|
|
case DHCP_STATE_REBOOTING:
|
2013-12-09 22:43:29 +01:00
|
|
|
|
case DHCP_STATE_REQUESTING:
|
2013-12-20 16:16:18 +01:00
|
|
|
|
case DHCP_STATE_RENEWING:
|
2013-12-20 16:16:20 +01:00
|
|
|
|
case DHCP_STATE_REBINDING:
|
2013-12-20 16:16:18 +01:00
|
|
|
|
|
2014-02-13 20:56:16 +01:00
|
|
|
|
r = client_handle_ack(client, message, len);
|
2014-05-17 21:23:20 +02:00
|
|
|
|
if (r >= 0) {
|
2016-01-27 11:21:23 +01:00
|
|
|
|
client->start_delay = 0;
|
2018-11-13 05:50:08 +01:00
|
|
|
|
(void) event_source_disable(client->timeout_resend);
|
2014-10-30 20:23:00 +01:00
|
|
|
|
client->receive_message =
|
|
|
|
|
sd_event_source_unref(client->receive_message);
|
2020-10-13 14:11:36 +02:00
|
|
|
|
client->fd = safe_close(client->fd);
|
2013-12-09 22:43:29 +01:00
|
|
|
|
|
2014-01-31 10:31:22 +01:00
|
|
|
|
if (IN_SET(client->state, DHCP_STATE_REQUESTING,
|
|
|
|
|
DHCP_STATE_REBOOTING))
|
2015-09-22 14:46:21 +02:00
|
|
|
|
notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
|
|
|
|
|
else if (r != SD_DHCP_CLIENT_EVENT_IP_ACQUIRE)
|
2013-12-20 16:16:18 +01:00
|
|
|
|
notify_event = r;
|
|
|
|
|
|
2013-12-09 22:43:29 +01:00
|
|
|
|
client->state = DHCP_STATE_BOUND;
|
2019-05-05 17:10:22 +02:00
|
|
|
|
client->attempt = 0;
|
2013-12-09 22:43:29 +01:00
|
|
|
|
|
|
|
|
|
client->last_addr = client->lease->address;
|
|
|
|
|
|
2014-03-19 17:19:22 +01:00
|
|
|
|
r = client_set_lease_timeouts(client);
|
2014-12-11 14:43:09 +01:00
|
|
|
|
if (r < 0) {
|
|
|
|
|
log_dhcp_client(client, "could not set lease timeouts");
|
2013-12-09 22:43:30 +01:00
|
|
|
|
goto error;
|
2014-12-11 14:43:09 +01:00
|
|
|
|
}
|
2013-12-09 22:43:30 +01:00
|
|
|
|
|
2019-09-23 13:25:21 +02:00
|
|
|
|
r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port, client->ip_service_type);
|
2014-07-25 14:44:12 +02:00
|
|
|
|
if (r < 0) {
|
|
|
|
|
log_dhcp_client(client, "could not bind UDP socket");
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client->fd = r;
|
|
|
|
|
|
|
|
|
|
client_initialize_io_events(client, client_receive_message_udp);
|
|
|
|
|
|
2014-04-09 12:12:07 +02:00
|
|
|
|
if (notify_event) {
|
2014-05-22 15:18:28 +02:00
|
|
|
|
client_notify(client, notify_event);
|
|
|
|
|
if (client->state == DHCP_STATE_STOPPED)
|
2014-04-09 12:12:07 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2013-12-09 22:43:29 +01:00
|
|
|
|
|
2014-05-17 21:23:20 +02:00
|
|
|
|
} else if (r == -EADDRNOTAVAIL) {
|
|
|
|
|
/* got a NAK, let's restart the client */
|
2018-10-19 19:41:51 +02:00
|
|
|
|
client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
|
|
|
|
|
|
2014-05-17 21:23:20 +02:00
|
|
|
|
r = client_initialize(client);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
goto error;
|
|
|
|
|
|
2016-01-27 11:21:23 +01:00
|
|
|
|
r = client_start_delayed(client);
|
2014-05-17 21:23:20 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
goto error;
|
|
|
|
|
|
2016-01-27 11:21:23 +01:00
|
|
|
|
log_dhcp_client(client, "REBOOT in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX,
|
|
|
|
|
client->start_delay, USEC_PER_SEC));
|
|
|
|
|
|
|
|
|
|
client->start_delay = CLAMP(client->start_delay * 2,
|
|
|
|
|
RESTART_AFTER_NAK_MIN_USEC, RESTART_AFTER_NAK_MAX_USEC);
|
2014-05-17 21:23:20 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
2014-03-19 16:05:44 +01:00
|
|
|
|
} else if (r == -ENOMSG)
|
|
|
|
|
/* invalid message, let's ignore it */
|
|
|
|
|
return 0;
|
2013-12-17 16:24:16 +01:00
|
|
|
|
|
2013-12-09 22:43:29 +01:00
|
|
|
|
break;
|
|
|
|
|
|
2014-05-16 00:50:44 +02:00
|
|
|
|
case DHCP_STATE_BOUND:
|
|
|
|
|
r = client_handle_forcerenew(client, message, len);
|
|
|
|
|
if (r >= 0) {
|
|
|
|
|
r = client_timeout_t1(NULL, 0, client);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
goto error;
|
|
|
|
|
} else if (r == -ENOMSG)
|
|
|
|
|
/* invalid message, let's ignore it */
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2013-12-09 22:43:26 +01:00
|
|
|
|
case DHCP_STATE_INIT:
|
|
|
|
|
case DHCP_STATE_INIT_REBOOT:
|
|
|
|
|
|
|
|
|
|
break;
|
2014-04-09 12:12:08 +02:00
|
|
|
|
|
|
|
|
|
case DHCP_STATE_STOPPED:
|
|
|
|
|
r = -EINVAL;
|
|
|
|
|
goto error;
|
2013-12-09 22:43:26 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
error:
|
2014-05-17 21:23:20 +02:00
|
|
|
|
if (r < 0)
|
2014-04-09 12:12:07 +02:00
|
|
|
|
client_stop(client, r);
|
2013-12-09 22:43:27 +01:00
|
|
|
|
|
2014-04-09 12:12:07 +02:00
|
|
|
|
return r;
|
2013-12-09 22:43:26 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int client_receive_message_udp(
|
|
|
|
|
sd_event_source *s,
|
|
|
|
|
int fd,
|
|
|
|
|
uint32_t revents,
|
|
|
|
|
void *userdata) {
|
|
|
|
|
|
2014-02-13 20:56:16 +01:00
|
|
|
|
sd_dhcp_client *client = userdata;
|
2014-02-23 17:30:13 +01:00
|
|
|
|
_cleanup_free_ DHCPMessage *message = NULL;
|
2019-02-06 18:13:20 +01:00
|
|
|
|
const uint8_t *expected_chaddr = NULL;
|
2014-10-08 21:15:45 +02:00
|
|
|
|
uint8_t expected_hlen = 0;
|
2016-02-15 22:50:01 +01:00
|
|
|
|
ssize_t len, buflen;
|
2014-02-13 20:56:16 +01:00
|
|
|
|
|
|
|
|
|
assert(s);
|
|
|
|
|
assert(client);
|
|
|
|
|
|
2016-02-15 22:50:01 +01:00
|
|
|
|
buflen = next_datagram_size_fd(fd);
|
2020-10-09 15:06:34 +02:00
|
|
|
|
if (buflen == -ENETDOWN)
|
2019-02-17 19:06:34 +01:00
|
|
|
|
/* the link is down. Don't return an error or the I/O event
|
|
|
|
|
source will be disconnected and we won't be able to receive
|
|
|
|
|
packets again when the link comes back. */
|
|
|
|
|
return 0;
|
2016-02-15 22:50:01 +01:00
|
|
|
|
if (buflen < 0)
|
|
|
|
|
return buflen;
|
2014-02-23 17:30:13 +01:00
|
|
|
|
|
|
|
|
|
message = malloc0(buflen);
|
|
|
|
|
if (!message)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
2016-05-21 23:00:32 +02:00
|
|
|
|
len = recv(fd, message, buflen, 0);
|
2014-04-06 19:35:36 +02:00
|
|
|
|
if (len < 0) {
|
2019-02-17 19:06:34 +01:00
|
|
|
|
/* see comment above for why we shouldn't error out on ENETDOWN. */
|
|
|
|
|
if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
|
2015-11-24 18:25:52 +01:00
|
|
|
|
return 0;
|
|
|
|
|
|
2017-05-13 17:26:55 +02:00
|
|
|
|
return log_dhcp_client_errno(client, errno,
|
|
|
|
|
"Could not receive message from UDP socket: %m");
|
2016-05-23 15:56:01 +02:00
|
|
|
|
}
|
|
|
|
|
if ((size_t) len < sizeof(DHCPMessage)) {
|
2015-11-24 18:25:52 +01:00
|
|
|
|
log_dhcp_client(client, "Too small to be a DHCP message: ignoring");
|
2014-02-13 20:56:16 +01:00
|
|
|
|
return 0;
|
2014-07-26 17:53:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
|
2015-11-24 18:25:52 +01:00
|
|
|
|
log_dhcp_client(client, "Not a DHCP message: ignoring");
|
2014-07-26 17:53:33 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (message->op != BOOTREPLY) {
|
2015-11-24 18:25:52 +01:00
|
|
|
|
log_dhcp_client(client, "Not a BOOTREPLY message: ignoring");
|
2014-07-26 17:53:33 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-08 21:15:45 +02:00
|
|
|
|
if (message->htype != client->arp_type) {
|
2015-11-24 18:25:52 +01:00
|
|
|
|
log_dhcp_client(client, "Packet type does not match client type");
|
2014-10-08 21:15:45 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->arp_type == ARPHRD_ETHER) {
|
|
|
|
|
expected_hlen = ETH_ALEN;
|
2019-02-06 18:13:20 +01:00
|
|
|
|
expected_chaddr = &client->mac_addr[0];
|
2014-10-08 21:15:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (message->hlen != expected_hlen) {
|
2015-11-24 18:25:52 +01:00
|
|
|
|
log_dhcp_client(client, "Unexpected packet hlen %d", message->hlen);
|
2014-07-26 17:53:33 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 18:13:20 +01:00
|
|
|
|
if (expected_hlen > 0 && memcmp(&message->chaddr[0], expected_chaddr, expected_hlen)) {
|
2015-11-24 18:25:52 +01:00
|
|
|
|
log_dhcp_client(client, "Received chaddr does not match expected: ignoring");
|
2014-07-26 17:53:33 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-02-13 20:56:16 +01:00
|
|
|
|
|
2014-05-16 00:50:44 +02:00
|
|
|
|
if (client->state != DHCP_STATE_BOUND &&
|
|
|
|
|
be32toh(message->xid) != client->xid) {
|
|
|
|
|
/* in BOUND state, we may receive FORCERENEW with xid set by server,
|
|
|
|
|
so ignore the xid in this case */
|
2015-11-24 18:25:52 +01:00
|
|
|
|
log_dhcp_client(client, "Received xid (%u) does not match expected (%u): ignoring",
|
2014-05-16 00:50:44 +02:00
|
|
|
|
be32toh(message->xid), client->xid);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-19 17:19:22 +01:00
|
|
|
|
return client_handle_message(client, message, len);
|
2014-02-13 20:56:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 17:52:44 +02:00
|
|
|
|
static int client_receive_message_raw(
|
|
|
|
|
sd_event_source *s,
|
|
|
|
|
int fd,
|
|
|
|
|
uint32_t revents,
|
|
|
|
|
void *userdata) {
|
|
|
|
|
|
2014-02-13 20:56:16 +01:00
|
|
|
|
sd_dhcp_client *client = userdata;
|
2014-02-23 17:30:13 +01:00
|
|
|
|
_cleanup_free_ DHCPPacket *packet = NULL;
|
2020-04-24 23:54:25 +02:00
|
|
|
|
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct tpacket_auxdata))) control;
|
2014-02-24 01:09:21 +01:00
|
|
|
|
struct iovec iov = {};
|
|
|
|
|
struct msghdr msg = {
|
|
|
|
|
.msg_iov = &iov,
|
|
|
|
|
.msg_iovlen = 1,
|
2020-04-24 23:54:25 +02:00
|
|
|
|
.msg_control = &control,
|
|
|
|
|
.msg_controllen = sizeof(control),
|
2014-02-24 01:09:21 +01:00
|
|
|
|
};
|
|
|
|
|
struct cmsghdr *cmsg;
|
|
|
|
|
bool checksum = true;
|
2016-02-15 22:50:01 +01:00
|
|
|
|
ssize_t buflen, len;
|
|
|
|
|
int r;
|
2014-02-13 20:56:16 +01:00
|
|
|
|
|
|
|
|
|
assert(s);
|
|
|
|
|
assert(client);
|
|
|
|
|
|
2016-02-15 22:50:01 +01:00
|
|
|
|
buflen = next_datagram_size_fd(fd);
|
2019-02-17 19:06:34 +01:00
|
|
|
|
if (buflen == -ENETDOWN)
|
|
|
|
|
return 0;
|
2016-02-15 22:50:01 +01:00
|
|
|
|
if (buflen < 0)
|
|
|
|
|
return buflen;
|
2014-02-23 17:30:13 +01:00
|
|
|
|
|
|
|
|
|
packet = malloc0(buflen);
|
|
|
|
|
if (!packet)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
2018-11-27 10:34:32 +01:00
|
|
|
|
iov = IOVEC_MAKE(packet, buflen);
|
2014-02-24 01:09:21 +01:00
|
|
|
|
|
2020-04-23 19:57:53 +02:00
|
|
|
|
len = recvmsg_safe(fd, &msg, 0);
|
|
|
|
|
if (IN_SET(len, -EAGAIN, -EINTR, -ENETDOWN))
|
|
|
|
|
return 0;
|
|
|
|
|
if (len < 0)
|
|
|
|
|
return log_dhcp_client_errno(client, len,
|
2017-05-13 17:26:55 +02:00
|
|
|
|
"Could not receive message from raw socket: %m");
|
2020-04-23 19:57:53 +02:00
|
|
|
|
|
|
|
|
|
if ((size_t) len < sizeof(DHCPPacket))
|
2014-04-06 19:35:36 +02:00
|
|
|
|
return 0;
|
2014-02-24 01:09:21 +01:00
|
|
|
|
|
2020-04-17 11:52:05 +02:00
|
|
|
|
cmsg = cmsg_find(&msg, SOL_PACKET, PACKET_AUXDATA, CMSG_LEN(sizeof(struct tpacket_auxdata)));
|
|
|
|
|
if (cmsg) {
|
|
|
|
|
struct tpacket_auxdata *aux = (struct tpacket_auxdata*) CMSG_DATA(cmsg);
|
|
|
|
|
checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
|
|
|
|
|
}
|
2014-02-13 20:56:16 +01:00
|
|
|
|
|
2016-11-11 00:34:19 +01:00
|
|
|
|
r = dhcp_packet_verify_headers(packet, len, checksum, client->port);
|
2014-02-23 01:34:05 +01:00
|
|
|
|
if (r < 0)
|
2014-02-16 23:28:19 +01:00
|
|
|
|
return 0;
|
2014-02-13 20:56:16 +01:00
|
|
|
|
|
|
|
|
|
len -= DHCP_IP_UDP_SIZE;
|
|
|
|
|
|
2014-03-19 17:19:22 +01:00
|
|
|
|
return client_handle_message(client, &packet->dhcp, len);
|
2014-02-13 20:56:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-04 09:20:04 +01:00
|
|
|
|
int sd_dhcp_client_send_renew(sd_dhcp_client *client) {
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
assert_return(client->fd >= 0, -EINVAL);
|
|
|
|
|
|
2020-07-15 12:25:55 +02:00
|
|
|
|
if (!client->lease)
|
2020-07-07 02:43:10 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
2019-03-04 09:20:04 +01:00
|
|
|
|
client->start_delay = 0;
|
|
|
|
|
client->attempt = 1;
|
|
|
|
|
client->state = DHCP_STATE_RENEWING;
|
|
|
|
|
|
|
|
|
|
return client_initialize_time_events(client);
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-16 19:55:25 +01:00
|
|
|
|
int sd_dhcp_client_start(sd_dhcp_client *client) {
|
2013-12-20 16:16:19 +01:00
|
|
|
|
int r;
|
2013-12-09 22:43:25 +01:00
|
|
|
|
|
2013-12-09 22:43:19 +01:00
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
|
2014-03-12 10:46:40 +01:00
|
|
|
|
r = client_initialize(client);
|
|
|
|
|
if (r < 0)
|
2013-12-20 16:16:19 +01:00
|
|
|
|
return r;
|
2013-12-09 22:43:26 +01:00
|
|
|
|
|
2017-08-03 19:42:06 +02:00
|
|
|
|
/* RFC7844 section 3.3:
|
|
|
|
|
SHOULD perform a complete four-way handshake, starting with a
|
|
|
|
|
DHCPDISCOVER, to obtain a new address lease. If the client can
|
|
|
|
|
ascertain that this is exactly the same network to which it was
|
|
|
|
|
previously connected, and if the link-layer address did not change,
|
|
|
|
|
the client MAY issue a DHCPREQUEST to try to reclaim the current
|
|
|
|
|
address. */
|
|
|
|
|
if (client->last_addr && !client->anonymize)
|
2014-03-12 10:46:40 +01:00
|
|
|
|
client->state = DHCP_STATE_INIT_REBOOT;
|
2013-12-09 22:43:25 +01:00
|
|
|
|
|
2014-04-12 01:01:13 +02:00
|
|
|
|
r = client_start(client);
|
|
|
|
|
if (r >= 0)
|
2016-05-23 16:13:18 +02:00
|
|
|
|
log_dhcp_client(client, "STARTED on ifindex %i", client->ifindex);
|
2014-04-12 01:01:13 +02:00
|
|
|
|
|
|
|
|
|
return r;
|
2013-12-09 22:43:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-13 16:30:28 +02:00
|
|
|
|
int sd_dhcp_client_send_release(sd_dhcp_client *client) {
|
|
|
|
|
assert_return(client, -EINVAL);
|
2019-10-02 10:22:49 +02:00
|
|
|
|
assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE);
|
2019-10-02 10:29:41 +02:00
|
|
|
|
assert_return(client->lease, -EUNATCH);
|
2019-10-02 10:22:49 +02:00
|
|
|
|
|
|
|
|
|
_cleanup_free_ DHCPPacket *release = NULL;
|
|
|
|
|
size_t optoffset, optlen;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
r = client_message_init(client, &release, DHCP_RELEASE, &optlen, &optoffset);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
/* Fill up release IP and MAC */
|
|
|
|
|
release->dhcp.ciaddr = client->lease->address;
|
|
|
|
|
memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
|
2019-05-13 16:30:28 +02:00
|
|
|
|
|
2019-10-02 10:22:49 +02:00
|
|
|
|
r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
|
|
|
|
|
SD_DHCP_OPTION_END, 0, NULL);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
r = dhcp_network_send_udp_socket(client->fd,
|
|
|
|
|
client->lease->server_address,
|
|
|
|
|
DHCP_PORT_SERVER,
|
|
|
|
|
&release->dhcp,
|
|
|
|
|
sizeof(DHCPMessage) + optoffset);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
log_dhcp_client(client, "RELEASE");
|
2019-05-13 16:30:28 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-20 14:13:18 +01:00
|
|
|
|
int sd_dhcp_client_send_decline(sd_dhcp_client *client) {
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE);
|
|
|
|
|
assert_return(client->lease, -EUNATCH);
|
|
|
|
|
|
|
|
|
|
_cleanup_free_ DHCPPacket *release = NULL;
|
|
|
|
|
size_t optoffset, optlen;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
r = client_message_init(client, &release, DHCP_DECLINE, &optlen, &optoffset);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
release->dhcp.ciaddr = client->lease->address;
|
|
|
|
|
memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
|
|
|
|
|
|
|
|
|
|
r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
|
|
|
|
|
SD_DHCP_OPTION_END, 0, NULL);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
r = dhcp_network_send_udp_socket(client->fd,
|
|
|
|
|
client->lease->server_address,
|
|
|
|
|
DHCP_PORT_SERVER,
|
|
|
|
|
&release->dhcp,
|
|
|
|
|
sizeof(DHCPMessage) + optoffset);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
log_dhcp_client(client, "DECLINE");
|
|
|
|
|
|
|
|
|
|
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
|
|
|
|
|
|
|
|
|
|
if (client->state != DHCP_STATE_STOPPED) {
|
|
|
|
|
r = sd_dhcp_client_start(client);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-16 19:55:25 +01:00
|
|
|
|
int sd_dhcp_client_stop(sd_dhcp_client *client) {
|
2020-10-15 00:24:17 +02:00
|
|
|
|
if (!client)
|
|
|
|
|
return 0;
|
2014-05-22 15:18:28 +02:00
|
|
|
|
|
2020-10-15 00:24:17 +02:00
|
|
|
|
DHCP_CLIENT_DONT_DESTROY(client);
|
2014-04-09 12:12:07 +02:00
|
|
|
|
|
2015-09-22 14:46:21 +02:00
|
|
|
|
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
|
2014-05-22 15:18:28 +02:00
|
|
|
|
client->state = DHCP_STATE_STOPPED;
|
2014-04-09 12:12:07 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
2013-12-09 22:43:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-16 19:33:36 +01:00
|
|
|
|
int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t priority) {
|
2014-01-18 19:32:45 +01: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_dhcp_client_detach_event(sd_dhcp_client *client) {
|
|
|
|
|
assert_return(client, -EINVAL);
|
|
|
|
|
|
|
|
|
|
client->event = sd_event_unref(client->event);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
|
2016-05-23 16:27:05 +02:00
|
|
|
|
assert_return(client, NULL);
|
2014-01-18 19:32:45 +01:00
|
|
|
|
|
|
|
|
|
return client->event;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-27 07:01:46 +02:00
|
|
|
|
static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) {
|
2019-10-24 12:04:48 +02:00
|
|
|
|
if (!client)
|
|
|
|
|
return NULL;
|
2014-04-09 12:12:07 +02:00
|
|
|
|
|
2015-08-26 21:05:53 +02:00
|
|
|
|
log_dhcp_client(client, "FREE");
|
2014-04-11 05:45:46 +02:00
|
|
|
|
|
2018-11-13 05:50:08 +01:00
|
|
|
|
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
|
|
|
|
|
client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
|
|
|
|
|
client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
|
|
|
|
|
client->timeout_expire = sd_event_source_unref(client->timeout_expire);
|
2015-08-26 21:05:53 +02:00
|
|
|
|
|
2018-11-13 05:50:08 +01:00
|
|
|
|
client_initialize(client);
|
2015-08-26 21:05:53 +02:00
|
|
|
|
|
|
|
|
|
sd_dhcp_client_detach_event(client);
|
|
|
|
|
|
|
|
|
|
sd_dhcp_lease_unref(client->lease);
|
|
|
|
|
|
2020-10-14 05:47:58 +02:00
|
|
|
|
set_free(client->req_opts);
|
2015-08-26 21:05:53 +02:00
|
|
|
|
free(client->hostname);
|
|
|
|
|
free(client->vendor_class_identifier);
|
2020-03-30 16:42:48 +02:00
|
|
|
|
free(client->mudurl);
|
2018-05-07 14:21:02 +02:00
|
|
|
|
client->user_class = strv_free(client->user_class);
|
2020-02-28 19:28:49 +01:00
|
|
|
|
ordered_hashmap_free(client->extra_options);
|
|
|
|
|
ordered_hashmap_free(client->vendor_options);
|
2016-10-17 00:28:30 +02:00
|
|
|
|
return mfree(client);
|
2013-12-09 22:43:32 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-27 07:01:46 +02:00
|
|
|
|
DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_client, sd_dhcp_client, dhcp_client_free);
|
|
|
|
|
|
2017-08-03 03:32:46 +02:00
|
|
|
|
int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
|
2020-10-14 05:47:58 +02:00
|
|
|
|
const uint8_t *opts;
|
|
|
|
|
size_t n_opts;
|
|
|
|
|
int r;
|
|
|
|
|
|
2014-01-18 19:32:45 +01:00
|
|
|
|
assert_return(ret, -EINVAL);
|
2013-12-09 22:43:25 +01:00
|
|
|
|
|
2019-10-24 12:04:48 +02:00
|
|
|
|
_cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = new(sd_dhcp_client, 1);
|
2013-12-09 22:43:08 +01:00
|
|
|
|
if (!client)
|
2014-01-18 19:32:45 +01:00
|
|
|
|
return -ENOMEM;
|
2013-12-09 22:43:08 +01:00
|
|
|
|
|
2018-11-13 05:49:12 +01:00
|
|
|
|
*client = (sd_dhcp_client) {
|
|
|
|
|
.n_ref = 1,
|
|
|
|
|
.state = DHCP_STATE_INIT,
|
|
|
|
|
.ifindex = -1,
|
|
|
|
|
.fd = -1,
|
|
|
|
|
.mtu = DHCP_DEFAULT_MIN_SIZE,
|
|
|
|
|
.port = DHCP_PORT_CLIENT,
|
|
|
|
|
.anonymize = !!anonymize,
|
2019-05-04 13:40:54 +02:00
|
|
|
|
.max_attempts = (uint64_t) -1,
|
2019-09-23 13:25:21 +02:00
|
|
|
|
.ip_service_type = -1,
|
2018-11-13 05:49:12 +01:00
|
|
|
|
};
|
2017-08-03 03:32:46 +02:00
|
|
|
|
/* NOTE: this could be moved to a function. */
|
|
|
|
|
if (anonymize) {
|
2020-10-14 05:47:58 +02:00
|
|
|
|
n_opts = ELEMENTSOF(default_req_opts_anonymize);
|
|
|
|
|
opts = default_req_opts_anonymize;
|
2017-08-03 03:32:46 +02:00
|
|
|
|
} else {
|
2020-10-14 05:47:58 +02:00
|
|
|
|
n_opts = ELEMENTSOF(default_req_opts);
|
|
|
|
|
opts = default_req_opts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < n_opts; i++) {
|
|
|
|
|
r = sd_dhcp_client_set_request_option(client, opts[i]);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2017-08-03 03:32:46 +02:00
|
|
|
|
}
|
2014-01-18 19:32:45 +01:00
|
|
|
|
|
2018-04-05 07:26:26 +02:00
|
|
|
|
*ret = TAKE_PTR(client);
|
2013-12-09 22:43:08 +01:00
|
|
|
|
|
2014-01-18 19:32:45 +01:00
|
|
|
|
return 0;
|
2013-12-09 22:43:08 +01:00
|
|
|
|
}
|