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