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 sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
32 assert_se(REFCNT_INC(server->n_ref) >= 2);
37 sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
38 if (server && REFCNT_DEC(server->n_ref) <= 0) {
39 log_dhcp_server(server, "UNREF");
41 sd_dhcp_server_stop(server);
43 sd_event_unref(server->event);
50 int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
51 _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
53 assert_return(ret, -EINVAL);
54 assert_return(ifindex > 0, -EINVAL);
56 server = new0(sd_dhcp_server, 1);
60 server->n_ref = REFCNT_INIT;
62 server->index = ifindex;
70 int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int priority) {
73 assert_return(server, -EINVAL);
74 assert_return(!server->event, -EBUSY);
77 server->event = sd_event_ref(event);
79 r = sd_event_default(&server->event);
84 server->event_priority = priority;
89 int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
90 assert_return(server, -EINVAL);
92 server->event = sd_event_unref(server->event);
97 sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
98 assert_return(server, NULL);
100 return server->event;
103 int sd_dhcp_server_stop(sd_dhcp_server *server) {
104 assert_return(server, -EINVAL);
106 server->receive_message =
107 sd_event_source_unref(server->receive_message);
109 server->fd = safe_close(server->fd);
111 log_dhcp_server(server, "STOPPED");
116 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
123 if (message->op != BOOTREQUEST ||
124 message->htype != ARPHRD_ETHER ||
125 message->hlen != ETHER_ADDR_LEN)
128 type = dhcp_option_parse(message, length, NULL, NULL);
132 log_dhcp_server(server, "received message of type %d", type);
137 static int server_receive_message(sd_event_source *s, int fd,
138 uint32_t revents, void *userdata) {
139 _cleanup_free_ DHCPMessage *message = NULL;
140 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
141 sd_dhcp_server *server = userdata;
142 struct iovec iov = {};
143 struct msghdr msg = {
146 .msg_control = cmsgbuf,
147 .msg_controllen = sizeof(cmsgbuf),
149 struct cmsghdr *cmsg;
150 int buflen = 0, len, r;
154 r = ioctl(fd, FIONREAD, &buflen);
160 message = malloc0(buflen);
164 iov.iov_base = message;
165 iov.iov_len = buflen;
167 len = recvmsg(fd, &msg, 0);
170 else if ((size_t)len < sizeof(DHCPMessage))
173 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
174 if (cmsg->cmsg_level == IPPROTO_IP &&
175 cmsg->cmsg_type == IP_PKTINFO &&
176 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
177 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
179 /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
180 if (server->index != info->ipi_ifindex)
187 return dhcp_server_handle_message(server, message, (size_t)len);
190 int sd_dhcp_server_start(sd_dhcp_server *server) {
193 assert_return(server, -EINVAL);
194 assert_return(server->event, -EINVAL);
195 assert_return(!server->receive_message, -EBUSY);
196 assert_return(server->fd == -1, -EBUSY);
198 r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
200 sd_dhcp_server_stop(server);
205 r = sd_event_add_io(server->event, &server->receive_message,
207 server_receive_message, server);
209 sd_dhcp_server_stop(server);
213 r = sd_event_source_set_priority(server->receive_message,
214 server->event_priority);
216 sd_dhcp_server_stop(server);
220 log_dhcp_server(server, "STARTED");