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