sd-dhcp-server.c revision 4dc355680460fdc8e0d590d8572dff1b6a257d88
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering/***
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering This file is part of systemd.
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering Copyright (C) 2013 Intel Corporation. All rights reserved.
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering Copyright (C) 2014 Tom Gundersen
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering systemd is free software; you can redistribute it and/or modify it
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering under the terms of the GNU Lesser General Public License as published by
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering (at your option) any later version.
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering systemd is distributed in the hope that it will be useful, but
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering Lesser General Public License for more details.
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering You should have received a copy of the GNU Lesser General Public License
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering***/
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include <sys/ioctl.h>
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include <netinet/if_ether.h>
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "sd-dhcp-server.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "dhcp-server-internal.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#include "dhcp-internal.h"
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering#define DHCP_DEFAULT_LEASE_TIME 60
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringint sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert_return(server, -EINVAL);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert_return(address, -EINVAL);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert_return(address->s_addr, -EINVAL);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert_return(server->address == htobe32(INADDR_ANY), -EBUSY);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering server->address = address->s_addr;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringsd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (server)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert_se(REFCNT_INC(server->n_ref) >= 2);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return server;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringsd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (server && REFCNT_DEC(server->n_ref) <= 0) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering log_dhcp_server(server, "UNREF");
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering sd_dhcp_server_stop(server);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering sd_event_unref(server->event);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering free(server);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return NULL;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
9f6445e34a57c270f013c9416c123e56261553ddLennart Poetteringint sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
507f22bd0172bff5e5d98145b1419bd472a2c57fZbigniew Jędrzejewski-Szmek _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
507f22bd0172bff5e5d98145b1419bd472a2c57fZbigniew Jędrzejewski-Szmek
e627440b41bb0284e4892f7aa9d84c77972487e2Lennart Poettering assert_return(ret, -EINVAL);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert_return(ifindex > 0, -EINVAL);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering server = new0(sd_dhcp_server, 1);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (!server)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return -ENOMEM;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering server->n_ref = REFCNT_INIT;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering server->fd_raw = -1;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering server->fd = -1;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering server->address = htobe32(INADDR_ANY);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering server->index = ifindex;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering *ret = server;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering server = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringint sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int priority) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering int r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert_return(server, -EINVAL);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert_return(!server->event, -EBUSY);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (event)
b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5dbLennart Poettering server->event = sd_event_ref(event);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering else {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = sd_event_default(&server->event);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering server->event_priority = priority;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringint sd_dhcp_server_detach_event(sd_dhcp_server *server) {
baed47c3c20512507e497058d388782400a072f6Lennart Poettering assert_return(server, -EINVAL);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering server->event = sd_event_unref(server->event);
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringsd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert_return(server, NULL);
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering return server->event;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringint sd_dhcp_server_stop(sd_dhcp_server *server) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert_return(server, -EINVAL);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering server->receive_message =
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering sd_event_source_unref(server->receive_message);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering server->fd_raw = safe_close(server->fd_raw);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering server->fd = safe_close(server->fd);
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering log_dhcp_server(server, "STOPPED");
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic int dhcp_server_send_unicast_raw(sd_dhcp_server *server, DHCPPacket *packet,
b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5dbLennart Poettering size_t len) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering union sockaddr_union link = {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering .ll.sll_family = AF_PACKET,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering .ll.sll_protocol = htons(ETH_P_IP),
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering .ll.sll_ifindex = server->index,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering .ll.sll_halen = ETH_ALEN,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering };
baed47c3c20512507e497058d388782400a072f6Lennart Poettering int r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(server);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(server->index > 0);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(server->address);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(packet);
baed47c3c20512507e497058d388782400a072f6Lennart Poettering assert(len > sizeof(DHCPPacket));
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering packet->dhcp.yiaddr, DHCP_PORT_CLIENT, len);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5dbLennart Poettering r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
507f22bd0172bff5e5d98145b1419bd472a2c57fZbigniew Jędrzejewski-Szmek return r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering DHCPMessage *message, size_t len) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering union sockaddr_union dest = {
b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5dbLennart Poettering .in.sin_family = AF_INET,
b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5dbLennart Poettering .in.sin_port = htobe16(DHCP_PORT_CLIENT),
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering .in.sin_addr.s_addr = destination,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering };
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering struct iovec iov = {
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering .iov_base = message,
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering .iov_len = len,
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering };
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering struct msghdr msg = {
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering .msg_name = &dest,
baed47c3c20512507e497058d388782400a072f6Lennart Poettering .msg_namelen = sizeof(dest.in),
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering .msg_iov = &iov,
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering .msg_iovlen = 1,
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering .msg_control = cmsgbuf,
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering .msg_controllen = sizeof(cmsgbuf),
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering };
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering struct cmsghdr *cmsg;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering struct in_pktinfo *pktinfo;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering int r;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering assert(server);
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering assert(server->fd > 0);
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering assert(message);
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering assert(len > sizeof(DHCPMessage));
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering cmsg = CMSG_FIRSTHDR(&msg);
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering assert(cmsg);
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering cmsg->cmsg_level = IPPROTO_IP;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering cmsg->cmsg_type = IP_PKTINFO;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering /* we attach source interface and address info to the message
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering rather than binding the socket. This will be mostly useful
507f22bd0172bff5e5d98145b1419bd472a2c57fZbigniew Jędrzejewski-Szmek when we gain support for arbitrary number of server addresses
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering */
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering assert(pktinfo);
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering pktinfo->ipi_ifindex = server->index;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering pktinfo->ipi_spec_dst.s_addr = server->address;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = sendmsg(server->fd, &msg, 0);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return -errno;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic bool requested_broadcast(DHCPRequest *req) {
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering assert(req);
671e021c92c835c6c701dc61463149d05b6f31afLennart Poettering
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering return req->message->flags & htobe16(0x8000);
baed47c3c20512507e497058d388782400a072f6Lennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringint dhcp_server_send_packet(sd_dhcp_server *server,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering DHCPRequest *req, DHCPPacket *packet,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering int type, size_t optoffset) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering be32_t destination = INADDR_ANY;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering int r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering assert(server);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(req);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(req->max_optlen);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(optoffset <= req->max_optlen);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(packet);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
7851983162ef851d5b9ce12bd88de86fc402f88aMichal Schmidt DHCP_OPTION_SERVER_IDENTIFIER,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering 4, &server->address);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering DHCP_OPTION_END, 0, NULL);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering /* RFC 2131 Section 4.1
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering If the ’giaddr’ field in a DHCP message from a client is non-zero,
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering the server sends any return messages to the ’DHCP server’ port on the
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering field is zero and the ’ciaddr’ field is nonzero, then the server
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
d05089d86ef032b245c7f928e623b88c82998ab0Michal Schmidt If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering set, then the server broadcasts DHCPOFFER and DHCPACK messages to
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering messages to the client’s hardware address and ’yiaddr’ address. In
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering messages to 0xffffffff.
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering Section 4.3.2
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering different subnet. The server MUST set the broadcast bit in the
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering client, because the client may not have a correct network address
3c1668da6202f1ead3d4d3981b89e9da1a0e98e3Lennart Poettering or subnet mask, and the client may not be answering ARP requests.
3c1668da6202f1ead3d4d3981b89e9da1a0e98e3Lennart Poettering */
3c1668da6202f1ead3d4d3981b89e9da1a0e98e3Lennart Poettering if (req->message->giaddr) {
3c1668da6202f1ead3d4d3981b89e9da1a0e98e3Lennart Poettering destination = req->message->giaddr;
3c1668da6202f1ead3d4d3981b89e9da1a0e98e3Lennart Poettering if (type == DHCP_NAK)
3c1668da6202f1ead3d4d3981b89e9da1a0e98e3Lennart Poettering packet->dhcp.flags = htobe16(0x8000);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering } else if (req->message->ciaddr && type != DHCP_NAK)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering destination = req->message->ciaddr;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (destination || requested_broadcast(req) || type == DHCP_NAK)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return dhcp_server_send_udp(server, destination, &packet->dhcp,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering sizeof(DHCPMessage) + optoffset);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering else
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering /* we cannot send UDP packet to specific MAC address when the address is
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering not yet configured, so must fall back to raw packets */
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return dhcp_server_send_unicast_raw(server, packet,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering sizeof(DHCPPacket) + optoffset);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering uint8_t type, size_t *_optoffset, DHCPRequest *req) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_free_ DHCPPacket *packet = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering size_t optoffset;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering int r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(server);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(ret);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(_optoffset);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(type == DHCP_OFFER);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (!packet)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return -ENOMEM;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering r = dhcp_message_init(&packet->dhcp, BOOTREPLY, be32toh(req->message->xid),
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering type, req->max_optlen, &optoffset);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering packet->dhcp.flags = req->message->flags;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering packet->dhcp.giaddr = req->message->giaddr;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering *_optoffset = optoffset;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering *ret = packet;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering packet = NULL;
14d10188de1fd58e663d73683a400d8d7dc67dbaLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic int server_send_offer(sd_dhcp_server *server, DHCPRequest *req) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_free_ DHCPPacket *packet = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering size_t offset;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering be32_t lease_time;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering int r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering /* for now offer a random IP */
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering packet->dhcp.yiaddr = random_u32();
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering /* for one minute */
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering lease_time = htobe32(DHCP_DEFAULT_LEASE_TIME);
baed47c3c20512507e497058d388782400a072f6Lennart Poettering r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (r < 0)
baed47c3c20512507e497058d388782400a072f6Lennart Poettering return r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
272410e1790cb407bcedee4e5dfaac3f625957afLennart Poettering void *user_data) {
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt DHCPRequest *req = user_data;
272410e1790cb407bcedee4e5dfaac3f625957afLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(req);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering switch(code) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering case DHCP_OPTION_SERVER_IDENTIFIER:
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (len == 4)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering req->server_id = *(be32_t*)option;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering break;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering case DHCP_OPTION_CLIENT_IDENTIFIER:
baed47c3c20512507e497058d388782400a072f6Lennart Poettering if (len >= 2) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering uint8_t *data;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering data = memdup(option, len);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (!data)
baed47c3c20512507e497058d388782400a072f6Lennart Poettering return -ENOMEM;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering free(req->client_id.data);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering req->client_id.data = data;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering req->client_id.length = len;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering break;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (len == 2)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering req->max_optlen = be16toh(*(be16_t*)option) -
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering - sizeof(DHCPPacket);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering break;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic void dhcp_request_free(DHCPRequest *req) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (!req)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return;
3e4b9b506d676d1cb8692306b38c05f8529e5cdbLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering free(req->client_id.data);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering free(req);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart PoetteringDEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
baed47c3c20512507e497058d388782400a072f6Lennart Poettering#define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(req);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(message);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering req->message = message;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering /* set client id based on mac address if client did not send an explicit one */
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (!req->client_id.data) {
baed47c3c20512507e497058d388782400a072f6Lennart Poettering uint8_t *data;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering data = new0(uint8_t, ETH_ALEN + 1);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (!data)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return -ENOMEM;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering req->client_id.length = ETH_ALEN + 1;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering req->client_id.data = data;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering req->client_id.data[0] = 0x01;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
baed47c3c20512507e497058d388782400a072f6Lennart Poettering req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5dbLennart Poettering return 0;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering}
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5dbLennart Poetteringint dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering size_t length) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering int type, r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
baed47c3c20512507e497058d388782400a072f6Lennart Poettering assert(server);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(message);
03e334a1c7dc8c20c38902aa039440763acc9b17Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (message->op != BOOTREQUEST ||
03e334a1c7dc8c20c38902aa039440763acc9b17Lennart Poettering message->htype != ARPHRD_ETHER ||
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering message->hlen != ETHER_ADDR_LEN)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering req = new0(DHCPRequest, 1);
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering if (!req)
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering return -ENOMEM;
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering type = dhcp_option_parse(message, length, parse_request, req);
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering if (type < 0)
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering return 0;
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering r = ensure_sane_request(req, message);
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering if (r < 0)
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering /* this only fails on critical errors */
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering return r;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering switch(type) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering case DHCP_DISCOVER:
baed47c3c20512507e497058d388782400a072f6Lennart Poettering log_dhcp_server(server, "DISCOVER (0x%x)",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering be32toh(req->message->xid));
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering r = server_send_offer(server, req);
72fbdd3349ad30d8a5074ea9a650f0909f96c299Lennart Poettering if (r < 0) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering /* this only fails on critical errors */
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering log_dhcp_server(server, "could not send offer: %s",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering strerror(-r));
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering } else {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering log_dhcp_server(server, "OFFER (0x%x)",
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering be32toh(req->message->xid));
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return DHCP_OFFER;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering }
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering break;
baed47c3c20512507e497058d388782400a072f6Lennart Poettering }
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return 0;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering}
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poetteringstatic int server_receive_message(sd_event_source *s, int fd,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering uint32_t revents, void *userdata) {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering _cleanup_free_ DHCPMessage *message = NULL;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering sd_dhcp_server *server = userdata;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering struct iovec iov = {};
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering struct msghdr msg = {
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering .msg_iov = &iov,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering .msg_iovlen = 1,
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering .msg_control = cmsgbuf,
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering .msg_controllen = sizeof(cmsgbuf),
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering };
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering struct cmsghdr *cmsg;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering int buflen = 0, len, r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering assert(server);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering r = ioctl(fd, FIONREAD, &buflen);
5996c7c295e073ce21d41305169132c8aa993ad0Lennart Poettering if (r < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return r;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (buflen < 0)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return -EIO;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering message = malloc0(buflen);
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering if (!message)
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering return -ENOMEM;
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering
0284adc6a60ce0af1107cb0b50041a65d731f39eLennart Poettering iov.iov_base = message;
4da416aa20b956571d74720bc91222881443e24bLennart Poettering iov.iov_len = buflen;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering len = recvmsg(fd, &msg, 0);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering if (len < buflen)
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering return 0;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering else if ((size_t)len < sizeof(DHCPMessage))
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering return 0;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering if (cmsg->cmsg_level == IPPROTO_IP &&
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering cmsg->cmsg_type == IP_PKTINFO &&
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering if (server->index != info->ipi_ifindex)
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering return 0;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering break;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering }
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering }
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering return dhcp_server_handle_message(server, message, (size_t)len);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering}
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poetteringint sd_dhcp_server_start(sd_dhcp_server *server) {
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering int r;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering assert_return(server, -EINVAL);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering assert_return(server->event, -EINVAL);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering assert_return(!server->receive_message, -EBUSY);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering assert_return(server->fd_raw == -1, -EBUSY);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering assert_return(server->fd == -1, -EBUSY);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering if (r < 0) {
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering r = -errno;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering sd_dhcp_server_stop(server);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering return r;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering }
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering server->fd_raw = r;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering if (r < 0) {
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering sd_dhcp_server_stop(server);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering return r;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering }
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering server->fd = r;
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering r = sd_event_add_io(server->event, &server->receive_message,
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering server->fd, EPOLLIN,
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering server_receive_message, server);
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering if (r < 0) {
feb12d3ed2c7f9132c64773c7c41b9e3a608a814Lennart Poettering sd_dhcp_server_stop(server);
4da416aa20b956571d74720bc91222881443e24bLennart Poettering return r;
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering }
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering r = sd_event_source_set_priority(server->receive_message,
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering server->event_priority);
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering if (r < 0) {
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering sd_dhcp_server_stop(server);
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering return r;
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering }
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering log_dhcp_server(server, "STARTED");
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering return 0;
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering}
89fef99014662a5a63e7deaedd6292b7fb4ab2f8Lennart Poettering