diff --git a/man/systemd.network.xml b/man/systemd.network.xml index ca82f33bb4..2f94dc518d 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1470,6 +1470,14 @@ + + SendRelease= + + When true, the DHCPv4 client sends a DHCP release packet when it stops. + Defaults to false. + + + RapidCommit= diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 84ce8e0da8..6e077c0860 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -606,7 +606,7 @@ static int client_message_init( assert(ret); assert(_optlen); assert(_optoffset); - assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST)); + assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST, DHCP_RELEASE)); optlen = DHCP_MIN_OPTIONS_SIZE; size = sizeof(DHCPPacket) + optlen; @@ -697,7 +697,7 @@ static int client_message_init( MAY contain the Parameter Request List option. */ /* NOTE: in case that there would be an option to do not send * any PRL at all, the size should be checked before sending */ - if (client->req_opts_size > 0) { + if (client->req_opts_size > 0 && type != DHCP_RELEASE) { r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_PARAMETER_REQUEST_LIST, client->req_opts_size, client->req_opts); @@ -729,7 +729,7 @@ static int client_message_init( */ /* RFC7844 section 3: SHOULD NOT contain any other option. */ - if (!client->anonymize) { + if (!client->anonymize && type != DHCP_RELEASE) { max_size = htobe16(size); r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0, SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, @@ -861,6 +861,41 @@ static int client_send_discover(sd_dhcp_client *client) { return 0; } +static int client_send_release(sd_dhcp_client *client) { + _cleanup_free_ DHCPPacket *release = NULL; + size_t optoffset, optlen; + int r; + + assert(client); + assert(!IN_SET(client->state, DHCP_STATE_STOPPED)); + + r = client_message_init(client, &release, DHCP_RELEASE, + &optlen, &optoffset); + if (r < 0) + return r; + + /* Fill up release IP and MAC */ + 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, "RELEASE"); + + return 0; +} + static int client_send_request(sd_dhcp_client *client) { _cleanup_free_ DHCPPacket *request = NULL; size_t optoffset, optlen; @@ -1858,6 +1893,14 @@ int sd_dhcp_client_start(sd_dhcp_client *client) { return r; } +int sd_dhcp_client_send_release(sd_dhcp_client *client) { + assert_return(client, -EINVAL); + + client_send_release(client); + + 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 85e088e204..81c694822c 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -564,6 +564,9 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m"); } + if (link->network->dhcp_send_release) + (void) sd_dhcp_client_send_release(client); + _fallthrough_; case SD_DHCP_CLIENT_EVENT_EXPIRED: case SD_DHCP_CLIENT_EVENT_IP_CHANGE: diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index a57d9395f6..cbb1dd8180 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -149,6 +149,7 @@ DHCP.RouteTable, config_parse_section_route_table, DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) DHCP.IAID, config_parse_iaid, 0, 0 DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port) +DHCP.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release) DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit) DHCP.BlackList, config_parse_dhcp_black_listed_ip_address, 0, 0 DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information) diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 9217e838d2..d00062fe59 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -128,6 +128,7 @@ struct Network { bool rapid_commit; bool dhcp_use_hostname; bool dhcp_route_table_set; + bool dhcp_send_release; DHCPUseDomains dhcp_use_domains; Set *dhcp_black_listed_ip; diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 5dbfe8e4a1..ab62368e9c 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -176,6 +176,7 @@ int sd_dhcp_client_get_lease( 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); sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client); sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client); diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 5d48f0825b..3742d02d3d 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -64,6 +64,7 @@ ListenPort= UseTimezone= RouteTable= BlackList= +SendRelease= [Route] Destination= Protocol=