]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: DHCP4 introduce send decline
authorSusant Sahani <ssahani@vmware.com>
Fri, 20 Dec 2019 13:13:18 +0000 (14:13 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 20 Dec 2019 15:26:44 +0000 (00:26 +0900)
man/systemd.network.xml
src/libsystemd-network/sd-dhcp-client.c
src/network/networkd-dhcp4.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/systemd/sd-dhcp-client.h
test/fuzz/fuzz-network-parser/directives.network

index 24471adaa9b5dac21f322360b90d52d3f3d6e66c..fc5658153a8ddfb7c89542767bce00aeed01acfe 100644 (file)
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><varname>SendDecline=</varname></term>
+          <listitem>
+            <para>A boolen. When <literal>true</literal>, 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 <ulink url="https://tools.ietf.org/html/rfc5227">RFC 5224</ulink>.
+            Defaults to <literal>unset</literal>.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
         <term><varname>BlackList=</varname></term>
         <listitem>
index 901985fc1bb756bbdd22eba065179b4ef9f4fcf1..4122d08d9658c821cc5d77c2b828d1fe1f27e1f0 100644 (file)
@@ -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);
 
index 0694ffc939f12c0f8c85b7f8c13156812d2ccbed..c000159ec1ffa3ee62441b414b2d0996fdc295d7 100644 (file)
@@ -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(
index 29a05b2f3019f37c6233905de2c68dc4379ea10c..d44b5670696a544a02bc2ecebf295b00a9d34259 100644 (file)
@@ -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)
index e2e29993546135a1cc3880aa2f7a08d12e995a74..40394eb76652e00b4824fca834054f3d5eb4846e 100644 (file)
@@ -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);
index bd3c96d972e87f03c54d46f611b79b35ab69e25c..4f229103a7b038762af630b8b6378432e031d338 100644 (file)
@@ -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;
index f97e35b6549a8d9f49bd95f4d40f33ca9d481146..0002ea0e5952d658eae3da8e259f234fcdf61b16 100644 (file)
@@ -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);
index 0c76bd5f910ed3d6ac83e784dc3d681cbf5a6e85..b7fde0e01839d8fa5bd7077420d8b142d6405156 100644 (file)
@@ -98,6 +98,7 @@ SendRelease=
 MaxAttempts=
 IPServiceType=
 SendOption=
+SendDecline=
 [DHCPv6]
 UseNTP=
 UseDNS=