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