From 964ae8cc391520440cf5aa13e2b9cc34850ea6c2 Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Tue, 26 Feb 2019 19:01:41 +0100 Subject: New upstream version 1.14.6 --- src/NetworkManagerUtils.c | 4 +- src/NetworkManagerUtils.h | 1 + src/devices/bluetooth/nm-device-bt.c | 2 +- src/devices/nm-device-bridge.c | 101 ++- src/devices/nm-device-ethernet.c | 5 +- src/devices/nm-device-infiniband.c | 1 + src/devices/nm-device-ip-tunnel.c | 1 + src/devices/nm-device-macvlan.c | 1 + src/devices/nm-device-wpan.c | 1 + src/devices/nm-device.c | 441 +++++---- src/devices/nm-device.h | 2 + src/devices/nm-lldp-listener.c | 4 +- src/devices/ovs/nm-ovsdb.c | 78 +- src/devices/tests/test-lldp.c | 5 +- src/devices/wifi/nm-device-olpc-mesh.c | 19 +- src/devices/wwan/nm-device-modem.c | 38 +- src/dhcp/nm-dhcp-client.c | 75 +- src/dhcp/nm-dhcp-client.h | 13 +- src/dhcp/nm-dhcp-dhclient-utils.c | 36 +- src/dhcp/nm-dhcp-dhclient-utils.h | 5 +- src/dhcp/nm-dhcp-dhclient.c | 98 +- src/dhcp/nm-dhcp-dhcpcanon.c | 19 +- src/dhcp/nm-dhcp-dhcpcd.c | 5 +- src/dhcp/nm-dhcp-manager.c | 90 +- src/dhcp/nm-dhcp-manager.h | 2 + src/dhcp/nm-dhcp-systemd.c | 302 +++---- src/dhcp/nm-dhcp-utils.c | 4 +- src/dhcp/tests/test-dhcp-dhclient.c | 69 +- src/dns/nm-dns-dnsmasq.c | 13 +- src/dns/nm-dns-manager.c | 163 ++-- src/dns/nm-dns-manager.h | 6 + src/dnsmasq/nm-dnsmasq-manager.c | 2 + src/initrd/meson.build | 32 + src/initrd/tests/meson.build | 23 + src/ndisc/nm-ndisc.h | 1 + src/nm-config-data.c | 6 +- src/nm-config-data.h | 9 + src/nm-connectivity.h | 15 + src/nm-core-utils.c | 984 +++++++++++++++------ src/nm-core-utils.h | 82 +- src/nm-dbus-manager.c | 26 +- src/nm-ip4-config.c | 24 +- src/nm-ip6-config.c | 24 +- src/nm-manager.c | 14 +- src/nm-policy.c | 11 +- src/nm-test-utils-core.h | 4 +- src/platform/nm-fake-platform.c | 1 + src/platform/nm-linux-platform.c | 23 +- src/platform/nm-platform-utils.c | 1 + src/platform/nm-platform.c | 49 +- src/platform/nm-platform.h | 21 +- src/platform/nmp-object.c | 1 + src/platform/tests/test-common.c | 2 - src/platform/tests/test-common.h | 3 + src/platform/wifi/nm-wifi-utils-nl80211.c | 1 + src/platform/wpan/nm-wpan-utils.c | 2 + src/settings/nm-settings-connection.c | 22 +- .../plugins/ibft/tests/iscsiadm-test-bad-dns1 | 21 + .../plugins/ibft/tests/iscsiadm-test-bad-dns2 | 21 + .../plugins/ibft/tests/iscsiadm-test-bad-entry | 20 + .../plugins/ibft/tests/iscsiadm-test-bad-gateway | 21 + .../plugins/ibft/tests/iscsiadm-test-bad-ipaddr | 21 + .../plugins/ibft/tests/iscsiadm-test-bad-record | 18 + src/settings/plugins/ibft/tests/iscsiadm-test-dhcp | 33 + .../plugins/ibft/tests/iscsiadm-test-static | 35 + src/settings/plugins/ibft/tests/iscsiadm-test-vlan | 19 + .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 31 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 9 +- .../plugins/ifupdown/nms-ifupdown-plugin.c | 6 +- src/supplicant/nm-supplicant-config.c | 46 +- src/supplicant/nm-supplicant-interface.c | 36 +- src/supplicant/tests/test-supplicant-config.c | 4 +- src/systemd/meson.build | 3 +- src/systemd/nm-sd-utils.c | 61 ++ src/systemd/nm-sd-utils.h | 38 + src/systemd/src/basic/path-util.c | 2 - .../src/libsystemd-network/dhcp-identifier.c | 8 +- .../src/libsystemd-network/dhcp-identifier.h | 2 +- src/systemd/src/libsystemd-network/dhcp6-option.c | 78 +- .../src/libsystemd-network/sd-dhcp-client.c | 31 +- src/systemd/src/libsystemd-network/sd-dhcp-lease.c | 2 + .../src/libsystemd-network/sd-dhcp6-client.c | 25 +- src/tests/config/test-config.c | 19 + src/tests/test-general.c | 253 +++++- src/tests/test-ip6-config.c | 1 + src/tests/test-systemd.c | 46 + src/vpn/nm-vpn-connection.c | 40 +- 87 files changed, 2777 insertions(+), 1160 deletions(-) create mode 100644 src/initrd/meson.build create mode 100644 src/initrd/tests/meson.build create mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 create mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 create mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry create mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway create mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr create mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-bad-record create mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-dhcp create mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-static create mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-vlan create mode 100644 src/systemd/nm-sd-utils.c create mode 100644 src/systemd/nm-sd-utils.h (limited to 'src') diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 1404854d..e3766809 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -881,6 +881,7 @@ nm_utils_match_connection (NMConnection *const*connections, int nm_match_spec_device_by_pllink (const NMPlatformLink *pllink, const char *match_device_type, + const char *match_dhcp_plugin, const GSList *specs, int no_match_value) { @@ -897,7 +898,8 @@ nm_match_spec_device_by_pllink (const NMPlatformLink *pllink, pllink ? pllink->driver : NULL, NULL, NULL, - NULL); + NULL, + match_dhcp_plugin); switch (m) { case NM_MATCH_SPEC_MATCH: diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index b26d08bd..efbd9037 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -50,6 +50,7 @@ NMConnection *nm_utils_match_connection (NMConnection *const*connections, int nm_match_spec_device_by_pllink (const NMPlatformLink *pllink, const char *match_device_type, + const char *match_dhcp_plugin, const GSList *specs, int no_match_value); diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c index 1f650d33..f4a1b709 100644 --- a/src/devices/bluetooth/nm-device-bt.c +++ b/src/devices/bluetooth/nm-device-bt.c @@ -323,7 +323,7 @@ complete_connection (NMDevice *device, } else { fallback_prefix = _("CDMA connection"); if (!nm_setting_cdma_get_number (s_cdma)) - g_object_set (G_OBJECT (s_cdma), NM_SETTING_GSM_NUMBER, "#777", NULL); + g_object_set (G_OBJECT (s_cdma), NM_SETTING_CDMA_NUMBER, "#777", NULL); } } else { g_set_error_literal (error, diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index e79de95c..68e1ac28 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -168,26 +168,52 @@ complete_connection (NMDevice *device, typedef struct { const char *name; const char *sysname; - gboolean default_if_zero; - gboolean user_hz_compensate; + uint nm_min; + uint nm_max; + uint nm_default; + bool default_if_zero; + bool user_hz_compensate; + bool only_with_stp; } Option; static const Option master_options[] = { - { NM_SETTING_BRIDGE_STP, "stp_state", FALSE, FALSE }, - { NM_SETTING_BRIDGE_PRIORITY, "priority", TRUE, FALSE }, - { NM_SETTING_BRIDGE_FORWARD_DELAY, "forward_delay", TRUE, TRUE }, - { NM_SETTING_BRIDGE_HELLO_TIME, "hello_time", TRUE, TRUE }, - { NM_SETTING_BRIDGE_MAX_AGE, "max_age", TRUE, TRUE }, - { NM_SETTING_BRIDGE_AGEING_TIME, "ageing_time", TRUE, TRUE }, - { NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, "group_fwd_mask", TRUE, FALSE }, - { NM_SETTING_BRIDGE_MULTICAST_SNOOPING, "multicast_snooping", FALSE, FALSE }, + { NM_SETTING_BRIDGE_STP, "stp_state", /* this must stay as the first item */ + 0, 1, 1, + FALSE, FALSE, FALSE }, + { NM_SETTING_BRIDGE_PRIORITY, "priority", + 0, G_MAXUINT16, 0x8000, + TRUE, FALSE, TRUE }, + { NM_SETTING_BRIDGE_FORWARD_DELAY, "forward_delay", + 0, NM_BR_MAX_FORWARD_DELAY, 15, + TRUE, TRUE, TRUE}, + { NM_SETTING_BRIDGE_HELLO_TIME, "hello_time", + 0, NM_BR_MAX_HELLO_TIME, 2, + TRUE, TRUE, TRUE }, + { NM_SETTING_BRIDGE_MAX_AGE, "max_age", + 0, NM_BR_MAX_MAX_AGE, 20, + TRUE, TRUE, TRUE }, + { NM_SETTING_BRIDGE_AGEING_TIME, "ageing_time", + NM_BR_MIN_AGEING_TIME, NM_BR_MAX_AGEING_TIME, 300, + TRUE, TRUE, FALSE }, + { NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, "group_fwd_mask", + 0, 0xFFFF, 0, + TRUE, FALSE, FALSE }, + { NM_SETTING_BRIDGE_MULTICAST_SNOOPING, "multicast_snooping", + 0, 1, 1, + FALSE, FALSE, FALSE }, { NULL, NULL } }; static const Option slave_options[] = { - { NM_SETTING_BRIDGE_PORT_PRIORITY, "priority", TRUE, FALSE }, - { NM_SETTING_BRIDGE_PORT_PATH_COST, "path_cost", TRUE, FALSE }, - { NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, "hairpin_mode", FALSE, FALSE }, + { NM_SETTING_BRIDGE_PORT_PRIORITY, "priority", + 0, NM_BR_PORT_MAX_PRIORITY, NM_BR_PORT_DEF_PRIORITY, + TRUE, FALSE }, + { NM_SETTING_BRIDGE_PORT_PATH_COST, "path_cost", + 0, NM_BR_PORT_MAX_PATH_COST, 100, + TRUE, FALSE }, + { NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, "hairpin_mode", + 0, 1, 0, + FALSE, FALSE }, { NULL, NULL } }; @@ -275,23 +301,43 @@ update_connection (NMDevice *device, NMConnection *connection) NMSettingBridge *s_bridge = nm_connection_get_setting_bridge (connection); int ifindex = nm_device_get_ifindex (device); const Option *option; + gs_free char *stp = NULL; + int stp_value; if (!s_bridge) { s_bridge = (NMSettingBridge *) nm_setting_bridge_new (); nm_connection_add_setting (connection, (NMSetting *) s_bridge); } - for (option = master_options; option->name; option++) { + option = master_options; + nm_assert (nm_streq (option->sysname, "stp_state")); + + stp = nm_platform_sysctl_master_get_option (nm_device_get_platform (device), ifindex, option->sysname); + stp_value = _nm_utils_ascii_str_to_int64 (stp, 10, option->nm_min, option->nm_max, option->nm_default); + g_object_set (s_bridge, option->name, stp_value, NULL); + option++; + + for (; option->name; option++) { gs_free char *str = nm_platform_sysctl_master_get_option (nm_device_get_platform (device), ifindex, option->sysname); - int value; + uint value; - if (str) { - value = strtol (str, NULL, 10); + if (!stp_value && option->only_with_stp) + continue; + if (str) { /* See comments in set_sysfs_uint() about centiseconds. */ - if (option->user_hz_compensate) + if (option->user_hz_compensate) { + value = _nm_utils_ascii_str_to_int64 (str, 10, + option->nm_min * 100, + option->nm_max * 100, + option->nm_default * 100); value /= 100; - + } else { + value = _nm_utils_ascii_str_to_int64 (str, 10, + option->nm_min, + option->nm_max, + option->nm_default); + } g_object_set (s_bridge, option->name, value, NULL); } else _LOGW (LOGD_BRIDGE, "failed to read bridge setting '%s'", option->sysname); @@ -322,15 +368,22 @@ master_update_slave_connection (NMDevice *device, for (option = slave_options; option->name; option++) { gs_free char *str = nm_platform_sysctl_slave_get_option (nm_device_get_platform (device), ifindex_slave, option->sysname); - int value; + uint value; if (str) { - value = strtol (str, NULL, 10); - /* See comments in set_sysfs_uint() about centiseconds. */ - if (option->user_hz_compensate) + if (option->user_hz_compensate) { + value = _nm_utils_ascii_str_to_int64 (str, 10, + option->nm_min * 100, + option->nm_max * 100, + option->nm_default * 100); value /= 100; - + } else { + value = _nm_utils_ascii_str_to_int64 (str, 10, + option->nm_min, + option->nm_max, + option->nm_default); + } g_object_set (s_port, option->name, value, NULL); } else _LOGW (LOGD_BRIDGE, "failed to read bridge port setting '%s'", option->sysname); diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 0fb8fea4..e7262683 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -1446,7 +1446,6 @@ new_default_connection (NMDevice *self) const char *uprop = "0"; gs_free char *defname = NULL; gs_free char *uuid = NULL; - gs_free char *machine_id = NULL; guint i, n_connections; if (nm_config_get_no_auto_default_for_device (nm_config_get (), self)) @@ -1470,12 +1469,10 @@ new_default_connection (NMDevice *self) if (!defname) return NULL; - machine_id = nm_utils_machine_id_read (); - /* Create a stable UUID. The UUID is also the Network_ID for stable-privacy addr-gen-mode, * thus when it changes we will also generate different IPv6 addresses. */ uuid = _nm_utils_uuid_generate_from_strings ("default-wired", - machine_id ?: "", + nm_utils_machine_id_str (), defname, perm_hw_addr, NULL); diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index 41fac157..5138b684 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -22,6 +22,7 @@ #include "nm-device-infiniband.h" +#include #include #include "NetworkManagerUtils.h" diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c index 568403c6..1c7e6d51 100644 --- a/src/devices/nm-device-ip-tunnel.c +++ b/src/devices/nm-device-ip-tunnel.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "nm-device-private.h" #include "nm-manager.h" diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index ff386c82..2b212154 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -23,6 +23,7 @@ #include "nm-device-macvlan.h" #include +#include #include "nm-device-private.h" #include "settings/nm-settings.h" diff --git a/src/devices/nm-device-wpan.c b/src/devices/nm-device-wpan.c index f910b0b8..05b507d5 100644 --- a/src/devices/nm-device-wpan.c +++ b/src/devices/nm-device-wpan.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "nm-act-request.h" #include "nm-device-private.h" diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index f2dbe659..89e1cc51 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -37,7 +37,6 @@ #include #include #include -#include #include "nm-utils/nm-dedup-multi.h" #include "nm-utils/nm-random-utils.h" @@ -386,13 +385,14 @@ typedef struct _NMDevicePrivate { bool v4_route_table_initialized:1; bool v6_route_table_initialized:1; - NMDeviceAutoconnectBlockedFlags autoconnect_blocked_flags:4; + NMDeviceAutoconnectBlockedFlags autoconnect_blocked_flags:5; bool is_enslaved:1; bool master_ready_handled:1; bool ipv6ll_handle:1; /* TRUE if NM handles the device's IPv6LL address */ bool ipv6ll_has:1; + bool device_link_changed_down:1; /* Generic DHCP stuff */ char * dhcp_anycast_address; @@ -1264,6 +1264,8 @@ _get_stable_id (NMDevice *self, gs_free char *generated = NULL; NMUtilsStableType stable_type; NMSettingConnection *s_con; + gboolean hwaddr_is_fake; + const char *hwaddr; const char *stable_id; const char *uuid; @@ -1280,9 +1282,15 @@ _get_stable_id (NMDevice *self, uuid = nm_connection_get_uuid (connection); + /* the cloned-mac-address may be generated based on the stable-id. + * Thus, at this point, we can only use the permanant MAC address + * as seed. */ + hwaddr = nm_device_get_permanent_hw_address_full (self, TRUE, &hwaddr_is_fake); + stable_type = nm_utils_stable_id_parse (stable_id, nm_device_get_ip_iface (self), - NULL, + !hwaddr_is_fake ? hwaddr : NULL, + nm_utils_boot_id_str (), uuid, &generated); @@ -3585,8 +3593,10 @@ device_link_changed (NMDevice *self) gboolean was_up; gboolean update_unmanaged_specs = FALSE; gboolean got_hw_addr = FALSE, had_hw_addr; + gboolean seen_down = priv->device_link_changed_down; priv->device_link_changed_id = 0; + priv->device_link_changed_down = FALSE; ifindex = nm_device_get_ifindex (self); pllink = nm_platform_link_get (nm_device_get_platform (self), ifindex); @@ -3696,7 +3706,7 @@ device_link_changed (NMDevice *self) device_recheck_slave_status (self, pllink); - if (priv->up && !was_up) { + if (priv->up && (!was_up || seen_down)) { /* the link was down and just came up. That happens for example, while changing MTU. * We must restore IP configuration. */ if (priv->ip4_state == IP_DONE) { @@ -3772,6 +3782,8 @@ link_changed_cb (NMPlatform *platform, priv = NM_DEVICE_GET_PRIVATE (self); if (ifindex == nm_device_get_ifindex (self)) { + if (!(info->n_ifi_flags & IFF_UP)) + priv->device_link_changed_down = TRUE; if (!priv->device_link_changed_id) { priv->device_link_changed_id = g_idle_add ((GSourceFunc) device_link_changed, self); _LOGD (LOGD_DEVICE, "queued link change for ifindex %d", ifindex); @@ -4133,7 +4145,7 @@ device_init_static_sriov_num_vfs (NMDevice *self) num_vfs = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXINT32, -1); if (num_vfs >= 0) { nm_platform_link_set_sriov_params (nm_device_get_platform (self), - priv->ifindex, num_vfs, -1); + priv->ifindex, num_vfs, NM_TERNARY_DEFAULT); } } } @@ -4296,8 +4308,6 @@ realize_start_setup (NMDevice *self, nm_device_set_carrier_from_platform (self); - device_init_static_sriov_num_vfs (self); - nm_assert (!priv->stats.timeout_id); real_rate = _stats_refresh_rate_real (priv->stats.refresh_rate_ms); if (real_rate) @@ -6201,7 +6211,7 @@ act_stage1_prepare (NMDevice *self, NMDeviceStateReason *out_failure_reason) nm_auto_freev NMPlatformVF **plat_vfs = NULL; gs_free_error GError *error = NULL; NMSriovVF *vf; - int autoprobe; + NMTernary autoprobe; autoprobe = nm_setting_sriov_get_autoprobe_drivers (s_sriov); if (autoprobe == NM_TERNARY_DEFAULT) { @@ -7449,18 +7459,6 @@ get_dhcp_timeout (NMDevice *self, int addr_family) return timeout ?: NM_DHCP_TIMEOUT_DEFAULT; } -static GBytes * -dhcp4_get_client_id_mac (const guint8 *hwaddr /* ETH_ALEN bytes */) -{ - guint8 *client_id_buf; - guint8 hwaddr_type = ARPHRD_ETHER; - - client_id_buf = g_malloc (ETH_ALEN + 1); - client_id_buf[0] = hwaddr_type; - memcpy (&client_id_buf[1], hwaddr, ETH_ALEN); - return g_bytes_new_take (client_id_buf, ETH_ALEN + 1); -} - static GBytes * dhcp4_get_client_id (NMDevice *self, NMConnection *connection, @@ -7473,6 +7471,7 @@ dhcp4_get_client_id (NMDevice *self, const char *fail_reason; guint8 hwaddr_bin_buf[NM_UTILS_HWADDR_LEN_MAX]; const guint8 *hwaddr_bin; + int arp_type; gsize hwaddr_len; GBytes *result; gs_free char *logstr1 = NULL; @@ -7497,17 +7496,18 @@ dhcp4_get_client_id (NMDevice *self, if (nm_streq (client_id, "mac")) { if (!hwaddr) { - fail_reason = "failed to get current MAC address"; + fail_reason = "missing link-layer address"; goto out_fail; } hwaddr_bin = g_bytes_get_data (hwaddr, &hwaddr_len); - if (hwaddr_len != ETH_ALEN) { - fail_reason = "MAC address is not ethernet"; + arp_type = nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len); + if (arp_type < 0) { + fail_reason = "unsupported link-layer address"; goto out_fail; } - result = dhcp4_get_client_id_mac (hwaddr_bin); + result = nm_utils_dhcp_client_id_mac (arp_type, hwaddr_bin, hwaddr_len); goto out_good; } @@ -7516,32 +7516,37 @@ dhcp4_get_client_id (NMDevice *self, hwaddr_str = nm_device_get_permanent_hw_address (self); if (!hwaddr_str) { - fail_reason = "failed to get permanent MAC address"; + fail_reason = "missing permanent link-layer address"; goto out_fail; } if (!_nm_utils_hwaddr_aton (hwaddr_str, hwaddr_bin_buf, sizeof (hwaddr_bin_buf), &hwaddr_len)) g_return_val_if_reached (NULL); - if (hwaddr_len != ETH_ALEN) { - /* unsupported type. */ - fail_reason = "MAC address is not ethernet"; + arp_type = nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len); + if (arp_type < 0) { + fail_reason = "unsupported permanent link-layer address"; goto out_fail; } - result = dhcp4_get_client_id_mac (hwaddr_bin_buf); + result = nm_utils_dhcp_client_id_mac (arp_type, hwaddr_bin_buf, hwaddr_len); + goto out_good; + } + + if (nm_streq (client_id, "duid")) { + result = nm_utils_dhcp_client_id_systemd_node_specific (TRUE, + nm_device_get_ip_iface (self)); goto out_good; } if (nm_streq (client_id, "stable")) { + nm_auto_free_checksum GChecksum *sum = NULL; + guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA1]; NMUtilsStableType stable_type; const char *stable_id; - GChecksum *sum; - guint8 buf[20]; - gsize buf_size; guint32 salted_header; - const guint8 *secret_key; - gsize secret_key_len; + const guint8 *host_id; + gsize host_id_len; stable_id = _get_stable_id (self, connection, &stable_type); if (!stable_id) @@ -7549,23 +7554,17 @@ dhcp4_get_client_id (NMDevice *self, salted_header = htonl (2011610591 + stable_type); - nm_utils_secret_key_get (&secret_key, &secret_key_len); + nm_utils_host_id_get (&host_id, &host_id_len); sum = g_checksum_new (G_CHECKSUM_SHA1); - g_checksum_update (sum, (const guchar *) &salted_header, sizeof (salted_header)); g_checksum_update (sum, (const guchar *) stable_id, strlen (stable_id) + 1); - g_checksum_update (sum, (const guchar *) secret_key, secret_key_len); - - buf_size = sizeof (buf); - g_checksum_get_digest (sum, buf, &buf_size); - nm_assert (buf_size == sizeof (buf)); - - g_checksum_free (sum); + g_checksum_update (sum, (const guchar *) host_id, host_id_len); + nm_utils_checksum_get_digest (sum, digest); client_id_buf = g_malloc (1 + 15); client_id_buf[0] = 0; - memcpy (&client_id_buf[1], buf, 15); + memcpy (&client_id_buf[1], digest, 15); result = g_bytes_new_take (client_id_buf, 1 + 15); goto out_good; } @@ -8187,53 +8186,63 @@ dhcp6_prefix_delegated (NMDhcpClient *client, g_signal_emit (self, signals[IP6_PREFIX_DELEGATED], 0, prefix); } +/*****************************************************************************/ + /* RFC 3315 defines the epoch for the DUID-LLT time field on Jan 1st 2000. */ #define EPOCH_DATETIME_200001010000 946684800 static GBytes * -generate_duid_llt (const guint8 *hwaddr /* ETH_ALEN bytes */, +generate_duid_llt (int arp_type, + const guint8 *hwaddr, + gsize hwaddr_len, gint64 time) { guint8 *arr; const guint16 duid_type = htons (1); - const guint16 hw_type = htons (ARPHRD_ETHER); + const guint16 hw_type = htons (arp_type); const guint32 duid_time = htonl (NM_MAX (0, time - EPOCH_DATETIME_200001010000)); - arr = g_new (guint8, 2 + 2 + 4 + ETH_ALEN); + if (!nm_utils_arp_type_get_hwaddr_relevant_part (arp_type, &hwaddr, &hwaddr_len)) + nm_assert_not_reached (); + + arr = g_new (guint8, 2 + 2 + 4 + hwaddr_len); memcpy (&arr[0], &duid_type, 2); memcpy (&arr[2], &hw_type, 2); memcpy (&arr[4], &duid_time, 4); - memcpy (&arr[8], hwaddr, ETH_ALEN); + memcpy (&arr[8], hwaddr, hwaddr_len); - return g_bytes_new_take (arr, 2 + 2 + 4 + ETH_ALEN); + return g_bytes_new_take (arr, 2 + 2 + 4 + hwaddr_len); } static GBytes * -generate_duid_ll (const guint8 *hwaddr /* ETH_ALEN bytes */) +generate_duid_ll (int arp_type, + const guint8 *hwaddr, + gsize hwaddr_len) { guint8 *arr; const guint16 duid_type = htons (3); - const guint16 hw_type = htons (ARPHRD_ETHER); + const guint16 hw_type = htons (arp_type); - arr = g_new (guint8, 2 + 2 + ETH_ALEN); + if (!nm_utils_arp_type_get_hwaddr_relevant_part (arp_type, &hwaddr, &hwaddr_len)) + nm_assert_not_reached (); + + arr = g_new (guint8, 2 + 2 + hwaddr_len); memcpy (&arr[0], &duid_type, 2); memcpy (&arr[2], &hw_type, 2); - memcpy (&arr[4], hwaddr, ETH_ALEN); + memcpy (&arr[4], hwaddr, hwaddr_len); - return g_bytes_new_take (arr, 2 + 2 + ETH_ALEN); + return g_bytes_new_take (arr, 2 + 2 + hwaddr_len); } static GBytes * -generate_duid_uuid (guint8 *data, gsize data_len) +generate_duid_uuid (const NMUuid *uuid) { - const guint16 duid_type = g_htons (4); - const int DUID_SIZE = 18; + const guint16 duid_type = htons (4); guint8 *duid_buffer; - nm_assert (data); - nm_assert (data_len >= 16); + nm_assert (uuid); /* Generate a DHCP Unique Identifier for DHCPv6 using the * DUID-UUID method (see RFC 6355 section 4). Format is: @@ -8241,44 +8250,47 @@ generate_duid_uuid (guint8 *data, gsize data_len) * u16: type (DUID-UUID = 4) * u8[16]: UUID bytes */ - duid_buffer = g_malloc (DUID_SIZE); - G_STATIC_ASSERT_EXPR (sizeof (duid_type) == 2); + G_STATIC_ASSERT_EXPR (sizeof (*uuid) == 16); + duid_buffer = g_malloc (18); memcpy (&duid_buffer[0], &duid_type, 2); - - /* UUID is 128 bits, we just take the first 128 bits - * (regardless of data size) as the DUID-UUID. - */ - memcpy (&duid_buffer[2], data, 16); - - return g_bytes_new_take (duid_buffer, DUID_SIZE); + memcpy (&duid_buffer[2], uuid, 16); + return g_bytes_new_take (duid_buffer, 18); } static GBytes * generate_duid_from_machine_id (void) { - gs_free const char *machine_id_s = NULL; - uuid_t uuid; - GChecksum *sum; - guint8 sha256_digest[32]; - gsize len = sizeof (sha256_digest); - static GBytes *global_duid = NULL; + static GBytes *volatile global_duid = NULL; + GBytes *p; - if (global_duid) - return g_bytes_ref (global_duid); +again: + p = g_atomic_pointer_get (&global_duid); + if (G_UNLIKELY (!p)) { + nm_auto_free_checksum GChecksum *sum = NULL; + const NMUuid *machine_id; + union { + guint8 sha256[NM_UTILS_CHECKSUM_LENGTH_SHA256]; + NMUuid uuid; + } digest; - machine_id_s = nm_utils_machine_id_read (); - if (!nm_utils_machine_id_parse (machine_id_s, uuid)) - return NULL; + machine_id = nm_utils_machine_id_bin (); + + /* Hash the machine ID so it's not leaked to the network */ + sum = g_checksum_new (G_CHECKSUM_SHA256); + g_checksum_update (sum, (const guchar *) machine_id, sizeof (*machine_id)); + nm_utils_checksum_get_digest (sum, digest.sha256); - /* Hash the machine ID so it's not leaked to the network */ - sum = g_checksum_new (G_CHECKSUM_SHA256); - g_checksum_update (sum, (const guchar *) &uuid, sizeof (uuid)); - g_checksum_get_digest (sum, sha256_digest, &len); - g_checksum_free (sum); + G_STATIC_ASSERT_EXPR (sizeof (digest.sha256) > sizeof (digest.uuid)); + p = generate_duid_uuid (&digest.uuid); + + if (!g_atomic_pointer_compare_and_exchange (&global_duid, NULL, p)) { + g_bytes_unref (p); + goto again; + } + } - global_duid = generate_duid_uuid (sha256_digest, len); - return g_bytes_ref (global_duid); + return g_bytes_ref (p); } static GBytes * @@ -8289,10 +8301,11 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole gs_free char *duid_default = NULL; const char *duid_error; GBytes *duid_out; - guint8 sha256_digest[32]; - gsize len = sizeof (sha256_digest); gboolean duid_enforce = TRUE; gs_free char *logstr1 = NULL; + const guint8 *hwaddr_bin; + gsize hwaddr_len; + int arp_type; s_ip6 = nm_connection_get_setting_ip6_config (connection); duid = nm_setting_ip6_config_get_dhcp_duid (NM_SETTING_IP6_CONFIG (s_ip6)); @@ -8308,10 +8321,6 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole if (nm_streq (duid, "lease")) { duid_enforce = FALSE; duid_out = generate_duid_from_machine_id (); - if (!duid_out) { - duid_error = "failure to read machine-id"; - goto out_fail; - } goto out_good; } @@ -8328,78 +8337,138 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole duid_error = "missing link-layer address"; goto out_fail; } - if (g_bytes_get_size (hwaddr) != ETH_ALEN) { + + hwaddr_bin = g_bytes_get_data (hwaddr, &hwaddr_len); + arp_type = nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len); + if (arp_type < 0) { duid_error = "unsupported link-layer address"; goto out_fail; } - if (nm_streq (duid, "ll")) { - duid_out = generate_duid_ll (g_bytes_get_data (hwaddr, NULL)); - } else { - gint64 time; - - time = nm_utils_secret_key_get_timestamp (); - if (!time) { - duid_error = "cannot retrieve the secret key timestamp"; - goto out_fail; - } - - duid_out = generate_duid_llt (g_bytes_get_data (hwaddr, NULL), time); + if (nm_streq (duid, "ll")) + duid_out = generate_duid_ll (arp_type, hwaddr_bin, hwaddr_len); + else { + duid_out = generate_duid_llt (arp_type, hwaddr_bin, hwaddr_len, + nm_utils_host_id_get_timestamp_ns () / NM_UTILS_NS_PER_SECOND); } goto out_good; } if (NM_IN_STRSET (duid, "stable-ll", "stable-llt", "stable-uuid")) { + /* preferably, we would salt the checksum differently for each @duid type. We missed + * to do that initially, so most types use the DEFAULT_SALT. + * + * Implemenations that are added later, should use a distinct salt instead, + * like "stable-ll"/"stable-llt" with ARPHRD_INFINIBAND below. */ + const guint32 DEFAULT_SALT = 670531087u; + nm_auto_free_checksum GChecksum *sum = NULL; NMUtilsStableType stable_type; const char *stable_id = NULL; guint32 salted_header; - GChecksum *sum; - const guint8 *secret_key; - gsize secret_key_len; + const guint8 *host_id; + gsize host_id_len; + union { + guint8 sha256[NM_UTILS_CHECKSUM_LENGTH_SHA256]; + guint8 hwaddr_eth[ETH_ALEN]; + guint8 hwaddr_infiniband[INFINIBAND_ALEN]; + NMUuid uuid; + struct _nm_packed { + guint8 hwaddr[ETH_ALEN]; + guint32 timestamp; + } llt_eth; + struct _nm_packed { + guint8 hwaddr[INFINIBAND_ALEN]; + guint32 timestamp; + } llt_infiniband; + } digest; stable_id = _get_stable_id (self, connection, &stable_type); if (!stable_id) g_return_val_if_reached (NULL); - salted_header = htonl (670531087 + stable_type); + if (NM_IN_STRSET (duid, "stable-ll", "stable-llt")) { + /* for stable LL/LLT DUIDs, we still need a hardware address to detect + * the arp-type. Alternatively, we would be able to detect it based on + * other means (e.g. NMDevice type), but instead require the hardware + * address to be present. This is at least consistent with the "ll"/"llt" + * modes above. */ + if (!hwaddr) { + duid_error = "missing link-layer address"; + goto out_fail; + } + if ((arp_type = nm_utils_arp_type_detect_from_hwaddrlen (g_bytes_get_size (hwaddr))) < 0) { + duid_error = "unsupported link-layer address"; + goto out_fail; + } + + if (arp_type == ARPHRD_ETHER) + salted_header = DEFAULT_SALT; + else { + nm_assert (arp_type == ARPHRD_INFINIBAND); + salted_header = 0x42492CEFu + ((guint32) arp_type); + } + } else { + salted_header = DEFAULT_SALT; + arp_type = -1; + } - nm_utils_secret_key_get (&secret_key, &secret_key_len); + salted_header = htonl (salted_header + ((guint32) stable_type)); - sum = g_checksum_new (G_CHECKSUM_SHA256); + nm_utils_host_id_get (&host_id, &host_id_len); + sum = g_checksum_new (G_CHECKSUM_SHA256); g_checksum_update (sum, (const guchar *) &salted_header, sizeof (salted_header)); g_checksum_update (sum, (const guchar *) stable_id, -1); - g_checksum_update (sum, (const guchar *) secret_key, secret_key_len); + g_checksum_update (sum, (const guchar *) host_id, host_id_len); + nm_utils_checksum_get_digest (sum, digest.sha256); - g_checksum_get_digest (sum, sha256_digest, &len); - g_checksum_free (sum); + G_STATIC_ASSERT_EXPR (sizeof (digest) == sizeof (digest.sha256)); if (nm_streq (duid, "stable-ll")) { - duid_out = generate_duid_ll (sha256_digest); + switch (arp_type) { + case ARPHRD_ETHER: + duid_out = generate_duid_ll (arp_type, digest.hwaddr_eth, sizeof (digest.hwaddr_eth)); + break; + case ARPHRD_INFINIBAND: + duid_out = generate_duid_ll (arp_type, digest.hwaddr_infiniband, sizeof (digest.hwaddr_infiniband)); + break; + default: + g_return_val_if_reached (NULL); + } } else if (nm_streq (duid, "stable-llt")) { gint64 time; + guint32 timestamp; #define EPOCH_DATETIME_THREE_YEARS (356 * 24 * 3600 * 3) - /* We want a variable time between the secret_key timestamp and three years + /* We want a variable time between the host_id timestamp and three years * before. Let's compute the time (in seconds) from 0 to 3 years; then we'll - * subtract it from the secret_key timestamp. + * subtract it from the host_id timestamp. */ - time = nm_utils_secret_key_get_timestamp (); - if (!time) { - duid_error = "cannot retrieve the secret key timestamp"; - goto out_fail; - } + time = nm_utils_host_id_get_timestamp_ns () / NM_UTILS_NS_PER_SECOND; + /* don't use too old timestamps. They cannot be expressed in DUID-LLT and * would all be truncated to zero. */ time = NM_MAX (time, EPOCH_DATETIME_200001010000 + EPOCH_DATETIME_THREE_YEARS); - time -= (unaligned_read_be32 (&sha256_digest[ETH_ALEN]) % EPOCH_DATETIME_THREE_YEARS); - duid_out = generate_duid_llt (sha256_digest, time); + switch (arp_type) { + case ARPHRD_ETHER: + timestamp = unaligned_read_be32 (&digest.llt_eth.timestamp); + time -= timestamp % EPOCH_DATETIME_THREE_YEARS; + duid_out = generate_duid_llt (arp_type, digest.llt_eth.hwaddr, sizeof (digest.llt_eth.hwaddr), time); + break; + case ARPHRD_INFINIBAND: + timestamp = unaligned_read_be32 (&digest.llt_infiniband.timestamp); + time -= timestamp % EPOCH_DATETIME_THREE_YEARS; + duid_out = generate_duid_llt (arp_type, digest.llt_infiniband.hwaddr, sizeof (digest.llt_infiniband.hwaddr), time); + break; + default: + g_return_val_if_reached (NULL); + } } else { nm_assert (nm_streq (duid, "stable-uuid")); - duid_out = generate_duid_uuid (sha256_digest, len); + duid_out = generate_duid_uuid (&digest.uuid); } goto out_good; @@ -8410,14 +8479,14 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole out_fail: nm_assert (!duid_out && duid_error); { - guint8 uuid[16]; + NMUuid uuid; _LOGW (LOGD_IP6 | LOGD_DHCP6, "ipv6.dhcp-duid: failure to generate %s DUID: %s. Fallback to random DUID-UUID.", duid, duid_error); - nm_utils_random_bytes (uuid, sizeof (uuid)); - duid_out = generate_duid_uuid (uuid, sizeof (uuid)); + nm_utils_random_bytes (&uuid, sizeof (uuid)); + duid_out = generate_duid_uuid (&uuid); } out_good: @@ -8432,6 +8501,8 @@ out_good: return duid_out; } +/*****************************************************************************/ + static gboolean dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) { @@ -11961,7 +12032,7 @@ nm_device_set_ip_config (NMDevice *self, priv->needs_ip6_subnet = FALSE; } - if (IS_IPv4) { + if (IS_IPv4 && FALSE /* rp_filter handling is disabled */) { if (!nm_device_sys_iface_state_is_external_or_assume (self)) ip4_rp_filter_update (self); } @@ -12859,7 +12930,7 @@ queued_ip_config_change (NMDevice *self, int addr_family) set_unmanaged_external_down (self, TRUE); - if (IS_IPv4) { + if (IS_IPv4 && FALSE /* rp_filter handling is disabled */) { if (!nm_device_sys_iface_state_is_external_or_assume (self)) { priv->v4_has_shadowed_routes = _v4_has_shadowed_routes_detect (self);; ip4_rp_filter_update (self); @@ -14384,9 +14455,11 @@ nm_device_spawn_iface_helper (NMDevice *self) if (client_id) { g_ptr_array_add (argv, g_strdup ("--dhcp4-clientid")); g_ptr_array_add (argv, - _nm_utils_bin2str (g_bytes_get_data (client_id, NULL), - g_bytes_get_size (client_id), - FALSE)); + _nm_utils_bin2hexstr_full (g_bytes_get_data (client_id, NULL), + g_bytes_get_size (client_id), + ':', + FALSE, + NULL)); } hostname = nm_dhcp_client_get_hostname (priv->dhcp4.client); @@ -14424,9 +14497,11 @@ nm_device_spawn_iface_helper (NMDevice *self) if (nm_device_get_ip_iface_identifier (self, &iid, FALSE)) { g_ptr_array_add (argv, g_strdup ("--iid")); g_ptr_array_add (argv, - _nm_utils_bin2str (iid.id_u8, - sizeof (NMUtilsIPv6IfaceId), - FALSE)); + _nm_utils_bin2hexstr_full (iid.id_u8, + sizeof (NMUtilsIPv6IfaceId), + ':', + FALSE, + NULL)); } g_ptr_array_add (argv, g_strdup ("--addr-gen-mode")); @@ -14561,6 +14636,7 @@ _set_state_full (NMDevice *self, NMActRequest *req; gboolean no_firmware = FALSE; NMSettingsConnection *sett_conn; + NMSettingSriov *s_sriov; g_return_if_fail (NM_IS_DEVICE (self)); @@ -14668,6 +14744,7 @@ _set_state_full (NMDevice *self, save_ip6_properties (self); if (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED) ip6_managed_setup (self); + device_init_static_sriov_num_vfs (self); } if (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED) { @@ -14756,6 +14833,12 @@ _set_state_full (NMDevice *self, } break; case NM_DEVICE_STATE_DEACTIVATING: + if ( (s_sriov = (NMSettingSriov *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_SRIOV)) + && priv->ifindex > 0) { + nm_platform_link_set_sriov_params (nm_device_get_platform (self), + priv->ifindex, 0, NM_TERNARY_TRUE); + } + _cancel_activation (self); /* We cache the ignore_carrier state to not react on config-reloads while the connection @@ -15273,7 +15356,7 @@ _get_generate_mac_address_mask_setting (NMDevice *self, NMConnection *connection } a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - is_wifi ? "wifi.generate-mac-address-mask" : "ethernet.generate-mac-mac-address-mask", + is_wifi ? "wifi.generate-mac-address-mask" : "ethernet.generate-mac-address-mask", self); if (!a) return NULL; @@ -15314,7 +15397,8 @@ _hw_addr_set (NMDevice *self, NMPlatformError plerr; guint8 addr_bytes[NM_UTILS_HWADDR_LEN_MAX]; gsize addr_len; - gboolean was_up; + gboolean was_taken_down = FALSE; + gboolean retry_down; nm_assert (NM_IS_DEVICE (self)); nm_assert (addr); @@ -15337,21 +15421,37 @@ _hw_addr_set (NMDevice *self, _LOGT (LOGD_DEVICE, "set-hw-addr: setting MAC address to '%s' (%s, %s)...", addr, operation, detail); - was_up = nm_device_is_up (self); - if (was_up) { - /* Can't change MAC address while device is up */ + if (nm_device_get_device_type (self) == NM_DEVICE_TYPE_WIFI) { + /* Always take the device down for Wi-Fi because + * wpa_supplicant needs it to properly detect the MAC + * change. */ + retry_down = FALSE; + was_taken_down = TRUE; nm_device_take_down (self, FALSE); } +again: plerr = nm_platform_link_set_address (nm_device_get_platform (self), nm_device_get_ip_ifindex (self), addr_bytes, addr_len); success = (plerr == NM_PLATFORM_ERROR_SUCCESS); - if (success) { - /* MAC address succesfully changed; update the current MAC to match */ + if (!success) { + retry_down = !was_taken_down + && plerr != NM_PLATFORM_ERROR_NOT_FOUND + && nm_platform_link_is_up (nm_device_get_platform (self), + nm_device_get_ip_ifindex (self)); + _NMLOG ( retry_down + || plerr == NM_PLATFORM_ERROR_NOT_FOUND + ? LOGL_DEBUG + : LOGL_WARN, + LOGD_DEVICE, + "set-hw-addr: failed to %s MAC address to %s (%s) (%s)%s", + operation, addr, detail, + nm_platform_error_to_string_a (plerr), + retry_down ? " (retry with taking down)" : ""); + } else { + /* MAC address successfully changed; update the current MAC to match */ nm_device_update_hw_address (self); - if (_hw_addr_matches (self, addr_bytes, addr_len)) { - _LOGI (LOGD_DEVICE, "set-hw-addr: %s MAC address to %s (%s)", - operation, addr, detail); - } else { + + if (!_hw_addr_matches (self, addr_bytes, addr_len)) { gint64 poll_end, now; _LOGD (LOGD_DEVICE, @@ -15392,24 +15492,40 @@ handle_fail: success = FALSE; break; } + } - if (success) { - _LOGI (LOGD_DEVICE, "set-hw-addr: %s MAC address to %s (%s)", - operation, addr, detail); - } else { - _LOGW (LOGD_DEVICE, - "set-hw-addr: new MAC address %s not successfully %s (%s)", - addr, operation, detail); - } + if (success) { + retry_down = FALSE; + _LOGI (LOGD_DEVICE, "set-hw-addr: %s MAC address to %s (%s)", + operation, addr, detail); + } else { + retry_down = !was_taken_down + && nm_platform_link_is_up (nm_device_get_platform (self), + nm_device_get_ip_ifindex (self)); + + _NMLOG ( retry_down + ? LOGL_DEBUG + : LOGL_WARN, + LOGD_DEVICE, + "set-hw-addr: new MAC address %s not successfully %s (%s)%s", + addr, + operation, + detail, + retry_down ? " (retry with taking down)" : ""); } - } else { - _NMLOG (plerr == NM_PLATFORM_ERROR_NOT_FOUND ? LOGL_DEBUG : LOGL_WARN, - LOGD_DEVICE, "set-hw-addr: failed to %s MAC address to %s (%s) (%s)", - operation, addr, detail, - nm_platform_error_to_string_a (plerr)); } - if (was_up) { + if (retry_down) { + /* changing the MAC address failed, but also the device was up (and we did not yet try to take + * it down). Optimally, we change the MAC address without taking the device down, but some + * devices don't like that. So, retry with taking the device down. */ + retry_down = FALSE; + was_taken_down = TRUE; + nm_device_take_down (self, FALSE); + goto again; + } + + if (was_taken_down) { if (!nm_device_bring_up (self, TRUE, NULL)) return FALSE; } @@ -15728,7 +15844,8 @@ nm_device_spec_match_list_full (NMDevice *self, const GSList *specs, int no_matc nm_device_get_driver (self), nm_device_get_driver_version (self), nm_device_get_permanent_hw_address (self), - klass->get_s390_subchannels ? klass->get_s390_subchannels (self) : NULL); + klass->get_s390_subchannels ? klass->get_s390_subchannels (self) : NULL, + nm_dhcp_manager_get_config (nm_dhcp_manager_get ())); switch (m) { case NM_MATCH_SPEC_MATCH: diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 8f376b20..3c703564 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -722,6 +722,8 @@ typedef enum { NM_DEVICE_AUTOCONNECT_BLOCKED_WRONG_PIN = (1LL << 1), NM_DEVICE_AUTOCONNECT_BLOCKED_MANUAL_DISCONNECT = (1LL << 2), + NM_DEVICE_AUTOCONNECT_BLOCKED_SIM_MISSING = (1LL << 3), + NM_DEVICE_AUTOCONNECT_BLOCKED_INIT_FAILED = (1LL << 4), _NM_DEVICE_AUTOCONNECT_BLOCKED_LAST, diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c index f637825b..c0484ed3 100644 --- a/src/devices/nm-lldp-listener.c +++ b/src/devices/nm-lldp-listener.c @@ -534,11 +534,13 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error) l = data8[2]; if (len != 3 + l) continue; + if (l > 32) + continue; _lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_VID, _access_uint16 (&data8[0])); _lldp_attr_set_str_ptr (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME, - &data8[3], len); + &data8[3], l); break; } default: diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c index b00397cf..494e2ea0 100644 --- a/src/devices/ovs/nm-ovsdb.c +++ b/src/devices/ovs/nm-ovsdb.c @@ -349,14 +349,14 @@ _insert_interface (json_t *params, NMConnection *interface) if (s_ovs_iface) type = nm_setting_ovs_interface_get_interface_type (s_ovs_iface); - json_array_append (options, json_string ("map")); + json_array_append_new (options, json_string ("map")); s_ovs_patch = nm_connection_get_setting_ovs_patch (interface); if (s_ovs_patch) { - json_array_append (options, json_pack ("[[s, s]]", + json_array_append_new (options, json_pack ("[[s, s]]", "peer", nm_setting_ovs_patch_get_peer (s_ovs_patch))); } else { - json_array_append (options, json_array ()); + json_array_append_new (options, json_array ()); } json_array_append_new (params, @@ -504,12 +504,15 @@ _add_interface (NMOvsdb *self, json_t *params, OpenvswitchBridge *ovs_bridge = NULL; OpenvswitchPort *ovs_port = NULL; OpenvswitchInterface *ovs_interface = NULL; + nm_auto_decref_json json_t *bridges = NULL; + nm_auto_decref_json json_t *new_bridges = NULL; + nm_auto_decref_json json_t *ports = NULL; + nm_auto_decref_json json_t *new_ports = NULL; + nm_auto_decref_json json_t *interfaces = NULL; + nm_auto_decref_json json_t *new_interfaces = NULL; + gboolean has_interface = FALSE; int pi; int ii; - json_t *bridges, *new_bridges; - json_t *ports, *new_ports; - json_t *interfaces, *new_interfaces; - gboolean has_interface = FALSE; bridges = json_array (); ports = json_array (); @@ -585,14 +588,6 @@ _add_interface (NMOvsdb *self, json_t *params, _insert_interface (params, interface); json_array_append_new (new_interfaces, json_pack ("[s, s]", "named-uuid", "rowInterface")); } - - json_decref (interfaces); - json_decref (ports); - json_decref (bridges); - - json_decref (new_interfaces); - json_decref (new_ports); - json_decref (new_bridges); } /** @@ -612,14 +607,13 @@ _delete_interface (NMOvsdb *self, json_t *params, const char *ifname) OpenvswitchBridge *ovs_bridge; OpenvswitchPort *ovs_port; OpenvswitchInterface *ovs_interface; - int pi; - int ii; - json_t *bridges, *new_bridges; - json_t *ports, *new_ports; - json_t *interfaces, *new_interfaces; + nm_auto_decref_json json_t *bridges = NULL; + nm_auto_decref_json json_t *new_bridges = NULL; gboolean bridges_changed; gboolean ports_changed; gboolean interfaces_changed; + int pi; + int ii; bridges = json_array (); new_bridges = json_array (); @@ -627,20 +621,26 @@ _delete_interface (NMOvsdb *self, json_t *params, const char *ifname) g_hash_table_iter_init (&iter, priv->bridges); while (g_hash_table_iter_next (&iter, (gpointer) &bridge_uuid, (gpointer) &ovs_bridge)) { - json_array_append_new (bridges, json_pack ("[s,s]", "uuid", bridge_uuid)); + nm_auto_decref_json json_t *ports = NULL; + nm_auto_decref_json json_t *new_ports = NULL; ports = json_array (); new_ports = json_array (); ports_changed = FALSE; + json_array_append_new (bridges, json_pack ("[s,s]", "uuid", bridge_uuid)); + for (pi = 0; pi < ovs_bridge->ports->len; pi++) { + nm_auto_decref_json json_t *interfaces = NULL; + nm_auto_decref_json json_t *new_interfaces = NULL; + + interfaces = json_array (); + new_interfaces = json_array (); port_uuid = g_ptr_array_index (ovs_bridge->ports, pi); ovs_port = g_hash_table_lookup (priv->ports, port_uuid); json_array_append_new (ports, json_pack ("[s,s]", "uuid", port_uuid)); - interfaces = json_array (); - new_interfaces = json_array (); interfaces_changed = FALSE; for (ii = 0; ii < ovs_port->interfaces->len; ii++) { @@ -667,9 +667,6 @@ _delete_interface (NMOvsdb *self, json_t *params, const char *ifname) } json_array_append_new (new_ports, json_pack ("[s,s]", "uuid", port_uuid)); } - - json_decref (interfaces); - json_decref (new_interfaces); } if (json_array_size (new_ports) == 0) { @@ -681,9 +678,6 @@ _delete_interface (NMOvsdb *self, json_t *params, const char *ifname) } json_array_append_new (new_bridges, json_pack ("[s,s]", "uuid", bridge_uuid)); } - - json_decref (ports); - json_decref (new_ports); } if (bridges_changed) { @@ -708,7 +702,7 @@ ovsdb_next_command (NMOvsdb *self) NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); OvsdbMethodCall *call = NULL; char *cmd; - json_t *msg = NULL; + nm_auto_decref_json json_t *msg = NULL; json_t *params; if (!priv->conn) @@ -764,7 +758,6 @@ ovsdb_next_command (NMOvsdb *self) cmd = json_dumps (msg, 0); g_string_append (priv->output, cmd); - json_decref (msg); free (cmd); ovsdb_write (self); @@ -1041,7 +1034,7 @@ static void ovsdb_got_echo (NMOvsdb *self, json_int_t id, json_t *data) { NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); - json_t *msg; + nm_auto_decref_json json_t *msg = NULL; char *reply; gboolean output_was_empty; @@ -1050,7 +1043,6 @@ ovsdb_got_echo (NMOvsdb *self, json_int_t id, json_t *data) msg = json_pack ("{s:I, s:O}", "id", id, "result", data); reply = json_dumps (msg, 0); g_string_append (priv->output, reply); - json_decref (msg); free (reply); if (output_was_empty) @@ -1300,6 +1292,9 @@ ovsdb_disconnect (NMOvsdb *self, gboolean is_disposing) gpointer user_data; gs_free_error GError *error = NULL; + if (!priv->client) + return; + _LOGD ("disconnecting from ovsdb"); nm_utils_error_set_cancelled (&error, is_disposing, "NMOvsdb"); @@ -1317,6 +1312,7 @@ ovsdb_disconnect (NMOvsdb *self, gboolean is_disposing) g_clear_object (&priv->client); g_clear_object (&priv->conn); g_clear_pointer (&priv->db_uuid, g_free); + nm_clear_g_cancellable (&priv->cancellable); } static void @@ -1535,11 +1531,14 @@ dispose (GObject *object) ovsdb_disconnect (self, TRUE); - g_string_free (priv->input, TRUE); - priv->input = NULL; - g_string_free (priv->output, TRUE); - priv->output = NULL; - + if (priv->input) { + g_string_free (priv->input, TRUE); + priv->input = NULL; + } + if (priv->output) { + g_string_free (priv->output, TRUE); + priv->output = NULL; + } if (priv->calls) { g_array_free (priv->calls, TRUE); priv->calls = NULL; @@ -1549,9 +1548,6 @@ dispose (GObject *object) g_clear_pointer (&priv->ports, g_hash_table_destroy); g_clear_pointer (&priv->interfaces, g_hash_table_destroy); - g_cancellable_cancel (priv->cancellable); - g_clear_object (&priv->cancellable); - G_OBJECT_CLASS (nm_ovsdb_parent_class)->dispose (object); } diff --git a/src/devices/tests/test-lldp.c b/src/devices/tests/test-lldp.c index c2ac8e19..7227d082 100644 --- a/src/devices/tests/test-lldp.c +++ b/src/devices/tests/test-lldp.c @@ -219,11 +219,10 @@ TEST_RECV_FRAME_DEFINE (_test_recv_data1_frame0, 0x01, 0xe8, 0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02, /* IEEE 802.1 - Port and Protocol VLAN ID */ 0x01, 0x00, 0x00, - 0xfe, 0x17, 0x00, 0x80, 0xc2, 0x03, /* IEEE 802.1 - VLAN Name */ - 0x01, 0xe8, 0x10, 0x76, 0x32, 0x2d, + 0xfe, 0x16, 0x00, 0x80, 0xc2, 0x03, /* IEEE 802.1 - VLAN Name */ + 0x01, 0xe8, 0x0f, 0x76, 0x32, 0x2d, 0x30, 0x34, 0x38, 0x38, 0x2d, 0x30, 0x33, 0x2d, 0x30, 0x35, 0x30, 0x35, - 0x00, 0xfe, 0x05, 0x00, 0x80, 0xc2, 0x04, /* IEEE 802.1 - Protocol Identity */ 0x00, 0x00, 0x00 /* End of LLDPDU */ diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c index 9ae81139..4c42e7d3 100644 --- a/src/devices/wifi/nm-device-olpc-mesh.c +++ b/src/devices/wifi/nm-device-olpc-mesh.c @@ -82,7 +82,19 @@ G_DEFINE_TYPE (NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE) static gboolean get_autoconnect_allowed (NMDevice *device) { - return FALSE; + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + /* We shall always have a companion if we're >= DISCONENCTED, and this + * ought not be called until then. */ + g_return_val_if_fail (priv->companion, FALSE); + + /* We must not attempt to autoconnect when the companion is connected or + * connecting, * because we'd tear down its connection. */ + if (nm_device_get_state (priv->companion) > NM_DEVICE_STATE_DISCONNECTED) + return FALSE; + + return TRUE; } #define DEFAULT_SSID "olpc-mesh" @@ -265,6 +277,11 @@ companion_state_changed_cb (NMDeviceWifi *companion, NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); NMDeviceState self_state = nm_device_get_state (NM_DEVICE (self)); + if ( old_state > NM_DEVICE_STATE_DISCONNECTED + && state <= NM_DEVICE_STATE_DISCONNECTED) { + nm_device_emit_recheck_auto_activate (NM_DEVICE (self)); + } + if ( self_state < NM_DEVICE_STATE_PREPARE || self_state > NM_DEVICE_STATE_ACTIVATED || state < NM_DEVICE_STATE_PREPARE diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index 4119d598..8ff931aa 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -125,14 +125,40 @@ modem_prepare_result (NMModem *modem, if (success) nm_device_activate_schedule_stage2_device_config (device); else { - if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT) { - /* If the connect failed because the SIM PIN was wrong don't allow - * the device to be auto-activated anymore, which would risk locking - * the SIM if the incorrect PIN continues to be used. - */ + /* There are several reasons to block autoconnection at device level: + * + * - Wrong SIM-PIN: The device won't autoconnect because it doesn't make sense + * to retry the connection with the same PIN. This error also makes autoconnection + * blocked at settings level, so not even a modem unplug and replug will allow + * autoconnection again. It is somewhat redundant to block autoconnection at + * both device and setting level really. + * + * - SIM wrong or not inserted: If the modem is reporting a SIM not inserted error, + * we can block autoconnection at device level, so that if the same device is + * unplugged and replugged with a SIM (or if a SIM hotplug event happens in MM, + * recreating the device completely), we can try the autoconnection again. + * + * - Modem initialization failed: For some reason unknown to NM, the modem wasn't + * initialized correctly, which leads to an unusable device. A device unplug and + * replug may solve the issue, so make it a device-level autoconnection blocking + * reason. + */ + switch (nm_device_state_reason_check (reason)) { + case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: + case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED: + case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT: nm_device_autoconnect_blocked_set (device, NM_DEVICE_AUTOCONNECT_BLOCKED_WRONG_PIN); + break; + case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED: + case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG: + nm_device_autoconnect_blocked_set (device, NM_DEVICE_AUTOCONNECT_BLOCKED_SIM_MISSING); + break; + case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED: + nm_device_autoconnect_blocked_set (device, NM_DEVICE_AUTOCONNECT_BLOCKED_INIT_FAILED); + break; + default: + break; } - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); } } diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c index 16db8306..37fb18c4 100644 --- a/src/dhcp/nm-dhcp-client.c +++ b/src/dhcp/nm-dhcp-client.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include "nm-utils/nm-dedup-multi.h" @@ -62,6 +61,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDhcpClient, PROP_ROUTE_TABLE, PROP_TIMEOUT, PROP_UUID, + PROP_HOSTNAME, ); typedef struct _NMDhcpClientPrivate { @@ -69,7 +69,6 @@ typedef struct _NMDhcpClientPrivate { char * iface; GBytes * hwaddr; char * uuid; - GBytes * duid; GBytes * client_id; char * hostname; pid_t pid; @@ -139,14 +138,6 @@ nm_dhcp_client_get_uuid (NMDhcpClient *self) return NM_DHCP_CLIENT_GET_PRIVATE (self)->uuid; } -GBytes * -nm_dhcp_client_get_duid (NMDhcpClient *self) -{ - g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE (self)->duid; -} - GBytes * nm_dhcp_client_get_hw_addr (NMDhcpClient *self) { @@ -230,6 +221,18 @@ _set_client_id (NMDhcpClient *self, GBytes *client_id, gboolean take) priv->client_id = client_id; if (!take && client_id) g_bytes_ref (client_id); + + { + gs_free char *s = NULL; + + _LOGT ("%s: set %s", + nm_dhcp_client_get_addr_family (self) == AF_INET6 + ? "duid" + : "client-id", + priv->client_id + ? (s = nm_dhcp_utils_duid_to_string (priv->client_id)) + : "default"); + } } void @@ -362,7 +365,7 @@ nm_dhcp_client_stop_pid (pid_t pid, const char *iface) } static void -stop (NMDhcpClient *self, gboolean release, GBytes *duid) +stop (NMDhcpClient *self, gboolean release) { NMDhcpClientPrivate *priv; @@ -509,7 +512,6 @@ gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self, GBytes *client_id, const char *dhcp_anycast_addr, - const char *hostname, const char *last_ip4_address, GError **error) { @@ -529,9 +531,6 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self, nm_dhcp_client_set_client_id (self, client_id); - g_clear_pointer (&priv->hostname, g_free); - priv->hostname = g_strdup (hostname); - return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_anycast_addr, last_ip4_address, @@ -550,34 +549,29 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self, gboolean enforce_duid, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, - const char *hostname, NMSettingIP6ConfigPrivacy privacy, guint needed_prefixes, GError **error) { NMDhcpClientPrivate *priv; - gs_free char *str = NULL; + gs_unref_bytes GBytes *own_client_id = NULL; g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + g_return_val_if_fail (client_id, FALSE); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + g_return_val_if_fail (priv->pid == -1, FALSE); g_return_val_if_fail (priv->addr_family == AF_INET6, FALSE); g_return_val_if_fail (priv->uuid != NULL, FALSE); - - nm_assert (!priv->duid); - nm_assert (client_id); + g_return_val_if_fail (!priv->client_id, FALSE); if (!enforce_duid) - priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self); - - if (!priv->duid) - priv->duid = g_bytes_ref (client_id); - - _LOGD ("DUID is '%s'", (str = nm_dhcp_utils_duid_to_string (priv->duid))); + own_client_id = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self); - g_clear_pointer (&priv->hostname, g_free); - priv->hostname = g_strdup (hostname); + _set_client_id (self, + own_client_id ?: client_id, + FALSE); if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) _LOGI ("activation: beginning transaction (no timeout)"); @@ -588,7 +582,6 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self, dhcp_anycast_addr, ll_addr, privacy, - priv->duid, needed_prefixes, error); } @@ -655,7 +648,7 @@ nm_dhcp_client_stop (NMDhcpClient *self, gboolean release) /* Kill the DHCP client */ old_pid = priv->pid; - NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release, priv->duid); + NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release); if (old_pid > 0) _LOGI ("canceled DHCP transaction, DHCP client pid %d", old_pid); else @@ -859,6 +852,9 @@ get_property (GObject *object, guint prop_id, case PROP_UUID: g_value_set_string (value, priv->uuid); break; + case PROP_HOSTNAME: + g_value_set_string (value, priv->hostname); + break; case PROP_ROUTE_METRIC: g_value_set_uint (value, priv->route_metric); break; @@ -899,11 +895,13 @@ set_property (GObject *object, guint prop_id, case PROP_IFACE: /* construct-only */ priv->iface = g_value_dup_string (value); + g_return_if_fail ( priv->iface + && nm_utils_is_valid_iface_name (priv->iface, NULL)); break; case PROP_IFINDEX: /* construct-only */ priv->ifindex = g_value_get_int (value); - g_warn_if_fail (priv->ifindex > 0); + g_return_if_fail (priv->ifindex > 0); break; case PROP_HWADDR: /* construct-only */ @@ -919,6 +917,10 @@ set_property (GObject *object, guint prop_id, /* construct-only */ priv->uuid = g_value_dup_string (value); break; + case PROP_HOSTNAME: + /* construct-only */ + priv->hostname = g_value_dup_string (value); + break; case PROP_ROUTE_TABLE: priv->route_table = g_value_get_uint (value); break; @@ -971,7 +973,6 @@ dispose (GObject *object) g_clear_pointer (&priv->uuid, g_free); g_clear_pointer (&priv->client_id, g_bytes_unref); g_clear_pointer (&priv->hwaddr, g_bytes_unref); - g_clear_pointer (&priv->duid, g_bytes_unref); G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object); @@ -1028,6 +1029,12 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_HOSTNAME] = + g_param_spec_string (NM_DHCP_CLIENT_HOSTNAME, "", "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_ROUTE_TABLE] = g_param_spec_uint (NM_DHCP_CLIENT_ROUTE_TABLE, "", "", 0, G_MAXUINT32, RT_TABLE_MAIN, @@ -1058,7 +1065,7 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) g_signal_new (NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMDhcpClientClass, state_changed), + 0, NULL, NULL, NULL, G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_OBJECT, G_TYPE_HASH_TABLE, G_TYPE_STRING); @@ -1066,7 +1073,7 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) g_signal_new (NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMDhcpClientClass, state_changed), + 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER); } diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h index 86d60e38..8be50717 100644 --- a/src/dhcp/nm-dhcp-client.h +++ b/src/dhcp/nm-dhcp-client.h @@ -41,6 +41,7 @@ #define NM_DHCP_CLIENT_IFINDEX "ifindex" #define NM_DHCP_CLIENT_INTERFACE "iface" #define NM_DHCP_CLIENT_MULTI_IDX "multi-idx" +#define NM_DHCP_CLIENT_HOSTNAME "hostname" #define NM_DHCP_CLIENT_ROUTE_METRIC "route-metric" #define NM_DHCP_CLIENT_ROUTE_TABLE "route-table" #define NM_DHCP_CLIENT_TIMEOUT "timeout" @@ -85,13 +86,11 @@ typedef struct { const char *anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error); void (*stop) (NMDhcpClient *self, - gboolean release, - GBytes *duid); + gboolean release); /** * get_duid: @@ -103,12 +102,6 @@ typedef struct { * returned. */ GBytes *(*get_duid) (NMDhcpClient *self); - - /* Signals */ - void (*state_changed) (NMDhcpClient *self, - NMDhcpState state, - GObject *ip_config, - GHashTable *options); } NMDhcpClientClass; GType nm_dhcp_client_get_type (void); @@ -150,7 +143,6 @@ gboolean nm_dhcp_client_get_use_fqdn (NMDhcpClient *self); gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self, GBytes *client_id, const char *dhcp_anycast_addr, - const char *hostname, const char *last_ip4_address, GError **error); @@ -159,7 +151,6 @@ gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self, gboolean enforce_duid, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, - const char *hostname, NMSettingIP6ConfigPrivacy privacy, guint needed_prefixes, GError **error); diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c index a2c3bfb6..be8d06d9 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.c +++ b/src/dhcp/nm-dhcp-dhclient-utils.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "nm-utils/nm-dedup-multi.h" @@ -233,29 +234,6 @@ read_client_id (const char *str) return nm_utils_hexstr2bin (s); } -GBytes * -nm_dhcp_dhclient_get_client_id_from_config_file (const char *path) -{ - gs_free char *contents = NULL; - gs_strfreev char **lines = NULL; - char **line; - - g_return_val_if_fail (path != NULL, NULL); - - if (!g_file_test (path, G_FILE_TEST_EXISTS)) - return NULL; - - if (!g_file_get_contents (path, &contents, NULL, NULL)) - return NULL; - - lines = g_strsplit_set (contents, "\n\r", 0); - for (line = lines; lines && *line; line++) { - if (!strncmp (*line, CLIENTID_TAG, NM_STRLEN (CLIENTID_TAG))) - return read_client_id (*line); - } - return NULL; -} - static gboolean read_interface (const char *line, char *interface, guint size) { @@ -570,6 +548,7 @@ error: #define DUID_PREFIX "default-duid \"" +/* Beware: @error may be unset even if the function returns %NULL. */ GBytes * nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error) { @@ -606,9 +585,10 @@ nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error) gboolean nm_dhcp_dhclient_save_duid (const char *leasefile, - const char *escaped_duid, + GBytes *duid, GError **error) { + gs_free char *escaped_duid = NULL; gs_strfreev char **lines = NULL; char **iter, *l; GString *s; @@ -616,6 +596,14 @@ nm_dhcp_dhclient_save_duid (const char *leasefile, gsize len = 0; g_return_val_if_fail (leasefile != NULL, FALSE); + + if (!duid) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, + "missing duid"); + g_return_val_if_reached (FALSE); + } + + escaped_duid = nm_dhcp_dhclient_escape_duid (duid); g_return_val_if_fail (escaped_duid != NULL, FALSE); if (g_file_test (leasefile, G_FILE_TEST_EXISTS)) { diff --git a/src/dhcp/nm-dhcp-dhclient-utils.h b/src/dhcp/nm-dhcp-dhclient-utils.h index fab9196a..57a711db 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.h +++ b/src/dhcp/nm-dhcp-dhclient-utils.h @@ -40,10 +40,7 @@ GBytes *nm_dhcp_dhclient_unescape_duid (const char *duid); GBytes *nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error); gboolean nm_dhcp_dhclient_save_duid (const char *leasefile, - const char *escaped_duid, + GBytes *duid, GError **error); -GBytes *nm_dhcp_dhclient_get_client_id_from_config_file (const char *path); - #endif /* __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__ */ - diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c index 33c26712..0146c8b4 100644 --- a/src/dhcp/nm-dhcp-dhclient.c +++ b/src/dhcp/nm-dhcp-dhclient.c @@ -121,8 +121,8 @@ get_dhclient_leasefile (int addr_family, const char *uuid, char **out_preferred_path) { - char *rundir_path; - char *path; + gs_free char *rundir_path = NULL; + gs_free char *path = NULL; /* First, see if the lease file is in /run */ rundir_path = g_strdup_printf (NMRUNDIR "/dhclient%s-%s-%s.lease", @@ -132,7 +132,7 @@ get_dhclient_leasefile (int addr_family, if (g_file_test (rundir_path, G_FILE_TEST_EXISTS)) { NM_SET_OUT (out_preferred_path, g_strdup (rundir_path)); - return rundir_path; + return g_steal_pointer (&rundir_path); } /* /var/lib/NetworkManager is the preferred leasefile path */ @@ -142,18 +142,14 @@ get_dhclient_leasefile (int addr_family, iface); if (g_file_test (path, G_FILE_TEST_EXISTS)) { - g_free (rundir_path); NM_SET_OUT (out_preferred_path, g_strdup (path)); - return path; + return g_steal_pointer (&path); } - if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD) { - g_free (path); - path = rundir_path; - } else { - g_free (rundir_path); - } - NM_SET_OUT (out_preferred_path, g_steal_pointer (&path)); + if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD) + NM_SET_OUT (out_preferred_path, g_steal_pointer (&rundir_path)); + else + NM_SET_OUT (out_preferred_path, g_steal_pointer (&path)); /* If the leasefile we're looking for doesn't exist yet in the new location * (eg, /var/lib/NetworkManager) then look in old locations to maintain @@ -166,17 +162,16 @@ get_dhclient_leasefile (int addr_family, path = g_strdup_printf (LOCALSTATEDIR "/lib/dhcp/dhclient%s-%s-%s.lease", _addr_family_to_path_part (addr_family), uuid, iface); if (g_file_test (path, G_FILE_TEST_EXISTS)) - return path; + return g_steal_pointer (&path); /* Old Red Hat and Fedora location */ g_free (path); path = g_strdup_printf (LOCALSTATEDIR "/lib/dhclient/dhclient%s-%s-%s.lease", _addr_family_to_path_part (addr_family), uuid, iface); if (g_file_test (path, G_FILE_TEST_EXISTS)) - return path; + return g_steal_pointer (&path); /* Fail */ - g_free (path); return NULL; } @@ -327,8 +322,18 @@ create_dhclient_config (NMDhcpDhclient *self, else _LOGD ("no existing dhclient configuration to merge"); - if (!merge_dhclient_config (self, addr_family, iface, new, client_id, dhcp_anycast_addr, - hostname, timeout, use_fqdn, orig, out_new_client_id, &error)) { + if (!merge_dhclient_config (self, + addr_family, + iface, + new, + client_id, + dhcp_anycast_addr, + hostname, + timeout, + use_fqdn, + orig, + out_new_client_id, + &error)) { _LOGW ("error creating dhclient configuration: %s", error->message); g_clear_error (&error); } @@ -339,7 +344,6 @@ create_dhclient_config (NMDhcpDhclient *self, static gboolean dhclient_start (NMDhcpClient *client, const char *mode_opt, - GBytes *duid, gboolean release, pid_t *out_pid, int prefixes, @@ -418,10 +422,9 @@ dhclient_start (NMDhcpClient *client, /* Save the DUID to the leasefile dhclient will actually use */ if (addr_family == AF_INET6) { - gs_free char *escaped = NULL; - - escaped = nm_dhcp_dhclient_escape_duid (duid); - if (!nm_dhcp_dhclient_save_duid (priv->lease_file, escaped, &local)) { + if (!nm_dhcp_dhclient_save_duid (priv->lease_file, + nm_dhcp_client_get_client_id (client), + &local)) { nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "failed to save DUID to '%s': %s", @@ -540,7 +543,6 @@ ip4_start (NMDhcpClient *client, nm_dhcp_client_set_client_id (client, new_client_id); } return dhclient_start (client, - NULL, NULL, FALSE, NULL, @@ -553,7 +555,6 @@ ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error) { @@ -581,7 +582,6 @@ ip6_start (NMDhcpClient *client, nm_dhcp_client_get_info_only (NM_DHCP_CLIENT (self)) ? "-S" : "-N", - duid, FALSE, NULL, needed_prefixes, @@ -589,12 +589,12 @@ ip6_start (NMDhcpClient *client, } static void -stop (NMDhcpClient *client, gboolean release, GBytes *duid) +stop (NMDhcpClient *client, gboolean release) { NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client); NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self); - NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release, duid); + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release); if (priv->conf_file) if (remove (priv->conf_file) == -1) @@ -613,7 +613,6 @@ stop (NMDhcpClient *client, gboolean release, GBytes *duid) if (dhclient_start (client, NULL, - duid, TRUE, &rpid, 0, @@ -624,31 +623,13 @@ stop (NMDhcpClient *client, gboolean release, GBytes *duid) } } -static void -state_changed (NMDhcpClient *client, - NMDhcpState state, - GObject *ip_config, - GHashTable *options) -{ - NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE ((NMDhcpDhclient *) client); - gs_unref_bytes GBytes *client_id = NULL; - - if (nm_dhcp_client_get_client_id (client)) - return; - if (state != NM_DHCP_STATE_BOUND) - return; - - client_id = nm_dhcp_dhclient_get_client_id_from_config_file (priv->conf_file); - nm_dhcp_client_set_client_id (client, client_id); -} - static GBytes * get_duid (NMDhcpClient *client) { NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client); NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self); GBytes *duid = NULL; - char *leasefile; + gs_free char *leasefile = NULL; GError *error = NULL; /* Look in interface-specific leasefile first for backwards compat */ @@ -659,25 +640,23 @@ get_duid (NMDhcpClient *client) if (leasefile) { _LOGD ("looking for DUID in '%s'", leasefile); duid = nm_dhcp_dhclient_read_duid (leasefile, &error); - if (error) { _LOGW ("failed to read leasefile '%s': %s", leasefile, error->message); g_clear_error (&error); } - g_free (leasefile); + if (duid) + return duid; } - if (!duid) { - /* Otherwise read the default machine-wide DUID */ - _LOGD ("looking for default DUID in '%s'", priv->def_leasefile); - duid = nm_dhcp_dhclient_read_duid (priv->def_leasefile, &error); - if (error) { - _LOGW ("failed to read leasefile '%s': %s", - priv->def_leasefile, - error->message); - g_clear_error (&error); - } + /* Otherwise read the default machine-wide DUID */ + _LOGD ("looking for default DUID in '%s'", priv->def_leasefile); + duid = nm_dhcp_dhclient_read_duid (priv->def_leasefile, &error); + if (error) { + _LOGW ("failed to read leasefile '%s': %s", + priv->def_leasefile, + error->message); + g_clear_error (&error); } return duid; @@ -742,7 +721,6 @@ nm_dhcp_dhclient_class_init (NMDhcpDhclientClass *dhclient_class) client_class->ip6_start = ip6_start; client_class->stop = stop; client_class->get_duid = get_duid; - client_class->state_changed = state_changed; } const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient = { diff --git a/src/dhcp/nm-dhcp-dhcpcanon.c b/src/dhcp/nm-dhcp-dhcpcanon.c index de403020..0f033e22 100644 --- a/src/dhcp/nm-dhcp-dhcpcanon.c +++ b/src/dhcp/nm-dhcp-dhcpcanon.c @@ -193,20 +193,20 @@ ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "dhcpcanon plugin does not support IPv6"); return FALSE; } + static void -stop (NMDhcpClient *client, gboolean release, GBytes *duid) +stop (NMDhcpClient *client, gboolean release) { NMDhcpDhcpcanon *self = NM_DHCP_DHCPCANON (client); NMDhcpDhcpcanonPrivate *priv = NM_DHCP_DHCPCANON_GET_PRIVATE (self); - NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcanon_parent_class)->stop (client, release, duid); + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcanon_parent_class)->stop (client, release); if (priv->pid_file) { if (remove (priv->pid_file) == -1) @@ -216,18 +216,6 @@ stop (NMDhcpClient *client, gboolean release, GBytes *duid) } } -static void -state_changed (NMDhcpClient *client, - NMDhcpState state, - GObject *ip_config, - GHashTable *options) -{ - if (nm_dhcp_client_get_client_id (client)) - return; - if (state != NM_DHCP_STATE_BOUND) - return; -} - /*****************************************************************************/ static void @@ -270,7 +258,6 @@ nm_dhcp_dhcpcanon_class_init (NMDhcpDhcpcanonClass *dhcpcanon_class) client_class->ip4_start = ip4_start; client_class->ip6_start = ip6_start; client_class->stop = stop; - client_class->state_changed = state_changed; } const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon = { diff --git a/src/dhcp/nm-dhcp-dhcpcd.c b/src/dhcp/nm-dhcp-dhcpcd.c index 98ab5342..e2a1354f 100644 --- a/src/dhcp/nm-dhcp-dhcpcd.c +++ b/src/dhcp/nm-dhcp-dhcpcd.c @@ -187,7 +187,6 @@ ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error) { @@ -196,12 +195,12 @@ ip6_start (NMDhcpClient *client, } static void -stop (NMDhcpClient *client, gboolean release, GBytes *duid) +stop (NMDhcpClient *client, gboolean release) { NMDhcpDhcpcd *self = NM_DHCP_DHCPCD (client); NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (self); - NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client, release, duid); + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client, release); if (priv->pid_file) { if (remove (priv->pid_file) == -1) diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c index 5ae16d72..a51c6e38 100644 --- a/src/dhcp/nm-dhcp-manager.c +++ b/src/dhcp/nm-dhcp-manager.c @@ -181,6 +181,7 @@ client_start (NMDhcpManager *self, gsize hwaddr_len; g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); + g_return_val_if_fail (iface, NULL); g_return_val_if_fail (ifindex > 0, NULL); g_return_val_if_fail (uuid != NULL, NULL); g_return_val_if_fail (!dhcp_client_id || g_bytes_get_size (dhcp_client_id) >= 2, NULL); @@ -221,6 +222,7 @@ client_start (NMDhcpManager *self, NM_DHCP_CLIENT_IFINDEX, ifindex, NM_DHCP_CLIENT_HWADDR, hwaddr, NM_DHCP_CLIENT_UUID, uuid, + NM_DHCP_CLIENT_HOSTNAME, hostname, NM_DHCP_CLIENT_ROUTE_TABLE, (guint) route_table, NM_DHCP_CLIENT_ROUTE_METRIC, (guint) route_metric, NM_DHCP_CLIENT_TIMEOUT, (guint) timeout, @@ -233,11 +235,36 @@ client_start (NMDhcpManager *self, c_list_link_tail (&priv->dhcp_client_lst_head, &client->dhcp_client_lst); g_signal_connect (client, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (client_state_changed), self); + /* unfortunately, our implementations work differently per address-family regarding client-id/DUID. + * + * - for IPv4, the calling code may determine a client-id (from NM's connection profile). + * If present, it is taken. If not present, the DHCP plugin uses a plugin specific default. + * - for "internal" plugin, the default is just "duid". + * - for "dhclient", we try to get the configuration from dhclient's /etc/dhcp or fallback + * to whatever dhclient uses by default. + * We do it this way, because for dhclient the user may configure a default + * outside of NM, and we want to honor that. Worse, dhclient could be a wapper + * script where the wrapper script overwrites the client-id. We need to distinguish + * between: force a particular client-id and leave it unspecified to whatever dhclient + * wants. + * + * - for IPv6, the calling code always determines a client-id. It also specifies @enforce_duid, + * to determine whether the given client-id must be used. + * - for "internal" plugin @enforce_duid doesn't matter and the given client-id is + * always used. + * - for "dhclient", @enforce_duid FALSE means to first try to load the DUID from the + * lease file, and only otherwise fallback to the given client-id. + * - other plugins don't support DHCPv6. + * It's done this way, so that existing dhclient setups don't change behavior on upgrade. + * + * This difference is cumbersome and only exists because of "dhclient" which supports hacking the + * default outside of NetworkManager API. + */ + if (addr_family == AF_INET) { success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, - hostname, last_ip4_address, error); } else { @@ -246,7 +273,6 @@ client_start (NMDhcpManager *self, enforce_duid, dhcp_anycast_addr, ipv6_ll_addr, - hostname, privacy, needed_prefixes, error); @@ -311,10 +337,27 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self, } } - return client_start (self, AF_INET, multi_idx, iface, ifindex, hwaddr, uuid, - route_table, route_metric, NULL, - dhcp_client_id, 0, timeout, dhcp_anycast_addr, hostname, - use_fqdn, FALSE, 0, last_ip_address, 0, error); + return client_start (self, + AF_INET, + multi_idx, + iface, + ifindex, + hwaddr, + uuid, + route_table, + route_metric, + NULL, + dhcp_client_id, + FALSE, + timeout, + dhcp_anycast_addr, + hostname, + use_fqdn, + FALSE, + 0, + last_ip_address, + 0, + error); } /* Caller owns a reference to the NMDhcpClient on return */ @@ -349,10 +392,27 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self, /* Always prefer the explicit dhcp-hostname if given */ hostname = dhcp_hostname ?: priv->default_hostname; } - return client_start (self, AF_INET6, multi_idx, iface, ifindex, hwaddr, uuid, - route_table, route_metric, ll_addr, duid, enforce_duid, - timeout, dhcp_anycast_addr, hostname, TRUE, info_only, - privacy, NULL, needed_prefixes, error); + return client_start (self, + AF_INET6, + multi_idx, + iface, + ifindex, + hwaddr, + uuid, + route_table, + route_metric, + ll_addr, + duid, + enforce_duid, + timeout, + dhcp_anycast_addr, + hostname, + TRUE, + info_only, + privacy, + NULL, + needed_prefixes, + error); } void @@ -384,6 +444,12 @@ nm_dhcp_manager_get_config (NMDhcpManager *self) NM_DEFINE_SINGLETON_GETTER (NMDhcpManager, nm_dhcp_manager_get, NM_TYPE_DHCP_MANAGER); +void +nmtst_dhcp_manager_unget (gpointer self) +{ + _nmtst_nm_dhcp_manager_get_reset (self); +} + static void nm_dhcp_manager_init (NMDhcpManager *self) { @@ -446,6 +512,10 @@ nm_dhcp_manager_init (NMDhcpManager *self) nm_log_info (LOGD_DHCP, "dhcp-init: Using DHCP client '%s'", client_factory->name); + /* NOTE: currently the DHCP plugin is chosen once at start. It's not + * possible to reload that configuration. If that ever becomes possible, + * beware that the "dhcp-plugin" device spec made decisions based on + * the previous plugin and may need reevaluation. */ priv->client_factory = client_factory; } diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h index 1d9e5c21..f8f39e53 100644 --- a/src/dhcp/nm-dhcp-manager.h +++ b/src/dhcp/nm-dhcp-manager.h @@ -87,4 +87,6 @@ extern const char* nm_dhcp_helper_path; extern const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4]; +void nmtst_dhcp_manager_unget (gpointer singleton_instance); + #endif /* __NETWORKMANAGER_DHCP_MANAGER_H__ */ diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c index 5b7b5fbe..5c60af5f 100644 --- a/src/dhcp/nm-dhcp-systemd.c +++ b/src/dhcp/nm-dhcp-systemd.c @@ -34,6 +34,7 @@ #include "nm-utils.h" #include "nm-config.h" #include "nm-dhcp-utils.h" +#include "nm-core-utils.h" #include "NetworkManagerUtils.h" #include "platform/nm-platform.h" #include "nm-dhcp-client-logging.h" @@ -482,22 +483,6 @@ get_leasefile_path (int addr_family, const char *iface, const char *uuid) /*****************************************************************************/ -static void -_save_client_id (NMDhcpSystemd *self, - uint8_t type, - const uint8_t *client_id, - size_t len) -{ - g_return_if_fail (self != NULL); - g_return_if_fail (client_id != NULL); - g_return_if_fail (len > 0); - - if (!nm_dhcp_client_get_client_id (NM_DHCP_CLIENT (self))) { - nm_dhcp_client_set_client_id_bin (NM_DHCP_CLIENT (self), - type, client_id, len); - } -} - static void bound4_handle (NMDhcpSystemd *self) { @@ -529,17 +514,9 @@ bound4_handle (NMDhcpSystemd *self) TRUE, &error); if (ip4_config) { - const uint8_t *client_id = NULL; - size_t client_id_len = 0; - uint8_t type = 0; - add_requests_to_options (options, dhcp4_requests); dhcp_lease_save (lease, priv->lease_file); - sd_dhcp_client_get_client_id (priv->client4, &type, &client_id, &client_id_len); - if (client_id) - _save_client_id (self, type, client_id, client_id_len); - nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_BOUND, NM_IP_CONFIG_CAST (ip4_config), @@ -582,127 +559,122 @@ dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data) } } -static guint16 -get_arp_type (GBytes *hwaddr) -{ - switch (g_bytes_get_size (hwaddr)) { - case ETH_ALEN: - return ARPHRD_ETHER; - case INFINIBAND_ALEN: - return ARPHRD_INFINIBAND; - default: - return ARPHRD_NONE; - } -} - static gboolean ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address, GError **error) { + nm_auto (sd_dhcp_client_unrefp) sd_dhcp_client *sd_client = NULL; NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); - const char *iface = nm_dhcp_client_get_iface (client); + gs_free char *lease_file = NULL; GBytes *hwaddr; - sd_dhcp_lease *lease = NULL; - GBytes *override_client_id; - const uint8_t *client_id = NULL; - size_t client_id_len = 0; + const uint8_t *hwaddr_arr; + gsize hwaddr_len; + int arp_type; + GBytes *client_id; + gs_unref_bytes GBytes *client_id_new = NULL; + const uint8_t *client_id_arr; + size_t client_id_len; struct in_addr last_addr = { 0 }; const char *hostname; int r, i; - gboolean success = FALSE; - g_assert (priv->client4 == NULL); - g_assert (priv->client6 == NULL); - - g_free (priv->lease_file); - priv->lease_file = get_leasefile_path (AF_INET, iface, nm_dhcp_client_get_uuid (client)); + g_return_val_if_fail (!priv->client4, FALSE); + g_return_val_if_fail (!priv->client6, FALSE); - r = sd_dhcp_client_new (&priv->client4, FALSE); + r = sd_dhcp_client_new (&sd_client, FALSE); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to create dhcp-client: %s"); return FALSE; } - _LOGT ("dhcp-client4: set %p", priv->client4); + _LOGT ("dhcp-client4: set %p", sd_client); - r = sd_dhcp_client_attach_event (priv->client4, NULL, 0); + r = sd_dhcp_client_attach_event (sd_client, NULL, 0); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to attach event: %s"); - goto errout; + return FALSE; } hwaddr = nm_dhcp_client_get_hw_addr (client); - if (hwaddr) { - const uint8_t *data; - gsize len; - - data = g_bytes_get_data (hwaddr, &len); - r = sd_dhcp_client_set_mac (priv->client4, - data, - len, - get_arp_type (hwaddr)); - if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set MAC address: %s"); - goto errout; - } + if ( !hwaddr + || !(hwaddr_arr = g_bytes_get_data (hwaddr, &hwaddr_len)) + || (arp_type = nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len)) < 0) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "invalid MAC address"); + return FALSE; } - - r = sd_dhcp_client_set_ifindex (priv->client4, nm_dhcp_client_get_ifindex (client)); + r = sd_dhcp_client_set_mac (sd_client, + hwaddr_arr, + hwaddr_len, + (guint16) arp_type); if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set ifindex: %s"); - goto errout; + nm_utils_error_set_errno (error, r, "failed to set MAC address: %s"); + return FALSE; } - r = sd_dhcp_client_set_callback (priv->client4, dhcp_event_cb, client); + r = sd_dhcp_client_set_ifindex (sd_client, + nm_dhcp_client_get_ifindex (client)); if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set callback: %s"); - goto errout; + nm_utils_error_set_errno (error, r, "failed to set ifindex: %s"); + return FALSE; } - dhcp_lease_load (&lease, priv->lease_file); + lease_file = get_leasefile_path (AF_INET, + nm_dhcp_client_get_iface (client), + nm_dhcp_client_get_uuid (client)); if (last_ip4_address) inet_pton (AF_INET, last_ip4_address, &last_addr); - else if (lease) - sd_dhcp_lease_get_address (lease, &last_addr); + else { + nm_auto (sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; + + dhcp_lease_load (&lease, lease_file); + if (lease) + sd_dhcp_lease_get_address (lease, &last_addr); + } if (last_addr.s_addr) { - r = sd_dhcp_client_set_request_address (priv->client4, &last_addr); + r = sd_dhcp_client_set_request_address (sd_client, &last_addr); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to set last IPv4 address: %s"); - goto errout; + return FALSE; } } - override_client_id = nm_dhcp_client_get_client_id (client); - if (override_client_id) { - client_id = g_bytes_get_data (override_client_id, &client_id_len); - nm_assert (client_id && client_id_len >= 2); - sd_dhcp_client_set_client_id (priv->client4, - client_id[0], - client_id + 1, - NM_MIN (client_id_len - 1, _NM_SD_MAX_CLIENT_ID_LEN)); - } else if (lease) { - r = sd_dhcp_lease_get_client_id (lease, (const void **) &client_id, &client_id_len); - if (r == 0 && client_id_len >= 2) { - sd_dhcp_client_set_client_id (priv->client4, - client_id[0], - client_id + 1, - client_id_len - 1); - _save_client_id (NM_DHCP_SYSTEMD (client), - client_id[0], - client_id + 1, - client_id_len - 1); - } + client_id = nm_dhcp_client_get_client_id (client); + if (!client_id) { + client_id_new = nm_utils_dhcp_client_id_systemd_node_specific (TRUE, + nm_dhcp_client_get_iface (client)); + client_id = client_id_new; + } + + if ( !(client_id_arr = g_bytes_get_data (client_id, &client_id_len)) + || client_id_len < 2) { + + /* invalid client-ids are not expected. */ + nm_assert_not_reached (); + + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "no valid IPv4 client-id"); + return FALSE; + } + + /* Note that we always set a client-id. In particular for infiniband that is necessary, + * see https://tools.ietf.org/html/rfc4390#section-2.1 . */ + r = sd_dhcp_client_set_client_id (sd_client, + client_id_arr[0], + client_id_arr + 1, + NM_MIN (client_id_len - 1, _NM_SD_MAX_CLIENT_ID_LEN)); + if (r < 0) { + nm_utils_error_set_errno (error, r, "failed to set IPv4 client-id: %s"); + return FALSE; } /* Add requested options */ for (i = 0; dhcp4_requests[i].name; i++) { if (dhcp4_requests[i].include) - sd_dhcp_client_set_request_option (priv->client4, dhcp4_requests[i].num); + sd_dhcp_client_set_request_option (sd_client, dhcp4_requests[i].num); } hostname = nm_dhcp_client_get_hostname (client); @@ -711,28 +683,36 @@ ip4_start (NMDhcpClient *client, * only based on whether the hostname has a domain part or not. At the * moment there is no way to force one or another. */ - r = sd_dhcp_client_set_hostname (priv->client4, hostname); + r = sd_dhcp_client_set_hostname (sd_client, hostname); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to set DHCP hostname: %s"); - goto errout; + return FALSE; } } + r = sd_dhcp_client_set_callback (sd_client, dhcp_event_cb, client); + if (r < 0) { + nm_utils_error_set_errno (error, r, "failed to set callback: %s"); + return FALSE; + } + + priv->client4 = g_steal_pointer (&sd_client); + + g_free (priv->lease_file); + priv->lease_file = g_steal_pointer (&lease_file); + + nm_dhcp_client_set_client_id (client, client_id); + r = sd_dhcp_client_start (priv->client4); if (r < 0) { + sd_dhcp_client_set_callback (priv->client4, NULL, NULL); + nm_clear_pointer (&priv->client4, sd_dhcp_client_unref); nm_utils_error_set_errno (error, r, "failed to start DHCP client: %s"); - goto errout; + return FALSE; } nm_dhcp_client_start_timeout (client); - - success = TRUE; - -errout: - sd_dhcp_lease_unref (lease); - if (!success) - sd_dhcp_client_unref (g_steal_pointer (&priv->client4)); - return success; + return TRUE; } static NMIP6Config * @@ -895,31 +875,33 @@ ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error) { NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); - const char *iface = nm_dhcp_client_get_iface (client); + nm_auto (sd_dhcp6_client_unrefp) sd_dhcp6_client *sd_client = NULL; GBytes *hwaddr; const char *hostname; int r, i; const guint8 *duid_arr; gsize duid_len; - - g_assert (priv->client4 == NULL); - g_assert (priv->client6 == NULL); - g_return_val_if_fail (duid != NULL, FALSE); - - duid_arr = g_bytes_get_data (duid, &duid_len); - if (!duid_arr || duid_len < 2) + GBytes *duid; + const uint8_t *hwaddr_arr; + gsize hwaddr_len; + int arp_type; + + g_return_val_if_fail (!priv->client4, FALSE); + g_return_val_if_fail (!priv->client6, FALSE); + + if ( !(duid = nm_dhcp_client_get_client_id (client)) + || !(duid_arr = g_bytes_get_data (duid, &duid_len)) + || duid_len < 2) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "missing DUID"); g_return_val_if_reached (FALSE); + } - g_free (priv->lease_file); - priv->lease_file = get_leasefile_path (AF_INET6, iface, nm_dhcp_client_get_uuid (client)); - - r = sd_dhcp6_client_new (&priv->client6); + r = sd_dhcp6_client_new (&sd_client); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to create dhcp-client: %s"); return FALSE; @@ -930,12 +912,12 @@ ip6_start (NMDhcpClient *client, needed_prefixes); } - _LOGT ("dhcp-client6: set %p", priv->client6); + _LOGT ("dhcp-client6: set %p", sd_client); if (nm_dhcp_client_get_info_only (client)) - sd_dhcp6_client_set_information_request (priv->client6, 1); + sd_dhcp6_client_set_information_request (sd_client, 1); - r = sd_dhcp6_client_set_duid (priv->client6, + r = sd_dhcp6_client_set_duid (sd_client, unaligned_read_be16 (&duid_arr[0]), &duid_arr[2], duid_len - 2); @@ -944,82 +926,82 @@ ip6_start (NMDhcpClient *client, return FALSE; } - r = sd_dhcp6_client_attach_event (priv->client6, NULL, 0); + r = sd_dhcp6_client_attach_event (sd_client, NULL, 0); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to attach event: %s"); - goto errout; + return FALSE; } hwaddr = nm_dhcp_client_get_hw_addr (client); - if (hwaddr) { - const uint8_t *data; - gsize len; - - data = g_bytes_get_data (hwaddr, &len); - r = sd_dhcp6_client_set_mac (priv->client6, - data, - len, - get_arp_type (hwaddr)); - if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set MAC address: %s"); - goto errout; - } + if ( !hwaddr + || !(hwaddr_arr = g_bytes_get_data (hwaddr, &hwaddr_len)) + || (arp_type = nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len)) < 0) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "invalid MAC address"); + return FALSE; } - - r = sd_dhcp6_client_set_ifindex (priv->client6, nm_dhcp_client_get_ifindex (client)); + r = sd_dhcp6_client_set_mac (sd_client, + hwaddr_arr, + hwaddr_len, + (guint16) arp_type); if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set ifindex: %s"); - goto errout; + nm_utils_error_set_errno (error, r, "failed to set MAC address: %s"); + return FALSE; } - r = sd_dhcp6_client_set_callback (priv->client6, dhcp6_event_cb, client); + r = sd_dhcp6_client_set_ifindex (sd_client, + nm_dhcp_client_get_ifindex (client)); if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set callback: %s"); - goto errout; + nm_utils_error_set_errno (error, r, "failed to set ifindex: %s"); + return FALSE; } /* Add requested options */ for (i = 0; dhcp6_requests[i].name; i++) { if (dhcp6_requests[i].include) - sd_dhcp6_client_set_request_option (priv->client6, dhcp6_requests[i].num); + sd_dhcp6_client_set_request_option (sd_client, dhcp6_requests[i].num); } - r = sd_dhcp6_client_set_local_address (priv->client6, ll_addr); + r = sd_dhcp6_client_set_local_address (sd_client, ll_addr); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to set local address: %s"); - goto errout; + return FALSE; } hostname = nm_dhcp_client_get_hostname (client); - r = sd_dhcp6_client_set_fqdn (priv->client6, hostname); + r = sd_dhcp6_client_set_fqdn (sd_client, hostname); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to set DHCP hostname: %s"); - goto errout; + return FALSE; } + r = sd_dhcp6_client_set_callback (sd_client, dhcp6_event_cb, client); + if (r < 0) { + nm_utils_error_set_errno (error, r, "failed to set callback: %s"); + return FALSE; + } + + priv->client6 = g_steal_pointer (&sd_client); + r = sd_dhcp6_client_start (priv->client6); if (r < 0) { + sd_dhcp6_client_set_callback (priv->client6, NULL, NULL); + nm_clear_pointer (&priv->client6, sd_dhcp6_client_unref); nm_utils_error_set_errno (error, r, "failed to start client: %s"); - goto errout; + return FALSE; } nm_dhcp_client_start_timeout (client); - return TRUE; - -errout: - sd_dhcp6_client_unref (g_steal_pointer (&priv->client6)); - return FALSE; } static void -stop (NMDhcpClient *client, gboolean release, GBytes *duid) +stop (NMDhcpClient *client, gboolean release) { NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); int r = 0; - NM_DHCP_CLIENT_CLASS (nm_dhcp_systemd_parent_class)->stop (client, release, duid); + NM_DHCP_CLIENT_CLASS (nm_dhcp_systemd_parent_class)->stop (client, release); _LOGT ("dhcp-client%d: stop %p", priv->client4 ? '4' : '6', diff --git a/src/dhcp/nm-dhcp-utils.c b/src/dhcp/nm-dhcp-utils.c index 6bbc670b..9b1653b8 100644 --- a/src/dhcp/nm-dhcp-utils.c +++ b/src/dhcp/nm-dhcp-utils.c @@ -726,10 +726,10 @@ nm_dhcp_utils_duid_to_string (GBytes *duid) gconstpointer data; gsize len; - g_return_val_if_fail (duid != NULL, NULL); + g_return_val_if_fail (duid, NULL); data = g_bytes_get_data (duid, &len); - return _nm_utils_bin2str (data, len, FALSE); + return _nm_utils_bin2hexstr_full (data, len, ':', FALSE, NULL); } /** diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c index edac4257..ab1f5551 100644 --- a/src/dhcp/tests/test-dhcp-dhclient.c +++ b/src/dhcp/tests/test-dhcp-dhclient.c @@ -760,62 +760,74 @@ test_read_commented_duid_from_leasefile (void) g_assert (duid == NULL); } +/*****************************************************************************/ + +static void +_save_duid (const char *path, + const guint8 *duid_bin, + gsize duid_len) +{ + gs_unref_bytes GBytes *duid = NULL; + GError *error = NULL; + gboolean success; + + g_assert (path); + g_assert (duid_bin); + g_assert (duid_len > 0); + + duid = g_bytes_new (duid_bin, duid_len); + success = nm_dhcp_dhclient_save_duid (path, duid, &error); + nmtst_assert_success (success, error); +} + static void test_write_duid (void) { - const char *duid = "\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254"; + const guint8 duid[] = { 000, 001, 000, 001, 027, 'X', 0350, 'X', 0, '#', 025, 010, '~', 0254 }; const char *expected_contents = "default-duid \"\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254\";\n"; GError *error = NULL; - char *contents = NULL; + gs_free char *contents = NULL; gboolean success; const char *path = "test-dhclient-write-duid.leases"; - success = nm_dhcp_dhclient_save_duid (path, duid, &error); - g_assert_no_error (error); - g_assert (success); + _save_duid (path, duid, G_N_ELEMENTS (duid)); success = g_file_get_contents (path, &contents, NULL, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); unlink (path); - g_assert_cmpstr (expected_contents, ==, contents); - g_free (contents); + g_assert_cmpstr (expected_contents, ==, contents); } static void test_write_existing_duid (void) { - const char *duid = "\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302"; + const guint8 duid[] = { 000, 001, 000, 001, 023, 'o', 023, 'n', 000, '\"', 0372, 0214, 0326, 0302 }; const char *original_contents = "default-duid \"\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254\";\n"; const char *expected_contents = "default-duid \"\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302\";\n"; GError *error = NULL; - char *contents = NULL; + gs_free char *contents = NULL; gboolean success; const char *path = "test-dhclient-write-existing-duid.leases"; success = g_file_set_contents (path, original_contents, -1, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); /* Save other DUID; should be overwritten */ - success = nm_dhcp_dhclient_save_duid (path, duid, &error); - g_assert_no_error (error); - g_assert (success); + _save_duid (path, duid, G_N_ELEMENTS (duid)); /* reread original contents */ success = g_file_get_contents (path, &contents, NULL, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); unlink (path); g_assert_cmpstr (expected_contents, ==, contents); - - g_free (contents); } +static const guint8 DUID_BIN[] = { 000, 001, 000, 001, 023, 'o', 023, 'n', 000, '\"', 0372, 0214, 0326, 0302 }; #define DUID "\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302" + static void test_write_existing_commented_duid (void) { @@ -824,28 +836,22 @@ test_write_existing_commented_duid (void) "default-duid \"" DUID "\";\n" ORIG_CONTENTS; GError *error = NULL; - char *contents = NULL; + gs_free char *contents = NULL; gboolean success; const char *path = "test-dhclient-write-existing-commented-duid.leases"; success = g_file_set_contents (path, ORIG_CONTENTS, -1, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); /* Save other DUID; should be saved on top */ - success = nm_dhcp_dhclient_save_duid (path, DUID, &error); - g_assert_no_error (error); - g_assert (success); + _save_duid (path, DUID_BIN, G_N_ELEMENTS (DUID_BIN)); /* reread original contents */ success = g_file_get_contents (path, &contents, NULL, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); unlink (path); g_assert_cmpstr (expected_contents, ==, contents); - - g_free (contents); #undef ORIG_CONTENTS } @@ -865,8 +871,7 @@ test_write_existing_multiline_duid (void) success = g_file_set_contents (path, ORIG_CONTENTS, -1, &error); nmtst_assert_success (success, error); - success = nm_dhcp_dhclient_save_duid (path, DUID, &error); - nmtst_assert_success (success, error); + _save_duid (path, DUID_BIN, G_N_ELEMENTS (DUID_BIN)); success = g_file_get_contents (path, &contents, NULL, &error); nmtst_assert_success (success, error); diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c index b48c6b87..b54df730 100644 --- a/src/dns/nm-dns-dnsmasq.c +++ b/src/dns/nm-dns-dnsmasq.c @@ -254,9 +254,16 @@ name_owner_changed (GObject *object, priv->running = TRUE; send_dnsmasq_update (self); } else { - _LOGI ("dnsmasq disappeared"); - priv->running = FALSE; - g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED); + if (priv->running) { + _LOGI ("dnsmasq disappeared"); + priv->running = FALSE; + g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED); + } else { + /* The only reason for which (!priv->running) here + * is that the dnsmasq process quit. We don't care + * of that here, the manager handles child restarts + * by itself. */ + } } } diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index 6a59b41c..aebe3e12 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -55,7 +55,7 @@ #include "nm-dns-systemd-resolved.h" #include "nm-dns-unbound.h" -#define HASH_LEN 20 +#define HASH_LEN NM_UTILS_CHECKSUM_LENGTH_SHA1 #ifndef RESOLVCONF_PATH #define RESOLVCONF_PATH "/sbin/resolvconf" @@ -582,53 +582,85 @@ again: } static char * -create_resolv_conf (char **searches, - char **nameservers, - char **options) +create_resolv_conf (const char *const*searches, + const char *const*nameservers, + const char *const*options) { - gs_free char *searches_str = NULL; - gs_free char *nameservers_str = NULL; - gs_free char *options_str = NULL; - char *tmp_str; GString *str; - int i; + gsize i; - if (searches) { - tmp_str = g_strjoinv (" ", searches); - searches_str = g_strconcat ("search ", tmp_str, "\n", NULL); - g_free (tmp_str); - } + str = g_string_new_len (NULL, 245); - if (options) { - tmp_str = g_strjoinv (" ", options); - options_str = g_strconcat ("options ", tmp_str, "\n", NULL); - g_free (tmp_str); - } + g_string_append (str, "# Generated by NetworkManager\n"); - if (nameservers) { - int num = g_strv_length (nameservers); + if (searches && searches[0]) { + gsize search_base_idx; - str = g_string_new (""); - for (i = 0; i < num; i++) { - if (i == 3) { - g_string_append (str, "# "); - g_string_append (str, "NOTE: the libc resolver may not support more than 3 nameservers."); - g_string_append (str, "\n# "); - g_string_append (str, "The nameservers listed below may not be recognized."); - g_string_append_c (str, '\n'); + g_string_append (str, "search"); + search_base_idx = str->len; + + for (i = 0; searches[i]; i++) { + const char *s = searches[i]; + gsize l = strlen (s); + + if ( l == 0 + || NM_STRCHAR_ANY (s, ch, NM_IN_SET (ch, ' ', '\t', '\n'))) { + /* there should be no such characters in the search entry. Also, + * because glibc parser would treat them as line/word separator. + * + * Skip the value silently. */ + continue; } + if (search_base_idx > 0) { + if (str->len - search_base_idx + 1 + l > 254) { + /* this entry crosses the 256 character boundery. Older glibc versions + * would truncate the entry at this point. + * + * Fill the line with spaces to cross the 256 char boundary and continue + * afterwards. This way, the truncation happens between two search entries. */ + while (str->len - search_base_idx < 257) + g_string_append_c (str, ' '); + search_base_idx = 0; + } + } + + g_string_append_c (str, ' '); + g_string_append_len (str, s, l); + } + g_string_append_c (str, '\n'); + } + + if (nameservers && nameservers[0]) { + for (i = 0; nameservers[i]; i++) { + if (i == 3) { + g_string_append (str, "# NOTE: the libc resolver may not support more than 3 nameservers.\n"); + g_string_append (str, "# The nameservers listed below may not be recognized.\n"); + } g_string_append (str, "nameserver "); g_string_append (str, nameservers[i]); g_string_append_c (str, '\n'); } - nameservers_str = g_string_free (str, FALSE); } - return g_strdup_printf ("# Generated by NetworkManager\n%s%s%s", - searches_str ?: "", - nameservers_str ?: "", - options_str ?: ""); + if (options && options[0]) { + g_string_append (str, "options"); + for (i = 0; options[i]; i++) { + g_string_append_c (str, ' '); + g_string_append (str, options[i]); + } + g_string_append_c (str, '\n'); + } + + return g_string_free (str, FALSE); +} + +char * +nmtst_dns_create_resolv_conf (const char *const*searches, + const char *const*nameservers, + const char *const*options) +{ + return create_resolv_conf (searches, nameservers, options); } static gboolean @@ -654,9 +686,9 @@ write_resolv_conf_contents (FILE *f, static gboolean write_resolv_conf (FILE *f, - char **searches, - char **nameservers, - char **options, + const char *const*searches, + const char *const*nameservers, + const char *const*options, GError **error) { gs_free char *content = NULL; @@ -718,7 +750,11 @@ dispatch_resolvconf (NMDnsManager *self, return SR_ERROR; } - success = write_resolv_conf (f, searches, nameservers, options, error); + success = write_resolv_conf (f, + NM_CAST_STRV_CC (searches), + NM_CAST_STRV_CC (nameservers), + NM_CAST_STRV_CC (options), + error); err = pclose (f); if (err < 0) { errnosv = errno; @@ -757,9 +793,9 @@ _read_link_cached (const char *path, gboolean *is_cached, char **cached) static SpawnResult update_resolv_conf (NMDnsManager *self, - char **searches, - char **nameservers, - char **options, + const char *const*searches, + const char *const*nameservers, + const char *const*options, GError **error, NMDnsManagerResolvConfManager rc_manager) { @@ -958,12 +994,11 @@ update_resolv_conf (NMDnsManager *self, static void compute_hash (NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[HASH_LEN]) { - GChecksum *sum; - gsize len = HASH_LEN; + nm_auto_free_checksum GChecksum *sum = NULL; NMDnsIPConfigData *ip_data; sum = g_checksum_new (G_CHECKSUM_SHA1); - nm_assert (len == g_checksum_type_get_length (G_CHECKSUM_SHA1)); + nm_assert (HASH_LEN == g_checksum_type_get_length (G_CHECKSUM_SHA1)); if (global) nm_global_dns_config_update_checksum (global, sum); @@ -977,8 +1012,7 @@ compute_hash (NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer nm_ip_config_hash (ip_data->ip_config, sum, TRUE); } - g_checksum_get_digest (sum, buffer, &len); - g_checksum_free (sum); + nm_utils_checksum_get_digest_len (sum, buffer, HASH_LEN); } static gboolean @@ -1066,7 +1100,6 @@ _collect_resolv_conf_data (NMDnsManager *self, const char **out_nis_domain) { NMDnsManagerPrivate *priv; - guint i, num, len; NMResolvConfData rc = { .nameservers = g_ptr_array_new (), .searches = g_ptr_array_new (), @@ -1136,17 +1169,6 @@ _collect_resolv_conf_data (NMDnsManager *self, } } - /* Per 'man resolv.conf', the search list is limited to 6 domains - * totalling 256 characters. - */ - num = MIN (rc.searches->len, 6u); - for (i = 0, len = 0; i < num; i++) { - len += strlen (rc.searches->pdata[i]) + 1; /* +1 for spaces */ - if (len > 256) - break; - } - g_ptr_array_set_size (rc.searches, i); - *out_searches = _ptrarray_to_strv (rc.searches); *out_options = _ptrarray_to_strv (rc.options); *out_nameservers = _ptrarray_to_strv (rc.nameservers); @@ -1449,7 +1471,12 @@ update_dns (NMDnsManager *self, switch (priv->rc_manager) { case NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK: case NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE: - result = update_resolv_conf (self, searches, nameservers, options, error, priv->rc_manager); + result = update_resolv_conf (self, + NM_CAST_STRV_CC (searches), + NM_CAST_STRV_CC (nameservers), + NM_CAST_STRV_CC (options), + error, + priv->rc_manager); resolv_conf_updated = TRUE; /* If we have ended with no nameservers avoid updating again resolv.conf * on stop, as some external changes may be applied to it in the meanwhile */ @@ -1474,15 +1501,26 @@ update_dns (NMDnsManager *self, if (result == SR_NOTFOUND) { _LOGD ("update-dns: program not available, writing to resolv.conf"); g_clear_error (error); - result = update_resolv_conf (self, searches, nameservers, options, error, NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK); + result = update_resolv_conf (self, + NM_CAST_STRV_CC (searches), + NM_CAST_STRV_CC (nameservers), + NM_CAST_STRV_CC (options), + error, + NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK); resolv_conf_updated = TRUE; } } /* Unless we've already done it, update private resolv.conf in NMRUNDIR ignoring any errors */ - if (!resolv_conf_updated) - update_resolv_conf (self, searches, nameservers, options, NULL, NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED); + if (!resolv_conf_updated) { + update_resolv_conf (self, + NM_CAST_STRV_CC (searches), + NM_CAST_STRV_CC (nameservers), + NM_CAST_STRV_CC (options), + NULL, + NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED); + } /* signal that resolv.conf was changed */ if (update && result == SR_SUCCESS) @@ -1542,6 +1580,7 @@ plugin_child_quit (NMDnsPlugin *plugin, int exit_status, gpointer user_data) } else { priv->plugin_ratelimit.num_restarts++; if (priv->plugin_ratelimit.num_restarts > PLUGIN_RATELIMIT_BURST) { + plugin_failed (plugin, self); _LOGW ("plugin %s child respawning too fast, delaying update for %u seconds", nm_dns_plugin_get_name (plugin), PLUGIN_RATELIMIT_DELAY); priv->plugin_ratelimit.timer = g_timeout_add_seconds (PLUGIN_RATELIMIT_DELAY, diff --git a/src/dns/nm-dns-manager.h b/src/dns/nm-dns-manager.h index ed1974a5..a3e9472e 100644 --- a/src/dns/nm-dns-manager.h +++ b/src/dns/nm-dns-manager.h @@ -129,4 +129,10 @@ typedef enum { void nm_dns_manager_stop (NMDnsManager *self); +/*****************************************************************************/ + +char *nmtst_dns_create_resolv_conf (const char *const*searches, + const char *const*nameservers, + const char *const*options); + #endif /* __NETWORKMANAGER_DNS_MANAGER_H__ */ diff --git a/src/dnsmasq/nm-dnsmasq-manager.c b/src/dnsmasq/nm-dnsmasq-manager.c index 09889441..3fe2f489 100644 --- a/src/dnsmasq/nm-dnsmasq-manager.c +++ b/src/dnsmasq/nm-dnsmasq-manager.c @@ -235,6 +235,7 @@ create_dm_cmd_line (const char *iface, g_string_append_c (s, ','); g_string_append (s, nm_utils_inet4_ntop (nm_ip4_config_get_nameserver (ip4_config, i), tmpaddr)); } + nm_cmd_line_add_string (cmd, s->str); g_string_truncate (s, 0); } @@ -244,6 +245,7 @@ create_dm_cmd_line (const char *iface, g_string_append_c (s, ','); g_string_append (s, nm_ip4_config_get_search (ip4_config, i)); } + nm_cmd_line_add_string (cmd, s->str); g_string_truncate (s, 0); } diff --git a/src/initrd/meson.build b/src/initrd/meson.build new file mode 100644 index 00000000..66e825d5 --- /dev/null +++ b/src/initrd/meson.build @@ -0,0 +1,32 @@ +sources = files( + 'nmi-cmdline-reader.c', + 'nmi-ibft-reader.c' +) + +nm_cflags = ['-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_DAEMON'] + +libnmi_core = static_library( + 'nmi-core', + c_args: nm_cflags, + sources: sources, + include_directories: src_inc, + dependencies: nm_core_dep, +) + +name = 'nm-initrd-generator' +executable( + name, + name + '.c', + c_args: nm_cflags, + include_directories: src_inc, + dependencies: [ nm_core_dep ], + link_with: [libnetwork_manager_base, libnmi_core], + link_args: ldflags_linker_script_binary, + link_depends: linker_script_binary, + install: true, + install_dir: nm_libexecdir, +) + +if enable_tests + subdir('tests') +endif diff --git a/src/initrd/tests/meson.build b/src/initrd/tests/meson.build new file mode 100644 index 00000000..6b316d4e --- /dev/null +++ b/src/initrd/tests/meson.build @@ -0,0 +1,23 @@ +test_units = [ + 'test-ibft-reader', + 'test-cmdline-reader', +] + +cflags = [ + '-DTEST_INITRD_DIR="@0@"'.format(meson.current_source_dir()), +] + +foreach test_unit : test_units + exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, + c_args: cflags, + link_with: libnmi_core + ) + test( + 'initrd/' + test_unit, + test_script, + args: test_args + [exe.full_path()] + ) +endforeach diff --git a/src/ndisc/nm-ndisc.h b/src/ndisc/nm-ndisc.h index 73eef368..6c0c0264 100644 --- a/src/ndisc/nm-ndisc.h +++ b/src/ndisc/nm-ndisc.h @@ -23,6 +23,7 @@ #include #include +#include #include "nm-setting-ip6-config.h" #include "NetworkManagerUtils.h" diff --git a/src/nm-config-data.c b/src/nm-config-data.c index 8d84e74a..0259f001 100644 --- a/src/nm-config-data.c +++ b/src/nm-config-data.c @@ -1241,9 +1241,13 @@ _match_section_infos_lookup (const MatchSectionInfo *match_section_infos, const char *match_device_type, char **out_value) { + const char *match_dhcp_plugin; + if (!match_section_infos) return NULL; + match_dhcp_plugin = nm_dhcp_manager_get_config (nm_dhcp_manager_get ()); + for (; match_section_infos->group_name; match_section_infos++) { char *value = NULL; gboolean match; @@ -1263,7 +1267,7 @@ _match_section_infos_lookup (const MatchSectionInfo *match_section_infos, if (device) match = nm_device_spec_match_list (device, match_section_infos->match_device.spec); else if (pllink) - match = nm_match_spec_device_by_pllink (pllink, match_device_type, match_section_infos->match_device.spec, FALSE); + match = nm_match_spec_device_by_pllink (pllink, match_device_type, match_dhcp_plugin, match_section_infos->match_device.spec, FALSE); else match = FALSE; } else diff --git a/src/nm-config-data.h b/src/nm-config-data.h index b52ddcc4..545d9a87 100644 --- a/src/nm-config-data.h +++ b/src/nm-config-data.h @@ -232,5 +232,14 @@ GKeyFile *_nm_config_data_get_keyfile (const NMConfigData *self); GKeyFile *_nm_config_data_get_keyfile_user (const NMConfigData *self); GKeyFile *_nm_config_data_get_keyfile_intern (const NMConfigData *self); +/*****************************************************************************/ + +/* nm-config-data.c requires getting the DHCP manager's configuration. That is a bit + * ugly, and optimally, NMConfig* is independent of NMDhcpManager. Instead of + * including the header, forward declare the two functions that we need. */ +struct _NMDhcpManager; +struct _NMDhcpManager *nm_dhcp_manager_get (void); +const char *nm_dhcp_manager_get_config (struct _NMDhcpManager *self); + #endif /* NM_CONFIG_DATA_H */ diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h index 178f27ad..99333ced 100644 --- a/src/nm-connectivity.h +++ b/src/nm-connectivity.h @@ -24,6 +24,21 @@ #include "nm-dbus-interface.h" +/*****************************************************************************/ + +static inline int +nm_connectivity_state_cmp (NMConnectivityState a, NMConnectivityState b) +{ + if (a == NM_CONNECTIVITY_PORTAL && b == NM_CONNECTIVITY_LIMITED) + return 1; + if (b == NM_CONNECTIVITY_PORTAL && a == NM_CONNECTIVITY_LIMITED) + return -1; + NM_CMP_DIRECT (a, b); + return 0; +} + +/*****************************************************************************/ + #define NM_CONNECTIVITY_ERROR ((NMConnectivityState) -1) #define NM_CONNECTIVITY_FAKE ((NMConnectivityState) -2) #define NM_CONNECTIVITY_CANCELLED ((NMConnectivityState) -3) diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index ca8c9526..a65ac636 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -30,15 +30,19 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include "nm-utils/nm-random-utils.h" #include "nm-utils/nm-io-utils.h" +#include "nm-utils/unaligned.h" +#include "nm-utils/nm-secret-utils.h" #include "nm-utils.h" #include "nm-core-internal.h" #include "nm-setting-connection.h" @@ -48,6 +52,17 @@ #include "nm-setting-wireless-security.h" G_STATIC_ASSERT (sizeof (NMUtilsTestFlags) <= sizeof (int)); + +/* we read _nm_utils_testing without memory barrier. This is thread-safe, + * because the static variable is initialized to zero, and only reset + * once to a non-zero value (via g_atomic_int_compare_and_exchange()). + * + * Since there is only one integer that contains the data, there is no + * caching problem reading this (atomic int) variable without + * synchronization/memory-barrier. Contrary to a double-checked locking, + * where one needs a memory barrier to read the variable and ensure + * that also the related data is coherent in cache. Here there is no + * related data. */ static int _nm_utils_testing = 0; gboolean @@ -66,6 +81,7 @@ nm_utils_get_testing () { NMUtilsTestFlags flags; +again: flags = (NMUtilsTestFlags) _nm_utils_testing; if (flags != NM_UTILS_TEST_NONE) { /* Flags already initialized. Return them. */ @@ -78,12 +94,11 @@ nm_utils_get_testing () if (g_test_initialized ()) flags |= _NM_UTILS_TEST_GENERAL; - if (g_atomic_int_compare_and_exchange (&_nm_utils_testing, 0, (int) flags)) { - /* Done. We set it. */ - return flags & NM_UTILS_TEST_ALL; - } - /* It changed in the meantime (??). Re-read the value. */ - return ((NMUtilsTestFlags) _nm_utils_testing) & NM_UTILS_TEST_ALL; + g_atomic_int_compare_and_exchange (&_nm_utils_testing, 0, (int) flags); + + /* regardless of whether we won the race of initializing _nm_utils_testing, + * go back and read the value again. It must be non-zero by now. */ + goto again; } void @@ -1133,6 +1148,7 @@ nm_utils_read_link_absolute (const char *link_file, GError **error) #define DEVICE_TYPE_TAG "type:" #define DRIVER_TAG "driver:" #define SUBCHAN_TAG "s390-subchannels:" +#define DHCP_PLUGIN_TAG "dhcp-plugin:" #define EXCEPT_TAG "except:" #define MATCH_TAG_CONFIG_NM_VERSION "nm-version:" #define MATCH_TAG_CONFIG_NM_VERSION_MIN "nm-version-min:" @@ -1144,6 +1160,7 @@ typedef struct { const char *device_type; const char *driver; const char *driver_version; + const char *dhcp_plugin; struct { const char *value; gboolean is_parsed; @@ -1280,6 +1297,34 @@ match_device_hwaddr_eval (const char *spec_str, _has; \ }) +static NMMatchSpecMatchType +_match_result (gboolean has_except, + gboolean has_not_except, + gboolean has_match, + gboolean has_match_except) +{ + if ( has_except + && !has_not_except) { + /* a match spec that only consists of a list of except matches is treated specially. */ + nm_assert (!has_match); + if (has_match_except) { + /* one of the "except:" matches matched. The result is an explicit + * negative match. */ + return NM_MATCH_SPEC_NEG_MATCH; + } else { + /* none of the "except:" matches matched. The result is a positive match, + * despite there being no positive match. */ + return NM_MATCH_SPEC_MATCH; + } + } + + if (has_match_except) + return NM_MATCH_SPEC_NEG_MATCH; + if (has_match) + return NM_MATCH_SPEC_MATCH; + return NM_MATCH_SPEC_NO_MATCH; +} + static const char * match_except (const char *spec_str, gboolean *out_except) { @@ -1361,6 +1406,9 @@ match_device_eval (const char *spec_str, if (_MATCH_CHECK (spec_str, SUBCHAN_TAG)) return match_data_s390_subchannels_eval (spec_str, match_data); + if (_MATCH_CHECK (spec_str, DHCP_PLUGIN_TAG)) + return nm_streq0 (spec_str, match_data->dhcp_plugin); + if (allow_fuzzy) { if (match_device_hwaddr_eval (spec_str, match_data)) return TRUE; @@ -1379,17 +1427,21 @@ nm_match_spec_device (const GSList *specs, const char *driver, const char *driver_version, const char *hwaddr, - const char *s390_subchannels) + const char *s390_subchannels, + const char *dhcp_plugin) { const GSList *iter; - NMMatchSpecMatchType match; + gboolean has_match = FALSE; + gboolean has_match_except = FALSE; + gboolean has_except = FALSE; + gboolean has_not_except = FALSE; const char *spec_str; - gboolean except; MatchDeviceData match_data = { .interface_name = interface_name, .device_type = nm_str_not_empty (device_type), .driver = nm_str_not_empty (driver), .driver_version = nm_str_not_empty (driver_version), + .dhcp_plugin = nm_str_not_empty (dhcp_plugin), .hwaddr = { .value = hwaddr, }, @@ -1403,19 +1455,9 @@ nm_match_spec_device (const GSList *specs, if (!specs) return NM_MATCH_SPEC_NO_MATCH; - match = NM_MATCH_SPEC_NO_MATCH; - - /* pre-search for "*" */ for (iter = specs; iter; iter = iter->next) { - spec_str = iter->data; - - if (spec_str && spec_str[0] == '*' && spec_str[1] == '\0') { - match = NM_MATCH_SPEC_MATCH; - break; - } - } + gboolean except; - for (iter = specs; iter; iter = iter->next) { spec_str = iter->data; if (!spec_str || !*spec_str) @@ -1423,10 +1465,14 @@ nm_match_spec_device (const GSList *specs, spec_str = match_except (spec_str, &except); - if ( !except - && match == NM_MATCH_SPEC_MATCH) { - /* we have no "except-match" but already match. No need to evaluate - * the match, we cannot match stronger. */ + if (except) + has_except = TRUE; + else + has_not_except = TRUE; + + if ( ( except && has_match_except) + || (!except && has_match)) { + /* evaluating the match does not give new information. Skip it. */ continue; } @@ -1436,11 +1482,12 @@ nm_match_spec_device (const GSList *specs, continue; if (except) - return NM_MATCH_SPEC_NEG_MATCH; - match = NM_MATCH_SPEC_MATCH; + has_match_except = TRUE; + else + has_match = TRUE; } - return match; + return _match_result (has_except, has_not_except, has_match, has_match_except); } static gboolean @@ -1510,7 +1557,10 @@ NMMatchSpecMatchType nm_match_spec_config (const GSList *specs, guint cur_nm_version, const char *env) { const GSList *iter; - NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH; + gboolean has_match = FALSE; + gboolean has_match_except = FALSE; + gboolean has_except = FALSE; + gboolean has_not_except = FALSE; if (!specs) return NM_MATCH_SPEC_NO_MATCH; @@ -1525,6 +1575,17 @@ nm_match_spec_config (const GSList *specs, guint cur_nm_version, const char *env spec_str = match_except (spec_str, &except); + if (except) + has_except = TRUE; + else + has_not_except = TRUE; + + if ( ( except && has_match_except) + || (!except && has_match)) { + /* evaluating the match does not give new information. Skip it. */ + continue; + } + if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_NM_VERSION)) v_match = match_config_eval (spec_str, MATCH_TAG_CONFIG_NM_VERSION, cur_nm_version); else if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MIN)) @@ -1534,15 +1595,18 @@ nm_match_spec_config (const GSList *specs, guint cur_nm_version, const char *env else if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_ENV)) v_match = env && env[0] && !strcmp (spec_str, env); else + v_match = FALSE; + + if (!v_match) continue; - if (v_match) { - if (except) - return NM_MATCH_SPEC_NEG_MATCH; - match = NM_MATCH_SPEC_MATCH; - } + if (except) + has_match_except = TRUE; + else + has_match = TRUE; } - return match; + + return _match_result (has_except, has_not_except, has_match, has_match_except); } #undef _MATCH_CHECK @@ -2503,209 +2567,549 @@ nm_utils_is_specific_hostname (const char *name) /*****************************************************************************/ -gboolean -nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid) -{ - int i; - guint8 v0, v1; - - if (!id_str) - return FALSE; - - for (i = 0; i < 32; i++) { - if (!g_ascii_isxdigit (id_str[i])) - return FALSE; - } - if (id_str[i] != '\0') - return FALSE; +typedef struct { + NMUuid bin; + char _nul_sentinel; /* just for safety, if somebody accidentally uses the binary in a string context. */ - if (out_uuid) { - for (i = 0; i < 16; i++) { - v0 = g_ascii_xdigit_value (*(id_str++)); - v1 = g_ascii_xdigit_value (*(id_str++)); - out_uuid[i] = (v0 << 4) + v1; - } + /* depending on whether the string is packed or not (with/without hyphens), + * it's 32 or 36 characters long (plus the trailing NUL). + * + * The difference is that boot-id is a valid RFC 4211 UUID and represented + * as a 36 ascii string (with hyphens). The machine-id technically is not + * a UUID, but just a 32 byte sequence of hexchars. */ + char str[37]; + bool is_fake; +} UuidData; + +static UuidData * +_uuid_data_init (UuidData *uuid_data, + gboolean packed, + gboolean is_fake, + const NMUuid *uuid) +{ + nm_assert (uuid_data); + nm_assert (uuid); + + uuid_data->bin = *uuid; + uuid_data->_nul_sentinel = '\0'; + uuid_data->is_fake = is_fake; + if (packed) { + G_STATIC_ASSERT_EXPR (sizeof (uuid_data->str) >= (sizeof (*uuid) * 2 + 1)); + _nm_utils_bin2hexstr_full (uuid, + sizeof (*uuid), + '\0', + FALSE, + uuid_data->str); + } else { + G_STATIC_ASSERT_EXPR (sizeof (uuid_data->str) >= 37); + _nm_utils_uuid_unparse (uuid, uuid_data->str); } - return TRUE; + return uuid_data; } -char * -nm_utils_machine_id_read (void) +/*****************************************************************************/ + +static const UuidData * +_machine_id_get (gboolean allow_fake) { - gs_free char *contents = NULL; - int i; + static const UuidData *volatile p_uuid_data; + const UuidData *d; + +again: + d = g_atomic_pointer_get (&p_uuid_data); + if (G_UNLIKELY (!d)) { + static gsize lock; + static UuidData uuid_data; + gs_free char *content = NULL; + gboolean is_fake = TRUE; + const char *fake_type = NULL; + NMUuid uuid; + + /* Get the machine ID from /etc/machine-id; it's always in /etc no matter + * where our configured SYSCONFDIR is. Alternatively, it might be in + * LOCALSTATEDIR /lib/dbus/machine-id. + */ + if ( nm_utils_file_get_contents (-1, "/etc/machine-id", 100*1024, 0, &content, NULL, NULL) >= 0 + || nm_utils_file_get_contents (-1, LOCALSTATEDIR"/lib/dbus/machine-id", 100*1024, 0, &content, NULL, NULL) >= 0) { + g_strstrip (content); + if (_nm_utils_hexstr2bin_full (content, + FALSE, + FALSE, + NULL, + 16, + (guint8 *) &uuid, + sizeof (uuid), + NULL)) { + if (!nm_utils_uuid_is_null (&uuid)) { + /* an all-zero machine-id is not valid. */ + is_fake = FALSE; + } + } + } - /* Get the machine ID from /etc/machine-id; it's always in /etc no matter - * where our configured SYSCONFDIR is. Alternatively, it might be in - * LOCALSTATEDIR /lib/dbus/machine-id. - */ - if ( !g_file_get_contents ("/etc/machine-id", &contents, NULL, NULL) - && !g_file_get_contents (LOCALSTATEDIR "/lib/dbus/machine-id", &contents, NULL, NULL)) - return NULL; + if (is_fake) { + const guint8 *seed_bin; + const char *hash_seed; + gsize seed_len; + + if (!allow_fake) { + /* we don't allow generating (and memoizing) a fake key. + * Signal that no valid machine-id exists. */ + return NULL; + } - contents = g_strstrip (contents); + if (nm_utils_host_id_get (&seed_bin, &seed_len)) { + /* we have no valid machine-id. Generate a fake one by hashing + * the secret-key. This key is commonly persisted, so it should be + * stable accross reboots (despite having a broken system without + * proper machine-id). */ + fake_type = "secret-key"; + hash_seed = "ab085f06-b629-46d1-a553-84eeba5683b6"; + } else { + /* the secret-key is not valid/persistent either. That happens when we fail + * to read/write the secret-key to disk. Fallback to boot-id. The boot-id + * itself may be fake and randomly generated ad-hoc, but that is as best + * as it gets. */ + seed_bin = (const guint8 *) nm_utils_boot_id_bin (); + seed_len = sizeof (NMUuid); + fake_type = "boot-id"; + hash_seed = "7ff0c8f5-5399-4901-ab63-61bf594abe8b"; + } - for (i = 0; i < 32; i++) { - if (!g_ascii_isxdigit (contents[i])) - return NULL; - if (contents[i] >= 'A' && contents[i] <= 'F') { - /* canonicalize to lower-case */ - contents[i] = 'a' + (contents[i] - 'A'); + /* the fake machine-id is based on secret-key/boot-id, but we hash it + * again, so that they are not literally the same. */ + nm_utils_uuid_generate_from_string_bin (&uuid, + (const char *) seed_bin, + seed_len, + NM_UTILS_UUID_TYPE_VERSION5, + (gpointer) hash_seed); } + + if (!g_once_init_enter (&lock)) + goto again; + + d = _uuid_data_init (&uuid_data, TRUE, is_fake, &uuid); + g_atomic_pointer_set (&p_uuid_data, d); + g_once_init_leave (&lock, 1); + + if (is_fake) { + nm_log_err (LOGD_CORE, + "/etc/machine-id: no valid machine-id. Use fake one based on %s: %s", + fake_type, + d->str); + } else + nm_log_dbg (LOGD_CORE, "/etc/machine-id: %s", d->str); } - if (contents[i] != '\0') - return NULL; - return g_steal_pointer (&contents); + return d; +} + +const char * +nm_utils_machine_id_str (void) +{ + return _machine_id_get (TRUE)->str; +} + +const NMUuid * +nm_utils_machine_id_bin (void) +{ + return &_machine_id_get (TRUE)->bin; +} + +gboolean +nm_utils_machine_id_is_fake (void) +{ + return _machine_id_get (TRUE)->is_fake; } /*****************************************************************************/ +/* prefix for version2 secret key. The secret key is hashed with /etc/machine-id. */ +#define SECRET_KEY_V2_PREFIX "nm-v2:" +#define SECRET_KEY_FILE NMSTATEDIR"/secret_key" + static gboolean -_secret_key_read (guint8 **out_secret_key, - gsize *out_key_len) +_host_id_read_timestamp (gboolean use_secret_key_file, + const guint8 *host_id, + gsize host_id_len, + gint64 *out_timestamp_ns) { - guint8 *secret_key; - gboolean success = TRUE; - gsize key_len; - gs_free_error GError *error = NULL; + struct stat st; + gint64 now; + guint64 v; + + if ( use_secret_key_file + && stat (SECRET_KEY_FILE, &st) == 0) { + /* don't check for overflow or timestamps in the future. We get whatever + * (bogus) date is on the file. */ + *out_timestamp_ns = (st.st_mtim.tv_sec * NM_UTILS_NS_PER_SECOND) + st.st_mtim.tv_nsec; + return TRUE; + } - /* Let's try to load a saved secret key first. */ - if (g_file_get_contents (NMSTATEDIR "/secret_key", (char **) &secret_key, &key_len, &error)) { - if (key_len >= 16) - goto out; + /* generate a fake timestamp based on the host-id. + * + * This really should never happen under normal circumstances. We already + * are in a code path, where the system has a problem (unable to get good randomness + * and/or can't access the secret_key). In such a scenario, a fake timestamp is the + * least of our problems. + * + * At least, generate something sensible so we don't have to worry about the + * timestamp. It is wrong to worry about using a fake timestamp (which is tied to + * the secret_key) if we are unable to access the secret_key file in the first place. + * + * Pick a random timestamp from the past two years. Yes, this timestamp + * is not stable accross restarts, but apparently neither is the host-id + * nor the secret_key itself. */ - /* the secret key is borked. Log a warning, but proceed below to generate - * a new one. */ - nm_log_warn (LOGD_CORE, "secret-key: too short secret key in \"%s\" (generate new key)", NMSTATEDIR "/secret_key"); - nm_clear_g_free (&secret_key); - } else { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { +#define EPOCH_TWO_YEARS (G_GINT64_CONSTANT (2 * 365 * 24 * 3600) * NM_UTILS_NS_PER_SECOND) + + v = nm_hash_siphash42 (1156657133u, host_id, host_id_len); + + now = time (NULL); + *out_timestamp_ns = NM_MAX ((gint64) 1, + (now * NM_UTILS_NS_PER_SECOND) - ((gint64) (v % ((guint64) (EPOCH_TWO_YEARS))))); + return FALSE; +} + +static const guint8 * +_host_id_hash_v2 (const guint8 *seed_arr, + gsize seed_len, + guint8 *out_digest /* 32 bytes (NM_UTILS_CHECKSUM_LENGTH_SHA256) */) +{ + nm_auto_free_checksum GChecksum *sum = g_checksum_new (G_CHECKSUM_SHA256); + const UuidData *machine_id_data; + char slen[100]; + + /* + (stat -c '%s' /var/lib/NetworkManager/secret_key; + echo -n ' '; + cat /var/lib/NetworkManager/secret_key; + cat /etc/machine-id | tr -d '\n' | sed -n 's/[a-f0-9-]/\0/pg') | sha256sum + */ + + nm_sprintf_buf (slen, "%"G_GSIZE_FORMAT" ", seed_len); + g_checksum_update (sum, (const guchar *) slen, strlen (slen)); + + g_checksum_update (sum, (const guchar *) seed_arr, seed_len); + + machine_id_data = _machine_id_get (FALSE); + if ( machine_id_data + && !machine_id_data->is_fake) + g_checksum_update (sum, (const guchar *) machine_id_data->str, strlen (machine_id_data->str)); + + nm_utils_checksum_get_digest_len (sum, out_digest, NM_UTILS_CHECKSUM_LENGTH_SHA256); + return out_digest; +} + +static gboolean +_host_id_read (guint8 **out_host_id, + gsize *out_host_id_len) +{ +#define SECRET_KEY_LEN 32u + guint8 sha256_digest[NM_UTILS_CHECKSUM_LENGTH_SHA256]; + nm_auto_clear_secret_ptr NMSecretPtr file_content = { 0 }; + const guint8 *secret_arr; + gsize secret_len; + GError *error = NULL; + gboolean success; + + if (nm_utils_file_get_contents (-1, + SECRET_KEY_FILE, + 10*1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET, + (char **) &file_content.str, + &file_content.len, + &error) < 0) { + if (!nm_utils_error_is_notfound (error)) { nm_log_warn (LOGD_CORE, "secret-key: failure reading secret key in \"%s\": %s (generate new key)", - NMSTATEDIR "/secret_key", error->message); + SECRET_KEY_FILE, error->message); } g_clear_error (&error); - } - - /* RFC7217 mandates the key SHOULD be at least 128 bits. - * Let's use twice as much. */ - key_len = 32; - secret_key = g_malloc (key_len + 1); - - /* the secret-key is binary. Still, ensure that it's NULL terminated, just like - * g_file_set_contents() does. */ - secret_key[32] = '\0'; + } else if ( file_content.len >= NM_STRLEN (SECRET_KEY_V2_PREFIX) + SECRET_KEY_LEN + && memcmp (file_content.bin, SECRET_KEY_V2_PREFIX, NM_STRLEN (SECRET_KEY_V2_PREFIX)) == 0) { + /* for this type of secret key, we require a prefix followed at least SECRET_KEY_LEN (32) bytes. We + * (also) do that, because older versions of NetworkManager wrote exactly 32 bytes without + * prefix, so we won't wrongly interpret such legacy keys as v2 (if they accidentally have + * a SECRET_KEY_V2_PREFIX prefix, they'll still have the wrong size). + * + * Note that below we generate the random seed in base64 encoding. But that is only done + * to write an ASCII file. There is no base64 decoding and the ASCII is hashed as-is. + * We would accept any binary data just as well (provided a suitable prefix and at least + * 32 bytes). + * + * Note that when hashing the v2 content, we also hash the prefix. There is no strong reason, + * except that it seems simpler not to distinguish between the v2 prefix and the content. + * It's all just part of the seed. */ - if (!nm_utils_random_bytes (secret_key, key_len)) { - nm_log_warn (LOGD_CORE, "secret-key: failure to generate good random data for secret-key (use non-persistent key)"); - success = FALSE; + secret_arr = _host_id_hash_v2 (file_content.bin, file_content.len, sha256_digest); + secret_len = NM_UTILS_CHECKSUM_LENGTH_SHA256; + success = TRUE; goto out; + } else if (file_content.len >= 16) { + secret_arr = file_content.bin; + secret_len = file_content.len; + success = TRUE; + goto out; + } else { + /* the secret key is borked. Log a warning, but proceed below to generate + * a new one. */ + nm_log_warn (LOGD_CORE, "secret-key: too short secret key in \"%s\" (generate new key)", SECRET_KEY_FILE); } - if (!nm_utils_file_set_contents (NMSTATEDIR "/secret_key", (char *) secret_key, key_len, 0077, &error)) { - nm_log_warn (LOGD_CORE, "secret-key: failure to persist secret key in \"%s\" (%s) (use non-persistent key)", - NMSTATEDIR "/secret_key", error->message); - success = FALSE; - goto out; + /* generate and persist new key */ + { +#define SECRET_KEY_LEN_BASE64 ((((SECRET_KEY_LEN / 3) + 1) * 4) + 4) + guint8 rnd_buf[SECRET_KEY_LEN]; + guint8 new_content[NM_STRLEN (SECRET_KEY_V2_PREFIX) + SECRET_KEY_LEN_BASE64]; + int base64_state = 0; + int base64_save = 0; + gsize len; + + success = nm_utils_random_bytes (rnd_buf, sizeof (rnd_buf)); + + /* Our key is really binary data. But since we anyway generate a random seed + * (with 32 random bytes), don't write it in binary, but instead create + * an pure ASCII (base64) representation. Note that the ASCII will still be taken + * as-is (no base64 decoding is done). The sole purpose is to write a ASCII file + * instead of a binary. The content is gibberish either way. */ + memcpy (new_content, SECRET_KEY_V2_PREFIX, NM_STRLEN (SECRET_KEY_V2_PREFIX)); + len = NM_STRLEN (SECRET_KEY_V2_PREFIX); + len += g_base64_encode_step (rnd_buf, + sizeof (rnd_buf), + FALSE, + (char *) &new_content[len], + &base64_state, + &base64_save); + len += g_base64_encode_close (FALSE, + (char *) &new_content[len], + &base64_state, + &base64_save); + nm_assert (len <= sizeof (new_content)); + + secret_arr = _host_id_hash_v2 (new_content, len, sha256_digest); + secret_len = NM_UTILS_CHECKSUM_LENGTH_SHA256; + + if (!success) + nm_log_warn (LOGD_CORE, "secret-key: failure to generate good random data for secret-key (use non-persistent key)"); + else if (nm_utils_get_testing ()) { + /* for test code, we don't write the generated secret-key to disk. */ + } else if (!nm_utils_file_set_contents (SECRET_KEY_FILE, + (const char *) new_content, + len, + 0077, + &error)) { + nm_log_warn (LOGD_CORE, "secret-key: failure to persist secret key in \"%s\" (%s) (use non-persistent key)", + SECRET_KEY_FILE, error->message); + g_clear_error (&error); + success = FALSE; + } else + nm_log_dbg (LOGD_CORE, "secret-key: persist new secret key to \"%s\"", SECRET_KEY_FILE); + + nm_explicit_bzero (rnd_buf, sizeof (rnd_buf)); + nm_explicit_bzero (new_content, sizeof (new_content)); } out: - /* regardless of success or failue, we always return a secret-key. The - * caller may choose to ignore the error and proceed. */ - *out_key_len = key_len; - *out_secret_key = secret_key; + *out_host_id_len = secret_len; + *out_host_id = nm_memdup (secret_arr, secret_len); return success; } typedef struct { - const guint8 *secret_key; - gsize key_len; + guint8 *host_id; + gsize host_id_len; + gint64 timestamp_ns; bool is_good:1; -} SecretKeyData; + bool timestamp_is_good:1; +} HostIdData; -gboolean -nm_utils_secret_key_get (const guint8 **out_secret_key, - gsize *out_key_len) +static const HostIdData * +_host_id_get (void) { - static volatile const SecretKeyData *secret_key_static; - const SecretKeyData *secret_key; + static const HostIdData *volatile host_id_static; + const HostIdData *host_id; - secret_key = g_atomic_pointer_get (&secret_key_static); - if (G_UNLIKELY (!secret_key)) { +again: + host_id = g_atomic_pointer_get (&host_id_static); + if (G_UNLIKELY (!host_id)) { + static HostIdData host_id_data; static gsize init_value = 0; - static SecretKeyData secret_key_data; - gboolean tmp_success; - gs_free guint8 *tmp_secret_key = NULL; - gsize tmp_key_len; - - tmp_success = _secret_key_read (&tmp_secret_key, &tmp_key_len); - if (g_once_init_enter (&init_value)) { - secret_key_data.secret_key = tmp_secret_key; - secret_key_data.key_len = tmp_key_len; - secret_key_data.is_good = tmp_success; - - if (g_atomic_pointer_compare_and_exchange (&secret_key_static, NULL, &secret_key_data)) { - g_steal_pointer (&tmp_secret_key); - secret_key = &secret_key_data; - } - g_once_init_leave (&init_value, 1); - } - if (!secret_key) - secret_key = g_atomic_pointer_get (&secret_key_static); + if (!g_once_init_enter (&init_value)) + goto again; + + host_id_data.is_good = _host_id_read (&host_id_data.host_id, + &host_id_data.host_id_len); + + host_id_data.timestamp_is_good = _host_id_read_timestamp (host_id_data.is_good, + host_id_data.host_id, + host_id_data.host_id_len, + &host_id_data.timestamp_ns); + if ( !host_id_data.timestamp_is_good + && host_id_data.is_good) + nm_log_warn (LOGD_CORE, "secret-key: failure reading host timestamp (use fake one)"); + + host_id = &host_id_data; + g_atomic_pointer_set (&host_id_static, host_id); + g_once_init_leave (&init_value, 1); } - *out_secret_key = secret_key->secret_key; - *out_key_len = secret_key->key_len; - return secret_key->is_good; + return host_id; } -gint64 -nm_utils_secret_key_get_timestamp (void) +/** + * nm_utils_host_id_get: + * @out_host_id: (out) (transfer none): the binary host key + * @out_host_id_len: the length of the host key. + * + * This returns a per-host key that depends on /var/lib/NetworkManage/secret_key + * and (depending on the version) on /etc/machine-id. If /var/lib/NetworkManage/secret_key + * does not exist, it will be generated and persisted for next boot. + * + * Returns: %TRUE, if the host key is "good". Note that this function + * will always succeed to return a host-key, and that this key + * won't change during the run of the program (no matter what). + * A %FALSE return possibly means, that the secret_key is not persisted + * to disk, and/or that it was generated with bad randomness. + */ +gboolean +nm_utils_host_id_get (const guint8 **out_host_id, + gsize *out_host_id_len) { - struct stat stat_buf; - const guint8 *key; - gsize key_len; + const HostIdData *host_id; - if (!nm_utils_secret_key_get (&key, &key_len)) - return 0; - - if (stat (NMSTATEDIR "/secret_key", &stat_buf) != 0) - return 0; + host_id = _host_id_get (); + *out_host_id = host_id->host_id; + *out_host_id_len = host_id->host_id_len; + return host_id->is_good; +} - return stat_buf.st_mtim.tv_sec; +gint64 +nm_utils_host_id_get_timestamp_ns (void) +{ + return _host_id_get ()->timestamp_ns; } /*****************************************************************************/ -const char * -nm_utils_get_boot_id (void) +static const UuidData * +_boot_id_get (void) { - static const char *boot_id; - - if (G_UNLIKELY (!boot_id)) { + static const UuidData *volatile p_boot_id; + const UuidData *d; + +again: + d = g_atomic_pointer_get (&p_boot_id); + if (G_UNLIKELY (!d)) { + static gsize lock; + static UuidData boot_id; gs_free char *contents = NULL; + NMUuid uuid; + gboolean is_fake = FALSE; nm_utils_file_get_contents (-1, "/proc/sys/kernel/random/boot_id", 0, NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, &contents, NULL, NULL); - if (contents) { - g_strstrip (contents); - if (contents[0]) { - /* clone @contents because we keep @boot_id until the program - * ends. - * nm_utils_file_get_contents() likely allocated a larger - * buffer chunk initially and (although using realloc to shrink - * the buffer) it might not be best to keep this memory - * around. */ - boot_id = g_strdup (contents); - } + if ( !contents + || !_nm_utils_uuid_parse (nm_strstrip (contents), &uuid)) { + /* generate a random UUID instead. */ + is_fake = TRUE; + _nm_utils_uuid_generate_random (&uuid); } - if (!boot_id) - boot_id = nm_utils_uuid_generate (); + + if (!g_once_init_enter (&lock)) + goto again; + + d = _uuid_data_init (&boot_id, FALSE, is_fake, &uuid); + g_atomic_pointer_set (&p_boot_id, d); + g_once_init_leave (&lock, 1); } - return boot_id; + return d; +} + +const char * +nm_utils_boot_id_str (void) +{ + return _boot_id_get ()->str; +} + +const NMUuid * +nm_utils_boot_id_bin (void) +{ + return &_boot_id_get ()->bin; +} + +/*****************************************************************************/ + +/** + * nm_utils_arp_type_detect_from_hwaddrlen: + * @hwaddr_len: the length of the hardware address in bytes. + * + * Detects the arp-type based on the length of the MAC address. + * On success, this returns a (positive) value in uint16_t range, + * like ARPHRD_ETHER or ARPHRD_INFINIBAND. + * + * On failure, returns a negative error code. + * + * Returns: the arp-type or negative value on error. */ +int +nm_utils_arp_type_detect_from_hwaddrlen (gsize hwaddr_len) +{ + switch (hwaddr_len) { + case ETH_ALEN: + return ARPHRD_ETHER; + case INFINIBAND_ALEN: + return ARPHRD_INFINIBAND; + default: + /* Note: if you ever support anything but ethernet and infiniband, + * make sure to look at all callers. They assert that it's one of + * these two. */ + return -EINVAL; + } +} + +gboolean +nm_utils_arp_type_validate_hwaddr (int arp_type, + const guint8 *hwaddr, + gsize hwaddr_len) +{ + + if (!hwaddr) + return FALSE; + + if (arp_type == ARPHRD_ETHER) { + G_STATIC_ASSERT (ARPHRD_ETHER >= 0 && ARPHRD_ETHER <= 0xFF); + if (hwaddr_len != ETH_ALEN) + return FALSE; + } else if (arp_type == ARPHRD_INFINIBAND) { + G_STATIC_ASSERT (ARPHRD_INFINIBAND >= 0 && ARPHRD_INFINIBAND <= 0xFF); + if (hwaddr_len != INFINIBAND_ALEN) + return FALSE; + } else + return FALSE; + + nm_assert (arp_type == nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len)); + return TRUE; +} + +gboolean +nm_utils_arp_type_get_hwaddr_relevant_part (int arp_type, + const guint8 **hwaddr, + gsize *hwaddr_len) +{ + g_return_val_if_fail ( hwaddr + && hwaddr_len + && nm_utils_arp_type_validate_hwaddr (arp_type, *hwaddr, *hwaddr_len), + FALSE); + + /* for infiniband, we only consider the last 8 bytes. */ + if (arp_type == ARPHRD_INFINIBAND) { + *hwaddr += (INFINIBAND_ALEN - 8); + *hwaddr_len = 8; + } + + return TRUE; } /*****************************************************************************/ @@ -2920,9 +3324,8 @@ nm_utils_stable_id_random (void) char * nm_utils_stable_id_generated_complete (const char *stable_id_generated) { - guint8 buf[20]; - GChecksum *sum; - gsize buf_size; + nm_auto_free_checksum GChecksum *sum = NULL; + guint8 buf[NM_UTILS_CHECKSUM_LENGTH_SHA1]; char *base64; /* for NM_UTILS_STABLE_TYPE_GENERATED we genererate a possibly long string @@ -2933,15 +3336,8 @@ nm_utils_stable_id_generated_complete (const char *stable_id_generated) g_return_val_if_fail (stable_id_generated, NULL); sum = g_checksum_new (G_CHECKSUM_SHA1); - nm_assert (sum); - g_checksum_update (sum, (guchar *) stable_id_generated, strlen (stable_id_generated)); - - buf_size = sizeof (buf); - g_checksum_get_digest (sum, buf, &buf_size); - nm_assert (buf_size == sizeof (buf)); - - g_checksum_free (sum); + nm_utils_checksum_get_digest (sum, buf); /* we don't care to use the sha1 sum in common hex representation. * Use instead base64, it's 27 chars (stripping the padding) vs. @@ -2967,6 +3363,7 @@ _stable_id_append (GString *str, NMUtilsStableType nm_utils_stable_id_parse (const char *stable_id, const char *deviceid, + const char *hwaddr, const char *bootid, const char *uuid, char **out_generated) @@ -3042,9 +3439,11 @@ nm_utils_stable_id_parse (const char *stable_id, if (CHECK_PREFIX ("${CONNECTION}")) _stable_id_append (str, uuid); else if (CHECK_PREFIX ("${BOOT}")) - _stable_id_append (str, bootid ?: nm_utils_get_boot_id ()); + _stable_id_append (str, bootid); else if (CHECK_PREFIX ("${DEVICE}")) _stable_id_append (str, deviceid); + else if (CHECK_PREFIX ("${MAC}")) + _stable_id_append (str, hwaddr); else if (g_str_has_prefix (&stable_id[i], "${RANDOM}")) { /* RANDOM makes not so much sense for cloned-mac-address * as the result is simmilar to specyifing "cloned-mac-address=random". @@ -3121,28 +3520,20 @@ _set_stable_privacy (NMUtilsStableType stable_type, const char *ifname, const char *network_id, guint32 dad_counter, - const guint8 *secret_key, - gsize key_len, + const guint8 *host_id, + gsize host_id_len, GError **error) { - GChecksum *sum; - guint8 digest[32]; + nm_auto_free_checksum GChecksum *sum = NULL; + guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA256]; guint32 tmp[2]; - gsize len = sizeof (digest); - nm_assert (key_len); + nm_assert (host_id_len); nm_assert (network_id); - /* Documentation suggests that this can fail. - * Maybe in case of a missing algorithm in crypto library? */ sum = g_checksum_new (G_CHECKSUM_SHA256); - if (!sum) { - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "Can't create a SHA256 hash"); - return FALSE; - } - key_len = MIN (key_len, G_MAXUINT32); + host_id_len = MIN (host_id_len, G_MAXUINT32); if (stable_type != NM_UTILS_STABLE_TYPE_UUID) { guint8 stable_type_uint8; @@ -3155,7 +3546,7 @@ _set_stable_privacy (NMUtilsStableType stable_type, * * That is no real problem and it is still impossible to * force a collision here, because of how the remaining - * fields are hashed. That is, as we also hash @key_len + * fields are hashed. That is, as we also hash @host_id_len * and the terminating '\0' of @network_id, it is unambigiously * possible to revert the process and deduce the @stable_type. */ @@ -3166,27 +3557,20 @@ _set_stable_privacy (NMUtilsStableType stable_type, g_checksum_update (sum, (const guchar *) ifname, strlen (ifname) + 1); g_checksum_update (sum, (const guchar *) network_id, strlen (network_id) + 1); tmp[0] = htonl (dad_counter); - tmp[1] = htonl (key_len); + tmp[1] = htonl (host_id_len); g_checksum_update (sum, (const guchar *) tmp, sizeof (tmp)); - g_checksum_update (sum, (const guchar *) secret_key, key_len); - - g_checksum_get_digest (sum, digest, &len); - - nm_assert (len == sizeof (digest)); + g_checksum_update (sum, (const guchar *) host_id, host_id_len); + nm_utils_checksum_get_digest (sum, digest); while (_is_reserved_ipv6_iid (digest)) { g_checksum_reset (sum); tmp[0] = htonl (++dad_counter); - g_checksum_update (sum, digest, len); + g_checksum_update (sum, digest, sizeof (digest)); g_checksum_update (sum, (const guchar *) &tmp[0], sizeof (tmp[0])); - g_checksum_get_digest (sum, digest, &len); - nm_assert (len == sizeof (digest)); + nm_utils_checksum_get_digest (sum, digest); } - g_checksum_free (sum); - memcpy (addr->s6_addr + 8, &digest[0], 8); - return TRUE; } @@ -3196,11 +3580,11 @@ nm_utils_ipv6_addr_set_stable_privacy_impl (NMUtilsStableType stable_type, const char *ifname, const char *network_id, guint32 dad_counter, - guint8 *secret_key, - gsize key_len, + guint8 *host_id, + gsize host_id_len, GError **error) { - return _set_stable_privacy (stable_type, addr, ifname, network_id, dad_counter, secret_key, key_len, error); + return _set_stable_privacy (stable_type, addr, ifname, network_id, dad_counter, host_id, host_id_len, error); } #define RFC7217_IDGEN_RETRIES 3 @@ -3220,8 +3604,8 @@ nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type, guint32 dad_counter, GError **error) { - const guint8 *secret_key; - gsize key_len; + const guint8 *host_id; + gsize host_id_len; g_return_val_if_fail (network_id, FALSE); @@ -3231,10 +3615,10 @@ nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type, return FALSE; } - nm_utils_secret_key_get (&secret_key, &key_len); + nm_utils_host_id_get (&host_id, &host_id_len); return _set_stable_privacy (stable_type, addr, ifname, network_id, dad_counter, - secret_key, key_len, error); + host_id, host_id_len, error); } /*****************************************************************************/ @@ -3306,42 +3690,36 @@ nm_utils_hw_addr_gen_random_eth (const char *current_mac_address, static char * _hw_addr_gen_stable_eth (NMUtilsStableType stable_type, const char *stable_id, - const guint8 *secret_key, - gsize key_len, + const guint8 *host_id, + gsize host_id_len, const char *ifname, const char *current_mac_address, const char *generate_mac_address_mask) { - GChecksum *sum; + nm_auto_free_checksum GChecksum *sum = NULL; guint32 tmp; - guint8 digest[32]; - gsize len = sizeof (digest); + guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA256]; struct ether_addr bin_addr; guint8 stable_type_uint8; nm_assert (stable_id); - nm_assert (secret_key); + nm_assert (host_id); sum = g_checksum_new (G_CHECKSUM_SHA256); - if (!sum) - return NULL; - key_len = MIN (key_len, G_MAXUINT32); + host_id_len = MIN (host_id_len, G_MAXUINT32); nm_assert (stable_type < (NMUtilsStableType) 255); stable_type_uint8 = stable_type; g_checksum_update (sum, (const guchar *) &stable_type_uint8, sizeof (stable_type_uint8)); - tmp = htonl ((guint32) key_len); + tmp = htonl ((guint32) host_id_len); g_checksum_update (sum, (const guchar *) &tmp, sizeof (tmp)); - g_checksum_update (sum, (const guchar *) secret_key, key_len); + g_checksum_update (sum, (const guchar *) host_id, host_id_len); g_checksum_update (sum, (const guchar *) (ifname ?: ""), ifname ? (strlen (ifname) + 1) : 1); g_checksum_update (sum, (const guchar *) stable_id, strlen (stable_id) + 1); - g_checksum_get_digest (sum, digest, &len); - g_checksum_free (sum); - - g_return_val_if_fail (len == 32, NULL); + nm_utils_checksum_get_digest (sum, digest); memcpy (&bin_addr, digest, ETH_ALEN); _hw_addr_eth_complete (&bin_addr, current_mac_address, generate_mac_address_mask); @@ -3351,13 +3729,13 @@ _hw_addr_gen_stable_eth (NMUtilsStableType stable_type, char * nm_utils_hw_addr_gen_stable_eth_impl (NMUtilsStableType stable_type, const char *stable_id, - const guint8 *secret_key, - gsize key_len, + const guint8 *host_id, + gsize host_id_len, const char *ifname, const char *current_mac_address, const char *generate_mac_address_mask) { - return _hw_addr_gen_stable_eth (stable_type, stable_id, secret_key, key_len, ifname, current_mac_address, generate_mac_address_mask); + return _hw_addr_gen_stable_eth (stable_type, stable_id, host_id, host_id_len, ifname, current_mac_address, generate_mac_address_mask); } char * @@ -3367,17 +3745,17 @@ nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, const char *current_mac_address, const char *generate_mac_address_mask) { - const guint8 *secret_key; - gsize key_len; + const guint8 *host_id; + gsize host_id_len; g_return_val_if_fail (stable_id, NULL); - nm_utils_secret_key_get (&secret_key, &key_len); + nm_utils_host_id_get (&host_id, &host_id_len); return _hw_addr_gen_stable_eth (stable_type, stable_id, - secret_key, - key_len, + host_id, + host_id_len, ifname, current_mac_address, generate_mac_address_mask); @@ -3385,6 +3763,118 @@ nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, /*****************************************************************************/ +GBytes * +nm_utils_dhcp_client_id_mac (int arp_type, + const guint8 *hwaddr, + gsize hwaddr_len) +{ + guint8 *client_id_buf; + const guint8 hwaddr_type = arp_type; + + if (!nm_utils_arp_type_get_hwaddr_relevant_part (arp_type, &hwaddr, &hwaddr_len)) + g_return_val_if_reached (NULL); + + client_id_buf = g_malloc (hwaddr_len + 1); + client_id_buf[0] = hwaddr_type; + memcpy (&client_id_buf[1], hwaddr, hwaddr_len); + return g_bytes_new_take (client_id_buf, hwaddr_len + 1); +} + +/** + * nm_utils_dhcp_client_id_systemd_node_specific_full: + * @legacy_unstable_byteorder: historically, the code would generate a iaid + * dependent on host endianness. This is undesirable, if backward compatibility + * are not a concern, generate stable endianness. + * @interface_id: a binary identifer that is hashed into the DUID. + * Comonly this is the interface-name, but it may be the MAC address. + * @interface_id_len: the length of @interface_id. + * @machine_id: the binary identifier for the machine. It is hashed + * into the DUID. It commonly is /etc/machine-id (parsed in binary as NMUuid). + * @machine_id_len: the length of the @machine_id. + * + * Systemd's sd_dhcp_client generates a default client ID (type 255, node-specific, + * RFC 4361) if no explicit client-id is set. This function duplicates that + * implementation and exposes it as (internal) API. + * + * Returns: a %GBytes of generated client-id. This function cannot fail. + */ +GBytes * +nm_utils_dhcp_client_id_systemd_node_specific_full (gboolean legacy_unstable_byteorder, + const guint8 *interface_id, + gsize interface_id_len, + const guint8 *machine_id, + gsize machine_id_len) +{ + const guint8 HASH_KEY[16] = { 0x80, 0x11, 0x8c, 0xc2, 0xfe, 0x4a, 0x03, 0xee, 0x3e, 0xd6, 0x0c, 0x6f, 0x36, 0x39, 0x14, 0x09 }; + const guint16 DUID_TYPE_EN = 2; + const guint32 SYSTEMD_PEN = 43793; + struct _nm_packed { + guint8 type; + guint32 iaid; + struct _nm_packed { + guint16 type; + union { + struct _nm_packed { + /* DUID_TYPE_EN */ + guint32 pen; + uint8_t id[8]; + } en; + }; + } duid; + } *client_id; + guint64 u64; + guint32 u32; + + g_return_val_if_fail (interface_id, NULL); + g_return_val_if_fail (interface_id_len > 0, NULL); + g_return_val_if_fail (machine_id, NULL); + g_return_val_if_fail (machine_id_len > 0, NULL); + + client_id = g_malloc (sizeof (*client_id)); + + client_id->type = 255; + + u64 = c_siphash_hash (HASH_KEY, interface_id, interface_id_len); + u32 = (u64 & 0xffffffffu) ^ (u64 >> 32); + if (legacy_unstable_byteorder) { + /* original systemd code dhcp_identifier_set_iaid() generates the iaid + * in native endianness. Do that too, to preserve compatibility + * (https://github.com/systemd/systemd/pull/10614). */ + u32 = bswap_32 (u32); + } else { + /* generate fixed byteorder, in a way that on little endian systems + * the values agree. Meaning: legacy behavior is identical to this + * on little endian. */ + u32 = be32toh (u32); + } + unaligned_write_ne32 (&client_id->iaid, u32); + + unaligned_write_be16 (&client_id->duid.type, DUID_TYPE_EN); + + unaligned_write_be32 (&client_id->duid.en.pen, SYSTEMD_PEN); + + u64 = htole64 (c_siphash_hash (HASH_KEY, machine_id, machine_id_len)); + memcpy(client_id->duid.en.id, &u64, sizeof (client_id->duid.en.id)); + + G_STATIC_ASSERT_EXPR (sizeof (*client_id) == 19); + return g_bytes_new_take (client_id, 19); +} + +GBytes * +nm_utils_dhcp_client_id_systemd_node_specific (gboolean legacy_unstable_byteorder, + const char *ifname) +{ + g_return_val_if_fail (ifname && ifname[0], NULL); + + return nm_utils_dhcp_client_id_systemd_node_specific_full (legacy_unstable_byteorder, + (const guint8 *) ifname, + strlen (ifname), + (const guint8 *) nm_utils_machine_id_bin (), + sizeof (NMUuid)); +} + +/*****************************************************************************/ + /** * nm_utils_setpgid: * @unused: unused diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 30d1360a..2e75ea62 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -61,20 +61,35 @@ void _nm_singleton_instance_register_destruction (GObject *instance); #define NM_DEFINE_SINGLETON_GETTER(TYPE, GETTER, GTYPE, ...) \ NM_DEFINE_SINGLETON_INSTANCE (TYPE); \ NM_DEFINE_SINGLETON_REGISTER (TYPE); \ +static char _already_created_##GETTER = FALSE; \ TYPE * \ GETTER (void) \ { \ if (G_UNLIKELY (!singleton_instance)) { \ - static char _already_created = FALSE; \ -\ - g_assert (!_already_created || (NM_DEFINE_SINGLETON_ALLOW_MULTIPLE)); \ - _already_created = TRUE;\ + g_assert (!(_already_created_##GETTER) || (NM_DEFINE_SINGLETON_ALLOW_MULTIPLE)); \ + (_already_created_##GETTER) = TRUE;\ singleton_instance = (g_object_new (GTYPE, ##__VA_ARGS__, NULL)); \ g_assert (singleton_instance); \ nm_singleton_instance_register (); \ nm_log_dbg (LOGD_CORE, "create %s singleton (%p)", G_STRINGIFY (TYPE), singleton_instance); \ } \ return singleton_instance; \ +} \ +_nm_unused static void \ +_nmtst_##GETTER##_reset (TYPE *instance) \ +{ \ + /* usually, the singleton can only be created once (and further instantiations + * are guarded by an assert). For testing, we need to reset the singleton to + * allow multiple instantiations. */ \ + g_assert (G_IS_OBJECT (instance)); \ + g_assert (instance == singleton_instance); \ + g_assert (_already_created_##GETTER); \ + g_object_unref (instance); \ + \ + /* require that the last unref also destroyed the singleton. If this fails, + * somebody still keeps a reference. Fix your test! */ \ + g_assert (!singleton_instance); \ + _already_created_##GETTER = FALSE; \ } /* attach @instance to the data or @owner. @owner owns a reference @@ -208,7 +223,8 @@ NMMatchSpecMatchType nm_match_spec_device (const GSList *specs, const char *driver, const char *driver_version, const char *hwaddr, - const char *s390_subchannels); + const char *s390_subchannels, + const char *dhcp_plugin); NMMatchSpecMatchType nm_match_spec_config (const GSList *specs, guint nm_version, const char *env); @@ -264,14 +280,32 @@ gboolean nm_utils_sysctl_ip_conf_is_path (int addr_family, const char *path, con gboolean nm_utils_is_specific_hostname (const char *name); -char *nm_utils_machine_id_read (void); -gboolean nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid); +struct _NMUuid; + +const char *nm_utils_machine_id_str (void); +const struct _NMUuid *nm_utils_machine_id_bin (void); +gboolean nm_utils_machine_id_is_fake (void); -gboolean nm_utils_secret_key_get (const guint8 **out_secret_key, - gsize *out_key_len); -gint64 nm_utils_secret_key_get_timestamp (void); +const char *nm_utils_boot_id_str (void); +const struct _NMUuid *nm_utils_boot_id_bin (void); -const char *nm_utils_get_boot_id (void); +gboolean nm_utils_host_id_get (const guint8 **out_host_id, + gsize *out_host_id_len); +gint64 nm_utils_host_id_get_timestamp_ns (void); + +/*****************************************************************************/ + +int nm_utils_arp_type_detect_from_hwaddrlen (gsize hwaddr_len); + +gboolean nm_utils_arp_type_validate_hwaddr (int arp_type, + const guint8 *hwaddr, + gsize hwaddr_len); + +gboolean nm_utils_arp_type_get_hwaddr_relevant_part (int arp_type, + const guint8 **hwaddr, + gsize *hwaddr_len); + +/*****************************************************************************/ /* IPv6 Interface Identifier helpers */ @@ -326,6 +360,7 @@ typedef enum { NMUtilsStableType nm_utils_stable_id_parse (const char *stable_id, const char *deviceid, + const char *hwaddr, const char *bootid, const char *uuid, char **out_generated); @@ -338,8 +373,8 @@ gboolean nm_utils_ipv6_addr_set_stable_privacy_impl (NMUtilsStableType stable_ty const char *ifname, const char *network_id, guint32 dad_counter, - guint8 *secret_key, - gsize key_len, + guint8 *host_id, + gsize host_id_len, GError **error); gboolean nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType id_type, @@ -353,8 +388,8 @@ char *nm_utils_hw_addr_gen_random_eth (const char *current_mac_address, const char *generate_mac_address_mask); char *nm_utils_hw_addr_gen_stable_eth_impl (NMUtilsStableType stable_type, const char *stable_id, - const guint8 *secret_key, - gsize key_len, + const guint8 *host_id, + gsize host_id_len, const char *ifname, const char *current_mac_address, const char *generate_mac_address_mask); @@ -364,6 +399,23 @@ char *nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, const char *current_mac_address, const char *generate_mac_address_mask); +/*****************************************************************************/ + +GBytes *nm_utils_dhcp_client_id_mac (int arp_type, + const guint8 *hwaddr, + gsize hwaddr_len); + +GBytes *nm_utils_dhcp_client_id_systemd_node_specific_full (gboolean legacy_unstable_byteorder, + const guint8 *interface_id, + gsize interface_id_len, + const guint8 *machine_id, + gsize machine_id_len); + +GBytes *nm_utils_dhcp_client_id_systemd_node_specific (gboolean legacy_unstable_byteorder, + const char *ifname); + +/*****************************************************************************/ + void nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete, gsize len); void nm_utils_setpgid (gpointer unused); diff --git a/src/nm-dbus-manager.c b/src/nm-dbus-manager.c index ca7bde57..a5c7c12b 100644 --- a/src/nm-dbus-manager.c +++ b/src/nm-dbus-manager.c @@ -1538,6 +1538,18 @@ nm_dbus_manager_acquire_bus (NMDBusManager *self) return FALSE; } + registration_id = g_dbus_connection_register_object (connection, + OBJECT_MANAGER_SERVER_BASE_PATH, + NM_UNCONST_PTR (GDBusInterfaceInfo, &interface_info_objmgr), + &dbus_vtable_objmgr, + self, + NULL, + &error); + if (!registration_id) { + _LOGE ("failure to register object manager: %s", error->message); + return FALSE; + } + ret = _nm_dbus_proxy_call_sync (proxy, "RequestName", g_variant_new ("(su)", @@ -1550,6 +1562,7 @@ nm_dbus_manager_acquire_bus (NMDBusManager *self) if (!ret) { _LOGE ("fatal failure to acquire D-Bus service \"%s"": %s", NM_DBUS_SERVICE, error->message); + g_dbus_connection_unregister_object(connection, registration_id); return FALSE; } @@ -1557,18 +1570,7 @@ nm_dbus_manager_acquire_bus (NMDBusManager *self) if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { _LOGE ("fatal failure to acquire D-Bus service \"%s\" (%u). Service already taken", NM_DBUS_SERVICE, (guint) result); - return FALSE; - } - - registration_id = g_dbus_connection_register_object (connection, - OBJECT_MANAGER_SERVER_BASE_PATH, - NM_UNCONST_PTR (GDBusInterfaceInfo, &interface_info_objmgr), - &dbus_vtable_objmgr, - self, - NULL, - &error); - if (!registration_id) { - _LOGE ("failure to register object manager: %s", error->message); + g_dbus_connection_unregister_object(connection, registration_id); return FALSE; } diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 6604711c..ce7f7fc4 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -2996,29 +2996,19 @@ nm_ip4_config_hash (const NMIP4Config *self, GChecksum *sum, gboolean dns_only) gboolean nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b) { - GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1); - GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1); - guchar a_data[20], b_data[20]; - gsize a_len = sizeof (a_data); - gsize b_len = sizeof (b_data); - gboolean equal; + nm_auto_free_checksum GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1); + nm_auto_free_checksum GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1); + guint8 a_data[NM_UTILS_CHECKSUM_LENGTH_SHA1]; + guint8 b_data[NM_UTILS_CHECKSUM_LENGTH_SHA1]; if (a) nm_ip4_config_hash (a, a_checksum, FALSE); if (b) nm_ip4_config_hash (b, b_checksum, FALSE); - g_checksum_get_digest (a_checksum, a_data, &a_len); - g_checksum_get_digest (b_checksum, b_data, &b_len); - - nm_assert (a_len == sizeof (a_data)); - nm_assert (b_len == sizeof (b_data)); - equal = !memcmp (a_data, b_data, a_len); - - g_checksum_free (a_checksum); - g_checksum_free (b_checksum); - - return equal; + nm_utils_checksum_get_digest (a_checksum, a_data); + nm_utils_checksum_get_digest (b_checksum, b_data); + return !memcmp (a_data, b_data, sizeof (a_data)); } /*****************************************************************************/ diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index a8c4aecd..42240e69 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -2426,29 +2426,19 @@ nm_ip6_config_hash (const NMIP6Config *self, GChecksum *sum, gboolean dns_only) gboolean nm_ip6_config_equal (const NMIP6Config *a, const NMIP6Config *b) { - GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1); - GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1); - guchar a_data[20], b_data[20]; - gsize a_len = sizeof (a_data); - gsize b_len = sizeof (b_data); - gboolean equal; + nm_auto_free_checksum GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1); + nm_auto_free_checksum GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1); + guint8 a_data[NM_UTILS_CHECKSUM_LENGTH_SHA1]; + guint8 b_data[NM_UTILS_CHECKSUM_LENGTH_SHA1]; if (a) nm_ip6_config_hash (a, a_checksum, FALSE); if (b) nm_ip6_config_hash (b, b_checksum, FALSE); - g_checksum_get_digest (a_checksum, a_data, &a_len); - g_checksum_get_digest (b_checksum, b_data, &b_len); - - nm_assert (a_len == sizeof (a_data)); - nm_assert (b_len == sizeof (b_data)); - equal = !memcmp (a_data, b_data, a_len); - - g_checksum_free (a_checksum); - g_checksum_free (b_checksum); - - return equal; + nm_utils_checksum_get_digest (a_checksum, a_data); + nm_utils_checksum_get_digest (b_checksum, b_data); + return !memcmp (a_data, b_data, sizeof (a_data)); } /*****************************************************************************/ diff --git a/src/nm-manager.c b/src/nm-manager.c index 7598995d..3ddc3b92 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1596,7 +1596,12 @@ again: static gboolean device_is_wake_on_lan (NMPlatform *platform, NMDevice *device) { - return nm_platform_link_get_wake_on_lan (platform, nm_device_get_ip_ifindex (device)); + int ifindex; + + ifindex = nm_device_get_ip_ifindex (device); + if (ifindex <= 0) + return FALSE; + return nm_platform_link_get_wake_on_lan (platform, ifindex); } static void @@ -2816,18 +2821,21 @@ device_connectivity_changed (NMDevice *device, best_state = nm_device_get_connectivity_state (device); if (best_state < NM_CONNECTIVITY_FULL) { + /* FIXME: is this really correct, to considere devices that don't have + * (the best) default route for connectivity checking? */ c_list_for_each_entry (dev, &priv->devices_lst_head, devices_lst) { state = nm_device_get_connectivity_state (dev); - if (state <= best_state) + if (nm_connectivity_state_cmp (state, best_state) <= 0) continue; best_state = state; - if (best_state >= NM_CONNECTIVITY_FULL) { + if (nm_connectivity_state_cmp (best_state, NM_CONNECTIVITY_FULL) >= 0) { /* it doesn't get better than this. */ break; } } } nm_assert (best_state <= NM_CONNECTIVITY_FULL); + nm_assert (nm_connectivity_state_cmp (best_state, NM_CONNECTIVITY_FULL) <= 0); if (best_state != priv->connectivity_state) { priv->connectivity_state = best_state; diff --git a/src/nm-policy.c b/src/nm-policy.c index 7f8c665c..a5960105 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -1751,17 +1751,14 @@ device_state_changed (NMDevice *device, NMSettingConnection *s_con = NULL; switch (nm_device_state_reason_check (reason)) { - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED: - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING: - case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED: case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED: - case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG: case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT: - case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED: case NM_DEVICE_STATE_REASON_GSM_APN_FAILED: - /* Block autoconnect of the just-failed connection for situations - * where a retry attempt would just fail again. + /* Block autoconnection at settings level if there is any settings-specific + * error reported by the modem (e.g. wrong SIM-PIN or wrong APN). Do not block + * autoconnection at settings level for errors in the device domain (e.g. + * a missing SIM or wrong modem initialization). */ if (sett_conn) { nm_settings_connection_autoconnect_blocked_reason_set (sett_conn, diff --git a/src/nm-test-utils-core.h b/src/nm-test-utils-core.h index 5e89cb39..dbfe71ad 100644 --- a/src/nm-test-utils-core.h +++ b/src/nm-test-utils-core.h @@ -88,8 +88,8 @@ nmtst_platform_ip4_address_full (const char *address, const char *peer_address, { NMPlatformIP4Address *addr = nmtst_platform_ip4_address (address, peer_address, plen); - G_STATIC_ASSERT (IFNAMSIZ == sizeof (addr->label)); - g_assert (!label || strlen (label) < IFNAMSIZ); + G_STATIC_ASSERT (NMP_IFNAMSIZ == sizeof (addr->label)); + g_assert (!label || strlen (label) < NMP_IFNAMSIZ); addr->ifindex = ifindex; addr->addr_source = source; diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 82f9e2fb..ef69b391 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "nm-utils.h" diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index e73d5d8c..c0224fff 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -2031,7 +2031,7 @@ _wireguard_update_from_peers_nla (CList *peers, } } if (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]) - peer_c->data.persistent_keepalive_interval = nla_get_u64 (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]); + peer_c->data.persistent_keepalive_interval = nla_get_u16 (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]); if (tb[WGPEER_A_LAST_HANDSHAKE_TIME]) nla_memcpy (&peer_c->data.last_handshake_time, tb[WGPEER_A_LAST_HANDSHAKE_TIME], sizeof (peer_c->data.last_handshake_time)); if (tb[WGPEER_A_RX_BYTES]) @@ -5737,12 +5737,13 @@ static gboolean link_set_sriov_params (NMPlatform *platform, int ifindex, guint num_vfs, - int autoprobe) + NMTernary autoprobe) { nm_auto_pop_netns NMPNetns *netns = NULL; nm_auto_close int dirfd = -1; - gboolean current_autoprobe; - guint total, current_num; + int current_autoprobe; + guint total; + gint64 current_num; char ifname[IFNAMSIZ]; char buf[64]; @@ -5775,14 +5776,14 @@ link_set_sriov_params (NMPlatform *platform, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_numvfs"), - 10, 0, G_MAXUINT, 0); + 10, 0, G_MAXUINT, -1); current_autoprobe = nm_platform_sysctl_get_int_checked (platform, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_drivers_autoprobe"), - 10, 0, G_MAXUINT, 0); + 10, 0, 1, -1); if ( current_num == num_vfs - && (autoprobe == -1 || current_autoprobe == autoprobe)) + && (autoprobe == NM_TERNARY_DEFAULT || current_autoprobe == autoprobe)) return TRUE; if (current_num != 0) { @@ -5800,14 +5801,14 @@ link_set_sriov_params (NMPlatform *platform, if (num_vfs == 0) return TRUE; - if ( autoprobe >= 0 + if ( NM_IN_SET (autoprobe, NM_TERNARY_TRUE, NM_TERNARY_FALSE) && current_autoprobe != autoprobe && !nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_drivers_autoprobe"), - nm_sprintf_buf (buf, "%d", autoprobe))) { - _LOGW ("link: couldn't set SR-IOV drivers-autoprobe to %d: %s", autoprobe, strerror (errno)); + nm_sprintf_buf (buf, "%d", (int) autoprobe))) { + _LOGW ("link: couldn't set SR-IOV drivers-autoprobe to %d: %s", (int) autoprobe, strerror (errno)); return FALSE; } @@ -7044,7 +7045,7 @@ ip6_address_add (NMPlatform *platform, ifindex, &addr, plen, - &peer_addr, + IN6_IS_ADDR_UNSPECIFIED (&peer_addr) ? NULL : &peer_addr, flags, RT_SCOPE_UNIVERSE, lifetime, diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c index 216b1547..cc43b27f 100644 --- a/src/platform/nm-platform-utils.c +++ b/src/platform/nm-platform-utils.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 7ddcf41a..757a0f43 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -1192,13 +1193,29 @@ nm_platform_link_refresh (NMPlatform *self, int ifindex) return TRUE; } -static guint -_link_get_flags (NMPlatform *self, int ifindex) +int +nm_platform_link_get_ifi_flags (NMPlatform *self, + int ifindex, + guint requested_flags) { const NMPlatformLink *pllink; - pllink = nm_platform_link_get (self, ifindex); - return pllink ? pllink->n_ifi_flags : IFF_NOARP; + _CHECK_SELF (self, klass, -EINVAL); + + if (ifindex <= 0) + return -EINVAL; + + /* include invisible links (only in netlink, not udev). */ + pllink = NMP_OBJECT_CAST_LINK (nm_platform_link_get_obj (self, ifindex, FALSE)); + if (!pllink) + return -ENODEV; + + /* Errors are signaled as negative values. That means, you cannot request + * the most significant bit (2^31) with this API. Assert against that. */ + nm_assert ((int) requested_flags >= 0); + nm_assert (requested_flags < (guint) G_MAXINT); + + return (int) (pllink->n_ifi_flags & requested_flags); } /** @@ -1211,9 +1228,7 @@ _link_get_flags (NMPlatform *self, int ifindex) gboolean nm_platform_link_is_up (NMPlatform *self, int ifindex) { - _CHECK_SELF (self, klass, FALSE); - - return NM_FLAGS_HAS (_link_get_flags (self, ifindex), IFF_UP); + return nm_platform_link_get_ifi_flags (self, ifindex, IFF_UP) == IFF_UP; } /** @@ -1244,9 +1259,15 @@ nm_platform_link_is_connected (NMPlatform *self, int ifindex) gboolean nm_platform_link_uses_arp (NMPlatform *self, int ifindex) { - _CHECK_SELF (self, klass, FALSE); + int f; + + f = nm_platform_link_get_ifi_flags (self, ifindex, IFF_NOARP); - return !NM_FLAGS_HAS (_link_get_flags (self, ifindex), IFF_NOARP); + if (f < 0) + return FALSE; + if (f == IFF_NOARP) + return FALSE; + return TRUE; } /** @@ -1468,25 +1489,25 @@ nm_platform_link_supports_sriov (NMPlatform *self, int ifindex) * @self: platform instance * @ifindex: the index of the interface to change * @num_vfs: the number of VFs to create - * @autoprobe: -1 to keep the current autoprobe-drivers value, - * or {0,1} to set a new value + * @autoprobe: the new autoprobe-drivers value (pass + * %NM_TERNARY_DEFAULT to keep current value) */ gboolean nm_platform_link_set_sriov_params (NMPlatform *self, int ifindex, guint num_vfs, - int autoprobe) + NMTernary autoprobe) { _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (NM_IN_SET (autoprobe, -1, 0, 1), FALSE); _LOGD ("link: setting %u total VFs and autoprobe %d for %s (%d)", num_vfs, - autoprobe, + (int) autoprobe, nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)), ifindex); + return klass->link_set_sriov_params (self, ifindex, num_vfs, autoprobe); } diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 11495aff..7e91f1f3 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -21,12 +21,6 @@ #ifndef __NETWORKMANAGER_PLATFORM_H__ #define __NETWORKMANAGER_PLATFORM_H__ -#include -#include -#include -#include -#include - #include "nm-dbus-interface.h" #include "nm-core-types-internal.h" @@ -53,6 +47,12 @@ /*****************************************************************************/ +/* IFNAMSIZ is both defined in and . In the past, these + * headers conflicted, so we cannot simply include either of them in a header-file.*/ +#define NMP_IFNAMSIZ 16 + +/*****************************************************************************/ + struct udev_device; typedef gboolean (*NMPObjectPredicateFunc) (const NMPObject *obj, @@ -208,7 +208,7 @@ typedef enum { struct _NMPlatformLink { __NMPlatformObject_COMMON; - char name[IFNAMSIZ]; + char name[NMP_IFNAMSIZ]; NMLinkType type; /* rtnl_link_get_type(), IFLA_INFO_KIND. */ @@ -355,7 +355,7 @@ struct _NMPlatformIP4Address { * */ in_addr_t peer_address; /* PTP peer address */ - char label[IFNAMSIZ]; + char label[NMP_IFNAMSIZ]; }; /** @@ -1105,12 +1105,12 @@ const char *nm_platform_error_to_string (NMPlatformError error, ((const char *) NULL), -1, (path) #define NMP_SYSCTL_PATHID_NETDIR_unsafe(dirfd, ifname, path) \ - nm_sprintf_bufa (NM_STRLEN ("net:/sys/class/net//\0") + IFNAMSIZ + strlen (path), \ + nm_sprintf_bufa (NM_STRLEN ("net:/sys/class/net//\0") + NMP_IFNAMSIZ + strlen (path), \ "net:/sys/class/net/%s/%s", (ifname), (path)), \ (dirfd), (path) #define NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, path) \ - nm_sprintf_bufa (NM_STRLEN ("net:/sys/class/net//"path"/\0") + IFNAMSIZ, \ + nm_sprintf_bufa (NM_STRLEN ("net:/sys/class/net//"path"/\0") + NMP_IFNAMSIZ, \ "net:/sys/class/net/%s/%s", (ifname), path), \ (dirfd), (""path"") @@ -1165,6 +1165,7 @@ int nm_platform_link_get_ifindex (NMPlatform *self, const char *name); const char *nm_platform_link_get_name (NMPlatform *self, int ifindex); NMLinkType nm_platform_link_get_type (NMPlatform *self, int ifindex); gboolean nm_platform_link_is_software (NMPlatform *self, int ifindex); +int nm_platform_link_get_ifi_flags (NMPlatform *self, int ifindex, guint requested_flags); gboolean nm_platform_link_is_up (NMPlatform *self, int ifindex); gboolean nm_platform_link_is_connected (NMPlatform *self, int ifindex); gboolean nm_platform_link_uses_arp (NMPlatform *self, int ifindex); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index fdc27440..f7fa6cb3 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -24,6 +24,7 @@ #include #include +#include #include #include "nm-utils.h" diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index 3dedbd10..1cb2f516 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -787,8 +787,6 @@ nmtstp_ip_address_assert_lifetime (const NMPlatformIPAddress *addr, if (lft == NM_PLATFORM_LIFETIME_PERMANENT) g_assert_cmpint (adr, ==, NM_PLATFORM_LIFETIME_PERMANENT); else { - g_assert_cmpint (adr, <=, lft); - g_assert_cmpint (offset, <=, adr); g_assert_cmpint (adr - offset, <=, lft + CHECK_LIFETIME_MAX_DIFF); g_assert_cmpint (adr - offset, >=, lft - CHECK_LIFETIME_MAX_DIFF); } diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index 1baadfa1..7e81baea 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -21,6 +21,9 @@ #include #include #include +#include +#include +#include #include "platform/nm-platform.h" #include "platform/nmp-object.h" diff --git a/src/platform/wifi/nm-wifi-utils-nl80211.c b/src/platform/wifi/nm-wifi-utils-nl80211.c index b3bb2bb6..39e3f971 100644 --- a/src/platform/wifi/nm-wifi-utils-nl80211.c +++ b/src/platform/wifi/nm-wifi-utils-nl80211.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "platform/nm-netlink.h" #include "nm-wifi-utils-private.h" diff --git a/src/platform/wpan/nm-wpan-utils.c b/src/platform/wpan/nm-wpan-utils.c index 0544539a..882c4eec 100644 --- a/src/platform/wpan/nm-wpan-utils.c +++ b/src/platform/wpan/nm-wpan-utils.c @@ -21,6 +21,8 @@ #include "nm-wpan-utils.h" +#include + #include "platform/linux/nl802154.h" #include "platform/nm-netlink.h" diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 4c25d0e9..6f47e14d 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -640,7 +640,9 @@ nm_settings_connection_update (NMSettingsConnection *self, gboolean replaced = FALSE; gs_free char *logmsg_change = NULL; GError *local = NULL; + gs_unref_object NMConnection *simple = NULL; gs_unref_variant GVariant *con_agent_secrets = NULL; + gs_unref_variant GVariant *new_agent_secrets = NULL; g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); @@ -681,6 +683,16 @@ nm_settings_connection_update (NMSettingsConnection *self, replace_connection = reread_connection ?: new_connection; + /* Save agent-owned secrets from the new connection for later use */ + if (new_connection) { + simple = nm_simple_connection_new_clone (new_connection); + nm_connection_clear_secrets_with_flags (simple, + secrets_filter_cb, + GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); + new_agent_secrets = nm_connection_to_dbus (simple, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); + g_clear_object (&simple); + } + /* Disconnect the changed signal to ensure we don't set Unsaved when * it's not required. */ @@ -691,7 +703,6 @@ nm_settings_connection_update (NMSettingsConnection *self, && !nm_connection_compare (nm_settings_connection_get_connection (self), replace_connection, NM_SETTING_COMPARE_FLAG_EXACT)) { - gs_unref_object NMConnection *simple = NULL; if (log_diff_name) { nm_utils_log_connection_diff (replace_connection, nm_settings_connection_get_connection (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ", @@ -738,6 +749,15 @@ nm_settings_connection_update (NMSettingsConnection *self, (void) nm_connection_update_secrets (nm_settings_connection_get_connection (self), NULL, con_agent_secrets, NULL); } + /* Apply agent-owned secrets from the new connection so that + * they can be sent to agents */ + if (new_agent_secrets) { + (void) nm_connection_update_secrets (nm_settings_connection_get_connection (self), + NULL, + new_agent_secrets, + NULL); + } + nm_settings_connection_recheck_visibility (self); if ( replaced diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 new file mode 100755 index 00000000..54f02da6 --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 @@ -0,0 +1,21 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = STATIC +iface.ipaddress = 192.168.32.72 +iface.subnet_mask = 255.255.252.0 +iface.gateway = 192.168.35.254 +iface.primary_dns = 10000.500.250.1 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +EOF + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 new file mode 100755 index 00000000..ebd7a9ca --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 @@ -0,0 +1,21 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = STATIC +iface.ipaddress = 192.168.32.72 +iface.subnet_mask = 255.255.252.0 +iface.gateway = 192.168.35.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = blah.foo.bar.baz +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +EOF + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry new file mode 100755 index 00000000..4e326048 --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry @@ -0,0 +1,20 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = STATIC +iface.ipaddress 192.168.32.72 +iface.subnet_mask = 255.255.252.0 +iface.gateway = 192.168.35.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway new file mode 100755 index 00000000..5390a6c3 --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway @@ -0,0 +1,21 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = STATIC +iface.ipaddress = 192.168.32.72 +iface.subnet_mask = 255.255.252.0 +iface.gateway = bb.cc.dd.ee +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +EOF + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr new file mode 100755 index 00000000..b41cd1f1 --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr @@ -0,0 +1,21 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = STATIC +iface.ipaddress = aa.bb.cc.dd +iface.subnet_mask = 255.255.252.0 +iface.gateway = 192.168.35.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +EOF + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record new file mode 100755 index 00000000..22b34e6f --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record @@ -0,0 +1,18 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = DHCP +iface.gateway = 10.16.52.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +EOF + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp b/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp new file mode 100755 index 00000000..556b0586 --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp @@ -0,0 +1,33 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = DHCP +iface.gateway = 10.16.52.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f1 +iface.bootproto = DHCP +iface.gateway = 10.16.52.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth1 +node.name = iqn.1.2008-11.com.blahblah:iscsi1 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +EOF + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-static b/src/settings/plugins/ibft/tests/iscsiadm-test-static new file mode 100755 index 00000000..51711480 --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-static @@ -0,0 +1,35 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = STATIC +iface.ipaddress = 192.168.32.72 +iface.subnet_mask = 255.255.252.0 +iface.gateway = 192.168.35.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f1 +iface.bootproto = DHCP +iface.gateway = 10.16.52.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth1 +node.name = iqn.1.2008-11.com.blahblah:iscsi1 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +EOF + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-vlan b/src/settings/plugins/ibft/tests/iscsiadm-test-vlan new file mode 100755 index 00000000..59b80bd0 --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-vlan @@ -0,0 +1,19 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD 6.2.0.873-21 +iface.initiatorname = iqn.2010-04.org.ipxe:d05faa97-c4be-44f6-a723-efde9aa399a0 +iface.transport_name = tcp +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = STATIC +iface.ipaddress = 192.168.6.200 +iface.subnet_mask = 255.255.255.0 +iface.vlan_id = 123 +iface.net_ifacename = eth0 +node.name = iqn.2003-01.org.x:disk1 +node.conn[0].address = 192.168.6.32 +node.conn[0].port = 3260 +node.boot_lun = 01000000 +# END RECORD +EOF + diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 09a37991..261dfea2 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -189,7 +189,7 @@ _secret_password_raw_to_bytes (const char *ifcfg_key, password_raw += 2; secret = nm_secret_buf_new (strlen (password_raw) / 2 + 3); - if (!_nm_utils_str2bin_full (password_raw, FALSE, ":", secret->bin, secret->len, &len)) { + if (!_nm_utils_hexstr2bin_full (password_raw, FALSE, FALSE, ":", 0, secret->bin, secret->len, &len)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Invalid hex password in %s", ifcfg_key); @@ -2226,20 +2226,19 @@ make_sriov_setting (shvarFile *ifcfg) { gs_unref_hashtable GHashTable *keys = NULL; gs_unref_ptrarray GPtrArray *vfs = NULL; - NMTernary autoprobe_drivers; + int autoprobe_drivers; NMSettingSriov *s_sriov; - int total_vfs; + gint64 total_vfs; - total_vfs = svGetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", 10, 0, G_MAXINT32, 0); - if (!total_vfs) - return NULL; + + total_vfs = svGetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", 10, 0, G_MAXUINT32, -1); autoprobe_drivers = svGetValueInt64 (ifcfg, "SRIOV_AUTOPROBE_DRIVERS", 10, - NM_TERNARY_FALSE, + NM_TERNARY_DEFAULT, NM_TERNARY_TRUE, - NM_TERNARY_DEFAULT); + -2); keys = svGetKeys (ifcfg, SV_KEY_TYPE_SRIOV_VF); if (keys) { @@ -2261,7 +2260,7 @@ make_sriov_setting (shvarFile *ifcfg) key += NM_STRLEN ("SRIOV_VF"); - vf = _nm_utils_sriov_vf_from_strparts (key, value, &error); + vf = _nm_utils_sriov_vf_from_strparts (key, value, TRUE, &error); if (!vf) { PARSE_WARNING ("ignoring invalid SR-IOV VF '%s %s': %s", key, value, error->message); @@ -2273,11 +2272,21 @@ make_sriov_setting (shvarFile *ifcfg) } } + /* Create the setting when at least one key is set */ + if ( total_vfs < 0 + && !vfs + && autoprobe_drivers < NM_TERNARY_DEFAULT) + return NULL; + s_sriov = (NMSettingSriov *) nm_setting_sriov_new (); + + autoprobe_drivers = NM_MAX (autoprobe_drivers, NM_TERNARY_DEFAULT); + total_vfs = NM_MAX (total_vfs, 0); + g_object_set (s_sriov, - NM_SETTING_SRIOV_TOTAL_VFS, total_vfs, + NM_SETTING_SRIOV_TOTAL_VFS, (guint) total_vfs, NM_SETTING_SRIOV_VFS, vfs, - NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, (int) autoprobe_drivers, + NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, autoprobe_drivers, NULL); return (NMSetting *) s_sriov; diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index b70690cc..f5be7520 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -2223,16 +2223,15 @@ write_sriov_setting (NMConnection *connection, shvarFile *ifcfg) svUnsetAll (ifcfg, SV_KEY_TYPE_SRIOV_VF); - s_sriov = NM_SETTING_SRIOV (nm_connection_get_setting (connection, NM_TYPE_SETTING_SRIOV)); - if (s_sriov) - num = nm_setting_sriov_get_total_vfs (s_sriov); - if (num == 0) { + s_sriov = NM_SETTING_SRIOV (nm_connection_get_setting (connection, + NM_TYPE_SETTING_SRIOV)); + if (!s_sriov) { svUnsetValue (ifcfg, "SRIOV_TOTAL_VFS"); svUnsetValue (ifcfg, "SRIOV_AUTOPROBE_DRIVERS"); return; } - svSetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", num); + svSetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", nm_setting_sriov_get_total_vfs (s_sriov)); b = nm_setting_sriov_get_autoprobe_drivers (s_sriov); if (b != NM_TERNARY_DEFAULT) diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c index b66eedc7..f0e64d1d 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c @@ -282,8 +282,10 @@ initialize (NMSettingsPlugin *plugin) g_hash_table_iter_init (&iter, priv->eni_ifaces); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) conn)) { - _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self), - NM_SETTINGS_CONNECTION (conn)); + if (conn) { + _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_CONNECTION (conn)); + } } } } diff --git a/src/supplicant/nm-supplicant-config.c b/src/supplicant/nm-supplicant-config.c index 043b5550..cfb33008 100644 --- a/src/supplicant/nm-supplicant-config.c +++ b/src/supplicant/nm-supplicant-config.c @@ -648,31 +648,28 @@ add_string_val (NMSupplicantConfig *self, static void wep128_passphrase_hash (const char *input, - size_t input_len, - guint8 *out_digest, - size_t *out_digest_len) + gsize input_len, + guint8 *digest /* 13 bytes */) { - GChecksum *sum; + nm_auto_free_checksum GChecksum *sum = NULL; + guint8 md5[NM_UTILS_CHECKSUM_LENGTH_MD5]; guint8 data[64]; int i; - g_return_if_fail (out_digest != NULL); - g_return_if_fail (out_digest_len != NULL); - g_return_if_fail (*out_digest_len >= 16); + nm_assert (input); + nm_assert (input_len); + nm_assert (digest); /* Get at least 64 bytes by repeating the passphrase into the buffer */ for (i = 0; i < sizeof (data); i++) data[i] = input[i % input_len]; sum = g_checksum_new (G_CHECKSUM_MD5); - g_assert (sum); g_checksum_update (sum, data, sizeof (data)); - g_checksum_get_digest (sum, out_digest, out_digest_len); - g_checksum_free (sum); + nm_utils_checksum_get_digest (sum, md5); - g_assert (*out_digest_len == 16); /* WEP104 keys are 13 bytes in length (26 hex characters) */ - *out_digest_len = 13; + memcpy (digest, md5, 13); } static gboolean @@ -682,9 +679,10 @@ add_wep_key (NMSupplicantConfig *self, NMWepKeyType wep_type, GError **error) { - size_t key_len = key ? strlen (key) : 0; + gsize key_len; - if (!key || !key_len) + if ( !key + || (key_len = strlen (key)) == 0) return TRUE; if (wep_type == NM_WEP_KEY_TYPE_UNKNOWN) { @@ -723,11 +721,10 @@ add_wep_key (NMSupplicantConfig *self, return FALSE; } } else if (wep_type == NM_WEP_KEY_TYPE_PASSPHRASE) { - guint8 digest[16]; - size_t digest_len = sizeof (digest); + guint8 digest[13]; - wep128_passphrase_hash (key, key_len, digest, &digest_len); - if (!nm_supplicant_config_add_option (self, name, (const char *) digest, digest_len, "", error)) + wep128_passphrase_hash (key, key_len, digest); + if (!nm_supplicant_config_add_option (self, name, (const char *) digest, sizeof (digest), "", error)) return FALSE; } @@ -747,6 +744,7 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, NMSupplicantConfigPrivate *priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self); const char *key_mgmt, *key_mgmt_conf, *auth_alg; const char *psk; + gboolean set_pmf; g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE); g_return_val_if_fail (setting != NULL, FALSE); @@ -834,13 +832,14 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE; /* Check if we actually support PMF */ + set_pmf = TRUE; if (!priv->support_pmf) { if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) { g_set_error_literal (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "Supplicant does not support PMF"); return FALSE; - } else if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL) - pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE; + } + set_pmf = FALSE; } /* Only WPA-specific things when using WPA */ @@ -854,13 +853,14 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, if (!ADD_STRING_LIST_VAL (self, setting, wireless_security, group, groups, "group", ' ', TRUE, NULL, error)) return FALSE; - if ( !nm_streq (key_mgmt, "wpa-none") + if ( set_pmf + && !nm_streq (key_mgmt, "wpa-none") && NM_IN_SET (pmf, - NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL, + NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE, NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED)) { if (!nm_supplicant_config_add_option (self, "ieee80211w", - pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL ? "1" : "2", + pmf == NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE ? "0" : "2", -1, NULL, error)) diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c index 5237acb2..0af9ebdb 100644 --- a/src/supplicant/nm-supplicant-interface.c +++ b/src/supplicant/nm-supplicant-interface.c @@ -555,6 +555,26 @@ iface_check_netreply_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ iface_check_ready (self); } +static void +iface_set_pmf_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +{ + NMSupplicantInterface *self; + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + + variant = g_dbus_proxy_call_finish (proxy, result, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + + /* This can fail if the supplicant doesn't support PMF */ + if (error) + _LOGD ("failed to set Pmf=1: %s", error->message); + + iface_check_ready (self); +} + NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self) { @@ -1155,8 +1175,22 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ NULL, NULL); - /* Check whether NetworkReply and AP mode are supported */ + /* Initialize global PMF setting to 'optional' */ priv->ready_count = 1; + g_dbus_proxy_call (priv->iface_proxy, + DBUS_INTERFACE_PROPERTIES ".Set", + g_variant_new ("(ssv)", + WPAS_DBUS_IFACE_INTERFACE, + "Pmf", + g_variant_new_string ("1")), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->init_cancellable, + (GAsyncReadyCallback) iface_set_pmf_cb, + self); + + /* Check whether NetworkReply and AP mode are supported */ + priv->ready_count++; g_dbus_proxy_call (priv->iface_proxy, "NetworkReply", g_variant_new ("(oss)", diff --git a/src/supplicant/tests/test-supplicant-config.c b/src/supplicant/tests/test-supplicant-config.c index 36831e67..d7ec1fe2 100644 --- a/src/supplicant/tests/test-supplicant-config.c +++ b/src/supplicant/tests/test-supplicant-config.c @@ -359,8 +359,8 @@ test_wifi_wpa_psk (const char *detail, NMTST_EXPECT_NM_INFO ("Config: added 'pairwise' value 'TKIP CCMP'"); NMTST_EXPECT_NM_INFO ("Config: added 'group' value 'TKIP CCMP'"); switch (pmf) { - case NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL: - NMTST_EXPECT_NM_INFO ("Config: added 'ieee80211w' value '1'"); + case NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE: + NMTST_EXPECT_NM_INFO ("Config: added 'ieee80211w' value '0'"); break; case NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED: NMTST_EXPECT_NM_INFO ("Config: added 'ieee80211w' value '2'"); diff --git a/src/systemd/meson.build b/src/systemd/meson.build index 870721b0..1bf1ea41 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -49,7 +49,8 @@ sources = files( 'src/libsystemd/sd-id128/id128-util.c', 'src/libsystemd/sd-id128/sd-id128.c', 'src/shared/dns-domain.c', - 'nm-sd.c' + 'nm-sd.c', + 'nm-sd-utils.c', ) incs = [ diff --git a/src/systemd/nm-sd-utils.c b/src/systemd/nm-sd-utils.c new file mode 100644 index 00000000..c6c4c123 --- /dev/null +++ b/src/systemd/nm-sd-utils.c @@ -0,0 +1,61 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-sd-utils.h" + +#include "nm-core-internal.h" + +#include "nm-sd-adapt.h" + +#include "path-util.h" +#include "sd-id128.h" + +/*****************************************************************************/ + +gboolean +nm_sd_utils_path_equal (const char *a, const char *b) +{ + return path_equal (a, b); +} + +char * +nm_sd_utils_path_simplify (char *path, gboolean kill_dots) +{ + return path_simplify (path, kill_dots); +} + +const char * +nm_sd_utils_path_startswith (const char *path, const char *prefix) +{ + return path_startswith (path, prefix); +} + +/*****************************************************************************/ + +NMUuid * +nm_sd_utils_id128_get_machine (NMUuid *out_uuid) +{ + g_assert (out_uuid); + + G_STATIC_ASSERT_EXPR (sizeof (*out_uuid) == sizeof (sd_id128_t)); + if (sd_id128_get_machine ((sd_id128_t *) out_uuid) < 0) + return NULL; + return out_uuid; +} diff --git a/src/systemd/nm-sd-utils.h b/src/systemd/nm-sd-utils.h new file mode 100644 index 00000000..0af514eb --- /dev/null +++ b/src/systemd/nm-sd-utils.h @@ -0,0 +1,38 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#ifndef __NM_SD_UTILS_H__ +#define __NM_SD_UTILS_H__ + +/*****************************************************************************/ + +gboolean nm_sd_utils_path_equal (const char *a, const char *b); + +char *nm_sd_utils_path_simplify (char *path, gboolean kill_dots); + +const char *nm_sd_utils_path_startswith (const char *path, const char *prefix); + +/*****************************************************************************/ + +struct _NMUuid; + +struct _NMUuid *nm_sd_utils_id128_get_machine (struct _NMUuid *out_uuid); + +/*****************************************************************************/ + +#endif /* __NM_SD_UTILS_H__ */ diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c index 5202eae3..3656a011 100644 --- a/src/systemd/src/basic/path-util.c +++ b/src/systemd/src/basic/path-util.c @@ -380,7 +380,6 @@ char *path_simplify(char *path, bool kill_dots) { return path; } -#if 0 /* NM_IGNORED */ char* path_startswith(const char *path, const char *prefix) { assert(path); assert(prefix); @@ -423,7 +422,6 @@ char* path_startswith(const char *path, const char *prefix) { prefix += b; } } -#endif /* NM_IGNORED */ int path_compare(const char *a, const char *b) { int d; diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c index f18713e8..b1afbf00 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.c +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c @@ -25,13 +25,19 @@ #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03) #define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */ -int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) { +int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) { struct duid d; assert_cc(sizeof(d.raw) >= MAX_DUID_LEN); if (duid_len > MAX_DUID_LEN) return -EINVAL; + if (!strict) { + /* Strict validation is not requested. We only ensure that the + * DUID is not too long. */ + return 0; + } + switch (duid_type) { case DUID_TYPE_LLT: if (duid_len <= sizeof(d.llt)) diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.h b/src/systemd/src/libsystemd-network/dhcp-identifier.h index 64315d3a..e6834039 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.h +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.h @@ -52,7 +52,7 @@ struct duid { }; } _packed_; -int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len); +int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict); int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len); diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c index ff1cbf13..22970443 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-option.c +++ b/src/systemd/src/libsystemd-network/dhcp6-option.c @@ -105,7 +105,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { return -EINVAL; } - if (*buflen < len) + if (*buflen < offsetof(DHCP6Option, data) + len) return -ENOBUFS; ia_hdr = *buf; @@ -465,13 +465,15 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { case SD_DHCP6_OPTION_STATUS_CODE: - status = dhcp6_option_parse_status(option, optlen); - if (status) { + status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option)); + if (status < 0) { + r = status; + goto error; + } + if (status > 0) { log_dhcp6_client(client, "IA status %d", status); - dhcp6_lease_free_ia(ia); - r = -EINVAL; goto error; } @@ -553,6 +555,7 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * bool first = true; for (;;) { + const char *label; uint8_t c; c = optval[pos++]; @@ -560,47 +563,41 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * if (c == 0) /* End of name */ break; - else if (c <= 63) { - const char *label; - - /* Literal label */ - label = (const char *)&optval[pos]; - pos += c; - if (pos >= optlen) - return -EMSGSIZE; - - if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) { - r = -ENOMEM; - goto fail; - } - - if (first) - first = false; - else - ret[n++] = '.'; - - r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); - if (r < 0) - goto fail; - - n += r; - continue; - } else { - r = -EBADMSG; - goto fail; - } - } + if (c > 63) + return -EBADMSG; + + /* Literal label */ + label = (const char *)&optval[pos]; + pos += c; + if (pos >= optlen) + return -EMSGSIZE; + + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) + return -ENOMEM; + + if (first) + first = false; + else + ret[n++] = '.'; - if (!GREEDY_REALLOC(ret, allocated, n + 1)) { - r = -ENOMEM; - goto fail; + r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + + n += r; } + if (n == 0) + continue; + + if (!GREEDY_REALLOC(ret, allocated, n + 1)) + return -ENOMEM; + ret[n] = 0; r = strv_extend(&names, ret); if (r < 0) - goto fail; + return r; idx++; } @@ -608,7 +605,4 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * *str_arr = TAKE_PTR(names); return idx; - -fail: - return r; } diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c index 42707f10..0c385a84 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -301,27 +301,22 @@ int sd_dhcp_client_set_client_id( assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL); G_STATIC_ASSERT_EXPR (_NM_SD_MAX_CLIENT_ID_LEN == MAX_CLIENT_ID_LEN); - switch (type) { - - case ARPHRD_ETHER: - if (data_len != ETH_ALEN) - return -EINVAL; - break; - - case ARPHRD_INFINIBAND: - if (data_len != INFINIBAND_ALEN) - return -EINVAL; - break; - - default: - break; - } - if (client->client_id_len == data_len + sizeof(client->client_id.type) && client->client_id.type == type && memcmp(&client->client_id.raw.data, data, data_len) == 0) return 0; + /* For hardware types, log debug message about unexpected data length. + * + * Note that infiniband's INFINIBAND_ALEN is 20 bytes long, but only + * last last 8 bytes of the address are stable and suitable to put into + * the client-id. The caller is advised to account for that. */ + if ((type == ARPHRD_ETHER && data_len != ETH_ALEN) || + (type == ARPHRD_INFINIBAND && data_len != 8)) + log_dhcp_client(client, "Changing client ID to hardware type %u with " + "unexpected address length %zu", + type, data_len); + if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { log_dhcp_client(client, "Changing client ID on running DHCP " "client, restarting"); @@ -362,7 +357,7 @@ static int dhcp_client_set_iaid_duid_internal( assert_return(duid_len == 0 || duid != NULL, -EINVAL); if (duid != NULL) { - r = dhcp_validate_duid_len(duid_type, duid_len); + r = dhcp_validate_duid_len(duid_type, duid_len, true); if (r < 0) return r; } @@ -1688,6 +1683,8 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i client->timeout_resend = sd_event_source_unref(client->timeout_resend); + client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED); + r = client_initialize(client); if (r < 0) goto error; diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index d2402595..cac07d3e 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -279,6 +279,8 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { free(option); } + free(lease->root_path); + free(lease->timezone); free(lease->hostname); free(lease->domainname); free(lease->dns); diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c index 8444a750..f0cd2f97 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c @@ -197,9 +197,13 @@ static int dhcp6_client_set_duid_internal( assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); if (duid != NULL) { - r = dhcp_validate_duid_len(duid_type, duid_len); - if (r < 0) - return r; + r = dhcp_validate_duid_len(duid_type, duid_len, true); + if (r < 0) { + r = dhcp_validate_duid_len(duid_type, duid_len, false); + if (r < 0) + return r; + log_dhcp6_client(client, "Setting DUID of type %u with unexpected content", duid_type); + } client->duid.type = htobe16(duid_type); memcpy(&client->duid.raw.data, duid, duid_len); @@ -818,8 +822,8 @@ static int client_parse_message( uint8_t *optval; be32_t iaid_lease; - if (len < offsetof(DHCP6Option, data) || - len < offsetof(DHCP6Option, data) + be16toh(option->len)) + if (len < pos + offsetof(DHCP6Option, data) || + len < pos + offsetof(DHCP6Option, data) + be16toh(option->len)) return -ENOBUFS; optcode = be16toh(option->code); @@ -870,13 +874,14 @@ static int client_parse_message( break; case SD_DHCP6_OPTION_STATUS_CODE: - status = dhcp6_option_parse_status(option, optlen); - if (status) { + status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option)); + if (status < 0) + return status; + + if (status > 0) { log_dhcp6_client(client, "%s Status %s", dhcp6_message_type_to_string(message->type), dhcp6_message_status_to_string(status)); - dhcp6_lease_free_ia(&lease->ia); - dhcp6_lease_free_ia(&lease->pd); return -EINVAL; } @@ -1274,6 +1279,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { log_dhcp6_client(client, "T1 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); + client->lease->ia.timeout_t1 = sd_event_source_unref(client->lease->ia.timeout_t1); r = sd_event_add_time(client->event, &client->lease->ia.timeout_t1, clock_boottime_or_monotonic(), time_now + timeout, @@ -1296,6 +1302,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { log_dhcp6_client(client, "T2 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); + client->lease->ia.timeout_t2 = sd_event_source_unref(client->lease->ia.timeout_t2); r = sd_event_add_time(client->event, &client->lease->ia.timeout_t2, clock_boottime_or_monotonic(), time_now + timeout, diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c index 75fef4fa..20a05df1 100644 --- a/src/tests/config/test-config.c +++ b/src/tests/config/test-config.c @@ -25,6 +25,7 @@ #include "nm-config.h" #include "nm-test-device.h" #include "platform/nm-fake-platform.h" +#include "dhcp/nm-dhcp-manager.h" #include "nm-dbus-manager.h" #include "nm-connectivity.h" @@ -123,6 +124,24 @@ setup_config (GError **error, const char *config_file, const char *intern_config g_assert_no_error (local_error); } nm_config_cmd_line_options_free (cli); + + if (config) { + NMDhcpManager *dhcp_manager; + gpointer logging_old_state; + + logging_old_state = nmtst_logging_disable (FALSE); + + dhcp_manager = nm_dhcp_manager_get (); + g_test_assert_expected_messages (); + + nmtst_logging_reenable (logging_old_state); + + g_object_set_data_full (G_OBJECT (config), + "nmtst-config-keep-dhcp-manager-alive", + dhcp_manager, + nmtst_dhcp_manager_unget); + } + return config; } diff --git a/src/tests/test-general.c b/src/tests/test-general.c index 1e7329d1..9dce9435 100644 --- a/src/tests/test-general.c +++ b/src/tests/test-general.c @@ -22,12 +22,19 @@ #include #include +#include +#include /* need math.h for isinf() and INFINITY. No need to link with -lm */ #include #include "NetworkManagerUtils.h" #include "nm-core-internal.h" +#include "nm-core-utils.h" +#include "systemd/nm-sd-utils.h" + +#include "dns/nm-dns-manager.h" +#include "nm-connectivity.h" #include "nm-test-utils-core.h" @@ -1093,7 +1100,7 @@ static NMMatchSpecMatchType _test_match_spec_device (const GSList *specs, const char *match_str) { if (match_str && g_str_has_prefix (match_str, MATCH_S390)) - return nm_match_spec_device (specs, NULL, NULL, NULL, NULL, NULL, &match_str[NM_STRLEN (MATCH_S390)]); + return nm_match_spec_device (specs, NULL, NULL, NULL, NULL, NULL, &match_str[NM_STRLEN (MATCH_S390)], NULL); if (match_str && g_str_has_prefix (match_str, MATCH_DRIVER)) { gs_free char *s = g_strdup (&match_str[NM_STRLEN (MATCH_DRIVER)]); char *t; @@ -1103,9 +1110,9 @@ _test_match_spec_device (const GSList *specs, const char *match_str) t[0] = '\0'; t++; } - return nm_match_spec_device (specs, NULL, NULL, s, t, NULL, NULL); + return nm_match_spec_device (specs, NULL, NULL, s, t, NULL, NULL, NULL); } - return nm_match_spec_device (specs, match_str, NULL, NULL, NULL, NULL, NULL); + return nm_match_spec_device (specs, match_str, NULL, NULL, NULL, NULL, NULL, NULL); } static void @@ -1226,6 +1233,10 @@ test_match_spec_device (void) S ("em", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"), NULL, S ("em*")); + _do_test_match_spec_device ("except:interface-name:em*", + S ("", "eth", "eth1", "e1"), + S (NULL), + S ("em", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3")); _do_test_match_spec_device ("aa,bb,cc\\,dd,e,,", S ("aa", "bb", "cc,dd", "e"), NULL, @@ -1298,7 +1309,8 @@ _do_test_match_spec_config (const char *file, int line, const char *spec_str, gu if (expected != match_result) g_error ("%s:%d: faild comparing \"%s\" with %u.%u.%u. Expected %d, but got %d", file, line, spec_str, v_maj, v_min, v_mic, (int) expected, (int) match_result); - if (g_slist_length (specs) == 1 && match_result != NM_MATCH_SPEC_NEG_MATCH) { + if ( g_slist_length (specs) == 1 + && !g_str_has_prefix (specs->data, "except:")) { /* there is only one spec in the list... test that we match except: */ char *sss = g_strdup_printf ("except:%s", (char *) specs->data); GSList *specs2 = g_slist_append (NULL, sss); @@ -1306,7 +1318,7 @@ _do_test_match_spec_config (const char *file, int line, const char *spec_str, gu match_result2 = nm_match_spec_config (specs2, version, NULL); if (match_result == NM_MATCH_SPEC_NO_MATCH) - g_assert_cmpint (match_result2, ==, NM_MATCH_SPEC_NO_MATCH); + g_assert_cmpint (match_result2, ==, NM_MATCH_SPEC_MATCH); else g_assert_cmpint (match_result2, ==, NM_MATCH_SPEC_NEG_MATCH); @@ -1390,7 +1402,7 @@ test_match_spec_config (void) do_test_match_spec_config ("nm-version-max:1", 1, 4, 30, NM_MATCH_SPEC_MATCH); do_test_match_spec_config ("nm-version-max:1", 2, 4, 30, NM_MATCH_SPEC_NO_MATCH); - do_test_match_spec_config ("except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config ("except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_MATCH); do_test_match_spec_config ("nm-version-min:1.6,except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_MATCH); do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 0, NM_MATCH_SPEC_NO_MATCH); @@ -1733,7 +1745,7 @@ do_test_stable_id_parse (const char *stable_id, else g_assert (stable_id); - stable_type = nm_utils_stable_id_parse (stable_id, "_DEVICE", "_BOOT", "_CONNECTION", &generated); + stable_type = nm_utils_stable_id_parse (stable_id, "_DEVICE", "_MAC", "_BOOT", "_CONNECTION", &generated); g_assert_cmpint (expected_stable_type, ==, stable_type); @@ -1772,6 +1784,7 @@ test_stable_id_parse (void) _parse_generated ("x${BOOT}", "x${BOOT}=5{_BOOT}"); _parse_generated ("x${BOOT}${CONNECTION}", "x${BOOT}=5{_BOOT}${CONNECTION}=11{_CONNECTION}"); _parse_generated ("xX${BOOT}yY${CONNECTION}zZ", "xX${BOOT}=5{_BOOT}yY${CONNECTION}=11{_CONNECTION}zZ"); + _parse_generated ("${MAC}x", "${MAC}=4{_MAC}x"); _parse_random ("${RANDOM}"); _parse_random (" ${RANDOM}"); _parse_random ("${BOOT}${RANDOM}"); @@ -1845,6 +1858,223 @@ test_nm_utils_exp10 (void) /*****************************************************************************/ +#define _TEST_RC(searches, nameservers, options, expected) \ + G_STMT_START { \ + const char *const*const _searches = (searches); \ + const char *const*const _nameservers = (nameservers); \ + const char *const*const _options = (options); \ + gs_free char *_content = NULL; \ + \ + _content = nmtst_dns_create_resolv_conf (_searches, _nameservers, _options); \ + g_assert_cmpstr (_content, ==, expected); \ + } G_STMT_END + +static void +test_dns_create_resolv_conf (void) +{ + _TEST_RC (NM_MAKE_STRV ("a"), + NULL, + NULL, + "# Generated by NetworkManager\n" + "search a\n" + ""); + + _TEST_RC (NM_MAKE_STRV ("a", "b.com"), + NM_MAKE_STRV ("192.168.55.1", "192.168.56.1"), + NM_MAKE_STRV ("opt1", "opt2"), + "# Generated by NetworkManager\n" + "search a b.com\n" + "nameserver 192.168.55.1\n" + "nameserver 192.168.56.1\n" + "options opt1 opt2\n" + ""); + + _TEST_RC (NM_MAKE_STRV ("a2x456789.b2x456789.c2x456789.d2x456789.e2x456789.f2x456789.g2x456789.h2x456789.i2x456789.j2x4567890", + "a2y456789.b2y456789.c2y456789.d2y456789.e2y456789.f2y456789.g2y456789.h2y456789.i2y456789.j2y4567890", + "a2z456789.b2z456789.c2z456789.d2z456789.e2z456789.f2z456789.g2z456789.h2z456789.i2z456789.j2z4567890"), + NULL, + NULL, + "# Generated by NetworkManager\n" + "search a2x456789.b2x456789.c2x456789.d2x456789.e2x456789.f2x456789.g2x456789.h2x456789.i2x456789.j2x4567890 a2y456789.b2y456789.c2y456789.d2y456789.e2y456789.f2y456789.g2y456789.h2y456789.i2y456789.j2y4567890 a2z456789.b2z456789.c2z456789.d2z456789.e2z456789.f2z456789.g2z456789.h2z456789.i2z456789.j2z4567890\n" + ""); + +} + +/*****************************************************************************/ + +static void +test_machine_id_read (void) +{ + NMUuid machine_id_sd; + const NMUuid *machine_id; + char machine_id_str[33]; + gpointer logstate; + + logstate = nmtst_logging_disable (FALSE); + /* If you run this test as root, without a valid /etc/machine-id, + * the code will try to get the secret-key. That is a bit ugly, + * but no real problem. */ + machine_id = nm_utils_machine_id_bin (); + nmtst_logging_reenable (logstate); + + g_assert (machine_id); + g_assert (_nm_utils_bin2hexstr_full (machine_id, + sizeof (NMUuid), + '\0', + FALSE, + machine_id_str) == machine_id_str); + g_assert (strlen (machine_id_str) == 32); + g_assert_cmpstr (machine_id_str, ==, nm_utils_machine_id_str ()); + + /* double check with systemd's implementation... */ + if (!nm_sd_utils_id128_get_machine (&machine_id_sd)) { + /* if systemd failed to read /etc/machine-id, the file likely + * is invalid. Our machine-id is fake, and we have nothing to + * compare against. */ + + /* NOTE: this test will fail, if you don't have /etc/machine-id, + * but a valid "LOCALSTATEDIR/lib/dbus/machine-id" file. + * Just don't do that. */ + g_assert (nm_utils_machine_id_is_fake ()); + } else { + g_assert (!nm_utils_machine_id_is_fake ()); + g_assert_cmpmem (&machine_id_sd, sizeof (NMUuid), machine_id, 16); + } +} + +/*****************************************************************************/ + +static void +test_nm_utils_dhcp_client_id_systemd_node_specific (gconstpointer test_data) +{ + const int TEST_IDX = GPOINTER_TO_INT (test_data); + const guint8 HASH_KEY[16] = { 0x80, 0x11, 0x8c, 0xc2, 0xfe, 0x4a, 0x03, 0xee, 0x3e, 0xd6, 0x0c, 0x6f, 0x36, 0x39, 0x14, 0x09 }; + const guint16 duid_type_en = htons (2); + const guint32 systemd_pen = htonl (43793); + const struct { + NMUuid machine_id; + const char *ifname; + guint64 ifname_hash_1; + guint32 iaid_ifname; + guint64 duid_id; + } d_array[] = { + [0] = { + .machine_id = { 0xcb, 0xc2, 0x2e, 0x47, 0x41, 0x8e, 0x40, 0x2a, 0xa7, 0xb3, 0x0d, 0xea, 0x92, 0x83, 0x94, 0xef }, + .ifname = "lo", + .ifname_hash_1 = 0x7297085c2b12c911llu, + .iaid_ifname = htobe32 (0x5985c14du), + .duid_id = htobe64 (0x3d769bb2c14d29e1u), + }, + [1] = { + .machine_id = { 0x11, 0x4e, 0xb4, 0xda, 0xd3, 0x22, 0x4a, 0xff, 0x9f, 0xc3, 0x30, 0x83, 0x38, 0xa0, 0xeb, 0xb7 }, + .ifname = "eth0", + .ifname_hash_1 = 0x9e1cb083b54cd7b6llu, + .iaid_ifname = htobe32 (0x2b506735u), + .duid_id = htobe64 (0x551572e0f2a2a10fu), + }, + }; + int i; + typeof (d_array[0]) *d = &d_array[TEST_IDX]; + gint64 u64; + gint32 u32; + + /* the test already hard-codes the expected values iaid_ifname and duid_id + * above. Still, redo the steps to derive them from the ifname/machine-id + * and double check. */ + u64 = c_siphash_hash (HASH_KEY, (const guint8 *) d->ifname, strlen (d->ifname)); + g_assert_cmpint (u64, ==, d->ifname_hash_1); + u32 = be32toh ((u64 & 0xffffffffu) ^ (u64 >> 32)); + g_assert_cmpint (u32, ==, d->iaid_ifname); + + u64 = htole64 (c_siphash_hash (HASH_KEY, (const guint8 *) &d->machine_id, sizeof (d->machine_id))); + g_assert_cmpint (u64, ==, d->duid_id); + + for (i = 0; i < 2; i++) { + const gboolean legacy_unstable_byteorder = (i != 0); + gs_unref_bytes GBytes *client_id = NULL; + const guint8 *cid; + guint32 iaid = d->iaid_ifname; + + client_id = nm_utils_dhcp_client_id_systemd_node_specific_full (legacy_unstable_byteorder, + (const guint8 *) d->ifname, + strlen (d->ifname), + (const guint8 *) &d->machine_id, + sizeof (d->machine_id)); + + g_assert (client_id); + g_assert_cmpint (g_bytes_get_size (client_id), ==, 19); + cid = g_bytes_get_data (client_id, NULL); + g_assert_cmpint (cid[0], ==, 255); +#if __BYTE_ORDER == __BIG_ENDIAN + if (legacy_unstable_byteorder) { + /* on non-little endian, the legacy behavior is to have the bytes + * swapped. */ + iaid = bswap_32 (iaid); + } +#endif + g_assert_cmpmem (&cid[1], 4, &iaid, sizeof (iaid)); + g_assert_cmpmem (&cid[5], 2, &duid_type_en, sizeof (duid_type_en)); + g_assert_cmpmem (&cid[7], 4, &systemd_pen, sizeof (systemd_pen)); + g_assert_cmpmem (&cid[11], 8, &d->duid_id, sizeof (d->duid_id)); + } +} + +/*****************************************************************************/ + +static void +test_connectivity_state_cmp (void) +{ + NMConnectivityState a; + +#define _cmp(a, b, cmp) \ + G_STMT_START { \ + const NMConnectivityState _a = (a); \ + const NMConnectivityState _b = (b); \ + const int _cmp = (cmp); \ + \ + g_assert (NM_IN_SET (_cmp, -1, 0, 1)); \ + g_assert_cmpint (nm_connectivity_state_cmp (_a, _b), ==, _cmp); \ + g_assert_cmpint (nm_connectivity_state_cmp (_b, _a), ==, -_cmp); \ + } G_STMT_END + + for (a = NM_CONNECTIVITY_UNKNOWN; a <= NM_CONNECTIVITY_FULL; a++) + _cmp (a, a, 0); + + _cmp (NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_UNKNOWN, 0); + _cmp (NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_NONE, -1); + _cmp (NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_LIMITED, -1); + _cmp (NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_PORTAL, -1); + _cmp (NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, -1); + + _cmp (NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_UNKNOWN, 1); + _cmp (NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_NONE, 0); + _cmp (NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_LIMITED, -1); + _cmp (NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_PORTAL, -1); + _cmp (NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_FULL, -1); + + _cmp (NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_UNKNOWN, 1); + _cmp (NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_NONE, 1); + _cmp (NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_LIMITED, 0); + _cmp (NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_PORTAL, -1); + _cmp (NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_FULL, -1); + + _cmp (NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_UNKNOWN, 1); + _cmp (NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_NONE, 1); + _cmp (NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_LIMITED, 1); + _cmp (NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_PORTAL, 0); + _cmp (NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_FULL, -1); + + _cmp (NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, 1); + _cmp (NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_NONE, 1); + _cmp (NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_LIMITED, 1); + _cmp (NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_PORTAL, 1); + _cmp (NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_FULL, 0); + +#undef _cmp +} + +/*****************************************************************************/ + NMTST_DEFINE (); int @@ -1891,6 +2121,15 @@ main (int argc, char **argv) g_test_add_func ("/general/stable-id/parse", test_stable_id_parse); g_test_add_func ("/general/stable-id/generated-complete", test_stable_id_generated_complete); + g_test_add_func ("/general/machine-id/read", test_machine_id_read); + + g_test_add_func ("/general/test_dns_create_resolv_conf", test_dns_create_resolv_conf); + + g_test_add_data_func ("/general/nm_utils_dhcp_client_id_systemd_node_specific/0", GINT_TO_POINTER (0), test_nm_utils_dhcp_client_id_systemd_node_specific); + g_test_add_data_func ("/general/nm_utils_dhcp_client_id_systemd_node_specific/1", GINT_TO_POINTER (1), test_nm_utils_dhcp_client_id_systemd_node_specific); + + g_test_add_func ("/core/general/test_connectivity_state_cmp", test_connectivity_state_cmp); + return g_test_run (); } diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c index a03d89b0..5b255068 100644 --- a/src/tests/test-ip6-config.c +++ b/src/tests/test-ip6-config.c @@ -22,6 +22,7 @@ #include #include +#include #include "nm-ip6-config.h" diff --git a/src/tests/test-systemd.c b/src/tests/test-systemd.c index ab5fed22..4660bd0d 100644 --- a/src/tests/test-systemd.c +++ b/src/tests/test-systemd.c @@ -20,6 +20,7 @@ #include "nm-default.h" #include "systemd/nm-sd.h" +#include "systemd/nm-sd-utils.h" #include "nm-test-utils-core.h" @@ -173,6 +174,50 @@ test_sd_event (void) /*****************************************************************************/ +static void +test_path_equal (void) +{ +#define _path_equal_check1(path, kill_dots, expected) \ + G_STMT_START { \ + const gboolean _kill_dots = (kill_dots); \ + const char *_path0 = (path); \ + const char *_expected = (expected); \ + gs_free char *_path = g_strdup (_path0); \ + const char *_path_result; \ + \ + if ( !_kill_dots \ + && !nm_sd_utils_path_equal (_path0, _expected)) \ + g_error ("Paths \"%s\" and \"%s\" don't compare equal", _path0, _expected); \ + \ + _path_result = nm_sd_utils_path_simplify (_path, _kill_dots); \ + g_assert (_path_result == _path); \ + g_assert_cmpstr (_path, ==, _expected); \ + } G_STMT_END + +#define _path_equal_check(path, expected_no_kill_dots, expected_kill_dots) \ + G_STMT_START { \ + _path_equal_check1 (path, FALSE, expected_no_kill_dots); \ + _path_equal_check1 (path, TRUE, expected_kill_dots ?: expected_no_kill_dots); \ + } G_STMT_END + + _path_equal_check ("", "", NULL); + _path_equal_check (".", ".", ""); + _path_equal_check ("..", "..", NULL); + _path_equal_check ("/..", "/..", NULL); + _path_equal_check ("//..", "/..", NULL); + _path_equal_check ("/.", "/.", "/"); + _path_equal_check ("./", ".", ""); + _path_equal_check ("./.", "./.", ""); + _path_equal_check (".///.", "./.", ""); + _path_equal_check (".///./", "./.", ""); + _path_equal_check (".////", ".", ""); + _path_equal_check ("//..//foo/", "/../foo", NULL); + _path_equal_check ("///foo//./bar/.", "/foo/./bar/.", "/foo/bar"); + _path_equal_check (".//./foo//./bar/.", "././foo/./bar/.", "foo/bar"); +} + +/*****************************************************************************/ + NMTST_DEFINE (); int @@ -183,6 +228,7 @@ main (int argc, char **argv) g_test_add_func ("/systemd/dhcp/create", test_dhcp_create); g_test_add_func ("/systemd/lldp/create", test_lldp_create); g_test_add_func ("/systemd/sd-event", test_sd_event); + g_test_add_func ("/systemd/test_path_equal", test_path_equal); return g_test_run (); } diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index bd847d75..3b3a97f4 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -729,15 +729,23 @@ add_ip4_vpn_gateway_route (NMIP4Config *config, const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (route_resolved); if (r->ifindex == ifindex) { + const NMPObject *obj; + /* `ip route get` always resolves the route, even if the destination is unreachable. * In which case, it pretends the destination is directly reachable. * - * So, only accept direct routes, if @vpn_gw is a private network. */ - if ( nm_platform_route_table_is_main (r->table_coerced) - && ( r->gateway - || nm_utils_ip_is_site_local (AF_INET, &vpn_gw))) { - parent_gw = r->gateway; - has_parent_gw = TRUE; + * So, only accept direct routes if @vpn_gw is a private network + * or if the parent device also has a direct default route */ + if (nm_platform_route_table_is_main (r->table_coerced)) { + if (r->gateway) { + parent_gw = r->gateway; + has_parent_gw = TRUE; + } else if (nm_utils_ip_is_site_local (AF_INET, &vpn_gw)) { + has_parent_gw = TRUE; + } else if ( (obj = nm_device_get_best_default_route (parent_device, AF_INET)) + && !NMP_OBJECT_CAST_IP4_ROUTE (obj)->gateway) { + has_parent_gw = TRUE; + } } } } @@ -803,15 +811,23 @@ add_ip6_vpn_gateway_route (NMIP6Config *config, const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (route_resolved); if (r->ifindex == ifindex) { + const NMPObject *obj; + /* `ip route get` always resolves the route, even if the destination is unreachable. * In which case, it pretends the destination is directly reachable. * - * So, only accept direct routes, if @vpn_gw is a private network. */ - if ( nm_platform_route_table_is_main (r->table_coerced) - && ( !IN6_IS_ADDR_UNSPECIFIED (&r->gateway) - || nm_utils_ip_is_site_local (AF_INET6, &vpn_gw))) { - parent_gw = &r->gateway; - has_parent_gw = TRUE; + * So, only accept direct routes if @vpn_gw is a private network + * or if the parent device also has a direct default route */ + if (nm_platform_route_table_is_main (r->table_coerced)) { + if (!IN6_IS_ADDR_UNSPECIFIED (&r->gateway)) { + parent_gw = &r->gateway; + has_parent_gw = TRUE; + } else if (nm_utils_ip_is_site_local (AF_INET6, &vpn_gw)) { + has_parent_gw = TRUE; + } else if ( (obj = nm_device_get_best_default_route (parent_device, AF_INET6)) + && IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (obj)->gateway)) { + has_parent_gw = TRUE; + } } } } -- cgit 1.3.0-6-gf8a5