]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-server.c
Merge pull request #9017 from keszybz/man-coredump
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-server.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
b44cd882
TG
2/***
3 This file is part of systemd.
4
5 Copyright (C) 2013 Intel Corporation. All rights reserved.
6 Copyright (C) 2014 Tom Gundersen
b44cd882
TG
7***/
8
ff734080
TG
9#include <sys/ioctl.h>
10
b44cd882 11#include "sd-dhcp-server.h"
07630cea 12
b5efdb8a 13#include "alloc-util.h"
ff734080 14#include "dhcp-internal.h"
07630cea 15#include "dhcp-server-internal.h"
3ffd4af2 16#include "fd-util.h"
07630cea 17#include "in-addr-util.h"
dccca82b 18#include "sd-id128.h"
07630cea
LP
19#include "siphash24.h"
20#include "string-util.h"
9ae84244 21#include "unaligned.h"
b44cd882 22
586ac6f7
LP
23#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
24#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
4dc35568 25
45a9eac9
RM
26static void dhcp_lease_free(DHCPLease *lease) {
27 if (!lease)
28 return;
29
30 free(lease->client_id.data);
31 free(lease);
32}
33
99634696
TG
34/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
35 * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
36 * moreover, the server's own address may be in the pool, and is in that case reserved in order not to
37 * accidentally hand it out */
38int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) {
39 struct in_addr netmask_addr;
40 be32_t netmask;
41 uint32_t server_off, broadcast_off, size_max;
42
2dead812
TG
43 assert_return(server, -EINVAL);
44 assert_return(address, -EINVAL);
99634696
TG
45 assert_return(address->s_addr != INADDR_ANY, -EINVAL);
46 assert_return(prefixlen <= 32, -ERANGE);
99634696 47
5a941f5f 48 assert_se(in4_addr_prefixlen_to_netmask(&netmask_addr, prefixlen));
99634696
TG
49 netmask = netmask_addr.s_addr;
50
51 server_off = be32toh(address->s_addr & ~netmask);
52 broadcast_off = be32toh(~netmask);
53
54 /* the server address cannot be the subnet address */
55 assert_return(server_off != 0, -ERANGE);
56
57 /* nor the broadcast address */
58 assert_return(server_off != broadcast_off, -ERANGE);
59
60 /* 0 offset means we should set a default, we skip the first (subnet) address
61 and take the next one */
62 if (offset == 0)
63 offset = 1;
64
65 size_max = (broadcast_off + 1) /* the number of addresses in the subnet */
66 - offset /* exclude the addresses before the offset */
67 - 1; /* exclude the last (broadcast) address */
68
69 /* The pool must contain at least one address */
70 assert_return(size_max >= 1, -ERANGE);
71
72 if (size != 0)
73 assert_return(size <= size_max, -ERANGE);
74 else
75 size = size_max;
87322b3a 76
45a9eac9 77 if (server->address != address->s_addr || server->netmask != netmask || server->pool_size != size || server->pool_offset != offset) {
45a9eac9
RM
78
79 free(server->bound_leases);
80 server->bound_leases = new0(DHCPLease*, size);
81 if (!server->bound_leases)
82 return -ENOMEM;
2dead812 83
45a9eac9
RM
84 server->pool_offset = offset;
85 server->pool_size = size;
2dead812 86
45a9eac9
RM
87 server->address = address->s_addr;
88 server->netmask = netmask;
89 server->subnet = address->s_addr & netmask;
99634696 90
45a9eac9
RM
91 if (server_off >= offset && server_off - offset < size)
92 server->bound_leases[server_off - offset] = &server->invalid_lease;
93
94 /* Drop any leases associated with the old address range */
224b0e7a 95 hashmap_clear_with_destructor(server->leases_by_client_id, dhcp_lease_free);
45a9eac9 96 }
20af7091
TG
97
98 return 0;
99}
100
04c01369 101int sd_dhcp_server_is_running(sd_dhcp_server *server) {
75677581 102 assert_return(server, false);
7c16313f
TG
103
104 return !!server->receive_message;
105}
106
b44cd882 107sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
3733eec3
LP
108
109 if (!server)
110 return NULL;
111
112 assert(server->n_ref >= 1);
113 server->n_ref++;
b44cd882
TG
114
115 return server;
116}
117
b826ab58 118void client_id_hash_func(const void *p, struct siphash *state) {
87322b3a
TG
119 const DHCPClientId *id = p;
120
121 assert(id);
122 assert(id->length);
123 assert(id->data);
124
1e2527a6 125 siphash24_compress(&id->length, sizeof(id->length), state);
b826ab58 126 siphash24_compress(id->data, id->length, state);
87322b3a
TG
127}
128
129int client_id_compare_func(const void *_a, const void *_b) {
130 const DHCPClientId *a, *b;
131
132 a = _a;
133 b = _b;
134
135 assert(!a->length || a->data);
136 assert(!b->length || b->data);
137
138 if (a->length != b->length)
139 return a->length < b->length ? -1 : 1;
140
141 return memcmp(a->data, b->data, a->length);
142}
143
d5099efc
MS
144static const struct hash_ops client_id_hash_ops = {
145 .hash = client_id_hash_func,
146 .compare = client_id_compare_func
147};
148
b44cd882 149sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
3bdace9b 150 DHCPLease *lease;
87322b3a 151
3bdace9b
LP
152 if (!server)
153 return NULL;
b44cd882 154
3733eec3
LP
155 assert(server->n_ref >= 1);
156 server->n_ref--;
157
158 if (server->n_ref > 0)
3bdace9b 159 return NULL;
ff734080 160
3bdace9b 161 log_dhcp_server(server, "UNREF");
87322b3a 162
3bdace9b 163 sd_dhcp_server_stop(server);
87322b3a 164
3bdace9b
LP
165 sd_event_unref(server->event);
166
8eb9058d 167 free(server->timezone);
1a04db0f
LP
168 free(server->dns);
169 free(server->ntp);
8eb9058d 170
3bdace9b
LP
171 while ((lease = hashmap_steal_first(server->leases_by_client_id)))
172 dhcp_lease_free(lease);
173 hashmap_free(server->leases_by_client_id);
174
175 free(server->bound_leases);
6b430fdb 176 return mfree(server);
b44cd882
TG
177}
178
3a864fe4 179int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
4afd3348 180 _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
b44cd882
TG
181
182 assert_return(ret, -EINVAL);
3a864fe4 183 assert_return(ifindex > 0, -EINVAL);
b44cd882
TG
184
185 server = new0(sd_dhcp_server, 1);
186 if (!server)
187 return -ENOMEM;
188
3733eec3 189 server->n_ref = 1;
8de4a226 190 server->fd_raw = -1;
ff734080 191 server->fd = -1;
20af7091 192 server->address = htobe32(INADDR_ANY);
59b8f6b6 193 server->netmask = htobe32(INADDR_ANY);
b3ec603c 194 server->ifindex = ifindex;
357e1b17 195
d5099efc 196 server->leases_by_client_id = hashmap_new(&client_id_hash_ops);
357e1b17
LP
197 if (!server->leases_by_client_id)
198 return -ENOMEM;
199
586ac6f7
LP
200 server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC);
201 server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC);
b44cd882 202
1cc6c93a 203 *ret = TAKE_PTR(server);
b44cd882
TG
204
205 return 0;
206}
207
32d20645 208int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int64_t priority) {
b44cd882
TG
209 int r;
210
211 assert_return(server, -EINVAL);
212 assert_return(!server->event, -EBUSY);
213
214 if (event)
215 server->event = sd_event_ref(event);
216 else {
217 r = sd_event_default(&server->event);
218 if (r < 0)
219 return r;
220 }
221
222 server->event_priority = priority;
223
224 return 0;
225}
226
227int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
228 assert_return(server, -EINVAL);
229
230 server->event = sd_event_unref(server->event);
231
232 return 0;
233}
234
235sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
236 assert_return(server, NULL);
237
238 return server->event;
239}
ff734080
TG
240
241int sd_dhcp_server_stop(sd_dhcp_server *server) {
242 assert_return(server, -EINVAL);
243
244 server->receive_message =
245 sd_event_source_unref(server->receive_message);
246
8de4a226 247 server->fd_raw = safe_close(server->fd_raw);
ff734080
TG
248 server->fd = safe_close(server->fd);
249
250 log_dhcp_server(server, "STOPPED");
251
252 return 0;
253}
254
a6f1e036
TG
255static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
256 DHCPPacket *packet, size_t len) {
969b009d
TG
257 union sockaddr_union link = {
258 .ll.sll_family = AF_PACKET,
8e38570e 259 .ll.sll_protocol = htobe16(ETH_P_IP),
b3ec603c 260 .ll.sll_ifindex = server->ifindex,
969b009d
TG
261 .ll.sll_halen = ETH_ALEN,
262 };
969b009d
TG
263
264 assert(server);
b3ec603c 265 assert(server->ifindex > 0);
969b009d
TG
266 assert(server->address);
267 assert(packet);
268 assert(len > sizeof(DHCPPacket));
269
270 memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
271
272 dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
a6f1e036
TG
273 packet->dhcp.yiaddr,
274 DHCP_PORT_CLIENT, len);
969b009d 275
b3ec603c 276 return dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
969b009d
TG
277}
278
279static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
8eb7b6a5 280 uint16_t destination_port,
969b009d
TG
281 DHCPMessage *message, size_t len) {
282 union sockaddr_union dest = {
283 .in.sin_family = AF_INET,
8eb7b6a5 284 .in.sin_port = htobe16(destination_port),
969b009d
TG
285 .in.sin_addr.s_addr = destination,
286 };
287 struct iovec iov = {
288 .iov_base = message,
289 .iov_len = len,
290 };
291 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
292 struct msghdr msg = {
293 .msg_name = &dest,
294 .msg_namelen = sizeof(dest.in),
295 .msg_iov = &iov,
296 .msg_iovlen = 1,
297 .msg_control = cmsgbuf,
298 .msg_controllen = sizeof(cmsgbuf),
299 };
300 struct cmsghdr *cmsg;
301 struct in_pktinfo *pktinfo;
969b009d
TG
302
303 assert(server);
4c9cb12c 304 assert(server->fd >= 0);
969b009d
TG
305 assert(message);
306 assert(len > sizeof(DHCPMessage));
307
308 cmsg = CMSG_FIRSTHDR(&msg);
309 assert(cmsg);
310
311 cmsg->cmsg_level = IPPROTO_IP;
312 cmsg->cmsg_type = IP_PKTINFO;
313 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
314
315 /* we attach source interface and address info to the message
316 rather than binding the socket. This will be mostly useful
317 when we gain support for arbitrary number of server addresses
318 */
319 pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
320 assert(pktinfo);
321
b3ec603c 322 pktinfo->ipi_ifindex = server->ifindex;
969b009d
TG
323 pktinfo->ipi_spec_dst.s_addr = server->address;
324
0f01c1f9 325 if (sendmsg(server->fd, &msg, 0) < 0)
969b009d
TG
326 return -errno;
327
328 return 0;
329}
330
331static bool requested_broadcast(DHCPRequest *req) {
332 assert(req);
333
334 return req->message->flags & htobe16(0x8000);
335}
336
337int dhcp_server_send_packet(sd_dhcp_server *server,
338 DHCPRequest *req, DHCPPacket *packet,
339 int type, size_t optoffset) {
340 be32_t destination = INADDR_ANY;
8eb7b6a5 341 uint16_t destination_port = DHCP_PORT_CLIENT;
969b009d
TG
342 int r;
343
344 assert(server);
345 assert(req);
346 assert(req->max_optlen);
347 assert(optoffset <= req->max_optlen);
348 assert(packet);
349
350 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
22805d92 351 SD_DHCP_OPTION_SERVER_IDENTIFIER,
969b009d
TG
352 4, &server->address);
353 if (r < 0)
354 return r;
355
356 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
22805d92 357 SD_DHCP_OPTION_END, 0, NULL);
969b009d
TG
358 if (r < 0)
359 return r;
360
361 /* RFC 2131 Section 4.1
362
363 If the ’giaddr’ field in a DHCP message from a client is non-zero,
364 the server sends any return messages to the ’DHCP server’ port on the
365 BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
366 field is zero and the ’ciaddr’ field is nonzero, then the server
367 unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
368 If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
369 set, then the server broadcasts DHCPOFFER and DHCPACK messages to
370 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
371 ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
372 messages to the client’s hardware address and ’yiaddr’ address. In
373 all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
374 messages to 0xffffffff.
375
376 Section 4.3.2
377
378 If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
379 different subnet. The server MUST set the broadcast bit in the
380 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
381 client, because the client may not have a correct network address
382 or subnet mask, and the client may not be answering ARP requests.
383 */
384 if (req->message->giaddr) {
385 destination = req->message->giaddr;
8eb7b6a5 386 destination_port = DHCP_PORT_SERVER;
969b009d
TG
387 if (type == DHCP_NAK)
388 packet->dhcp.flags = htobe16(0x8000);
389 } else if (req->message->ciaddr && type != DHCP_NAK)
390 destination = req->message->ciaddr;
391
d6bd972d 392 if (destination != INADDR_ANY)
8eb7b6a5
PF
393 return dhcp_server_send_udp(server, destination,
394 destination_port, &packet->dhcp,
969b009d 395 sizeof(DHCPMessage) + optoffset);
d6bd972d 396 else if (requested_broadcast(req) || type == DHCP_NAK)
a6f1e036 397 return dhcp_server_send_udp(server, INADDR_BROADCAST,
8eb7b6a5 398 destination_port, &packet->dhcp,
d6bd972d 399 sizeof(DHCPMessage) + optoffset);
969b009d 400 else
a6f1e036
TG
401 /* we cannot send UDP packet to specific MAC address when the
402 address is not yet configured, so must fall back to raw
403 packets */
969b009d
TG
404 return dhcp_server_send_unicast_raw(server, packet,
405 sizeof(DHCPPacket) + optoffset);
406}
407
4dc35568 408static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
a6f1e036
TG
409 uint8_t type, size_t *_optoffset,
410 DHCPRequest *req) {
4dc35568 411 _cleanup_free_ DHCPPacket *packet = NULL;
1231c4d2 412 size_t optoffset = 0;
4dc35568
TG
413 int r;
414
415 assert(server);
416 assert(ret);
417 assert(_optoffset);
bd57b450 418 assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
4dc35568
TG
419
420 packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
421 if (!packet)
422 return -ENOMEM;
423
a6f1e036 424 r = dhcp_message_init(&packet->dhcp, BOOTREPLY,
76253e73
DW
425 be32toh(req->message->xid), type, ARPHRD_ETHER,
426 req->max_optlen, &optoffset);
4dc35568
TG
427 if (r < 0)
428 return r;
429
430 packet->dhcp.flags = req->message->flags;
431 packet->dhcp.giaddr = req->message->giaddr;
432 memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
433
434 *_optoffset = optoffset;
1cc6c93a 435 *ret = TAKE_PTR(packet);
4dc35568
TG
436
437 return 0;
438}
439
a6f1e036
TG
440static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
441 be32_t address) {
4dc35568
TG
442 _cleanup_free_ DHCPPacket *packet = NULL;
443 size_t offset;
444 be32_t lease_time;
445 int r;
446
447 r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
448 if (r < 0)
449 return r;
450
2dead812 451 packet->dhcp.yiaddr = address;
4dc35568 452
c7d9ffe6 453 lease_time = htobe32(req->lifetime);
4dc35568 454 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d92 455 SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
a6f1e036 456 &lease_time);
4dc35568
TG
457 if (r < 0)
458 return r;
459
59b8f6b6 460 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d92 461 SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
59b8f6b6
TG
462 if (r < 0)
463 return r;
464
77ff6022
CG
465 if (server->emit_router) {
466 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
467 SD_DHCP_OPTION_ROUTER, 4, &server->address);
468 if (r < 0)
469 return r;
470 }
59b8f6b6 471
4dc35568
TG
472 r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
473 if (r < 0)
474 return r;
475
476 return 0;
477}
478
a6f1e036
TG
479static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
480 be32_t address) {
2dead812
TG
481 _cleanup_free_ DHCPPacket *packet = NULL;
482 size_t offset;
483 be32_t lease_time;
484 int r;
485
486 r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
487 if (r < 0)
488 return r;
489
490 packet->dhcp.yiaddr = address;
491
c7d9ffe6 492 lease_time = htobe32(req->lifetime);
2dead812 493 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d92 494 SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
a6f1e036 495 &lease_time);
2dead812
TG
496 if (r < 0)
497 return r;
498
59b8f6b6 499 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d92 500 SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
59b8f6b6
TG
501 if (r < 0)
502 return r;
503
77ff6022
CG
504 if (server->emit_router) {
505 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
506 SD_DHCP_OPTION_ROUTER, 4, &server->address);
507 if (r < 0)
508 return r;
509 }
59b8f6b6 510
1a04db0f
LP
511 if (server->n_dns > 0) {
512 r = dhcp_option_append(
513 &packet->dhcp, req->max_optlen, &offset, 0,
22805d92 514 SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
1a04db0f
LP
515 sizeof(struct in_addr) * server->n_dns, server->dns);
516 if (r < 0)
517 return r;
518 }
519
520 if (server->n_ntp > 0) {
521 r = dhcp_option_append(
522 &packet->dhcp, req->max_optlen, &offset, 0,
22805d92 523 SD_DHCP_OPTION_NTP_SERVER,
1a04db0f
LP
524 sizeof(struct in_addr) * server->n_ntp, server->ntp);
525 if (r < 0)
526 return r;
527 }
528
8eb9058d
LP
529 if (server->timezone) {
530 r = dhcp_option_append(
531 &packet->dhcp, req->max_optlen, &offset, 0,
22805d92 532 SD_DHCP_OPTION_NEW_TZDB_TIMEZONE,
8eb9058d
LP
533 strlen(server->timezone), server->timezone);
534 if (r < 0)
535 return r;
536 }
537
2dead812
TG
538 r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
539 if (r < 0)
540 return r;
541
542 return 0;
543}
544
bd57b450
TG
545static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
546 _cleanup_free_ DHCPPacket *packet = NULL;
547 size_t offset;
548 int r;
549
550 r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
551 if (r < 0)
552 return r;
553
b3ec603c 554 return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
bd57b450
TG
555}
556
a6f1e036
TG
557static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
558 be32_t gateway, uint8_t chaddr[]) {
52750344
TG
559 _cleanup_free_ DHCPPacket *packet = NULL;
560 size_t optoffset = 0;
561 int r;
562
563 assert(server);
564 assert(address != INADDR_ANY);
565 assert(chaddr);
566
567 packet = malloc0(sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE);
568 if (!packet)
569 return -ENOMEM;
570
571 r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0,
76253e73
DW
572 DHCP_FORCERENEW, ARPHRD_ETHER,
573 DHCP_MIN_OPTIONS_SIZE, &optoffset);
52750344
TG
574 if (r < 0)
575 return r;
576
577 r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE,
22805d92 578 &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL);
52750344
TG
579 if (r < 0)
580 return r;
581
582 memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN);
583
8eb7b6a5
PF
584 r = dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT,
585 &packet->dhcp,
52750344 586 sizeof(DHCPMessage) + optoffset);
c15fb62a
TA
587 if (r < 0)
588 return r;
52750344
TG
589
590 return 0;
591}
592
b3ec603c 593static int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) {
89ca10c6 594 DHCPRequest *req = userdata;
816e2e7a
TG
595
596 assert(req);
597
598 switch(code) {
22805d92 599 case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
c7d9ffe6 600 if (len == 4)
9ae84244 601 req->lifetime = unaligned_read_be32(option);
c7d9ffe6
TG
602
603 break;
22805d92 604 case SD_DHCP_OPTION_REQUESTED_IP_ADDRESS:
2dead812 605 if (len == 4)
9ae84244 606 memcpy(&req->requested_ip, option, sizeof(be32_t));
2dead812
TG
607
608 break;
22805d92 609 case SD_DHCP_OPTION_SERVER_IDENTIFIER:
816e2e7a 610 if (len == 4)
9ae84244 611 memcpy(&req->server_id, option, sizeof(be32_t));
816e2e7a
TG
612
613 break;
22805d92 614 case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
816e2e7a
TG
615 if (len >= 2) {
616 uint8_t *data;
617
618 data = memdup(option, len);
619 if (!data)
620 return -ENOMEM;
621
622 free(req->client_id.data);
623 req->client_id.data = data;
624 req->client_id.length = len;
625 }
626
627 break;
22805d92 628 case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
76a9d0f1
LP
629
630 if (len == 2 && unaligned_read_be16(option) >= sizeof(DHCPPacket))
9ae84244 631 req->max_optlen = unaligned_read_be16(option) - sizeof(DHCPPacket);
816e2e7a
TG
632
633 break;
634 }
635
636 return 0;
637}
638
639static void dhcp_request_free(DHCPRequest *req) {
640 if (!req)
641 return;
642
643 free(req->client_id.data);
644 free(req);
645}
646
647DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
816e2e7a 648
586ac6f7 649static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) {
816e2e7a
TG
650 assert(req);
651 assert(message);
652
653 req->message = message;
654
e2acdb6b 655 /* set client id based on MAC address if client did not send an explicit
a6f1e036 656 one */
816e2e7a 657 if (!req->client_id.data) {
9a0f246f 658 void *data;
816e2e7a 659
9a0f246f 660 data = malloc0(ETH_ALEN + 1);
816e2e7a
TG
661 if (!data)
662 return -ENOMEM;
663
9a0f246f
LP
664 ((uint8_t*) data)[0] = 0x01;
665 memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN);
666
816e2e7a
TG
667 req->client_id.length = ETH_ALEN + 1;
668 req->client_id.data = data;
816e2e7a
TG
669 }
670
671 if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
672 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
673
9a0f246f 674 if (req->lifetime <= 0)
586ac6f7
LP
675 req->lifetime = MAX(1ULL, server->default_lease_time);
676
677 if (server->max_lease_time > 0 && req->lifetime > server->max_lease_time)
678 req->lifetime = server->max_lease_time;
c7d9ffe6 679
816e2e7a
TG
680 return 0;
681}
682
87322b3a
TG
683static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
684 assert(server);
685
686 if (!server->pool_size)
687 return -EINVAL;
688
99634696
TG
689 if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) ||
690 be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size)))
691 return -ERANGE;
87322b3a 692
99634696 693 return be32toh(requested_ip & ~server->netmask) - server->pool_offset;
87322b3a
TG
694}
695
83cedf7a
TG
696#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
697
be077570
TG
698int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
699 size_t length) {
8e766630 700 _cleanup_(dhcp_request_freep) DHCPRequest *req = NULL;
f693e9b3 701 _cleanup_free_ char *error_message = NULL;
87322b3a 702 DHCPLease *existing_lease;
816e2e7a 703 int type, r;
be077570
TG
704
705 assert(server);
706 assert(message);
707
708 if (message->op != BOOTREQUEST ||
709 message->htype != ARPHRD_ETHER ||
710 message->hlen != ETHER_ADDR_LEN)
711 return 0;
712
816e2e7a
TG
713 req = new0(DHCPRequest, 1);
714 if (!req)
715 return -ENOMEM;
716
f693e9b3 717 type = dhcp_option_parse(message, length, parse_request, req, &error_message);
be077570
TG
718 if (type < 0)
719 return 0;
720
586ac6f7 721 r = ensure_sane_request(server, req, message);
816e2e7a
TG
722 if (r < 0)
723 /* this only fails on critical errors */
724 return r;
725
a6f1e036
TG
726 existing_lease = hashmap_get(server->leases_by_client_id,
727 &req->client_id);
87322b3a 728
4dc35568 729 switch(type) {
9a0f246f
LP
730
731 case DHCP_DISCOVER: {
87322b3a
TG
732 be32_t address = INADDR_ANY;
733 unsigned i;
2dead812 734
4dc35568
TG
735 log_dhcp_server(server, "DISCOVER (0x%x)",
736 be32toh(req->message->xid));
737
2dead812
TG
738 if (!server->pool_size)
739 /* no pool allocated */
740 return 0;
741
87322b3a
TG
742 /* for now pick a random free address from the pool */
743 if (existing_lease)
744 address = existing_lease->address;
745 else {
b826ab58 746 struct siphash state;
0cb3c286 747 uint64_t hash;
99634696 748 uint32_t next_offer;
83cedf7a
TG
749
750 /* even with no persistence of leases, we try to offer the same client
751 the same IP address. we do this by using the hash of the client id
752 as the offset into the pool of leases when finding the next free one */
753
0cb3c286 754 siphash24_init(&state, HASH_KEY.bytes);
b826ab58 755 client_id_hash_func(&req->client_id, &state);
933f9cae 756 hash = htole64(siphash24_finalize(&state));
0cb3c286 757 next_offer = hash % server->pool_size;
83cedf7a 758
87322b3a 759 for (i = 0; i < server->pool_size; i++) {
83cedf7a 760 if (!server->bound_leases[next_offer]) {
99634696 761 address = server->subnet | htobe32(server->pool_offset + next_offer);
87322b3a 762 break;
6408ba5f
LP
763 }
764
765 next_offer = (next_offer + 1) % server->pool_size;
87322b3a
TG
766 }
767 }
768
769 if (address == INADDR_ANY)
770 /* no free addresses left */
771 return 0;
2dead812
TG
772
773 r = server_send_offer(server, req, address);
6e741541 774 if (r < 0)
4dc35568 775 /* this only fails on critical errors */
6e741541 776 return log_dhcp_server_errno(server, r, "Could not send offer: %m");
be077570 777
6e741541
LP
778 log_dhcp_server(server, "OFFER (0x%x)", be32toh(req->message->xid));
779 return DHCP_OFFER;
4dc35568 780 }
5b34277c 781 case DHCP_DECLINE:
f693e9b3 782 log_dhcp_server(server, "DECLINE (0x%x): %s", be32toh(req->message->xid), strna(error_message));
5b34277c
TG
783
784 /* TODO: make sure we don't offer this address again */
785
786 return 1;
787
9a0f246f 788 case DHCP_REQUEST: {
2dead812 789 be32_t address;
bd57b450 790 bool init_reboot = false;
87322b3a 791 int pool_offset;
2dead812
TG
792
793 /* see RFC 2131, section 4.3.2 */
794
795 if (req->server_id) {
796 log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
797 be32toh(req->message->xid));
798
799 /* SELECTING */
800 if (req->server_id != server->address)
801 /* client did not pick us */
802 return 0;
803
804 if (req->message->ciaddr)
805 /* this MUST be zero */
806 return 0;
807
808 if (!req->requested_ip)
809 /* this must be filled in with the yiaddr
810 from the chosen OFFER */
811 return 0;
812
813 address = req->requested_ip;
814 } else if (req->requested_ip) {
815 log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
816 be32toh(req->message->xid));
817
818 /* INIT-REBOOT */
819 if (req->message->ciaddr)
820 /* this MUST be zero */
821 return 0;
822
bd57b450 823 /* TODO: check more carefully if IP is correct */
2dead812 824 address = req->requested_ip;
bd57b450 825 init_reboot = true;
2dead812
TG
826 } else {
827 log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
828 be32toh(req->message->xid));
829
830 /* REBINDING / RENEWING */
831 if (!req->message->ciaddr)
832 /* this MUST be filled in with clients IP address */
833 return 0;
834
835 address = req->message->ciaddr;
836 }
837
87322b3a
TG
838 pool_offset = get_pool_offset(server, address);
839
840 /* verify that the requested address is from the pool, and either
841 owned by the current client or free */
842 if (pool_offset >= 0 &&
843 server->bound_leases[pool_offset] == existing_lease) {
844 DHCPLease *lease;
a7f7d1bd 845 usec_t time_now = 0;
87322b3a
TG
846
847 if (!existing_lease) {
848 lease = new0(DHCPLease, 1);
357e1b17
LP
849 if (!lease)
850 return -ENOMEM;
28c78e07 851 lease->address = address;
87322b3a
TG
852 lease->client_id.data = memdup(req->client_id.data,
853 req->client_id.length);
02557f97
TA
854 if (!lease->client_id.data) {
855 free(lease);
87322b3a 856 return -ENOMEM;
02557f97 857 }
87322b3a 858 lease->client_id.length = req->client_id.length;
a6f1e036
TG
859 memcpy(&lease->chaddr, &req->message->chaddr,
860 ETH_ALEN);
52750344 861 lease->gateway = req->message->giaddr;
87322b3a
TG
862 } else
863 lease = existing_lease;
864
a6f1e036
TG
865 r = sd_event_now(server->event,
866 clock_boottime_or_monotonic(),
867 &time_now);
bff92d2c
SS
868 if (r < 0) {
869 if (!existing_lease)
870 dhcp_lease_free(lease);
38a03f06 871 return r;
bff92d2c
SS
872 }
873
87322b3a
TG
874 lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
875
2dead812
TG
876 r = server_send_ack(server, req, address);
877 if (r < 0) {
878 /* this only fails on critical errors */
6e741541 879 log_dhcp_server_errno(server, r, "Could not send ack: %m");
87322b3a
TG
880
881 if (!existing_lease)
882 dhcp_lease_free(lease);
883
2dead812
TG
884 return r;
885 } else {
886 log_dhcp_server(server, "ACK (0x%x)",
887 be32toh(req->message->xid));
87322b3a
TG
888
889 server->bound_leases[pool_offset] = lease;
a6f1e036
TG
890 hashmap_put(server->leases_by_client_id,
891 &lease->client_id, lease);
87322b3a 892
2dead812
TG
893 return DHCP_ACK;
894 }
6e741541 895
bd57b450
TG
896 } else if (init_reboot) {
897 r = server_send_nak(server, req);
6e741541 898 if (r < 0)
bd57b450 899 /* this only fails on critical errors */
6e741541
LP
900 return log_dhcp_server_errno(server, r, "Could not send nak: %m");
901
902 log_dhcp_server(server, "NAK (0x%x)", be32toh(req->message->xid));
903 return DHCP_NAK;
bd57b450 904 }
2dead812
TG
905
906 break;
907 }
9a0f246f 908
500792d8
TG
909 case DHCP_RELEASE: {
910 int pool_offset;
911
912 log_dhcp_server(server, "RELEASE (0x%x)",
913 be32toh(req->message->xid));
914
915 if (!existing_lease)
916 return 0;
917
918 if (existing_lease->address != req->message->ciaddr)
919 return 0;
920
921 pool_offset = get_pool_offset(server, req->message->ciaddr);
922 if (pool_offset < 0)
923 return 0;
924
925 if (server->bound_leases[pool_offset] == existing_lease) {
926 server->bound_leases[pool_offset] = NULL;
927 hashmap_remove(server->leases_by_client_id, existing_lease);
928 dhcp_lease_free(existing_lease);
c3922c0c 929 }
500792d8 930
c3922c0c
LP
931 return 0;
932 }}
4dc35568
TG
933
934 return 0;
be077570
TG
935}
936
ff734080
TG
937static int server_receive_message(sd_event_source *s, int fd,
938 uint32_t revents, void *userdata) {
be077570 939 _cleanup_free_ DHCPMessage *message = NULL;
3a864fe4 940 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
ff734080
TG
941 sd_dhcp_server *server = userdata;
942 struct iovec iov = {};
943 struct msghdr msg = {
944 .msg_iov = &iov,
945 .msg_iovlen = 1,
3a864fe4
TG
946 .msg_control = cmsgbuf,
947 .msg_controllen = sizeof(cmsgbuf),
ff734080 948 };
3a864fe4 949 struct cmsghdr *cmsg;
4edc2c9b 950 ssize_t buflen, len;
57027d03 951 int r;
ff734080
TG
952
953 assert(server);
954
4edc2c9b
LP
955 buflen = next_datagram_size_fd(fd);
956 if (buflen < 0)
957 return buflen;
ff734080 958
0d43d2fc 959 message = malloc(buflen);
ff734080
TG
960 if (!message)
961 return -ENOMEM;
962
963 iov.iov_base = message;
964 iov.iov_len = buflen;
965
a38d9945 966 len = recvmsg(fd, &msg, 0);
0d43d2fc 967 if (len < 0) {
4c701096 968 if (IN_SET(errno, EAGAIN, EINTR))
0d43d2fc
TG
969 return 0;
970
971 return -errno;
6408ba5f
LP
972 }
973 if ((size_t)len < sizeof(DHCPMessage))
be077570 974 return 0;
ff734080 975
2a1288ff 976 CMSG_FOREACH(cmsg, &msg) {
3a864fe4
TG
977 if (cmsg->cmsg_level == IPPROTO_IP &&
978 cmsg->cmsg_type == IP_PKTINFO &&
979 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
980 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
981
a6f1e036
TG
982 /* TODO figure out if this can be done as a filter on
983 * the socket, like for IPv6 */
b3ec603c 984 if (server->ifindex != info->ipi_ifindex)
3a864fe4
TG
985 return 0;
986
987 break;
988 }
989 }
990
57027d03
LP
991 r = dhcp_server_handle_message(server, message, (size_t) len);
992 if (r < 0)
993 log_dhcp_server_errno(server, r, "Couldn't process incoming message: %m");
994
995 return 0;
ff734080
TG
996}
997
998int sd_dhcp_server_start(sd_dhcp_server *server) {
999 int r;
1000
1001 assert_return(server, -EINVAL);
1002 assert_return(server->event, -EINVAL);
1003 assert_return(!server->receive_message, -EBUSY);
cfcbb135
LP
1004 assert_return(server->fd_raw < 0, -EBUSY);
1005 assert_return(server->fd < 0, -EBUSY);
20af7091 1006 assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
ff734080 1007
8de4a226
TG
1008 r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
1009 if (r < 0) {
1010 r = -errno;
1011 sd_dhcp_server_stop(server);
1012 return r;
1013 }
1014 server->fd_raw = r;
1015
3e7b9f76 1016 r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY, DHCP_PORT_SERVER);
ff734080
TG
1017 if (r < 0) {
1018 sd_dhcp_server_stop(server);
1019 return r;
1020 }
1021 server->fd = r;
1022
1023 r = sd_event_add_io(server->event, &server->receive_message,
1024 server->fd, EPOLLIN,
1025 server_receive_message, server);
1026 if (r < 0) {
1027 sd_dhcp_server_stop(server);
1028 return r;
1029 }
1030
1031 r = sd_event_source_set_priority(server->receive_message,
1032 server->event_priority);
1033 if (r < 0) {
1034 sd_dhcp_server_stop(server);
1035 return r;
1036 }
1037
1038 log_dhcp_server(server, "STARTED");
1039
1040 return 0;
1041}
52750344
TG
1042
1043int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
1044 unsigned i;
621ac3d2 1045 int r = 0;
52750344
TG
1046
1047 assert_return(server, -EINVAL);
1048 assert(server->bound_leases);
1049
1050 for (i = 0; i < server->pool_size; i++) {
1051 DHCPLease *lease = server->bound_leases[i];
1052
99634696 1053 if (!lease || lease == &server->invalid_lease)
52750344
TG
1054 continue;
1055
1056 r = server_send_forcerenew(server, lease->address,
1057 lease->gateway,
1058 lease->chaddr);
1059 if (r < 0)
1060 return r;
6408ba5f
LP
1061
1062 log_dhcp_server(server, "FORCERENEW");
52750344
TG
1063 }
1064
1065 return r;
1066}
8eb9058d 1067
64d6c229 1068int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) {
8eb9058d
LP
1069 int r;
1070
1071 assert_return(server, -EINVAL);
089fb865 1072 assert_return(timezone_is_valid(tz, LOG_DEBUG), -EINVAL);
8eb9058d 1073
64d6c229 1074 if (streq_ptr(tz, server->timezone))
8eb9058d
LP
1075 return 0;
1076
64d6c229 1077 r = free_and_strdup(&server->timezone, tz);
8eb9058d
LP
1078 if (r < 0)
1079 return r;
1080
1081 return 1;
1082}
586ac6f7
LP
1083
1084int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t) {
1085 assert_return(server, -EINVAL);
1086
1087 if (t == server->max_lease_time)
1088 return 0;
1089
1090 server->max_lease_time = t;
1091 return 1;
1092}
1093
1094int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t) {
1095 assert_return(server, -EINVAL);
1096
1097 if (t == server->default_lease_time)
1098 return 0;
1099
1100 server->default_lease_time = t;
1101 return 1;
1102}
1a04db0f
LP
1103
1104int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n) {
1105 assert_return(server, -EINVAL);
1106 assert_return(dns || n <= 0, -EINVAL);
1107
1108 if (server->n_dns == n &&
1109 memcmp(server->dns, dns, sizeof(struct in_addr) * n) == 0)
1110 return 0;
1111
1112 if (n <= 0) {
1113 server->dns = mfree(server->dns);
1114 server->n_dns = 0;
1115 } else {
1116 struct in_addr *c;
1117
1118 c = newdup(struct in_addr, dns, n);
1119 if (!c)
1120 return -ENOMEM;
1121
1122 free(server->dns);
1123 server->dns = c;
1124 server->n_dns = n;
1125 }
1126
1127 return 1;
1128}
1129
1130int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n) {
1131 assert_return(server, -EINVAL);
1132 assert_return(ntp || n <= 0, -EINVAL);
1133
1134 if (server->n_ntp == n &&
1135 memcmp(server->ntp, ntp, sizeof(struct in_addr) * n) == 0)
1136 return 0;
1137
1138 if (n <= 0) {
1139 server->ntp = mfree(server->ntp);
1140 server->n_ntp = 0;
1141 } else {
1142 struct in_addr *c;
1143
1144 c = newdup(struct in_addr, ntp, n);
1145 if (!c)
1146 return -ENOMEM;
1147
1148 free(server->ntp);
1149 server->ntp = c;
1150 server->n_ntp = n;
1151 }
1152
1153 return 1;
1154}
77ff6022
CG
1155
1156int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled) {
1157 assert_return(server, -EINVAL);
1158
1159 if (enabled == server->emit_router)
1160 return 0;
1161
1162 server->emit_router = enabled;
1163
1164 return 1;
1165}