]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-server.c
sd-dhcp6-client: make gcc happy
[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 23#include <sys/ioctl.h>
be077570 24#include <netinet/if_ether.h>
ff734080 25
87322b3a
TG
26#include "siphash24.h"
27
b44cd882
TG
28#include "sd-dhcp-server.h"
29#include "dhcp-server-internal.h"
ff734080 30#include "dhcp-internal.h"
b44cd882 31
d122f9ee 32#define DHCP_DEFAULT_LEASE_TIME 3600 /* one hour */
4dc35568 33
2dead812
TG
34int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, struct in_addr *address,
35 size_t size) {
36 assert_return(server, -EINVAL);
37 assert_return(address, -EINVAL);
38 assert_return(address->s_addr, -EINVAL);
39 assert_return(size, -EINVAL);
40 assert_return(server->pool_start == htobe32(INADDR_ANY), -EBUSY);
41 assert_return(!server->pool_size, -EBUSY);
87322b3a
TG
42 assert_return(!server->bound_leases, -EBUSY);
43
44 server->bound_leases = new0(DHCPLease*, size);
45 if (!server->bound_leases)
46 return -ENOMEM;
2dead812
TG
47
48 server->pool_start = address->s_addr;
49 server->pool_size = size;
50
51 return 0;
52}
53
20af7091
TG
54int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address) {
55 assert_return(server, -EINVAL);
56 assert_return(address, -EINVAL);
57 assert_return(address->s_addr, -EINVAL);
58 assert_return(server->address == htobe32(INADDR_ANY), -EBUSY);
59
60 server->address = address->s_addr;
61
62 return 0;
63}
64
7c16313f
TG
65bool sd_dhcp_server_is_running(sd_dhcp_server *server) {
66 assert_return(server, -EINVAL);
67
68 return !!server->receive_message;
69}
70
b44cd882
TG
71sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
72 if (server)
73 assert_se(REFCNT_INC(server->n_ref) >= 2);
74
75 return server;
76}
77
87322b3a
TG
78unsigned long client_id_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
79 uint64_t u;
80 const DHCPClientId *id = p;
81
82 assert(id);
83 assert(id->length);
84 assert(id->data);
85
86 siphash24((uint8_t*) &u, id->data, id->length, hash_key);
87
88 return (unsigned long) u;
89}
90
91int client_id_compare_func(const void *_a, const void *_b) {
92 const DHCPClientId *a, *b;
93
94 a = _a;
95 b = _b;
96
97 assert(!a->length || a->data);
98 assert(!b->length || b->data);
99
100 if (a->length != b->length)
101 return a->length < b->length ? -1 : 1;
102
103 return memcmp(a->data, b->data, a->length);
104}
105
106static void dhcp_lease_free(DHCPLease *lease) {
107 if (!lease)
108 return;
109
110 free(lease->client_id.data);
111 free(lease);
112}
113
b44cd882 114sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
3bdace9b 115 DHCPLease *lease;
87322b3a 116
3bdace9b
LP
117 if (!server)
118 return NULL;
b44cd882 119
3bdace9b
LP
120 if (REFCNT_DEC(server->n_ref) > 0)
121 return NULL;
ff734080 122
3bdace9b 123 log_dhcp_server(server, "UNREF");
87322b3a 124
3bdace9b 125 sd_dhcp_server_stop(server);
87322b3a 126
3bdace9b
LP
127 sd_event_unref(server->event);
128
129 while ((lease = hashmap_steal_first(server->leases_by_client_id)))
130 dhcp_lease_free(lease);
131 hashmap_free(server->leases_by_client_id);
132
133 free(server->bound_leases);
134 free(server);
b44cd882
TG
135
136 return NULL;
137}
138
3a864fe4 139int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
b44cd882
TG
140 _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
141
142 assert_return(ret, -EINVAL);
3a864fe4 143 assert_return(ifindex > 0, -EINVAL);
b44cd882
TG
144
145 server = new0(sd_dhcp_server, 1);
146 if (!server)
147 return -ENOMEM;
148
149 server->n_ref = REFCNT_INIT;
8de4a226 150 server->fd_raw = -1;
ff734080 151 server->fd = -1;
20af7091 152 server->address = htobe32(INADDR_ANY);
3a864fe4 153 server->index = ifindex;
87322b3a 154 server->leases_by_client_id = hashmap_new(client_id_hash_func, client_id_compare_func);
b44cd882
TG
155
156 *ret = server;
157 server = NULL;
158
159 return 0;
160}
161
162int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int priority) {
163 int r;
164
165 assert_return(server, -EINVAL);
166 assert_return(!server->event, -EBUSY);
167
168 if (event)
169 server->event = sd_event_ref(event);
170 else {
171 r = sd_event_default(&server->event);
172 if (r < 0)
173 return r;
174 }
175
176 server->event_priority = priority;
177
178 return 0;
179}
180
181int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
182 assert_return(server, -EINVAL);
183
184 server->event = sd_event_unref(server->event);
185
186 return 0;
187}
188
189sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
190 assert_return(server, NULL);
191
192 return server->event;
193}
ff734080
TG
194
195int sd_dhcp_server_stop(sd_dhcp_server *server) {
196 assert_return(server, -EINVAL);
197
198 server->receive_message =
199 sd_event_source_unref(server->receive_message);
200
8de4a226 201 server->fd_raw = safe_close(server->fd_raw);
ff734080
TG
202 server->fd = safe_close(server->fd);
203
204 log_dhcp_server(server, "STOPPED");
205
206 return 0;
207}
208
969b009d
TG
209static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, DHCPPacket *packet,
210 size_t len) {
211 union sockaddr_union link = {
212 .ll.sll_family = AF_PACKET,
213 .ll.sll_protocol = htons(ETH_P_IP),
214 .ll.sll_ifindex = server->index,
215 .ll.sll_halen = ETH_ALEN,
216 };
217 int r;
218
219 assert(server);
220 assert(server->index > 0);
221 assert(server->address);
222 assert(packet);
223 assert(len > sizeof(DHCPPacket));
224
225 memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
226
227 dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
228 packet->dhcp.yiaddr, DHCP_PORT_CLIENT, len);
229
230 r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
231 if (r < 0)
232 return r;
233
234 return 0;
235}
236
237static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
238 DHCPMessage *message, size_t len) {
239 union sockaddr_union dest = {
240 .in.sin_family = AF_INET,
241 .in.sin_port = htobe16(DHCP_PORT_CLIENT),
242 .in.sin_addr.s_addr = destination,
243 };
244 struct iovec iov = {
245 .iov_base = message,
246 .iov_len = len,
247 };
248 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
249 struct msghdr msg = {
250 .msg_name = &dest,
251 .msg_namelen = sizeof(dest.in),
252 .msg_iov = &iov,
253 .msg_iovlen = 1,
254 .msg_control = cmsgbuf,
255 .msg_controllen = sizeof(cmsgbuf),
256 };
257 struct cmsghdr *cmsg;
258 struct in_pktinfo *pktinfo;
259 int r;
260
261 assert(server);
262 assert(server->fd > 0);
263 assert(message);
264 assert(len > sizeof(DHCPMessage));
265
266 cmsg = CMSG_FIRSTHDR(&msg);
267 assert(cmsg);
268
269 cmsg->cmsg_level = IPPROTO_IP;
270 cmsg->cmsg_type = IP_PKTINFO;
271 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
272
273 /* we attach source interface and address info to the message
274 rather than binding the socket. This will be mostly useful
275 when we gain support for arbitrary number of server addresses
276 */
277 pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
278 assert(pktinfo);
279
280 pktinfo->ipi_ifindex = server->index;
281 pktinfo->ipi_spec_dst.s_addr = server->address;
282
283 r = sendmsg(server->fd, &msg, 0);
284 if (r < 0)
285 return -errno;
286
287 return 0;
288}
289
290static bool requested_broadcast(DHCPRequest *req) {
291 assert(req);
292
293 return req->message->flags & htobe16(0x8000);
294}
295
296int dhcp_server_send_packet(sd_dhcp_server *server,
297 DHCPRequest *req, DHCPPacket *packet,
298 int type, size_t optoffset) {
299 be32_t destination = INADDR_ANY;
300 int r;
301
302 assert(server);
303 assert(req);
304 assert(req->max_optlen);
305 assert(optoffset <= req->max_optlen);
306 assert(packet);
307
308 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
309 DHCP_OPTION_SERVER_IDENTIFIER,
310 4, &server->address);
311 if (r < 0)
312 return r;
313
314 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
315 DHCP_OPTION_END, 0, NULL);
316 if (r < 0)
317 return r;
318
319 /* RFC 2131 Section 4.1
320
321 If the ’giaddr’ field in a DHCP message from a client is non-zero,
322 the server sends any return messages to the ’DHCP server’ port on the
323 BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
324 field is zero and the ’ciaddr’ field is nonzero, then the server
325 unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
326 If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
327 set, then the server broadcasts DHCPOFFER and DHCPACK messages to
328 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
329 ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
330 messages to the client’s hardware address and ’yiaddr’ address. In
331 all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
332 messages to 0xffffffff.
333
334 Section 4.3.2
335
336 If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
337 different subnet. The server MUST set the broadcast bit in the
338 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
339 client, because the client may not have a correct network address
340 or subnet mask, and the client may not be answering ARP requests.
341 */
342 if (req->message->giaddr) {
343 destination = req->message->giaddr;
344 if (type == DHCP_NAK)
345 packet->dhcp.flags = htobe16(0x8000);
346 } else if (req->message->ciaddr && type != DHCP_NAK)
347 destination = req->message->ciaddr;
348
d6bd972d 349 if (destination != INADDR_ANY)
969b009d
TG
350 return dhcp_server_send_udp(server, destination, &packet->dhcp,
351 sizeof(DHCPMessage) + optoffset);
d6bd972d
TG
352 else if (requested_broadcast(req) || type == DHCP_NAK)
353 return dhcp_server_send_udp(server, INADDR_BROADCAST, &packet->dhcp,
354 sizeof(DHCPMessage) + optoffset);
969b009d
TG
355 else
356 /* we cannot send UDP packet to specific MAC address when the address is
357 not yet configured, so must fall back to raw packets */
358 return dhcp_server_send_unicast_raw(server, packet,
359 sizeof(DHCPPacket) + optoffset);
360}
361
4dc35568
TG
362static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
363 uint8_t type, size_t *_optoffset, DHCPRequest *req) {
364 _cleanup_free_ DHCPPacket *packet = NULL;
365 size_t optoffset;
366 int r;
367
368 assert(server);
369 assert(ret);
370 assert(_optoffset);
bd57b450 371 assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
4dc35568
TG
372
373 packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
374 if (!packet)
375 return -ENOMEM;
376
377 r = dhcp_message_init(&packet->dhcp, BOOTREPLY, be32toh(req->message->xid),
378 type, req->max_optlen, &optoffset);
379 if (r < 0)
380 return r;
381
382 packet->dhcp.flags = req->message->flags;
383 packet->dhcp.giaddr = req->message->giaddr;
384 memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
385
386 *_optoffset = optoffset;
387 *ret = packet;
388 packet = NULL;
389
390 return 0;
391}
392
2dead812 393static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
4dc35568
TG
394 _cleanup_free_ DHCPPacket *packet = NULL;
395 size_t offset;
396 be32_t lease_time;
397 int r;
398
399 r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
400 if (r < 0)
401 return r;
402
2dead812 403 packet->dhcp.yiaddr = address;
4dc35568 404
c7d9ffe6 405 lease_time = htobe32(req->lifetime);
4dc35568
TG
406 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
407 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
408 if (r < 0)
409 return r;
410
411 r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
412 if (r < 0)
413 return r;
414
415 return 0;
416}
417
2dead812
TG
418static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
419 _cleanup_free_ DHCPPacket *packet = NULL;
420 size_t offset;
421 be32_t lease_time;
422 int r;
423
424 r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
425 if (r < 0)
426 return r;
427
428 packet->dhcp.yiaddr = address;
429
c7d9ffe6 430 lease_time = htobe32(req->lifetime);
2dead812
TG
431 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
432 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
433 if (r < 0)
434 return r;
435
436 r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
437 if (r < 0)
438 return r;
439
440 return 0;
441}
442
bd57b450
TG
443static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
444 _cleanup_free_ DHCPPacket *packet = NULL;
445 size_t offset;
446 int r;
447
448 r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
449 if (r < 0)
450 return r;
451
452 r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
453 if (r < 0)
454 return r;
455
456 return 0;
457}
458
816e2e7a
TG
459static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
460 void *user_data) {
461 DHCPRequest *req = user_data;
462
463 assert(req);
464
465 switch(code) {
c7d9ffe6
TG
466 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
467 if (len == 4)
468 req->lifetime = be32toh(*(be32_t*)option);
469
470 break;
2dead812
TG
471 case DHCP_OPTION_REQUESTED_IP_ADDRESS:
472 if (len == 4)
473 req->requested_ip = *(be32_t*)option;
474
475 break;
816e2e7a
TG
476 case DHCP_OPTION_SERVER_IDENTIFIER:
477 if (len == 4)
478 req->server_id = *(be32_t*)option;
479
480 break;
481 case DHCP_OPTION_CLIENT_IDENTIFIER:
482 if (len >= 2) {
483 uint8_t *data;
484
485 data = memdup(option, len);
486 if (!data)
487 return -ENOMEM;
488
489 free(req->client_id.data);
490 req->client_id.data = data;
491 req->client_id.length = len;
492 }
493
494 break;
495 case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
496 if (len == 2)
497 req->max_optlen = be16toh(*(be16_t*)option) -
498 - sizeof(DHCPPacket);
499
500 break;
501 }
502
503 return 0;
504}
505
506static void dhcp_request_free(DHCPRequest *req) {
507 if (!req)
508 return;
509
510 free(req->client_id.data);
511 free(req);
512}
513
514DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
515#define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
516
517static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
518 assert(req);
519 assert(message);
520
521 req->message = message;
522
523 /* set client id based on mac address if client did not send an explicit one */
524 if (!req->client_id.data) {
525 uint8_t *data;
526
527 data = new0(uint8_t, ETH_ALEN + 1);
528 if (!data)
529 return -ENOMEM;
530
531 req->client_id.length = ETH_ALEN + 1;
532 req->client_id.data = data;
533 req->client_id.data[0] = 0x01;
534 memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
535 }
536
537 if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
538 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
539
c7d9ffe6
TG
540 if (!req->lifetime)
541 req->lifetime = DHCP_DEFAULT_LEASE_TIME;
542
816e2e7a
TG
543 return 0;
544}
545
87322b3a
TG
546static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
547 assert(server);
548
549 if (!server->pool_size)
550 return -EINVAL;
551
552 if (be32toh(requested_ip) < be32toh(server->pool_start) ||
553 be32toh(requested_ip) >= be32toh(server->pool_start) +
500792d8 554 + server->pool_size)
87322b3a
TG
555 return -EINVAL;
556
557 return be32toh(requested_ip) - be32toh(server->pool_start);
558}
559
be077570
TG
560int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
561 size_t length) {
816e2e7a 562 _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
87322b3a 563 DHCPLease *existing_lease;
816e2e7a 564 int type, r;
be077570
TG
565
566 assert(server);
567 assert(message);
568
569 if (message->op != BOOTREQUEST ||
570 message->htype != ARPHRD_ETHER ||
571 message->hlen != ETHER_ADDR_LEN)
572 return 0;
573
816e2e7a
TG
574 req = new0(DHCPRequest, 1);
575 if (!req)
576 return -ENOMEM;
577
578 type = dhcp_option_parse(message, length, parse_request, req);
be077570
TG
579 if (type < 0)
580 return 0;
581
816e2e7a
TG
582 r = ensure_sane_request(req, message);
583 if (r < 0)
584 /* this only fails on critical errors */
585 return r;
586
87322b3a
TG
587 existing_lease = hashmap_get(server->leases_by_client_id, &req->client_id);
588
4dc35568
TG
589 switch(type) {
590 case DHCP_DISCOVER:
2dead812 591 {
87322b3a
TG
592 be32_t address = INADDR_ANY;
593 unsigned i;
2dead812 594
4dc35568
TG
595 log_dhcp_server(server, "DISCOVER (0x%x)",
596 be32toh(req->message->xid));
597
2dead812
TG
598 if (!server->pool_size)
599 /* no pool allocated */
600 return 0;
601
87322b3a
TG
602 /* for now pick a random free address from the pool */
603 if (existing_lease)
604 address = existing_lease->address;
605 else {
606 for (i = 0; i < server->pool_size; i++) {
607 if (!server->bound_leases[server->next_offer]) {
608 address = htobe32(be32toh(server->pool_start) + server->next_offer);
609 break;
610 } else
611 server->next_offer = (server->next_offer + 1) % server->pool_size;
612 }
613 }
614
615 if (address == INADDR_ANY)
616 /* no free addresses left */
617 return 0;
2dead812
TG
618
619 r = server_send_offer(server, req, address);
4dc35568
TG
620 if (r < 0) {
621 /* this only fails on critical errors */
622 log_dhcp_server(server, "could not send offer: %s",
623 strerror(-r));
624 return r;
625 } else {
626 log_dhcp_server(server, "OFFER (0x%x)",
627 be32toh(req->message->xid));
628 return DHCP_OFFER;
629 }
be077570 630
4dc35568
TG
631 break;
632 }
5b34277c
TG
633 case DHCP_DECLINE:
634 log_dhcp_server(server, "DECLINE (0x%x)",
635 be32toh(req->message->xid));
636
637 /* TODO: make sure we don't offer this address again */
638
639 return 1;
640
641 break;
2dead812
TG
642 case DHCP_REQUEST:
643 {
644 be32_t address;
bd57b450 645 bool init_reboot = false;
87322b3a 646 int pool_offset;
2dead812
TG
647
648 /* see RFC 2131, section 4.3.2 */
649
650 if (req->server_id) {
651 log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
652 be32toh(req->message->xid));
653
654 /* SELECTING */
655 if (req->server_id != server->address)
656 /* client did not pick us */
657 return 0;
658
659 if (req->message->ciaddr)
660 /* this MUST be zero */
661 return 0;
662
663 if (!req->requested_ip)
664 /* this must be filled in with the yiaddr
665 from the chosen OFFER */
666 return 0;
667
668 address = req->requested_ip;
669 } else if (req->requested_ip) {
670 log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
671 be32toh(req->message->xid));
672
673 /* INIT-REBOOT */
674 if (req->message->ciaddr)
675 /* this MUST be zero */
676 return 0;
677
bd57b450 678 /* TODO: check more carefully if IP is correct */
2dead812 679 address = req->requested_ip;
bd57b450 680 init_reboot = true;
2dead812
TG
681 } else {
682 log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
683 be32toh(req->message->xid));
684
685 /* REBINDING / RENEWING */
686 if (!req->message->ciaddr)
687 /* this MUST be filled in with clients IP address */
688 return 0;
689
690 address = req->message->ciaddr;
691 }
692
87322b3a
TG
693 pool_offset = get_pool_offset(server, address);
694
695 /* verify that the requested address is from the pool, and either
696 owned by the current client or free */
697 if (pool_offset >= 0 &&
698 server->bound_leases[pool_offset] == existing_lease) {
699 DHCPLease *lease;
700 usec_t time_now;
701
702 if (!existing_lease) {
703 lease = new0(DHCPLease, 1);
704 lease->address = req->requested_ip;
705 lease->client_id.data = memdup(req->client_id.data,
706 req->client_id.length);
02557f97
TA
707 if (!lease->client_id.data) {
708 free(lease);
87322b3a 709 return -ENOMEM;
02557f97 710 }
87322b3a
TG
711 lease->client_id.length = req->client_id.length;
712 } else
713 lease = existing_lease;
714
715 r = sd_event_now(server->event, CLOCK_MONOTONIC, &time_now);
716 if (r < 0)
717 time_now = now(CLOCK_MONOTONIC);
718 lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
719
2dead812
TG
720 r = server_send_ack(server, req, address);
721 if (r < 0) {
722 /* this only fails on critical errors */
723 log_dhcp_server(server, "could not send ack: %s",
724 strerror(-r));
87322b3a
TG
725
726 if (!existing_lease)
727 dhcp_lease_free(lease);
728
2dead812
TG
729 return r;
730 } else {
731 log_dhcp_server(server, "ACK (0x%x)",
732 be32toh(req->message->xid));
87322b3a
TG
733
734 server->bound_leases[pool_offset] = lease;
735 hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
736
2dead812
TG
737 return DHCP_ACK;
738 }
bd57b450
TG
739 } else if (init_reboot) {
740 r = server_send_nak(server, req);
741 if (r < 0) {
742 /* this only fails on critical errors */
743 log_dhcp_server(server, "could not send nak: %s",
744 strerror(-r));
745 return r;
746 } else {
747 log_dhcp_server(server, "NAK (0x%x)",
748 be32toh(req->message->xid));
749 return DHCP_NAK;
750 }
751 }
2dead812
TG
752
753 break;
754 }
500792d8
TG
755 case DHCP_RELEASE: {
756 int pool_offset;
757
758 log_dhcp_server(server, "RELEASE (0x%x)",
759 be32toh(req->message->xid));
760
761 if (!existing_lease)
762 return 0;
763
764 if (existing_lease->address != req->message->ciaddr)
765 return 0;
766
767 pool_offset = get_pool_offset(server, req->message->ciaddr);
768 if (pool_offset < 0)
769 return 0;
770
771 if (server->bound_leases[pool_offset] == existing_lease) {
772 server->bound_leases[pool_offset] = NULL;
773 hashmap_remove(server->leases_by_client_id, existing_lease);
774 dhcp_lease_free(existing_lease);
775
776 return 1;
777 } else
778 return 0;
779 }
2dead812 780 }
4dc35568
TG
781
782 return 0;
be077570
TG
783}
784
ff734080
TG
785static int server_receive_message(sd_event_source *s, int fd,
786 uint32_t revents, void *userdata) {
be077570 787 _cleanup_free_ DHCPMessage *message = NULL;
3a864fe4 788 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
ff734080
TG
789 sd_dhcp_server *server = userdata;
790 struct iovec iov = {};
791 struct msghdr msg = {
792 .msg_iov = &iov,
793 .msg_iovlen = 1,
3a864fe4
TG
794 .msg_control = cmsgbuf,
795 .msg_controllen = sizeof(cmsgbuf),
ff734080 796 };
3a864fe4 797 struct cmsghdr *cmsg;
ff734080
TG
798 int buflen = 0, len, r;
799
800 assert(server);
801
802 r = ioctl(fd, FIONREAD, &buflen);
803 if (r < 0)
804 return r;
805 if (buflen < 0)
806 return -EIO;
807
808 message = malloc0(buflen);
809 if (!message)
810 return -ENOMEM;
811
812 iov.iov_base = message;
813 iov.iov_len = buflen;
814
815 len = recvmsg(fd, &msg, 0);
816 if (len < buflen)
817 return 0;
be077570
TG
818 else if ((size_t)len < sizeof(DHCPMessage))
819 return 0;
ff734080 820
3a864fe4
TG
821 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
822 if (cmsg->cmsg_level == IPPROTO_IP &&
823 cmsg->cmsg_type == IP_PKTINFO &&
824 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
825 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
826
827 /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
828 if (server->index != info->ipi_ifindex)
829 return 0;
830
831 break;
832 }
833 }
834
be077570 835 return dhcp_server_handle_message(server, message, (size_t)len);
ff734080
TG
836}
837
838int sd_dhcp_server_start(sd_dhcp_server *server) {
839 int r;
840
841 assert_return(server, -EINVAL);
842 assert_return(server->event, -EINVAL);
843 assert_return(!server->receive_message, -EBUSY);
8de4a226 844 assert_return(server->fd_raw == -1, -EBUSY);
ff734080 845 assert_return(server->fd == -1, -EBUSY);
20af7091 846 assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
ff734080 847
8de4a226
TG
848 r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
849 if (r < 0) {
850 r = -errno;
851 sd_dhcp_server_stop(server);
852 return r;
853 }
854 server->fd_raw = r;
855
ff734080
TG
856 r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
857 if (r < 0) {
858 sd_dhcp_server_stop(server);
859 return r;
860 }
861 server->fd = r;
862
863 r = sd_event_add_io(server->event, &server->receive_message,
864 server->fd, EPOLLIN,
865 server_receive_message, server);
866 if (r < 0) {
867 sd_dhcp_server_stop(server);
868 return r;
869 }
870
871 r = sd_event_source_set_priority(server->receive_message,
872 server->event_priority);
873 if (r < 0) {
874 sd_dhcp_server_stop(server);
875 return r;
876 }
877
878 log_dhcp_server(server, "STARTED");
879
880 return 0;
881}