Merge pull request #14407 from ssahani/dhcp-decline

network: DHCP4 introduce send decline
This commit is contained in:
Yu Watanabe
2019-12-21 08:40:22 +09:00
committed by GitHub
11 changed files with 213 additions and 13 deletions
+11
View File
@@ -1633,6 +1633,17 @@
</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>
+43 -1
View 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);
+117 -12
View File
@@ -48,6 +48,15 @@ void dhcp4_release_old_lease(Link *link) {
link_dirty(link);
}
static void dhcp4_check_ready(Link *link) {
if (link->dhcp4_messages == 0) {
link->dhcp4_configured = true;
/* New address and routes are configured now. Let's release old lease. */
dhcp4_release_old_lease(link);
link_check_ready(link);
}
}
static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
@@ -89,10 +98,8 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
return 1;
}
link->dhcp4_configured = true;
/* New address and routes are configured now. Let's release old lease. */
dhcp4_release_old_lease(link);
link_check_ready(link);
if (!link->network->dhcp_send_decline)
dhcp4_check_ready(link);
}
return 1;
@@ -655,6 +662,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));
}
dhcp4_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;
@@ -684,13 +763,31 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
return 1;
}
if (link->dhcp4_messages == 0) {
link->dhcp4_configured = true;
/* The new address is configured, and no route is requested.
* Let's drop the old lease. */
dhcp4_release_old_lease(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");
} else
dhcp4_check_ready(link);
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(
+1
View 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)
+3
View 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);
+3
View 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;
+1
View 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);
@@ -98,6 +98,7 @@ SendRelease=
MaxAttempts=
IPServiceType=
SendOption=
SendDecline=
[DHCPv6]
UseNTP=
UseDNS=
@@ -0,0 +1,9 @@
[Match]
Name=veth99
[Network]
DHCP=ipv4
IPv6AcceptRA=false
[DHCPv4]
SendDecline=yes
@@ -0,0 +1,14 @@
[Match]
Name=veth-peer
[Network]
Address=192.168.5.1/24
Address=192.168.5.10/24
IPv6AcceptRA=false
DHCPServer=yes
[DHCPServer]
PoolOffset=10
PoolSize=1
DNS=192.168.5.1
NTP=192.168.5.1
@@ -2632,6 +2632,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'25-vrf.netdev',
'25-vrf.network',
'dhcp-client-anonymize.network',
'dhcp-client-decline.network',
'dhcp-client-gateway-onlink-implicit.network',
'dhcp-client-ipv4-dhcp-settings.network',
'dhcp-client-ipv4-only-ipv6-disabled.network',
@@ -2656,6 +2657,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network',
'dhcp-client-with-static-address.network',
'dhcp-client.network',
'dhcp-server-decline.network',
'dhcp-server-veth-peer.network',
'dhcp-v4-server-veth-peer.network',
'dhcp-client-use-domains.network',
@@ -3328,6 +3330,14 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
print(output)
self.assertRegex(output, 'example.com')
def test_dhcp_client_decline(self):
copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-decline.network', 'dhcp-client-decline.network')
start_networkd()
self.wait_online(['veth-peer:carrier'])
rc = call(*wait_online_cmd, '--timeout=10s', '--interface=veth99:routable', env=env)
self.assertTrue(rc == 1)
class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
links = ['veth99']