sd-dhcp-client.c revision ba6c0fd6303c63576983c7be892d80d954c1e4c5
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer This file is part of systemd.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer Copyright (C) 2013 Intel Corporation. All rights reserved.
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.
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.
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/>.
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#define MAX_CLIENT_ID_LEN 64 /* Arbitrary limit */
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier uint8_t type; /* 0: Generic (non-LL) (RFC 2132) */
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier uint8_t type; /* 1: Ethernet Link-Layer (RFC 2132) */
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier uint8_t type; /* 2 - 254: ARP/Link-Layer (RFC 2132) */
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier uint8_t type; /* 255: Node-specific (RFC 4361) */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int client_receive_message_raw(sd_event_source *s, int fd,
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int client_receive_message_udp(sd_event_source *s, int fd,
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic void client_stop(sd_dhcp_client *client, int error);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierint sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
e3ce42e70504922f0ea7149f90fc80b549844e93Evgeny Vereshchaginint sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
e3ce42e70504922f0ea7149f90fc80b549844e93Evgeny Vereshchagin client->request_broadcast = !!broadcast;
e3ce42e70504922f0ea7149f90fc80b549844e93Evgeny Vereshchaginint sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
e3ce42e70504922f0ea7149f90fc80b549844e93Evgeny Vereshchagin assert_return (IN_SET(client->state, DHCP_STATE_INIT,
e3ce42e70504922f0ea7149f90fc80b549844e93Evgeny Vereshchagin case DHCP_OPTION_PARAMETER_REQUEST_LIST:
e3ce42e70504922f0ea7149f90fc80b549844e93Evgeny Vereshchagin for (i = 0; i < client->req_opts_size; i++)
e3ce42e70504922f0ea7149f90fc80b549844e93Evgeny Vereshchagin if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
e3ce42e70504922f0ea7149f90fc80b549844e93Evgeny Vereshchagin client->req_opts[client->req_opts_size++] = option;
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchaginint sd_dhcp_client_set_request_address(sd_dhcp_client *client,
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin assert_return (IN_SET(client->state, DHCP_STATE_INIT,
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchaginint sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin assert_return (IN_SET(client->state, DHCP_STATE_INIT,
a2fbff31c9c319da51528f85ae97d019f1e61a86Evgeny Vereshchagin assert_return(interface_index > 0, -EINVAL);
cb2f9d3f296bc80b55f09880d61dfdf47fc98212Evgeny Vereshchaginint sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
9974ff63b182e67bf3d3d9262e2bfa84f0a1378bEvgeny Vereshchagin assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert_return(addr_len == ETH_ALEN, -EINVAL);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
ac289ce3f5eb3f13806f7c631c6b23cee18b26daEvgeny Vereshchagin memcmp(&client->mac_addr, addr, addr_len) == 0)
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");
8a8332f77e61d41f3bb28b8f929ed41e0ffaf721Zbigniew Jędrzejewski-Szmek client_stop(client, DHCP_EVENT_STOP);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (need_restart && client->state != DHCP_STATE_STOPPED)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierint sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
739d81ddd005fae2bb82edce5b8a6173c7c48b34Zbigniew Jędrzejewski-Szmek assert_return(client, -EINVAL);
739d81ddd005fae2bb82edce5b8a6173c7c48b34Zbigniew Jędrzejewski-Szmek *data_len = client->client_id_len -
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierint sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (client->client_id_len == data_len + sizeof (client->client_id.raw.type) &&
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier memcmp(&client->client_id.raw.data, data, data_len) == 0)
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 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 if (need_restart && client->state != DHCP_STATE_STOPPED)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierint sd_dhcp_client_set_hostname(sd_dhcp_client *client,
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier const char *hostname) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierint sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client,
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier const char *vci) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierint sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierint sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
99877b7e3782a51b31bf191825f0335500f52fe5Harald Hoyerstatic void client_notify(sd_dhcp_client *client, int event) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier client->cb(client, event, client->userdata);
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pittstatic int client_initialize(sd_dhcp_client *client) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier sd_event_source_unref(client->receive_message);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier client->fd = asynchronous_close(client->fd);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier client->timeout_resend = sd_event_source_unref(client->timeout_resend);
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);
417491f122b346a31cf8dc406c4f9195a5900cecEvgeny Vereshchagin client->lease = sd_dhcp_lease_unref(client->lease);
417491f122b346a31cf8dc406c4f9195a5900cecEvgeny Vereshchaginstatic void client_stop(sd_dhcp_client *client, int error) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier log_dhcp_client(client, "STOPPED: %s", strerror(-error));
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier log_dhcp_client(client, "STOPPED: Unknown event");
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 assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
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);
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;
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 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 /* 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 memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
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 memcpy(&client->client_id.eth.haddr, &client->mac_addr, ETH_ALEN);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer client->client_id_len = sizeof (client->client_id.eth);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Some DHCP servers will refuse to issue an DHCP lease if the Client
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer Identifier option is not set */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
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 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
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 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 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 r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return dhcp_network_send_raw_socket(client->fd, &client->link,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_send_discover(sd_dhcp_client *client) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = client_message_init(client, &discover, DHCP_DISCOVER,
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 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
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 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
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 r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_send_request(sd_dhcp_client *client) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = client_message_init(client, &request, DHCP_REQUEST,
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 /* 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 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
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 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
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 /* fall through */
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 This message MUST be broadcast to the 0xffffffff IP broadcast address.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_dhcp_client(client, "REQUEST (requesting)");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_dhcp_client(client, "REQUEST (init-reboot)");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_dhcp_client(client, "REQUEST (rebinding)");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_start(sd_dhcp_client *client);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_timeout_resend(sd_event_source *s, uint64_t usec,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer time_left = (client->lease->t2 - client->lease->t1) / 2;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer next_timeout = time_now + time_left * USEC_PER_SEC;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer time_left = (client->lease->lifetime - client->lease->t2) / 2;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer next_timeout = time_now + time_left * USEC_PER_SEC;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* start over as we did not receive a timely ack or nak */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer client->timeout_resend = sd_event_source_unref(client->timeout_resend);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = sd_event_source_set_priority(client->timeout_resend,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r >= 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Errors were dealt with when stopping the client, don't spill
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer errors into the event loop handler */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_initialize_io_events(sd_dhcp_client *client,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = sd_event_add_io(client->event, &client->receive_message,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = sd_event_source_set_priority(client->receive_message,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_initialize_time_events(sd_dhcp_client *client) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer client->timeout_resend = sd_event_source_unref(client->timeout_resend);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = sd_event_source_set_priority(client->timeout_resend,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_initialize_events(sd_dhcp_client *client,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer client_initialize_io_events(client, io_callback);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_start(sd_dhcp_client *client) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_return(client->state == DHCP_STATE_INIT ||
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = dhcp_network_bind_raw_socket(client->index, &client->link,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0) {
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 return client_initialize_events(client, client_receive_message_raw);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int client_timeout_expire(sd_event_source *s, uint64_t usec,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer /* lease was lost, start over if not freed or stopped in callback */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerstatic int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer client->receive_message = sd_event_source_unref(client->receive_message);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = dhcp_network_bind_raw_socket(client->index, &client->link,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer if (r < 0) {
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer return client_initialize_events(client, client_receive_message_raw);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerstatic int client_timeout_t1(sd_event_source *s, uint64_t usec,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerstatic int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer log_dhcp_client(client, "received message was not an OFFER, ignoring");
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer log_dhcp_client(client, "received lease lacks address, server "
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer "address or lease lifetime, ignoring");
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 Hoyerstatic int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerstatic int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer log_dhcp_client(client, "received message was not an ACK, ignoring");
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer log_dhcp_client(client, "received lease lacks address, server "
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer "address or lease lifetime, ignoring");
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 if (client->lease->address != lease->address ||
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer client->lease->subnet_mask != lease->subnet_mask ||
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer client->lease = sd_dhcp_lease_unref(client->lease);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerstatic uint64_t client_compute_timeout(sd_dhcp_client *client,
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyerstatic int client_set_lease_timeouts(sd_dhcp_client *client) {
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 /* don't set timers for infinite leases */
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
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 /* both T1 and T2 are given */
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 /* 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 } 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 /* 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 } 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 /* 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 /* 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 /* arm lifetime timeout */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = sd_event_add_time(client->event, &client->timeout_expire,
1ecf6a2b4960229ad1d06c591b4776ddf065e834Harald Hoyer r = sd_event_source_set_priority(client->timeout_expire,
33a5e20ffaa2cbb2853f14265566bac66a7f9026Harald Hoyer r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer format_timespan(time_string, FORMAT_TIMESPAN_MAX,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* don't arm earlier timeouts if this has already expired */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* arm T2 timeout */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = sd_event_source_set_priority(client->timeout_t2,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer format_timespan(time_string, FORMAT_TIMESPAN_MAX,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* don't arm earlier timeout if this has already expired */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* arm T1 timeout */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = sd_event_source_set_priority(client->timeout_t1,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer format_timespan(time_string, FORMAT_TIMESPAN_MAX,
int len) {
int r = 0, notify_event = 0;
case DHCP_STATE_SELECTING:
goto error;
goto error;
goto error;
} else if (r == -ENOMSG)
case DHCP_STATE_REBOOTING:
case DHCP_STATE_REQUESTING:
case DHCP_STATE_RENEWING:
case DHCP_STATE_REBINDING:
else if (r != DHCP_EVENT_IP_ACQUIRE)
notify_event = r;
goto error;
goto error;
if (notify_event) {
} else if (r == -EADDRNOTAVAIL) {
goto error;
goto error;
} else if (r == -ENOMSG)
case DHCP_STATE_BOUND:
goto error;
} else if (r == -ENOMSG)
case DHCP_STATE_INIT:
case DHCP_STATE_INIT_REBOOT:
case DHCP_STATE_STOPPED:
r = -EINVAL;
goto error;
assert(s);
if (buflen < 0)
return -EIO;
if (!message)
return -ENOMEM;
if (len < 0) {
expected_hlen = 0;
bool checksum = true;
assert(s);
if (buflen < 0)
return -EIO;
if (!packet)
return -ENOMEM;
if (len < 0) {
int priority) {
if (event)
if (!client)
return NULL;
if (client)
return client;
return NULL;
if (!client)
return -ENOMEM;
return -ENOMEM;