sd-dhcp-client.c revision d23c45bfccb3ed6e2628e6d12b4ea12b8c920ab9
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt/***
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt This file is part of systemd.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt Copyright (C) 2013 Intel Corporation. All rights reserved.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt systemd is free software; you can redistribute it and/or modify it
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt under the terms of the GNU Lesser General Public License as published by
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt the Free Software Foundation; either version 2.1 of the License, or
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt (at your option) any later version.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt systemd is distributed in the hope that it will be useful, but
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt WITHOUT ANY WARRANTY; without even the implied warranty of
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt Lesser General Public License for more details.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt You should have received a copy of the GNU Lesser General Public License
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt along with systemd; If not, see <http://www.gnu.org/licenses/>.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt***/
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include <stdlib.h>
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include <errno.h>
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include <string.h>
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include <stdio.h>
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt#include <net/ethernet.h>
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include <sys/param.h>
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt#include <sys/ioctl.h>
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt#include "util.h"
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen#include "list.h"
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include "dhcp-protocol.h"
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include "dhcp-internal.h"
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt#include "dhcp-lease-internal.h"
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include "sd-dhcp-client.h"
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flyktstruct sd_dhcp_client {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt DHCPState state;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt sd_event *event;
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen int event_priority;
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen sd_event_source *timeout_resend;
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen int index;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt int fd;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt union sockaddr_union link;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt sd_event_source *receive_message;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt uint8_t *req_opts;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt size_t req_opts_allocated;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt size_t req_opts_size;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt be32_t last_addr;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt struct ether_addr mac_addr;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt uint32_t xid;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt usec_t start_time;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt uint16_t secs;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt unsigned int attempt;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt usec_t request_sent;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt sd_event_source *timeout_t1;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt sd_event_source *timeout_t2;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt sd_event_source *timeout_expire;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt sd_dhcp_client_cb_t cb;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt void *userdata;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt sd_dhcp_lease *lease;
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen};
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersenstatic const uint8_t default_req_opts[] = {
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen DHCP_OPTION_SUBNET_MASK,
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen DHCP_OPTION_ROUTER,
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen DHCP_OPTION_HOST_NAME,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt DHCP_OPTION_DOMAIN_NAME,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt DHCP_OPTION_DOMAIN_NAME_SERVER,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt DHCP_OPTION_NTP_SERVER,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt};
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic int client_receive_message_raw(sd_event_source *s, int fd,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt uint32_t revents, void *userdata);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic int client_receive_message_udp(sd_event_source *s, int fd,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt uint32_t revents, void *userdata);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktint sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt void *userdata) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt assert_return(client, -EINVAL);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt client->cb = cb;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt client->userdata = userdata;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return 0;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt}
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktint sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt size_t i;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert_return(client, -EINVAL);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert_return (client->state == DHCP_STATE_INIT, -EBUSY);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt switch(option) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_OPTION_PAD:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_OPTION_OVERLOAD:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_OPTION_MESSAGE_TYPE:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_OPTION_PARAMETER_REQUEST_LIST:
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt case DHCP_OPTION_END:
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt return -EINVAL;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt default:
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt break;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt }
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt for (i = 0; i < client->req_opts_size; i++)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (client->req_opts[i] == option)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return -EEXIST;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->req_opts_size + 1))
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return -ENOMEM;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->req_opts[client->req_opts_size++] = option;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return 0;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt}
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktint sd_dhcp_client_set_request_address(sd_dhcp_client *client,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt const struct in_addr *last_addr) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert_return(client, -EINVAL);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (last_addr)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->last_addr = last_addr->s_addr;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt else
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->last_addr = INADDR_ANY;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return 0;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt}
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktint sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert_return(client, -EINVAL);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt assert_return(interface_index >= -1, -EINVAL);
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt client->index = interface_index;
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt return 0;
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt}
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flyktint sd_dhcp_client_set_mac(sd_dhcp_client *client,
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt const struct ether_addr *addr) {
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt assert_return(client, -EINVAL);
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_dhcp_client(client, "set MAC address to "
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt addr->ether_addr_octet[0],
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt addr->ether_addr_octet[1],
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt addr->ether_addr_octet[2],
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt addr->ether_addr_octet[3],
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt addr->ether_addr_octet[4],
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt addr->ether_addr_octet[5]);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt memcpy(&client->mac_addr, addr, ETH_ALEN);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return 0;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt}
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktint sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt assert_return(client, -EINVAL);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt assert_return(ret, -EINVAL);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (client->state != DHCP_STATE_BOUND &&
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt client->state != DHCP_STATE_RENEWING &&
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt client->state != DHCP_STATE_REBINDING)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return -EADDRNOTAVAIL;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt *ret = sd_dhcp_lease_ref(client->lease);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt return 0;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt}
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int client_notify(sd_dhcp_client *client, int event) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (client->cb)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt client->cb(client, event, client->userdata);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return 0;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt}
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktstatic int client_stop(sd_dhcp_client *client, int error) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert_return(client, -EINVAL);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->receive_message =
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt sd_event_source_unref(client->receive_message);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (client->fd >= 0)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt close_nointr_nofail(client->fd);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->fd = -1;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->timeout_resend = sd_event_source_unref(client->timeout_resend);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt client->timeout_expire = sd_event_source_unref(client->timeout_expire);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt client->attempt = 1;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt client_notify(client, error);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt client->start_time = 0;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt client->secs = 0;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt client->state = DHCP_STATE_INIT;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (client->lease)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt client->lease = sd_dhcp_lease_unref(client->lease);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt log_dhcp_client(client, "STOPPED");
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return 0;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt}
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic int client_message_init(sd_dhcp_client *client, DHCPMessage *message,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt uint8_t type, uint16_t secs, uint8_t **opt,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt size_t *optlen) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt int r;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt assert(secs);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt r = dhcp_message_init(message, BOOTREQUEST, client->xid, type, opt,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt optlen);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (r < 0)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return r;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt refuse to issue an DHCP lease if 'secs' is set to zero */
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt message->secs = htobe16(secs);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (client->state == DHCP_STATE_RENEWING ||
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt client->state == DHCP_STATE_REBINDING)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt message->ciaddr = client->lease->address;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt /* Some DHCP servers will refuse to issue an DHCP lease if the Client
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt Identifier option is not set */
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt r = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt ETH_ALEN, &client->mac_addr);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (r < 0)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return r;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt be16_t max_size;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt r = dhcp_option_append(opt, optlen,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt DHCP_OPTION_PARAMETER_REQUEST_LIST,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt client->req_opts_size,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt client->req_opts);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return r;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt /* Some DHCP servers will send bigger DHCP packets than the
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt defined default size unless the Maximum Messge Size option
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt is explicitely set */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE +
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt DHCP_MIN_OPTIONS_SIZE);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt r = dhcp_option_append(opt, optlen,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt 2, &max_size);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return r;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt }
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return 0;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt}
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt size_t len) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt INADDR_BROADCAST, DHCP_PORT_SERVER, len);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return dhcp_network_send_raw_socket(client->fd, &client->link,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt packet, len);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt}
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int client_send_discover(sd_dhcp_client *client, uint16_t secs) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt int err = 0;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt _cleanup_free_ DHCPPacket *discover;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt size_t optlen, len;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt uint8_t *opt;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt optlen = DHCP_MIN_OPTIONS_SIZE;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt len = sizeof(DHCPPacket) + optlen;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt discover = malloc0(len);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (!discover)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return -ENOMEM;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = client_message_init(client, &discover->dhcp, DHCP_DISCOVER,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt secs, &opt, &optlen);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (err < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return err;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (client->last_addr != INADDR_ANY) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt err = dhcp_option_append(&opt, &optlen,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt DHCP_OPTION_REQUESTED_IP_ADDRESS,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt 4, &client->last_addr);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (err < 0)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return err;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt }
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (err < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return err;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = dhcp_client_send_raw(client, discover, len);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (err < 0)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return err;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt log_dhcp_client(client, "DISCOVER");
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return 0;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt}
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int client_send_request(sd_dhcp_client *client, uint16_t secs) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt _cleanup_free_ DHCPPacket *request;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt size_t optlen, len;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt int err;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt uint8_t *opt;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt optlen = DHCP_MIN_OPTIONS_SIZE;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt len = sizeof(DHCPPacket) + optlen;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt request = malloc0(len);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (!request)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return -ENOMEM;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = client_message_init(client, &request->dhcp, DHCP_REQUEST, secs,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt &opt, &optlen);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (err < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return err;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (client->state == DHCP_STATE_REQUESTING) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = dhcp_option_append(&opt, &optlen,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt DHCP_OPTION_REQUESTED_IP_ADDRESS,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt 4, &client->lease->address);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (err < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return err;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = dhcp_option_append(&opt, &optlen,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt DHCP_OPTION_SERVER_IDENTIFIER,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt 4, &client->lease->server_address);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (err < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return err;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt }
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (err < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return err;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (client->state == DHCP_STATE_RENEWING) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = dhcp_network_send_udp_socket(client->fd,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt client->lease->server_address,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt DHCP_PORT_SERVER,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt &request->dhcp,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt len - DHCP_IP_UDP_SIZE);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt } else {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = dhcp_client_send_raw(client, request, len);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt }
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (err < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return err;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt log_dhcp_client(client, "REQUEST");
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt return 0;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt}
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flyktstatic uint16_t client_update_secs(sd_dhcp_client *client, usec_t time_now)
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt{
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt return client->secs;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt}
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flyktstatic int client_timeout_resend(sd_event_source *s, uint64_t usec,
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt void *userdata) {
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt sd_dhcp_client *client = userdata;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt usec_t next_timeout = 0;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt uint64_t time_now;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt uint32_t time_left;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt int r;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt assert(s);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt assert(client);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt assert(client->event);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt r = sd_event_get_now_monotonic(client->event, &time_now);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt if (r < 0)
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt goto error;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt switch (client->state) {
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt case DHCP_STATE_RENEWING:
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt time_left = (client->lease->t2 - client->lease->t1) / 2;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt if (time_left < 60)
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt time_left = 60;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt next_timeout = time_now + time_left * USEC_PER_SEC;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt break;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt case DHCP_STATE_REBINDING:
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt time_left = (client->lease->lifetime - client->lease->t2) / 2;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt if (time_left < 60)
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt time_left = 60;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt next_timeout = time_now + time_left * USEC_PER_SEC;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt break;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_STATE_INIT:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_STATE_INIT_REBOOT:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_STATE_REBOOTING:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_STATE_SELECTING:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_STATE_REQUESTING:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_STATE_BOUND:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (client->attempt < 64)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->attempt *= 2;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt break;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt }
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt next_timeout += (random_u32() & 0x1fffff);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->timeout_resend = sd_event_source_unref(client->timeout_resend);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = sd_event_add_monotonic(client->event,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt &client->timeout_resend,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt next_timeout,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt 10 * USEC_PER_MSEC,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client_timeout_resend, client);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt goto error;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = sd_event_source_set_priority(client->timeout_resend,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->event_priority);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt goto error;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt switch (client->state) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_STATE_INIT:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client_update_secs(client, time_now);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = client_send_discover(client, client->secs);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r >= 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->state = DHCP_STATE_SELECTING;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->attempt = 1;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt } else {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (client->attempt >= 64)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt goto error;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt }
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt break;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_STATE_SELECTING:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client_update_secs(client, time_now);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = client_send_discover(client, client->secs);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0 && client->attempt >= 64)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt goto error;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt break;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_STATE_REQUESTING:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_STATE_RENEWING:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_STATE_REBINDING:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = client_send_request(client, client->secs);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0 && client->attempt >= 64)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt goto error;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->request_sent = time_now;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt break;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_STATE_INIT_REBOOT:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_STATE_REBOOTING:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case DHCP_STATE_BOUND:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt break;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt }
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykterror:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client_stop(client, r);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* Errors were dealt with when stopping the client, don't spill
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt errors into the event loop handler */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt}
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int client_initialize_events(sd_dhcp_client *client,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt sd_event_io_handler_t io_callback) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt int r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert(client);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert(client->event);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = sd_event_add_io(client->event, &client->receive_message,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->fd, EPOLLIN, io_callback,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt goto error;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = sd_event_source_set_priority(client->receive_message,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->event_priority);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt goto error;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->timeout_resend = sd_event_source_unref(client->timeout_resend);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = sd_event_add_monotonic(client->event,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt &client->timeout_resend, 0, 0,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client_timeout_resend, client);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt goto error;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = sd_event_source_set_priority(client->timeout_resend,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->event_priority);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykterror:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client_stop(client, r);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt}
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int client_timeout_expire(sd_event_source *s, uint64_t usec,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt void *userdata) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt sd_dhcp_client *client = userdata;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_dhcp_client(client, "EXPIRED");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client_stop(client, DHCP_EVENT_EXPIRED);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 0;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt}
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt sd_dhcp_client *client = userdata;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt int r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (client->fd >= 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->receive_message =
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt sd_event_source_unref(client->receive_message);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt close_nointr_nofail(client->fd);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->fd = -1;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt }
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->state = DHCP_STATE_REBINDING;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->attempt = 1;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = dhcp_network_bind_raw_socket(client->index, &client->link);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client_stop(client, r);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt }
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->fd = r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_dhcp_client(client, "TIMEOUT T2");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return client_initialize_events(client, client_receive_message_raw);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt}
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int client_timeout_t1(sd_event_source *s, uint64_t usec,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt void *userdata) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt sd_dhcp_client *client = userdata;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt int r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->state = DHCP_STATE_RENEWING;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->attempt = 1;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = dhcp_network_bind_udp_socket(client->index,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->lease->address,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt DHCP_PORT_CLIENT);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client_stop(client, r);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt }
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->fd = r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_dhcp_client(client, "TIMEOUT T1");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return client_initialize_events(client, client_receive_message_udp);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt}
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt size_t len) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt int r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = dhcp_lease_new(&lease);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r != DHCP_OFFER)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return -ENOMSG;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt lease->next_server = offer->siaddr;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt lease->address = offer->yiaddr;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (lease->address == INADDR_ANY ||
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt lease->server_address == INADDR_ANY ||
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt lease->subnet_mask == INADDR_ANY ||
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt lease->lifetime == 0)
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt return -ENOMSG;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt client->lease = lease;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt lease = NULL;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt log_dhcp_client(client, "OFFER");
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt return 0;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt}
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flyktstatic int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt size_t len) {
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt int r;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt r = dhcp_lease_new(&lease);
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt if (r < 0)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return r;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt if (r == DHCP_NAK) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt log_dhcp_client(client, "NAK");
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt return DHCP_EVENT_NO_LEASE;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt }
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (r != DHCP_ACK)
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt return -ENOMSG;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt lease->next_server = ack->siaddr;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt lease->address = ack->yiaddr;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt if (lease->address == INADDR_ANY ||
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt lease->server_address == INADDR_ANY ||
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt lease->subnet_mask == INADDR_ANY || lease->lifetime == 0)
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt return -ENOMSG;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt r = DHCP_EVENT_IP_ACQUIRE;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt if (client->lease) {
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt if (client->lease->address != lease->address ||
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt client->lease->subnet_mask != lease->subnet_mask ||
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt client->lease->router != lease->router) {
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt r = DHCP_EVENT_IP_CHANGE;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt }
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt client->lease = sd_dhcp_lease_unref(client->lease);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt }
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt client->lease = lease;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt lease = NULL;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt log_dhcp_client(client, "ACK");
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return r;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt}
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic uint64_t client_compute_timeout(uint64_t request_sent,
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt uint32_t lifetime) {
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt return request_sent + (lifetime - 3) * USEC_PER_SEC +
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt + (random_u32() & 0x1fffff);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt}
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktstatic int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt uint64_t next_timeout;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt int r;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert(client);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert(client->event);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (client->lease->lifetime < 10)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return -EINVAL;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->timeout_expire = sd_event_source_unref(client->timeout_expire);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt if (!client->lease->t1)
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt client->lease->t1 = client->lease->lifetime / 2;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt next_timeout = client_compute_timeout(client->request_sent,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->lease->t1);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (next_timeout < usec)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return -EINVAL;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = sd_event_add_monotonic(client->event,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt &client->timeout_t1,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt next_timeout,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt 10 * USEC_PER_MSEC,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client_timeout_t1, client);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return r;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = sd_event_source_set_priority(client->timeout_t1,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->event_priority);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return r;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (!client->lease->t2)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->lease->t2 = client->lease->lifetime * 7 / 8;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (client->lease->t2 < client->lease->t1)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return -EINVAL;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (client->lease->lifetime < client->lease->t2)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return -EINVAL;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt next_timeout = client_compute_timeout(client->request_sent,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->lease->t2);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (next_timeout < usec)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return -EINVAL;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = sd_event_add_monotonic(client->event,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt &client->timeout_t2,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt next_timeout,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt 10 * USEC_PER_MSEC,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client_timeout_t2, client);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return r;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = sd_event_source_set_priority(client->timeout_t2,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->event_priority);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return r;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt next_timeout = client_compute_timeout(client->request_sent,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->lease->lifetime);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (next_timeout < usec)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return -EINVAL;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = sd_event_add_monotonic(client->event,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt &client->timeout_expire, next_timeout,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt 10 * USEC_PER_MSEC,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client_timeout_expire, client);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return r;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = sd_event_source_set_priority(client->timeout_expire,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->event_priority);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return r;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return 0;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt}
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktstatic int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen int len, usec_t time_now) {
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen int r = 0, notify_event = 0;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert(client);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert(client->event);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert(message);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (len < DHCP_MESSAGE_SIZE) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_dhcp_client(client, "message too small (%d bytes): "
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt "ignoring", len);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return 0;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt }
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (message->op != BOOTREPLY) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen return 0;
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen }
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen if (be32toh(message->xid) != client->xid) {
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen log_dhcp_client(client, "received xid (%u) does not match "
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen "expected (%u): ignoring",
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen be32toh(message->xid), client->xid);
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen return 0;
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen }
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen if (memcmp(&message->chaddr[0], &client->mac_addr.ether_addr_octet,
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen ETHER_ADDR_LEN)) {
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen log_dhcp_client(client, "received chaddr does not match "
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt "expected: ignoring");
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return 0;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt }
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt 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_monotonic(client->event,
&client->timeout_resend, 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;
}
break;
case DHCP_STATE_REQUESTING:
case DHCP_STATE_RENEWING:
case DHCP_STATE_REBINDING:
r = client_handle_ack(client, message, len);
if (r == DHCP_EVENT_NO_LEASE)
goto error;
if (r >= 0) {
client->timeout_resend =
sd_event_source_unref(client->timeout_resend);
if (client->state == DHCP_STATE_REQUESTING)
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, time_now);
if (r < 0)
goto error;
if (notify_event)
client_notify(client, notify_event);
client->receive_message =
sd_event_source_unref(client->receive_message);
close_nointr_nofail(client->fd);
client->fd = -1;
}
r = 0;
break;
case DHCP_STATE_INIT:
case DHCP_STATE_INIT_REBOOT:
case DHCP_STATE_REBOOTING:
case DHCP_STATE_BOUND:
break;
}
error:
if (r < 0 || r == DHCP_EVENT_NO_LEASE)
return client_stop(client, r);
return 0;
}
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;
usec_t time_now;
assert(s);
assert(client);
assert(client->event);
r = ioctl(fd, FIONREAD, &buflen);
if (r < 0 || buflen <= 0)
buflen = sizeof(DHCPMessage) + DHCP_MIN_OPTIONS_SIZE;
message = malloc0(buflen);
if (!message)
return -ENOMEM;
len = read(fd, message, buflen);
if (len < 0)
return 0;
r = sd_event_get_now_monotonic(client->event, &time_now);
if (r < 0)
return client_stop(client, r);
return client_handle_message(client, message, len,
time_now);
}
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;
usec_t time_now;
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);
assert(client->event);
r = ioctl(fd, FIONREAD, &buflen);
if (r < 0 || buflen <= 0)
buflen = sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE;
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: %s", strerror(errno));
return 0;
}
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) {
struct tpacket_auxdata *aux = (void *)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;
r = sd_event_get_now_monotonic(client->event, &time_now);
if (r < 0)
return client_stop(client, r);
return client_handle_message(client, &packet->dhcp, len, time_now);
}
int sd_dhcp_client_start(sd_dhcp_client *client) {
int r;
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
assert_return(client->index > 0, -EINVAL);
assert_return(client->state == DHCP_STATE_INIT ||
client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
client->xid = random_u32();
r = dhcp_network_bind_raw_socket(client->index, &client->link);
if (r < 0) {
client_stop(client, r);
return r;
}
client->fd = r;
client->start_time = now(CLOCK_MONOTONIC);
client->secs = 0;
log_dhcp_client(client, "STARTED");
return client_initialize_events(client, client_receive_message_raw);
}
int sd_dhcp_client_stop(sd_dhcp_client *client) {
return client_stop(client, DHCP_EVENT_STOP);
}
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;
}
void sd_dhcp_client_free(sd_dhcp_client *client) {
if (!client)
return;
sd_dhcp_client_stop(client);
sd_dhcp_client_detach_event(client);
free(client->req_opts);
free(client);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_free);
#define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_freep)
int sd_dhcp_client_new(sd_dhcp_client **ret) {
_cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
assert_return(ret, -EINVAL);
client = new0(sd_dhcp_client, 1);
if (!client)
return -ENOMEM;
client->state = DHCP_STATE_INIT;
client->index = -1;
client->fd = -1;
client->attempt = 1;
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;
}