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