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