sd-dhcp-client.c revision 144c348883a71ecc580ecf11967ceb9f304bd9da
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering/***
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering This file is part of systemd.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering Copyright (C) 2013 Intel Corporation. All rights reserved.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering systemd is free software; you can redistribute it and/or modify it
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering under the terms of the GNU Lesser General Public License as published by
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering (at your option) any later version.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering systemd is distributed in the hope that it will be useful, but
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering Lesser General Public License for more details.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering You should have received a copy of the GNU Lesser General Public License
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering***/
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <stdlib.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <errno.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <string.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <stdio.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <net/ethernet.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <net/if_arp.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <linux/if_infiniband.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <sys/ioctl.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "util.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "refcnt.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "random-util.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "async.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "dhcp-protocol.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "dhcp-internal.h"
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers#include "dhcp-lease-internal.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "dhcp-identifier.h"
c978343015c787713651dff571acb5207367f5f2Lennart Poettering#include "sd-dhcp-client.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstruct sd_dhcp_client {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering RefCount n_ref;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCPState state;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering sd_event *event;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering int event_priority;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering sd_event_source *timeout_resend;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering int index;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering int fd;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering union sockaddr_union link;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering sd_event_source *receive_message;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering bool request_broadcast;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering uint8_t *req_opts;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering size_t req_opts_allocated;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering size_t req_opts_size;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering be32_t last_addr;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering uint8_t mac_addr[MAX_MAC_ADDR_LEN];
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering size_t mac_addr_len;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering uint16_t arp_type;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering struct {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering uint8_t type;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering union {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering struct {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* 0: Generic (non-LL) (RFC 2132) */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering uint8_t data[MAX_CLIENT_ID_LEN];
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering } _packed_ gen;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering struct {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* 1: Ethernet Link-Layer (RFC 2132) */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering uint8_t haddr[ETH_ALEN];
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering } _packed_ eth;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering struct {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* 2 - 254: ARP/Link-Layer (RFC 2132) */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering uint8_t haddr[0];
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering } _packed_ ll;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering struct {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* 255: Node-specific (RFC 4361) */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering uint32_t iaid;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering struct duid duid;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering } _packed_ ns;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering struct {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering uint8_t data[MAX_CLIENT_ID_LEN];
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering } _packed_ raw;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering };
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering } _packed_ client_id;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers size_t client_id_len;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering char *hostname;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering char *vendor_class_identifier;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering uint32_t mtu;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers uint32_t xid;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers usec_t start_time;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers unsigned int attempt;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering usec_t request_sent;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering sd_event_source *timeout_t1;
599659860c770058f2eb04d578c521c16e0b1853Lennart Poettering sd_event_source *timeout_t2;
599659860c770058f2eb04d578c521c16e0b1853Lennart Poettering sd_event_source *timeout_expire;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering sd_dhcp_client_cb_t cb;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering void *userdata;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering sd_dhcp_lease *lease;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering};
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sieversstatic const uint8_t default_req_opts[] = {
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers DHCP_OPTION_SUBNET_MASK,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_OPTION_ROUTER,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_OPTION_HOST_NAME,
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers DHCP_OPTION_DOMAIN_NAME,
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers DHCP_OPTION_DOMAIN_NAME_SERVER,
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers DHCP_OPTION_NTP_SERVER,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering};
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic int client_receive_message_raw(sd_event_source *s, int fd,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering uint32_t revents, void *userdata);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic int client_receive_message_udp(sd_event_source *s, int fd,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering uint32_t revents, void *userdata);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic void client_stop(sd_dhcp_client *client, int error);
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sieversint sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers void *userdata) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(client, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers client->cb = cb;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers client->userdata = userdata;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringint sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(client, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers client->request_broadcast = !!broadcast;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sieversint sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers size_t i;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers assert_return(client, -EINVAL);
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers assert_return (IN_SET(client->state, DHCP_STATE_INIT,
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers DHCP_STATE_STOPPED), -EBUSY);
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers switch(option) {
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers case DHCP_OPTION_PAD:
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers case DHCP_OPTION_OVERLOAD:
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers case DHCP_OPTION_MESSAGE_TYPE:
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers case DHCP_OPTION_PARAMETER_REQUEST_LIST:
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers case DHCP_OPTION_END:
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers return -EINVAL;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers default:
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers break;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers }
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers for (i = 0; i < client->req_opts_size; i++)
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers if (client->req_opts[i] == option)
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers return -EEXIST;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers client->req_opts_size + 1))
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers return -ENOMEM;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers client->req_opts[client->req_opts_size++] = option;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers return 0;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers}
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sieversint sd_dhcp_client_set_request_address(sd_dhcp_client *client,
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers const struct in_addr *last_addr) {
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers assert_return(client, -EINVAL);
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers assert_return (IN_SET(client->state, DHCP_STATE_INIT,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_STATE_STOPPED), -EBUSY);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (last_addr)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->last_addr = last_addr->s_addr;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering else
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->last_addr = INADDR_ANY;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringint sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(client, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return (IN_SET(client->state, DHCP_STATE_INIT,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_STATE_STOPPED), -EBUSY);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(interface_index > 0, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->index = interface_index;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringint sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering size_t addr_len, uint16_t arp_type) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_CLIENT_DONT_DESTROY(client);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering bool need_restart = false;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(client, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(addr, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(arp_type > 0, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (arp_type == ARPHRD_ETHER)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(addr_len == ETH_ALEN, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering else if (arp_type == ARPHRD_INFINIBAND)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering else
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return -EINVAL;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (client->mac_addr_len == addr_len &&
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering memcmp(&client->mac_addr, addr, addr_len) == 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_dhcp_client(client, "Changing MAC address on running DHCP "
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering "client, restarting");
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering need_restart = true;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client_stop(client, DHCP_EVENT_STOP);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering memcpy(&client->mac_addr, addr, addr_len);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->mac_addr_len = addr_len;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->arp_type = arp_type;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (need_restart && client->state != DHCP_STATE_STOPPED)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering sd_dhcp_client_start(client);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringint sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering const uint8_t **data, size_t *data_len) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(client, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(type, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(data, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(data_len, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering *type = 0;
599659860c770058f2eb04d578c521c16e0b1853Lennart Poettering *data = NULL;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering *data_len = 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (client->client_id_len) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering *type = client->client_id.type;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering *data = client->client_id.raw.data;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering *data_len = client->client_id_len - sizeof(client->client_id.type);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringint sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering const uint8_t *data, size_t data_len) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_CLIENT_DONT_DESTROY(client);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering bool need_restart = false;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(client, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(data, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering switch (type) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering case ARPHRD_ETHER:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (data_len != ETH_ALEN)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return -EINVAL;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering break;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering case ARPHRD_INFINIBAND:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (data_len != INFINIBAND_ALEN)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return -EINVAL;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering break;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering default:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering break;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (client->client_id_len == data_len + sizeof(client->client_id.type) &&
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->client_id.type == type &&
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering memcmp(&client->client_id.raw.data, data, data_len) == 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_dhcp_client(client, "Changing client ID on running DHCP "
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering "client, restarting");
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering need_restart = true;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client_stop(client, DHCP_EVENT_STOP);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->client_id.type = type;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering memcpy(&client->client_id.raw.data, data, data_len);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->client_id_len = data_len + sizeof (client->client_id.type);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (need_restart && client->state != DHCP_STATE_STOPPED)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering sd_dhcp_client_start(client);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringint sd_dhcp_client_set_hostname(sd_dhcp_client *client,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering const char *hostname) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering char *new_hostname = NULL;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(client, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (streq_ptr(client->hostname, hostname))
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (hostname) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering new_hostname = strdup(hostname);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (!new_hostname)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return -ENOMEM;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering free(client->hostname);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->hostname = new_hostname;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringint sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering const char *vci) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering char *new_vci = NULL;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(client, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering new_vci = strdup(vci);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (!new_vci)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return -ENOMEM;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering free(client->vendor_class_identifier);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->vendor_class_identifier = new_vci;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringint sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(client, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->mtu = mtu;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringint sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(client, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(ret, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (client->state != DHCP_STATE_BOUND &&
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->state != DHCP_STATE_RENEWING &&
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->state != DHCP_STATE_REBINDING)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return -EADDRNOTAVAIL;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
c978343015c787713651dff571acb5207367f5f2Lennart Poettering *ret = sd_dhcp_lease_ref(client->lease);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic void client_notify(sd_dhcp_client *client, int event) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (client->cb)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->cb(client, event, client->userdata);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic int client_initialize(sd_dhcp_client *client) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_return(client, -EINVAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->receive_message =
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering sd_event_source_unref(client->receive_message);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->fd = asynchronous_close(client->fd);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->timeout_resend = sd_event_source_unref(client->timeout_resend);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->timeout_expire = sd_event_source_unref(client->timeout_expire);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->attempt = 1;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->state = DHCP_STATE_INIT;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->xid = 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (client->lease)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->lease = sd_dhcp_lease_unref(client->lease);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic void client_stop(sd_dhcp_client *client, int error) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(client);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (error < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_dhcp_client(client, "STOPPED: %s", strerror(-error));
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering else if (error == DHCP_EVENT_STOP)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_dhcp_client(client, "STOPPED");
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering else
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_dhcp_client(client, "STOPPED: Unknown event");
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client_notify(client, error);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client_initialize(client);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
f75cb30bf97f623417cc7ee4b1bcc5c36cdbeb20Dave Reisnerstatic int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering uint8_t type, size_t *_optlen, size_t *_optoffset) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering _cleanup_free_ DHCPPacket *packet;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering size_t optlen, optoffset, size;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering be16_t max_size;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering usec_t time_now;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering uint16_t secs;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering int r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(client);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(client->start_time);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(ret);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(_optlen);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(_optoffset);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering optlen = DHCP_MIN_OPTIONS_SIZE;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering size = sizeof(DHCPPacket) + optlen;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering packet = malloc0(size);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (!packet)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return -ENOMEM;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->arp_type, optlen, &optoffset);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering refuse to issue an DHCP lease if 'secs' is set to zero */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(time_now >= client->start_time);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* seconds between sending first and last DISCOVER
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering * must always be strictly positive to deal with broken servers */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering packet->dhcp.secs = htobe16(secs);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* RFC2132 section 4.1
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering A client that cannot receive unicast IP datagrams until its protocol
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering software has been configured with an IP address SHOULD set the
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCPREQUEST messages that client sends. The BROADCAST bit will
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering provide a hint to the DHCP server and BOOTP relay agent to broadcast
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering any messages to the client on the client's subnet.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering Note: some interfaces needs this to be enabled, but some networks
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering needs this to be disabled as broadcasts are filteretd, so this
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering needs to be configurable */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering packet->dhcp.flags = htobe16(0x8000);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* RFC2132 section 4.1.1:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering The client MUST include its hardware address in the ’chaddr’ field, if
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering necessary for delivery of DHCP reply messages. Non-Ethernet
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering interfaces will leave 'chaddr' empty and use the client identifier
857a493d55f94731394e4d9f61ffce661858e9a0Lennart Poettering instead (eg, RFC 4390 section 2.1).
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (client->arp_type == ARPHRD_ETHER)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
857a493d55f94731394e4d9f61ffce661858e9a0Lennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* If no client identifier exists, construct an RFC 4361-compliant one */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (client->client_id_len == 0) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering size_t duid_len;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->client_id.type = 255;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
7591abd48079edc1f2adbd922e4b83eb73abeabeLennart Poettering return r;
7591abd48079edc1f2adbd922e4b83eb73abeabeLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
c978343015c787713651dff571acb5207367f5f2Lennart Poettering return r;
c978343015c787713651dff571acb5207367f5f2Lennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* Some DHCP servers will refuse to issue an DHCP lease if the Client
7591abd48079edc1f2adbd922e4b83eb73abeabeLennart Poettering Identifier option is not set */
7591abd48079edc1f2adbd922e4b83eb73abeabeLennart Poettering if (client->client_id_len) {
7591abd48079edc1f2adbd922e4b83eb73abeabeLennart Poettering r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
7591abd48079edc1f2adbd922e4b83eb73abeabeLennart Poettering DHCP_OPTION_CLIENT_IDENTIFIER,
7591abd48079edc1f2adbd922e4b83eb73abeabeLennart Poettering client->client_id_len,
7591abd48079edc1f2adbd922e4b83eb73abeabeLennart Poettering &client->client_id);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* RFC2131 section 3.5:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering in its initial DHCPDISCOVER or DHCPREQUEST message, a
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client may provide the server with a list of specific
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering parameters the client is interested in. If the client
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering includes a list of parameters in a DHCPDISCOVER message,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering it MUST include that list in any subsequent DHCPREQUEST
c978343015c787713651dff571acb5207367f5f2Lennart Poettering messages.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_OPTION_PARAMETER_REQUEST_LIST,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->req_opts_size, client->req_opts);
c978343015c787713651dff571acb5207367f5f2Lennart Poettering if (r < 0)
c978343015c787713651dff571acb5207367f5f2Lennart Poettering return r;
c978343015c787713651dff571acb5207367f5f2Lennart Poettering
c978343015c787713651dff571acb5207367f5f2Lennart Poettering /* RFC2131 section 3.5:
c978343015c787713651dff571acb5207367f5f2Lennart Poettering The client SHOULD include the ’maximum DHCP message size’ option to
c978343015c787713651dff571acb5207367f5f2Lennart Poettering let the server know how large the server may make its DHCP messages.
c978343015c787713651dff571acb5207367f5f2Lennart Poettering
c978343015c787713651dff571acb5207367f5f2Lennart Poettering Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering than the defined default size unless the Maximum Messge Size option
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering is explicitly set
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering RFC3442 "Requirements to Avoid Sizing Constraints":
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering Because a full routing table can be quite large, the standard 576
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering octet maximum size for a DHCP message may be too short to contain
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering some legitimate Classless Static Route options. Because of this,
7591abd48079edc1f2adbd922e4b83eb73abeabeLennart Poettering clients implementing the Classless Static Route option SHOULD send a
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering Maximum DHCP Message Size [4] option if the DHCP client's TCP/IP
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering stack is capable of receiving larger IP datagrams. In this case, the
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client SHOULD set the value of this option to at least the MTU of the
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering interface that the client is configuring. The client MAY set the
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering value of this option higher, up to the size of the largest UDP packet
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering it is prepared to accept. (Note that the value specified in the
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering Maximum DHCP Message Size option is the total maximum packet size,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering including IP and UDP headers.)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering max_size = htobe16(size);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering 2, &max_size);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering *_optlen = optlen;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering *_optoffset = optoffset;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering *ret = packet;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering packet = NULL;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
c978343015c787713651dff571acb5207367f5f2Lennart Poettering}
c978343015c787713651dff571acb5207367f5f2Lennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering size_t len) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering INADDR_BROADCAST, DHCP_PORT_SERVER, len);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return dhcp_network_send_raw_socket(client->fd, &client->link,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering packet, len);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic int client_send_discover(sd_dhcp_client *client) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering _cleanup_free_ DHCPPacket *discover = NULL;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering size_t optoffset, optlen;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering int r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(client);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(client->state == DHCP_STATE_INIT ||
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->state == DHCP_STATE_SELECTING);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = client_message_init(client, &discover, DHCP_DISCOVER,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering &optlen, &optoffset);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* the client may suggest values for the network address
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering and lease time in the DHCPDISCOVER message. The client may include
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering the ’requested IP address’ option to suggest that a particular IP
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering address be assigned, and may include the ’IP address lease time’
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering option to suggest the lease time it would like.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (client->last_addr != INADDR_ANY) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_OPTION_REQUESTED_IP_ADDRESS,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering 4, &client->last_addr);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* it is unclear from RFC 2131 if client should send hostname in
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCPDISCOVER but dhclient does and so we do as well
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (client->hostname) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_OPTION_HOST_NAME,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering strlen(client->hostname), client->hostname);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (client->vendor_class_identifier) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering strlen(client->vendor_class_identifier),
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client->vendor_class_identifier);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_OPTION_END, 0, NULL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* We currently ignore:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering The client SHOULD wait a random time between one and ten seconds to
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering desynchronize the use of DHCP at startup.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_dhcp_client(client, "DISCOVER");
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic int client_send_request(sd_dhcp_client *client) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering _cleanup_free_ DHCPPacket *request = NULL;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering size_t optoffset, optlen;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering int r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = client_message_init(client, &request, DHCP_REQUEST,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering &optlen, &optoffset);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering switch (client->state) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SELECTING should be REQUESTING)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering case DHCP_STATE_REQUESTING:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* Client inserts the address of the selected server in ’server
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering filled in with the yiaddr value from the chosen DHCPOFFER.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_OPTION_SERVER_IDENTIFIER,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering 4, &client->lease->server_address);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_OPTION_REQUESTED_IP_ADDRESS,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering 4, &client->lease->address);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering break;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering case DHCP_STATE_INIT_REBOOT:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering option MUST be filled in with client’s notion of its previously
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assigned address. ’ciaddr’ MUST be zero.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering DHCP_OPTION_REQUESTED_IP_ADDRESS,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering 4, &client->last_addr);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering break;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering case DHCP_STATE_RENEWING:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client’s IP address.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* fall through */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering case DHCP_STATE_REBINDING:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering client’s IP address.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering This message MUST be broadcast to the 0xffffffff IP broadcast address.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering request->dhcp.ciaddr = client->lease->address;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering break;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering case DHCP_STATE_INIT:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering case DHCP_STATE_SELECTING:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering case DHCP_STATE_REBOOTING:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering case DHCP_STATE_BOUND:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering case DHCP_STATE_STOPPED:
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return -EINVAL;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
if (client->hostname) {
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
DHCP_OPTION_HOST_NAME,
strlen(client->hostname), client->hostname);
if (r < 0)
return r;
}
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
if (client->state == DHCP_STATE_RENEWING) {
r = dhcp_network_send_udp_socket(client->fd,
client->lease->server_address,
DHCP_PORT_SERVER,
&request->dhcp,
sizeof(DHCPMessage) + optoffset);
} else {
r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
}
if (r < 0)
return r;
switch (client->state) {
case DHCP_STATE_REQUESTING:
log_dhcp_client(client, "REQUEST (requesting)");
break;
case DHCP_STATE_INIT_REBOOT:
log_dhcp_client(client, "REQUEST (init-reboot)");
break;
case DHCP_STATE_RENEWING:
log_dhcp_client(client, "REQUEST (renewing)");
break;
case DHCP_STATE_REBINDING:
log_dhcp_client(client, "REQUEST (rebinding)");
break;
default:
log_dhcp_client(client, "REQUEST (invalid)");
break;
}
return 0;
}
static int client_start(sd_dhcp_client *client);
static int client_timeout_resend(sd_event_source *s, uint64_t usec,
void *userdata) {
sd_dhcp_client *client = userdata;
DHCP_CLIENT_DONT_DESTROY(client);
usec_t next_timeout = 0;
uint64_t time_now;
uint32_t time_left;
int r;
assert(s);
assert(client);
assert(client->event);
r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
goto error;
switch (client->state) {
case DHCP_STATE_RENEWING:
time_left = (client->lease->t2 - client->lease->t1) / 2;
if (time_left < 60)
time_left = 60;
next_timeout = time_now + time_left * USEC_PER_SEC;
break;
case DHCP_STATE_REBINDING:
time_left = (client->lease->lifetime - client->lease->t2) / 2;
if (time_left < 60)
time_left = 60;
next_timeout = time_now + time_left * USEC_PER_SEC;
break;
case DHCP_STATE_REBOOTING:
/* start over as we did not receive a timely ack or nak */
r = client_initialize(client);
if (r < 0)
goto error;
r = client_start(client);
if (r < 0)
goto error;
else {
log_dhcp_client(client, "REBOOTED");
return 0;
}
case DHCP_STATE_INIT:
case DHCP_STATE_INIT_REBOOT:
case DHCP_STATE_SELECTING:
case DHCP_STATE_REQUESTING:
case DHCP_STATE_BOUND:
if (client->attempt < 64)
client->attempt *= 2;
next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
break;
case DHCP_STATE_STOPPED:
r = -EINVAL;
goto error;
}
next_timeout += (random_u32() & 0x1fffff);
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
r = sd_event_add_time(client->event,
&client->timeout_resend,
clock_boottime_or_monotonic(),
next_timeout, 10 * USEC_PER_MSEC,
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;
switch (client->state) {
case DHCP_STATE_INIT:
r = client_send_discover(client);
if (r >= 0) {
client->state = DHCP_STATE_SELECTING;
client->attempt = 1;
} else {
if (client->attempt >= 64)
goto error;
}
break;
case DHCP_STATE_SELECTING:
r = client_send_discover(client);
if (r < 0 && client->attempt >= 64)
goto error;
break;
case DHCP_STATE_INIT_REBOOT:
case DHCP_STATE_REQUESTING:
case DHCP_STATE_RENEWING:
case DHCP_STATE_REBINDING:
r = client_send_request(client);
if (r < 0 && client->attempt >= 64)
goto error;
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);
if (r < 0)
goto error;
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;
CMSG_FOREACH(cmsg, &msg) {
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;
}