1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2013 Intel Corporation. All rights reserved.
7 Copyright (C) 2014 Tom Gundersen
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.
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.
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/>.
23 #include <sys/ioctl.h>
24 #include <netinet/if_ether.h>
26 #include "siphash24.h"
28 #include "sd-dhcp-server.h"
29 #include "dhcp-server-internal.h"
30 #include "dhcp-internal.h"
32 #define DHCP_DEFAULT_LEASE_TIME 60
34 int sd_dhcp_server_set_lease_pool(sd_dhcp_server
*server
, struct in_addr
*address
,
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
);
42 assert_return(!server
->bound_leases
, -EBUSY
);
44 server
->bound_leases
= new0(DHCPLease
*, size
);
45 if (!server
->bound_leases
)
48 server
->pool_start
= address
->s_addr
;
49 server
->pool_size
= size
;
54 int 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
);
60 server
->address
= address
->s_addr
;
65 sd_dhcp_server
*sd_dhcp_server_ref(sd_dhcp_server
*server
) {
67 assert_se(REFCNT_INC(server
->n_ref
) >= 2);
72 unsigned long client_id_hash_func(const void *p
, const uint8_t hash_key
[HASH_KEY_SIZE
]) {
74 const DHCPClientId
*id
= p
;
80 siphash24((uint8_t*) &u
, id
->data
, id
->length
, hash_key
);
82 return (unsigned long) u
;
85 int client_id_compare_func(const void *_a
, const void *_b
) {
86 const DHCPClientId
*a
, *b
;
91 assert(!a
->length
|| a
->data
);
92 assert(!b
->length
|| b
->data
);
94 if (a
->length
!= b
->length
)
95 return a
->length
< b
->length
? -1 : 1;
97 return memcmp(a
->data
, b
->data
, a
->length
);
100 static void dhcp_lease_free(DHCPLease
*lease
) {
104 free(lease
->client_id
.data
);
108 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPLease
*, dhcp_lease_free
);
109 #define _cleanup_dhcp_lease_free_ _cleanup_(dhcp_lease_freep)
111 sd_dhcp_server
*sd_dhcp_server_unref(sd_dhcp_server
*server
) {
112 if (server
&& REFCNT_DEC(server
->n_ref
) <= 0) {
116 log_dhcp_server(server
, "UNREF");
118 sd_dhcp_server_stop(server
);
120 sd_event_unref(server
->event
);
122 HASHMAP_FOREACH(lease
, server
->leases_by_client_id
, i
) {
123 hashmap_remove(server
->leases_by_client_id
, lease
);
124 dhcp_lease_free(lease
);
127 hashmap_free(server
->leases_by_client_id
);
128 free(server
->bound_leases
);
135 int sd_dhcp_server_new(sd_dhcp_server
**ret
, int ifindex
) {
136 _cleanup_dhcp_server_unref_ sd_dhcp_server
*server
= NULL
;
138 assert_return(ret
, -EINVAL
);
139 assert_return(ifindex
> 0, -EINVAL
);
141 server
= new0(sd_dhcp_server
, 1);
145 server
->n_ref
= REFCNT_INIT
;
148 server
->address
= htobe32(INADDR_ANY
);
149 server
->index
= ifindex
;
150 server
->leases_by_client_id
= hashmap_new(client_id_hash_func
, client_id_compare_func
);
158 int sd_dhcp_server_attach_event(sd_dhcp_server
*server
, sd_event
*event
, int priority
) {
161 assert_return(server
, -EINVAL
);
162 assert_return(!server
->event
, -EBUSY
);
165 server
->event
= sd_event_ref(event
);
167 r
= sd_event_default(&server
->event
);
172 server
->event_priority
= priority
;
177 int sd_dhcp_server_detach_event(sd_dhcp_server
*server
) {
178 assert_return(server
, -EINVAL
);
180 server
->event
= sd_event_unref(server
->event
);
185 sd_event
*sd_dhcp_server_get_event(sd_dhcp_server
*server
) {
186 assert_return(server
, NULL
);
188 return server
->event
;
191 int sd_dhcp_server_stop(sd_dhcp_server
*server
) {
192 assert_return(server
, -EINVAL
);
194 server
->receive_message
=
195 sd_event_source_unref(server
->receive_message
);
197 server
->fd_raw
= safe_close(server
->fd_raw
);
198 server
->fd
= safe_close(server
->fd
);
200 log_dhcp_server(server
, "STOPPED");
205 static int dhcp_server_send_unicast_raw(sd_dhcp_server
*server
, DHCPPacket
*packet
,
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
,
216 assert(server
->index
> 0);
217 assert(server
->address
);
219 assert(len
> sizeof(DHCPPacket
));
221 memcpy(&link
.ll
.sll_addr
, &packet
->dhcp
.chaddr
, ETH_ALEN
);
223 dhcp_packet_append_ip_headers(packet
, server
->address
, DHCP_PORT_SERVER
,
224 packet
->dhcp
.yiaddr
, DHCP_PORT_CLIENT
, len
);
226 r
= dhcp_network_send_raw_socket(server
->fd_raw
, &link
, packet
, len
);
233 static 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
,
244 uint8_t cmsgbuf
[CMSG_LEN(sizeof(struct in_pktinfo
))] = {};
245 struct msghdr msg
= {
247 .msg_namelen
= sizeof(dest
.in
),
250 .msg_control
= cmsgbuf
,
251 .msg_controllen
= sizeof(cmsgbuf
),
253 struct cmsghdr
*cmsg
;
254 struct in_pktinfo
*pktinfo
;
258 assert(server
->fd
> 0);
260 assert(len
> sizeof(DHCPMessage
));
262 cmsg
= CMSG_FIRSTHDR(&msg
);
265 cmsg
->cmsg_level
= IPPROTO_IP
;
266 cmsg
->cmsg_type
= IP_PKTINFO
;
267 cmsg
->cmsg_len
= CMSG_LEN(sizeof(struct in_pktinfo
));
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
273 pktinfo
= (struct in_pktinfo
*) CMSG_DATA(cmsg
);
276 pktinfo
->ipi_ifindex
= server
->index
;
277 pktinfo
->ipi_spec_dst
.s_addr
= server
->address
;
279 r
= sendmsg(server
->fd
, &msg
, 0);
286 static bool requested_broadcast(DHCPRequest
*req
) {
289 return req
->message
->flags
& htobe16(0x8000);
292 int 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
;
300 assert(req
->max_optlen
);
301 assert(optoffset
<= req
->max_optlen
);
304 r
= dhcp_option_append(&packet
->dhcp
, req
->max_optlen
, &optoffset
, 0,
305 DHCP_OPTION_SERVER_IDENTIFIER
,
306 4, &server
->address
);
310 r
= dhcp_option_append(&packet
->dhcp
, req
->max_optlen
, &optoffset
, 0,
311 DHCP_OPTION_END
, 0, NULL
);
315 /* RFC 2131 Section 4.1
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.
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.
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
;
345 if (destination
|| requested_broadcast(req
) || type
== DHCP_NAK
)
346 return dhcp_server_send_udp(server
, destination
, &packet
->dhcp
,
347 sizeof(DHCPMessage
) + optoffset
);
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
);
355 static 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
;
364 assert(IN_SET(type
, DHCP_OFFER
, DHCP_ACK
, DHCP_NAK
));
366 packet
= malloc0(sizeof(DHCPPacket
) + req
->max_optlen
);
370 r
= dhcp_message_init(&packet
->dhcp
, BOOTREPLY
, be32toh(req
->message
->xid
),
371 type
, req
->max_optlen
, &optoffset
);
375 packet
->dhcp
.flags
= req
->message
->flags
;
376 packet
->dhcp
.giaddr
= req
->message
->giaddr
;
377 memcpy(&packet
->dhcp
.chaddr
, &req
->message
->chaddr
, ETH_ALEN
);
379 *_optoffset
= optoffset
;
386 static int server_send_offer(sd_dhcp_server
*server
, DHCPRequest
*req
, be32_t address
) {
387 _cleanup_free_ DHCPPacket
*packet
= NULL
;
392 r
= server_message_init(server
, &packet
, DHCP_OFFER
, &offset
, req
);
396 packet
->dhcp
.yiaddr
= address
;
398 lease_time
= htobe32(req
->lifetime
);
399 r
= dhcp_option_append(&packet
->dhcp
, req
->max_optlen
, &offset
, 0,
400 DHCP_OPTION_IP_ADDRESS_LEASE_TIME
, 4, &lease_time
);
404 r
= dhcp_server_send_packet(server
, req
, packet
, DHCP_OFFER
, offset
);
411 static int server_send_ack(sd_dhcp_server
*server
, DHCPRequest
*req
, be32_t address
) {
412 _cleanup_free_ DHCPPacket
*packet
= NULL
;
417 r
= server_message_init(server
, &packet
, DHCP_ACK
, &offset
, req
);
421 packet
->dhcp
.yiaddr
= address
;
423 lease_time
= htobe32(req
->lifetime
);
424 r
= dhcp_option_append(&packet
->dhcp
, req
->max_optlen
, &offset
, 0,
425 DHCP_OPTION_IP_ADDRESS_LEASE_TIME
, 4, &lease_time
);
429 r
= dhcp_server_send_packet(server
, req
, packet
, DHCP_ACK
, offset
);
436 static int server_send_nak(sd_dhcp_server
*server
, DHCPRequest
*req
) {
437 _cleanup_free_ DHCPPacket
*packet
= NULL
;
441 r
= server_message_init(server
, &packet
, DHCP_NAK
, &offset
, req
);
445 r
= dhcp_server_send_packet(server
, req
, packet
, DHCP_NAK
, offset
);
452 static int parse_request(uint8_t code
, uint8_t len
, const uint8_t *option
,
454 DHCPRequest
*req
= user_data
;
459 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME
:
461 req
->lifetime
= be32toh(*(be32_t
*)option
);
464 case DHCP_OPTION_REQUESTED_IP_ADDRESS
:
466 req
->requested_ip
= *(be32_t
*)option
;
469 case DHCP_OPTION_SERVER_IDENTIFIER
:
471 req
->server_id
= *(be32_t
*)option
;
474 case DHCP_OPTION_CLIENT_IDENTIFIER
:
478 data
= memdup(option
, len
);
482 free(req
->client_id
.data
);
483 req
->client_id
.data
= data
;
484 req
->client_id
.length
= len
;
488 case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE
:
490 req
->max_optlen
= be16toh(*(be16_t
*)option
) -
491 - sizeof(DHCPPacket
);
499 static void dhcp_request_free(DHCPRequest
*req
) {
503 free(req
->client_id
.data
);
507 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest
*, dhcp_request_free
);
508 #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
510 static int ensure_sane_request(DHCPRequest
*req
, DHCPMessage
*message
) {
514 req
->message
= message
;
516 /* set client id based on mac address if client did not send an explicit one */
517 if (!req
->client_id
.data
) {
520 data
= new0(uint8_t, ETH_ALEN
+ 1);
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
);
530 if (req
->max_optlen
< DHCP_MIN_OPTIONS_SIZE
)
531 req
->max_optlen
= DHCP_MIN_OPTIONS_SIZE
;
534 req
->lifetime
= DHCP_DEFAULT_LEASE_TIME
;
539 static int get_pool_offset(sd_dhcp_server
*server
, be32_t requested_ip
) {
542 if (!server
->pool_size
)
545 if (be32toh(requested_ip
) < be32toh(server
->pool_start
) ||
546 be32toh(requested_ip
) >= be32toh(server
->pool_start
) +
550 return be32toh(requested_ip
) - be32toh(server
->pool_start
);
553 int dhcp_server_handle_message(sd_dhcp_server
*server
, DHCPMessage
*message
,
555 _cleanup_dhcp_request_free_ DHCPRequest
*req
= NULL
;
556 DHCPLease
*existing_lease
;
562 if (message
->op
!= BOOTREQUEST
||
563 message
->htype
!= ARPHRD_ETHER
||
564 message
->hlen
!= ETHER_ADDR_LEN
)
567 req
= new0(DHCPRequest
, 1);
571 type
= dhcp_option_parse(message
, length
, parse_request
, req
);
575 r
= ensure_sane_request(req
, message
);
577 /* this only fails on critical errors */
580 existing_lease
= hashmap_get(server
->leases_by_client_id
, &req
->client_id
);
585 be32_t address
= INADDR_ANY
;
588 log_dhcp_server(server
, "DISCOVER (0x%x)",
589 be32toh(req
->message
->xid
));
591 if (!server
->pool_size
)
592 /* no pool allocated */
595 /* for now pick a random free address from the pool */
597 address
= existing_lease
->address
;
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
);
604 server
->next_offer
= (server
->next_offer
+ 1) % server
->pool_size
;
608 if (address
== INADDR_ANY
)
609 /* no free addresses left */
612 r
= server_send_offer(server
, req
, address
);
614 /* this only fails on critical errors */
615 log_dhcp_server(server
, "could not send offer: %s",
619 log_dhcp_server(server
, "OFFER (0x%x)",
620 be32toh(req
->message
->xid
));
627 log_dhcp_server(server
, "DECLINE (0x%x)",
628 be32toh(req
->message
->xid
));
630 /* TODO: make sure we don't offer this address again */
638 bool init_reboot
= false;
641 /* see RFC 2131, section 4.3.2 */
643 if (req
->server_id
) {
644 log_dhcp_server(server
, "REQUEST (selecting) (0x%x)",
645 be32toh(req
->message
->xid
));
648 if (req
->server_id
!= server
->address
)
649 /* client did not pick us */
652 if (req
->message
->ciaddr
)
653 /* this MUST be zero */
656 if (!req
->requested_ip
)
657 /* this must be filled in with the yiaddr
658 from the chosen OFFER */
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
));
667 if (req
->message
->ciaddr
)
668 /* this MUST be zero */
671 /* TODO: check more carefully if IP is correct */
672 address
= req
->requested_ip
;
675 log_dhcp_server(server
, "REQUEST (rebinding/renewing) (0x%x)",
676 be32toh(req
->message
->xid
));
678 /* REBINDING / RENEWING */
679 if (!req
->message
->ciaddr
)
680 /* this MUST be filled in with clients IP address */
683 address
= req
->message
->ciaddr
;
686 pool_offset
= get_pool_offset(server
, address
);
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
) {
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
)
702 lease
->client_id
.length
= req
->client_id
.length
;
704 lease
= existing_lease
;
706 r
= sd_event_now(server
->event
, CLOCK_MONOTONIC
, &time_now
);
708 time_now
= now(CLOCK_MONOTONIC
);
709 lease
->expiration
= req
->lifetime
* USEC_PER_SEC
+ time_now
;
711 r
= server_send_ack(server
, req
, address
);
713 /* this only fails on critical errors */
714 log_dhcp_server(server
, "could not send ack: %s",
718 dhcp_lease_free(lease
);
722 log_dhcp_server(server
, "ACK (0x%x)",
723 be32toh(req
->message
->xid
));
725 server
->bound_leases
[pool_offset
] = lease
;
726 hashmap_put(server
->leases_by_client_id
, &lease
->client_id
, lease
);
730 } else if (init_reboot
) {
731 r
= server_send_nak(server
, req
);
733 /* this only fails on critical errors */
734 log_dhcp_server(server
, "could not send nak: %s",
738 log_dhcp_server(server
, "NAK (0x%x)",
739 be32toh(req
->message
->xid
));
751 static int server_receive_message(sd_event_source
*s
, int fd
,
752 uint32_t revents
, void *userdata
) {
753 _cleanup_free_ DHCPMessage
*message
= NULL
;
754 uint8_t cmsgbuf
[CMSG_LEN(sizeof(struct in_pktinfo
))];
755 sd_dhcp_server
*server
= userdata
;
756 struct iovec iov
= {};
757 struct msghdr msg
= {
760 .msg_control
= cmsgbuf
,
761 .msg_controllen
= sizeof(cmsgbuf
),
763 struct cmsghdr
*cmsg
;
764 int buflen
= 0, len
, r
;
768 r
= ioctl(fd
, FIONREAD
, &buflen
);
774 message
= malloc0(buflen
);
778 iov
.iov_base
= message
;
779 iov
.iov_len
= buflen
;
781 len
= recvmsg(fd
, &msg
, 0);
784 else if ((size_t)len
< sizeof(DHCPMessage
))
787 for (cmsg
= CMSG_FIRSTHDR(&msg
); cmsg
; cmsg
= CMSG_NXTHDR(&msg
, cmsg
)) {
788 if (cmsg
->cmsg_level
== IPPROTO_IP
&&
789 cmsg
->cmsg_type
== IP_PKTINFO
&&
790 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct in_pktinfo
))) {
791 struct in_pktinfo
*info
= (struct in_pktinfo
*)CMSG_DATA(cmsg
);
793 /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
794 if (server
->index
!= info
->ipi_ifindex
)
801 return dhcp_server_handle_message(server
, message
, (size_t)len
);
804 int sd_dhcp_server_start(sd_dhcp_server
*server
) {
807 assert_return(server
, -EINVAL
);
808 assert_return(server
->event
, -EINVAL
);
809 assert_return(!server
->receive_message
, -EBUSY
);
810 assert_return(server
->fd_raw
== -1, -EBUSY
);
811 assert_return(server
->fd
== -1, -EBUSY
);
812 assert_return(server
->address
!= htobe32(INADDR_ANY
), -EUNATCH
);
814 r
= socket(AF_PACKET
, SOCK_DGRAM
| SOCK_NONBLOCK
, 0);
817 sd_dhcp_server_stop(server
);
822 r
= dhcp_network_bind_udp_socket(INADDR_ANY
, DHCP_PORT_SERVER
);
824 sd_dhcp_server_stop(server
);
829 r
= sd_event_add_io(server
->event
, &server
->receive_message
,
831 server_receive_message
, server
);
833 sd_dhcp_server_stop(server
);
837 r
= sd_event_source_set_priority(server
->receive_message
,
838 server
->event_priority
);
840 sd_dhcp_server_stop(server
);
844 log_dhcp_server(server
, "STARTED");