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