]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-server.c
network: use request queue to configure DHCP server
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-server.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
b44cd882 2/***
810adae9 3 Copyright © 2013 Intel Corporation. All rights reserved.
b44cd882
TG
4***/
5
ddb82ec2 6#include <net/if_arp.h>
ff734080
TG
7#include <sys/ioctl.h>
8
b44cd882 9#include "sd-dhcp-server.h"
6e86b24d 10#include "sd-id128.h"
07630cea 11
b5efdb8a 12#include "alloc-util.h"
ff734080 13#include "dhcp-internal.h"
07630cea 14#include "dhcp-server-internal.h"
3ffd4af2 15#include "fd-util.h"
07630cea 16#include "in-addr-util.h"
cb310866 17#include "io-util.h"
61a9fa8f 18#include "network-common.h"
ebffea2a 19#include "ordered-set.h"
07630cea
LP
20#include "siphash24.h"
21#include "string-util.h"
9ae84244 22#include "unaligned.h"
b44cd882 23
586ac6f7
LP
24#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
25#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
4dc35568 26
6121fc30 27static DHCPLease *dhcp_lease_free(DHCPLease *lease) {
45a9eac9 28 if (!lease)
6121fc30 29 return NULL;
45a9eac9
RM
30
31 free(lease->client_id.data);
6121fc30 32 return mfree(lease);
45a9eac9
RM
33}
34
65a0ef23 35DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPLease*, dhcp_lease_free);
36
99634696
TG
37/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
38 * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
39 * moreover, the server's own address may be in the pool, and is in that case reserved in order not to
40 * accidentally hand it out */
90810f7a
LP
41int sd_dhcp_server_configure_pool(
42 sd_dhcp_server *server,
43 const struct in_addr *address,
44 unsigned char prefixlen,
45 uint32_t offset,
46 uint32_t size) {
47
99634696
TG
48 struct in_addr netmask_addr;
49 be32_t netmask;
50 uint32_t server_off, broadcast_off, size_max;
51
2dead812
TG
52 assert_return(server, -EINVAL);
53 assert_return(address, -EINVAL);
99634696
TG
54 assert_return(address->s_addr != INADDR_ANY, -EINVAL);
55 assert_return(prefixlen <= 32, -ERANGE);
99634696 56
5a941f5f 57 assert_se(in4_addr_prefixlen_to_netmask(&netmask_addr, prefixlen));
99634696
TG
58 netmask = netmask_addr.s_addr;
59
60 server_off = be32toh(address->s_addr & ~netmask);
61 broadcast_off = be32toh(~netmask);
62
63 /* the server address cannot be the subnet address */
64 assert_return(server_off != 0, -ERANGE);
65
66 /* nor the broadcast address */
67 assert_return(server_off != broadcast_off, -ERANGE);
68
69 /* 0 offset means we should set a default, we skip the first (subnet) address
70 and take the next one */
71 if (offset == 0)
72 offset = 1;
73
74 size_max = (broadcast_off + 1) /* the number of addresses in the subnet */
75 - offset /* exclude the addresses before the offset */
76 - 1; /* exclude the last (broadcast) address */
77
78 /* The pool must contain at least one address */
79 assert_return(size_max >= 1, -ERANGE);
80
81 if (size != 0)
82 assert_return(size <= size_max, -ERANGE);
83 else
84 size = size_max;
87322b3a 85
45a9eac9 86 if (server->address != address->s_addr || server->netmask != netmask || server->pool_size != size || server->pool_offset != offset) {
45a9eac9
RM
87
88 free(server->bound_leases);
89 server->bound_leases = new0(DHCPLease*, size);
90 if (!server->bound_leases)
91 return -ENOMEM;
2dead812 92
45a9eac9
RM
93 server->pool_offset = offset;
94 server->pool_size = size;
2dead812 95
45a9eac9
RM
96 server->address = address->s_addr;
97 server->netmask = netmask;
98 server->subnet = address->s_addr & netmask;
99634696 99
45a9eac9
RM
100 if (server_off >= offset && server_off - offset < size)
101 server->bound_leases[server_off - offset] = &server->invalid_lease;
102
103 /* Drop any leases associated with the old address range */
6121fc30 104 hashmap_clear(server->leases_by_client_id);
36d35f22
MAL
105
106 if (server->callback)
107 server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
45a9eac9 108 }
20af7091
TG
109
110 return 0;
111}
112
04c01369 113int sd_dhcp_server_is_running(sd_dhcp_server *server) {
75677581 114 assert_return(server, false);
7c16313f
TG
115
116 return !!server->receive_message;
117}
118
c95df587
YA
119int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server) {
120 assert_return(server, -EINVAL);
121
122 return in4_addr_is_set(&server->relay_target);
123}
124
7a08d314 125void client_id_hash_func(const DHCPClientId *id, struct siphash *state) {
87322b3a
TG
126 assert(id);
127 assert(id->length);
128 assert(id->data);
129
1e2527a6 130 siphash24_compress(&id->length, sizeof(id->length), state);
b826ab58 131 siphash24_compress(id->data, id->length, state);
87322b3a
TG
132}
133
7a08d314 134int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b) {
a0edd02e 135 int r;
87322b3a 136
87322b3a
TG
137 assert(!a->length || a->data);
138 assert(!b->length || b->data);
139
a0edd02e
FB
140 r = CMP(a->length, b->length);
141 if (r != 0)
142 return r;
87322b3a
TG
143
144 return memcmp(a->data, b->data, a->length);
145}
146
6121fc30
YW
147DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(dhcp_lease_hash_ops, DHCPClientId, client_id_hash_func, client_id_compare_func,
148 DHCPLease, dhcp_lease_free);
d5099efc 149
8301aa0b 150static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
8301aa0b 151 assert(server);
ff734080 152
3bdace9b 153 log_dhcp_server(server, "UNREF");
87322b3a 154
3bdace9b 155 sd_dhcp_server_stop(server);
87322b3a 156
3bdace9b
LP
157 sd_event_unref(server->event);
158
8eb9058d 159 free(server->timezone);
ddb82ec2 160
2324fd3a 161 for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
ddb82ec2 162 free(server->servers[i].addr);
8eb9058d 163
3bdace9b 164 hashmap_free(server->leases_by_client_id);
65a0ef23 165 hashmap_free(server->static_leases_by_client_id);
3bdace9b 166
ebffea2a
SS
167 ordered_set_free(server->extra_options);
168 ordered_set_free(server->vendor_options);
564ca984 169
11c38d3e
YA
170 free(server->agent_circuit_id);
171 free(server->agent_remote_id);
172
3bdace9b 173 free(server->bound_leases);
61a9fa8f
YW
174
175 free(server->ifname);
6b430fdb 176 return mfree(server);
b44cd882
TG
177}
178
8301aa0b
YW
179DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_server, sd_dhcp_server, dhcp_server_free);
180
3a864fe4 181int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
4afd3348 182 _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
b44cd882
TG
183
184 assert_return(ret, -EINVAL);
3a864fe4 185 assert_return(ifindex > 0, -EINVAL);
b44cd882 186
61a9fa8f 187 server = new(sd_dhcp_server, 1);
b44cd882
TG
188 if (!server)
189 return -ENOMEM;
190
61a9fa8f
YW
191 *server = (sd_dhcp_server) {
192 .n_ref = 1,
193 .fd_raw = -1,
194 .fd = -1,
21b6b87e 195 .fd_broadcast = -1,
61a9fa8f
YW
196 .address = htobe32(INADDR_ANY),
197 .netmask = htobe32(INADDR_ANY),
198 .ifindex = ifindex,
21b6b87e 199 .bind_to_interface = true,
61a9fa8f
YW
200 .default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC),
201 .max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC),
202 };
357e1b17 203
6121fc30 204 server->leases_by_client_id = hashmap_new(&dhcp_lease_hash_ops);
357e1b17
LP
205 if (!server->leases_by_client_id)
206 return -ENOMEM;
65a0ef23 207 server->static_leases_by_client_id = hashmap_new(&dhcp_lease_hash_ops);
208 if (!server->static_leases_by_client_id)
209 return -ENOMEM;
357e1b17 210
1cc6c93a 211 *ret = TAKE_PTR(server);
b44cd882
TG
212
213 return 0;
214}
215
61a9fa8f
YW
216int sd_dhcp_server_set_ifname(sd_dhcp_server *server, const char *ifname) {
217 assert_return(server, -EINVAL);
218 assert_return(ifname, -EINVAL);
219
220 if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
221 return -EINVAL;
222
223 return free_and_strdup(&server->ifname, ifname);
224}
225
226const char *sd_dhcp_server_get_ifname(sd_dhcp_server *server) {
227 if (!server)
228 return NULL;
229
230 return get_ifname(server->ifindex, &server->ifname);
231}
232
32d20645 233int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int64_t priority) {
b44cd882
TG
234 int r;
235
236 assert_return(server, -EINVAL);
237 assert_return(!server->event, -EBUSY);
238
239 if (event)
240 server->event = sd_event_ref(event);
241 else {
242 r = sd_event_default(&server->event);
243 if (r < 0)
244 return r;
245 }
246
247 server->event_priority = priority;
248
249 return 0;
250}
251
252int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
253 assert_return(server, -EINVAL);
254
255 server->event = sd_event_unref(server->event);
256
257 return 0;
258}
259
260sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
261 assert_return(server, NULL);
262
263 return server->event;
264}
ff734080
TG
265
266int sd_dhcp_server_stop(sd_dhcp_server *server) {
c8bae363
YW
267 if (!server)
268 return 0;
ff734080 269
21b6b87e
YA
270 server->receive_message = sd_event_source_unref(server->receive_message);
271 server->receive_broadcast = sd_event_source_unref(server->receive_broadcast);
ff734080 272
8de4a226 273 server->fd_raw = safe_close(server->fd_raw);
ff734080 274 server->fd = safe_close(server->fd);
21b6b87e 275 server->fd_broadcast = safe_close(server->fd_broadcast);
ff734080
TG
276
277 log_dhcp_server(server, "STOPPED");
278
279 return 0;
280}
281
a6f1e036
TG
282static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
283 DHCPPacket *packet, size_t len) {
969b009d
TG
284 union sockaddr_union link = {
285 .ll.sll_family = AF_PACKET,
8e38570e 286 .ll.sll_protocol = htobe16(ETH_P_IP),
b3ec603c 287 .ll.sll_ifindex = server->ifindex,
969b009d
TG
288 .ll.sll_halen = ETH_ALEN,
289 };
969b009d
TG
290
291 assert(server);
b3ec603c 292 assert(server->ifindex > 0);
969b009d
TG
293 assert(server->address);
294 assert(packet);
295 assert(len > sizeof(DHCPPacket));
296
297 memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
298
299 dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
a6f1e036 300 packet->dhcp.yiaddr,
afe42aef 301 DHCP_PORT_CLIENT, len, -1);
969b009d 302
b3ec603c 303 return dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
969b009d
TG
304}
305
306static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
8eb7b6a5 307 uint16_t destination_port,
969b009d
TG
308 DHCPMessage *message, size_t len) {
309 union sockaddr_union dest = {
310 .in.sin_family = AF_INET,
8eb7b6a5 311 .in.sin_port = htobe16(destination_port),
969b009d
TG
312 .in.sin_addr.s_addr = destination,
313 };
314 struct iovec iov = {
315 .iov_base = message,
316 .iov_len = len,
317 };
fb29cdbe 318 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct in_pktinfo))) control = {};
969b009d
TG
319 struct msghdr msg = {
320 .msg_name = &dest,
321 .msg_namelen = sizeof(dest.in),
322 .msg_iov = &iov,
323 .msg_iovlen = 1,
969b009d
TG
324 };
325 struct cmsghdr *cmsg;
326 struct in_pktinfo *pktinfo;
969b009d
TG
327
328 assert(server);
4c9cb12c 329 assert(server->fd >= 0);
969b009d
TG
330 assert(message);
331 assert(len > sizeof(DHCPMessage));
332
21b6b87e
YA
333 if (server->bind_to_interface) {
334 msg.msg_control = &control;
335 msg.msg_controllen = sizeof(control);
969b009d 336
21b6b87e
YA
337 cmsg = CMSG_FIRSTHDR(&msg);
338 assert(cmsg);
969b009d 339
21b6b87e
YA
340 cmsg->cmsg_level = IPPROTO_IP;
341 cmsg->cmsg_type = IP_PKTINFO;
342 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
969b009d 343
21b6b87e
YA
344 /* we attach source interface and address info to the message
345 rather than binding the socket. This will be mostly useful
346 when we gain support for arbitrary number of server addresses
347 */
348 pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
349 assert(pktinfo);
350
351 pktinfo->ipi_ifindex = server->ifindex;
352 pktinfo->ipi_spec_dst.s_addr = server->address;
353 }
969b009d 354
0f01c1f9 355 if (sendmsg(server->fd, &msg, 0) < 0)
969b009d
TG
356 return -errno;
357
358 return 0;
359}
360
c95df587
YA
361static bool requested_broadcast(DHCPMessage *message) {
362 assert(message);
363 return message->flags & htobe16(0x8000);
364}
969b009d 365
c95df587
YA
366static int dhcp_server_send(sd_dhcp_server *server, be32_t destination, uint16_t destination_port,
367 DHCPPacket *packet, size_t optoffset, bool l2_broadcast) {
368 if (destination != INADDR_ANY)
369 return dhcp_server_send_udp(server, destination,
370 destination_port, &packet->dhcp,
371 sizeof(DHCPMessage) + optoffset);
372 else if (l2_broadcast)
373 return dhcp_server_send_udp(server, INADDR_BROADCAST,
374 destination_port, &packet->dhcp,
375 sizeof(DHCPMessage) + optoffset);
376 else
377 /* we cannot send UDP packet to specific MAC address when the
378 address is not yet configured, so must fall back to raw
379 packets */
380 return dhcp_server_send_unicast_raw(server, packet,
381 sizeof(DHCPPacket) + optoffset);
969b009d
TG
382}
383
384int dhcp_server_send_packet(sd_dhcp_server *server,
385 DHCPRequest *req, DHCPPacket *packet,
386 int type, size_t optoffset) {
387 be32_t destination = INADDR_ANY;
8eb7b6a5 388 uint16_t destination_port = DHCP_PORT_CLIENT;
969b009d
TG
389 int r;
390
391 assert(server);
392 assert(req);
393 assert(req->max_optlen);
11c38d3e 394 assert(req->message);
969b009d
TG
395 assert(optoffset <= req->max_optlen);
396 assert(packet);
397
398 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
22805d92 399 SD_DHCP_OPTION_SERVER_IDENTIFIER,
969b009d
TG
400 4, &server->address);
401 if (r < 0)
402 return r;
403
11c38d3e
YA
404 if (req->agent_info_option) {
405 size_t opt_full_length = *(req->agent_info_option + 1) + 2;
406 /* there must be space left for SD_DHCP_OPTION_END */
407 if (optoffset + opt_full_length < req->max_optlen) {
408 memcpy(packet->dhcp.options + optoffset, req->agent_info_option, opt_full_length);
409 optoffset += opt_full_length;
410 }
411 }
412
969b009d 413 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
22805d92 414 SD_DHCP_OPTION_END, 0, NULL);
969b009d
TG
415 if (r < 0)
416 return r;
417
418 /* RFC 2131 Section 4.1
419
420 If the ’giaddr’ field in a DHCP message from a client is non-zero,
421 the server sends any return messages to the ’DHCP server’ port on the
422 BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
423 field is zero and the ’ciaddr’ field is nonzero, then the server
424 unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
425 If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
426 set, then the server broadcasts DHCPOFFER and DHCPACK messages to
427 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
428 ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
429 messages to the client’s hardware address and ’yiaddr’ address. In
430 all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
431 messages to 0xffffffff.
432
433 Section 4.3.2
434
435 If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
436 different subnet. The server MUST set the broadcast bit in the
437 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
438 client, because the client may not have a correct network address
439 or subnet mask, and the client may not be answering ARP requests.
440 */
441 if (req->message->giaddr) {
442 destination = req->message->giaddr;
8eb7b6a5 443 destination_port = DHCP_PORT_SERVER;
969b009d
TG
444 if (type == DHCP_NAK)
445 packet->dhcp.flags = htobe16(0x8000);
446 } else if (req->message->ciaddr && type != DHCP_NAK)
447 destination = req->message->ciaddr;
448
c95df587
YA
449 bool l2_broadcast = requested_broadcast(req->message) || type == DHCP_NAK;
450 return dhcp_server_send(server, destination, destination_port, packet, optoffset, l2_broadcast);
969b009d
TG
451}
452
4dc35568 453static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
a6f1e036
TG
454 uint8_t type, size_t *_optoffset,
455 DHCPRequest *req) {
4dc35568 456 _cleanup_free_ DHCPPacket *packet = NULL;
1231c4d2 457 size_t optoffset = 0;
4dc35568
TG
458 int r;
459
460 assert(server);
461 assert(ret);
462 assert(_optoffset);
bd57b450 463 assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
4dc35568
TG
464
465 packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
466 if (!packet)
467 return -ENOMEM;
468
a6f1e036 469 r = dhcp_message_init(&packet->dhcp, BOOTREPLY,
76253e73
DW
470 be32toh(req->message->xid), type, ARPHRD_ETHER,
471 req->max_optlen, &optoffset);
4dc35568
TG
472 if (r < 0)
473 return r;
474
475 packet->dhcp.flags = req->message->flags;
476 packet->dhcp.giaddr = req->message->giaddr;
477 memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
478
479 *_optoffset = optoffset;
1cc6c93a 480 *ret = TAKE_PTR(packet);
4dc35568
TG
481
482 return 0;
483}
484
986c0edf 485static int server_send_offer_or_ack(
ddb82ec2
LP
486 sd_dhcp_server *server,
487 DHCPRequest *req,
986c0edf
YW
488 be32_t address,
489 uint8_t type) {
ddb82ec2 490
2dead812 491 _cleanup_free_ DHCPPacket *packet = NULL;
7354900d 492 sd_dhcp_option *j;
986c0edf 493 be32_t lease_time;
564ca984 494 size_t offset;
2dead812
TG
495 int r;
496
986c0edf
YW
497 assert(server);
498 assert(req);
499 assert(IN_SET(type, DHCP_OFFER, DHCP_ACK));
500
501 r = server_message_init(server, &packet, type, &offset, req);
2dead812
TG
502 if (r < 0)
503 return r;
504
505 packet->dhcp.yiaddr = address;
506
c7d9ffe6 507 lease_time = htobe32(req->lifetime);
2dead812 508 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d92 509 SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
a6f1e036 510 &lease_time);
2dead812
TG
511 if (r < 0)
512 return r;
513
59b8f6b6 514 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d92 515 SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
59b8f6b6
TG
516 if (r < 0)
517 return r;
518
77ff6022
CG
519 if (server->emit_router) {
520 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
521 SD_DHCP_OPTION_ROUTER, 4, &server->address);
522 if (r < 0)
523 return r;
524 }
59b8f6b6 525
986c0edf
YW
526 if (type == DHCP_ACK) {
527 static const uint8_t option_map[_SD_DHCP_LEASE_SERVER_TYPE_MAX] = {
528 [SD_DHCP_LEASE_DNS] = SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
529 [SD_DHCP_LEASE_NTP] = SD_DHCP_OPTION_NTP_SERVER,
530 [SD_DHCP_LEASE_SIP] = SD_DHCP_OPTION_SIP_SERVER,
531 [SD_DHCP_LEASE_POP3] = SD_DHCP_OPTION_POP3_SERVER,
532 [SD_DHCP_LEASE_SMTP] = SD_DHCP_OPTION_SMTP_SERVER,
533 [SD_DHCP_LEASE_LPR] = SD_DHCP_OPTION_LPR_SERVER,
534 };
535
536 for (sd_dhcp_lease_server_type_t k = 0; k < _SD_DHCP_LEASE_SERVER_TYPE_MAX; k++) {
537 if (server->servers[k].size <= 0)
538 continue;
539
540 r = dhcp_option_append(
541 &packet->dhcp, req->max_optlen, &offset, 0,
542 option_map[k],
543 sizeof(struct in_addr) * server->servers[k].size,
544 server->servers[k].addr);
545 if (r < 0)
546 return r;
547 }
2c649ca1 548
50018bfa 549
986c0edf
YW
550 if (server->timezone) {
551 r = dhcp_option_append(
552 &packet->dhcp, req->max_optlen, &offset, 0,
553 SD_DHCP_OPTION_NEW_TZDB_TIMEZONE,
554 strlen(server->timezone), server->timezone);
555 if (r < 0)
556 return r;
557 }
8eb9058d
LP
558 }
559
ebffea2a 560 ORDERED_SET_FOREACH(j, server->extra_options) {
7354900d
DW
561 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
562 j->option, j->length, j->data);
563 if (r < 0)
564 return r;
565 }
566
ebffea2a 567 if (!ordered_set_isempty(server->vendor_options)) {
564ca984
SS
568 r = dhcp_option_append(
569 &packet->dhcp, req->max_optlen, &offset, 0,
570 SD_DHCP_OPTION_VENDOR_SPECIFIC,
ebffea2a 571 ordered_set_size(server->vendor_options), server->vendor_options);
564ca984
SS
572 if (r < 0)
573 return r;
574 }
575
986c0edf 576 r = dhcp_server_send_packet(server, req, packet, type, offset);
2dead812
TG
577 if (r < 0)
578 return r;
579
580 return 0;
581}
582
bd57b450
TG
583static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
584 _cleanup_free_ DHCPPacket *packet = NULL;
585 size_t offset;
586 int r;
587
588 r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
589 if (r < 0)
590 return r;
591
b3ec603c 592 return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
bd57b450
TG
593}
594
a6f1e036 595static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
141261f1 596 be32_t gateway, const uint8_t chaddr[]) {
52750344
TG
597 _cleanup_free_ DHCPPacket *packet = NULL;
598 size_t optoffset = 0;
599 int r;
600
601 assert(server);
602 assert(address != INADDR_ANY);
603 assert(chaddr);
604
605 packet = malloc0(sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE);
606 if (!packet)
607 return -ENOMEM;
608
609 r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0,
76253e73
DW
610 DHCP_FORCERENEW, ARPHRD_ETHER,
611 DHCP_MIN_OPTIONS_SIZE, &optoffset);
52750344
TG
612 if (r < 0)
613 return r;
614
615 r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE,
22805d92 616 &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL);
52750344
TG
617 if (r < 0)
618 return r;
619
620 memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN);
621
8eb7b6a5
PF
622 r = dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT,
623 &packet->dhcp,
52750344 624 sizeof(DHCPMessage) + optoffset);
c15fb62a
TA
625 if (r < 0)
626 return r;
52750344
TG
627
628 return 0;
629}
630
b3ec603c 631static int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) {
89ca10c6 632 DHCPRequest *req = userdata;
816e2e7a
TG
633
634 assert(req);
635
636 switch(code) {
22805d92 637 case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
c7d9ffe6 638 if (len == 4)
9ae84244 639 req->lifetime = unaligned_read_be32(option);
c7d9ffe6
TG
640
641 break;
22805d92 642 case SD_DHCP_OPTION_REQUESTED_IP_ADDRESS:
2dead812 643 if (len == 4)
9ae84244 644 memcpy(&req->requested_ip, option, sizeof(be32_t));
2dead812
TG
645
646 break;
22805d92 647 case SD_DHCP_OPTION_SERVER_IDENTIFIER:
816e2e7a 648 if (len == 4)
9ae84244 649 memcpy(&req->server_id, option, sizeof(be32_t));
816e2e7a
TG
650
651 break;
22805d92 652 case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
816e2e7a
TG
653 if (len >= 2) {
654 uint8_t *data;
655
656 data = memdup(option, len);
657 if (!data)
658 return -ENOMEM;
659
660 free(req->client_id.data);
661 req->client_id.data = data;
662 req->client_id.length = len;
663 }
664
665 break;
22805d92 666 case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
76a9d0f1
LP
667
668 if (len == 2 && unaligned_read_be16(option) >= sizeof(DHCPPacket))
9ae84244 669 req->max_optlen = unaligned_read_be16(option) - sizeof(DHCPPacket);
816e2e7a 670
11c38d3e
YA
671 break;
672 case SD_DHCP_OPTION_RELAY_AGENT_INFORMATION:
673 req->agent_info_option = (uint8_t*)option - 2;
674
816e2e7a
TG
675 break;
676 }
677
678 return 0;
679}
680
75db809a 681static DHCPRequest* dhcp_request_free(DHCPRequest *req) {
816e2e7a 682 if (!req)
75db809a 683 return NULL;
816e2e7a
TG
684
685 free(req->client_id.data);
75db809a 686 return mfree(req);
816e2e7a
TG
687}
688
689DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
816e2e7a 690
586ac6f7 691static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) {
816e2e7a
TG
692 assert(req);
693 assert(message);
694
695 req->message = message;
696
e2acdb6b 697 /* set client id based on MAC address if client did not send an explicit
a6f1e036 698 one */
816e2e7a 699 if (!req->client_id.data) {
9a0f246f 700 void *data;
816e2e7a 701
9a0f246f 702 data = malloc0(ETH_ALEN + 1);
816e2e7a
TG
703 if (!data)
704 return -ENOMEM;
705
9a0f246f
LP
706 ((uint8_t*) data)[0] = 0x01;
707 memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN);
708
816e2e7a
TG
709 req->client_id.length = ETH_ALEN + 1;
710 req->client_id.data = data;
816e2e7a
TG
711 }
712
713 if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
714 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
715
9a0f246f 716 if (req->lifetime <= 0)
586ac6f7
LP
717 req->lifetime = MAX(1ULL, server->default_lease_time);
718
719 if (server->max_lease_time > 0 && req->lifetime > server->max_lease_time)
720 req->lifetime = server->max_lease_time;
c7d9ffe6 721
816e2e7a
TG
722 return 0;
723}
724
87322b3a
TG
725static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
726 assert(server);
727
728 if (!server->pool_size)
729 return -EINVAL;
730
99634696
TG
731 if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) ||
732 be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size)))
733 return -ERANGE;
87322b3a 734
99634696 735 return be32toh(requested_ip & ~server->netmask) - server->pool_offset;
87322b3a
TG
736}
737
11c38d3e
YA
738static int append_agent_information_option(sd_dhcp_server *server, DHCPMessage *message, size_t opt_length, size_t size) {
739 int r;
740 size_t offset;
741
742 assert(server);
743 assert(message);
744
745 r = dhcp_option_find_option(message->options, opt_length, SD_DHCP_OPTION_END, &offset);
746 if (r < 0)
747 return r;
748
749 r = dhcp_option_append(message, size, &offset, 0, SD_DHCP_OPTION_RELAY_AGENT_INFORMATION, 0, server);
750 if (r < 0)
751 return r;
752
753 r = dhcp_option_append(message, size, &offset, 0, SD_DHCP_OPTION_END, 0, NULL);
754 if (r < 0)
755 return r;
756 return offset;
757}
758
759static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *message, size_t opt_length, size_t buflen) {
c95df587 760 _cleanup_free_ DHCPPacket *packet = NULL;
11c38d3e 761 int r;
c95df587
YA
762
763 assert(server);
764 assert(message);
765 assert(sd_dhcp_server_is_in_relay_mode(server));
766
767 if (message->op == BOOTREQUEST) {
768 log_dhcp_server(server, "(relay agent) BOOTREQUEST (0x%x)", be32toh(message->xid));
769 if (message->hops >= 16)
770 return -ETIME;
771 message->hops++;
772
773 /* https://tools.ietf.org/html/rfc1542#section-4.1.1 */
774 if (message->giaddr == 0)
775 message->giaddr = server->address;
776
11c38d3e
YA
777 if (server->agent_circuit_id || server->agent_remote_id) {
778 r = append_agent_information_option(server, message, opt_length, buflen - sizeof(DHCPMessage));
779 if (r < 0)
780 return log_dhcp_server_errno(server, r, "could not append relay option: %m");
781 opt_length = r;
782 }
783
c95df587
YA
784 return dhcp_server_send_udp(server, server->relay_target.s_addr, DHCP_PORT_SERVER, message, sizeof(DHCPMessage) + opt_length);
785 } else if (message->op == BOOTREPLY) {
786 log_dhcp_server(server, "(relay agent) BOOTREPLY (0x%x)", be32toh(message->xid));
11c38d3e 787 if (message->giaddr != server->address)
c95df587 788 return log_dhcp_server_errno(server, SYNTHETIC_ERRNO(EBADMSG),
11c38d3e 789 "(relay agent) BOOTREPLY giaddr mismatch, discarding");
c95df587
YA
790
791 int message_type = dhcp_option_parse(message, sizeof(DHCPMessage) + opt_length, NULL, NULL, NULL);
792 if (message_type < 0)
793 return message_type;
794
795 packet = malloc0(sizeof(DHCPPacket) + opt_length);
796 if (!packet)
797 return -ENOMEM;
798 memcpy(&packet->dhcp, message, sizeof(DHCPMessage) + opt_length);
799
11c38d3e
YA
800 r = dhcp_option_remove_option(packet->dhcp.options, opt_length, SD_DHCP_OPTION_RELAY_AGENT_INFORMATION);
801 if (r > 0)
802 opt_length = r;
803
c95df587
YA
804 bool l2_broadcast = requested_broadcast(message) || message_type == DHCP_NAK;
805 const be32_t destination = message_type == DHCP_NAK ? INADDR_ANY : message->ciaddr;
806 return dhcp_server_send(server, destination, DHCP_PORT_CLIENT, packet, opt_length, l2_broadcast);
807 }
808 return -EBADMSG;
809}
810
65a0ef23 811static int prepare_new_lease(
812 DHCPLease **ret_lease,
813 be32_t address,
814 const DHCPClientId *client_id,
815 const uint8_t chaddr[static ETH_ALEN],
816 be32_t gateway,
817 usec_t expiration) {
818
819 _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
820
821 lease = new(DHCPLease, 1);
822 if (!lease)
823 return -ENOMEM;
824
825 *lease = (DHCPLease) {
826 .address = address,
827 .client_id.length = client_id->length,
828 .gateway = gateway,
829 .expiration = expiration,
830 };
831 lease->client_id.data = memdup(client_id->data, client_id->length);
832 if (!lease->client_id.data)
833 return -ENOMEM;
834
835 memcpy(&lease->chaddr, chaddr, ETH_ALEN);
836
837 *ret_lease = TAKE_PTR(lease);
838
839 return 0;
840}
841
842static bool static_leases_have_address(sd_dhcp_server *server, be32_t address) {
843 DHCPLease *s;
844
845 assert(server);
846
847 HASHMAP_FOREACH(s, server->static_leases_by_client_id)
848 if (s->address == address)
849 return true;
850
851 return false;
852}
853
83cedf7a
TG
854#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
855
65a0ef23 856int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length) {
8e766630 857 _cleanup_(dhcp_request_freep) DHCPRequest *req = NULL;
f693e9b3 858 _cleanup_free_ char *error_message = NULL;
65a0ef23 859 DHCPLease *existing_lease, *static_lease;
816e2e7a 860 int type, r;
be077570
TG
861
862 assert(server);
863 assert(message);
864
865 if (message->op != BOOTREQUEST ||
866 message->htype != ARPHRD_ETHER ||
867 message->hlen != ETHER_ADDR_LEN)
868 return 0;
869
816e2e7a
TG
870 req = new0(DHCPRequest, 1);
871 if (!req)
872 return -ENOMEM;
873
f693e9b3 874 type = dhcp_option_parse(message, length, parse_request, req, &error_message);
be077570
TG
875 if (type < 0)
876 return 0;
877
586ac6f7 878 r = ensure_sane_request(server, req, message);
816e2e7a
TG
879 if (r < 0)
880 /* this only fails on critical errors */
881 return r;
882
65a0ef23 883 existing_lease = hashmap_get(server->leases_by_client_id, &req->client_id);
884 static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id);
87322b3a 885
4dc35568 886 switch(type) {
9a0f246f
LP
887
888 case DHCP_DISCOVER: {
87322b3a
TG
889 be32_t address = INADDR_ANY;
890 unsigned i;
2dead812 891
65a0ef23 892 log_dhcp_server(server, "DISCOVER (0x%x)", be32toh(req->message->xid));
4dc35568 893
2dead812
TG
894 if (!server->pool_size)
895 /* no pool allocated */
896 return 0;
897
87322b3a 898 /* for now pick a random free address from the pool */
65a0ef23 899 if (static_lease)
900 address = static_lease->address;
901 else if (existing_lease)
87322b3a
TG
902 address = existing_lease->address;
903 else {
b826ab58 904 struct siphash state;
0cb3c286 905 uint64_t hash;
99634696 906 uint32_t next_offer;
83cedf7a
TG
907
908 /* even with no persistence of leases, we try to offer the same client
909 the same IP address. we do this by using the hash of the client id
910 as the offset into the pool of leases when finding the next free one */
911
0cb3c286 912 siphash24_init(&state, HASH_KEY.bytes);
b826ab58 913 client_id_hash_func(&req->client_id, &state);
933f9cae 914 hash = htole64(siphash24_finalize(&state));
0cb3c286 915 next_offer = hash % server->pool_size;
83cedf7a 916
87322b3a 917 for (i = 0; i < server->pool_size; i++) {
83cedf7a 918 if (!server->bound_leases[next_offer]) {
65a0ef23 919 be32_t tmp = server->subnet | htobe32(server->pool_offset + next_offer);
920 if (!static_leases_have_address(server, tmp)) {
921 address = tmp;
922 break;
923 }
6408ba5f
LP
924 }
925
926 next_offer = (next_offer + 1) % server->pool_size;
87322b3a
TG
927 }
928 }
929
930 if (address == INADDR_ANY)
931 /* no free addresses left */
932 return 0;
2dead812 933
986c0edf 934 r = server_send_offer_or_ack(server, req, address, DHCP_OFFER);
6e741541 935 if (r < 0)
4dc35568 936 /* this only fails on critical errors */
6e741541 937 return log_dhcp_server_errno(server, r, "Could not send offer: %m");
be077570 938
6e741541
LP
939 log_dhcp_server(server, "OFFER (0x%x)", be32toh(req->message->xid));
940 return DHCP_OFFER;
4dc35568 941 }
5b34277c 942 case DHCP_DECLINE:
f693e9b3 943 log_dhcp_server(server, "DECLINE (0x%x): %s", be32toh(req->message->xid), strna(error_message));
5b34277c
TG
944
945 /* TODO: make sure we don't offer this address again */
946
947 return 1;
948
9a0f246f 949 case DHCP_REQUEST: {
2dead812 950 be32_t address;
bd57b450 951 bool init_reboot = false;
87322b3a 952 int pool_offset;
2dead812
TG
953
954 /* see RFC 2131, section 4.3.2 */
955
956 if (req->server_id) {
957 log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
958 be32toh(req->message->xid));
959
960 /* SELECTING */
961 if (req->server_id != server->address)
962 /* client did not pick us */
963 return 0;
964
965 if (req->message->ciaddr)
966 /* this MUST be zero */
967 return 0;
968
969 if (!req->requested_ip)
970 /* this must be filled in with the yiaddr
971 from the chosen OFFER */
972 return 0;
973
974 address = req->requested_ip;
975 } else if (req->requested_ip) {
976 log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
977 be32toh(req->message->xid));
978
979 /* INIT-REBOOT */
980 if (req->message->ciaddr)
981 /* this MUST be zero */
982 return 0;
983
bd57b450 984 /* TODO: check more carefully if IP is correct */
2dead812 985 address = req->requested_ip;
bd57b450 986 init_reboot = true;
2dead812
TG
987 } else {
988 log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
989 be32toh(req->message->xid));
990
991 /* REBINDING / RENEWING */
992 if (!req->message->ciaddr)
993 /* this MUST be filled in with clients IP address */
994 return 0;
995
996 address = req->message->ciaddr;
997 }
998
87322b3a
TG
999 pool_offset = get_pool_offset(server, address);
1000
1001 /* verify that the requested address is from the pool, and either
1002 owned by the current client or free */
65a0ef23 1003 if (pool_offset >= 0 && static_lease) {
1004 _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL, *old_lease = NULL;
1005 usec_t time_now, expiration;
87322b3a 1006
65a0ef23 1007 r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now);
1008 if (r < 0)
38a03f06 1009 return r;
bff92d2c 1010
65a0ef23 1011 expiration = usec_add(req->lifetime * USEC_PER_SEC, time_now);
1012
1013 r = prepare_new_lease(&lease, static_lease->address, &req->client_id,
1014 req->message->chaddr, req->message->giaddr, expiration);
1015 if (r < 0)
1016 return r;
87322b3a 1017
986c0edf 1018 r = server_send_offer_or_ack(server, req, address, DHCP_ACK);
65a0ef23 1019 if (r < 0)
2dead812 1020 /* this only fails on critical errors */
65a0ef23 1021 return log_dhcp_server_errno(server, r, "Could not send ack: %m");
87322b3a 1022
65a0ef23 1023 log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid));
87322b3a 1024
65a0ef23 1025 server->bound_leases[pool_offset] = lease;
1026
1027 old_lease = hashmap_remove(server->leases_by_client_id, &lease->client_id);
1028 r = hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
1029 if (r < 0)
1030 return log_dhcp_server_errno(server, r, "Could not save lease: %m");
1031 TAKE_PTR(lease);
1032
1033 if (server->callback)
1034 server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
1035
1036 return DHCP_ACK;
1037
1038 } else if (pool_offset >= 0 && server->bound_leases[pool_offset] == existing_lease) {
1039 _cleanup_(dhcp_lease_freep) DHCPLease *new_lease = NULL;
1040 usec_t time_now, expiration;
1041 DHCPLease *lease;
1042
1043 r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now);
1044 if (r < 0)
2dead812 1045 return r;
87322b3a 1046
65a0ef23 1047 expiration = usec_add(req->lifetime * USEC_PER_SEC, time_now);
87322b3a 1048
65a0ef23 1049 if (!existing_lease) {
1050 r = prepare_new_lease(&new_lease, address, &req->client_id,
1051 req->message->chaddr, req->message->giaddr, expiration);
1052 if (r < 0)
1053 return r;
36d35f22 1054
65a0ef23 1055 lease = new_lease;
1056 } else {
1057 existing_lease->expiration = expiration;
1058 lease = existing_lease;
2dead812 1059 }
6e741541 1060
65a0ef23 1061 r = server_send_offer_or_ack(server, req, address, DHCP_ACK);
1062 if (r < 0)
1063 /* this only fails on critical errors */
1064 return log_dhcp_server_errno(server, r, "Could not send ack: %m");
1065
1066 log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid));
1067
1068 server->bound_leases[pool_offset] = lease;
1069 r = hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
1070 if (r < 0)
1071 return log_dhcp_server_errno(server, r, "Could not save lease: %m");
1072 TAKE_PTR(new_lease);
1073
1074 if (server->callback)
1075 server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
1076
1077 return DHCP_ACK;
1078
bd57b450
TG
1079 } else if (init_reboot) {
1080 r = server_send_nak(server, req);
6e741541 1081 if (r < 0)
bd57b450 1082 /* this only fails on critical errors */
6e741541
LP
1083 return log_dhcp_server_errno(server, r, "Could not send nak: %m");
1084
1085 log_dhcp_server(server, "NAK (0x%x)", be32toh(req->message->xid));
1086 return DHCP_NAK;
bd57b450 1087 }
2dead812
TG
1088
1089 break;
1090 }
9a0f246f 1091
500792d8
TG
1092 case DHCP_RELEASE: {
1093 int pool_offset;
1094
1095 log_dhcp_server(server, "RELEASE (0x%x)",
1096 be32toh(req->message->xid));
1097
1098 if (!existing_lease)
1099 return 0;
1100
1101 if (existing_lease->address != req->message->ciaddr)
1102 return 0;
1103
1104 pool_offset = get_pool_offset(server, req->message->ciaddr);
1105 if (pool_offset < 0)
1106 return 0;
1107
1108 if (server->bound_leases[pool_offset] == existing_lease) {
1109 server->bound_leases[pool_offset] = NULL;
1110 hashmap_remove(server->leases_by_client_id, existing_lease);
1111 dhcp_lease_free(existing_lease);
36d35f22
MAL
1112
1113 if (server->callback)
1114 server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
c3922c0c 1115 }
500792d8 1116
c3922c0c
LP
1117 return 0;
1118 }}
4dc35568
TG
1119
1120 return 0;
be077570
TG
1121}
1122
11c38d3e
YA
1123static size_t relay_agent_information_length(const char* agent_circuit_id, const char* agent_remote_id) {
1124 size_t sum = 0;
1125 if (agent_circuit_id)
1126 sum += 2 + strlen(agent_circuit_id);
1127 if (agent_remote_id)
1128 sum += 2 + strlen(agent_remote_id);
1129 return sum;
1130}
1131
ff734080
TG
1132static int server_receive_message(sd_event_source *s, int fd,
1133 uint32_t revents, void *userdata) {
be077570 1134 _cleanup_free_ DHCPMessage *message = NULL;
fb29cdbe 1135 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct in_pktinfo))) control;
ff734080
TG
1136 sd_dhcp_server *server = userdata;
1137 struct iovec iov = {};
1138 struct msghdr msg = {
1139 .msg_iov = &iov,
1140 .msg_iovlen = 1,
fb29cdbe
LP
1141 .msg_control = &control,
1142 .msg_controllen = sizeof(control),
ff734080 1143 };
3a864fe4 1144 struct cmsghdr *cmsg;
11c38d3e 1145 ssize_t datagram_size, len;
57027d03 1146 int r;
ff734080
TG
1147
1148 assert(server);
1149
11c38d3e
YA
1150 datagram_size = next_datagram_size_fd(fd);
1151 if (datagram_size < 0)
1152 return datagram_size;
1153
1154 size_t buflen = datagram_size;
1155 if (sd_dhcp_server_is_in_relay_mode(server))
19cc6d5e 1156 /* Preallocate the additional size for DHCP Relay Agent Information Option if needed */
11c38d3e 1157 buflen += relay_agent_information_length(server->agent_circuit_id, server->agent_remote_id) + 2;
ff734080 1158
0d43d2fc 1159 message = malloc(buflen);
ff734080
TG
1160 if (!message)
1161 return -ENOMEM;
1162
11c38d3e 1163 iov = IOVEC_MAKE(message, datagram_size);
ff734080 1164
2efa5bc6
LP
1165 len = recvmsg_safe(fd, &msg, 0);
1166 if (IN_SET(len, -EAGAIN, -EINTR))
1167 return 0;
1168 if (len < 0)
1169 return len;
1170 if ((size_t) len < sizeof(DHCPMessage))
be077570 1171 return 0;
ff734080 1172
2a1288ff 1173 CMSG_FOREACH(cmsg, &msg) {
3a864fe4
TG
1174 if (cmsg->cmsg_level == IPPROTO_IP &&
1175 cmsg->cmsg_type == IP_PKTINFO &&
1176 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
1177 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
1178
a6f1e036
TG
1179 /* TODO figure out if this can be done as a filter on
1180 * the socket, like for IPv6 */
b3ec603c 1181 if (server->ifindex != info->ipi_ifindex)
3a864fe4
TG
1182 return 0;
1183
1184 break;
1185 }
1186 }
1187
c95df587 1188 if (sd_dhcp_server_is_in_relay_mode(server)) {
11c38d3e 1189 r = dhcp_server_relay_message(server, message, len - sizeof(DHCPMessage), buflen);
c95df587
YA
1190 if (r < 0)
1191 log_dhcp_server_errno(server, r, "Couldn't relay message: %m");
1192 } else {
1193 r = dhcp_server_handle_message(server, message, (size_t) len);
1194 if (r < 0)
1195 log_dhcp_server_errno(server, r, "Couldn't process incoming message: %m");
1196 }
57027d03 1197 return 0;
ff734080
TG
1198}
1199
1200int sd_dhcp_server_start(sd_dhcp_server *server) {
1201 int r;
1202
1203 assert_return(server, -EINVAL);
1204 assert_return(server->event, -EINVAL);
1205 assert_return(!server->receive_message, -EBUSY);
cfcbb135
LP
1206 assert_return(server->fd_raw < 0, -EBUSY);
1207 assert_return(server->fd < 0, -EBUSY);
20af7091 1208 assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
ff734080 1209
3e29b889 1210 r = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
8de4a226
TG
1211 if (r < 0) {
1212 r = -errno;
21b6b87e 1213 goto on_error;
8de4a226
TG
1214 }
1215 server->fd_raw = r;
1216
21b6b87e
YA
1217 if (server->bind_to_interface)
1218 r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY, DHCP_PORT_SERVER, -1);
1219 else
1220 r = dhcp_network_bind_udp_socket(0, server->address, DHCP_PORT_SERVER, -1);
1221 if (r < 0)
1222 goto on_error;
ff734080
TG
1223 server->fd = r;
1224
1225 r = sd_event_add_io(server->event, &server->receive_message,
1226 server->fd, EPOLLIN,
1227 server_receive_message, server);
21b6b87e
YA
1228 if (r < 0)
1229 goto on_error;
ff734080
TG
1230
1231 r = sd_event_source_set_priority(server->receive_message,
1232 server->event_priority);
21b6b87e
YA
1233 if (r < 0)
1234 goto on_error;
1235
1236 if (!server->bind_to_interface) {
1237 r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_BROADCAST, DHCP_PORT_SERVER, -1);
1238 if (r < 0)
1239 goto on_error;
1240
1241 server->fd_broadcast = r;
1242
1243 r = sd_event_add_io(server->event, &server->receive_broadcast,
1244 server->fd_broadcast, EPOLLIN,
1245 server_receive_message, server);
1246 if (r < 0)
1247 goto on_error;
1248
1249 r = sd_event_source_set_priority(server->receive_broadcast,
1250 server->event_priority);
1251 if (r < 0)
1252 goto on_error;
ff734080
TG
1253 }
1254
1255 log_dhcp_server(server, "STARTED");
1256
1257 return 0;
21b6b87e
YA
1258
1259on_error:
1260 sd_dhcp_server_stop(server);
1261 return r;
ff734080 1262}
52750344
TG
1263
1264int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
621ac3d2 1265 int r = 0;
52750344
TG
1266
1267 assert_return(server, -EINVAL);
1268 assert(server->bound_leases);
1269
fe96c0f8 1270 for (uint32_t i = 0; i < server->pool_size; i++) {
52750344
TG
1271 DHCPLease *lease = server->bound_leases[i];
1272
99634696 1273 if (!lease || lease == &server->invalid_lease)
52750344
TG
1274 continue;
1275
1276 r = server_send_forcerenew(server, lease->address,
1277 lease->gateway,
1278 lease->chaddr);
1279 if (r < 0)
1280 return r;
6408ba5f
LP
1281
1282 log_dhcp_server(server, "FORCERENEW");
52750344
TG
1283 }
1284
1285 return r;
1286}
8eb9058d 1287
21b6b87e
YA
1288int sd_dhcp_server_set_bind_to_interface(sd_dhcp_server *server, int enabled) {
1289 assert_return(server, -EINVAL);
1290 assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
1291
1292 if (!!enabled == server->bind_to_interface)
1293 return 0;
1294
1295 server->bind_to_interface = enabled;
1296
1297 return 1;
1298}
1299
64d6c229 1300int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) {
8eb9058d
LP
1301 int r;
1302
1303 assert_return(server, -EINVAL);
089fb865 1304 assert_return(timezone_is_valid(tz, LOG_DEBUG), -EINVAL);
8eb9058d 1305
64d6c229 1306 if (streq_ptr(tz, server->timezone))
8eb9058d
LP
1307 return 0;
1308
64d6c229 1309 r = free_and_strdup(&server->timezone, tz);
8eb9058d
LP
1310 if (r < 0)
1311 return r;
1312
1313 return 1;
1314}
586ac6f7
LP
1315
1316int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t) {
1317 assert_return(server, -EINVAL);
1318
1319 if (t == server->max_lease_time)
1320 return 0;
1321
1322 server->max_lease_time = t;
1323 return 1;
1324}
1325
1326int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t) {
1327 assert_return(server, -EINVAL);
1328
1329 if (t == server->default_lease_time)
1330 return 0;
1331
1332 server->default_lease_time = t;
1333 return 1;
1334}
1a04db0f 1335
c8407baf
ZJS
1336int sd_dhcp_server_set_servers(
1337 sd_dhcp_server *server,
2324fd3a 1338 sd_dhcp_lease_server_type_t what,
c8407baf 1339 const struct in_addr addresses[],
90810f7a 1340 size_t n_addresses) {
1a04db0f 1341
ddb82ec2
LP
1342 struct in_addr *c = NULL;
1343
1a04db0f 1344 assert_return(server, -EINVAL);
c8407baf 1345 assert_return(addresses || n_addresses == 0, -EINVAL);
ddb82ec2
LP
1346 assert_return(what >= 0, -EINVAL);
1347 assert_return(what < _SD_DHCP_LEASE_SERVER_TYPE_MAX, -EINVAL);
1a04db0f 1348
ddb82ec2
LP
1349 if (server->servers[what].size == n_addresses &&
1350 memcmp(server->servers[what].addr, addresses, sizeof(struct in_addr) * n_addresses) == 0)
f678ac7e
SS
1351 return 0;
1352
c8407baf
ZJS
1353 if (n_addresses > 0) {
1354 c = newdup(struct in_addr, addresses, n_addresses);
f678ac7e
SS
1355 if (!c)
1356 return -ENOMEM;
f678ac7e
SS
1357 }
1358
ddb82ec2
LP
1359 free(server->servers[what].addr);
1360 server->servers[what].addr = c;
1361 server->servers[what].size = n_addresses;
f678ac7e
SS
1362 return 1;
1363}
1364
90810f7a 1365int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], size_t n) {
ddb82ec2 1366 return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_DNS, dns, n);
c8407baf 1367}
90810f7a 1368int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], size_t n) {
ddb82ec2 1369 return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_NTP, ntp, n);
c8407baf 1370}
90810f7a 1371int sd_dhcp_server_set_sip(sd_dhcp_server *server, const struct in_addr sip[], size_t n) {
ddb82ec2 1372 return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_SIP, sip, n);
c8407baf 1373}
90810f7a 1374int sd_dhcp_server_set_pop3(sd_dhcp_server *server, const struct in_addr pop3[], size_t n) {
ddb82ec2 1375 return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_POP3, pop3, n);
c8407baf 1376}
90810f7a 1377int sd_dhcp_server_set_smtp(sd_dhcp_server *server, const struct in_addr smtp[], size_t n) {
ddb82ec2 1378 return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_SMTP, smtp, n);
2c649ca1 1379}
90810f7a 1380int sd_dhcp_server_set_lpr(sd_dhcp_server *server, const struct in_addr lpr[], size_t n) {
ddb82ec2 1381 return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_LPR, lpr, n);
50018bfa 1382}
2c649ca1 1383
77ff6022
CG
1384int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled) {
1385 assert_return(server, -EINVAL);
1386
1387 if (enabled == server->emit_router)
1388 return 0;
1389
1390 server->emit_router = enabled;
1391
1392 return 1;
1393}
564ca984 1394
461dbb2f 1395int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v) {
564ca984
SS
1396 int r;
1397
1398 assert_return(server, -EINVAL);
1399 assert_return(v, -EINVAL);
1400
ebffea2a 1401 r = ordered_set_ensure_put(&server->extra_options, &dhcp_option_hash_ops, v);
7354900d
DW
1402 if (r < 0)
1403 return r;
1404
1405 sd_dhcp_option_ref(v);
1406 return 0;
1407}
1408
1409int sd_dhcp_server_add_vendor_option(sd_dhcp_server *server, sd_dhcp_option *v) {
1410 int r;
1411
1412 assert_return(server, -EINVAL);
1413 assert_return(v, -EINVAL);
1414
ebffea2a 1415 r = ordered_set_ensure_put(&server->vendor_options, &dhcp_option_hash_ops, v);
564ca984
SS
1416 if (r < 0)
1417 return r;
1418
461dbb2f 1419 sd_dhcp_option_ref(v);
564ca984
SS
1420
1421 return 1;
1422}
5b03043a
MAL
1423
1424int sd_dhcp_server_set_callback(sd_dhcp_server *server, sd_dhcp_server_callback_t cb, void *userdata) {
1425 assert_return(server, -EINVAL);
1426
1427 server->callback = cb;
1428 server->callback_userdata = userdata;
1429
1430 return 0;
1431}
c95df587 1432
11c38d3e 1433int sd_dhcp_server_set_relay_target(sd_dhcp_server *server, const struct in_addr *address) {
c95df587
YA
1434 assert_return(server, -EINVAL);
1435 assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
1436
1437 if (memcmp(address, &server->relay_target, sizeof(struct in_addr)) == 0)
1438 return 0;
1439
1440 server->relay_target = *address;
1441 return 1;
1442}
11c38d3e
YA
1443
1444int sd_dhcp_server_set_relay_agent_information(
1445 sd_dhcp_server *server,
1446 const char *agent_circuit_id,
1447 const char *agent_remote_id) {
1448 _cleanup_free_ char *circuit_id_dup = NULL, *remote_id_dup = NULL;
1449
1450 assert_return(server, -EINVAL);
1451
1452 if (relay_agent_information_length(agent_circuit_id, agent_remote_id) > UINT8_MAX)
1453 return -ENOBUFS;
1454
1455 if (agent_circuit_id) {
1456 circuit_id_dup = strdup(agent_circuit_id);
1457 if (!circuit_id_dup)
1458 return -ENOMEM;
1459 }
1460
1461 if (agent_remote_id) {
1462 remote_id_dup = strdup(agent_remote_id);
1463 if (!remote_id_dup)
1464 return -ENOMEM;
1465 }
1466
1467 free_and_replace(server->agent_circuit_id, circuit_id_dup);
1468 free_and_replace(server->agent_remote_id, remote_id_dup);
1469 return 0;
1470}
65a0ef23 1471
1472int sd_dhcp_server_set_static_lease(
1473 sd_dhcp_server *server,
1474 const struct in_addr *address,
1475 uint8_t *client_id,
1476 size_t client_id_size) {
1477
1478 _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL, *old = NULL;
1479 DHCPClientId c;
1480 int r;
1481
1482 assert_return(server, -EINVAL);
1483 assert_return(client_id, -EINVAL);
1484 assert_return(client_id_size == ETH_ALEN + 1, -EINVAL);
1485 assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
1486
1487 /* Static lease with an empty or omitted address is a valid entry,
1488 * the server removes any static lease with the specified mac address. */
1489 if (!address || address->s_addr == 0) {
1490 _cleanup_free_ void *data = NULL;
1491
1492 data = memdup(client_id, client_id_size);
1493 if (!data)
1494 return -ENOMEM;
1495
1496 c = (DHCPClientId) {
1497 .length = client_id_size,
1498 .data = data,
1499 };
1500
1501 old = hashmap_remove(server->static_leases_by_client_id, &c);
1502 return 0;
1503 }
1504
1505 if (static_leases_have_address(server, address->s_addr))
1506 return -EEXIST;
1507
1508 lease = new(DHCPLease, 1);
1509 if (!lease)
1510 return -ENOMEM;
1511
1512 *lease = (DHCPLease) {
1513 .address = address->s_addr,
1514 .client_id.length = client_id_size,
1515 .gateway = 0,
1516 .expiration = 0,
1517 };
1518 lease->client_id.data = memdup(client_id, client_id_size);
1519 if (!lease->client_id.data)
1520 return -ENOMEM;
1521
1522 r = hashmap_ensure_put(&server->static_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
1523 if (r < 0)
1524 return r;
1525
1526 TAKE_PTR(lease);
1527 return 0;
1528}