core: save DHCP lease information in state file in /run

DHCP leases for a given interface are already exported on D-Bus
through DHCP4Config and DHCP6Config objects. It is useful to have the
same information also available on the filesystem so that it can be
easily used by scripts.

NM already saves some information about DHCP leases in /var, however
that directory can only be accessed by root, for good reasons.

Append lease options to the existing state file
/run/NetworkManager/devices/$ifindex. Contrary to /var this directory
is not persistent, but it seems more correct to expose the lease only
when it is active and not after it expired or after a reboot.

Since the file is in keyfile format, we add new [dhcp4] and [dhcp6]
sections; however, since some options have the same name for DHCPv4
and DHCPv6, we add a "dhcp4." or "dhcp6." prefix to make the parsing
by scripts (e.g. via "grep") easier.

The option name is the same we use on D-Bus. Since some DHCPv6 options
also have a "dhcp6_" prefix, the key name can contain "dhcp6" twice.

The new sections look like this:

  [dhcp4]
  dhcp4.broadcast_address=172.25.1.255
  dhcp4.dhcp_lease_time=120
  dhcp4.dhcp_server_identifier=172.25.1.4
  dhcp4.domain_name_servers=172.25.1.4
  dhcp4.domain_search=example.com
  dhcp4.expiry=1641214444
  dhcp4.ip_address=172.25.1.182
  dhcp4.next_server=172.25.1.4
  dhcp4.routers=172.25.1.4
  dhcp4.subnet_mask=255.255.255.0

  [dhcp6]
  dhcp6.dhcp6_name_servers=fd01::1
  dhcp6.dhcp6_ntp_servers=ntp.example.com
  dhcp6.ip6_address=fd01::1aa
This commit is contained in:
Beniamino Galvani 2022-04-19 18:09:49 +02:00
parent 96d8637ced
commit 6ab5c4e578
5 changed files with 49 additions and 24 deletions

3
NEWS
View File

@ -20,6 +20,9 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
* Static IPv6 addresses from "ipv6.addresses" are now interpreted with
first address being preferred. Their order got inverted. This is now
consistent with IPv4.
* The device state file /run/NetworkManager/devices/$ifindex now has
new sections [dhcp4] and [dhcp6] containing the DHCP options for the
current lease.
=============================================
NetworkManager-1.38

View File

