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