b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen/***
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen This file is part of systemd.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Copyright (C) 2013 Intel Corporation. All rights reserved.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Copyright (C) 2014 Tom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen systemd is free software; you can redistribute it and/or modify it
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen under the terms of the GNU Lesser General Public License as published by
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen (at your option) any later version.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen systemd is distributed in the hope that it will be useful, but
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Lesser General Public License for more details.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen You should have received a copy of the GNU Lesser General Public License
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen***/
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen#include <sys/ioctl.h>
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "sd-dhcp-server.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering
b5efdb8af40ea759a1ea584c1bc44ecc81dd00ceLennart Poettering#include "alloc-util.h"
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen#include "dhcp-internal.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "dhcp-server-internal.h"
3ffd4af22052963e7a29431721ee204e634bea75Lennart Poettering#include "fd-util.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "in-addr-util.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "siphash24.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "string-util.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
99634696183dfabae20104e58157c69029a11594Tom Gundersen * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
99634696183dfabae20104e58157c69029a11594Tom Gundersen * moreover, the server's own address may be in the pool, and is in that case reserved in order not to
99634696183dfabae20104e58157c69029a11594Tom Gundersen * accidentally hand it out */
99634696183dfabae20104e58157c69029a11594Tom Gundersenint sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) {
99634696183dfabae20104e58157c69029a11594Tom Gundersen struct in_addr netmask_addr;
99634696183dfabae20104e58157c69029a11594Tom Gundersen be32_t netmask;
99634696183dfabae20104e58157c69029a11594Tom Gundersen uint32_t server_off, broadcast_off, size_max;
99634696183dfabae20104e58157c69029a11594Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen assert_return(server, -EINVAL);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen assert_return(address, -EINVAL);
99634696183dfabae20104e58157c69029a11594Tom Gundersen assert_return(address->s_addr != INADDR_ANY, -EINVAL);
99634696183dfabae20104e58157c69029a11594Tom Gundersen assert_return(prefixlen <= 32, -ERANGE);
99634696183dfabae20104e58157c69029a11594Tom Gundersen assert_return(server->address == INADDR_ANY, -EBUSY);
99634696183dfabae20104e58157c69029a11594Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen assert_se(in_addr_prefixlen_to_netmask(&netmask_addr, prefixlen));
99634696183dfabae20104e58157c69029a11594Tom Gundersen netmask = netmask_addr.s_addr;
99634696183dfabae20104e58157c69029a11594Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen server_off = be32toh(address->s_addr & ~netmask);
99634696183dfabae20104e58157c69029a11594Tom Gundersen broadcast_off = be32toh(~netmask);
99634696183dfabae20104e58157c69029a11594Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen /* the server address cannot be the subnet address */
99634696183dfabae20104e58157c69029a11594Tom Gundersen assert_return(server_off != 0, -ERANGE);
99634696183dfabae20104e58157c69029a11594Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen /* nor the broadcast address */
99634696183dfabae20104e58157c69029a11594Tom Gundersen assert_return(server_off != broadcast_off, -ERANGE);
99634696183dfabae20104e58157c69029a11594Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen /* 0 offset means we should set a default, we skip the first (subnet) address
99634696183dfabae20104e58157c69029a11594Tom Gundersen and take the next one */
99634696183dfabae20104e58157c69029a11594Tom Gundersen if (offset == 0)
99634696183dfabae20104e58157c69029a11594Tom Gundersen offset = 1;
99634696183dfabae20104e58157c69029a11594Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen size_max = (broadcast_off + 1) /* the number of addresses in the subnet */
99634696183dfabae20104e58157c69029a11594Tom Gundersen - offset /* exclude the addresses before the offset */
99634696183dfabae20104e58157c69029a11594Tom Gundersen - 1; /* exclude the last (broadcast) address */
99634696183dfabae20104e58157c69029a11594Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen /* The pool must contain at least one address */
99634696183dfabae20104e58157c69029a11594Tom Gundersen assert_return(size_max >= 1, -ERANGE);
99634696183dfabae20104e58157c69029a11594Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen if (size != 0)
99634696183dfabae20104e58157c69029a11594Tom Gundersen assert_return(size <= size_max, -ERANGE);
99634696183dfabae20104e58157c69029a11594Tom Gundersen else
99634696183dfabae20104e58157c69029a11594Tom Gundersen size = size_max;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen server->bound_leases = new0(DHCPLease*, size);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (!server->bound_leases)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return -ENOMEM;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen server->pool_offset = offset;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen server->pool_size = size;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen server->address = address->s_addr;
99634696183dfabae20104e58157c69029a11594Tom Gundersen server->netmask = netmask;
99634696183dfabae20104e58157c69029a11594Tom Gundersen server->subnet = address->s_addr & netmask;
99634696183dfabae20104e58157c69029a11594Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen if (server_off >= offset && server_off - offset < size)
99634696183dfabae20104e58157c69029a11594Tom Gundersen server->bound_leases[server_off - offset] = &server->invalid_lease;
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen return 0;
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen}
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen
04c0136989b7eb896bfb0fb176e11233d69e1453Lennart Poetteringint sd_dhcp_server_is_running(sd_dhcp_server *server) {
756775814cf69471f74ce853745bba69f2ba94efThomas Hindoe Paaboel Andersen assert_return(server, false);
7c16313f11e3953f3fe4dbf544f2d36f58d14138Tom Gundersen
7c16313f11e3953f3fe4dbf544f2d36f58d14138Tom Gundersen return !!server->receive_message;
7c16313f11e3953f3fe4dbf544f2d36f58d14138Tom Gundersen}
7c16313f11e3953f3fe4dbf544f2d36f58d14138Tom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersensd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
3733eec3e292e4ddb4cba5eb8d3bd8cbee7102d8Lennart Poettering
3733eec3e292e4ddb4cba5eb8d3bd8cbee7102d8Lennart Poettering if (!server)
3733eec3e292e4ddb4cba5eb8d3bd8cbee7102d8Lennart Poettering return NULL;
3733eec3e292e4ddb4cba5eb8d3bd8cbee7102d8Lennart Poettering
3733eec3e292e4ddb4cba5eb8d3bd8cbee7102d8Lennart Poettering assert(server->n_ref >= 1);
3733eec3e292e4ddb4cba5eb8d3bd8cbee7102d8Lennart Poettering server->n_ref++;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return server;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen}
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersenvoid client_id_hash_func(const void *p, struct siphash *state) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen const DHCPClientId *id = p;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen assert(id);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen assert(id->length);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen assert(id->data);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
1e2527a6fede996a429bd44b30a15e76ee293437Tom Gundersen siphash24_compress(&id->length, sizeof(id->length), state);
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen siphash24_compress(id->data, id->length, state);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen}
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersenint client_id_compare_func(const void *_a, const void *_b) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen const DHCPClientId *a, *b;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen a = _a;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen b = _b;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen assert(!a->length || a->data);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen assert(!b->length || b->data);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (a->length != b->length)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return a->length < b->length ? -1 : 1;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return memcmp(a->data, b->data, a->length);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen}
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidtstatic const struct hash_ops client_id_hash_ops = {
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt .hash = client_id_hash_func,
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt .compare = client_id_compare_func
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt};
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersenstatic void dhcp_lease_free(DHCPLease *lease) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (!lease)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen free(lease->client_id.data);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen free(lease);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen}
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersensd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering DHCPLease *lease;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering if (!server)
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering return NULL;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
3733eec3e292e4ddb4cba5eb8d3bd8cbee7102d8Lennart Poettering assert(server->n_ref >= 1);
3733eec3e292e4ddb4cba5eb8d3bd8cbee7102d8Lennart Poettering server->n_ref--;
3733eec3e292e4ddb4cba5eb8d3bd8cbee7102d8Lennart Poettering
3733eec3e292e4ddb4cba5eb8d3bd8cbee7102d8Lennart Poettering if (server->n_ref > 0)
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering return NULL;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering log_dhcp_server(server, "UNREF");
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering sd_dhcp_server_stop(server);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering sd_event_unref(server->event);
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering free(server->timezone);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering free(server->dns);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering free(server->ntp);
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering while ((lease = hashmap_steal_first(server->leases_by_client_id)))
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering dhcp_lease_free(lease);
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering hashmap_free(server->leases_by_client_id);
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering free(server->bound_leases);
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering free(server);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return NULL;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen}
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersenint sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
4afd3348c7506dd1d36305b7bcb9feb8952b9d6bLennart Poettering _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen assert_return(ret, -EINVAL);
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen assert_return(ifindex > 0, -EINVAL);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen server = new0(sd_dhcp_server, 1);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (!server)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return -ENOMEM;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
3733eec3e292e4ddb4cba5eb8d3bd8cbee7102d8Lennart Poettering server->n_ref = 1;
8de4a226c71ef43e652274b33b5d19211a44ac7bTom Gundersen server->fd_raw = -1;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen server->fd = -1;
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen server->address = htobe32(INADDR_ANY);
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen server->netmask = htobe32(INADDR_ANY);
b3ec603ce8053ba3f95da1d36f15ea762c83d1e1Lennart Poettering server->ifindex = ifindex;
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt server->leases_by_client_id = hashmap_new(&client_id_hash_ops);
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC);
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen *ret = server;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen server = NULL;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return 0;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen}
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersenint sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen int priority) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen int r;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen assert_return(server, -EINVAL);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen assert_return(!server->event, -EBUSY);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (event)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen server->event = sd_event_ref(event);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen else {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen r = sd_event_default(&server->event);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (r < 0)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return r;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen }
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen server->event_priority = priority;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return 0;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen}
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenint sd_dhcp_server_detach_event(sd_dhcp_server *server) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen assert_return(server, -EINVAL);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen server->event = sd_event_unref(server->event);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return 0;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen}
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersensd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen assert_return(server, NULL);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return server->event;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen}
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersenint sd_dhcp_server_stop(sd_dhcp_server *server) {
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert_return(server, -EINVAL);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen server->receive_message =
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen sd_event_source_unref(server->receive_message);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
8de4a226c71ef43e652274b33b5d19211a44ac7bTom Gundersen server->fd_raw = safe_close(server->fd_raw);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen server->fd = safe_close(server->fd);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen log_dhcp_server(server, "STOPPED");
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen return 0;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen}
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersenstatic int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen DHCPPacket *packet, size_t len) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen union sockaddr_union link = {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .ll.sll_family = AF_PACKET,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .ll.sll_protocol = htons(ETH_P_IP),
b3ec603ce8053ba3f95da1d36f15ea762c83d1e1Lennart Poettering .ll.sll_ifindex = server->ifindex,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .ll.sll_halen = ETH_ALEN,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen };
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(server);
b3ec603ce8053ba3f95da1d36f15ea762c83d1e1Lennart Poettering assert(server->ifindex > 0);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(server->address);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(packet);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(len > sizeof(DHCPPacket));
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen packet->dhcp.yiaddr,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen DHCP_PORT_CLIENT, len);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
b3ec603ce8053ba3f95da1d36f15ea762c83d1e1Lennart Poettering return dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen}
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenstatic int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen DHCPMessage *message, size_t len) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen union sockaddr_union dest = {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .in.sin_family = AF_INET,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .in.sin_port = htobe16(DHCP_PORT_CLIENT),
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .in.sin_addr.s_addr = destination,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen };
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen struct iovec iov = {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .iov_base = message,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .iov_len = len,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen };
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen struct msghdr msg = {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .msg_name = &dest,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .msg_namelen = sizeof(dest.in),
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .msg_iov = &iov,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .msg_iovlen = 1,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .msg_control = cmsgbuf,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .msg_controllen = sizeof(cmsgbuf),
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen };
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen struct cmsghdr *cmsg;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen struct in_pktinfo *pktinfo;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen int r;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(server);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(server->fd > 0);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(message);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(len > sizeof(DHCPMessage));
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen cmsg = CMSG_FIRSTHDR(&msg);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(cmsg);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen cmsg->cmsg_level = IPPROTO_IP;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen cmsg->cmsg_type = IP_PKTINFO;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen /* we attach source interface and address info to the message
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen rather than binding the socket. This will be mostly useful
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen when we gain support for arbitrary number of server addresses
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen */
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(pktinfo);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
b3ec603ce8053ba3f95da1d36f15ea762c83d1e1Lennart Poettering pktinfo->ipi_ifindex = server->ifindex;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen pktinfo->ipi_spec_dst.s_addr = server->address;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen r = sendmsg(server->fd, &msg, 0);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen if (r < 0)
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return -errno;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return 0;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen}
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenstatic bool requested_broadcast(DHCPRequest *req) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(req);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return req->message->flags & htobe16(0x8000);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen}
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenint dhcp_server_send_packet(sd_dhcp_server *server,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen DHCPRequest *req, DHCPPacket *packet,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen int type, size_t optoffset) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen be32_t destination = INADDR_ANY;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen int r;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(server);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(req);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(req->max_optlen);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(optoffset <= req->max_optlen);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(packet);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_SERVER_IDENTIFIER,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen 4, &server->address);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen if (r < 0)
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return r;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_END, 0, NULL);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen if (r < 0)
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return r;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen /* RFC 2131 Section 4.1
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen If the ’giaddr’ field in a DHCP message from a client is non-zero,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen the server sends any return messages to the ’DHCP server’ port on the
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen field is zero and the ’ciaddr’ field is nonzero, then the server
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen set, then the server broadcasts DHCPOFFER and DHCPACK messages to
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen messages to the client’s hardware address and ’yiaddr’ address. In
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen messages to 0xffffffff.
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen Section 4.3.2
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen different subnet. The server MUST set the broadcast bit in the
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen client, because the client may not have a correct network address
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen or subnet mask, and the client may not be answering ARP requests.
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen */
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen if (req->message->giaddr) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen destination = req->message->giaddr;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen if (type == DHCP_NAK)
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen packet->dhcp.flags = htobe16(0x8000);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen } else if (req->message->ciaddr && type != DHCP_NAK)
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen destination = req->message->ciaddr;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
d6bd972d061af306ede2affd2c9340a1660f7996Tom Gundersen if (destination != INADDR_ANY)
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return dhcp_server_send_udp(server, destination, &packet->dhcp,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen sizeof(DHCPMessage) + optoffset);
d6bd972d061af306ede2affd2c9340a1660f7996Tom Gundersen else if (requested_broadcast(req) || type == DHCP_NAK)
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen return dhcp_server_send_udp(server, INADDR_BROADCAST,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen &packet->dhcp,
d6bd972d061af306ede2affd2c9340a1660f7996Tom Gundersen sizeof(DHCPMessage) + optoffset);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen else
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen /* we cannot send UDP packet to specific MAC address when the
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen address is not yet configured, so must fall back to raw
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen packets */
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return dhcp_server_send_unicast_raw(server, packet,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen sizeof(DHCPPacket) + optoffset);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen}
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersenstatic int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen uint8_t type, size_t *_optoffset,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen DHCPRequest *req) {
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen _cleanup_free_ DHCPPacket *packet = NULL;
1231c4d238844e77018caf5b5852f01d96373c47Tom Gundersen size_t optoffset = 0;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen int r;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen assert(server);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen assert(ret);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen assert(_optoffset);
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen if (!packet)
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen return -ENOMEM;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen r = dhcp_message_init(&packet->dhcp, BOOTREPLY,
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams be32toh(req->message->xid), type, ARPHRD_ETHER,
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams req->max_optlen, &optoffset);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen if (r < 0)
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen return r;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen packet->dhcp.flags = req->message->flags;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen packet->dhcp.giaddr = req->message->giaddr;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen *_optoffset = optoffset;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen *ret = packet;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen packet = NULL;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen return 0;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen}
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersenstatic int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen be32_t address) {
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen _cleanup_free_ DHCPPacket *packet = NULL;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen size_t offset;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen be32_t lease_time;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen int r;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen if (r < 0)
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen return r;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen packet->dhcp.yiaddr = address;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen lease_time = htobe32(req->lifetime);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen &lease_time);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen if (r < 0)
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen return r;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen if (r < 0)
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen return r;
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_ROUTER, 4, &server->address);
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen if (r < 0)
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen return r;
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen if (r < 0)
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen return r;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen return 0;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen}
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersenstatic int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen be32_t address) {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen _cleanup_free_ DHCPPacket *packet = NULL;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen size_t offset;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen be32_t lease_time;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen int r;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (r < 0)
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return r;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen packet->dhcp.yiaddr = address;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen lease_time = htobe32(req->lifetime);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen &lease_time);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (r < 0)
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return r;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen if (r < 0)
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen return r;
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_ROUTER, 4, &server->address);
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen if (r < 0)
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen return r;
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering if (server->n_dns > 0) {
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering r = dhcp_option_append(
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering &packet->dhcp, req->max_optlen, &offset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering sizeof(struct in_addr) * server->n_dns, server->dns);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering if (r < 0)
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering return r;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering }
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering if (server->n_ntp > 0) {
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering r = dhcp_option_append(
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering &packet->dhcp, req->max_optlen, &offset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_NTP_SERVER,
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering sizeof(struct in_addr) * server->n_ntp, server->ntp);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering if (r < 0)
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering return r;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering }
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering if (server->timezone) {
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering r = dhcp_option_append(
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering &packet->dhcp, req->max_optlen, &offset, 0,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani SD_DHCP_OPTION_NEW_TZDB_TIMEZONE,
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering strlen(server->timezone), server->timezone);
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering if (r < 0)
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering return r;
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering }
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (r < 0)
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return r;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return 0;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen}
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersenstatic int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen _cleanup_free_ DHCPPacket *packet = NULL;
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen size_t offset;
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen int r;
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen if (r < 0)
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen return r;
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen
b3ec603ce8053ba3f95da1d36f15ea762c83d1e1Lennart Poettering return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen}
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersenstatic int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen be32_t gateway, uint8_t chaddr[]) {
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen _cleanup_free_ DHCPPacket *packet = NULL;
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen size_t optoffset = 0;
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen int r;
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen assert(server);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen assert(address != INADDR_ANY);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen assert(chaddr);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen packet = malloc0(sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen if (!packet)
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen return -ENOMEM;
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0,
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams DHCP_FORCERENEW, ARPHRD_ETHER,
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams DHCP_MIN_OPTIONS_SIZE, &optoffset);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen if (r < 0)
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen return r;
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE,
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen if (r < 0)
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen return r;
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen r = dhcp_server_send_udp(server, address, &packet->dhcp,
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen sizeof(DHCPMessage) + optoffset);
c15fb62a731f1a457af94e60ac6a4d23f219a8f6Thomas Hindoe Paaboel Andersen if (r < 0)
c15fb62a731f1a457af94e60ac6a4d23f219a8f6Thomas Hindoe Paaboel Andersen return r;
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen return 0;
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen}
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen
b3ec603ce8053ba3f95da1d36f15ea762c83d1e1Lennart Poetteringstatic int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) {
89ca10c6a61309d84d54c5dc5a295387ce39e610Lennart Poettering DHCPRequest *req = userdata;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen assert(req);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen switch(code) {
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen if (len == 4)
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen req->lifetime = be32toh(*(be32_t*)option);
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen break;
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani case SD_DHCP_OPTION_REQUESTED_IP_ADDRESS:
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (len == 4)
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen req->requested_ip = *(be32_t*)option;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen break;
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani case SD_DHCP_OPTION_SERVER_IDENTIFIER:
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen if (len == 4)
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen req->server_id = *(be32_t*)option;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen break;
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen if (len >= 2) {
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen uint8_t *data;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen data = memdup(option, len);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen if (!data)
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen return -ENOMEM;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen free(req->client_id.data);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen req->client_id.data = data;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen req->client_id.length = len;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen }
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen break;
22805d9207d5242681e5667ee304572e4abf9b94Beniamino Galvani case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen if (len == 2)
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen req->max_optlen = be16toh(*(be16_t*)option) -
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen - sizeof(DHCPPacket);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen break;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen }
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen return 0;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen}
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersenstatic void dhcp_request_free(DHCPRequest *req) {
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen if (!req)
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen return;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen free(req->client_id.data);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen free(req);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen}
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom GundersenDEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen#define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poetteringstatic int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) {
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen assert(req);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen assert(message);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen req->message = message;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
e2acdb6b0f68d9b4152708a9f21bf9e11f8b9e7eTorstein Husebø /* set client id based on MAC address if client did not send an explicit
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen one */
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen if (!req->client_id.data) {
9a0f246fcdb1238e6b3397169a10095f4df89210Lennart Poettering void *data;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
9a0f246fcdb1238e6b3397169a10095f4df89210Lennart Poettering data = malloc0(ETH_ALEN + 1);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen if (!data)
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen return -ENOMEM;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
9a0f246fcdb1238e6b3397169a10095f4df89210Lennart Poettering ((uint8_t*) data)[0] = 0x01;
9a0f246fcdb1238e6b3397169a10095f4df89210Lennart Poettering memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN);
9a0f246fcdb1238e6b3397169a10095f4df89210Lennart Poettering
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen req->client_id.length = ETH_ALEN + 1;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen req->client_id.data = data;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen }
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
9a0f246fcdb1238e6b3397169a10095f4df89210Lennart Poettering if (req->lifetime <= 0)
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering req->lifetime = MAX(1ULL, server->default_lease_time);
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering if (server->max_lease_time > 0 && req->lifetime > server->max_lease_time)
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering req->lifetime = server->max_lease_time;
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen return 0;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen}
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersenstatic int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen assert(server);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (!server->pool_size)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return -EINVAL;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) ||
99634696183dfabae20104e58157c69029a11594Tom Gundersen be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size)))
99634696183dfabae20104e58157c69029a11594Tom Gundersen return -ERANGE;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen return be32toh(requested_ip & ~server->netmask) - server->pool_offset;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen}
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen
be077570f779664ed87b50f60608df9fbe258821Tom Gundersenint dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen size_t length) {
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
f693e9b38f13575b988335e3324a50dcc8772d48Tom Gundersen _cleanup_free_ char *error_message = NULL;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen DHCPLease *existing_lease;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen int type, r;
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen assert(server);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen assert(message);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen if (message->op != BOOTREQUEST ||
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen message->htype != ARPHRD_ETHER ||
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen message->hlen != ETHER_ADDR_LEN)
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return 0;
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen req = new0(DHCPRequest, 1);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen if (!req)
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen return -ENOMEM;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
f693e9b38f13575b988335e3324a50dcc8772d48Tom Gundersen type = dhcp_option_parse(message, length, parse_request, req, &error_message);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen if (type < 0)
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return 0;
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering r = ensure_sane_request(server, req, message);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen if (r < 0)
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen /* this only fails on critical errors */
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen return r;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen existing_lease = hashmap_get(server->leases_by_client_id,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen &req->client_id);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen switch(type) {
9a0f246fcdb1238e6b3397169a10095f4df89210Lennart Poettering
9a0f246fcdb1238e6b3397169a10095f4df89210Lennart Poettering case DHCP_DISCOVER: {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen be32_t address = INADDR_ANY;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen unsigned i;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen log_dhcp_server(server, "DISCOVER (0x%x)",
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen be32toh(req->message->xid));
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (!server->pool_size)
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* no pool allocated */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return 0;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen /* for now pick a random free address from the pool */
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (existing_lease)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen address = existing_lease->address;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen else {
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen struct siphash state;
0cb3c286883b694fc52a18a3b559ff98931641f3Tom Gundersen uint64_t hash;
99634696183dfabae20104e58157c69029a11594Tom Gundersen uint32_t next_offer;
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen /* even with no persistence of leases, we try to offer the same client
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen the same IP address. we do this by using the hash of the client id
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen as the offset into the pool of leases when finding the next free one */
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen
0cb3c286883b694fc52a18a3b559ff98931641f3Tom Gundersen siphash24_init(&state, HASH_KEY.bytes);
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen client_id_hash_func(&req->client_id, &state);
933f9caeeb2b3c1b951d330e04beb04226e5a890Daniel Mack hash = htole64(siphash24_finalize(&state));
0cb3c286883b694fc52a18a3b559ff98931641f3Tom Gundersen next_offer = hash % server->pool_size;
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen for (i = 0; i < server->pool_size; i++) {
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen if (!server->bound_leases[next_offer]) {
99634696183dfabae20104e58157c69029a11594Tom Gundersen address = server->subnet | htobe32(server->pool_offset + next_offer);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen break;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen } else
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersen next_offer = (next_offer + 1) % server->pool_size;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen }
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen }
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (address == INADDR_ANY)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen /* no free addresses left */
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return 0;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = server_send_offer(server, req, address);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen if (r < 0) {
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen /* this only fails on critical errors */
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen log_dhcp_server(server, "could not send offer: %s",
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen strerror(-r));
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen return r;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen } else {
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen log_dhcp_server(server, "OFFER (0x%x)",
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen be32toh(req->message->xid));
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen return DHCP_OFFER;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen }
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen break;
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen }
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen case DHCP_DECLINE:
f693e9b38f13575b988335e3324a50dcc8772d48Tom Gundersen log_dhcp_server(server, "DECLINE (0x%x): %s", be32toh(req->message->xid), strna(error_message));
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen /* TODO: make sure we don't offer this address again */
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen return 1;
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen
9a0f246fcdb1238e6b3397169a10095f4df89210Lennart Poettering case DHCP_REQUEST: {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen be32_t address;
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen bool init_reboot = false;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen int pool_offset;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* see RFC 2131, section 4.3.2 */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (req->server_id) {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen be32toh(req->message->xid));
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* SELECTING */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (req->server_id != server->address)
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* client did not pick us */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return 0;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (req->message->ciaddr)
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* this MUST be zero */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return 0;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (!req->requested_ip)
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* this must be filled in with the yiaddr
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen from the chosen OFFER */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return 0;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen address = req->requested_ip;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen } else if (req->requested_ip) {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen be32toh(req->message->xid));
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* INIT-REBOOT */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (req->message->ciaddr)
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* this MUST be zero */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return 0;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen /* TODO: check more carefully if IP is correct */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen address = req->requested_ip;
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen init_reboot = true;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen } else {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen be32toh(req->message->xid));
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* REBINDING / RENEWING */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (!req->message->ciaddr)
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* this MUST be filled in with clients IP address */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return 0;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen address = req->message->ciaddr;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen }
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen pool_offset = get_pool_offset(server, address);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen /* verify that the requested address is from the pool, and either
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen owned by the current client or free */
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (pool_offset >= 0 &&
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen server->bound_leases[pool_offset] == existing_lease) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen DHCPLease *lease;
a7f7d1bde43fc825c49afea3f946f5b4b3d563e0Harald Hoyer usec_t time_now = 0;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (!existing_lease) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen lease = new0(DHCPLease, 1);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen lease->address = req->requested_ip;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen lease->client_id.data = memdup(req->client_id.data,
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen req->client_id.length);
02557f973aed0fed7154fefe53d67e2935f918dcThomas Hindoe Paaboel Andersen if (!lease->client_id.data) {
02557f973aed0fed7154fefe53d67e2935f918dcThomas Hindoe Paaboel Andersen free(lease);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return -ENOMEM;
02557f973aed0fed7154fefe53d67e2935f918dcThomas Hindoe Paaboel Andersen }
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen lease->client_id.length = req->client_id.length;
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen memcpy(&lease->chaddr, &req->message->chaddr,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen ETH_ALEN);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen lease->gateway = req->message->giaddr;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen } else
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen lease = existing_lease;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen r = sd_event_now(server->event,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen clock_boottime_or_monotonic(),
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen &time_now);
bff92d2c2f913c368d80066ee89855665818edc8Susant Sahani if (r < 0) {
bff92d2c2f913c368d80066ee89855665818edc8Susant Sahani if (!existing_lease)
bff92d2c2f913c368d80066ee89855665818edc8Susant Sahani dhcp_lease_free(lease);
38a03f06a7393d2721c23f23f0589d2f6d0904afLennart Poettering return r;
bff92d2c2f913c368d80066ee89855665818edc8Susant Sahani }
bff92d2c2f913c368d80066ee89855665818edc8Susant Sahani
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = server_send_ack(server, req, address);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (r < 0) {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen /* this only fails on critical errors */
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen log_dhcp_server(server, "could not send ack: %s",
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen strerror(-r));
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (!existing_lease)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen dhcp_lease_free(lease);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return r;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen } else {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen log_dhcp_server(server, "ACK (0x%x)",
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen be32toh(req->message->xid));
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen server->bound_leases[pool_offset] = lease;
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen hashmap_put(server->leases_by_client_id,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen &lease->client_id, lease);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return DHCP_ACK;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen }
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen } else if (init_reboot) {
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen r = server_send_nak(server, req);
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen if (r < 0) {
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen /* this only fails on critical errors */
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen log_dhcp_server(server, "could not send nak: %s",
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen strerror(-r));
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen return r;
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen } else {
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen log_dhcp_server(server, "NAK (0x%x)",
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen be32toh(req->message->xid));
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen return DHCP_NAK;
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen }
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen }
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen break;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen }
9a0f246fcdb1238e6b3397169a10095f4df89210Lennart Poettering
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen case DHCP_RELEASE: {
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen int pool_offset;
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen log_dhcp_server(server, "RELEASE (0x%x)",
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen be32toh(req->message->xid));
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen if (!existing_lease)
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen return 0;
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen if (existing_lease->address != req->message->ciaddr)
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen return 0;
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen pool_offset = get_pool_offset(server, req->message->ciaddr);
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen if (pool_offset < 0)
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen return 0;
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen if (server->bound_leases[pool_offset] == existing_lease) {
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen server->bound_leases[pool_offset] = NULL;
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen hashmap_remove(server->leases_by_client_id, existing_lease);
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen dhcp_lease_free(existing_lease);
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen return 1;
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen } else
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen return 0;
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen }
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen }
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen return 0;
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen}
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersenstatic int server_receive_message(sd_event_source *s, int fd,
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen uint32_t revents, void *userdata) {
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen _cleanup_free_ DHCPMessage *message = NULL;
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen sd_dhcp_server *server = userdata;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen struct iovec iov = {};
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen struct msghdr msg = {
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen .msg_iov = &iov,
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen .msg_iovlen = 1,
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen .msg_control = cmsgbuf,
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen .msg_controllen = sizeof(cmsgbuf),
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen };
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen struct cmsghdr *cmsg;
966d74c043098e12d4d5b101aa7650244c14b815Lennart Poettering int buflen = 0, len;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert(server);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
966d74c043098e12d4d5b101aa7650244c14b815Lennart Poettering if (ioctl(fd, FIONREAD, &buflen) < 0)
966d74c043098e12d4d5b101aa7650244c14b815Lennart Poettering return -errno;
0d43d2fcb7ac5264c739dc2f67f93ed0985a418aTom Gundersen else if (buflen < 0)
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen return -EIO;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
0d43d2fcb7ac5264c739dc2f67f93ed0985a418aTom Gundersen message = malloc(buflen);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen if (!message)
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen return -ENOMEM;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen iov.iov_base = message;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen iov.iov_len = buflen;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
a38d99451f2bf8026ec51aee91662292e823c6a8Lennart Poettering len = recvmsg(fd, &msg, 0);
0d43d2fcb7ac5264c739dc2f67f93ed0985a418aTom Gundersen if (len < 0) {
0d43d2fcb7ac5264c739dc2f67f93ed0985a418aTom Gundersen if (errno == EAGAIN || errno == EINTR)
0d43d2fcb7ac5264c739dc2f67f93ed0985a418aTom Gundersen return 0;
0d43d2fcb7ac5264c739dc2f67f93ed0985a418aTom Gundersen
0d43d2fcb7ac5264c739dc2f67f93ed0985a418aTom Gundersen return -errno;
0d43d2fcb7ac5264c739dc2f67f93ed0985a418aTom Gundersen } else if ((size_t)len < sizeof(DHCPMessage))
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return 0;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
2a1288ff89322a2f49c79f6d1832c8164c14a05cLennart Poettering CMSG_FOREACH(cmsg, &msg) {
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen if (cmsg->cmsg_level == IPPROTO_IP &&
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen cmsg->cmsg_type == IP_PKTINFO &&
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen /* TODO figure out if this can be done as a filter on
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen * the socket, like for IPv6 */
b3ec603ce8053ba3f95da1d36f15ea762c83d1e1Lennart Poettering if (server->ifindex != info->ipi_ifindex)
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen return 0;
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen break;
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen }
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen }
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return dhcp_server_handle_message(server, message, (size_t)len);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen}
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersenint sd_dhcp_server_start(sd_dhcp_server *server) {
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen int r;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert_return(server, -EINVAL);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert_return(server->event, -EINVAL);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert_return(!server->receive_message, -EBUSY);
8de4a226c71ef43e652274b33b5d19211a44ac7bTom Gundersen assert_return(server->fd_raw == -1, -EBUSY);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert_return(server->fd == -1, -EBUSY);
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
8de4a226c71ef43e652274b33b5d19211a44ac7bTom Gundersen r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
8de4a226c71ef43e652274b33b5d19211a44ac7bTom Gundersen if (r < 0) {
8de4a226c71ef43e652274b33b5d19211a44ac7bTom Gundersen r = -errno;
8de4a226c71ef43e652274b33b5d19211a44ac7bTom Gundersen sd_dhcp_server_stop(server);
8de4a226c71ef43e652274b33b5d19211a44ac7bTom Gundersen return r;
8de4a226c71ef43e652274b33b5d19211a44ac7bTom Gundersen }
8de4a226c71ef43e652274b33b5d19211a44ac7bTom Gundersen server->fd_raw = r;
8de4a226c71ef43e652274b33b5d19211a44ac7bTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen if (r < 0) {
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen sd_dhcp_server_stop(server);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen return r;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen }
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen server->fd = r;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen r = sd_event_add_io(server->event, &server->receive_message,
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen server->fd, EPOLLIN,
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen server_receive_message, server);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen if (r < 0) {
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen sd_dhcp_server_stop(server);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen return r;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen }
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen r = sd_event_source_set_priority(server->receive_message,
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen server->event_priority);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen if (r < 0) {
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen sd_dhcp_server_stop(server);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen return r;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen }
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen log_dhcp_server(server, "STARTED");
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen return 0;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen}
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersenint sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen unsigned i;
621ac3d2cc8f37169166df9c7f379b0cb6b17e36Thomas Hindoe Paaboel Andersen int r = 0;
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen assert_return(server, -EINVAL);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen assert(server->bound_leases);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen for (i = 0; i < server->pool_size; i++) {
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen DHCPLease *lease = server->bound_leases[i];
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen
99634696183dfabae20104e58157c69029a11594Tom Gundersen if (!lease || lease == &server->invalid_lease)
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen continue;
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen r = server_send_forcerenew(server, lease->address,
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen lease->gateway,
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen lease->chaddr);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen if (r < 0)
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen return r;
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen else
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen log_dhcp_server(server, "FORCERENEW");
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen }
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen return r;
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen}
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering
64d6c22905e94b02e760747c6c143dc9893083d9Thomas Hindoe Paaboel Andersenint sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) {
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering int r;
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering assert_return(server, -EINVAL);
64d6c22905e94b02e760747c6c143dc9893083d9Thomas Hindoe Paaboel Andersen assert_return(timezone_is_valid(tz), -EINVAL);
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering
64d6c22905e94b02e760747c6c143dc9893083d9Thomas Hindoe Paaboel Andersen if (streq_ptr(tz, server->timezone))
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering return 0;
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering
64d6c22905e94b02e760747c6c143dc9893083d9Thomas Hindoe Paaboel Andersen r = free_and_strdup(&server->timezone, tz);
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering if (r < 0)
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering return r;
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering return 1;
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering}
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poetteringint sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t) {
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering assert_return(server, -EINVAL);
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering if (t == server->max_lease_time)
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering return 0;
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering server->max_lease_time = t;
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering return 1;
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering}
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poetteringint sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t) {
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering assert_return(server, -EINVAL);
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering if (t == server->default_lease_time)
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering return 0;
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering server->default_lease_time = t;
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering return 1;
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poettering}
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poetteringint sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n) {
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering assert_return(server, -EINVAL);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering assert_return(dns || n <= 0, -EINVAL);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering if (server->n_dns == n &&
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering memcmp(server->dns, dns, sizeof(struct in_addr) * n) == 0)
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering return 0;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering if (n <= 0) {
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering server->dns = mfree(server->dns);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering server->n_dns = 0;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering } else {
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering struct in_addr *c;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering c = newdup(struct in_addr, dns, n);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering if (!c)
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering return -ENOMEM;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering free(server->dns);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering server->dns = c;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering server->n_dns = n;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering }
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering return 1;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering}
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poetteringint sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n) {
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering assert_return(server, -EINVAL);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering assert_return(ntp || n <= 0, -EINVAL);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering if (server->n_ntp == n &&
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering memcmp(server->ntp, ntp, sizeof(struct in_addr) * n) == 0)
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering return 0;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering if (n <= 0) {
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering server->ntp = mfree(server->ntp);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering server->n_ntp = 0;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering } else {
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering struct in_addr *c;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering c = newdup(struct in_addr, ntp, n);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering if (!c)
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering return -ENOMEM;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering free(server->ntp);
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering server->ntp = c;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering server->n_ntp = n;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering }
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering return 1;
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering}