sd-dhcp-client: several fixlets for sending RELEASE or DECLINE (#41002)
Fixes #39299.
This commit is contained in:
@@ -876,7 +876,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 (!set_isempty(client->req_opts) && type != DHCP_RELEASE) {
|
||||
if (!set_isempty(client->req_opts) && IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST)) {
|
||||
_cleanup_free_ uint8_t *opts = NULL;
|
||||
size_t n_opts, i = 0;
|
||||
void *val;
|
||||
@@ -2356,70 +2356,93 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static int client_send_release(sd_dhcp_client *client) {
|
||||
_cleanup_free_ DHCPPacket *release = NULL;
|
||||
size_t optoffset, optlen;
|
||||
static int client_send_release_or_decline(sd_dhcp_client *client, uint8_t type) {
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(IN_SET(type, DHCP_RELEASE, DHCP_DECLINE));
|
||||
|
||||
if (!client->send_release)
|
||||
return 0; /* disabled */
|
||||
if (!sd_dhcp_client_is_running(client) || !client->lease || client->bootp)
|
||||
return 0; /* there is nothing to release or decline */
|
||||
|
||||
if (!client->lease || client->bootp)
|
||||
return 0; /* there is nothing to be released */
|
||||
const char *name = type == DHCP_RELEASE ? "RELEASE" : "DECLINE";
|
||||
|
||||
r = client_message_init(client, DHCP_RELEASE, &release, &optlen, &optoffset);
|
||||
_cleanup_free_ DHCPPacket *packet = NULL;
|
||||
size_t optoffset, optlen;
|
||||
r = client_message_init(client, type, &packet, &optlen, &optoffset);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_dhcp_client_errno(client, r, "Failed to initialize DHCP %s message: %m", name);
|
||||
|
||||
/* Fill up release IP and MAC */
|
||||
release->dhcp.ciaddr = client->lease->address;
|
||||
memcpy(&release->dhcp.chaddr, client->hw_addr.bytes, client->hw_addr.length);
|
||||
/* See RFC 2131, Table 5 */
|
||||
switch (type) {
|
||||
case DHCP_RELEASE:
|
||||
/* On release, the acquired address must be set in ciaddr. */
|
||||
packet->dhcp.ciaddr = client->lease->address;
|
||||
break;
|
||||
|
||||
r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
|
||||
SD_DHCP_OPTION_END, 0, NULL);
|
||||
case DHCP_DECLINE:
|
||||
/* On decline, the acquired address must be set in Requested IP Address option. */
|
||||
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, /* overload= */ 0,
|
||||
SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
|
||||
4, &client->lease->address);
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(
|
||||
client, r,
|
||||
"Failed to append Requested IP Address option to DHCP %s message: %m",
|
||||
name);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
/* In both cases, the server identifier must be set. */
|
||||
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, /* overload= */ 0,
|
||||
SD_DHCP_OPTION_SERVER_IDENTIFIER,
|
||||
4, &client->lease->server_address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_dhcp_client_errno(
|
||||
client, r,
|
||||
"Failed to append Server Identifier option to DHCP %s message: %m",
|
||||
name);
|
||||
|
||||
r = dhcp_network_send_udp_socket(client->fd,
|
||||
client->lease->server_address,
|
||||
client->server_port,
|
||||
&release->dhcp,
|
||||
sizeof(DHCPMessage) + optoffset);
|
||||
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, /* overload= */ 0,
|
||||
SD_DHCP_OPTION_END, /* optlen= */ 0, /* optval= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_dhcp_client_errno(
|
||||
client, r,
|
||||
"Failed to finalize DHCP %s message: %m",
|
||||
name);
|
||||
|
||||
log_dhcp_client(client, "RELEASE");
|
||||
return 0;
|
||||
switch (type) {
|
||||
case DHCP_RELEASE:
|
||||
r = dhcp_network_send_udp_socket(
|
||||
client->fd,
|
||||
client->lease->server_address,
|
||||
client->server_port,
|
||||
&packet->dhcp,
|
||||
sizeof(DHCPMessage) + optoffset);
|
||||
break;
|
||||
case DHCP_DECLINE:
|
||||
r = dhcp_client_send_raw(client, packet, sizeof(DHCPPacket) + optoffset);
|
||||
break;
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(
|
||||
client, r,
|
||||
"Failed to send DHCP %s message: %m",
|
||||
name);
|
||||
|
||||
log_dhcp_client(client, "%s", name);
|
||||
return 1; /* sent */
|
||||
}
|
||||
|
||||
int sd_dhcp_client_send_decline(sd_dhcp_client *client) {
|
||||
_cleanup_free_ DHCPPacket *release = NULL;
|
||||
size_t optoffset, optlen;
|
||||
int r;
|
||||
|
||||
if (!sd_dhcp_client_is_running(client) || !client->lease)
|
||||
return 0; /* do nothing */
|
||||
|
||||
r = client_message_init(client, DHCP_DECLINE, &release, &optlen, &optoffset);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
release->dhcp.ciaddr = client->lease->address;
|
||||
memcpy(&release->dhcp.chaddr, client->hw_addr.bytes, client->hw_addr.length);
|
||||
|
||||
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,
|
||||
client->server_port,
|
||||
&release->dhcp,
|
||||
sizeof(DHCPMessage) + optoffset);
|
||||
if (r < 0)
|
||||
r = client_send_release_or_decline(client, DHCP_DECLINE);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
log_dhcp_client(client, "DECLINE");
|
||||
@@ -2434,20 +2457,15 @@ int sd_dhcp_client_send_decline(sd_dhcp_client *client) {
|
||||
}
|
||||
|
||||
int sd_dhcp_client_stop(sd_dhcp_client *client) {
|
||||
int r;
|
||||
|
||||
if (!client)
|
||||
return 0;
|
||||
|
||||
DHCP_CLIENT_DONT_DESTROY(client);
|
||||
|
||||
r = client_send_release(client);
|
||||
if (r < 0)
|
||||
log_dhcp_client_errno(client, r,
|
||||
"Failed to send DHCP release message, ignoring: %m");
|
||||
if (client->send_release)
|
||||
(void) client_send_release_or_decline(client, DHCP_RELEASE);
|
||||
|
||||
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Match]
|
||||
Name=client
|
||||
|
||||
[Network]
|
||||
DHCP=ipv4
|
||||
IPv6AcceptRA=no
|
||||
@@ -869,13 +869,18 @@ def dnr_v6_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None):
|
||||
|
||||
return data
|
||||
|
||||
def start_dnsmasq(*additional_options, interface='veth-peer', ra_mode=None, ipv4_range='192.168.5.10,192.168.5.200', ipv4_router='192.168.5.1', ipv6_range='2600::10,2600::20'):
|
||||
def start_dnsmasq(*additional_options, namespace=None, interface='veth-peer', ra_mode=None, ipv4_range='192.168.5.10,192.168.5.200', ipv4_router='192.168.5.1', ipv6_range='2600::10,2600::20'):
|
||||
if ra_mode:
|
||||
ra_mode = f',{ra_mode}'
|
||||
else:
|
||||
ra_mode = ''
|
||||
|
||||
command = (
|
||||
if namespace:
|
||||
ns_command = ('ip', 'netns', 'exec', namespace)
|
||||
else:
|
||||
ns_command = ()
|
||||
|
||||
command = ns_command + (
|
||||
'dnsmasq',
|
||||
f'--log-facility={dnsmasq_log_file}',
|
||||
'--log-queries=extra',
|
||||
@@ -1104,6 +1109,8 @@ def tear_down_common():
|
||||
|
||||
# 3. remove network namespace
|
||||
call_quiet('ip netns del ns99')
|
||||
call_quiet('ip netns del ns-bridge')
|
||||
call_quiet('ip netns del ns-server')
|
||||
|
||||
# 4. remove links
|
||||
flush_l2tp_tunnels()
|
||||
@@ -7988,6 +7995,63 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
||||
|
||||
self.teardown_nftset('addr4', 'network4', 'ifindex')
|
||||
|
||||
def test_dhcp_client_send_release(self):
|
||||
check_output('ip netns add ns-bridge')
|
||||
check_output('ip netns exec ns-bridge ip link add bridge99 type bridge')
|
||||
check_output('ip netns exec ns-bridge ip link set bridge99 address 12:34:56:78:90:ab')
|
||||
check_output('ip netns exec ns-bridge ip link set bridge99 up')
|
||||
|
||||
check_output('ip link add client type veth peer clientp')
|
||||
check_output('ip link set clientp netns ns-bridge')
|
||||
check_output('ip netns exec ns-bridge ip link set clientp master bridge99')
|
||||
check_output('ip netns exec ns-bridge ip link set clientp up')
|
||||
|
||||
check_output('ip link add server type veth peer serverp')
|
||||
check_output('ip link set serverp netns ns-bridge')
|
||||
check_output('ip netns exec ns-bridge ip link set serverp master bridge99')
|
||||
check_output('ip netns exec ns-bridge ip link set serverp up')
|
||||
|
||||
check_output('ip netns add ns-server')
|
||||
check_output('ip link set server netns ns-server')
|
||||
check_output('ip netns exec ns-server ip link set server up')
|
||||
check_output('ip netns exec ns-server ip address add 192.0.2.1/24 dev server')
|
||||
|
||||
start_dnsmasq(
|
||||
namespace='ns-server',
|
||||
interface='server',
|
||||
ipv4_range='192.0.2.100,192.0.2.109',
|
||||
ipv4_router='192.0.2.1',
|
||||
)
|
||||
|
||||
copy_network_unit('25-dhcp-client-simple.network')
|
||||
start_networkd()
|
||||
self.wait_online('client:routable')
|
||||
|
||||
print('## ip -4 address show dev client scope global')
|
||||
output = check_output('ip -4 address show dev client scope global')
|
||||
print(output)
|
||||
self.assertRegex(output, r'192.0.2.10[0-9]/24')
|
||||
|
||||
print('## ip -4 route show dev client')
|
||||
output = check_output('ip -4 route show dev client')
|
||||
print(output)
|
||||
self.assertRegex(output, r'default via 192.0.2.1 proto dhcp src 192.0.2.10[0-9]')
|
||||
self.assertRegex(output, r'192.0.2.0/24 proto kernel scope link src 192.0.2.10[0-9]')
|
||||
self.assertRegex(output, r'192.0.2.1 proto dhcp scope link src 192.0.2.10[0-9]')
|
||||
|
||||
networkctl('down', 'client')
|
||||
|
||||
print('## dnsmasq log')
|
||||
for _ in range(20):
|
||||
time.sleep(0.5)
|
||||
output = read_dnsmasq_log_file()
|
||||
if 'DHCPRELEASE' in output:
|
||||
print(output)
|
||||
break
|
||||
else:
|
||||
print(output)
|
||||
self.fail('Timed out waiting for DHCPRELEASE in dnsmasq log')
|
||||
|
||||
def test_dhcp_client_ipv4_dbus_status(self):
|
||||
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
|
||||
start_networkd()
|
||||
|
||||
Reference in New Issue
Block a user