network: DHCP4 introduce send decline

This commit is contained in:
Susant Sahani 2019-12-20 14:13:18 +01:00 committed by Yu Watanabe
parent 1cb342447d
commit 0f3ff4eae2
8 changed files with 169 additions and 2 deletions

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>

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);

View 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(

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)

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);

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;

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);

View File

@ -98,6 +98,7 @@ SendRelease=
MaxAttempts=
IPServiceType=
SendOption=
SendDecline=
[DHCPv6]
UseNTP=
UseDNS=