From 0f3ff4eae2f3b32616d2c6f85cb99f7faae5cfa6 Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Fri, 20 Dec 2019 14:13:18 +0100 Subject: [PATCH] network: DHCP4 introduce send decline --- man/systemd.network.xml | 11 ++ src/libsystemd-network/sd-dhcp-client.c | 44 ++++++- src/network/networkd-dhcp4.c | 107 +++++++++++++++++- src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 3 + src/network/networkd-network.h | 3 + src/systemd/sd-dhcp-client.h | 1 + .../fuzz-network-parser/directives.network | 1 + 8 files changed, 169 insertions(+), 2 deletions(-) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 24471adaa9..fc5658153a 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1633,6 +1633,17 @@ + + SendDecline= + + A boolen. When true, DHCPv4 clients receives IP address from DHCP server. + After new IP is received, DHCPv4 performs IPv4 Duplicate Address Detection. If duplicate use of IP is detected + the DHCPv4 client rejects the IP by sending a DHCPDECLINE packet DHCP clients try to obtain an IP address again. + See RFC 5224. + Defaults to unset. + + + BlackList= diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 901985fc1b..4122d08d96 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -643,7 +643,7 @@ static int client_message_init( assert(ret); assert(_optlen); assert(_optoffset); - assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST, DHCP_RELEASE)); + assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST, DHCP_RELEASE, DHCP_DECLINE)); optlen = DHCP_MIN_OPTIONS_SIZE; size = sizeof(DHCPPacket) + optlen; @@ -1966,6 +1966,48 @@ int sd_dhcp_client_send_release(sd_dhcp_client *client) { return 0; } +int sd_dhcp_client_send_decline(sd_dhcp_client *client) { + assert_return(client, -EINVAL); + assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE); + assert_return(client->lease, -EUNATCH); + + _cleanup_free_ DHCPPacket *release = NULL; + size_t optoffset, optlen; + int r; + + r = client_message_init(client, &release, DHCP_DECLINE, &optlen, &optoffset); + if (r < 0) + return r; + + release->dhcp.ciaddr = client->lease->address; + memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len); + + r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + r = dhcp_network_send_udp_socket(client->fd, + client->lease->server_address, + DHCP_PORT_SERVER, + &release->dhcp, + sizeof(DHCPMessage) + optoffset); + if (r < 0) + return r; + + log_dhcp_client(client, "DECLINE"); + + client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); + + if (client->state != DHCP_STATE_STOPPED) { + r = sd_dhcp_client_start(client); + if (r < 0) + return r; + } + + return 0; +} + int sd_dhcp_client_stop(sd_dhcp_client *client) { DHCP_CLIENT_DONT_DESTROY(client); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 0694ffc939..c000159ec1 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -655,6 +655,78 @@ static int dhcp_lease_lost(Link *link) { return 0; } +static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { + _cleanup_free_ char *pretty = NULL; + union in_addr_union address = {}; + Link *link; + int r; + + assert(acd); + assert(userdata); + + link = userdata; + + switch (event) { + case SD_IPV4ACD_EVENT_STOP: + log_link_debug(link, "Stopping ACD client for DHCP4..."); + return; + + case SD_IPV4ACD_EVENT_BIND: + if (DEBUG_LOGGING) { + (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address.in); + (void) in_addr_to_string(AF_INET, &address, &pretty); + log_link_debug(link, "Successfully claimed DHCP4 address %s", strna(pretty)); + } + link_check_ready(link); + break; + + case SD_IPV4ACD_EVENT_CONFLICT: + (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address.in); + (void) in_addr_to_string(AF_INET, &address, &pretty); + log_link_warning(link, "DAD conflict. Dropping DHCP4 address %s", strna(pretty)); + + (void) sd_dhcp_client_send_decline(link->dhcp_client); + + if (link->dhcp_lease) { + r = dhcp_lease_lost(link); + if (r < 0) + link_enter_failed(link); + } + break; + + default: + assert_not_reached("Invalid IPv4ACD event."); + } + + sd_ipv4acd_stop(acd); + + return; +} + +static int configure_dhcpv4_duplicate_address_detection(Link *link) { + int r; + + assert(link); + + r = sd_ipv4acd_new(&link->network->dhcp_acd); + if (r < 0) + return r; + + r = sd_ipv4acd_attach_event(link->network->dhcp_acd, NULL, 0); + if (r < 0) + return r; + + r = sd_ipv4acd_set_ifindex(link->network->dhcp_acd, link->ifindex); + if (r < 0) + return r; + + r = sd_ipv4acd_set_mac(link->network->dhcp_acd, &link->mac); + if (r < 0) + return r; + + return 0; +} + static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -692,6 +764,31 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * link_check_ready(link); } + if (link->network->dhcp_send_decline) { + union in_addr_union addr; + + (void) sd_dhcp_lease_get_address(link->dhcp_lease, &addr.in); + + r = sd_ipv4acd_set_address(link->network->dhcp_acd, &addr.in); + if (r < 0) + return r; + + r = sd_ipv4acd_set_callback(link->network->dhcp_acd, dhcp_address_on_acd, link); + if (r < 0) + return r; + + if (DEBUG_LOGGING) { + _cleanup_free_ char *pretty = NULL; + + (void) in_addr_to_string(AF_INET, &addr, &pretty); + log_debug("Starting IPv4ACD client. Probing DHCPv4 address %s", strna(pretty)); + } + + r = sd_ipv4acd_start(link->network->dhcp_acd, true); + if (r < 0) + log_link_warning_errno(link, r, "Failed to start IPv4ACD client, ignoring: %m"); + } + return 1; } @@ -1033,6 +1130,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { return r; if (r != 0) return -ENOMSG; + break; default: if (event < 0) @@ -1334,7 +1432,14 @@ int dhcp4_configure(Link *link) { if (r < 0) return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set ip service type: %m"); } - return dhcp4_set_client_identifier(link); + + if (link->network->dhcp_send_decline) { + r = configure_dhcpv4_duplicate_address_detection(link); + if (r < 0) + return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to configure service type: %m"); + } + + return dhcp4_set_client_identifier(link); } int config_parse_dhcp_max_attempts( diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 29a05b2f30..d44b567069 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -174,6 +174,7 @@ DHCPv4.UseTimezone, config_parse_bool, DHCPv4.IAID, config_parse_iaid, 0, 0 DHCPv4.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port) DHCPv4.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release) +DHCPv4.SendDecline, config_parse_bool, 0, offsetof(Network, dhcp_send_decline) DHCPv4.BlackList, config_parse_dhcp_black_listed_ip_address, 0, 0 DHCPv4.IPServiceType, config_parse_ip_service_type, 0, offsetof(Network, ip_service_type) DHCPv4.SendOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_client_send_options) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index e2e2999354..40394eb766 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -619,6 +619,9 @@ static Network *network_free(Network *network) { set_free(network->dhcp_request_options); free(network->mac); + if (network->dhcp_acd) + sd_ipv4acd_unref(network->dhcp_acd); + strv_free(network->ntp); free(network->dns); strv_free(network->sip); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index bd3c96d972..4f229103a7 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -5,6 +5,7 @@ #include "sd-bus.h" #include "sd-device.h" +#include "sd-ipv4acd.h" #include "bridge.h" #include "condition.h" @@ -111,7 +112,9 @@ struct Network { bool dhcp_use_hostname; bool dhcp_route_table_set; bool dhcp_send_release; + bool dhcp_send_decline; DHCPUseDomains dhcp_use_domains; + sd_ipv4acd *dhcp_acd; Set *dhcp_black_listed_ip; Set *dhcp_request_options; OrderedHashmap *dhcp_client_send_options; diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index f97e35b654..0002ea0e59 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -184,6 +184,7 @@ int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v); int sd_dhcp_client_stop(sd_dhcp_client *client); int sd_dhcp_client_start(sd_dhcp_client *client); int sd_dhcp_client_send_release(sd_dhcp_client *client); +int sd_dhcp_client_send_decline(sd_dhcp_client *client); int sd_dhcp_client_send_renew(sd_dhcp_client *client); sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client); diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 0c76bd5f91..b7fde0e018 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -98,6 +98,7 @@ SendRelease= MaxAttempts= IPServiceType= SendOption= +SendDecline= [DHCPv6] UseNTP= UseDNS=