2017-11-18 17:09:20 +01:00
|
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2014-05-24 18:48:41 +02:00
|
|
|
|
/***
|
2018-06-12 17:15:23 +02:00
|
|
|
|
Copyright © 2013 Intel Corporation. All rights reserved.
|
2014-05-24 18:48:41 +02:00
|
|
|
|
***/
|
|
|
|
|
|
2014-05-24 19:27:20 +02:00
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
|
2014-05-24 18:48:41 +02:00
|
|
|
|
#include "sd-dhcp-server.h"
|
2019-11-28 09:14:22 +01:00
|
|
|
|
#include "sd-id128.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
|
#include "alloc-util.h"
|
2014-05-24 19:27:20 +02:00
|
|
|
|
#include "dhcp-internal.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
|
#include "dhcp-server-internal.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
|
#include "fd-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
|
#include "in-addr-util.h"
|
2018-11-27 10:34:32 +01:00
|
|
|
|
#include "io-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
|
#include "siphash24.h"
|
|
|
|
|
#include "string-util.h"
|
2016-05-26 23:48:04 +02:00
|
|
|
|
#include "unaligned.h"
|
2014-05-24 18:48:41 +02:00
|
|
|
|
|
2015-08-27 01:47:42 +02:00
|
|
|
|
#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
|
|
|
|
|
#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
|
2014-05-25 17:31:17 +02:00
|
|
|
|
|
2018-11-25 15:58:49 +01:00
|
|
|
|
static DHCPLease *dhcp_lease_free(DHCPLease *lease) {
|
2017-06-09 17:18:25 +02:00
|
|
|
|
if (!lease)
|
2018-11-25 15:58:49 +01:00
|
|
|
|
return NULL;
|
2017-06-09 17:18:25 +02:00
|
|
|
|
|
|
|
|
|
free(lease->client_id.data);
|
2018-11-25 15:58:49 +01:00
|
|
|
|
return mfree(lease);
|
2017-06-09 17:18:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-28 20:29:10 +02:00
|
|
|
|
/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
|
|
|
|
|
* the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
|
|
|
|
|
* moreover, the server's own address may be in the pool, and is in that case reserved in order not to
|
|
|
|
|
* accidentally hand it out */
|
|
|
|
|
int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) {
|
|
|
|
|
struct in_addr netmask_addr;
|
|
|
|
|
be32_t netmask;
|
|
|
|
|
uint32_t server_off, broadcast_off, size_max;
|
|
|
|
|
|
2014-05-25 18:28:03 +02:00
|
|
|
|
assert_return(server, -EINVAL);
|
|
|
|
|
assert_return(address, -EINVAL);
|
2015-08-28 20:29:10 +02:00
|
|
|
|
assert_return(address->s_addr != INADDR_ANY, -EINVAL);
|
|
|
|
|
assert_return(prefixlen <= 32, -ERANGE);
|
|
|
|
|
|
2017-09-01 14:40:02 +02:00
|
|
|
|
assert_se(in4_addr_prefixlen_to_netmask(&netmask_addr, prefixlen));
|
2015-08-28 20:29:10 +02:00
|
|
|
|
netmask = netmask_addr.s_addr;
|
|
|
|
|
|
|
|
|
|
server_off = be32toh(address->s_addr & ~netmask);
|
|
|
|
|
broadcast_off = be32toh(~netmask);
|
|
|
|
|
|
|
|
|
|
/* the server address cannot be the subnet address */
|
|
|
|
|
assert_return(server_off != 0, -ERANGE);
|
|
|
|
|
|
|
|
|
|
/* nor the broadcast address */
|
|
|
|
|
assert_return(server_off != broadcast_off, -ERANGE);
|
|
|
|
|
|
|
|
|
|
/* 0 offset means we should set a default, we skip the first (subnet) address
|
|
|
|
|
and take the next one */
|
|
|
|
|
if (offset == 0)
|
|
|
|
|
offset = 1;
|
|
|
|
|
|
|
|
|
|
size_max = (broadcast_off + 1) /* the number of addresses in the subnet */
|
|
|
|
|
- offset /* exclude the addresses before the offset */
|
|
|
|
|
- 1; /* exclude the last (broadcast) address */
|
|
|
|
|
|
|
|
|
|
/* The pool must contain at least one address */
|
|
|
|
|
assert_return(size_max >= 1, -ERANGE);
|
|
|
|
|
|
|
|
|
|
if (size != 0)
|
|
|
|
|
assert_return(size <= size_max, -ERANGE);
|
|
|
|
|
else
|
|
|
|
|
size = size_max;
|
2014-05-25 22:07:53 +02:00
|
|
|
|
|
2017-06-09 17:18:25 +02:00
|
|
|
|
if (server->address != address->s_addr || server->netmask != netmask || server->pool_size != size || server->pool_offset != offset) {
|
|
|
|
|
|
|
|
|
|
free(server->bound_leases);
|
|
|
|
|
server->bound_leases = new0(DHCPLease*, size);
|
|
|
|
|
if (!server->bound_leases)
|
|
|
|
|
return -ENOMEM;
|
2014-05-25 18:28:03 +02:00
|
|
|
|
|
2017-06-09 17:18:25 +02:00
|
|
|
|
server->pool_offset = offset;
|
|
|
|
|
server->pool_size = size;
|
2014-05-25 18:28:03 +02:00
|
|
|
|
|
2017-06-09 17:18:25 +02:00
|
|
|
|
server->address = address->s_addr;
|
|
|
|
|
server->netmask = netmask;
|
|
|
|
|
server->subnet = address->s_addr & netmask;
|
2015-08-28 20:29:10 +02:00
|
|
|
|
|
2017-06-09 17:18:25 +02:00
|
|
|
|
if (server_off >= offset && server_off - offset < size)
|
|
|
|
|
server->bound_leases[server_off - offset] = &server->invalid_lease;
|
|
|
|
|
|
|
|
|
|
/* Drop any leases associated with the old address range */
|
2018-11-25 15:58:49 +01:00
|
|
|
|
hashmap_clear(server->leases_by_client_id);
|
2017-06-09 17:18:25 +02:00
|
|
|
|
}
|
2014-05-25 00:29:13 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-24 23:42:56 +02:00
|
|
|
|
int sd_dhcp_server_is_running(sd_dhcp_server *server) {
|
2015-08-06 00:32:25 +02:00
|
|
|
|
assert_return(server, false);
|
2014-06-29 13:11:44 +02:00
|
|
|
|
|
|
|
|
|
return !!server->receive_message;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-27 14:25:20 +01:00
|
|
|
|
void client_id_hash_func(const DHCPClientId *id, struct siphash *state) {
|
2014-05-25 22:07:53 +02:00
|
|
|
|
assert(id);
|
|
|
|
|
assert(id->length);
|
|
|
|
|
assert(id->data);
|
|
|
|
|
|
2015-10-04 01:59:06 +02:00
|
|
|
|
siphash24_compress(&id->length, sizeof(id->length), state);
|
2015-10-04 00:22:41 +02:00
|
|
|
|
siphash24_compress(id->data, id->length, state);
|
2014-05-25 22:07:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-27 14:25:20 +01:00
|
|
|
|
int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b) {
|
2018-08-03 01:43:37 +02:00
|
|
|
|
int r;
|
2014-05-25 22:07:53 +02:00
|
|
|
|
|
|
|
|
|
assert(!a->length || a->data);
|
|
|
|
|
assert(!b->length || b->data);
|
|
|
|
|
|
2018-08-03 01:43:37 +02:00
|
|
|
|
r = CMP(a->length, b->length);
|
|
|
|
|
if (r != 0)
|
|
|
|
|
return r;
|
2014-05-25 22:07:53 +02:00
|
|
|
|
|
|
|
|
|
return memcmp(a->data, b->data, a->length);
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-25 15:58:49 +01:00
|
|
|
|
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(dhcp_lease_hash_ops, DHCPClientId, client_id_hash_func, client_id_compare_func,
|
|
|
|
|
DHCPLease, dhcp_lease_free);
|
2014-08-13 01:00:18 +02:00
|
|
|
|
|
2018-08-27 07:01:46 +02:00
|
|
|
|
static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
|
|
|
|
|
assert(server);
|
2014-05-24 19:27:20 +02:00
|
|
|
|
|
2014-06-18 18:54:52 +02:00
|
|
|
|
log_dhcp_server(server, "UNREF");
|
2014-05-25 22:07:53 +02:00
|
|
|
|
|
2014-06-18 18:54:52 +02:00
|
|
|
|
sd_dhcp_server_stop(server);
|
2014-05-25 22:07:53 +02:00
|
|
|
|
|
2014-06-18 18:54:52 +02:00
|
|
|
|
sd_event_unref(server->event);
|
|
|
|
|
|
2015-08-26 19:19:32 +02:00
|
|
|
|
free(server->timezone);
|
2015-08-27 14:48:37 +02:00
|
|
|
|
free(server->dns);
|
|
|
|
|
free(server->ntp);
|
2019-09-18 15:22:47 +02:00
|
|
|
|
free(server->sip);
|
2015-08-26 19:19:32 +02:00
|
|
|
|
|
2014-06-18 18:54:52 +02:00
|
|
|
|
hashmap_free(server->leases_by_client_id);
|
|
|
|
|
|
2020-02-28 19:28:49 +01:00
|
|
|
|
ordered_hashmap_free(server->extra_options);
|
|
|
|
|
ordered_hashmap_free(server->vendor_options);
|
2019-09-20 04:22:17 +02:00
|
|
|
|
|
2014-06-18 18:54:52 +02:00
|
|
|
|
free(server->bound_leases);
|
2016-10-17 00:28:30 +02:00
|
|
|
|
return mfree(server);
|
2014-05-24 18:48:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-27 07:01:46 +02:00
|
|
|
|
DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_server, sd_dhcp_server, dhcp_server_free);
|
|
|
|
|
|
2014-05-24 19:38:17 +02:00
|
|
|
|
int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
|
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_server_unrefp) sd_dhcp_server *server = NULL;
|
2014-05-24 18:48:41 +02:00
|
|
|
|
|
|
|
|
|
assert_return(ret, -EINVAL);
|
2014-05-24 19:38:17 +02:00
|
|
|
|
assert_return(ifindex > 0, -EINVAL);
|
2014-05-24 18:48:41 +02:00
|
|
|
|
|
|
|
|
|
server = new0(sd_dhcp_server, 1);
|
|
|
|
|
if (!server)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
2015-08-26 21:05:53 +02:00
|
|
|
|
server->n_ref = 1;
|
2014-05-24 23:03:49 +02:00
|
|
|
|
server->fd_raw = -1;
|
2014-05-24 19:27:20 +02:00
|
|
|
|
server->fd = -1;
|
2014-05-25 00:29:13 +02:00
|
|
|
|
server->address = htobe32(INADDR_ANY);
|
2014-08-04 20:57:02 +02:00
|
|
|
|
server->netmask = htobe32(INADDR_ANY);
|
2015-08-26 23:31:47 +02:00
|
|
|
|
server->ifindex = ifindex;
|
2017-02-09 10:16:52 +01:00
|
|
|
|
|
2018-11-25 15:58:49 +01:00
|
|
|
|
server->leases_by_client_id = hashmap_new(&dhcp_lease_hash_ops);
|
2017-02-09 10:16:52 +01:00
|
|
|
|
if (!server->leases_by_client_id)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
2015-08-27 01:47:42 +02:00
|
|
|
|
server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC);
|
|
|
|
|
server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC);
|
2014-05-24 18:48:41 +02:00
|
|
|
|
|
2018-04-05 07:26:26 +02:00
|
|
|
|
*ret = TAKE_PTR(server);
|
2014-05-24 18:48:41 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-16 19:33:36 +01:00
|
|
|
|
int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int64_t priority) {
|
2014-05-24 18:48:41 +02:00
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert_return(server, -EINVAL);
|
|
|
|
|
assert_return(!server->event, -EBUSY);
|
|
|
|
|
|
|
|
|
|
if (event)
|
|
|
|
|
server->event = sd_event_ref(event);
|
|
|
|
|
else {
|
|
|
|
|
r = sd_event_default(&server->event);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
server->event_priority = priority;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
|
|
|
|
|
assert_return(server, -EINVAL);
|
|
|
|
|
|
|
|
|
|
server->event = sd_event_unref(server->event);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
|
|
|
|
|
assert_return(server, NULL);
|
|
|
|
|
|
|
|
|
|
return server->event;
|
|
|
|
|
}
|
2014-05-24 19:27:20 +02:00
|
|
|
|
|
|
|
|
|
int sd_dhcp_server_stop(sd_dhcp_server *server) {
|
|
|
|
|
assert_return(server, -EINVAL);
|
|
|
|
|
|
|
|
|
|
server->receive_message =
|
|
|
|
|
sd_event_source_unref(server->receive_message);
|
|
|
|
|
|
2014-05-24 23:03:49 +02:00
|
|
|
|
server->fd_raw = safe_close(server->fd_raw);
|
2014-05-24 19:27:20 +02:00
|
|
|
|
server->fd = safe_close(server->fd);
|
|
|
|
|
|
|
|
|
|
log_dhcp_server(server, "STOPPED");
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-14 01:26:55 +02:00
|
|
|
|
static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
|
|
|
|
|
DHCPPacket *packet, size_t len) {
|
2014-05-25 17:20:57 +02:00
|
|
|
|
union sockaddr_union link = {
|
|
|
|
|
.ll.sll_family = AF_PACKET,
|
2016-06-15 01:26:01 +02:00
|
|
|
|
.ll.sll_protocol = htobe16(ETH_P_IP),
|
2015-08-26 23:31:47 +02:00
|
|
|
|
.ll.sll_ifindex = server->ifindex,
|
2014-05-25 17:20:57 +02:00
|
|
|
|
.ll.sll_halen = ETH_ALEN,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
assert(server);
|
2015-08-26 23:31:47 +02:00
|
|
|
|
assert(server->ifindex > 0);
|
2014-05-25 17:20:57 +02:00
|
|
|
|
assert(server->address);
|
|
|
|
|
assert(packet);
|
|
|
|
|
assert(len > sizeof(DHCPPacket));
|
|
|
|
|
|
|
|
|
|
memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
|
|
|
|
|
|
|
|
|
|
dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
|
2014-08-14 01:26:55 +02:00
|
|
|
|
packet->dhcp.yiaddr,
|
2019-09-23 13:25:21 +02:00
|
|
|
|
DHCP_PORT_CLIENT, len, -1);
|
2014-05-25 17:20:57 +02:00
|
|
|
|
|
2015-08-26 23:31:47 +02:00
|
|
|
|
return dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
|
2014-05-25 17:20:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
|
2016-02-25 14:36:40 +01:00
|
|
|
|
uint16_t destination_port,
|
2014-05-25 17:20:57 +02:00
|
|
|
|
DHCPMessage *message, size_t len) {
|
|
|
|
|
union sockaddr_union dest = {
|
|
|
|
|
.in.sin_family = AF_INET,
|
2016-02-25 14:36:40 +01:00
|
|
|
|
.in.sin_port = htobe16(destination_port),
|
2014-05-25 17:20:57 +02:00
|
|
|
|
.in.sin_addr.s_addr = destination,
|
|
|
|
|
};
|
|
|
|
|
struct iovec iov = {
|
|
|
|
|
.iov_base = message,
|
|
|
|
|
.iov_len = len,
|
|
|
|
|
};
|
|
|
|
|
uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
|
|
|
|
|
struct msghdr msg = {
|
|
|
|
|
.msg_name = &dest,
|
|
|
|
|
.msg_namelen = sizeof(dest.in),
|
|
|
|
|
.msg_iov = &iov,
|
|
|
|
|
.msg_iovlen = 1,
|
|
|
|
|
.msg_control = cmsgbuf,
|
|
|
|
|
.msg_controllen = sizeof(cmsgbuf),
|
|
|
|
|
};
|
|
|
|
|
struct cmsghdr *cmsg;
|
|
|
|
|
struct in_pktinfo *pktinfo;
|
|
|
|
|
|
|
|
|
|
assert(server);
|
2018-02-26 18:33:51 +01:00
|
|
|
|
assert(server->fd >= 0);
|
2014-05-25 17:20:57 +02:00
|
|
|
|
assert(message);
|
|
|
|
|
assert(len > sizeof(DHCPMessage));
|
|
|
|
|
|
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
|
|
|
assert(cmsg);
|
|
|
|
|
|
|
|
|
|
cmsg->cmsg_level = IPPROTO_IP;
|
|
|
|
|
cmsg->cmsg_type = IP_PKTINFO;
|
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
|
|
|
|
|
|
|
|
|
|
/* we attach source interface and address info to the message
|
|
|
|
|
rather than binding the socket. This will be mostly useful
|
|
|
|
|
when we gain support for arbitrary number of server addresses
|
|
|
|
|
*/
|
|
|
|
|
pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
|
|
|
|
|
assert(pktinfo);
|
|
|
|
|
|
2015-08-26 23:31:47 +02:00
|
|
|
|
pktinfo->ipi_ifindex = server->ifindex;
|
2014-05-25 17:20:57 +02:00
|
|
|
|
pktinfo->ipi_spec_dst.s_addr = server->address;
|
|
|
|
|
|
2018-03-21 20:25:46 +01:00
|
|
|
|
if (sendmsg(server->fd, &msg, 0) < 0)
|
2014-05-25 17:20:57 +02:00
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool requested_broadcast(DHCPRequest *req) {
|
|
|
|
|
assert(req);
|
|
|
|
|
|
|
|
|
|
return req->message->flags & htobe16(0x8000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int dhcp_server_send_packet(sd_dhcp_server *server,
|
|
|
|
|
DHCPRequest *req, DHCPPacket *packet,
|
|
|
|
|
int type, size_t optoffset) {
|
|
|
|
|
be32_t destination = INADDR_ANY;
|
2016-02-25 14:36:40 +01:00
|
|
|
|
uint16_t destination_port = DHCP_PORT_CLIENT;
|
2014-05-25 17:20:57 +02:00
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(server);
|
|
|
|
|
assert(req);
|
|
|
|
|
assert(req->max_optlen);
|
|
|
|
|
assert(optoffset <= req->max_optlen);
|
|
|
|
|
assert(packet);
|
|
|
|
|
|
|
|
|
|
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_SERVER_IDENTIFIER,
|
2014-05-25 17:20:57 +02:00
|
|
|
|
4, &server->address);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_END, 0, NULL);
|
2014-05-25 17:20:57 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
/* RFC 2131 Section 4.1
|
|
|
|
|
|
|
|
|
|
If the ’giaddr’ field in a DHCP message from a client is non-zero,
|
|
|
|
|
the server sends any return messages to the ’DHCP server’ port on the
|
|
|
|
|
BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
|
|
|
|
|
field is zero and the ’ciaddr’ field is nonzero, then the server
|
|
|
|
|
unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
|
|
|
|
|
If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
|
|
|
|
|
set, then the server broadcasts DHCPOFFER and DHCPACK messages to
|
|
|
|
|
0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
|
|
|
|
|
’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
|
|
|
|
|
messages to the client’s hardware address and ’yiaddr’ address. In
|
|
|
|
|
all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
|
|
|
|
|
messages to 0xffffffff.
|
|
|
|
|
|
|
|
|
|
Section 4.3.2
|
|
|
|
|
|
|
|
|
|
If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
|
|
|
|
|
different subnet. The server MUST set the broadcast bit in the
|
|
|
|
|
DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
|
|
|
|
|
client, because the client may not have a correct network address
|
|
|
|
|
or subnet mask, and the client may not be answering ARP requests.
|
|
|
|
|
*/
|
|
|
|
|
if (req->message->giaddr) {
|
|
|
|
|
destination = req->message->giaddr;
|
2016-02-25 14:36:40 +01:00
|
|
|
|
destination_port = DHCP_PORT_SERVER;
|
2014-05-25 17:20:57 +02:00
|
|
|
|
if (type == DHCP_NAK)
|
|
|
|
|
packet->dhcp.flags = htobe16(0x8000);
|
|
|
|
|
} else if (req->message->ciaddr && type != DHCP_NAK)
|
|
|
|
|
destination = req->message->ciaddr;
|
|
|
|
|
|
2014-06-21 14:39:36 +02:00
|
|
|
|
if (destination != INADDR_ANY)
|
2016-02-25 14:36:40 +01:00
|
|
|
|
return dhcp_server_send_udp(server, destination,
|
|
|
|
|
destination_port, &packet->dhcp,
|
2014-05-25 17:20:57 +02:00
|
|
|
|
sizeof(DHCPMessage) + optoffset);
|
2014-06-21 14:39:36 +02:00
|
|
|
|
else if (requested_broadcast(req) || type == DHCP_NAK)
|
2014-08-14 01:26:55 +02:00
|
|
|
|
return dhcp_server_send_udp(server, INADDR_BROADCAST,
|
2016-02-25 14:36:40 +01:00
|
|
|
|
destination_port, &packet->dhcp,
|
2014-06-21 14:39:36 +02:00
|
|
|
|
sizeof(DHCPMessage) + optoffset);
|
2014-05-25 17:20:57 +02:00
|
|
|
|
else
|
2014-08-14 01:26:55 +02:00
|
|
|
|
/* we cannot send UDP packet to specific MAC address when the
|
|
|
|
|
address is not yet configured, so must fall back to raw
|
|
|
|
|
packets */
|
2014-05-25 17:20:57 +02:00
|
|
|
|
return dhcp_server_send_unicast_raw(server, packet,
|
|
|
|
|
sizeof(DHCPPacket) + optoffset);
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-25 17:31:17 +02:00
|
|
|
|
static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
|
2014-08-14 01:26:55 +02:00
|
|
|
|
uint8_t type, size_t *_optoffset,
|
|
|
|
|
DHCPRequest *req) {
|
2014-05-25 17:31:17 +02:00
|
|
|
|
_cleanup_free_ DHCPPacket *packet = NULL;
|
2014-07-12 23:07:33 +02:00
|
|
|
|
size_t optoffset = 0;
|
2014-05-25 17:31:17 +02:00
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(server);
|
|
|
|
|
assert(ret);
|
|
|
|
|
assert(_optoffset);
|
2014-05-25 20:39:02 +02:00
|
|
|
|
assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
|
2014-05-25 17:31:17 +02:00
|
|
|
|
|
|
|
|
|
packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
|
|
|
|
|
if (!packet)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
2014-08-14 01:26:55 +02:00
|
|
|
|
r = dhcp_message_init(&packet->dhcp, BOOTREPLY,
|
2014-10-08 21:15:45 +02:00
|
|
|
|
be32toh(req->message->xid), type, ARPHRD_ETHER,
|
|
|
|
|
req->max_optlen, &optoffset);
|
2014-05-25 17:31:17 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
packet->dhcp.flags = req->message->flags;
|
|
|
|
|
packet->dhcp.giaddr = req->message->giaddr;
|
|
|
|
|
memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
|
|
|
|
|
|
|
|
|
|
*_optoffset = optoffset;
|
2018-04-05 07:26:26 +02:00
|
|
|
|
*ret = TAKE_PTR(packet);
|
2014-05-25 17:31:17 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-14 01:26:55 +02:00
|
|
|
|
static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
|
|
|
|
|
be32_t address) {
|
2014-05-25 17:31:17 +02:00
|
|
|
|
_cleanup_free_ DHCPPacket *packet = NULL;
|
|
|
|
|
size_t offset;
|
|
|
|
|
be32_t lease_time;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
2014-05-25 18:28:03 +02:00
|
|
|
|
packet->dhcp.yiaddr = address;
|
2014-05-25 17:31:17 +02:00
|
|
|
|
|
2014-05-25 21:47:38 +02:00
|
|
|
|
lease_time = htobe32(req->lifetime);
|
2014-05-25 17:31:17 +02:00
|
|
|
|
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
|
2014-08-14 01:26:55 +02:00
|
|
|
|
&lease_time);
|
2014-05-25 17:31:17 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
2014-08-04 20:57:02 +02:00
|
|
|
|
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
|
2014-08-04 20:57:02 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
2016-05-18 01:34:25 +02:00
|
|
|
|
if (server->emit_router) {
|
|
|
|
|
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
|
|
|
|
|
SD_DHCP_OPTION_ROUTER, 4, &server->address);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
2014-08-04 20:57:02 +02:00
|
|
|
|
|
2014-05-25 17:31:17 +02:00
|
|
|
|
r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-14 01:26:55 +02:00
|
|
|
|
static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
|
|
|
|
|
be32_t address) {
|
2014-05-25 18:28:03 +02:00
|
|
|
|
_cleanup_free_ DHCPPacket *packet = NULL;
|
|
|
|
|
be32_t lease_time;
|
2020-02-28 19:28:49 +01:00
|
|
|
|
sd_dhcp_option *j;
|
|
|
|
|
Iterator i;
|
2019-09-20 04:22:17 +02:00
|
|
|
|
size_t offset;
|
2014-05-25 18:28:03 +02:00
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
packet->dhcp.yiaddr = address;
|
|
|
|
|
|
2014-05-25 21:47:38 +02:00
|
|
|
|
lease_time = htobe32(req->lifetime);
|
2014-05-25 18:28:03 +02:00
|
|
|
|
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
|
2014-08-14 01:26:55 +02:00
|
|
|
|
&lease_time);
|
2014-05-25 18:28:03 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
2014-08-04 20:57:02 +02:00
|
|
|
|
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
|
2014-08-04 20:57:02 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
2016-05-18 01:34:25 +02:00
|
|
|
|
if (server->emit_router) {
|
|
|
|
|
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
|
|
|
|
|
SD_DHCP_OPTION_ROUTER, 4, &server->address);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
2014-08-04 20:57:02 +02:00
|
|
|
|
|
2015-08-27 14:48:37 +02:00
|
|
|
|
if (server->n_dns > 0) {
|
|
|
|
|
r = dhcp_option_append(
|
|
|
|
|
&packet->dhcp, req->max_optlen, &offset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
|
2015-08-27 14:48:37 +02:00
|
|
|
|
sizeof(struct in_addr) * server->n_dns, server->dns);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (server->n_ntp > 0) {
|
|
|
|
|
r = dhcp_option_append(
|
|
|
|
|
&packet->dhcp, req->max_optlen, &offset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_NTP_SERVER,
|
2015-08-27 14:48:37 +02:00
|
|
|
|
sizeof(struct in_addr) * server->n_ntp, server->ntp);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-18 15:22:47 +02:00
|
|
|
|
if (server->n_sip > 0) {
|
|
|
|
|
r = dhcp_option_append(
|
|
|
|
|
&packet->dhcp, req->max_optlen, &offset, 0,
|
|
|
|
|
SD_DHCP_OPTION_SIP_SERVER,
|
|
|
|
|
sizeof(struct in_addr) * server->n_sip, server->sip);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-26 19:19:32 +02:00
|
|
|
|
if (server->timezone) {
|
|
|
|
|
r = dhcp_option_append(
|
|
|
|
|
&packet->dhcp, req->max_optlen, &offset, 0,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
SD_DHCP_OPTION_NEW_TZDB_TIMEZONE,
|
2015-08-26 19:19:32 +02:00
|
|
|
|
strlen(server->timezone), server->timezone);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-28 19:28:49 +01:00
|
|
|
|
ORDERED_HASHMAP_FOREACH(j, server->extra_options, i) {
|
|
|
|
|
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
|
|
|
|
|
j->option, j->length, j->data);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ordered_hashmap_isempty(server->vendor_options)) {
|
2019-09-20 04:22:17 +02:00
|
|
|
|
r = dhcp_option_append(
|
|
|
|
|
&packet->dhcp, req->max_optlen, &offset, 0,
|
|
|
|
|
SD_DHCP_OPTION_VENDOR_SPECIFIC,
|
2020-02-28 19:28:49 +01:00
|
|
|
|
ordered_hashmap_size(server->vendor_options), server->vendor_options);
|
2019-09-20 04:22:17 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-25 18:28:03 +02:00
|
|
|
|
r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-25 20:39:02 +02:00
|
|
|
|
static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
|
|
|
|
|
_cleanup_free_ DHCPPacket *packet = NULL;
|
|
|
|
|
size_t offset;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
2015-08-26 23:31:47 +02:00
|
|
|
|
return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
|
2014-05-25 20:39:02 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-14 01:26:55 +02:00
|
|
|
|
static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
|
|
|
|
|
be32_t gateway, uint8_t chaddr[]) {
|
2014-07-26 19:12:24 +02:00
|
|
|
|
_cleanup_free_ DHCPPacket *packet = NULL;
|
|
|
|
|
size_t optoffset = 0;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(server);
|
|
|
|
|
assert(address != INADDR_ANY);
|
|
|
|
|
assert(chaddr);
|
|
|
|
|
|
|
|
|
|
packet = malloc0(sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE);
|
|
|
|
|
if (!packet)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0,
|
2014-10-08 21:15:45 +02:00
|
|
|
|
DHCP_FORCERENEW, ARPHRD_ETHER,
|
|
|
|
|
DHCP_MIN_OPTIONS_SIZE, &optoffset);
|
2014-07-26 19:12:24 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE,
|
2016-01-20 14:44:24 +01:00
|
|
|
|
&optoffset, 0, SD_DHCP_OPTION_END, 0, NULL);
|
2014-07-26 19:12:24 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN);
|
|
|
|
|
|
2016-02-25 14:36:40 +01:00
|
|
|
|
r = dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT,
|
|
|
|
|
&packet->dhcp,
|
2014-07-26 19:12:24 +02:00
|
|
|
|
sizeof(DHCPMessage) + optoffset);
|
2014-08-03 19:30:38 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2014-07-26 19:12:24 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-26 23:31:47 +02:00
|
|
|
|
static int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) {
|
2015-08-26 22:47:53 +02:00
|
|
|
|
DHCPRequest *req = userdata;
|
2014-05-24 22:14:32 +02:00
|
|
|
|
|
|
|
|
|
assert(req);
|
|
|
|
|
|
|
|
|
|
switch(code) {
|
2016-01-20 14:44:24 +01:00
|
|
|
|
case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
|
2014-05-25 21:47:38 +02:00
|
|
|
|
if (len == 4)
|
2016-05-26 23:48:04 +02:00
|
|
|
|
req->lifetime = unaligned_read_be32(option);
|
2014-05-25 21:47:38 +02:00
|
|
|
|
|
|
|
|
|
break;
|
2016-01-20 14:44:24 +01:00
|
|
|
|
case SD_DHCP_OPTION_REQUESTED_IP_ADDRESS:
|
2014-05-25 18:28:03 +02:00
|
|
|
|
if (len == 4)
|
2016-05-26 23:48:04 +02:00
|
|
|
|
memcpy(&req->requested_ip, option, sizeof(be32_t));
|
2014-05-25 18:28:03 +02:00
|
|
|
|
|
|
|
|
|
break;
|
2016-01-20 14:44:24 +01:00
|
|
|
|
case SD_DHCP_OPTION_SERVER_IDENTIFIER:
|
2014-05-24 22:14:32 +02:00
|
|
|
|
if (len == 4)
|
2016-05-26 23:48:04 +02:00
|
|
|
|
memcpy(&req->server_id, option, sizeof(be32_t));
|
2014-05-24 22:14:32 +02:00
|
|
|
|
|
|
|
|
|
break;
|
2016-01-20 14:44:24 +01:00
|
|
|
|
case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
|
2014-05-24 22:14:32 +02:00
|
|
|
|
if (len >= 2) {
|
|
|
|
|
uint8_t *data;
|
|
|
|
|
|
|
|
|
|
data = memdup(option, len);
|
|
|
|
|
if (!data)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
free(req->client_id.data);
|
|
|
|
|
req->client_id.data = data;
|
|
|
|
|
req->client_id.length = len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
2016-01-20 14:44:24 +01:00
|
|
|
|
case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
|
2016-05-30 18:28:51 +02:00
|
|
|
|
|
|
|
|
|
if (len == 2 && unaligned_read_be16(option) >= sizeof(DHCPPacket))
|
2016-05-26 23:48:04 +02:00
|
|
|
|
req->max_optlen = unaligned_read_be16(option) - sizeof(DHCPPacket);
|
2014-05-24 22:14:32 +02:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void dhcp_request_free(DHCPRequest *req) {
|
|
|
|
|
if (!req)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
free(req->client_id.data);
|
|
|
|
|
free(req);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
|
|
|
|
|
|
2015-08-27 01:47:42 +02:00
|
|
|
|
static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) {
|
2014-05-24 22:14:32 +02:00
|
|
|
|
assert(req);
|
|
|
|
|
assert(message);
|
|
|
|
|
|
|
|
|
|
req->message = message;
|
|
|
|
|
|
2015-02-09 14:18:57 +01:00
|
|
|
|
/* set client id based on MAC address if client did not send an explicit
|
2014-08-14 01:26:55 +02:00
|
|
|
|
one */
|
2014-05-24 22:14:32 +02:00
|
|
|
|
if (!req->client_id.data) {
|
2015-08-26 23:30:27 +02:00
|
|
|
|
void *data;
|
2014-05-24 22:14:32 +02:00
|
|
|
|
|
2015-08-26 23:30:27 +02:00
|
|
|
|
data = malloc0(ETH_ALEN + 1);
|
2014-05-24 22:14:32 +02:00
|
|
|
|
if (!data)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
2015-08-26 23:30:27 +02:00
|
|
|
|
((uint8_t*) data)[0] = 0x01;
|
|
|
|
|
memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN);
|
|
|
|
|
|
2014-05-24 22:14:32 +02:00
|
|
|
|
req->client_id.length = ETH_ALEN + 1;
|
|
|
|
|
req->client_id.data = data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
|
|
|
|
|
req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
|
|
|
|
|
|
2015-08-26 23:30:27 +02:00
|
|
|
|
if (req->lifetime <= 0)
|
2015-08-27 01:47:42 +02:00
|
|
|
|
req->lifetime = MAX(1ULL, server->default_lease_time);
|
|
|
|
|
|
|
|
|
|
if (server->max_lease_time > 0 && req->lifetime > server->max_lease_time)
|
|
|
|
|
req->lifetime = server->max_lease_time;
|
2014-05-25 21:47:38 +02:00
|
|
|
|
|
2014-05-24 22:14:32 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-25 22:07:53 +02:00
|
|
|
|
static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
|
|
|
|
|
assert(server);
|
|
|
|
|
|
|
|
|
|
if (!server->pool_size)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
2015-08-28 20:29:10 +02:00
|
|
|
|
if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) ||
|
|
|
|
|
be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size)))
|
|
|
|
|
return -ERANGE;
|
2014-05-25 22:07:53 +02:00
|
|
|
|
|
2015-08-28 20:29:10 +02:00
|
|
|
|
return be32toh(requested_ip & ~server->netmask) - server->pool_offset;
|
2014-05-25 22:07:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-27 08:54:41 +02:00
|
|
|
|
#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
|
|
|
|
|
|
2014-05-24 21:04:27 +02:00
|
|
|
|
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
|
|
|
|
|
size_t length) {
|
tree-wide: drop redundant _cleanup_ macros (#8810)
This drops a good number of type-specific _cleanup_ macros, and patches
all users to just use the generic ones.
In most recent code we abstained from defining type-specific macros, and
this basically removes all those added already, with the exception of
the really low-level ones.
Having explicit macros for this is not too useful, as the expression
without the extra macro is generally just 2ch wider. We should generally
emphesize generic code, unless there are really good reasons for
specific code, hence let's follow this in this case too.
Note that _cleanup_free_ and similar really low-level, libc'ish, Linux
API'ish macros continue to be defined, only the really high-level OO
ones are dropped. From now on this should really be the rule: for really
low-level stuff, such as memory allocation, fd handling and so one, go
ahead and define explicit per-type macros, but for high-level, specific
program code, just use the generic _cleanup_() macro directly, in order
to keep things simple and as readable as possible for the uninitiated.
Note that before this patch some of the APIs (notable libudev ones) were
already used with the high-level macros at some places and with the
generic _cleanup_ macro at others. With this patch we hence unify on the
latter.
2018-04-25 12:31:45 +02:00
|
|
|
|
_cleanup_(dhcp_request_freep) DHCPRequest *req = NULL;
|
2015-11-25 17:29:30 +01:00
|
|
|
|
_cleanup_free_ char *error_message = NULL;
|
2014-05-25 22:07:53 +02:00
|
|
|
|
DHCPLease *existing_lease;
|
2014-05-24 22:14:32 +02:00
|
|
|
|
int type, r;
|
2014-05-24 21:04:27 +02:00
|
|
|
|
|
|
|
|
|
assert(server);
|
|
|
|
|
assert(message);
|
|
|
|
|
|
|
|
|
|
if (message->op != BOOTREQUEST ||
|
|
|
|
|
message->htype != ARPHRD_ETHER ||
|
|
|
|
|
message->hlen != ETHER_ADDR_LEN)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2014-05-24 22:14:32 +02:00
|
|
|
|
req = new0(DHCPRequest, 1);
|
|
|
|
|
if (!req)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
2015-11-25 17:29:30 +01:00
|
|
|
|
type = dhcp_option_parse(message, length, parse_request, req, &error_message);
|
2014-05-24 21:04:27 +02:00
|
|
|
|
if (type < 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2015-08-27 01:47:42 +02:00
|
|
|
|
r = ensure_sane_request(server, req, message);
|
2014-05-24 22:14:32 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
/* this only fails on critical errors */
|
|
|
|
|
return r;
|
|
|
|
|
|
2014-08-14 01:26:55 +02:00
|
|
|
|
existing_lease = hashmap_get(server->leases_by_client_id,
|
|
|
|
|
&req->client_id);
|
2014-05-25 22:07:53 +02:00
|
|
|
|
|
2014-05-25 17:31:17 +02:00
|
|
|
|
switch(type) {
|
2015-08-26 23:30:27 +02:00
|
|
|
|
|
|
|
|
|
case DHCP_DISCOVER: {
|
2014-05-25 22:07:53 +02:00
|
|
|
|
be32_t address = INADDR_ANY;
|
|
|
|
|
unsigned i;
|
2014-05-25 18:28:03 +02:00
|
|
|
|
|
2014-05-25 17:31:17 +02:00
|
|
|
|
log_dhcp_server(server, "DISCOVER (0x%x)",
|
|
|
|
|
be32toh(req->message->xid));
|
|
|
|
|
|
2014-05-25 18:28:03 +02:00
|
|
|
|
if (!server->pool_size)
|
|
|
|
|
/* no pool allocated */
|
|
|
|
|
return 0;
|
|
|
|
|
|
2014-05-25 22:07:53 +02:00
|
|
|
|
/* for now pick a random free address from the pool */
|
|
|
|
|
if (existing_lease)
|
|
|
|
|
address = existing_lease->address;
|
|
|
|
|
else {
|
2015-10-04 00:22:41 +02:00
|
|
|
|
struct siphash state;
|
2015-10-06 15:04:42 +02:00
|
|
|
|
uint64_t hash;
|
2015-08-28 20:29:10 +02:00
|
|
|
|
uint32_t next_offer;
|
2015-08-27 08:54:41 +02:00
|
|
|
|
|
|
|
|
|
/* even with no persistence of leases, we try to offer the same client
|
|
|
|
|
the same IP address. we do this by using the hash of the client id
|
|
|
|
|
as the offset into the pool of leases when finding the next free one */
|
|
|
|
|
|
2015-10-06 15:04:42 +02:00
|
|
|
|
siphash24_init(&state, HASH_KEY.bytes);
|
2015-10-04 00:22:41 +02:00
|
|
|
|
client_id_hash_func(&req->client_id, &state);
|
2015-11-16 23:17:52 +01:00
|
|
|
|
hash = htole64(siphash24_finalize(&state));
|
2015-10-06 15:04:42 +02:00
|
|
|
|
next_offer = hash % server->pool_size;
|
2015-08-27 08:54:41 +02:00
|
|
|
|
|
2014-05-25 22:07:53 +02:00
|
|
|
|
for (i = 0; i < server->pool_size; i++) {
|
2015-08-27 08:54:41 +02:00
|
|
|
|
if (!server->bound_leases[next_offer]) {
|
2015-08-28 20:29:10 +02:00
|
|
|
|
address = server->subnet | htobe32(server->pool_offset + next_offer);
|
2014-05-25 22:07:53 +02:00
|
|
|
|
break;
|
2018-03-21 20:29:07 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next_offer = (next_offer + 1) % server->pool_size;
|
2014-05-25 22:07:53 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (address == INADDR_ANY)
|
|
|
|
|
/* no free addresses left */
|
|
|
|
|
return 0;
|
2014-05-25 18:28:03 +02:00
|
|
|
|
|
|
|
|
|
r = server_send_offer(server, req, address);
|
2018-03-21 20:28:01 +01:00
|
|
|
|
if (r < 0)
|
2014-05-25 17:31:17 +02:00
|
|
|
|
/* this only fails on critical errors */
|
2018-03-21 20:28:01 +01:00
|
|
|
|
return log_dhcp_server_errno(server, r, "Could not send offer: %m");
|
2014-05-24 21:04:27 +02:00
|
|
|
|
|
2018-03-21 20:28:01 +01:00
|
|
|
|
log_dhcp_server(server, "OFFER (0x%x)", be32toh(req->message->xid));
|
|
|
|
|
return DHCP_OFFER;
|
2014-05-25 17:31:17 +02:00
|
|
|
|
}
|
2014-05-26 15:06:42 +02:00
|
|
|
|
case DHCP_DECLINE:
|
2015-11-25 17:29:30 +01:00
|
|
|
|
log_dhcp_server(server, "DECLINE (0x%x): %s", be32toh(req->message->xid), strna(error_message));
|
2014-05-26 15:06:42 +02:00
|
|
|
|
|
|
|
|
|
/* TODO: make sure we don't offer this address again */
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
2015-08-26 23:30:27 +02:00
|
|
|
|
case DHCP_REQUEST: {
|
2014-05-25 18:28:03 +02:00
|
|
|
|
be32_t address;
|
2014-05-25 20:39:02 +02:00
|
|
|
|
bool init_reboot = false;
|
2014-05-25 22:07:53 +02:00
|
|
|
|
int pool_offset;
|
2014-05-25 18:28:03 +02:00
|
|
|
|
|
|
|
|
|
/* see RFC 2131, section 4.3.2 */
|
|
|
|
|
|
|
|
|
|
if (req->server_id) {
|
|
|
|
|
log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
|
|
|
|
|
be32toh(req->message->xid));
|
|
|
|
|
|
|
|
|
|
/* SELECTING */
|
|
|
|
|
if (req->server_id != server->address)
|
|
|
|
|
/* client did not pick us */
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (req->message->ciaddr)
|
|
|
|
|
/* this MUST be zero */
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (!req->requested_ip)
|
|
|
|
|
/* this must be filled in with the yiaddr
|
|
|
|
|
from the chosen OFFER */
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
address = req->requested_ip;
|
|
|
|
|
} else if (req->requested_ip) {
|
|
|
|
|
log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
|
|
|
|
|
be32toh(req->message->xid));
|
|
|
|
|
|
|
|
|
|
/* INIT-REBOOT */
|
|
|
|
|
if (req->message->ciaddr)
|
|
|
|
|
/* this MUST be zero */
|
|
|
|
|
return 0;
|
|
|
|
|
|
2014-05-25 20:39:02 +02:00
|
|
|
|
/* TODO: check more carefully if IP is correct */
|
2014-05-25 18:28:03 +02:00
|
|
|
|
address = req->requested_ip;
|
2014-05-25 20:39:02 +02:00
|
|
|
|
init_reboot = true;
|
2014-05-25 18:28:03 +02:00
|
|
|
|
} else {
|
|
|
|
|
log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
|
|
|
|
|
be32toh(req->message->xid));
|
|
|
|
|
|
|
|
|
|
/* REBINDING / RENEWING */
|
|
|
|
|
if (!req->message->ciaddr)
|
|
|
|
|
/* this MUST be filled in with clients IP address */
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
address = req->message->ciaddr;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-25 22:07:53 +02:00
|
|
|
|
pool_offset = get_pool_offset(server, address);
|
|
|
|
|
|
|
|
|
|
/* verify that the requested address is from the pool, and either
|
|
|
|
|
owned by the current client or free */
|
|
|
|
|
if (pool_offset >= 0 &&
|
|
|
|
|
server->bound_leases[pool_offset] == existing_lease) {
|
|
|
|
|
DHCPLease *lease;
|
2015-03-27 12:02:49 +01:00
|
|
|
|
usec_t time_now = 0;
|
2014-05-25 22:07:53 +02:00
|
|
|
|
|
|
|
|
|
if (!existing_lease) {
|
|
|
|
|
lease = new0(DHCPLease, 1);
|
2017-02-09 10:16:52 +01:00
|
|
|
|
if (!lease)
|
|
|
|
|
return -ENOMEM;
|
2017-02-09 10:15:21 +01:00
|
|
|
|
lease->address = address;
|
2014-05-25 22:07:53 +02:00
|
|
|
|
lease->client_id.data = memdup(req->client_id.data,
|
|
|
|
|
req->client_id.length);
|
2014-06-13 22:54:22 +02:00
|
|
|
|
if (!lease->client_id.data) {
|
|
|
|
|
free(lease);
|
2014-05-25 22:07:53 +02:00
|
|
|
|
return -ENOMEM;
|
2014-06-13 22:54:22 +02:00
|
|
|
|
}
|
2014-05-25 22:07:53 +02:00
|
|
|
|
lease->client_id.length = req->client_id.length;
|
2014-08-14 01:26:55 +02:00
|
|
|
|
memcpy(&lease->chaddr, &req->message->chaddr,
|
|
|
|
|
ETH_ALEN);
|
2014-07-26 19:12:24 +02:00
|
|
|
|
lease->gateway = req->message->giaddr;
|
2014-05-25 22:07:53 +02:00
|
|
|
|
} else
|
|
|
|
|
lease = existing_lease;
|
|
|
|
|
|
2014-08-14 01:26:55 +02:00
|
|
|
|
r = sd_event_now(server->event,
|
|
|
|
|
clock_boottime_or_monotonic(),
|
|
|
|
|
&time_now);
|
2015-08-04 08:01:03 +02:00
|
|
|
|
if (r < 0) {
|
|
|
|
|
if (!existing_lease)
|
|
|
|
|
dhcp_lease_free(lease);
|
sd-event: make sure sd_event_now() cannot fail
Previously, if the event loop never ran before sd_event_now() would
fail. With this change it will instead fall back to invoking now(). This
way, the function cannot fail anymore, except for programming error when
invoking it with wrong parameters.
This takes into account the fact that many callers did not handle the
error condition correctly, and if the callers did, then they kept simply
invoking now() as fall back on their own. Hence let's shorten the code
using this call, and make things more robust, and let's just fall back
to now() internally.
Whether now() is used or the cache timestamp may still be detected via
the return value of sd_event_now(). If > 0 is returned, then the fall
back to now() was used, if == 0 is returned, then the cached value was
returned.
This patch also simplifies many of the invocations of sd_event_now():
the manual fall back to now() can be removed. Also, in cases where the
call is invoked withing void functions we can now protect the invocation
via assert_se(), acknowledging the fact that the call cannot fail
anymore except for programming errors with the parameters.
This change is inspired by #841.
2015-08-03 17:29:09 +02:00
|
|
|
|
return r;
|
2015-08-04 08:01:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-05-25 22:07:53 +02:00
|
|
|
|
lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
|
|
|
|
|
|
2014-05-25 18:28:03 +02:00
|
|
|
|
r = server_send_ack(server, req, address);
|
|
|
|
|
if (r < 0) {
|
|
|
|
|
/* this only fails on critical errors */
|
2018-03-21 20:28:01 +01:00
|
|
|
|
log_dhcp_server_errno(server, r, "Could not send ack: %m");
|
2014-05-25 22:07:53 +02:00
|
|
|
|
|
|
|
|
|
if (!existing_lease)
|
|
|
|
|
dhcp_lease_free(lease);
|
|
|
|
|
|
2014-05-25 18:28:03 +02:00
|
|
|
|
return r;
|
|
|
|
|
} else {
|
|
|
|
|
log_dhcp_server(server, "ACK (0x%x)",
|
|
|
|
|
be32toh(req->message->xid));
|
2014-05-25 22:07:53 +02:00
|
|
|
|
|
|
|
|
|
server->bound_leases[pool_offset] = lease;
|
2014-08-14 01:26:55 +02:00
|
|
|
|
hashmap_put(server->leases_by_client_id,
|
|
|
|
|
&lease->client_id, lease);
|
2014-05-25 22:07:53 +02:00
|
|
|
|
|
2014-05-25 18:28:03 +02:00
|
|
|
|
return DHCP_ACK;
|
|
|
|
|
}
|
2018-03-21 20:28:01 +01:00
|
|
|
|
|
2014-05-25 20:39:02 +02:00
|
|
|
|
} else if (init_reboot) {
|
|
|
|
|
r = server_send_nak(server, req);
|
2018-03-21 20:28:01 +01:00
|
|
|
|
if (r < 0)
|
2014-05-25 20:39:02 +02:00
|
|
|
|
/* this only fails on critical errors */
|
2018-03-21 20:28:01 +01:00
|
|
|
|
return log_dhcp_server_errno(server, r, "Could not send nak: %m");
|
|
|
|
|
|
|
|
|
|
log_dhcp_server(server, "NAK (0x%x)", be32toh(req->message->xid));
|
|
|
|
|
return DHCP_NAK;
|
2014-05-25 20:39:02 +02:00
|
|
|
|
}
|
2014-05-25 18:28:03 +02:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-08-26 23:30:27 +02:00
|
|
|
|
|
2014-05-26 15:18:47 +02:00
|
|
|
|
case DHCP_RELEASE: {
|
|
|
|
|
int pool_offset;
|
|
|
|
|
|
|
|
|
|
log_dhcp_server(server, "RELEASE (0x%x)",
|
|
|
|
|
be32toh(req->message->xid));
|
|
|
|
|
|
|
|
|
|
if (!existing_lease)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (existing_lease->address != req->message->ciaddr)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
pool_offset = get_pool_offset(server, req->message->ciaddr);
|
|
|
|
|
if (pool_offset < 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (server->bound_leases[pool_offset] == existing_lease) {
|
|
|
|
|
server->bound_leases[pool_offset] = NULL;
|
|
|
|
|
hashmap_remove(server->leases_by_client_id, existing_lease);
|
|
|
|
|
dhcp_lease_free(existing_lease);
|
2018-03-21 20:29:43 +01:00
|
|
|
|
}
|
2014-05-26 15:18:47 +02:00
|
|
|
|
|
2018-03-21 20:29:43 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}}
|
2014-05-25 17:31:17 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
2014-05-24 21:04:27 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-05-24 19:27:20 +02:00
|
|
|
|
static int server_receive_message(sd_event_source *s, int fd,
|
|
|
|
|
uint32_t revents, void *userdata) {
|
2014-05-24 21:04:27 +02:00
|
|
|
|
_cleanup_free_ DHCPMessage *message = NULL;
|
2014-05-24 19:38:17 +02:00
|
|
|
|
uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
|
2014-05-24 19:27:20 +02:00
|
|
|
|
sd_dhcp_server *server = userdata;
|
|
|
|
|
struct iovec iov = {};
|
|
|
|
|
struct msghdr msg = {
|
|
|
|
|
.msg_iov = &iov,
|
|
|
|
|
.msg_iovlen = 1,
|
2014-05-24 19:38:17 +02:00
|
|
|
|
.msg_control = cmsgbuf,
|
|
|
|
|
.msg_controllen = sizeof(cmsgbuf),
|
2014-05-24 19:27:20 +02:00
|
|
|
|
};
|
2014-05-24 19:38:17 +02:00
|
|
|
|
struct cmsghdr *cmsg;
|
2016-02-15 22:50:01 +01:00
|
|
|
|
ssize_t buflen, len;
|
2018-03-21 20:30:56 +01:00
|
|
|
|
int r;
|
2014-05-24 19:27:20 +02:00
|
|
|
|
|
|
|
|
|
assert(server);
|
|
|
|
|
|
2016-02-15 22:50:01 +01:00
|
|
|
|
buflen = next_datagram_size_fd(fd);
|
|
|
|
|
if (buflen < 0)
|
|
|
|
|
return buflen;
|
2014-05-24 19:27:20 +02:00
|
|
|
|
|
2015-11-24 18:25:52 +01:00
|
|
|
|
message = malloc(buflen);
|
2014-05-24 19:27:20 +02:00
|
|
|
|
if (!message)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
2018-11-27 10:34:32 +01:00
|
|
|
|
iov = IOVEC_MAKE(message, buflen);
|
2014-05-24 19:27:20 +02:00
|
|
|
|
|
2015-02-12 11:44:48 +01:00
|
|
|
|
len = recvmsg(fd, &msg, 0);
|
2015-11-24 18:25:52 +01:00
|
|
|
|
if (len < 0) {
|
2017-10-04 16:01:32 +02:00
|
|
|
|
if (IN_SET(errno, EAGAIN, EINTR))
|
2015-11-24 18:25:52 +01:00
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return -errno;
|
2018-03-21 20:29:07 +01:00
|
|
|
|
}
|
|
|
|
|
if ((size_t)len < sizeof(DHCPMessage))
|
2014-05-24 21:04:27 +02:00
|
|
|
|
return 0;
|
2014-05-24 19:27:20 +02:00
|
|
|
|
|
2015-06-10 19:10:47 +02:00
|
|
|
|
CMSG_FOREACH(cmsg, &msg) {
|
2014-05-24 19:38:17 +02:00
|
|
|
|
if (cmsg->cmsg_level == IPPROTO_IP &&
|
|
|
|
|
cmsg->cmsg_type == IP_PKTINFO &&
|
|
|
|
|
cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
|
|
|
|
|
struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
|
|
|
|
|
|
2014-08-14 01:26:55 +02:00
|
|
|
|
/* TODO figure out if this can be done as a filter on
|
|
|
|
|
* the socket, like for IPv6 */
|
2015-08-26 23:31:47 +02:00
|
|
|
|
if (server->ifindex != info->ipi_ifindex)
|
2014-05-24 19:38:17 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-21 20:30:56 +01:00
|
|
|
|
r = dhcp_server_handle_message(server, message, (size_t) len);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
log_dhcp_server_errno(server, r, "Couldn't process incoming message: %m");
|
|
|
|
|
|
|
|
|
|
return 0;
|
2014-05-24 19:27:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sd_dhcp_server_start(sd_dhcp_server *server) {
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert_return(server, -EINVAL);
|
|
|
|
|
assert_return(server->event, -EINVAL);
|
|
|
|
|
assert_return(!server->receive_message, -EBUSY);
|
2018-03-21 20:30:29 +01:00
|
|
|
|
assert_return(server->fd_raw < 0, -EBUSY);
|
|
|
|
|
assert_return(server->fd < 0, -EBUSY);
|
2014-05-25 00:29:13 +02:00
|
|
|
|
assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
|
2014-05-24 19:27:20 +02:00
|
|
|
|
|
2018-11-11 18:03:22 +01:00
|
|
|
|
r = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
|
2014-05-24 23:03:49 +02:00
|
|
|
|
if (r < 0) {
|
|
|
|
|
r = -errno;
|
|
|
|
|
sd_dhcp_server_stop(server);
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
server->fd_raw = r;
|
|
|
|
|
|
2019-09-23 13:25:21 +02:00
|
|
|
|
r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY, DHCP_PORT_SERVER, -1);
|
2014-05-24 19:27:20 +02:00
|
|
|
|
if (r < 0) {
|
|
|
|
|
sd_dhcp_server_stop(server);
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
server->fd = r;
|
|
|
|
|
|
|
|
|
|
r = sd_event_add_io(server->event, &server->receive_message,
|
|
|
|
|
server->fd, EPOLLIN,
|
|
|
|
|
server_receive_message, server);
|
|
|
|
|
if (r < 0) {
|
|
|
|
|
sd_dhcp_server_stop(server);
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r = sd_event_source_set_priority(server->receive_message,
|
|
|
|
|
server->event_priority);
|
|
|
|
|
if (r < 0) {
|
|
|
|
|
sd_dhcp_server_stop(server);
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_dhcp_server(server, "STARTED");
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-07-26 19:12:24 +02:00
|
|
|
|
|
|
|
|
|
int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
|
|
|
|
|
unsigned i;
|
2014-08-03 19:45:20 +02:00
|
|
|
|
int r = 0;
|
2014-07-26 19:12:24 +02:00
|
|
|
|
|
|
|
|
|
assert_return(server, -EINVAL);
|
|
|
|
|
assert(server->bound_leases);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < server->pool_size; i++) {
|
|
|
|
|
DHCPLease *lease = server->bound_leases[i];
|
|
|
|
|
|
2015-08-28 20:29:10 +02:00
|
|
|
|
if (!lease || lease == &server->invalid_lease)
|
2014-07-26 19:12:24 +02:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
r = server_send_forcerenew(server, lease->address,
|
|
|
|
|
lease->gateway,
|
|
|
|
|
lease->chaddr);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2018-03-21 20:29:07 +01:00
|
|
|
|
|
|
|
|
|
log_dhcp_server(server, "FORCERENEW");
|
2014-07-26 19:12:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return r;
|
|
|
|
|
}
|
2015-08-26 19:19:32 +02:00
|
|
|
|
|
2015-08-30 03:18:33 +02:00
|
|
|
|
int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) {
|
2015-08-26 19:19:32 +02:00
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert_return(server, -EINVAL);
|
2018-05-12 21:20:13 +02:00
|
|
|
|
assert_return(timezone_is_valid(tz, LOG_DEBUG), -EINVAL);
|
2015-08-26 19:19:32 +02:00
|
|
|
|
|
2015-08-30 03:18:33 +02:00
|
|
|
|
if (streq_ptr(tz, server->timezone))
|
2015-08-26 19:19:32 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
2015-08-30 03:18:33 +02:00
|
|
|
|
r = free_and_strdup(&server->timezone, tz);
|
2015-08-26 19:19:32 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2015-08-27 01:47:42 +02:00
|
|
|
|
|
|
|
|
|
int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t) {
|
|
|
|
|
assert_return(server, -EINVAL);
|
|
|
|
|
|
|
|
|
|
if (t == server->max_lease_time)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
server->max_lease_time = t;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t) {
|
|
|
|
|
assert_return(server, -EINVAL);
|
|
|
|
|
|
|
|
|
|
if (t == server->default_lease_time)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
server->default_lease_time = t;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2015-08-27 14:48:37 +02:00
|
|
|
|
|
|
|
|
|
int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n) {
|
|
|
|
|
assert_return(server, -EINVAL);
|
|
|
|
|
assert_return(dns || n <= 0, -EINVAL);
|
|
|
|
|
|
|
|
|
|
if (server->n_dns == n &&
|
|
|
|
|
memcmp(server->dns, dns, sizeof(struct in_addr) * n) == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (n <= 0) {
|
|
|
|
|
server->dns = mfree(server->dns);
|
|
|
|
|
server->n_dns = 0;
|
|
|
|
|
} else {
|
|
|
|
|
struct in_addr *c;
|
|
|
|
|
|
|
|
|
|
c = newdup(struct in_addr, dns, n);
|
|
|
|
|
if (!c)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
free(server->dns);
|
|
|
|
|
server->dns = c;
|
|
|
|
|
server->n_dns = n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n) {
|
|
|
|
|
assert_return(server, -EINVAL);
|
|
|
|
|
assert_return(ntp || n <= 0, -EINVAL);
|
|
|
|
|
|
|
|
|
|
if (server->n_ntp == n &&
|
|
|
|
|
memcmp(server->ntp, ntp, sizeof(struct in_addr) * n) == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (n <= 0) {
|
|
|
|
|
server->ntp = mfree(server->ntp);
|
|
|
|
|
server->n_ntp = 0;
|
|
|
|
|
} else {
|
|
|
|
|
struct in_addr *c;
|
|
|
|
|
|
|
|
|
|
c = newdup(struct in_addr, ntp, n);
|
|
|
|
|
if (!c)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
free(server->ntp);
|
|
|
|
|
server->ntp = c;
|
|
|
|
|
server->n_ntp = n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2016-05-18 01:34:25 +02:00
|
|
|
|
|
2019-09-18 15:22:47 +02:00
|
|
|
|
int sd_dhcp_server_set_sip(sd_dhcp_server *server, const struct in_addr sip[], unsigned n) {
|
|
|
|
|
assert_return(server, -EINVAL);
|
|
|
|
|
assert_return(sip || n <= 0, -EINVAL);
|
|
|
|
|
|
|
|
|
|
if (server->n_sip == n &&
|
|
|
|
|
memcmp(server->sip, sip, sizeof(struct in_addr) * n) == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (n <= 0) {
|
|
|
|
|
server->sip = mfree(server->sip);
|
|
|
|
|
server->n_sip = 0;
|
|
|
|
|
} else {
|
|
|
|
|
struct in_addr *c;
|
|
|
|
|
|
|
|
|
|
c = newdup(struct in_addr, sip, n);
|
|
|
|
|
if (!c)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
free(server->sip);
|
|
|
|
|
server->sip = c;
|
|
|
|
|
server->n_sip = n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-18 01:34:25 +02:00
|
|
|
|
int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled) {
|
|
|
|
|
assert_return(server, -EINVAL);
|
|
|
|
|
|
|
|
|
|
if (enabled == server->emit_router)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
server->emit_router = enabled;
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2019-09-20 04:22:17 +02:00
|
|
|
|
|
2019-11-18 10:14:18 +01:00
|
|
|
|
int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v) {
|
2019-09-20 04:22:17 +02:00
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert_return(server, -EINVAL);
|
|
|
|
|
assert_return(v, -EINVAL);
|
|
|
|
|
|
2020-02-28 19:28:49 +01:00
|
|
|
|
r = ordered_hashmap_ensure_allocated(&server->extra_options, &dhcp_option_hash_ops);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
r = ordered_hashmap_put(server->extra_options, UINT_TO_PTR(v->option), v);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
sd_dhcp_option_ref(v);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sd_dhcp_server_add_vendor_option(sd_dhcp_server *server, sd_dhcp_option *v) {
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert_return(server, -EINVAL);
|
|
|
|
|
assert_return(v, -EINVAL);
|
|
|
|
|
|
|
|
|
|
r = ordered_hashmap_ensure_allocated(&server->vendor_options, &dhcp_option_hash_ops);
|
2019-09-20 04:22:17 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
2020-02-28 19:28:49 +01:00
|
|
|
|
r = ordered_hashmap_put(server->vendor_options, v, v);
|
2019-09-20 04:22:17 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
2019-11-18 10:14:18 +01:00
|
|
|
|
sd_dhcp_option_ref(v);
|
2019-09-20 04:22:17 +02:00
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|