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>
25 #include "sd-dhcp-server.h"
26 #include "dhcp-server-internal.h"
27 #include "dhcp-internal.h"
29 sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
31 assert_se(REFCNT_INC(server->n_ref) >= 2);
36 sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
37 if (server && REFCNT_DEC(server->n_ref) <= 0) {
38 log_dhcp_server(server, "UNREF");
40 sd_dhcp_server_stop(server);
42 sd_event_unref(server->event);
49 int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
50 _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
52 assert_return(ret, -EINVAL);
53 assert_return(ifindex > 0, -EINVAL);
55 server = new0(sd_dhcp_server, 1);
59 server->n_ref = REFCNT_INIT;
61 server->index = ifindex;
69 int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int priority) {
72 assert_return(server, -EINVAL);
73 assert_return(!server->event, -EBUSY);
76 server->event = sd_event_ref(event);
78 r = sd_event_default(&server->event);
83 server->event_priority = priority;
88 int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
89 assert_return(server, -EINVAL);
91 server->event = sd_event_unref(server->event);
96 sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
97 assert_return(server, NULL);
102 int sd_dhcp_server_stop(sd_dhcp_server *server) {
103 assert_return(server, -EINVAL);
105 server->receive_message =
106 sd_event_source_unref(server->receive_message);
108 server->fd = safe_close(server->fd);
110 log_dhcp_server(server, "STOPPED");
115 static int server_receive_message(sd_event_source *s, int fd,
116 uint32_t revents, void *userdata) {
117 _cleanup_free_ uint8_t *message = NULL;
118 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
119 sd_dhcp_server *server = userdata;
120 struct iovec iov = {};
121 struct msghdr msg = {
124 .msg_control = cmsgbuf,
125 .msg_controllen = sizeof(cmsgbuf),
127 struct cmsghdr *cmsg;
128 int buflen = 0, len, r;
132 r = ioctl(fd, FIONREAD, &buflen);
138 message = malloc0(buflen);
142 iov.iov_base = message;
143 iov.iov_len = buflen;
145 len = recvmsg(fd, &msg, 0);
149 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
150 if (cmsg->cmsg_level == IPPROTO_IP &&
151 cmsg->cmsg_type == IP_PKTINFO &&
152 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
153 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
155 /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
156 if (server->index != info->ipi_ifindex)
163 log_dhcp_server(server, "received message");
168 int sd_dhcp_server_start(sd_dhcp_server *server) {
171 assert_return(server, -EINVAL);
172 assert_return(server->event, -EINVAL);
173 assert_return(!server->receive_message, -EBUSY);
174 assert_return(server->fd == -1, -EBUSY);
176 r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
178 sd_dhcp_server_stop(server);
183 r = sd_event_add_io(server->event, &server->receive_message,
185 server_receive_message, server);
187 sd_dhcp_server_stop(server);
191 r = sd_event_source_set_priority(server->receive_message,
192 server->event_priority);
194 sd_dhcp_server_stop(server);
198 log_dhcp_server(server, "STARTED");