sd-dhcp-server.c revision 8eb9058dc1f99a5eb9b8726a978fcc0720837a10
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
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
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen#include "siphash24.h"
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "sd-dhcp-server.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "dhcp-server-internal.h"
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen#include "dhcp-internal.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
d122f9ee3a0e3c02ff8100a3dcd1866e90a6537aTom Gundersen#define DHCP_DEFAULT_LEASE_TIME 3600 /* one hour */
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersenint sd_dhcp_server_set_lease_pool(sd_dhcp_server *server,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen struct in_addr *address,
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen size_t size) {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen assert_return(server, -EINVAL);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen assert_return(address, -EINVAL);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen assert_return(address->s_addr, -EINVAL);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen assert_return(size, -EINVAL);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen assert_return(server->pool_start == htobe32(INADDR_ANY), -EBUSY);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen assert_return(!server->pool_size, -EBUSY);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen assert_return(!server->bound_leases, -EBUSY);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen server->bound_leases = new0(DHCPLease*, size);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (!server->bound_leases)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return -ENOMEM;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen server->pool_start = address->s_addr;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen server->pool_size = size;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return 0;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen}
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersenint sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen unsigned char prefixlen) {
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen assert_return(server, -EINVAL);
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen assert_return(address, -EINVAL);
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen assert_return(address->s_addr, -EINVAL);
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen assert_return(prefixlen <= 32, -ERANGE);
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen assert_return(server->address == htobe32(INADDR_ANY), -EBUSY);
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen assert_return(server->netmask == htobe32(INADDR_ANY), -EBUSY);
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen server->address = address->s_addr;
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen server->netmask = htobe32(0xfffffffflu << (32 - prefixlen));
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen return 0;
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen}
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen
7c16313f11e3953f3fe4dbf544f2d36f58d14138Tom Gundersenbool 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) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (server)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen assert_se(REFCNT_INC(server->n_ref) >= 2);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return server;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen}
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersenunsigned long client_id_hash_func(const void *p,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen const uint8_t hash_key[HASH_KEY_SIZE]) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen uint64_t u;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen const DHCPClientId *id = p;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen assert(id);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen assert(id->length);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen assert(id->data);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen siphash24((uint8_t*) &u, id->data, id->length, hash_key);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return (unsigned long) u;
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
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering if (REFCNT_DEC(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);
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) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen _cleanup_dhcp_server_unref_ 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
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen server->n_ref = REFCNT_INIT;
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);
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen server->index = ifindex;
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt server->leases_by_client_id = hashmap_new(&client_id_hash_ops);
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),
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .ll.sll_ifindex = server->index,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen .ll.sll_halen = ETH_ALEN,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen };
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen int r;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(server);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen assert(server->index > 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
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen if (r < 0)
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return r;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return 0;
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
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen pktinfo->ipi_ifindex = server->index;
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,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen 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,
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen 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,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen 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,
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen 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,
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen 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,
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen 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,
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen 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,
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen DHCP_OPTION_ROUTER, 4, &server->address);
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen if (r < 0)
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen return r;
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering if (server->timezone) {
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering r = dhcp_option_append(
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering &packet->dhcp, req->max_optlen, &offset, 0,
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering 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
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen if (r < 0)
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen return r;
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen return 0;
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,
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen &optoffset, 0, 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
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersenstatic int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen void *user_data) {
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen DHCPRequest *req = user_data;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen assert(req);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen switch(code) {
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen if (len == 4)
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen req->lifetime = be32toh(*(be32_t*)option);
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen break;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen case DHCP_OPTION_REQUESTED_IP_ADDRESS:
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (len == 4)
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen req->requested_ip = *(be32_t*)option;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen break;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen case DHCP_OPTION_SERVER_IDENTIFIER:
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen if (len == 4)
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen req->server_id = *(be32_t*)option;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen break;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen case 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;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen case 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
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersenstatic int ensure_sane_request(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) {
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen uint8_t *data;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen data = new0(uint8_t, ETH_ALEN + 1);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen if (!data)
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen return -ENOMEM;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen req->client_id.length = ETH_ALEN + 1;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen req->client_id.data = data;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen req->client_id.data[0] = 0x01;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
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
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen if (!req->lifetime)
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen req->lifetime = DHCP_DEFAULT_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
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (be32toh(requested_ip) < be32toh(server->pool_start) ||
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen be32toh(requested_ip) >= be32toh(server->pool_start) +
500792d8180c9a11d65f107cdc79dea21b2964c4Tom Gundersen + server->pool_size)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return -EINVAL;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return be32toh(requested_ip) - be32toh(server->pool_start);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen}
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom 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;
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
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen type = dhcp_option_parse(message, length, parse_request, req);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen if (type < 0)
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen return 0;
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen r = ensure_sane_request(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) {
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen case DHCP_DISCOVER:
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen {
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 {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen for (i = 0; i < server->pool_size; i++) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (!server->bound_leases[server->next_offer]) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen address = htobe32(be32toh(server->pool_start) + server->next_offer);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen break;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen } else
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen server->next_offer = (server->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:
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen log_dhcp_server(server, "DECLINE (0x%x)",
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen be32toh(req->message->xid));
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen /* TODO: make sure we don't offer this address again */
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen return 1;
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen
5b34277c2015e32e51d10cfa076df2c7106b4537Tom Gundersen break;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen case DHCP_REQUEST:
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen {
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 }
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;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen int buflen = 0, len, r;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert(server);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen r = ioctl(fd, FIONREAD, &buflen);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen if (r < 0)
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen return r;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen if (buflen < 0)
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen return -EIO;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen message = malloc0(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);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen if (len < buflen)
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen return 0;
be077570f779664ed87b50f60608df9fbe258821Tom 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 */
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen if (server->index != 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
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen if (!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
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poetteringint sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone) {
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering int r;
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering assert_return(server, -EINVAL);
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering assert_return(timezone_is_valid(timezone), -EINVAL);
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering if (streq_ptr(timezone, server->timezone))
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering return 0;
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering r = free_and_strdup(&server->timezone, timezone);
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering if (r < 0)
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering return r;
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering return 1;
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering}