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 "sd-dhcp-server.h"
27 #include "dhcp-server-internal.h"
28 #include "dhcp-internal.h"
30 int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address) {
31 assert_return(server, -EINVAL);
32 assert_return(address, -EINVAL);
33 assert_return(address->s_addr, -EINVAL);
34 assert_return(server->address == htobe32(INADDR_ANY), -EBUSY);
36 server->address = address->s_addr;
41 sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
43 assert_se(REFCNT_INC(server->n_ref) >= 2);
48 sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
49 if (server && REFCNT_DEC(server->n_ref) <= 0) {
50 log_dhcp_server(server, "UNREF");
52 sd_dhcp_server_stop(server);
54 sd_event_unref(server->event);
61 int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
62 _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
64 assert_return(ret, -EINVAL);
65 assert_return(ifindex > 0, -EINVAL);
67 server = new0(sd_dhcp_server, 1);
71 server->n_ref = REFCNT_INIT;
74 server->address = htobe32(INADDR_ANY);
75 server->index = ifindex;
83 int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int priority) {
86 assert_return(server, -EINVAL);
87 assert_return(!server->event, -EBUSY);
90 server->event = sd_event_ref(event);
92 r = sd_event_default(&server->event);
97 server->event_priority = priority;
102 int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
103 assert_return(server, -EINVAL);
105 server->event = sd_event_unref(server->event);
110 sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
111 assert_return(server, NULL);
113 return server->event;
116 int sd_dhcp_server_stop(sd_dhcp_server *server) {
117 assert_return(server, -EINVAL);
119 server->receive_message =
120 sd_event_source_unref(server->receive_message);
122 server->fd_raw = safe_close(server->fd_raw);
123 server->fd = safe_close(server->fd);
125 log_dhcp_server(server, "STOPPED");
130 static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, DHCPPacket *packet,
132 union sockaddr_union link = {
133 .ll.sll_family = AF_PACKET,
134 .ll.sll_protocol = htons(ETH_P_IP),
135 .ll.sll_ifindex = server->index,
136 .ll.sll_halen = ETH_ALEN,
141 assert(server->index > 0);
142 assert(server->address);
144 assert(len > sizeof(DHCPPacket));
146 memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
148 dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
149 packet->dhcp.yiaddr, DHCP_PORT_CLIENT, len);
151 r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
158 static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
159 DHCPMessage *message, size_t len) {
160 union sockaddr_union dest = {
161 .in.sin_family = AF_INET,
162 .in.sin_port = htobe16(DHCP_PORT_CLIENT),
163 .in.sin_addr.s_addr = destination,
169 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
170 struct msghdr msg = {
172 .msg_namelen = sizeof(dest.in),
175 .msg_control = cmsgbuf,
176 .msg_controllen = sizeof(cmsgbuf),
178 struct cmsghdr *cmsg;
179 struct in_pktinfo *pktinfo;
183 assert(server->fd > 0);
185 assert(len > sizeof(DHCPMessage));
187 cmsg = CMSG_FIRSTHDR(&msg);
190 cmsg->cmsg_level = IPPROTO_IP;
191 cmsg->cmsg_type = IP_PKTINFO;
192 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
194 /* we attach source interface and address info to the message
195 rather than binding the socket. This will be mostly useful
196 when we gain support for arbitrary number of server addresses
198 pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
201 pktinfo->ipi_ifindex = server->index;
202 pktinfo->ipi_spec_dst.s_addr = server->address;
204 r = sendmsg(server->fd, &msg, 0);
211 static bool requested_broadcast(DHCPRequest *req) {
214 return req->message->flags & htobe16(0x8000);
217 int dhcp_server_send_packet(sd_dhcp_server *server,
218 DHCPRequest *req, DHCPPacket *packet,
219 int type, size_t optoffset) {
220 be32_t destination = INADDR_ANY;
225 assert(req->max_optlen);
226 assert(optoffset <= req->max_optlen);
229 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
230 DHCP_OPTION_SERVER_IDENTIFIER,
231 4, &server->address);
235 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
236 DHCP_OPTION_END, 0, NULL);
240 /* RFC 2131 Section 4.1
242 If the ’giaddr’ field in a DHCP message from a client is non-zero,
243 the server sends any return messages to the ’DHCP server’ port on the
244 BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
245 field is zero and the ’ciaddr’ field is nonzero, then the server
246 unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
247 If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
248 set, then the server broadcasts DHCPOFFER and DHCPACK messages to
249 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
250 ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
251 messages to the client’s hardware address and ’yiaddr’ address. In
252 all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
253 messages to 0xffffffff.
257 If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
258 different subnet. The server MUST set the broadcast bit in the
259 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
260 client, because the client may not have a correct network address
261 or subnet mask, and the client may not be answering ARP requests.
263 if (req->message->giaddr) {
264 destination = req->message->giaddr;
265 if (type == DHCP_NAK)
266 packet->dhcp.flags = htobe16(0x8000);
267 } else if (req->message->ciaddr && type != DHCP_NAK)
268 destination = req->message->ciaddr;
270 if (destination || requested_broadcast(req) || type == DHCP_NAK)
271 return dhcp_server_send_udp(server, destination, &packet->dhcp,
272 sizeof(DHCPMessage) + optoffset);
274 /* we cannot send UDP packet to specific MAC address when the address is
275 not yet configured, so must fall back to raw packets */
276 return dhcp_server_send_unicast_raw(server, packet,
277 sizeof(DHCPPacket) + optoffset);
280 static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
282 DHCPRequest *req = user_data;
287 case DHCP_OPTION_SERVER_IDENTIFIER:
289 req->server_id = *(be32_t*)option;
292 case DHCP_OPTION_CLIENT_IDENTIFIER:
296 data = memdup(option, len);
300 free(req->client_id.data);
301 req->client_id.data = data;
302 req->client_id.length = len;
306 case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
308 req->max_optlen = be16toh(*(be16_t*)option) -
309 - sizeof(DHCPPacket);
317 static void dhcp_request_free(DHCPRequest *req) {
321 free(req->client_id.data);
325 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
326 #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
328 static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
332 req->message = message;
334 /* set client id based on mac address if client did not send an explicit one */
335 if (!req->client_id.data) {
338 data = new0(uint8_t, ETH_ALEN + 1);
342 req->client_id.length = ETH_ALEN + 1;
343 req->client_id.data = data;
344 req->client_id.data[0] = 0x01;
345 memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
348 if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
349 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
354 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
356 _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
362 if (message->op != BOOTREQUEST ||
363 message->htype != ARPHRD_ETHER ||
364 message->hlen != ETHER_ADDR_LEN)
367 req = new0(DHCPRequest, 1);
371 type = dhcp_option_parse(message, length, parse_request, req);
375 r = ensure_sane_request(req, message);
377 /* this only fails on critical errors */
380 log_dhcp_server(server, "received message of type %d", type);
385 static int server_receive_message(sd_event_source *s, int fd,
386 uint32_t revents, void *userdata) {
387 _cleanup_free_ DHCPMessage *message = NULL;
388 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
389 sd_dhcp_server *server = userdata;
390 struct iovec iov = {};
391 struct msghdr msg = {
394 .msg_control = cmsgbuf,
395 .msg_controllen = sizeof(cmsgbuf),
397 struct cmsghdr *cmsg;
398 int buflen = 0, len, r;
402 r = ioctl(fd, FIONREAD, &buflen);
408 message = malloc0(buflen);
412 iov.iov_base = message;
413 iov.iov_len = buflen;
415 len = recvmsg(fd, &msg, 0);
418 else if ((size_t)len < sizeof(DHCPMessage))
421 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
422 if (cmsg->cmsg_level == IPPROTO_IP &&
423 cmsg->cmsg_type == IP_PKTINFO &&
424 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
425 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
427 /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
428 if (server->index != info->ipi_ifindex)
435 return dhcp_server_handle_message(server, message, (size_t)len);
438 int sd_dhcp_server_start(sd_dhcp_server *server) {
441 assert_return(server, -EINVAL);
442 assert_return(server->event, -EINVAL);
443 assert_return(!server->receive_message, -EBUSY);
444 assert_return(server->fd_raw == -1, -EBUSY);
445 assert_return(server->fd == -1, -EBUSY);
446 assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
448 r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
451 sd_dhcp_server_stop(server);
456 r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
458 sd_dhcp_server_stop(server);
463 r = sd_event_add_io(server->event, &server->receive_message,
465 server_receive_message, server);
467 sd_dhcp_server_stop(server);
471 r = sd_event_source_set_priority(server->receive_message,
472 server->event_priority);
474 sd_dhcp_server_stop(server);
478 log_dhcp_server(server, "STARTED");