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