sd-dhcp-client.c revision d23c45bfccb3ed6e2628e6d12b4ea12b8c920ab9
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt This file is part of systemd.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt Copyright (C) 2013 Intel Corporation. All rights reserved.
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 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 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/>.
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt unsigned int attempt;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic int client_receive_message_raw(sd_event_source *s, int fd,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic int client_receive_message_udp(sd_event_source *s, int fd,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktint sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktint sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert_return (client->state == DHCP_STATE_INIT, -EBUSY);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->req_opts[client->req_opts_size++] = option;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktint sd_dhcp_client_set_request_address(sd_dhcp_client *client,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktint sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flyktint sd_dhcp_client_set_mac(sd_dhcp_client *client,
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktint sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int client_notify(sd_dhcp_client *client, int event) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktstatic int client_stop(sd_dhcp_client *client, int error) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt sd_event_source_unref(client->receive_message);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->timeout_resend = sd_event_source_unref(client->timeout_resend);
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 client->lease = sd_dhcp_lease_unref(client->lease);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic int client_message_init(sd_dhcp_client *client, DHCPMessage *message,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt r = dhcp_message_init(message, BOOTREQUEST, client->xid, type, opt,
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 memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);
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 if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
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 Flyktstatic int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return dhcp_network_send_raw_socket(client->fd, &client->link,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int client_send_discover(sd_dhcp_client *client, uint16_t secs) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = client_message_init(client, &discover->dhcp, DHCP_DISCOVER,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = dhcp_client_send_raw(client, discover, len);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int client_send_request(sd_dhcp_client *client, uint16_t secs) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = client_message_init(client, &request->dhcp, DHCP_REQUEST, secs,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt err = dhcp_client_send_raw(client, request, len);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flyktstatic uint16_t client_update_secs(sd_dhcp_client *client, usec_t time_now)
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flyktstatic int client_timeout_resend(sd_event_source *s, uint64_t usec,
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt r = sd_event_get_now_monotonic(client->event, &time_now);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt time_left = (client->lease->t2 - client->lease->t1) / 2;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt next_timeout = time_now + time_left * USEC_PER_SEC;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt time_left = (client->lease->lifetime - client->lease->t2) / 2;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt next_timeout = time_now + time_left * USEC_PER_SEC;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->timeout_resend = sd_event_source_unref(client->timeout_resend);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = sd_event_source_set_priority(client->timeout_resend,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = client_send_discover(client, client->secs);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r >= 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = client_send_discover(client, client->secs);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* Errors were dealt with when stopping the client, don't spill
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt errors into the event loop handler */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int client_initialize_events(sd_dhcp_client *client,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = sd_event_add_io(client->event, &client->receive_message,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = sd_event_source_set_priority(client->receive_message,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt client->timeout_resend = sd_event_source_unref(client->timeout_resend);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = sd_event_source_set_priority(client->timeout_resend,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int client_timeout_expire(sd_event_source *s, uint64_t usec,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt sd_event_source_unref(client->receive_message);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = dhcp_network_bind_raw_socket(client->index, &client->link);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return client_initialize_events(client, client_receive_message_raw);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int client_timeout_t1(sd_event_source *s, uint64_t usec,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = dhcp_network_bind_udp_socket(client->index,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return client_initialize_events(client, client_receive_message_udp);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flyktstatic int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt lease->subnet_mask == INADDR_ANY || lease->lifetime == 0)
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt if (client->lease->address != lease->address ||
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt client->lease->subnet_mask != lease->subnet_mask ||
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt client->lease = sd_dhcp_lease_unref(client->lease);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic uint64_t client_compute_timeout(uint64_t request_sent,
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt return request_sent + (lifetime - 3) * USEC_PER_SEC +
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktstatic int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) {
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 client->lease->t1 = client->lease->lifetime / 2;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt next_timeout = client_compute_timeout(client->request_sent,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = sd_event_source_set_priority(client->timeout_t1,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt client->lease->t2 = client->lease->lifetime * 7 / 8;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (client->lease->lifetime < client->lease->t2)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt next_timeout = client_compute_timeout(client->request_sent,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = sd_event_source_set_priority(client->timeout_t2,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt next_timeout = client_compute_timeout(client->request_sent,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = sd_event_source_set_priority(client->timeout_expire,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktstatic int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen int r = 0, notify_event = 0;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_dhcp_client(client, "message too small (%d bytes): "
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen log_dhcp_client(client, "received xid (%u) does not match "
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen "expected (%u): ignoring",
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen if (memcmp(&message->chaddr[0], &client->mac_addr.ether_addr_octet,
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen log_dhcp_client(client, "received chaddr does not match "
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt "expected: ignoring");
case DHCP_STATE_SELECTING:
client);
goto error;
goto error;
case DHCP_STATE_REQUESTING:
case DHCP_STATE_RENEWING:
case DHCP_STATE_REBINDING:
if (r == DHCP_EVENT_NO_LEASE)
goto error;
else if (r != DHCP_EVENT_IP_ACQUIRE)
notify_event = r;
goto error;
if (notify_event)
case DHCP_STATE_INIT:
case DHCP_STATE_INIT_REBOOT:
case DHCP_STATE_REBOOTING:
case DHCP_STATE_BOUND:
if (r < 0 || r == DHCP_EVENT_NO_LEASE)
assert(s);
if (r < 0 || buflen <= 0)
if (!message)
return -ENOMEM;
if (len < 0)
time_now);
bool checksum = true;
assert(s);
if (r < 0 || buflen <= 0)
if (!packet)
return -ENOMEM;
if (len < 0) {
int priority) {
if (event)
if (!client)
return NULL;
if (!client)
if (!client)
return -ENOMEM;
return -ENOMEM;