sd-dhcp-client.c revision a38d99451f2bf8026ec51aee91662292e823c6a8
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann/***
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann This file is part of systemd.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann Copyright (C) 2013 Intel Corporation. All rights reserved.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann systemd is free software; you can redistribute it and/or modify it
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann under the terms of the GNU Lesser General Public License as published by
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann the Free Software Foundation; either version 2.1 of the License, or
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann (at your option) any later version.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann systemd is distributed in the hope that it will be useful, but
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann WITHOUT ANY WARRANTY; without even the implied warranty of
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann Lesser General Public License for more details.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann You should have received a copy of the GNU Lesser General Public License
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann along with systemd; If not, see <http://www.gnu.org/licenses/>.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann***/
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <stdlib.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <errno.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <string.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <stdio.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <net/ethernet.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <net/if_arp.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <linux/if_infiniband.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <netinet/ether.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <sys/param.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <sys/ioctl.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "util.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "list.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "refcnt.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "async.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "dhcp-protocol.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "dhcp-internal.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "dhcp-lease-internal.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "dhcp-identifier.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "sd-dhcp-client.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstruct sd_dhcp_client {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann RefCount n_ref;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCPState state;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann sd_event *event;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int event_priority;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann sd_event_source *timeout_resend;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int index;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int fd;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann union sockaddr_union link;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann sd_event_source *receive_message;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann bool request_broadcast;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint8_t *req_opts;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann size_t req_opts_allocated;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann size_t req_opts_size;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann be32_t last_addr;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint8_t mac_addr[MAX_MAC_ADDR_LEN];
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann size_t mac_addr_len;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint16_t arp_type;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann struct {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint8_t type;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann union {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann struct {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* 0: Generic (non-LL) (RFC 2132) */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint8_t data[MAX_CLIENT_ID_LEN];
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } _packed_ gen;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann struct {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* 1: Ethernet Link-Layer (RFC 2132) */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint8_t haddr[ETH_ALEN];
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } _packed_ eth;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann struct {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* 2 - 254: ARP/Link-Layer (RFC 2132) */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint8_t haddr[0];
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } _packed_ ll;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann struct {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* 255: Node-specific (RFC 4361) */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint32_t iaid;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann struct duid duid;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } _packed_ ns;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann struct {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint8_t data[MAX_CLIENT_ID_LEN];
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } _packed_ raw;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann };
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } _packed_ client_id;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann size_t client_id_len;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char *hostname;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char *vendor_class_identifier;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint32_t mtu;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint32_t xid;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann usec_t start_time;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann unsigned int attempt;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann usec_t request_sent;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann sd_event_source *timeout_t1;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann sd_event_source *timeout_t2;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann sd_event_source *timeout_expire;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann sd_dhcp_client_cb_t cb;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann void *userdata;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann sd_dhcp_lease *lease;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann};
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic const uint8_t default_req_opts[] = {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_SUBNET_MASK,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_ROUTER,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_HOST_NAME,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_DOMAIN_NAME,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_DOMAIN_NAME_SERVER,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_NTP_SERVER,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann};
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int client_receive_message_raw(sd_event_source *s, int fd,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint32_t revents, void *userdata);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int client_receive_message_udp(sd_event_source *s, int fd,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint32_t revents, void *userdata);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic void client_stop(sd_dhcp_client *client, int error);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann void *userdata) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(client, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->cb = cb;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->userdata = userdata;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(client, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->request_broadcast = !!broadcast;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann size_t i;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(client, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return (IN_SET(client->state, DHCP_STATE_INIT,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_STATE_STOPPED), -EBUSY);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann switch(option) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_OPTION_PAD:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_OPTION_OVERLOAD:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_OPTION_MESSAGE_TYPE:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_OPTION_PARAMETER_REQUEST_LIST:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_OPTION_END:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -EINVAL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann default:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann for (i = 0; i < client->req_opts_size; i++)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->req_opts[i] == option)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -EEXIST;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->req_opts_size + 1))
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->req_opts[client->req_opts_size++] = option;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint sd_dhcp_client_set_request_address(sd_dhcp_client *client,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const struct in_addr *last_addr) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(client, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return (IN_SET(client->state, DHCP_STATE_INIT,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_STATE_STOPPED), -EBUSY);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (last_addr)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->last_addr = last_addr->s_addr;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann else
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->last_addr = INADDR_ANY;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(client, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return (IN_SET(client->state, DHCP_STATE_INIT,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_STATE_STOPPED), -EBUSY);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(interface_index > 0, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->index = interface_index;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann size_t addr_len, uint16_t arp_type) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_CLIENT_DONT_DESTROY(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann bool need_restart = false;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(client, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(addr, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(arp_type > 0, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (arp_type == ARPHRD_ETHER)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(addr_len == ETH_ALEN, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann else if (arp_type == ARPHRD_INFINIBAND)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann else
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -EINVAL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->mac_addr_len == addr_len &&
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann memcmp(&client->mac_addr, addr, addr_len) == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann log_dhcp_client(client, "Changing MAC address on running DHCP "
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann "client, restarting");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann need_restart = true;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client_stop(client, DHCP_EVENT_STOP);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann memcpy(&client->mac_addr, addr, addr_len);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->mac_addr_len = addr_len;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->arp_type = arp_type;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (need_restart && client->state != DHCP_STATE_STOPPED)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann sd_dhcp_client_start(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const uint8_t **data, size_t *data_len) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(client, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(type, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(data, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(data_len, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *type = 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *data = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *data_len = 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->client_id_len) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *type = client->client_id.type;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *data = client->client_id.raw.data;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *data_len = client->client_id_len - sizeof(client->client_id.type);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const uint8_t *data, size_t data_len) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_CLIENT_DONT_DESTROY(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann bool need_restart = false;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(client, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(data, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann switch (type) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case ARPHRD_ETHER:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (data_len != ETH_ALEN)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -EINVAL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case ARPHRD_INFINIBAND:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (data_len != INFINIBAND_ALEN)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -EINVAL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann default:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->client_id_len == data_len + sizeof(client->client_id.type) &&
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->client_id.type == type &&
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann memcmp(&client->client_id.raw.data, data, data_len) == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann log_dhcp_client(client, "Changing client ID on running DHCP "
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann "client, restarting");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann need_restart = true;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client_stop(client, DHCP_EVENT_STOP);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->client_id.type = type;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann memcpy(&client->client_id.raw.data, data, data_len);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->client_id_len = data_len + sizeof (client->client_id.type);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (need_restart && client->state != DHCP_STATE_STOPPED)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann sd_dhcp_client_start(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint sd_dhcp_client_set_hostname(sd_dhcp_client *client,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *hostname) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char *new_hostname = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(client, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (streq_ptr(client->hostname, hostname))
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (hostname) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann new_hostname = strdup(hostname);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!new_hostname)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann free(client->hostname);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->hostname = new_hostname;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *vci) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char *new_vci = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(client, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann new_vci = strdup(vci);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!new_vci)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann free(client->vendor_class_identifier);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->vendor_class_identifier = new_vci;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(client, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->mtu = mtu;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(client, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(ret, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->state != DHCP_STATE_BOUND &&
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->state != DHCP_STATE_RENEWING &&
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->state != DHCP_STATE_REBINDING)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -EADDRNOTAVAIL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *ret = sd_dhcp_lease_ref(client->lease);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic void client_notify(sd_dhcp_client *client, int event) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->cb)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->cb(client, event, client->userdata);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int client_initialize(sd_dhcp_client *client) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_return(client, -EINVAL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->receive_message =
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann sd_event_source_unref(client->receive_message);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->fd = asynchronous_close(client->fd);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->timeout_resend = sd_event_source_unref(client->timeout_resend);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->timeout_expire = sd_event_source_unref(client->timeout_expire);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->attempt = 1;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->state = DHCP_STATE_INIT;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->xid = 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->lease)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->lease = sd_dhcp_lease_unref(client->lease);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic void client_stop(sd_dhcp_client *client, int error) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (error < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann log_dhcp_client(client, "STOPPED: %s", strerror(-error));
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann else if (error == DHCP_EVENT_STOP)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann log_dhcp_client(client, "STOPPED");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann else
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann log_dhcp_client(client, "STOPPED: Unknown event");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client_notify(client, error);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client_initialize(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint8_t type, size_t *_optlen, size_t *_optoffset) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_free_ DHCPPacket *packet;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann size_t optlen, optoffset, size;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann be16_t max_size;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann usec_t time_now;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint16_t secs;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(client->start_time);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(ret);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(_optlen);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(_optoffset);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann optlen = DHCP_MIN_OPTIONS_SIZE;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann size = sizeof(DHCPPacket) + optlen;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann packet = malloc0(size);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!packet)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->arp_type, optlen, &optoffset);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann refuse to issue an DHCP lease if 'secs' is set to zero */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(time_now >= client->start_time);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* seconds between sending first and last DISCOVER
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann * must always be strictly positive to deal with broken servers */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann packet->dhcp.secs = htobe16(secs);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* RFC2132 section 4.1
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann A client that cannot receive unicast IP datagrams until its protocol
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann software has been configured with an IP address SHOULD set the
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCPREQUEST messages that client sends. The BROADCAST bit will
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann provide a hint to the DHCP server and BOOTP relay agent to broadcast
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann any messages to the client on the client's subnet.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann Note: some interfaces needs this to be enabled, but some networks
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann needs this to be disabled as broadcasts are filteretd, so this
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann needs to be configurable */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann packet->dhcp.flags = htobe16(0x8000);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* RFC2132 section 4.1.1:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann The client MUST include its hardware address in the ’chaddr’ field, if
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann necessary for delivery of DHCP reply messages. Non-Ethernet
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann interfaces will leave 'chaddr' empty and use the client identifier
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann instead (eg, RFC 4390 section 2.1).
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->arp_type == ARPHRD_ETHER)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* If no client identifier exists, construct an RFC 4361-compliant one */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->client_id_len == 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann size_t duid_len;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->client_id.type = 255;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* Some DHCP servers will refuse to issue an DHCP lease if the Client
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann Identifier option is not set */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->client_id_len) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_CLIENT_IDENTIFIER,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->client_id_len,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann &client->client_id);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* RFC2131 section 3.5:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann in its initial DHCPDISCOVER or DHCPREQUEST message, a
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client may provide the server with a list of specific
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann parameters the client is interested in. If the client
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann includes a list of parameters in a DHCPDISCOVER message,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann it MUST include that list in any subsequent DHCPREQUEST
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann messages.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_PARAMETER_REQUEST_LIST,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->req_opts_size, client->req_opts);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* RFC2131 section 3.5:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann The client SHOULD include the ’maximum DHCP message size’ option to
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann let the server know how large the server may make its DHCP messages.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann than the defined default size unless the Maximum Messge Size option
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann is explicitly set
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann RFC3442 "Requirements to Avoid Sizing Constraints":
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann Because a full routing table can be quite large, the standard 576
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann octet maximum size for a DHCP message may be too short to contain
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann some legitimate Classless Static Route options. Because of this,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann clients implementing the Classless Static Route option SHOULD send a
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann Maximum DHCP Message Size [4] option if the DHCP client's TCP/IP
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann stack is capable of receiving larger IP datagrams. In this case, the
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client SHOULD set the value of this option to at least the MTU of the
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann interface that the client is configuring. The client MAY set the
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann value of this option higher, up to the size of the largest UDP packet
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann it is prepared to accept. (Note that the value specified in the
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann Maximum DHCP Message Size option is the total maximum packet size,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann including IP and UDP headers.)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann max_size = htobe16(size);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann 2, &max_size);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *_optlen = optlen;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *_optoffset = optoffset;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *ret = packet;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann packet = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann size_t len) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann INADDR_BROADCAST, DHCP_PORT_SERVER, len);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return dhcp_network_send_raw_socket(client->fd, &client->link,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann packet, len);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int client_send_discover(sd_dhcp_client *client) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_free_ DHCPPacket *discover = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann size_t optoffset, optlen;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(client->state == DHCP_STATE_INIT ||
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->state == DHCP_STATE_SELECTING);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = client_message_init(client, &discover, DHCP_DISCOVER,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann &optlen, &optoffset);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* the client may suggest values for the network address
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann and lease time in the DHCPDISCOVER message. The client may include
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann the ’requested IP address’ option to suggest that a particular IP
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann address be assigned, and may include the ’IP address lease time’
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann option to suggest the lease time it would like.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->last_addr != INADDR_ANY) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_REQUESTED_IP_ADDRESS,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann 4, &client->last_addr);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* it is unclear from RFC 2131 if client should send hostname in
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCPDISCOVER but dhclient does and so we do as well
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->hostname) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_HOST_NAME,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann strlen(client->hostname), client->hostname);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->vendor_class_identifier) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann strlen(client->vendor_class_identifier),
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->vendor_class_identifier);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_END, 0, NULL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* We currently ignore:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann The client SHOULD wait a random time between one and ten seconds to
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann desynchronize the use of DHCP at startup.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann log_dhcp_client(client, "DISCOVER");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int client_send_request(sd_dhcp_client *client) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_free_ DHCPPacket *request = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann size_t optoffset, optlen;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = client_message_init(client, &request, DHCP_REQUEST,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann &optlen, &optoffset);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann switch (client->state) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann SELECTING should be REQUESTING)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_REQUESTING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* Client inserts the address of the selected server in ’server
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann filled in with the yiaddr value from the chosen DHCPOFFER.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_SERVER_IDENTIFIER,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann 4, &client->lease->server_address);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_REQUESTED_IP_ADDRESS,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann 4, &client->lease->address);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_INIT_REBOOT:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann option MUST be filled in with client’s notion of its previously
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assigned address. ’ciaddr’ MUST be zero.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_REQUESTED_IP_ADDRESS,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann 4, &client->last_addr);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_RENEWING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client’s IP address.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* fall through */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_REBINDING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client’s IP address.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann This message MUST be broadcast to the 0xffffffff IP broadcast address.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann request->dhcp.ciaddr = client->lease->address;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_INIT:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_SELECTING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_REBOOTING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_BOUND:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_STOPPED:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -EINVAL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->hostname) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_HOST_NAME,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann strlen(client->hostname), client->hostname);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_OPTION_END, 0, NULL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->state == DHCP_STATE_RENEWING) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_network_send_udp_socket(client->fd,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->lease->server_address,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_PORT_SERVER,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann &request->dhcp,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann sizeof(DHCPMessage) + optoffset);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } else {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann switch (client->state) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_REQUESTING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann log_dhcp_client(client, "REQUEST (requesting)");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_INIT_REBOOT:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann log_dhcp_client(client, "REQUEST (init-reboot)");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_RENEWING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann log_dhcp_client(client, "REQUEST (renewing)");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_REBINDING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann log_dhcp_client(client, "REQUEST (rebinding)");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann default:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann log_dhcp_client(client, "REQUEST (invalid)");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int client_start(sd_dhcp_client *client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int client_timeout_resend(sd_event_source *s, uint64_t usec,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann void *userdata) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann sd_dhcp_client *client = userdata;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann DHCP_CLIENT_DONT_DESTROY(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann usec_t next_timeout = 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint64_t time_now;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann uint32_t time_left;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(s);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(client->event);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann goto error;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann switch (client->state) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_RENEWING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann time_left = (client->lease->t2 - client->lease->t1) / 2;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (time_left < 60)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann time_left = 60;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann next_timeout = time_now + time_left * USEC_PER_SEC;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_REBINDING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann time_left = (client->lease->lifetime - client->lease->t2) / 2;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (time_left < 60)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann time_left = 60;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann next_timeout = time_now + time_left * USEC_PER_SEC;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_REBOOTING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* start over as we did not receive a timely ack or nak */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = client_initialize(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann goto error;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = client_start(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann goto error;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann else {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann log_dhcp_client(client, "REBOOTED");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_INIT:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_INIT_REBOOT:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_SELECTING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_REQUESTING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_BOUND:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->attempt < 64)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->attempt *= 2;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_STOPPED:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = -EINVAL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann goto error;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann next_timeout += (random_u32() & 0x1fffff);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->timeout_resend = sd_event_source_unref(client->timeout_resend);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = sd_event_add_time(client->event,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann &client->timeout_resend,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann clock_boottime_or_monotonic(),
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann next_timeout, 10 * USEC_PER_MSEC,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client_timeout_resend, client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann goto error;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = sd_event_source_set_priority(client->timeout_resend,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->event_priority);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann goto error;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann goto error;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann switch (client->state) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_INIT:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = client_send_discover(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r >= 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->state = DHCP_STATE_SELECTING;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann client->attempt = 1;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } else {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->attempt >= 64)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann goto error;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_SELECTING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = client_send_discover(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0 && client->attempt >= 64)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann goto error;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_INIT_REBOOT:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_REQUESTING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_RENEWING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case DHCP_STATE_REBINDING:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = client_send_request(client);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0 && client->attempt >= 64)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann goto error;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (client->state == DHCP_STATE_INIT_REBOOT)
client->state = DHCP_STATE_REBOOTING;
client->request_sent = time_now;
break;
case DHCP_STATE_REBOOTING:
case DHCP_STATE_BOUND:
break;
case DHCP_STATE_STOPPED:
r = -EINVAL;
goto error;
}
return 0;
error:
client_stop(client, r);
/* Errors were dealt with when stopping the client, don't spill
errors into the event loop handler */
return 0;
}
static int client_initialize_io_events(sd_dhcp_client *client,
sd_event_io_handler_t io_callback) {
int r;
assert(client);
assert(client->event);
r = sd_event_add_io(client->event, &client->receive_message,
client->fd, EPOLLIN, io_callback,
client);
if (r < 0)
goto error;
r = sd_event_source_set_priority(client->receive_message,
client->event_priority);
if (r < 0)
goto error;
r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message");
if (r < 0)
goto error;
error:
if (r < 0)
client_stop(client, r);
return 0;
}
static int client_initialize_time_events(sd_dhcp_client *client) {
int r;
assert(client);
assert(client->event);
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
r = sd_event_add_time(client->event,
&client->timeout_resend,
clock_boottime_or_monotonic(),
0, 0,
client_timeout_resend, client);
if (r < 0)
goto error;
r = sd_event_source_set_priority(client->timeout_resend,
client->event_priority);
r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
if (r < 0)
goto error;
error:
if (r < 0)
client_stop(client, r);
return 0;
}
static int client_initialize_events(sd_dhcp_client *client,
sd_event_io_handler_t io_callback) {
client_initialize_io_events(client, io_callback);
client_initialize_time_events(client);
return 0;
}
static int client_start(sd_dhcp_client *client) {
int r;
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
assert_return(client->index > 0, -EINVAL);
assert_return(client->fd < 0, -EBUSY);
assert_return(client->xid == 0, -EINVAL);
assert_return(client->state == DHCP_STATE_INIT ||
client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
client->xid = random_u32();
r = dhcp_network_bind_raw_socket(client->index, &client->link,
client->xid, client->mac_addr,
client->mac_addr_len, client->arp_type);
if (r < 0) {
client_stop(client, r);
return r;
}
client->fd = r;
if (client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_INIT_REBOOT)
client->start_time = now(clock_boottime_or_monotonic());
return client_initialize_events(client, client_receive_message_raw);
}
static int client_timeout_expire(sd_event_source *s, uint64_t usec,
void *userdata) {
sd_dhcp_client *client = userdata;
DHCP_CLIENT_DONT_DESTROY(client);
log_dhcp_client(client, "EXPIRED");
client_notify(client, DHCP_EVENT_EXPIRED);
/* lease was lost, start over if not freed or stopped in callback */
if (client->state != DHCP_STATE_STOPPED) {
client_initialize(client);
client_start(client);
}
return 0;
}
static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
sd_dhcp_client *client = userdata;
DHCP_CLIENT_DONT_DESTROY(client);
int r;
client->receive_message = sd_event_source_unref(client->receive_message);
client->fd = asynchronous_close(client->fd);
client->state = DHCP_STATE_REBINDING;
client->attempt = 1;
r = dhcp_network_bind_raw_socket(client->index, &client->link,
client->xid, client->mac_addr,
client->mac_addr_len, client->arp_type);
if (r < 0) {
client_stop(client, r);
return 0;
}
client->fd = r;
return client_initialize_events(client, client_receive_message_raw);
}
static int client_timeout_t1(sd_event_source *s, uint64_t usec,
void *userdata) {
sd_dhcp_client *client = userdata;
DHCP_CLIENT_DONT_DESTROY(client);
client->state = DHCP_STATE_RENEWING;
client->attempt = 1;
return client_initialize_time_events(client);
}
static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
size_t len) {
_cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
int r;
r = dhcp_lease_new(&lease);
if (r < 0)
return r;
if (client->client_id_len) {
r = dhcp_lease_set_client_id(lease,
(uint8_t *) &client->client_id,
client->client_id_len);
if (r < 0)
return r;
}
r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
if (r != DHCP_OFFER) {
log_dhcp_client(client, "received message was not an OFFER, ignoring");
return -ENOMSG;
}
lease->next_server = offer->siaddr;
lease->address = offer->yiaddr;
if (lease->address == INADDR_ANY ||
lease->server_address == INADDR_ANY ||
lease->lifetime == 0) {
log_dhcp_client(client, "received lease lacks address, server "
"address or lease lifetime, ignoring");
return -ENOMSG;
}
if (lease->subnet_mask == INADDR_ANY) {
r = dhcp_lease_set_default_subnet_mask(lease);
if (r < 0) {
log_dhcp_client(client, "received lease lacks subnet "
"mask, and a fallback one can not be "
"generated, ignoring");
return -ENOMSG;
}
}
sd_dhcp_lease_unref(client->lease);
client->lease = lease;
lease = NULL;
log_dhcp_client(client, "OFFER");
return 0;
}
static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
size_t len) {
int r;
r = dhcp_option_parse(force, len, NULL, NULL);
if (r != DHCP_FORCERENEW)
return -ENOMSG;
log_dhcp_client(client, "FORCERENEW");
return 0;
}
static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
size_t len) {
_cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
int r;
r = dhcp_lease_new(&lease);
if (r < 0)
return r;
if (client->client_id_len) {
r = dhcp_lease_set_client_id(lease,
(uint8_t *) &client->client_id,
client->client_id_len);
if (r < 0)
return r;
}
r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
if (r == DHCP_NAK) {
log_dhcp_client(client, "NAK");
return -EADDRNOTAVAIL;
}
if (r != DHCP_ACK) {
log_dhcp_client(client, "received message was not an ACK, ignoring");
return -ENOMSG;
}
lease->next_server = ack->siaddr;
lease->address = ack->yiaddr;
if (lease->address == INADDR_ANY ||
lease->server_address == INADDR_ANY ||
lease->lifetime == 0) {
log_dhcp_client(client, "received lease lacks address, server "
"address or lease lifetime, ignoring");
return -ENOMSG;
}
if (lease->subnet_mask == INADDR_ANY) {
r = dhcp_lease_set_default_subnet_mask(lease);
if (r < 0) {
log_dhcp_client(client, "received lease lacks subnet "
"mask, and a fallback one can not be "
"generated, ignoring");
return -ENOMSG;
}
}
r = DHCP_EVENT_IP_ACQUIRE;
if (client->lease) {
if (client->lease->address != lease->address ||
client->lease->subnet_mask != lease->subnet_mask ||
client->lease->router != lease->router) {
r = DHCP_EVENT_IP_CHANGE;
} else
r = DHCP_EVENT_RENEW;
client->lease = sd_dhcp_lease_unref(client->lease);
}
client->lease = lease;
lease = NULL;
log_dhcp_client(client, "ACK");
return r;
}
static uint64_t client_compute_timeout(sd_dhcp_client *client,
uint32_t lifetime, double factor) {
assert(client);
assert(client->request_sent);
assert(lifetime);
return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
+ (random_u32() & 0x1fffff);
}
static int client_set_lease_timeouts(sd_dhcp_client *client) {
usec_t time_now;
uint64_t lifetime_timeout;
uint64_t t2_timeout;
uint64_t t1_timeout;
char time_string[FORMAT_TIMESPAN_MAX];
int r;
assert(client);
assert(client->event);
assert(client->lease);
assert(client->lease->lifetime);
client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
client->timeout_expire = sd_event_source_unref(client->timeout_expire);
/* don't set timers for infinite leases */
if (client->lease->lifetime == 0xffffffff)
return 0;
r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
return r;
assert(client->request_sent <= time_now);
/* convert the various timeouts from relative (secs) to absolute (usecs) */
lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
if (client->lease->t1 && client->lease->t2) {
/* both T1 and T2 are given */
if (client->lease->t1 < client->lease->t2 &&
client->lease->t2 < client->lease->lifetime) {
/* they are both valid */
t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
} else {
/* discard both */
t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
client->lease->t2 = (client->lease->lifetime * 7) / 8;
t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
client->lease->t1 = client->lease->lifetime / 2;
}
} else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) {
/* only T2 is given, and it is valid */
t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
client->lease->t1 = client->lease->lifetime / 2;
if (t2_timeout <= t1_timeout) {
/* the computed T1 would be invalid, so discard T2 */
t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
client->lease->t2 = (client->lease->lifetime * 7) / 8;
}
} else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) {
/* only T1 is given, and it is valid */
t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
client->lease->t2 = (client->lease->lifetime * 7) / 8;
if (t2_timeout <= t1_timeout) {
/* the computed T2 would be invalid, so discard T1 */
t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
client->lease->t2 = client->lease->lifetime / 2;
}
} else {
/* fall back to the default timeouts */
t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
client->lease->t1 = client->lease->lifetime / 2;
t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
client->lease->t2 = (client->lease->lifetime * 7) / 8;
}
/* arm lifetime timeout */
r = sd_event_add_time(client->event, &client->timeout_expire,
clock_boottime_or_monotonic(),
lifetime_timeout, 10 * USEC_PER_MSEC,
client_timeout_expire, client);
if (r < 0)
return r;
r = sd_event_source_set_priority(client->timeout_expire,
client->event_priority);
if (r < 0)
return r;
r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime");
if (r < 0)
return r;
log_dhcp_client(client, "lease expires in %s",
format_timespan(time_string, FORMAT_TIMESPAN_MAX,
lifetime_timeout - time_now, 0));
/* don't arm earlier timeouts if this has already expired */
if (lifetime_timeout <= time_now)
return 0;
/* arm T2 timeout */
r = sd_event_add_time(client->event,
&client->timeout_t2,
clock_boottime_or_monotonic(),
t2_timeout,
10 * USEC_PER_MSEC,
client_timeout_t2, client);
if (r < 0)
return r;
r = sd_event_source_set_priority(client->timeout_t2,
client->event_priority);
if (r < 0)
return r;
r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout");
if (r < 0)
return r;
log_dhcp_client(client, "T2 expires in %s",
format_timespan(time_string, FORMAT_TIMESPAN_MAX,
t2_timeout - time_now, 0));
/* don't arm earlier timeout if this has already expired */
if (t2_timeout <= time_now)
return 0;
/* arm T1 timeout */
r = sd_event_add_time(client->event,
&client->timeout_t1,
clock_boottime_or_monotonic(),
t1_timeout, 10 * USEC_PER_MSEC,
client_timeout_t1, client);
if (r < 0)
return r;
r = sd_event_source_set_priority(client->timeout_t1,
client->event_priority);
if (r < 0)
return r;
r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer");
if (r < 0)
return r;
log_dhcp_client(client, "T1 expires in %s",
format_timespan(time_string, FORMAT_TIMESPAN_MAX,
t1_timeout - time_now, 0));
return 0;
}
static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
int len) {
DHCP_CLIENT_DONT_DESTROY(client);
int r = 0, notify_event = 0;
assert(client);
assert(client->event);
assert(message);
switch (client->state) {
case DHCP_STATE_SELECTING:
r = client_handle_offer(client, message, len);
if (r >= 0) {
client->timeout_resend =
sd_event_source_unref(client->timeout_resend);
client->state = DHCP_STATE_REQUESTING;
client->attempt = 1;
r = sd_event_add_time(client->event,
&client->timeout_resend,
clock_boottime_or_monotonic(),
0, 0,
client_timeout_resend, client);
if (r < 0)
goto error;
r = sd_event_source_set_priority(client->timeout_resend,
client->event_priority);
if (r < 0)
goto error;
r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
if (r < 0)
goto error;
} else if (r == -ENOMSG)
/* invalid message, let's ignore it */
return 0;
break;
case DHCP_STATE_REBOOTING:
case DHCP_STATE_REQUESTING:
case DHCP_STATE_RENEWING:
case DHCP_STATE_REBINDING:
r = client_handle_ack(client, message, len);
if (r >= 0) {
client->timeout_resend =
sd_event_source_unref(client->timeout_resend);
client->receive_message =
sd_event_source_unref(client->receive_message);
client->fd = asynchronous_close(client->fd);
if (IN_SET(client->state, DHCP_STATE_REQUESTING,
DHCP_STATE_REBOOTING))
notify_event = DHCP_EVENT_IP_ACQUIRE;
else if (r != DHCP_EVENT_IP_ACQUIRE)
notify_event = r;
client->state = DHCP_STATE_BOUND;
client->attempt = 1;
client->last_addr = client->lease->address;
r = client_set_lease_timeouts(client);
if (r < 0) {
log_dhcp_client(client, "could not set lease timeouts");
goto error;
}
r = dhcp_network_bind_udp_socket(client->lease->address,
DHCP_PORT_CLIENT);
if (r < 0) {
log_dhcp_client(client, "could not bind UDP socket");
goto error;
}
client->fd = r;
client_initialize_io_events(client, client_receive_message_udp);
if (notify_event) {
client_notify(client, notify_event);
if (client->state == DHCP_STATE_STOPPED)
return 0;
}
} else if (r == -EADDRNOTAVAIL) {
/* got a NAK, let's restart the client */
client->timeout_resend =
sd_event_source_unref(client->timeout_resend);
r = client_initialize(client);
if (r < 0)
goto error;
r = client_start(client);
if (r < 0)
goto error;
log_dhcp_client(client, "REBOOTED");
return 0;
} else if (r == -ENOMSG)
/* invalid message, let's ignore it */
return 0;
break;
case DHCP_STATE_BOUND:
r = client_handle_forcerenew(client, message, len);
if (r >= 0) {
r = client_timeout_t1(NULL, 0, client);
if (r < 0)
goto error;
} else if (r == -ENOMSG)
/* invalid message, let's ignore it */
return 0;
break;
case DHCP_STATE_INIT:
case DHCP_STATE_INIT_REBOOT:
break;
case DHCP_STATE_STOPPED:
r = -EINVAL;
goto error;
}
error:
if (r < 0)
client_stop(client, r);
return r;
}
static int client_receive_message_udp(sd_event_source *s, int fd,
uint32_t revents, void *userdata) {
sd_dhcp_client *client = userdata;
_cleanup_free_ DHCPMessage *message = NULL;
int buflen = 0, len, r;
const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } };
const struct ether_addr *expected_chaddr = NULL;
uint8_t expected_hlen = 0;
assert(s);
assert(client);
r = ioctl(fd, FIONREAD, &buflen);
if (r < 0)
return r;
if (buflen < 0)
/* this can't be right */
return -EIO;
message = malloc0(buflen);
if (!message)
return -ENOMEM;
len = read(fd, message, buflen);
if (len < 0) {
log_dhcp_client(client, "could not receive message from UDP "
"socket: %m");
return 0;
} else if ((size_t)len < sizeof(DHCPMessage)) {
log_dhcp_client(client, "too small to be a DHCP message: ignoring");
return 0;
}
if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
log_dhcp_client(client, "not a DHCP message: ignoring");
return 0;
}
if (message->op != BOOTREPLY) {
log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
return 0;
}
if (message->htype != client->arp_type) {
log_dhcp_client(client, "packet type does not match client type");
return 0;
}
if (client->arp_type == ARPHRD_ETHER) {
expected_hlen = ETH_ALEN;
expected_chaddr = (const struct ether_addr *) &client->mac_addr;
} else {
/* Non-ethernet links expect zero chaddr */
expected_hlen = 0;
expected_chaddr = &zero_mac;
}
if (message->hlen != expected_hlen) {
log_dhcp_client(client, "unexpected packet hlen %d", message->hlen);
return 0;
}
if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) {
log_dhcp_client(client, "received chaddr does not match "
"expected: ignoring");
return 0;
}
if (client->state != DHCP_STATE_BOUND &&
be32toh(message->xid) != client->xid) {
/* in BOUND state, we may receive FORCERENEW with xid set by server,
so ignore the xid in this case */
log_dhcp_client(client, "received xid (%u) does not match "
"expected (%u): ignoring",
be32toh(message->xid), client->xid);
return 0;
}
return client_handle_message(client, message, len);
}
static int client_receive_message_raw(sd_event_source *s, int fd,
uint32_t revents, void *userdata) {
sd_dhcp_client *client = userdata;
_cleanup_free_ DHCPPacket *packet = NULL;
uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
struct iovec iov = {};
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = cmsgbuf,
.msg_controllen = sizeof(cmsgbuf),
};
struct cmsghdr *cmsg;
bool checksum = true;
int buflen = 0, len, r;
assert(s);
assert(client);
r = ioctl(fd, FIONREAD, &buflen);
if (r < 0)
return r;
if (buflen < 0)
/* this can't be right */
return -EIO;
packet = malloc0(buflen);
if (!packet)
return -ENOMEM;
iov.iov_base = packet;
iov.iov_len = buflen;
len = recvmsg(fd, &msg, 0);
if (len < 0) {
log_dhcp_client(client, "could not receive message from raw "
"socket: %m");
return 0;
} else if ((size_t)len < sizeof(DHCPPacket))
return 0;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_PACKET &&
cmsg->cmsg_type == PACKET_AUXDATA &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
break;
}
}
r = dhcp_packet_verify_headers(packet, len, checksum);
if (r < 0)
return 0;
len -= DHCP_IP_UDP_SIZE;
return client_handle_message(client, &packet->dhcp, len);
}
int sd_dhcp_client_start(sd_dhcp_client *client) {
int r;
assert_return(client, -EINVAL);
r = client_initialize(client);
if (r < 0)
return r;
if (client->last_addr)
client->state = DHCP_STATE_INIT_REBOOT;
r = client_start(client);
if (r >= 0)
log_dhcp_client(client, "STARTED on ifindex %i", client->index);
return r;
}
int sd_dhcp_client_stop(sd_dhcp_client *client) {
DHCP_CLIENT_DONT_DESTROY(client);
assert_return(client, -EINVAL);
client_stop(client, DHCP_EVENT_STOP);
client->state = DHCP_STATE_STOPPED;
return 0;
}
int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
int priority) {
int r;
assert_return(client, -EINVAL);
assert_return(!client->event, -EBUSY);
if (event)
client->event = sd_event_ref(event);
else {
r = sd_event_default(&client->event);
if (r < 0)
return 0;
}
client->event_priority = priority;
return 0;
}
int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
assert_return(client, -EINVAL);
client->event = sd_event_unref(client->event);
return 0;
}
sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
if (!client)
return NULL;
return client->event;
}
sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
if (client)
assert_se(REFCNT_INC(client->n_ref) >= 2);
return client;
}
sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
if (client && REFCNT_DEC(client->n_ref) == 0) {
log_dhcp_client(client, "FREE");
client_initialize(client);
client->receive_message =
sd_event_source_unref(client->receive_message);
sd_dhcp_client_detach_event(client);
sd_dhcp_lease_unref(client->lease);
free(client->req_opts);
free(client->hostname);
free(client->vendor_class_identifier);
free(client);
}
return NULL;
}
int sd_dhcp_client_new(sd_dhcp_client **ret) {
_cleanup_dhcp_client_unref_ sd_dhcp_client *client = NULL;
assert_return(ret, -EINVAL);
client = new0(sd_dhcp_client, 1);
if (!client)
return -ENOMEM;
client->n_ref = REFCNT_INIT;
client->state = DHCP_STATE_INIT;
client->index = -1;
client->fd = -1;
client->attempt = 1;
client->mtu = DHCP_DEFAULT_MIN_SIZE;
client->req_opts_size = ELEMENTSOF(default_req_opts);
client->req_opts = memdup(default_req_opts, client->req_opts_size);
if (!client->req_opts)
return -ENOMEM;
*ret = client;
client = NULL;
return 0;
}