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