From: pelaufer Date: Sat, 2 Sep 2023 18:20:44 +0000 (-0600) Subject: Add dhcp client prefix lease information to networkd json output X-Git-Tag: v255-rc1~590^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8fb6320e4b2f2839a18fe0b0e4da5510ffde4f99;p=thirdparty%2Fsystemd.git Add dhcp client prefix lease information to networkd json output --- diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 21b65920374..dd222edd820 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -701,6 +701,18 @@ int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { return 0; } +int sd_dhcp_client_get_lease_timestamp(sd_dhcp_client *client, uint64_t *timestamp) { + assert_return(client, -EINVAL); + + if (!IN_SET(client->state, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING)) + return -ENODATA; + + if(timestamp) + *timestamp = client->request_sent; + + return 0; +} + int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) { assert_return(client, -EINVAL); assert_return(!sd_dhcp_client_is_running(client), -EBUSY); diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c index a9e33ac627e..86488c98169 100644 --- a/src/network/networkd-json.c +++ b/src/network/networkd-json.c @@ -9,6 +9,7 @@ #include "netif-util.h" #include "networkd-address.h" #include "networkd-dhcp-common.h" +#include "networkd-dhcp-prefix-delegation.h" #include "networkd-json.h" #include "networkd-link.h" #include "networkd-manager.h" @@ -1016,6 +1017,7 @@ static int dhcp_server_append_json(Link *link, JsonVariant **v) { static int dhcp6_client_lease_append_json(Link *link, JsonVariant **v) { _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; + uint64_t lease_timestamp_usec; int r; assert(link); @@ -1024,15 +1026,59 @@ static int dhcp6_client_lease_append_json(Link *link, JsonVariant **v) { if (!link->dhcp6_lease) return 0; + r = sd_dhcp6_lease_get_timestamp(link->dhcp6_lease, CLOCK_BOOTTIME, &lease_timestamp_usec); + if (r < 0) + return 0; + r = json_build(&w, JSON_BUILD_OBJECT( - JSON_BUILD_PAIR_FINITE_USEC("T1", link->dhcp6_lease->lifetime_t1), - JSON_BUILD_PAIR_FINITE_USEC("T2", link->dhcp6_lease->lifetime_t2))); + JSON_BUILD_PAIR_FINITE_USEC("Timeout1USec", + link->dhcp6_lease->lifetime_t1 + lease_timestamp_usec), + JSON_BUILD_PAIR_FINITE_USEC("Timeout2USec", + link->dhcp6_lease->lifetime_t2 + lease_timestamp_usec), + JSON_BUILD_PAIR_FINITE_USEC("LeaseTimestampUSec", + lease_timestamp_usec))); if (r < 0) return r; return json_append_one(v, "Lease", w); } +static int dhcp6_client_pd_append_json(Link *link, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; + struct in6_addr prefix; + uint32_t lifetime_preferred, lifetime_valid; + uint8_t prefix_len; + uint64_t lease_timestamp_usec; + int r; + + assert(link); + assert(link->network); + assert(v); + + if (!link->network->dhcp6_use_pd_prefix || !link->dhcp6_lease || !dhcp6_lease_has_pd_prefix(link->dhcp6_lease)) + return 0; + + sd_dhcp6_lease_reset_pd_prefix_iter(link->dhcp6_lease); + + r = sd_dhcp6_lease_get_timestamp(link->dhcp6_lease, CLOCK_BOOTTIME, &lease_timestamp_usec); + if (r < 0) + return 0; + + while (sd_dhcp6_lease_get_pd(link->dhcp6_lease, &prefix, &prefix_len, &lifetime_preferred, &lifetime_valid) >= 0) { + r = json_variant_append_arrayb(&array, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_IN6_ADDR("Prefix", &prefix), + JSON_BUILD_PAIR_UNSIGNED("PrefixLength", prefix_len), + JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUSec", + sec_to_usec(lifetime_preferred, lease_timestamp_usec)), + JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUSec", + sec_to_usec(lifetime_valid, lease_timestamp_usec)))); + if (r < 0) + return r; + } + + return json_append_one(v, "Prefixes", array); +} + static int dhcp6_client_append_json(Link *link, JsonVariant **v) { _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; int r; @@ -1047,9 +1093,106 @@ static int dhcp6_client_append_json(Link *link, JsonVariant **v) { if (r < 0) return r; + r = dhcp6_client_pd_append_json(link, &w); + if (r < 0) + return r; + return json_append_one(v, "DHCPv6Client", w); } +static int dhcp_client_lease_append_json(Link *link, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; + uint32_t t1, t2; + uint64_t lease_timestamp_usec; + int r; + + assert(link); + assert(v); + + if (!link->dhcp_client || !link->dhcp_lease) + return 0; + + r = sd_dhcp_lease_get_t1(link->dhcp_lease, &t1); + if (r < 0) + return 0; + + r = sd_dhcp_lease_get_t2(link->dhcp_lease, &t2); + if (r < 0) + return 0; + + r = sd_dhcp_client_get_lease_timestamp(link->dhcp_client, &lease_timestamp_usec); + if (r < 0) + return 0; + + r = json_build(&w, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_FINITE_USEC("Timeout1USec", + sec_to_usec(t1, lease_timestamp_usec)), + JSON_BUILD_PAIR_FINITE_USEC("Timeout2USec", + sec_to_usec(t2, lease_timestamp_usec)), + JSON_BUILD_PAIR_FINITE_USEC("LeaseTimestampUSec", lease_timestamp_usec))); + if (r < 0) + return r; + + return json_append_one(v, "Lease", w); +} + +static int dhcp_client_pd_append_json(Link *link, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *addresses = NULL, *array = NULL; + uint8_t ipv4masklen, sixrd_prefixlen; + struct in6_addr sixrd_prefix; + const struct in_addr *br_addresses; + size_t n_br_addresses = 0; + int r; + + assert(link); + assert(link->network); + assert(v); + + if (!link->network->dhcp_use_6rd || !link->dhcp_lease || !dhcp4_lease_has_pd_prefix(link->dhcp_lease)) + return 0; + + r = sd_dhcp_lease_get_6rd(link->dhcp_lease, &ipv4masklen, &sixrd_prefixlen, &sixrd_prefix, &br_addresses, &n_br_addresses); + if (r < 0) + return r; + + FOREACH_ARRAY(br_address, br_addresses, n_br_addresses) { + r = json_variant_append_arrayb(&addresses, JSON_BUILD_IN4_ADDR(br_address)); + if (r < 0) + return r; + } + + r = json_build(&array, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_IN6_ADDR("Prefix", &sixrd_prefix), + JSON_BUILD_PAIR_UNSIGNED("PrefixLength", sixrd_prefixlen), + JSON_BUILD_PAIR_UNSIGNED("IPv4MaskLength", ipv4masklen), + JSON_BUILD_PAIR_VARIANT_NON_NULL("BorderRouters", addresses))); + if (r < 0) + return r; + + return json_append_one(v, "6rdPrefix", array); +} + +static int dhcp_client_append_json(Link *link, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; + int r; + + assert(link); + assert(v); + + if (!link->dhcp_client) + return 0; + + r = dhcp_client_lease_append_json(link, &w); + if (r < 0) + return r; + + r = dhcp_client_pd_append_json(link, &w); + if (r < 0) + return r; + + return json_append_one(v, "DHCPv4Client", w); +} + int link_build_json(Link *link, JsonVariant **ret) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_free_ char *type = NULL, *flags = NULL; @@ -1165,6 +1308,10 @@ int link_build_json(Link *link, JsonVariant **ret) { if (r < 0) return r; + r = dhcp_client_append_json(link, &v); + if (r < 0) + return r; + r = dhcp6_client_append_json(link, &v); if (r < 0) return r; diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index ddd445c9f05..45ab8765d25 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -329,6 +329,7 @@ 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); +int sd_dhcp_client_get_lease_timestamp(sd_dhcp_client *client, uint64_t *timestamp); 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/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index bc60e938504..03287f21bd1 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -5672,6 +5672,38 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): tear_down_common() def test_dhcp6pd(self): + def get_dbus_dhcp6_prefix(IF): + # busctl call org.freedesktop.network1 /org/freedesktop/network1 org.freedesktop.network1.Manager GetLinkByName s IF + out = subprocess.check_output(['busctl', 'call', 'org.freedesktop.network1', + '/org/freedesktop/network1', 'org.freedesktop.network1.Manager', + 'GetLinkByName', 's', IF]) + + assert out.startswith(b'io ') + out = out.strip() + assert out.endswith(b'"') + out = out.decode() + linkPath = out[:-1].split('"')[1] + + print(f"Found {IF} link path: {linkPath}") + + out = subprocess.check_output(['busctl', 'call', 'org.freedesktop.network1', + linkPath, 'org.freedesktop.network1.Link', 'Describe']) + assert out.startswith(b's "') + out = out.strip() + assert out.endswith(b'"') + json_raw = out[2:].decode() + check_json(json_raw) + description = json.loads(json_raw) # Convert from escaped sequences to json + check_json(description) + description = json.loads(description) # Now parse the json + + self.assertIn('DHCPv6Client', description.keys()) + self.assertIn('Prefixes', description['DHCPv6Client']) + + prefixInfo = description['DHCPv6Client']['Prefixes'] + + return prefixInfo + copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network', '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network', '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network', @@ -5690,6 +5722,22 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): self.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded', 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable']) + # Check DBus assigned prefix information to veth99 + prefixInfo = get_dbus_dhcp6_prefix('veth99') + + self.assertEqual(len(prefixInfo), 1) + prefixInfo = prefixInfo[0] + + self.assertIn('Prefix', prefixInfo.keys()) + self.assertIn('PrefixLength', prefixInfo.keys()) + self.assertIn('PreferredLifetimeUSec', prefixInfo.keys()) + self.assertIn('ValidLifetimeUSec', prefixInfo.keys()) + + self.assertEqual(prefixInfo['Prefix'][0:6], [63, 254, 5, 1, 255, 255]) + self.assertEqual(prefixInfo['PrefixLength'], 56) + self.assertGreater(prefixInfo['PreferredLifetimeUSec'], 0) + self.assertGreater(prefixInfo['ValidLifetimeUSec'], 0) + print('### ip -6 address show dev veth-peer scope global') output = check_output('ip -6 address show dev veth-peer scope global') print(output) @@ -6058,6 +6106,41 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): self.assertIn(f'via ::10.0.0.1 dev {tunnel_name}', output) def test_dhcp4_6rd(self): + def get_dbus_dhcp_6rd_prefix(IF): + out = subprocess.check_output(['busctl', 'call', 'org.freedesktop.network1', + '/org/freedesktop/network1', 'org.freedesktop.network1.Manager', + 'GetLinkByName', 's', IF]) + + assert out.startswith(b'io ') + out = out.strip() + assert out.endswith(b'"') + out = out.decode() + linkPath = out[:-1].split('"')[1] + + print(f"Found {IF} link path: {linkPath}") + + out = subprocess.check_output(['busctl', 'call', 'org.freedesktop.network1', + linkPath, 'org.freedesktop.network1.Link', 'Describe']) + assert out.startswith(b's "') + out = out.strip() + assert out.endswith(b'"') + json_raw = out[2:].decode() + check_json(json_raw) + description = json.loads(json_raw) # Convert from escaped sequences to json + check_json(description) + description = json.loads(description) # Now parse the json + + self.assertIn('DHCPv4Client', description.keys()) + self.assertIn('6rdPrefix', description['DHCPv4Client'].keys()) + + prefixInfo = description['DHCPv4Client']['6rdPrefix'] + self.assertIn('Prefix', prefixInfo.keys()) + self.assertIn('PrefixLength', prefixInfo.keys()) + self.assertIn('IPv4MaskLength', prefixInfo.keys()) + self.assertIn('BorderRouters', prefixInfo.keys()) + + return prefixInfo + copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network', '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network', '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network', @@ -6080,6 +6163,14 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): self.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded', 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable']) + # Check the DBus interface for assigned prefix information + prefixInfo = get_dbus_dhcp_6rd_prefix('veth99') + + self.assertEqual(prefixInfo['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8:: + self.assertEqual(prefixInfo['PrefixLength'], 32) + self.assertEqual(prefixInfo['IPv4MaskLength'], 8) + self.assertEqual(prefixInfo['BorderRouters'], [[10,0,0,1]]) + # Test case for a downstream which appears later check_output('ip link add dummy97 type dummy') self.wait_online(['dummy97:routable'])