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