sd-dhcp-server.c revision b826ab586c9e0a9c0d438a75c28cf3a8ab485929
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2013 Intel Corporation. All rights reserved.
Copyright (C) 2014 Tom Gundersen
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "in-addr-util.h"
#include "siphash24.h"
#include "sd-dhcp-server.h"
#include "dhcp-server-internal.h"
#include "dhcp-internal.h"
/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
* the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
* moreover, the server's own address may be in the pool, and is in that case reserved in order not to
* accidentally hand it out */
int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) {
struct in_addr netmask_addr;
/* the server address cannot be the subnet address */
/* nor the broadcast address */
/* 0 offset means we should set a default, we skip the first (subnet) address
and take the next one */
if (offset == 0)
offset = 1;
- offset /* exclude the addresses before the offset */
- 1; /* exclude the last (broadcast) address */
/* The pool must contain at least one address */
if (size != 0)
else
if (!server->bound_leases)
return -ENOMEM;
return 0;
}
assert_return(server, false);
return !!server->receive_message;
}
if (!server)
return NULL;
return server;
}
const DHCPClientId *id = p;
}
const DHCPClientId *a, *b;
a = _a;
b = _b;
}
static const struct hash_ops client_id_hash_ops = {
};
if (!lease)
return;
}
if (!server)
return NULL;
return NULL;
return NULL;
}
if (!server)
return -ENOMEM;
return 0;
}
int priority) {
int r;
if (event)
else {
if (r < 0)
return r;
}
return 0;
}
return 0;
}
}
return 0;
}
union sockaddr_union link = {
};
}
union sockaddr_union dest = {
};
};
.msg_iovlen = 1,
.msg_control = cmsgbuf,
.msg_controllen = sizeof(cmsgbuf),
};
struct in_pktinfo *pktinfo;
int r;
/* we attach source interface and address info to the message
rather than binding the socket. This will be mostly useful
when we gain support for arbitrary number of server addresses
*/
if (r < 0)
return -errno;
return 0;
}
}
int r;
if (r < 0)
return r;
DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
/* RFC 2131 Section 4.1
If the ’giaddr’ field in a DHCP message from a client is non-zero,
the server sends any return messages to the ’DHCP server’ port on the
BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
field is zero and the ’ciaddr’ field is nonzero, then the server
unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
set, then the server broadcasts DHCPOFFER and DHCPACK messages to
0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
messages to the client’s hardware address and ’yiaddr’ address. In
all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
messages to 0xffffffff.
Section 4.3.2
If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
different subnet. The server MUST set the broadcast bit in the
DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
client, because the client may not have a correct network address
or subnet mask, and the client may not be answering ARP requests.
*/
if (destination != INADDR_ANY)
sizeof(DHCPMessage) + optoffset);
sizeof(DHCPMessage) + optoffset);
else
/* we cannot send UDP packet to specific MAC address when the
address is not yet configured, so must fall back to raw
packets */
sizeof(DHCPPacket) + optoffset);
}
DHCPRequest *req) {
int r;
if (!packet)
return -ENOMEM;
if (r < 0)
return r;
*_optoffset = optoffset;
return 0;
}
int r;
if (r < 0)
return r;
&lease_time);
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
int r;
if (r < 0)
return r;
&lease_time);
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
r = dhcp_option_append(
if (r < 0)
return r;
}
r = dhcp_option_append(
if (r < 0)
return r;
}
r = dhcp_option_append(
if (r < 0)
return r;
}
if (r < 0)
return r;
return 0;
}
int r;
if (r < 0)
return r;
}
int r;
if (!packet)
return -ENOMEM;
if (r < 0)
return r;
if (r < 0)
return r;
sizeof(DHCPMessage) + optoffset);
if (r < 0)
return r;
return 0;
}
switch(code) {
if (len == 4)
break;
if (len == 4)
break;
if (len == 4)
break;
if (len >= 2) {
if (!data)
return -ENOMEM;
}
break;
if (len == 2)
- sizeof(DHCPPacket);
break;
}
return 0;
}
if (!req)
return;
}
/* set client id based on MAC address if client did not send an explicit
one */
void *data;
if (!data)
return -ENOMEM;
}
return 0;
}
return -EINVAL;
return -ERANGE;
}
int type, r;
return 0;
if (!req)
return -ENOMEM;
if (type < 0)
return 0;
if (r < 0)
/* this only fails on critical errors */
return r;
switch(type) {
case DHCP_DISCOVER: {
unsigned i;
/* no pool allocated */
return 0;
/* for now pick a random free address from the pool */
if (existing_lease)
else {
/* even with no persistence of leases, we try to offer the same client
the same IP address. we do this by using the hash of the client id
as the offset into the pool of leases when finding the next free one */
break;
} else
}
}
if (address == INADDR_ANY)
/* no free addresses left */
return 0;
if (r < 0) {
/* this only fails on critical errors */
strerror(-r));
return r;
} else {
return DHCP_OFFER;
}
break;
}
case DHCP_DECLINE:
/* TODO: make sure we don't offer this address again */
return 1;
case DHCP_REQUEST: {
bool init_reboot = false;
int pool_offset;
/* see RFC 2131, section 4.3.2 */
/* SELECTING */
/* client did not pick us */
return 0;
/* this MUST be zero */
return 0;
if (!req->requested_ip)
/* this must be filled in with the yiaddr
from the chosen OFFER */
return 0;
} else if (req->requested_ip) {
/* INIT-REBOOT */
/* this MUST be zero */
return 0;
/* TODO: check more carefully if IP is correct */
init_reboot = true;
} else {
/* REBINDING / RENEWING */
/* this MUST be filled in with clients IP address */
return 0;
}
/* verify that the requested address is from the pool, and either
owned by the current client or free */
if (pool_offset >= 0 &&
if (!existing_lease) {
return -ENOMEM;
}
ETH_ALEN);
} else
&time_now);
if (r < 0) {
if (!existing_lease)
return r;
}
if (r < 0) {
/* this only fails on critical errors */
strerror(-r));
if (!existing_lease)
return r;
} else {
return DHCP_ACK;
}
} else if (init_reboot) {
if (r < 0) {
/* this only fails on critical errors */
strerror(-r));
return r;
} else {
return DHCP_NAK;
}
}
break;
}
case DHCP_RELEASE: {
int pool_offset;
if (!existing_lease)
return 0;
return 0;
if (pool_offset < 0)
return 0;
return 1;
} else
return 0;
}
}
return 0;
}
.msg_iovlen = 1,
.msg_control = cmsgbuf,
.msg_controllen = sizeof(cmsgbuf),
};
return -errno;
if (buflen < 0)
return -EIO;
if (!message)
return -ENOMEM;
return 0;
return 0;
/* TODO figure out if this can be done as a filter on
* the socket, like for IPv6 */
return 0;
break;
}
}
}
int r;
if (r < 0) {
r = -errno;
return r;
}
if (r < 0) {
return r;
}
if (r < 0) {
return r;
}
if (r < 0) {
return r;
}
return 0;
}
unsigned i;
int r = 0;
continue;
if (r < 0)
return r;
else
}
return r;
}
int r;
return 0;
if (r < 0)
return r;
return 1;
}
if (t == server->max_lease_time)
return 0;
server->max_lease_time = t;
return 1;
}
if (t == server->default_lease_time)
return 0;
server->default_lease_time = t;
return 1;
}
return 0;
if (n <= 0) {
} else {
struct in_addr *c;
if (!c)
return -ENOMEM;
}
return 1;
}
return 0;
if (n <= 0) {
} else {
struct in_addr *c;
if (!c)
return -ENOMEM;
}
return 1;
}