@ -10064,6 +10064,7 @@ _dev_ipdhcpx_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_d
FALSE);
if (notify_data->lease_update.accepted) {
nm_manager_write_device_state(priv->manager, self, NULL);
if (priv->ipdhcp_data_x[IS_IPv4].state != NM_DEVICE_IP_STATE_READY) {
_dev_ipdhcpx_set_state(self, addr_family, NM_DEVICE_IP_STATE_READY);
nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_X(IS_IPv4),

View File

@ -11,7 +11,9 @@
#include <stdio.h>
#include "nm-utils.h"
#include "nm-dhcp-config.h"
#include "devices/nm-device.h"
#include "dhcp/nm-dhcp-options.h"
#include "NetworkManagerUtils.h"
#include "libnm-core-intern/nm-core-internal.h"
#include "libnm-core-intern/nm-keyfile-internal.h"
@ -2573,13 +2575,16 @@ nm_config_device_state_write(int ifindex,
NMTernary nm_owned,
guint32 route_metric_default_aspired,
guint32 route_metric_default_effective,
const char *next_server,
const char *root_path,
const char *dhcp_bootfile)
NMDhcpConfig *dhcp4_config,
NMDhcpConfig *dhcp6_config)
{
char path[NM_STRLEN(NM_CONFIG_DEVICE_STATE_DIR "/") + DEVICE_STATE_FILENAME_LEN_MAX + 1];
GError *local = NULL;
nm_auto_unref_keyfile GKeyFile *kf = NULL;
GError *local = NULL;
nm_auto_unref_keyfile GKeyFile *kf = NULL;
const char *root_path = NULL;
const char *next_server = NULL;
const char *dhcp_bootfile = NULL;
int IS_IPv4;
g_return_val_if_fail(ifindex > 0, FALSE);
g_return_val_if_fail(!connection_uuid || *connection_uuid, FALSE);
@ -2630,6 +2635,15 @@ nm_config_device_state_write(int ifindex,
route_metric_default_aspired);
}
}
if (dhcp4_config) {
next_server = nm_dhcp_config_get_option(dhcp4_config, "next_server");
root_path = nm_dhcp_config_get_option(dhcp4_config, "root_path");
dhcp_bootfile = nm_dhcp_config_get_option(dhcp4_config, "filename");
if (!dhcp_bootfile)
dhcp_bootfile = nm_dhcp_config_get_option(dhcp4_config, "bootfile_name");
}
if (next_server) {
g_key_file_set_string(kf,
DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
@ -2649,6 +2663,28 @@ nm_config_device_state_write(int ifindex,
dhcp_bootfile);
}
for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) {
NMDhcpConfig *dhcp_config = IS_IPv4 ? dhcp4_config : dhcp6_config;
gs_free NMUtilsNamedValue *values = NULL;
guint i;
guint num;
if (!dhcp_config)
continue;
values = nm_dhcp_config_get_option_values(dhcp_config, &num);
for (i = 0; i < num; i++) {
gs_free char *name_full = NULL;
const char *prefix = IS_IPv4 ? "dhcp4" : "dhcp6";
if (NM_STR_HAS_PREFIX(values[i].name, NM_DHCP_OPTION_REQPREFIX))
continue;
name_full = g_strdup_printf("%s.%s", prefix, values[i].name);
g_key_file_set_string(kf, prefix, name_full, values[i].value_str);
}
}
if (!g_key_file_save_to_file(kf, path, &local)) {
_LOGW("device-state: write #%d (%s) failed: %s", ifindex, path, local->message);
g_error_free(local);

View File

@ -183,9 +183,8 @@ gboolean nm_config_device_state_write(int
NMTernary nm_owned,
guint32 route_metric_default_aspired,
guint32 route_metric_default_effective,
const char *next_server,
const char *root_path,
const char *dhcp_bootfile);
NMDhcpConfig *dhcp4_config,
NMDhcpConfig *dhcp6_config);
void nm_config_device_state_prune_stale(GHashTable *preserve_ifindexes,
NMPlatform *preserve_in_platform);

View File

@ -7039,10 +7039,6 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde
guint32 route_metric_default_aspired;
guint32 route_metric_default_effective;
NMTernary nm_owned;
NMDhcpConfig *dhcp_config;
const char *next_server = NULL;
const char *root_path = NULL;
const char *dhcp_bootfile = NULL;
NM_SET_OUT(out_ifindex, 0);
@ -7084,15 +7080,6 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde
TRUE,
&route_metric_default_aspired);
dhcp_config = nm_device_get_dhcp_config(device, AF_INET);
if (dhcp_config) {
root_path = nm_dhcp_config_get_option(dhcp_config, "root_path");
next_server = nm_dhcp_config_get_option(dhcp_config, "next_server");
dhcp_bootfile = nm_dhcp_config_get_option(dhcp_config, "filename");
if (!dhcp_bootfile)
dhcp_bootfile = nm_dhcp_config_get_option(dhcp_config, "bootfile_name");
}
if (!nm_config_device_state_write(ifindex,
managed_type,
perm_hw_addr_fake,
@ -7100,9 +7087,8 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde
nm_owned,
route_metric_default_aspired,
route_metric_default_effective,
next_server,
root_path,
dhcp_bootfile))
nm_device_get_dhcp_config(device, AF_INET),
nm_device_get_dhcp_config(device, AF_INET6)))
return FALSE;
NM_SET_OUT(out_ifindex, ifindex);