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