sd-dhcp-server.c revision 02557f973aed0fed7154fefe53d67e2935f918dc
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering/***
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering This file is part of systemd.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering Copyright (C) 2013 Intel Corporation. All rights reserved.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering Copyright (C) 2014 Tom Gundersen
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering systemd is free software; you can redistribute it and/or modify it
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering under the terms of the GNU Lesser General Public License as published by
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering (at your option) any later version.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering systemd is distributed in the hope that it will be useful, but
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering Lesser General Public License for more details.
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering You should have received a copy of the GNU Lesser General Public License
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering***/
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering#include <sys/ioctl.h>
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering#include <netinet/if_ether.h>
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering#include "siphash24.h"
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering
72f1d5a2880d103dc1c1746f5c02e192e054705eLennart Poettering#include "sd-dhcp-server.h"
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering#include "dhcp-server-internal.h"
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering#include "dhcp-internal.h"
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering#define DHCP_DEFAULT_LEASE_TIME 60
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poetteringint sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, struct in_addr *address,
a09abc4ae0bdc0200324eaa0416f23ff2170ec4eLennart Poettering size_t size) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert_return(server, -EINVAL);
bb99a35a873c35e80b0b47fe045081022660374dLennart Poettering assert_return(address, -EINVAL);
bb99a35a873c35e80b0b47fe045081022660374dLennart Poettering assert_return(address->s_addr, -EINVAL);
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering assert_return(size, -EINVAL);
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering assert_return(server->pool_start == htobe32(INADDR_ANY), -EBUSY);
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering assert_return(!server->pool_size, -EBUSY);
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering assert_return(!server->bound_leases, -EBUSY);
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering server->bound_leases = new0(DHCPLease*, size);
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering if (!server->bound_leases)
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering return -ENOMEM;
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering server->pool_start = address->s_addr;
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering server->pool_size = size;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return 0;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering}
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringint sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert_return(server, -EINVAL);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert_return(address, -EINVAL);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert_return(address->s_addr, -EINVAL);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert_return(server->address == htobe32(INADDR_ANY), -EBUSY);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering server->address = address->s_addr;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return 0;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering}
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringsd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (server)
bb99a35a873c35e80b0b47fe045081022660374dLennart Poettering assert_se(REFCNT_INC(server->n_ref) >= 2);
bb99a35a873c35e80b0b47fe045081022660374dLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return server;
03e334a1c7dc8c20c38902aa039440763acc9b17Lennart Poettering}
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringunsigned long client_id_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering uint64_t u;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering const DHCPClientId *id = p;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(id);
a5344d2c3b0f14e954ce1c0ef905c5b44bc5bf0aLennart Poettering assert(id->length);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(id->data);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering siphash24((uint8_t*) &u, id->data, id->length, hash_key);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
d0bbc21caa6e68693a47db60c93e99422bf2a858Lennart Poettering return (unsigned long) u;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering}
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringint client_id_compare_func(const void *_a, const void *_b) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering const DHCPClientId *a, *b;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
a5344d2c3b0f14e954ce1c0ef905c5b44bc5bf0aLennart Poettering a = _a;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering b = _b;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(!a->length || a->data);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(!b->length || b->data);
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek if (a->length != b->length)
d0bbc21caa6e68693a47db60c93e99422bf2a858Lennart Poettering return a->length < b->length ? -1 : 1;
1ae464e09376853c52075ec4d8a6bfc4b4036d0cThomas Hindoe Paaboel Andersen
1ae464e09376853c52075ec4d8a6bfc4b4036d0cThomas Hindoe Paaboel Andersen return memcmp(a->data, b->data, a->length);
1ae464e09376853c52075ec4d8a6bfc4b4036d0cThomas Hindoe Paaboel Andersen}
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmekstatic void dhcp_lease_free(DHCPLease *lease) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (!lease)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering free(lease->client_id.data);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering free(lease);
d0bbc21caa6e68693a47db60c93e99422bf2a858Lennart Poettering}
d0bbc21caa6e68693a47db60c93e99422bf2a858Lennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart PoetteringDEFINE_TRIVIAL_CLEANUP_FUNC(DHCPLease*, dhcp_lease_free);
d0bbc21caa6e68693a47db60c93e99422bf2a858Lennart Poettering#define _cleanup_dhcp_lease_free_ _cleanup_(dhcp_lease_freep)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringsd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
44b601bc79e46722bc0f0862ee0ce34a2284ef11Lennart Poettering if (server && REFCNT_DEC(server->n_ref) <= 0) {
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering DHCPLease *lease;
25d042e81516246b1ebf706a57c47ac19abb0b8aLennart Poettering Iterator i;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering log_dhcp_server(server, "UNREF");
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering sd_dhcp_server_stop(server);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering sd_event_unref(server->event);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering HASHMAP_FOREACH(lease, server->leases_by_client_id, i) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering hashmap_remove(server->leases_by_client_id, lease);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering dhcp_lease_free(lease);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering hashmap_free(server->leases_by_client_id);
25d042e81516246b1ebf706a57c47ac19abb0b8aLennart Poettering free(server->bound_leases);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering free(server);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering }
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return NULL;
72f1d5a2880d103dc1c1746f5c02e192e054705eLennart Poettering}
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringint sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert_return(ret, -EINVAL);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert_return(ifindex > 0, -EINVAL);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering server = new0(sd_dhcp_server, 1);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (!server)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return -ENOMEM;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering server->n_ref = REFCNT_INIT;
72f1d5a2880d103dc1c1746f5c02e192e054705eLennart Poettering server->fd_raw = -1;
72f1d5a2880d103dc1c1746f5c02e192e054705eLennart Poettering server->fd = -1;
72f1d5a2880d103dc1c1746f5c02e192e054705eLennart Poettering server->address = htobe32(INADDR_ANY);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering server->index = ifindex;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering server->leases_by_client_id = hashmap_new(client_id_hash_func, client_id_compare_func);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
72f1d5a2880d103dc1c1746f5c02e192e054705eLennart Poettering *ret = server;
72f1d5a2880d103dc1c1746f5c02e192e054705eLennart Poettering server = NULL;
72f1d5a2880d103dc1c1746f5c02e192e054705eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return 0;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering}
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringint sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int priority) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering int r;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering assert_return(server, -EINVAL);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering assert_return(!server->event, -EBUSY);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (event)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering server->event = sd_event_ref(event);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering else {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering r = sd_event_default(&server->event);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (r < 0)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return r;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering server->event_priority = priority;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return 0;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering}
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poetteringint sd_dhcp_server_detach_event(sd_dhcp_server *server) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering assert_return(server, -EINVAL);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering server->event = sd_event_unref(server->event);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return 0;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering}
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poetteringsd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering assert_return(server, NULL);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return server->event;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering}
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringint sd_dhcp_server_stop(sd_dhcp_server *server) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert_return(server, -EINVAL);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering server->receive_message =
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering sd_event_source_unref(server->receive_message);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering server->fd_raw = safe_close(server->fd_raw);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering server->fd = safe_close(server->fd);
a5344d2c3b0f14e954ce1c0ef905c5b44bc5bf0aLennart Poettering
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering log_dhcp_server(server, "STOPPED");
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering
61c024b328d5493a334242a4d01ba923582093faZbigniew Jędrzejewski-Szmek return 0;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering}
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poetteringstatic int dhcp_server_send_unicast_raw(sd_dhcp_server *server, DHCPPacket *packet,
6e5abe1564070a760196b97031eca9cf5e95e8a2Zbigniew Jędrzejewski-Szmek size_t len) {
6e5abe1564070a760196b97031eca9cf5e95e8a2Zbigniew Jędrzejewski-Szmek union sockaddr_union link = {
6e5abe1564070a760196b97031eca9cf5e95e8a2Zbigniew Jędrzejewski-Szmek .ll.sll_family = AF_PACKET,
6e5abe1564070a760196b97031eca9cf5e95e8a2Zbigniew Jędrzejewski-Szmek .ll.sll_protocol = htons(ETH_P_IP),
6e5abe1564070a760196b97031eca9cf5e95e8a2Zbigniew Jędrzejewski-Szmek .ll.sll_ifindex = server->index,
6e5abe1564070a760196b97031eca9cf5e95e8a2Zbigniew Jędrzejewski-Szmek .ll.sll_halen = ETH_ALEN,
6e5abe1564070a760196b97031eca9cf5e95e8a2Zbigniew Jędrzejewski-Szmek };
6e5abe1564070a760196b97031eca9cf5e95e8a2Zbigniew Jędrzejewski-Szmek int r;
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering assert(server);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering assert(server->index > 0);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering assert(server->address);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering assert(packet);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering assert(len > sizeof(DHCPPacket));
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
1ae464e09376853c52075ec4d8a6bfc4b4036d0cThomas Hindoe Paaboel Andersen dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
1ae464e09376853c52075ec4d8a6bfc4b4036d0cThomas Hindoe Paaboel Andersen packet->dhcp.yiaddr, DHCP_PORT_CLIENT, len);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (r < 0)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return r;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return 0;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering}
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poetteringstatic int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
a5344d2c3b0f14e954ce1c0ef905c5b44bc5bf0aLennart Poettering DHCPMessage *message, size_t len) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering union sockaddr_union dest = {
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering .in.sin_family = AF_INET,
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering .in.sin_port = htobe16(DHCP_PORT_CLIENT),
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering .in.sin_addr.s_addr = destination,
29abad107f8610e73b2fc091216040b579c75453Zbigniew Jędrzejewski-Szmek };
29abad107f8610e73b2fc091216040b579c75453Zbigniew Jędrzejewski-Szmek struct iovec iov = {
2a0e0692565f0435657c93498e09cbb2d3517152Shawn Landden .iov_base = message,
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering .iov_len = len,
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering };
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering struct msghdr msg = {
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering .msg_name = &dest,
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering .msg_namelen = sizeof(dest.in),
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering .msg_iov = &iov,
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering .msg_iovlen = 1,
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering .msg_control = cmsgbuf,
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering .msg_controllen = sizeof(cmsgbuf),
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering };
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering struct cmsghdr *cmsg;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering struct in_pktinfo *pktinfo;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering int r;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(server);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(server->fd > 0);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(message);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(len > sizeof(DHCPMessage));
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering cmsg = CMSG_FIRSTHDR(&msg);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(cmsg);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering cmsg->cmsg_level = IPPROTO_IP;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering cmsg->cmsg_type = IP_PKTINFO;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering /* we attach source interface and address info to the message
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering rather than binding the socket. This will be mostly useful
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering when we gain support for arbitrary number of server addresses
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering */
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(pktinfo);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering pktinfo->ipi_ifindex = server->index;
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering pktinfo->ipi_spec_dst.s_addr = server->address;
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering r = sendmsg(server->fd, &msg, 0);
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering if (r < 0)
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering return -errno;
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering return 0;
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering}
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poetteringstatic bool requested_broadcast(DHCPRequest *req) {
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering assert(req);
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering return req->message->flags & htobe16(0x8000);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering}
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poetteringint dhcp_server_send_packet(sd_dhcp_server *server,
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering DHCPRequest *req, DHCPPacket *packet,
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering int type, size_t optoffset) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering be32_t destination = INADDR_ANY;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering int r;
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering assert(server);
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering assert(req);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering assert(req->max_optlen);
6c045c0b4c49c88a1d3b9360c05efa5084796d2dZbigniew Jędrzejewski-Szmek assert(optoffset <= req->max_optlen);
6c045c0b4c49c88a1d3b9360c05efa5084796d2dZbigniew Jędrzejewski-Szmek assert(packet);
6c045c0b4c49c88a1d3b9360c05efa5084796d2dZbigniew Jędrzejewski-Szmek
6c045c0b4c49c88a1d3b9360c05efa5084796d2dZbigniew Jędrzejewski-Szmek r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering DHCP_OPTION_SERVER_IDENTIFIER,
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering 4, &server->address);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering if (r < 0)
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering return r;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
8e33886ec582336564ae11b80023abe93d7599c0Zbigniew Jędrzejewski-Szmek DHCP_OPTION_END, 0, NULL);
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering if (r < 0)
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering return r;
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering /* RFC 2131 Section 4.1
73843b52585d42cc1a970a1c664818ece6942e9eLennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering If the ’giaddr’ field in a DHCP message from a client is non-zero,
73843b52585d42cc1a970a1c664818ece6942e9eLennart Poettering the server sends any return messages to the ’DHCP server’ port on the
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering field is zero and the ’ciaddr’ field is nonzero, then the server
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering set, then the server broadcasts DHCPOFFER and DHCPACK messages to
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
73843b52585d42cc1a970a1c664818ece6942e9eLennart Poettering ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering messages to the client’s hardware address and ’yiaddr’ address. In
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
87b0284327e34a4b96c22085fa2cdb3219294991Zbigniew Jędrzejewski-Szmek messages to 0xffffffff.
61c024b328d5493a334242a4d01ba923582093faZbigniew Jędrzejewski-Szmek
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering Section 4.3.2
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
73843b52585d42cc1a970a1c664818ece6942e9eLennart Poettering different subnet. The server MUST set the broadcast bit in the
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
73843b52585d42cc1a970a1c664818ece6942e9eLennart Poettering client, because the client may not have a correct network address
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering or subnet mask, and the client may not be answering ARP requests.
c79e98eadd3056a36a662699fa650db5b1bca0c3Lennart Poettering */
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering if (req->message->giaddr) {
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering destination = req->message->giaddr;
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering if (type == DHCP_NAK)
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering packet->dhcp.flags = htobe16(0x8000);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering } else if (req->message->ciaddr && type != DHCP_NAK)
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering destination = req->message->ciaddr;
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering if (destination || requested_broadcast(req) || type == DHCP_NAK)
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering return dhcp_server_send_udp(server, destination, &packet->dhcp,
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering sizeof(DHCPMessage) + optoffset);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering else
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering /* we cannot send UDP packet to specific MAC address when the address is
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering not yet configured, so must fall back to raw packets */
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering return dhcp_server_send_unicast_raw(server, packet,
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering sizeof(DHCPPacket) + optoffset);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering}
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poetteringstatic int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering uint8_t type, size_t *_optoffset, DHCPRequest *req) {
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering _cleanup_free_ DHCPPacket *packet = NULL;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering size_t optoffset;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering int r;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering assert(server);
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering assert(ret);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(_optoffset);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (!packet)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return -ENOMEM;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering r = dhcp_message_init(&packet->dhcp, BOOTREPLY, be32toh(req->message->xid),
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering type, req->max_optlen, &optoffset);
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering if (r < 0)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return r;
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering packet->dhcp.flags = req->message->flags;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering packet->dhcp.giaddr = req->message->giaddr;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering *_optoffset = optoffset;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering *ret = packet;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering packet = NULL;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return 0;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering}
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmekstatic int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering _cleanup_free_ DHCPPacket *packet = NULL;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering size_t offset;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering be32_t lease_time;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering int r;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (r < 0)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return r;
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering
5c0aa72a4999bdcf03fe93ed5c8213c2b4c681f0Lennart Poettering packet->dhcp.yiaddr = address;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering lease_time = htobe32(req->lifetime);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (r < 0)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return r;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (r < 0)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return r;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
4cd9a9d9ecf3a8835e21930f3215a5f5b74144beLennart Poettering return 0;
6e5abe1564070a760196b97031eca9cf5e95e8a2Zbigniew Jędrzejewski-Szmek}
6e5abe1564070a760196b97031eca9cf5e95e8a2Zbigniew Jędrzejewski-Szmek
6e5abe1564070a760196b97031eca9cf5e95e8a2Zbigniew Jędrzejewski-Szmekstatic int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
6e5abe1564070a760196b97031eca9cf5e95e8a2Zbigniew Jędrzejewski-Szmek _cleanup_free_ DHCPPacket *packet = NULL;
61c024b328d5493a334242a4d01ba923582093faZbigniew Jędrzejewski-Szmek size_t offset;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering be32_t lease_time;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering int r;
61c024b328d5493a334242a4d01ba923582093faZbigniew Jędrzejewski-Szmek
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
1ae464e09376853c52075ec4d8a6bfc4b4036d0cThomas Hindoe Paaboel Andersen if (r < 0)
1ae464e09376853c52075ec4d8a6bfc4b4036d0cThomas Hindoe Paaboel Andersen return r;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering packet->dhcp.yiaddr = address;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering lease_time = htobe32(req->lifetime);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
61c024b328d5493a334242a4d01ba923582093faZbigniew Jędrzejewski-Szmek if (r < 0)
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering return r;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
61c024b328d5493a334242a4d01ba923582093faZbigniew Jędrzejewski-Szmek r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
86b9b8e70d54e79db3ff4f67bbd5280ecfc82537Lennart Poettering if (r < 0)
86b9b8e70d54e79db3ff4f67bbd5280ecfc82537Lennart Poettering return r;
bb99a35a873c35e80b0b47fe045081022660374dLennart Poettering
bb99a35a873c35e80b0b47fe045081022660374dLennart Poettering return 0;
4cd9a9d9ecf3a8835e21930f3215a5f5b74144beLennart Poettering}
4cd9a9d9ecf3a8835e21930f3215a5f5b74144beLennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poetteringstatic int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
4cd9a9d9ecf3a8835e21930f3215a5f5b74144beLennart Poettering _cleanup_free_ DHCPPacket *packet = NULL;
8b38f3cc3eb73adf9536cb73d0f319e60d42ea0cLennart Poettering size_t offset;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering int r;
4cd9a9d9ecf3a8835e21930f3215a5f5b74144beLennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
a6e87e90ede66815989ba2db92a07102a69906feLennart Poettering if (r < 0)
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering return r;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
4cd9a9d9ecf3a8835e21930f3215a5f5b74144beLennart Poettering r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering if (r < 0)
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering return r;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
224f2ee221e77c326d1d7761abb6e812432b2163Lennart Poettering return 0;
224f2ee221e77c326d1d7761abb6e812432b2163Lennart Poettering}
224f2ee221e77c326d1d7761abb6e812432b2163Lennart Poettering
224f2ee221e77c326d1d7761abb6e812432b2163Lennart Poetteringstatic int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering void *user_data) {
553acb7b6b8d4f16a4747b1f978e8b7888fbfb2cZbigniew Jędrzejewski-Szmek DHCPRequest *req = user_data;
61c024b328d5493a334242a4d01ba923582093faZbigniew Jędrzejewski-Szmek
61c024b328d5493a334242a4d01ba923582093faZbigniew Jędrzejewski-Szmek assert(req);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
61c024b328d5493a334242a4d01ba923582093faZbigniew Jędrzejewski-Szmek switch(code) {
61c024b328d5493a334242a4d01ba923582093faZbigniew Jędrzejewski-Szmek case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
61c024b328d5493a334242a4d01ba923582093faZbigniew Jędrzejewski-Szmek if (len == 4)
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering req->lifetime = be32toh(*(be32_t*)option);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering break;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering case DHCP_OPTION_REQUESTED_IP_ADDRESS:
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (len == 4)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering req->requested_ip = *(be32_t*)option;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering break;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering case DHCP_OPTION_SERVER_IDENTIFIER:
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (len == 4)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering req->server_id = *(be32_t*)option;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering break;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering case DHCP_OPTION_CLIENT_IDENTIFIER:
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek if (len >= 2) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering uint8_t *data;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering data = memdup(option, len);
1ae464e09376853c52075ec4d8a6bfc4b4036d0cThomas Hindoe Paaboel Andersen if (!data)
1ae464e09376853c52075ec4d8a6bfc4b4036d0cThomas Hindoe Paaboel Andersen return -ENOMEM;
1ae464e09376853c52075ec4d8a6bfc4b4036d0cThomas Hindoe Paaboel Andersen
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering free(req->client_id.data);
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek req->client_id.data = data;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering req->client_id.length = len;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering break;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (len == 2)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering req->max_optlen = be16toh(*(be16_t*)option) -
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering - sizeof(DHCPPacket);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering break;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return 0;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering}
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poetteringstatic void dhcp_request_free(DHCPRequest *req) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (!req)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering free(req->client_id.data);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering free(req);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering}
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart PoetteringDEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering#define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poetteringstatic int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering assert(req);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering assert(message);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering req->message = message;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering /* set client id based on mac address if client did not send an explicit one */
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (!req->client_id.data) {
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering uint8_t *data;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering data = new0(uint8_t, ETH_ALEN + 1);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (!data)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return -ENOMEM;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering req->client_id.length = ETH_ALEN + 1;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering req->client_id.data = data;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering req->client_id.data[0] = 0x01;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (!req->lifetime)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering req->lifetime = DHCP_DEFAULT_LEASE_TIME;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return 0;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering}
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poetteringstatic int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering assert(server);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (!server->pool_size)
1ae464e09376853c52075ec4d8a6bfc4b4036d0cThomas Hindoe Paaboel Andersen return -EINVAL;
1ae464e09376853c52075ec4d8a6bfc4b4036d0cThomas Hindoe Paaboel Andersen
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (be32toh(requested_ip) < be32toh(server->pool_start) ||
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering be32toh(requested_ip) >= be32toh(server->pool_start) +
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering + server->pool_size)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return -EINVAL;
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return be32toh(requested_ip) - be32toh(server->pool_start);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering}
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poetteringint dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering size_t length) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering DHCPLease *existing_lease;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering int type, r;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(server);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(message);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (message->op != BOOTREQUEST ||
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering message->htype != ARPHRD_ETHER ||
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering message->hlen != ETHER_ADDR_LEN)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return 0;
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering req = new0(DHCPRequest, 1);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (!req)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return -ENOMEM;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering type = dhcp_option_parse(message, length, parse_request, req);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (type < 0)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return 0;
r = ensure_sane_request(req, message);
if (r < 0)
/* this only fails on critical errors */
return r;
existing_lease = hashmap_get(server->leases_by_client_id, &req->client_id);
switch(type) {
case DHCP_DISCOVER:
{
be32_t address = INADDR_ANY;
unsigned i;
log_dhcp_server(server, "DISCOVER (0x%x)",
be32toh(req->message->xid));
if (!server->pool_size)
/* no pool allocated */
return 0;
/* for now pick a random free address from the pool */
if (existing_lease)
address = existing_lease->address;
else {
for (i = 0; i < server->pool_size; i++) {
if (!server->bound_leases[server->next_offer]) {
address = htobe32(be32toh(server->pool_start) + server->next_offer);
break;
} else
server->next_offer = (server->next_offer + 1) % server->pool_size;
}
}
if (address == INADDR_ANY)
/* no free addresses left */
return 0;
r = server_send_offer(server, req, address);
if (r < 0) {
/* this only fails on critical errors */
log_dhcp_server(server, "could not send offer: %s",
strerror(-r));
return r;
} else {
log_dhcp_server(server, "OFFER (0x%x)",
be32toh(req->message->xid));
return DHCP_OFFER;
}
break;
}
case DHCP_DECLINE:
log_dhcp_server(server, "DECLINE (0x%x)",
be32toh(req->message->xid));
/* TODO: make sure we don't offer this address again */
return 1;
break;
case DHCP_REQUEST:
{
be32_t address;
bool init_reboot = false;
int pool_offset;
/* see RFC 2131, section 4.3.2 */
if (req->server_id) {
log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
be32toh(req->message->xid));
/* SELECTING */
if (req->server_id != server->address)
/* client did not pick us */
return 0;
if (req->message->ciaddr)
/* this MUST be zero */
return 0;
if (!req->requested_ip)
/* this must be filled in with the yiaddr
from the chosen OFFER */
return 0;
address = req->requested_ip;
} else if (req->requested_ip) {
log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
be32toh(req->message->xid));
/* INIT-REBOOT */
if (req->message->ciaddr)
/* this MUST be zero */
return 0;
/* TODO: check more carefully if IP is correct */
address = req->requested_ip;
init_reboot = true;
} else {
log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
be32toh(req->message->xid));
/* REBINDING / RENEWING */
if (!req->message->ciaddr)
/* this MUST be filled in with clients IP address */
return 0;
address = req->message->ciaddr;
}
pool_offset = get_pool_offset(server, address);
/* verify that the requested address is from the pool, and either
owned by the current client or free */
if (pool_offset >= 0 &&
server->bound_leases[pool_offset] == existing_lease) {
DHCPLease *lease;
usec_t time_now;
if (!existing_lease) {
lease = new0(DHCPLease, 1);
lease->address = req->requested_ip;
lease->client_id.data = memdup(req->client_id.data,
req->client_id.length);
if (!lease->client_id.data) {
free(lease);
return -ENOMEM;
}
lease->client_id.length = req->client_id.length;
} else
lease = existing_lease;
r = sd_event_now(server->event, CLOCK_MONOTONIC, &time_now);
if (r < 0)
time_now = now(CLOCK_MONOTONIC);
lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
r = server_send_ack(server, req, address);
if (r < 0) {
/* this only fails on critical errors */
log_dhcp_server(server, "could not send ack: %s",
strerror(-r));
if (!existing_lease)
dhcp_lease_free(lease);
return r;
} else {
log_dhcp_server(server, "ACK (0x%x)",
be32toh(req->message->xid));
server->bound_leases[pool_offset] = lease;
hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
return DHCP_ACK;
}
} else if (init_reboot) {
r = server_send_nak(server, req);
if (r < 0) {
/* this only fails on critical errors */
log_dhcp_server(server, "could not send nak: %s",
strerror(-r));
return r;
} else {
log_dhcp_server(server, "NAK (0x%x)",
be32toh(req->message->xid));
return DHCP_NAK;
}
}
break;
}
case DHCP_RELEASE: {
int pool_offset;
log_dhcp_server(server, "RELEASE (0x%x)",
be32toh(req->message->xid));
if (!existing_lease)
return 0;
if (existing_lease->address != req->message->ciaddr)
return 0;
pool_offset = get_pool_offset(server, req->message->ciaddr);
if (pool_offset < 0)
return 0;
if (server->bound_leases[pool_offset] == existing_lease) {
server->bound_leases[pool_offset] = NULL;
hashmap_remove(server->leases_by_client_id, existing_lease);
dhcp_lease_free(existing_lease);
return 1;
} else
return 0;
}
}
return 0;
}
static int server_receive_message(sd_event_source *s, int fd,
uint32_t revents, void *userdata) {
_cleanup_free_ DHCPMessage *message = NULL;
uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
sd_dhcp_server *server = userdata;
struct iovec iov = {};
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = cmsgbuf,
.msg_controllen = sizeof(cmsgbuf),
};
struct cmsghdr *cmsg;
int buflen = 0, len, r;
assert(server);
r = ioctl(fd, FIONREAD, &buflen);
if (r < 0)
return r;
if (buflen < 0)
return -EIO;
message = malloc0(buflen);
if (!message)
return -ENOMEM;
iov.iov_base = message;
iov.iov_len = buflen;
len = recvmsg(fd, &msg, 0);
if (len < buflen)
return 0;
else if ((size_t)len < sizeof(DHCPMessage))
return 0;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IP &&
cmsg->cmsg_type == IP_PKTINFO &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
/* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
if (server->index != info->ipi_ifindex)
return 0;
break;
}
}
return dhcp_server_handle_message(server, message, (size_t)len);
}
int sd_dhcp_server_start(sd_dhcp_server *server) {
int r;
assert_return(server, -EINVAL);
assert_return(server->event, -EINVAL);
assert_return(!server->receive_message, -EBUSY);
assert_return(server->fd_raw == -1, -EBUSY);
assert_return(server->fd == -1, -EBUSY);
assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
if (r < 0) {
r = -errno;
sd_dhcp_server_stop(server);
return r;
}
server->fd_raw = r;
r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
if (r < 0) {
sd_dhcp_server_stop(server);
return r;
}
server->fd = r;
r = sd_event_add_io(server->event, &server->receive_message,
server->fd, EPOLLIN,
server_receive_message, server);
if (r < 0) {
sd_dhcp_server_stop(server);
return r;
}
r = sd_event_source_set_priority(server->receive_message,
server->event_priority);
if (r < 0) {
sd_dhcp_server_stop(server);
return r;
}
log_dhcp_server(server, "STARTED");
return 0;
}