From cfb80376641fa49137b9996130352697e7f8b436 Mon Sep 17 00:00:00 2001 From: Sebastien Bacher Date: Wed, 25 Aug 2021 15:23:22 +0200 Subject: New upstream version 1.32.10 --- src/core/devices/nm-acd-manager.c | 11 +- src/core/devices/nm-device-bond.c | 4 +- src/core/devices/nm-device.c | 248 +- src/core/devices/nm-device.h | 1 + src/core/devices/wwan/nm-device-modem.c | 19 +- src/core/dhcp/nm-dhcp-nettools.c | 30 +- src/core/dhcp/nm-dhcp-options.c | 8 +- src/core/dhcp/nm-dhcp-options.h | 3 +- src/core/main.c | 4 +- src/core/nm-config-data.c | 81 +- src/core/nm-config-data.h | 4 + src/core/nm-config.c | 34 +- src/core/nm-config.h | 3 +- src/core/nm-core-utils.c | 106 + src/core/nm-core-utils.h | 4 + src/core/nm-firewall-utils.c | 26 +- src/core/nm-firewalld-manager.c | 76 +- src/core/nm-firewalld-manager.h | 6 + src/core/nm-ip4-config.c | 26 +- src/core/nm-ip6-config.c | 7 +- src/core/nm-manager.c | 92 +- src/core/nm-policy.c | 13 +- src/core/platform/nm-fake-platform.c | 3 + src/core/platform/tests/test-common.c | 4 + src/core/platform/tests/test-route.c | 104 +- src/core/ppp/meson.build | 1 + src/core/settings/nm-settings-connection.c | 227 +- src/core/settings/nm-settings-connection.h | 2 - src/core/settings/nm-settings.c | 63 +- src/core/settings/plugins/ifcfg-rh/nm-ifdown | 47 +- src/core/settings/plugins/ifcfg-rh/nm-ifup | 47 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 31 +- .../settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c | 2 + .../settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h | 2 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 6 + src/core/settings/plugins/ifcfg-rh/shvar.c | 136 +- src/core/settings/plugins/ifcfg-rh/shvar.h | 3 + .../ifcfg-test-wifi-wpa-eap-suite-b-192-tls | 1 - .../ifcfg-test-wifi-wpa-eap-ttls-tls | 1 - .../ifcfg-test-write-unknown-4.expected | 6 +- .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 53 +- src/core/tests/meson.build | 2 + src/libnm-base/nm-config-base.h | 2 + src/libnm-client-impl/libnm.ver | 5 + src/libnm-client-impl/nm-libnm-utils.c | 11 +- src/libnm-client-impl/nm-property-infos-nmcli.xml | 3 +- src/libnm-client-impl/nm-settings-docs-gir.xml | 2 + src/libnm-client-impl/tests/test-libnm.c | 54 +- src/libnm-core-aux-intern/nm-libnm-core-utils.c | 1 + src/libnm-core-impl/meson.build | 36 +- src/libnm-core-impl/nm-connection.c | 2 +- src/libnm-core-impl/nm-keyfile-utils.c | 4 +- src/libnm-core-impl/nm-keyfile.c | 3 + src/libnm-core-impl/nm-setting-bond.c | 37 + src/libnm-core-impl/nm-setting-ip-config.c | 61 + src/libnm-core-impl/nm-setting-ip4-config.c | 2 + src/libnm-core-impl/nm-setting-ip6-config.c | 9 + src/libnm-core-impl/nm-setting-wireless.c | 60 +- src/libnm-core-impl/tests/meson.build | 2 +- src/libnm-core-impl/tests/test-general.c | 14 + src/libnm-core-intern/nm-core-internal.h | 5 + src/libnm-core-public/nm-setting-bond.h | 1 + src/libnm-core-public/nm-setting-ip-config.h | 5 +- src/libnm-core-public/nm-version-macros.h | 3 +- src/libnm-core-public/nm-version-macros.h.in | 1 + src/libnm-core-public/nm-version.h | 6 + src/libnm-glib-aux/nm-dedup-multi.h | 46 +- src/libnm-glib-aux/nm-errno.c | 14 +- src/libnm-glib-aux/nm-io-utils.c | 64 + src/libnm-glib-aux/nm-io-utils.h | 2 + src/libnm-glib-aux/nm-keyfile-aux.c | 118 +- src/libnm-glib-aux/nm-keyfile-aux.h | 6 + src/libnm-glib-aux/nm-macros-internal.h | 8 - src/libnm-glib-aux/nm-random-utils.c | 297 ++- src/libnm-glib-aux/nm-shared-utils.c | 102 +- src/libnm-glib-aux/nm-shared-utils.h | 47 +- src/libnm-glib-aux/nm-str-buf.h | 7 + src/libnm-glib-aux/tests/test-shared-general.c | 44 + src/libnm-platform/nm-linux-platform.c | 1 + src/libnm-platform/nm-platform.c | 62 +- src/libnm-platform/nm-platform.h | 8 + src/libnm-platform/nmp-netns.c | 10 +- src/libnm-platform/nmp-object.h | 15 + src/libnm-std-aux/nm-std-aux.h | 16 +- .../sd-adapt-shared/nm-sd-adapt-shared.h | 7 +- src/libnmc-setting/nm-meta-setting-desc.c | 30 + src/libnmc-setting/settings-docs-input.xml | 2 +- src/libnmc-setting/settings-docs.h | 6 +- src/libnmc-setting/settings-docs.h.in | 6 +- src/libnmc-setting/tests/meson.build | 1 + src/n-dhcp4/src/n-dhcp4-c-lease.c | 44 +- src/n-dhcp4/src/n-dhcp4.h | 3 +- src/nm-cloud-setup/main.c | 14 +- src/nm-cloud-setup/meson.build | 1 + src/nm-cloud-setup/nm-cloud-setup-utils.c | 14 + src/nm-cloud-setup/nm-cloud-setup-utils.h | 2 + src/nm-cloud-setup/nm-cloud-setup.service.in | 1 + src/nm-cloud-setup/nmcs-provider-aliyun.c | 511 ++++ src/nm-cloud-setup/nmcs-provider-aliyun.h | 28 + src/nm-cloud-setup/nmcs-provider-azure.c | 10 +- src/nm-cloud-setup/nmcs-provider-ec2.c | 16 +- src/nm-cloud-setup/nmcs-provider.h | 2 + src/nm-cloud-setup/tests/meson.build | 1 + src/nm-dispatcher/meson.build | 1 + src/nm-dispatcher/tests/meson.build | 1 + src/nm-initrd-generator/meson.build | 2 +- src/nm-initrd-generator/nm-initrd-generator.c | 8 + src/nm-initrd-generator/nm-initrd-generator.h | 3 +- src/nm-initrd-generator/nmi-cmdline-reader.c | 151 +- .../tests/test-cmdline-reader.c | 289 ++- src/nm-online/meson.build | 1 + src/nmcli/common.c | 12 + src/nmcli/generate-docs-nm-settings-nmcli.xml | 8 +- src/nmcli/generate-docs-nm-settings-nmcli.xml.in | 8 +- src/nmcli/meson.build | 2 + src/nmcli/utils.h | 2 + .../test-client.check-on-disk/test_003.expected | 996 ++++---- .../test-client.check-on-disk/test_004.expected | 2676 +++++++++++--------- 118 files changed, 5282 insertions(+), 2320 deletions(-) mode change 100644 => 100755 src/core/settings/plugins/ifcfg-rh/nm-ifdown mode change 100644 => 100755 src/core/settings/plugins/ifcfg-rh/nm-ifup create mode 100644 src/nm-cloud-setup/nmcs-provider-aliyun.c create mode 100644 src/nm-cloud-setup/nmcs-provider-aliyun.h (limited to 'src') diff --git a/src/core/devices/nm-acd-manager.c b/src/core/devices/nm-acd-manager.c index 995abacd..145947bb 100644 --- a/src/core/devices/nm-acd-manager.c +++ b/src/core/devices/nm-acd-manager.c @@ -188,6 +188,7 @@ acd_event(int fd, GIOCondition condition, gpointer data) char to_string_buffer[ACD_EVENT_TO_STRING_BUF_SIZE]; gs_free char *hwaddr_str = NULL; gboolean check_probing_done = FALSE; + char buf[ETH_ALEN * 3]; switch (event->event) { case N_ACD_EVENT_READY: @@ -202,8 +203,9 @@ acd_event(int fd, GIOCondition condition, gpointer data) nm_platform_link_get_name(NM_PLATFORM_GET, self->ifindex), acd_error_to_string(r)); } else { - _LOGD("announcing address %s", - _nm_utils_inet4_ntop(info->address, address_str)); + _LOGD("announcing address %s (hw-addr %s)", + _nm_utils_inet4_ntop(info->address, address_str), + _nm_utils_hwaddr_ntoa(self->hwaddr, ETH_ALEN, TRUE, buf, sizeof(buf))); } } check_probing_done = TRUE; @@ -394,6 +396,7 @@ nm_acd_manager_announce_addresses(NMAcdManager *self) int r; int fd; gboolean success = TRUE; + char buf[ETH_ALEN * 3]; r = acd_init(self); if (r) { @@ -428,7 +431,9 @@ nm_acd_manager_announce_addresses(NMAcdManager *self) acd_error_to_string(r)); success = FALSE; } else - _LOGD("announcing address %s", _nm_utils_inet4_ntop(info->address, sbuf)); + _LOGD("announcing address %s (hw-addr %s)", + _nm_utils_inet4_ntop(info->address, sbuf), + _nm_utils_hwaddr_ntoa(self->hwaddr, ETH_ALEN, TRUE, buf, sizeof(buf))); } } diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c index 3b3b8fcd..b208e2c0 100644 --- a/src/core/devices/nm-device-bond.c +++ b/src/core/devices/nm-device-bond.c @@ -34,7 +34,7 @@ NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, \ NM_SETTING_BOND_OPTION_RESEND_IGMP, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB, \ NM_SETTING_BOND_OPTION_USE_CARRIER, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, \ - NM_SETTING_BOND_OPTION_NUM_GRAT_ARP + NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY #define OPTIONS_REAPPLY_SUBSET \ NM_SETTING_BOND_OPTION_MIIMON, NM_SETTING_BOND_OPTION_UPDELAY, \ @@ -46,7 +46,7 @@ NM_SETTING_BOND_OPTION_MIN_LINKS, NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, \ NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, NM_SETTING_BOND_OPTION_RESEND_IGMP, \ NM_SETTING_BOND_OPTION_USE_CARRIER, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, \ - NM_SETTING_BOND_OPTION_NUM_GRAT_ARP + NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY #define OPTIONS_REAPPLY_FULL \ OPTIONS_REAPPLY_SUBSET, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, \ diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 1c70ecb3..50386d00 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -499,8 +499,18 @@ typedef struct _NMDevicePrivate { NMDeviceStageState stage1_sriov_state : 3; + bool ip_config_started : 1; + char *current_stable_id; + union { + struct { + GSource *ip_req_timeout_source_6; + GSource *ip_req_timeout_source_4; + }; + GSource *ip_req_timeout_source_x[2]; + }; + /* Proxy Configuration */ NMProxyConfig * proxy_config; NMPacrunnerConfId *pacrunner_conf_id; @@ -766,6 +776,7 @@ static void sriov_op_cb(GError *error, gpointer user_data); static void device_ifindex_changed_cb(NMManager *manager, NMDevice *device_changed, NMDevice *self); static gboolean device_link_changed(NMDevice *self); +static void check_ip_state(NMDevice *self, gboolean may_fail, gboolean full_state_update); /*****************************************************************************/ @@ -1364,6 +1375,40 @@ out: return timeout; } +static guint32 +_prop_get_ipvx_required_timeout(NMDevice *self, int addr_family) +{ + NMConnection * connection; + NMSettingIPConfig *s_ip; + int timeout; + + nm_assert(NM_IS_DEVICE(self)); + nm_assert_addr_family(addr_family); + + connection = nm_device_get_applied_connection(self); + if (!connection) + return 0; + + s_ip = nm_connection_get_setting_ip_config(connection, addr_family); + if (!s_ip) + return 0; + + timeout = nm_setting_ip_config_get_required_timeout(s_ip); + nm_assert(timeout >= -1); + + if (timeout > -1) + return (guint32) timeout; + + return nm_config_data_get_connection_default_int64( + NM_CONFIG_GET_DATA, + NM_IS_IPv4(addr_family) ? NM_CON_DEFAULT("ipv4.required-timeout") + : NM_CON_DEFAULT("ipv6.required-timeout"), + self, + 0, + G_MAXINT32, + 0); +} + /** * _prop_get_ipvx_dhcp_iaid: * @self: the #NMDevice @@ -2787,14 +2832,72 @@ _add_capabilities(NMDevice *self, NMDeviceCapabilities capabilities) /*****************************************************************************/ +static gboolean +ip_required_timeout_x(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + _LOGD(LOGD_CORE, + "required-timeout expired for IPv%c", + nm_utils_addr_family_to_char(addr_family)); + nm_clear_g_source_inst(&priv->ip_req_timeout_source_x[NM_IS_IPv4(addr_family)]); + check_ip_state(self, FALSE, TRUE); + return G_SOURCE_CONTINUE; +} + +static gboolean +ip_required_timeout_4(gpointer data) +{ + return ip_required_timeout_x(data, AF_INET); +} + +static gboolean +ip_required_timeout_6(gpointer data) +{ + return ip_required_timeout_x(data, AF_INET6); +} + static void _set_ip_state(NMDevice *self, int addr_family, NMDeviceIPState new_state) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); const int IS_IPv4 = NM_IS_IPv4(addr_family); + guint timeout_msec; + int v4; nm_assert_addr_family(addr_family); + if (new_state == NM_DEVICE_IP_STATE_CONF && !priv->ip_config_started) { + /* Start the required-timeout timers when one of IPv4/IPv6 + * enters the CONF state. This means that if there is no carrier and + * ipv4.method=auto,ipv6.method=manual, the timeout for IPv4 will + * start as soon as connection is activated, even if DHCPv4 did not + * start yet. + */ + priv->ip_config_started = TRUE; + + for (v4 = 1; v4 >= 0; v4--) { + char buf[32]; + + nm_assert(!priv->ip_req_timeout_source_x[v4]); + if ((timeout_msec = _prop_get_ipvx_required_timeout(self, v4 ? AF_INET : AF_INET6))) { + _LOGD(LOGD_CORE, + "required-timeout in %s msec for IPv%c", + timeout_msec == G_MAXINT32 ? "∞" : nm_sprintf_buf(buf, "%u", timeout_msec), + v4 ? '4' : '6'); + + if (timeout_msec == G_MAXINT32) { + priv->ip_req_timeout_source_x[v4] = g_source_ref(nm_g_source_sentinel_get(0)); + } else { + priv->ip_req_timeout_source_x[v4] = + nm_g_timeout_add_source(timeout_msec, + v4 ? ip_required_timeout_4 : ip_required_timeout_6, + self); + } + } + } + } + if (priv->ip_state_x[IS_IPv4] == new_state) return; @@ -4671,10 +4774,6 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co */ nm_device_update_hw_address(self); - /* Send ARP announcements if did not yet and have addresses. */ - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && !priv->acd.announcing) - nm_device_arp_announce(self); - /* Restart IP configuration if we're waiting for slaves. Do this * after updating the hardware address as IP config may need the * new address. @@ -5003,6 +5102,10 @@ nm_device_set_carrier(NMDevice *self, gboolean carrier) nm_device_remove_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); _carrier_wait_check_queued_act_request(self); } + + /* Send ARP announcements if did not yet and have carrier. */ + if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && !priv->acd.announcing) + nm_device_arp_announce(self); } else { if (priv->carrier_wait_id) nm_device_add_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); @@ -6576,6 +6679,7 @@ check_ip_state(NMDevice *self, gboolean may_fail, gboolean full_state_update) gboolean ip4_disabled = FALSE, ip6_disabled = FALSE; NMSettingIPConfig *s_ip4, *s_ip6; NMDeviceState state; + int IS_IPv4; if (full_state_update && nm_device_get_state(self) != NM_DEVICE_STATE_IP_CONFIG) return; @@ -6605,6 +6709,13 @@ check_ip_state(NMDevice *self, gboolean may_fail, gboolean full_state_update) return; } + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + if (priv->ip_state_x[IS_IPv4] == NM_DEVICE_IP_STATE_CONF + && priv->ip_req_timeout_source_x[IS_IPv4]) { + return; + } + } + if ((priv->ip_state_4 == NM_DEVICE_IP_STATE_FAIL || (ip4_disabled && priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE)) && (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL @@ -7128,6 +7239,16 @@ _get_maybe_ipv6_disabled(NMDevice *self) return (nm_platform_sysctl_get_int32(platform, NMP_SYSCTL_PATHID_ABSOLUTE(path), 0) == 0); } +/* + * nm_device_generate_connection: + * + * Generates a connection from an existing interface. + * + * If the device doesn't have an IP configuration and it's not a port or a + * controller, then no connection gets generated and the function returns + * %NULL. In such case, @maybe_later is set to %TRUE if a connection can be + * generated later when an IP address is assigned to the interface. + */ NMConnection * nm_device_generate_connection(NMDevice *self, NMDevice *master, @@ -7384,6 +7505,8 @@ check_connection_compatible(NMDevice *self, NMConnection *connection, GError **e gs_free char * conn_iface = NULL; NMDeviceClass * klass; NMSettingMatch * s_match; + const GSList * specs; + gboolean has_match = FALSE; klass = NM_DEVICE_GET_CLASS(self); if (klass->connection_type_check_compatible) { @@ -7460,6 +7583,15 @@ check_connection_compatible(NMDevice *self, NMConnection *connection, GError **e } } + specs = + nm_config_data_get_device_allowed_connections_specs(NM_CONFIG_GET_DATA, self, &has_match); + if (has_match && !nm_utils_connection_match_spec_list(connection, specs, FALSE)) { + nm_utils_error_set_literal(error, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_DISALLOWED, + "device configuration doesn't allow this connection"); + return FALSE; + } + return TRUE; } @@ -7526,7 +7658,7 @@ nm_device_check_slave_connection_compatible(NMDevice *self, NMConnection *slave) * * Returns: %TRUE if the device is capable of assuming connections, %FALSE if not */ -static gboolean +gboolean nm_device_can_assume_connections(NMDevice *self) { return !!NM_DEVICE_GET_CLASS(self)->update_connection; @@ -9857,6 +9989,7 @@ dhcp6_start_with_link_ready(NMDevice *self, NMConnection *connection) gboolean iaid_explicit; NMSettingConnection * s_con; const NMPlatformIP6Address *ll_addr = NULL; + int ip_ifindex; g_return_val_if_fail(connection, FALSE); @@ -9876,7 +10009,13 @@ dhcp6_start_with_link_ready(NMDevice *self, NMConnection *connection) return FALSE; } - pllink = nm_platform_link_get(nm_device_get_platform(self), nm_device_get_ip_ifindex(self)); + ip_ifindex = nm_device_get_ip_ifindex(self); + if (ip_ifindex <= 0) { + _LOGD(LOGD_DHCP6, "can't start DHCPv6: interface is gone"); + return FALSE; + } + + pllink = nm_platform_link_get(nm_device_get_platform(self), ip_ifindex); if (pllink) hwaddr = nmp_link_address_get_as_bytes(&pllink->l_address); @@ -9887,7 +10026,7 @@ dhcp6_start_with_link_ready(NMDevice *self, NMConnection *connection) nm_dhcp_manager_get(), nm_device_get_multi_index(self), nm_device_get_ip_iface(self), - nm_device_get_ip_ifindex(self), + ip_ifindex, &ll_addr->address, nm_connection_get_uuid(connection), nm_device_get_route_table(self, AF_INET6), @@ -9997,7 +10136,9 @@ nm_device_request_ip6_prefixes(NMDevice *self, int needed_prefixes) _LOGD(LOGD_IP6, "ipv6-pd: asking DHCPv6 for %d prefixes", needed_prefixes); nm_device_dhcp6_renew(self, FALSE); } else { - _LOGI(LOGD_IP6, "ipv6-pd: device doesn't use DHCPv6, can't request prefixes"); + priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_OTHERCONF; + _LOGD(LOGD_DEVICE | LOGD_DHCP6, "ipv6-pd: starting DHCPv6 to request a prefix"); + dhcp6_start(self, FALSE); } } @@ -10541,10 +10682,30 @@ _commit_mtu(NMDevice *self, const NMIP4Config *config) } } +#define _IP6_MTU_SYS() \ + ({ \ + if (!ip6_mtu_sysctl.initialized) { \ + ip6_mtu_sysctl.value = nm_device_sysctl_ip_conf_get_int_checked(self, \ + AF_INET6, \ + "mtu", \ + 10, \ + 0, \ + G_MAXUINT32, \ + 0); \ + ip6_mtu_sysctl.initialized = TRUE; \ + } \ + ip6_mtu_sysctl.value; \ + }) + if (mtu_desired && NM_DEVICE_GET_CLASS(self)->mtu_force_set && !priv->mtu_force_set_done) { priv->mtu_force_set_done = TRUE; - if (mtu_desired == mtu_plat) { + if (!priv->mtu_initial && !priv->ip6_mtu_initial) { + /* before touching any of the MTU parameters, record the + * original setting to restore on deactivation. */ + priv->mtu_initial = mtu_plat; + priv->ip6_mtu_initial = _IP6_MTU_SYS(); + } mtu_plat--; if (NM_DEVICE_GET_CLASS(self)->set_platform_mtu(self, mtu_desired - 1)) { _LOGD(LOGD_DEVICE, "mtu: force-set MTU to %u", mtu_desired - 1); @@ -10563,20 +10724,6 @@ _commit_mtu(NMDevice *self, const NMIP4Config *config) ip6_mtu == ip6_mtu_orig ? "" : nm_sprintf_buf(sbuf2, " (was %u)", (guint) ip6_mtu_orig), ifindex); -#define _IP6_MTU_SYS() \ - ({ \ - if (!ip6_mtu_sysctl.initialized) { \ - ip6_mtu_sysctl.value = nm_device_sysctl_ip_conf_get_int_checked(self, \ - AF_INET6, \ - "mtu", \ - 10, \ - 0, \ - G_MAXUINT32, \ - 0); \ - ip6_mtu_sysctl.initialized = TRUE; \ - } \ - ip6_mtu_sysctl.value; \ - }) if ((mtu_desired && mtu_desired != mtu_plat) || (ip6_mtu && ip6_mtu != _IP6_MTU_SYS())) { gboolean anticipated_failure = FALSE; @@ -11805,11 +11952,12 @@ activate_stage5_ip_config_result_x(NMDevice *self, int addr_family) const char * method; int ip_ifindex; int errsv; - gboolean do_announce = FALSE; req = nm_device_get_act_request(self); g_assert(req); + nm_clear_g_source_inst(&priv->ip_req_timeout_source_x[IS_IPv4]); + /* Interface must be IFF_UP before IP config can be applied */ ip_ifindex = nm_device_get_ip_ifindex(self); g_return_if_fail(ip_ifindex); @@ -11931,31 +12079,13 @@ activate_stage5_ip_config_result_x(NMDevice *self, int addr_family) } } - if (IS_IPv4) { - /* Send ARP announcements */ - - if (nm_device_is_master(self)) { - CList * iter; - SlaveInfo *info; - - /* Skip announcement if there are no device enslaved, for two reasons: - * 1) the master has a temporary MAC address until the first slave comes - * 2) announcements are going to be dropped anyway without slaves - */ - do_announce = FALSE; - - c_list_for_each (iter, &priv->slaves) { - info = c_list_entry(iter, SlaveInfo, lst_slave); - if (info->slave_is_enslaved) { - do_announce = TRUE; - break; - } - } - } else - do_announce = TRUE; - - if (do_announce) - nm_device_arp_announce(self); + if (IS_IPv4 && priv->carrier) { + /* We send ARP announcements only when the link gets carrier, + * otherwise the announcements would be lost. Furthermore, for + * controllers having carrier implies that there is at least one + * port and therefore the MAC address is the correct one. + */ + nm_device_arp_announce(self); } if (IS_IPv4) { @@ -13697,18 +13827,24 @@ void nm_device_replace_vpn6_config(NMDevice *self, NMIP6Config *old, NMIP6Config *config) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMDeviceState state; nm_assert(!old || NM_IS_IP6_CONFIG(old)); + nm_assert(!old || nm_ip6_config_get_ifindex(old) > 0); + nm_assert(!old || nm_device_get_ip_ifindex(self) == 0 + || nm_device_get_ip_ifindex(self) == nm_ip6_config_get_ifindex(old)); nm_assert(!config || NM_IS_IP6_CONFIG(config)); - nm_assert(!old || nm_ip6_config_get_ifindex(old) == nm_device_get_ip_ifindex(self)); - nm_assert(!config || nm_ip6_config_get_ifindex(config) == nm_device_get_ip_ifindex(self)); + nm_assert(!config || nm_ip6_config_get_ifindex(config) > 0); + nm_assert(!config || nm_device_get_ip_ifindex(self) == nm_ip6_config_get_ifindex(config)); if (!_replace_vpn_config_in_list(&priv->vpn_configs_6, (GObject *) old, (GObject *) config)) return; - /* NULL to use existing configs */ - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "failed to set VPN routes for device"); + state = nm_device_get_state(self); + if (state >= NM_DEVICE_STATE_IP_CONFIG && state <= NM_DEVICE_STATE_ACTIVATED) { + if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) + _LOGW(LOGD_IP6, "failed to set VPN routes for device"); + } } NMIP6Config * @@ -15777,6 +15913,10 @@ _cleanup_generic_pre(NMDevice *self, CleanupType cleanup_type) _cleanup_ip_pre(self, AF_INET, cleanup_type); _cleanup_ip_pre(self, AF_INET6, cleanup_type); + + priv->ip_config_started = FALSE; + nm_clear_g_source_inst(&priv->ip_req_timeout_source_4); + nm_clear_g_source_inst(&priv->ip_req_timeout_source_6); } static void diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h index 53211c6f..f59b6fa8 100644 --- a/src/core/devices/nm-device.h +++ b/src/core/devices/nm-device.h @@ -527,6 +527,7 @@ nm_device_check_connection_compatible(NMDevice *device, NMConnection *connection gboolean nm_device_check_slave_connection_compatible(NMDevice *device, NMConnection *connection); +gboolean nm_device_can_assume_connections(NMDevice *self); gboolean nm_device_unmanage_on_quit(NMDevice *self); gboolean nm_device_spec_match_list(NMDevice *device, const GSList *specs); diff --git a/src/core/devices/wwan/nm-device-modem.c b/src/core/devices/wwan/nm-device-modem.c index a8d2a161..4d892e6f 100644 --- a/src/core/devices/wwan/nm-device-modem.c +++ b/src/core/devices/wwan/nm-device-modem.c @@ -193,15 +193,20 @@ modem_ip4_config_result(NMModem *modem, NMIP4Config *config, GError *error, gpoi NMDeviceModem *self = NM_DEVICE_MODEM(user_data); NMDevice * device = NM_DEVICE(self); - g_return_if_fail(nm_device_activate_ip4_state_in_conf(device) == TRUE); + if (!nm_device_activate_ip4_state_in_conf(device)) { + _LOGD(LOGD_MB | LOGD_IP4, + "retrieving IPv4 configuration while no longer in state IPv4 conf"); + return; + } if (error) { _LOGW(LOGD_MB | LOGD_IP4, "retrieving IPv4 configuration failed: %s", error->message); nm_device_ip_method_failed(device, AF_INET, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } else { - nm_device_set_dev2_ip_config(device, AF_INET, NM_IP_CONFIG_CAST(config)); - nm_device_activate_schedule_ip_config_result(device, AF_INET, NULL); + return; } + + nm_device_set_dev2_ip_config(device, AF_INET, NM_IP_CONFIG_CAST(config)); + nm_device_activate_schedule_ip_config_result(device, AF_INET, NULL); } static void @@ -218,7 +223,11 @@ modem_ip6_config_result(NMModem * modem, gs_unref_object NMIP6Config *ignored = NULL; gboolean got_config = !!config; - g_return_if_fail(nm_device_activate_ip6_state_in_conf(device) == TRUE); + if (!nm_device_activate_ip6_state_in_conf(device)) { + _LOGD(LOGD_MB | LOGD_IP6, + "retrieving IPv6 configuration while no longer in state IPv6 conf"); + return; + } if (error) { _LOGW(LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: %s", error->message); diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c index fe71f587..4bb6c833 100644 --- a/src/core/dhcp/nm-dhcp-nettools.c +++ b/src/core/dhcp/nm-dhcp-nettools.c @@ -694,12 +694,40 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, /* https://tools.ietf.org/html/rfc2132#section-8.1 */ - v_str = nm_utils_buf_utf8safe_escape((char *) l_data, l_data_len, 0, &to_free); + v_str = nm_utils_buf_utf8safe_escape((char *) l_data, + l_data_len, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &to_free); nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NIS_DOMAIN, v_str ?: ""); nm_ip4_config_set_nis_domain(ip4_config, v_str ?: ""); } + r = n_dhcp4_client_lease_get_file(lease, &v_str); + if (r == 0) { + gs_free char *to_free = NULL; + + v_str = nm_utils_buf_utf8safe_escape(v_str, + -1, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &to_free); + nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NM_FILENAME, v_str ?: ""); + } + + r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_BOOTFILE_NAME, &l_data, &l_data_len); + if (r == 0 && nm_dhcp_lease_data_parse_cstr(l_data, l_data_len, &l_data_len)) { + gs_free char *to_free = NULL; + + v_str = nm_utils_buf_utf8safe_escape((char *) l_data, + l_data_len, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &to_free); + nm_dhcp_option_add_option(options, + AF_INET, + NM_DHCP_OPTION_DHCP4_BOOTFILE_NAME, + v_str ?: ""); + } + lease_parse_address_list(lease, ip4_config, NM_DHCP_OPTION_DHCP4_NIS_SERVERS, options, &sbuf); lease_parse_address_list(lease, diff --git a/src/core/dhcp/nm-dhcp-options.c b/src/core/dhcp/nm-dhcp-options.c index 8f0d7408..011f23e4 100644 --- a/src/core/dhcp/nm-dhcp-options.c +++ b/src/core/dhcp/nm-dhcp-options.c @@ -169,6 +169,7 @@ const NMDhcpOption _nm_dhcp_option_dhcp4_options[] = { REQ(NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, "ip_address", FALSE), REQ(NM_DHCP_OPTION_DHCP4_NM_EXPIRY, "expiry", FALSE), REQ(NM_DHCP_OPTION_DHCP4_NM_NEXT_SERVER, "next_server", FALSE), + REQ(NM_DHCP_OPTION_DHCP4_NM_FILENAME, "filename", FALSE), }; static const NMDhcpOption *const _sorted_options_4[G_N_ELEMENTS(_nm_dhcp_option_dhcp4_options)] = { @@ -184,7 +185,7 @@ static const NMDhcpOption *const _sorted_options_4[G_N_ELEMENTS(_nm_dhcp_option_ A(98), A(99), A(100), A(101), A(102), A(103), A(104), A(105), A(106), A(107), A(108), A(109), A(110), A(111), A(112), A(113), A(114), A(115), A(116), A(117), A(118), A(119), A(120), A(121), A(122), A(123), A(124), A(125), A(126), A(127), A(128), A(129), A(130), A(131), A(132), A(133), - A(134), A(15), A(135), A(136), A(16), A(137), A(138), A(139), A(140), A(141), + A(134), A(15), A(135), A(136), A(16), A(137), A(138), A(139), A(140), A(141), A(142), #undef A }; @@ -404,7 +405,10 @@ nm_dhcp_option_add_option_utf8safe_escape(GHashTable * options, gs_free char *to_free = NULL; const char * escaped; - escaped = nm_utils_buf_utf8safe_escape((char *) data, n_data, 0, &to_free); + escaped = nm_utils_buf_utf8safe_escape((char *) data, + n_data, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &to_free); nm_dhcp_option_add_option(options, addr_family, option, escaped ?: ""); } diff --git a/src/core/dhcp/nm-dhcp-options.h b/src/core/dhcp/nm-dhcp-options.h index 585f1187..8ed2039d 100644 --- a/src/core/dhcp/nm-dhcp-options.h +++ b/src/core/dhcp/nm-dhcp-options.h @@ -153,6 +153,7 @@ typedef enum { NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS = 1024, NM_DHCP_OPTION_DHCP4_NM_EXPIRY = 1025, NM_DHCP_OPTION_DHCP4_NM_NEXT_SERVER = 1026, + NM_DHCP_OPTION_DHCP4_NM_FILENAME = 1027, /* 'file' DHCP header */ } NMDhcpOptionDhcp4Options; typedef enum { @@ -185,7 +186,7 @@ typedef struct { bool include; } NMDhcpOption; -extern const NMDhcpOption _nm_dhcp_option_dhcp4_options[142]; +extern const NMDhcpOption _nm_dhcp_option_dhcp4_options[143]; extern const NMDhcpOption _nm_dhcp_option_dhcp6_options[16]; static inline const char * diff --git a/src/core/main.c b/src/core/main.c index cfcdb860..3cb5c07f 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -304,14 +304,14 @@ main(int argc, char *argv[]) const char *const * warnings; int errsv; + _nm_utils_is_manager_process = TRUE; + /* Known to cause a possible deadlock upon GDBus initialization: * https://bugzilla.gnome.org/show_bug.cgi?id=674885 */ g_type_ensure(G_TYPE_SOCKET); g_type_ensure(G_TYPE_DBUS_CONNECTION); g_type_ensure(NM_TYPE_DBUS_MANAGER); - _nm_utils_is_manager_process = TRUE; - main_loop = g_main_loop_new(NULL, FALSE); /* we determine a first-start (contrary to a restart during the same boot) diff --git a/src/core/nm-config-data.c b/src/core/nm-config-data.c index e127ea23..0cbff027 100644 --- a/src/core/nm-config-data.c +++ b/src/core/nm-config-data.c @@ -26,6 +26,15 @@ typedef struct { gboolean has; GSList * spec; } match_device; + union { + struct { + GSList * allowed_connections; + gboolean allowed_connections_has; + } device; + }; + gboolean is_device; + + /* List of key/value pairs in the section, sorted by key */ gsize lookup_len; const NMUtilsNamedValue *lookup_idx; } MatchSectionInfo; @@ -1436,13 +1445,13 @@ _match_section_infos_lookup(const MatchSectionInfo *match_section_infos, match = TRUE; if (match) { - *out_value = value; + NM_SET_OUT(out_value, value); return match_section_infos; } } out: - *out_value = NULL; + NM_SET_OUT(out_value, NULL); return NULL; } @@ -1538,6 +1547,37 @@ nm_config_data_get_device_config_int64(const NMConfigData *self, return _nm_utils_ascii_str_to_int64(value, base, min, max, val_invalid); } +const GSList * +nm_config_data_get_device_allowed_connections_specs(const NMConfigData *self, + NMDevice * device, + gboolean * has_match) +{ + const NMConfigDataPrivate *priv; + const MatchSectionInfo * connection_info; + const GSList * ret = NULL; + + g_return_val_if_fail(self, NULL); + + priv = NM_CONFIG_DATA_GET_PRIVATE(self); + + connection_info = _match_section_infos_lookup(&priv->device_infos[0], + priv->keyfile, + NM_CONFIG_KEYFILE_KEY_DEVICE_ALLOWED_CONNECTIONS, + device, + NULL, + NULL, + NULL); + + if (connection_info) { + nm_assert(connection_info->device.allowed_connections_has); + ret = connection_info->device.allowed_connections; + NM_SET_OUT(has_match, TRUE); + } else + NM_SET_OUT(has_match, FALSE); + + return ret; +} + const char * nm_config_data_get_connection_default(const NMConfigData *self, const char * property, @@ -1610,7 +1650,10 @@ _match_section_info_get_str(const MatchSectionInfo *m, GKeyFile *keyfile, const } static void -_match_section_info_init(MatchSectionInfo *connection_info, GKeyFile *keyfile, char *group) +_match_section_info_init(MatchSectionInfo *connection_info, + GKeyFile * keyfile, + char * group, + gboolean is_device) { char ** keys = NULL; gsize n_keys; @@ -1629,6 +1672,14 @@ _match_section_info_init(MatchSectionInfo *connection_info, GKeyFile *keyfile, c connection_info->stop_match = nm_config_keyfile_get_boolean(keyfile, group, NM_CONFIG_KEYFILE_KEY_STOP_MATCH, FALSE); + if (is_device) { + connection_info->device.allowed_connections = + nm_config_get_match_spec(keyfile, + group, + NM_CONFIG_KEYFILE_KEY_DEVICE_ALLOWED_CONNECTIONS, + &connection_info->device.allowed_connections_has); + } + keys = g_key_file_get_keys(keyfile, group, &n_keys, NULL); nm_utils_strv_sort(keys, n_keys); @@ -1680,9 +1731,13 @@ _match_section_infos_free(MatchSectionInfo *match_section_infos) if (!match_section_infos) return; + for (m = match_section_infos; m->group_name; m++) { g_free(m->group_name); g_slist_free_full(m->match_device.spec, g_free); + if (m->is_device) { + g_slist_free_full(m->device.allowed_connections, g_free); + } for (i = 0; i < m->lookup_len; i++) { g_free(m->lookup_idx[i].name_mutable); g_free(m->lookup_idx[i].value_str_mutable); @@ -1693,12 +1748,16 @@ _match_section_infos_free(MatchSectionInfo *match_section_infos) } static MatchSectionInfo * -_match_section_infos_construct(GKeyFile *keyfile, const char *prefix) +_match_section_infos_construct(GKeyFile *keyfile, gboolean is_device) { char ** groups; gsize i, j, ngroups; char * connection_tag = NULL; MatchSectionInfo *match_section_infos = NULL; + const char * prefix; + + prefix = + is_device ? NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE : NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION; /* get the list of existing [connection.\+]/[device.\+] sections. * @@ -1730,13 +1789,17 @@ _match_section_infos_construct(GKeyFile *keyfile, const char *prefix) } match_section_infos = g_new0(MatchSectionInfo, ngroups + 1 + (connection_tag ? 1 : 0)); + match_section_infos->is_device = is_device; for (i = 0; i < ngroups; i++) { /* pass ownership of @group on... */ - _match_section_info_init(&match_section_infos[i], keyfile, groups[ngroups - i - 1]); + _match_section_info_init(&match_section_infos[i], + keyfile, + groups[ngroups - i - 1], + is_device); } if (connection_tag) { /* pass ownership of @connection_tag on... */ - _match_section_info_init(&match_section_infos[i], keyfile, connection_tag); + _match_section_info_init(&match_section_infos[i], keyfile, connection_tag, is_device); } g_free(groups); @@ -1950,10 +2013,8 @@ constructed(GObject *object) priv->keyfile = _merge_keyfiles(priv->keyfile_user, priv->keyfile_intern); - priv->connection_infos = - _match_section_infos_construct(priv->keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION); - priv->device_infos = - _match_section_infos_construct(priv->keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE); + priv->connection_infos = _match_section_infos_construct(priv->keyfile, FALSE); + priv->device_infos = _match_section_infos_construct(priv->keyfile, TRUE); priv->connectivity.enabled = nm_config_keyfile_get_boolean(priv->keyfile, diff --git a/src/core/nm-config-data.h b/src/core/nm-config-data.h index 42d69a8b..fa58d869 100644 --- a/src/core/nm-config-data.h +++ b/src/core/nm-config-data.h @@ -243,6 +243,10 @@ gint64 nm_config_data_get_device_config_int64(const NMConfigData *self, gint64 val_no_match, gint64 val_invalid); +const GSList *nm_config_data_get_device_allowed_connections_specs(const NMConfigData *self, + NMDevice * device, + gboolean * has_match); + char ** nm_config_data_get_groups(const NMConfigData *self); char ** nm_config_data_get_keys(const NMConfigData *self, const char *group); gboolean nm_config_data_is_intern_atomic_group(const NMConfigData *self, const char *group); diff --git a/src/core/nm-config.c b/src/core/nm-config.c index fdc450a0..817368d1 100644 --- a/src/core/nm-config.c +++ b/src/core/nm-config.c @@ -796,6 +796,7 @@ static gboolean _setting_is_device_spec(const char *group, const char *key) { #define _IS(group_v, key_v) (nm_streq(group, "" group_v "") && nm_streq(key, "" key_v "")) + return _IS(NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_NO_AUTO_DEFAULT) || _IS(NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_IGNORE_CARRIER) || _IS(NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_ASSUME_IPV6LL_ONLY) @@ -806,6 +807,13 @@ _setting_is_device_spec(const char *group, const char *key) && nm_streq(key, NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE)); } +static gboolean +_setting_is_connection_spec(const char *group, const char *key) +{ + return NM_STR_HAS_PREFIX(group, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE) + && nm_streq(key, NM_CONFIG_KEYFILE_KEY_DEVICE_ALLOWED_CONNECTIONS); +} + static gboolean _setting_is_string_list(const char *group, const char *key) { @@ -878,6 +886,8 @@ static const ConfigGroup config_groups[] = { NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER, NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS, + NM_CONFIG_KEYFILE_KEY_DEVICE_KEEP_CONFIGURATION, + NM_CONFIG_KEYFILE_KEY_DEVICE_ALLOWED_CONNECTIONS, NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND, NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS, NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_GENERATE_MAC_ADDRESS_MASK, @@ -1059,7 +1069,8 @@ read_config(GKeyFile * keyfile, is_string_list = _setting_is_string_list(group, base_key); - if (is_string_list || _setting_is_device_spec(group, base_key)) { + if (is_string_list || _setting_is_device_spec(group, base_key) + || _setting_is_connection_spec(group, base_key)) { gs_unref_ptrarray GPtrArray *new = g_ptr_array_new_with_free_func(g_free); char ** iter_val; gs_strfreev char **old_val = NULL; @@ -2343,8 +2354,9 @@ _nm_config_state_set(NMConfig *self, gboolean allow_persist, gboolean force_pers "route-metric-default-aspired" #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE \ "route-metric-default-effective" -#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH "root-path" -#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NEXT_SERVER "next-server" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH "root-path" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NEXT_SERVER "next-server" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_DHCP_BOOTFILE "dhcp-bootfile" static NM_UTILS_LOOKUP_STR_DEFINE( _device_state_managed_type_to_str, @@ -2565,7 +2577,8 @@ nm_config_device_state_write(int ifindex, guint32 route_metric_default_aspired, guint32 route_metric_default_effective, const char * next_server, - const char * root_path) + const char * root_path, + const char * dhcp_bootfile) { char path[NM_STRLEN(NM_CONFIG_DEVICE_STATE_DIR "/") + DEVICE_STATE_FILENAME_LEN_MAX + 1]; GError *local = NULL; @@ -2632,6 +2645,12 @@ nm_config_device_state_write(int ifindex, DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH, root_path); } + if (dhcp_bootfile) { + g_key_file_set_string(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_DHCP_BOOTFILE, + dhcp_bootfile); + } if (!g_key_file_save_to_file(kf, path, &local)) { _LOGW("device-state: write #%d (%s) failed: %s", ifindex, path, local->message); @@ -2639,7 +2658,9 @@ nm_config_device_state_write(int ifindex, return FALSE; } _LOGT("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s, " - "route-metric-default=%" G_GUINT32_FORMAT "-%" G_GUINT32_FORMAT "%s%s%s%s%s%s", + "route-metric-default=%" G_GUINT32_FORMAT "-%" G_GUINT32_FORMAT "%s%s%s" + "%s%s%s" + "%s%s%s", ifindex, path, _device_state_managed_type_to_str(managed), @@ -2648,7 +2669,8 @@ nm_config_device_state_write(int ifindex, route_metric_default_aspired, route_metric_default_effective, NM_PRINT_FMT_QUOTED(next_server, ", next-server=", next_server, "", ""), - NM_PRINT_FMT_QUOTED(root_path, ", root-path=", root_path, "", "")); + NM_PRINT_FMT_QUOTED(root_path, ", root-path=", root_path, "", ""), + NM_PRINT_FMT_QUOTED(dhcp_bootfile, ", dhcp-bootfile=", dhcp_bootfile, "", "")); return TRUE; } diff --git a/src/core/nm-config.h b/src/core/nm-config.h index 7f1c0c4c..8279f4d2 100644 --- a/src/core/nm-config.h +++ b/src/core/nm-config.h @@ -185,7 +185,8 @@ gboolean nm_config_device_state_write(int guint32 route_metric_default_aspired, guint32 route_metric_default_effective, const char * next_server, - const char * root_path); + const char * root_path, + const char * dhcp_bootfile); void nm_config_device_state_prune_stale(GHashTable *preserve_ifindexes, NMPlatform *preserve_in_platform); diff --git a/src/core/nm-core-utils.c b/src/core/nm-core-utils.c index 41d22565..8fdc7379 100644 --- a/src/core/nm-core-utils.c +++ b/src/core/nm-core-utils.c @@ -1484,6 +1484,112 @@ nm_match_spec_device(const GSList *specs, return _match_result(has_except, has_not_except, has_match, has_match_except); } +typedef struct { + const char *uuid; + const char *id; + const char *origin; +} MatchConnectionData; + +static gboolean +match_connection_eval(const char *spec_str, const MatchConnectionData *match_data) +{ + if (spec_str[0] == '*' && spec_str[1] == '\0') + return TRUE; + + if (_MATCH_CHECK(spec_str, "id:")) + return nm_streq0(spec_str, match_data->id); + + if (_MATCH_CHECK(spec_str, "uuid:")) + return nm_streq0(spec_str, match_data->uuid); + + if (_MATCH_CHECK(spec_str, "origin:")) + return nm_streq0(spec_str, match_data->origin); + + return FALSE; +} + +static NMMatchSpecMatchType +match_spec_connection(const GSList *specs, const char *id, const char *uuid, const char *origin) +{ + const GSList * iter; + gboolean has_match = FALSE; + gboolean has_match_except = FALSE; + gboolean has_except = FALSE; + gboolean has_not_except = FALSE; + const char * spec_str; + const MatchConnectionData match_data = { + .id = nm_str_not_empty(id), + .uuid = nm_str_not_empty(uuid), + .origin = nm_str_not_empty(origin), + }; + + if (!specs) + return NM_MATCH_SPEC_NO_MATCH; + + for (iter = specs; iter; iter = iter->next) { + gboolean except; + + spec_str = iter->data; + + if (!spec_str || !*spec_str) + continue; + + 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_connection_eval(spec_str, &match_data)) + continue; + + if (except) + has_match_except = TRUE; + else + has_match = TRUE; + } + + return _match_result(has_except, has_not_except, has_match, has_match_except); +} + +int +nm_utils_connection_match_spec_list(NMConnection *connection, + const GSList *specs, + int no_match_value) +{ + NMMatchSpecMatchType m; + NMSettingUser * s_user; + const char * origin = NULL; + + if (!specs) + return no_match_value; + + s_user = _nm_connection_get_setting(connection, NM_TYPE_SETTING_USER); + if (s_user) + origin = nm_setting_user_get_data(s_user, NM_USER_TAG_ORIGIN); + + m = match_spec_connection(specs, + nm_connection_get_id(connection), + nm_connection_get_uuid(connection), + origin); + switch (m) { + case NM_MATCH_SPEC_MATCH: + return TRUE; + case NM_MATCH_SPEC_NEG_MATCH: + return FALSE; + case NM_MATCH_SPEC_NO_MATCH: + return no_match_value; + } + nm_assert_not_reached(); + return no_match_value; +} + static gboolean match_config_eval(const char *str, const char *tag, guint cur_nm_version) { diff --git a/src/core/nm-core-utils.h b/src/core/nm-core-utils.h index bcb182b8..76c340d1 100644 --- a/src/core/nm-core-utils.h +++ b/src/core/nm-core-utils.h @@ -211,6 +211,10 @@ gboolean nm_utils_kernel_cmdline_match_check(const char *const *proc_cmdline, guint num_patterns, GError ** error); +int nm_utils_connection_match_spec_list(NMConnection *connection, + const GSList *specs, + int no_match_value); + /*****************************************************************************/ gboolean nm_utils_connection_has_default_route(NMConnection *connection, diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index 3f225156..a2446553 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -613,17 +613,17 @@ _fw_nft_set(gboolean add, const char *ip_iface, in_addr_t addr, guint8 plen) #define _append(p_strbuf, fmt, ...) nm_str_buf_append_printf((p_strbuf), "" fmt "\n", ##__VA_ARGS__) - _append(&strbuf, "add table inet %s", table_name); - _append(&strbuf, "%s table inet %s", add ? "flush" : "delete", table_name); + _append(&strbuf, "add table ip %s", table_name); + _append(&strbuf, "%s table ip %s", add ? "flush" : "delete", table_name); if (add) { _append(&strbuf, - "add chain inet %s nat_postrouting {" + "add chain ip %s nat_postrouting {" " type nat hook postrouting priority 100; policy accept; " "};", table_name); _append(&strbuf, - "add rule inet %s nat_postrouting ip saddr %s ip daddr != %s masquerade;", + "add rule ip %s nat_postrouting ip saddr %s ip daddr != %s masquerade;", table_name, str_subnet, str_subnet); @@ -634,41 +634,41 @@ _fw_nft_set(gboolean add, const char *ip_iface, in_addr_t addr, guint8 plen) */ /* _append(&strbuf, - "add chain inet %s filter_input {" + "add chain ip %s filter_input {" " type filter hook input priority 0; policy accept; " "};", table_name); - _append(&strbuf, "add rule inet %s filter_input tcp dport { 67, 53 } accept;", table_name); - _append(&strbuf, "add rule inet %s filter_input udp dport { 67, 53 } accept;", table_name); + _append(&strbuf, "add rule ip %s filter_input tcp dport { 67, 53 } accept;", table_name); + _append(&strbuf, "add rule ip %s filter_input udp dport { 67, 53 } accept;", table_name); */ _append(&strbuf, - "add chain inet %s filter_forward {" + "add chain ip %s filter_forward {" " type filter hook forward priority 0; policy accept; " "};", table_name); _append(&strbuf, - "add rule inet %s filter_forward ip daddr %s oifname \"%s\" " + "add rule ip %s filter_forward ip daddr %s oifname \"%s\" " " ct state { established, related } accept;", table_name, str_subnet, ip_iface); _append(&strbuf, - "add rule inet %s filter_forward ip saddr %s iifname \"%s\" accept;", + "add rule ip %s filter_forward ip saddr %s iifname \"%s\" accept;", table_name, str_subnet, ip_iface); _append(&strbuf, - "add rule inet %s filter_forward iifname \"%s\" oifname \"%s\" accept;", + "add rule ip %s filter_forward iifname \"%s\" oifname \"%s\" accept;", table_name, ip_iface, ip_iface); _append(&strbuf, - "add rule inet %s filter_forward iifname \"%s\" reject;", + "add rule ip %s filter_forward iifname \"%s\" reject;", table_name, ip_iface); _append(&strbuf, - "add rule inet %s filter_forward oifname \"%s\" reject;", + "add rule ip %s filter_forward oifname \"%s\" reject;", table_name, ip_iface); } diff --git a/src/core/nm-firewalld-manager.c b/src/core/nm-firewalld-manager.c index c562d978..7866894a 100644 --- a/src/core/nm-firewalld-manager.c +++ b/src/core/nm-firewalld-manager.c @@ -15,6 +15,7 @@ #define FIREWALL_DBUS_SERVICE "org.fedoraproject.FirewallD1" #define FIREWALL_DBUS_PATH "/org/fedoraproject/FirewallD1" +#define FIREWALL_DBUS_INTERFACE "org.fedoraproject.FirewallD1" #define FIREWALL_DBUS_INTERFACE_ZONE "org.fedoraproject.FirewallD1.zone" /*****************************************************************************/ @@ -30,10 +31,12 @@ typedef struct { CList pending_calls; + char *name_owner; + + guint reloaded_id; guint name_owner_changed_id; bool dbus_inited : 1; - bool running : 1; } NMFirewalldManagerPrivate; struct _NMFirewalldManager { @@ -105,7 +108,7 @@ _ops_type_to_string(OpsType ops_type) } #define _NMLOG_DOMAIN LOGD_FIREWALL -#define _NMLOG_PREFIX_NAME "firewall" +#define _NMLOG_PREFIX_NAME "firewalld" #define _NMLOG(level, call_id, ...) \ G_STMT_START \ { \ @@ -146,6 +149,14 @@ _ops_type_to_string(OpsType ops_type) /*****************************************************************************/ +static void +_signal_emit_state_changed(NMFirewalldManager *self, NMFirewalldManagerStateChangedType signal_type) +{ + g_signal_emit(self, signals[STATE_CHANGED], 0, (int) signal_type); +} + +/*****************************************************************************/ + static gboolean _get_running(NMFirewalldManagerPrivate *priv) { @@ -154,7 +165,7 @@ _get_running(NMFirewalldManagerPrivate *priv) * service is indeed running. That is the time when we queue the * requests, and they will be started once the get-name-owner call * returns. */ - return priv->running || (priv->dbus_connection && !priv->dbus_inited); + return priv->name_owner || (priv->dbus_connection && !priv->dbus_inited); } gboolean @@ -315,7 +326,7 @@ _handle_dbus_start(NMFirewalldManager *self, NMFirewalldManagerCallId *call_id) GVariant * arg; nm_assert(call_id); - nm_assert(priv->running); + nm_assert(priv->name_owner); nm_assert(!call_id->is_idle); nm_assert(c_list_contains(&priv->pending_calls, &call_id->lst)); @@ -341,7 +352,7 @@ _handle_dbus_start(NMFirewalldManager *self, NMFirewalldManagerCallId *call_id) call_id->dbus.cancellable = g_cancellable_new(); g_dbus_connection_call(priv->dbus_connection, - FIREWALL_DBUS_SERVICE, + priv->name_owner, FIREWALL_DBUS_PATH, FIREWALL_DBUS_INTERFACE_ZONE, dbus_method, @@ -378,10 +389,10 @@ _start_request(NMFirewalldManager * self, iface, NM_PRINT_FMT_QUOTED(zone, "\"", zone, "\"", "default"), call_id->is_idle ? " (not running, simulate success)" - : (!priv->running ? " (waiting to initialize)" : "")); + : (!priv->name_owner ? " (waiting to initialize)" : "")); if (!call_id->is_idle) { - if (priv->running) + if (priv->name_owner) _handle_dbus_start(self, call_id); if (!call_id->callback) { /* if the user did not provide a callback, the call_id is useless. @@ -463,6 +474,7 @@ name_owner_changed(NMFirewalldManager *self, const char *owner) gboolean was_running; gboolean now_running; gboolean just_initied; + gboolean name_owner_changed; owner = nm_str_not_empty(owner); @@ -474,8 +486,8 @@ name_owner_changed(NMFirewalldManager *self, const char *owner) was_running = _get_running(priv); just_initied = !priv->dbus_inited; - priv->dbus_inited = TRUE; - priv->running = !!owner; + priv->dbus_inited = TRUE; + name_owner_changed = nm_utils_strdup_reset(&priv->name_owner, owner); now_running = _get_running(priv); @@ -495,7 +507,7 @@ name_owner_changed(NMFirewalldManager *self, const char *owner) nm_assert(!call_id->is_idle); nm_assert(call_id->dbus.arg); - if (priv->running) { + if (priv->name_owner) { _LOGD(call_id, "initalizing: make D-Bus call"); _handle_dbus_start(self, call_id); } else { @@ -511,8 +523,30 @@ name_owner_changed(NMFirewalldManager *self, const char *owner) } } - if (was_running != now_running) - g_signal_emit(self, signals[STATE_CHANGED], 0, FALSE); + if (just_initied) + _signal_emit_state_changed(self, NM_FIREWALLD_MANAGER_STATE_CHANGED_TYPE_INITIALIZED); + else if (was_running != now_running || name_owner_changed) + _signal_emit_state_changed(self, + NM_FIREWALLD_MANAGER_STATE_CHANGED_TYPE_NAME_OWNER_CHANGED); +} + +static void +reloaded_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMFirewalldManager * self = user_data; + NMFirewalldManagerPrivate *priv = NM_FIREWALLD_MANAGER_GET_PRIVATE(self); + + if (!nm_streq0(sender_name, priv->name_owner)) + return; + + _LOGT(NULL, "reloaded signal received"); + _signal_emit_state_changed(self, NM_FIREWALLD_MANAGER_STATE_CHANGED_TYPE_RELOADED); } static void @@ -541,7 +575,7 @@ get_name_owner_cb(const char *name_owner, GError *error, gpointer user_data) NMFirewalldManager * self; NMFirewalldManagerPrivate *priv; - if (!name_owner && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + if (nm_utils_error_is_cancelled(error)) return; self = user_data; @@ -568,6 +602,17 @@ nm_firewalld_manager_init(NMFirewalldManager *self) return; } + priv->reloaded_id = g_dbus_connection_signal_subscribe(priv->dbus_connection, + FIREWALL_DBUS_SERVICE, + FIREWALL_DBUS_INTERFACE, + "Reloaded", + FIREWALL_DBUS_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + reloaded_cb, + self, + NULL); + priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection, FIREWALL_DBUS_SERVICE, @@ -594,6 +639,7 @@ dispose(GObject *object) * we don't expect pending operations at this point. */ nm_assert(c_list_is_empty(&priv->pending_calls)); + nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->reloaded_id); nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); nm_clear_g_cancellable(&priv->get_name_owner_cancellable); @@ -616,8 +662,8 @@ nm_firewalld_manager_class_init(NMFirewalldManagerClass *klass) 0, NULL, NULL, - g_cclosure_marshal_VOID__BOOLEAN, + g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, - G_TYPE_BOOLEAN /* initialized_now */); + G_TYPE_INT /* signal-type */); } diff --git a/src/core/nm-firewalld-manager.h b/src/core/nm-firewalld-manager.h index febb9bac..1f76beba 100644 --- a/src/core/nm-firewalld-manager.h +++ b/src/core/nm-firewalld-manager.h @@ -19,6 +19,12 @@ #define NM_FIREWALLD_MANAGER_STATE_CHANGED "state-changed" +typedef enum { + NM_FIREWALLD_MANAGER_STATE_CHANGED_TYPE_INITIALIZED, + NM_FIREWALLD_MANAGER_STATE_CHANGED_TYPE_NAME_OWNER_CHANGED, + NM_FIREWALLD_MANAGER_STATE_CHANGED_TYPE_RELOADED, +} NMFirewalldManagerStateChangedType; + typedef struct _NMFirewalldManagerCallId NMFirewalldManagerCallId; typedef struct _NMFirewalldManager NMFirewalldManager; diff --git a/src/core/nm-ip4-config.c b/src/core/nm-ip4-config.c index 47f0ee3a..90531d02 100644 --- a/src/core/nm-ip4-config.c +++ b/src/core/nm-ip4-config.c @@ -163,6 +163,11 @@ _nm_ip_config_add_obj(NMDedupMultiIndex * multi_idx, obj_new_stackinit.ip_route.rt_source = obj_old->ip_route.rt_source; modified = TRUE; } + if (!obj_new->ip_route.is_external && obj_old->ip_route.is_external) { + obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new); + obj_new_stackinit.ip_route.is_external = FALSE; + modified = TRUE; + } break; default: nm_assert_not_reached(); @@ -507,22 +512,6 @@ _notify_routes(NMIP4Config *self) /*****************************************************************************/ -static int -sort_captured_addresses(const CList *lst_a, const CList *lst_b, gconstpointer user_data) -{ - const NMPlatformIP4Address *addr_a = - NMP_OBJECT_CAST_IP4_ADDRESS(c_list_entry(lst_a, NMDedupMultiEntry, lst_entries)->obj); - const NMPlatformIP4Address *addr_b = - NMP_OBJECT_CAST_IP4_ADDRESS(c_list_entry(lst_b, NMDedupMultiEntry, lst_entries)->obj); - - nm_assert(addr_a); - nm_assert(addr_b); - - /* Primary addresses first */ - return NM_FLAGS_HAS(addr_a->n_ifa_flags, IFA_F_SECONDARY) - - NM_FLAGS_HAS(addr_b->n_ifa_flags, IFA_F_SECONDARY); -} - NMIP4Config * nm_ip4_config_clone(const NMIP4Config *self) { @@ -554,7 +543,7 @@ nm_ip4_config_capture(NMDedupMultiIndex *multi_idx, NMPlatform *platform, int if head_entry = nm_platform_lookup_object(platform, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex); if (head_entry) { - nmp_cache_iter_for_each (&iter, head_entry, &plobj) { + nmp_cache_iter_for_each_reverse (&iter, head_entry, &plobj) { if (!_nm_ip_config_add_obj(priv->multi_idx, &priv->idx_ip4_addresses_, ifindex, @@ -566,9 +555,6 @@ nm_ip4_config_capture(NMDedupMultiIndex *multi_idx, NMPlatform *platform, int if NULL)) nm_assert_not_reached(); } - head_entry = nm_ip4_config_lookup_addresses(self); - nm_assert(head_entry); - nm_dedup_multi_head_entry_sort(head_entry, sort_captured_addresses, NULL); _notify_addresses(self); } diff --git a/src/core/nm-ip6-config.c b/src/core/nm-ip6-config.c index d2ecf175..65e84737 100644 --- a/src/core/nm-ip6-config.c +++ b/src/core/nm-ip6-config.c @@ -316,7 +316,7 @@ nm_ip6_config_capture(NMDedupMultiIndex * multi_idx, head_entry = nm_platform_lookup_object(platform, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex); if (head_entry) { - nmp_cache_iter_for_each (&iter, head_entry, &plobj) { + nmp_cache_iter_for_each_reverse (&iter, head_entry, &plobj) { if (!_nm_ip_config_add_obj(priv->multi_idx, &priv->idx_ip6_addresses_, ifindex, @@ -328,11 +328,6 @@ nm_ip6_config_capture(NMDedupMultiIndex * multi_idx, NULL)) nm_assert_not_reached(); } - head_entry = nm_ip6_config_lookup_addresses(self); - nm_assert(head_entry); - nm_dedup_multi_head_entry_sort(head_entry, - sort_captured_addresses, - GINT_TO_POINTER(use_temporary)); _notify_addresses(self); } diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c index b66f7fbb..d859c1ea 100644 --- a/src/core/nm-manager.c +++ b/src/core/nm-manager.c @@ -2595,6 +2595,12 @@ get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generat nm_device_capture_initial_config(device); + if (!nm_device_can_assume_connections(device)) { + nm_device_assume_state_reset(device); + _LOG2D(LOGD_DEVICE, device, "assume: device cannot assume connection"); + return NULL; + } + if (ifindex) { int master_ifindex = nm_platform_link_get_master(priv->platform, ifindex); @@ -2626,39 +2632,47 @@ get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generat } } - /* The core of the API is nm_device_generate_connection() function and - * update_connection() virtual method and the convenient connection_type - * class attribute. Subclasses supporting the new API must have - * update_connection() implemented, otherwise nm_device_generate_connection() - * returns NULL. - */ - connection = nm_device_generate_connection(device, master, &maybe_later, &gen_error); - if (!connection) { - if (maybe_later) { - /* The device can generate a connection, but it failed for now. - * Give it a chance to match a connection from the state file. */ - only_by_uuid = TRUE; - } else { - nm_device_assume_state_reset(device); - _LOG2D(LOGD_DEVICE, - device, - "assume: cannot generate connection: %s", - gen_error->message); - return NULL; + if (nm_config_data_get_device_config_boolean(NM_CONFIG_GET_DATA, + NM_CONFIG_KEYFILE_KEY_DEVICE_KEEP_CONFIGURATION, + device, + TRUE, + TRUE)) { + /* The core of the API is nm_device_generate_connection() function, based on + * update_connection() virtual method and the @connection_type_supported + * class attribute. Devices that support assuming existing connections must + * have update_connection() implemented, otherwise + * nm_device_generate_connection() returns NULL. */ + connection = nm_device_generate_connection(device, master, &maybe_later, &gen_error); + if (!connection) { + if (maybe_later) { + /* The device can potentially assume connections, but at this + * time we can't generate a connection because no address is + * configured. Allow the device to assume a connection indicated + * in the state file by UUID. */ + only_by_uuid = TRUE; + } else { + nm_device_assume_state_reset(device); + _LOG2D(LOGD_DEVICE, + device, + "assume: cannot generate connection: %s", + gen_error->message); + return NULL; + } } + } else { + connection = NULL; + only_by_uuid = TRUE; + g_set_error(&gen_error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + "device %s has 'keep-configuration=no'", + nm_device_get_iface(device)); } nm_device_assume_state_get(device, &assume_state_guess_assume, &assume_state_connection_uuid); - /* Now we need to compare the generated connection to each configured - * connection. The comparison function is the heart of the connection - * assumption implementation and it must compare the connections very - * carefully to sort out various corner cases. Also, the comparison is - * not entirely symmetric. - * - * When no configured connection matches the generated connection, we keep - * the generated connection instead. - */ + /* If the device state file indicates a connection that was active before NM + * restarted, perform basic sanity checks on it. */ if (assume_state_connection_uuid && (connection_checked = nm_settings_get_connection_by_uuid(priv->settings, assume_state_connection_uuid)) @@ -2692,8 +2706,9 @@ get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generat gs_free NMSettingsConnection **sett_conns = NULL; guint len, i, j; - /* the state file doesn't indicate a connection UUID to assume. Search the - * persistent connections for a matching candidate. */ + /* @assume_state_guess_assume=TRUE means this is the first start of NM + * and the state file contains no UUID. Search persistent connections + * for a matching candidate. */ sett_conns = nm_manager_get_activatable_connections(self, FALSE, FALSE, &len); if (len > 0) { for (i = 0, j = 0; i < len; i++) { @@ -2766,6 +2781,8 @@ get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generat return matched; } + /* When no configured connection matches the generated connection, we keep + * the generated connection instead. */ _LOG2D(LOGD_DEVICE, device, "assume: generated connection '%s' (%s)", @@ -6803,8 +6820,9 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde guint32 route_metric_default_effective; NMTernary nm_owned; NMDhcpConfig * dhcp_config; - const char * next_server = NULL; - const char * root_path = NULL; + const char * next_server = NULL; + const char * root_path = NULL; + const char * dhcp_bootfile = NULL; NM_SET_OUT(out_ifindex, 0); @@ -6848,8 +6866,11 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde dhcp_config = nm_device_get_dhcp_config(device, AF_INET); if (dhcp_config) { - root_path = nm_dhcp_config_get_option(dhcp_config, "root_path"); - next_server = nm_dhcp_config_get_option(dhcp_config, "next_server"); + root_path = nm_dhcp_config_get_option(dhcp_config, "root_path"); + next_server = nm_dhcp_config_get_option(dhcp_config, "next_server"); + dhcp_bootfile = nm_dhcp_config_get_option(dhcp_config, "filename"); + if (!dhcp_bootfile) + dhcp_bootfile = nm_dhcp_config_get_option(dhcp_config, "bootfile_name"); } if (!nm_config_device_state_write(ifindex, @@ -6860,7 +6881,8 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde route_metric_default_aspired, route_metric_default_effective, next_server, - root_path)) + root_path, + dhcp_bootfile)) return FALSE; NM_SET_OUT(out_ifindex, ifindex); diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c index e4914acf..e147e504 100644 --- a/src/core/nm-policy.c +++ b/src/core/nm-policy.c @@ -2519,14 +2519,15 @@ connection_added(NMSettings *settings, NMSettingsConnection *connection, gpointe } static void -firewall_state_changed(NMFirewalldManager *manager, gboolean initialized_now, gpointer user_data) +firewall_state_changed(NMFirewalldManager *manager, int signal_type_i, gpointer user_data) { - NMPolicy * self = (NMPolicy *) user_data; - NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self); - const CList * tmp_lst; - NMDevice * device; + const NMFirewalldManagerStateChangedType signal_type = signal_type_i; + NMPolicy * self = user_data; + NMPolicyPrivate * priv = NM_POLICY_GET_PRIVATE(self); + const CList * tmp_lst; + NMDevice * device; - if (initialized_now) { + if (signal_type == NM_FIREWALLD_MANAGER_STATE_CHANGED_TYPE_INITIALIZED) { /* the firewall manager was initializing, but all requests * so fare were queued and are already sent. No need to * re-update the firewall zone of the devices. */ diff --git a/src/core/platform/nm-fake-platform.c b/src/core/platform/nm-fake-platform.c index 0108b581..b773df9a 100644 --- a/src/core/platform/nm-fake-platform.c +++ b/src/core/platform/nm-fake-platform.c @@ -1117,6 +1117,9 @@ ip_route_add(NMPlatform * platform, : NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); r = NMP_OBJECT_CAST_IP_ROUTE(obj); + + r->is_external = TRUE; + nm_platform_ip_route_normalize(addr_family, r); switch (addr_family) { diff --git a/src/core/platform/tests/test-common.c b/src/core/platform/tests/test-common.c index f9a141bc..2fc9e836 100644 --- a/src/core/platform/tests/test-common.c +++ b/src/core/platform/tests/test-common.c @@ -1427,6 +1427,10 @@ nmtstp_link_bridge_normalize_jiffies_time(const NMPlatformLnkBridge *requested, const NMPlatformLnkBridge *kernel, NMPlatformLnkBridge * dst) { + g_assert(requested); + g_assert(dst); + g_assert(kernel); + if (dst != requested) *dst = *requested; diff --git a/src/core/platform/tests/test-route.c b/src/core/platform/tests/test-route.c index 2b9b2f8f..4b1db7fa 100644 --- a/src/core/platform/tests/test-route.c +++ b/src/core/platform/tests/test-route.c @@ -332,30 +332,33 @@ test_ip4_route(void) /* Test route listing */ routes = nmtstp_ip4_route_get_all(NM_PLATFORM_GET, ifindex); memset(rts, 0, sizeof(rts)); - rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[0].network = gateway; - rts[0].plen = 32; - rts[0].ifindex = ifindex; - rts[0].gateway = INADDR_ANY; - rts[0].metric = metric; - rts[0].mss = mss; - rts[0].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_LINK); - rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[1].network = network; - rts[1].plen = plen; - rts[1].ifindex = ifindex; - rts[1].gateway = gateway; - rts[1].metric = metric; - rts[1].mss = mss; - rts[1].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE); - rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[2].network = 0; - rts[2].plen = 0; - rts[2].ifindex = ifindex; - rts[2].gateway = gateway; - rts[2].metric = metric; - rts[2].mss = mss; - rts[2].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE); + rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); + rts[0].network = gateway; + rts[0].plen = 32; + rts[0].ifindex = ifindex; + rts[0].gateway = INADDR_ANY; + rts[0].metric = metric; + rts[0].mss = mss; + rts[0].is_external = TRUE; + rts[0].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_LINK); + rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); + rts[1].network = network; + rts[1].plen = plen; + rts[1].ifindex = ifindex; + rts[1].gateway = gateway; + rts[1].metric = metric; + rts[1].mss = mss; + rts[1].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE); + rts[1].is_external = TRUE; + rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); + rts[2].network = 0; + rts[2].plen = 0; + rts[2].ifindex = ifindex; + rts[2].gateway = gateway; + rts[2].metric = metric; + rts[2].mss = mss; + rts[2].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE); + rts[2].is_external = TRUE; g_assert_cmpint(routes->len, ==, 3); nmtst_platform_ip4_routes_equal_aptr((const NMPObject *const *) routes->pdata, rts, @@ -489,30 +492,33 @@ test_ip6_route(void) /* Test route listing */ routes = nmtstp_ip6_route_get_all(NM_PLATFORM_GET, ifindex); memset(rts, 0, sizeof(rts)); - rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[0].network = gateway; - rts[0].plen = 128; - rts[0].ifindex = ifindex; - rts[0].gateway = in6addr_any; - rts[0].pref_src = in6addr_any; - rts[0].metric = metric; - rts[0].mss = mss; - rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[1].network = network; - rts[1].plen = plen; - rts[1].ifindex = ifindex; - rts[1].gateway = gateway; - rts[1].pref_src = pref_src; - rts[1].metric = metric; - rts[1].mss = mss; - rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[2].network = in6addr_any; - rts[2].plen = 0; - rts[2].ifindex = ifindex; - rts[2].gateway = gateway; - rts[2].pref_src = in6addr_any; - rts[2].metric = metric; - rts[2].mss = mss; + rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); + rts[0].network = gateway; + rts[0].plen = 128; + rts[0].ifindex = ifindex; + rts[0].gateway = in6addr_any; + rts[0].pref_src = in6addr_any; + rts[0].metric = metric; + rts[0].mss = mss; + rts[0].is_external = TRUE; + rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); + rts[1].network = network; + rts[1].plen = plen; + rts[1].ifindex = ifindex; + rts[1].gateway = gateway; + rts[1].pref_src = pref_src; + rts[1].metric = metric; + rts[1].mss = mss; + rts[1].is_external = TRUE; + rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); + rts[2].network = in6addr_any; + rts[2].plen = 0; + rts[2].ifindex = ifindex; + rts[2].gateway = gateway; + rts[2].pref_src = in6addr_any; + rts[2].metric = metric; + rts[2].mss = mss; + rts[2].is_external = TRUE; g_assert_cmpint(routes->len, ==, 3); nmtst_platform_ip6_routes_equal_aptr((const NMPObject *const *) routes->pdata, rts, @@ -709,6 +715,7 @@ test_ip4_route_options(gconstpointer test_data) for (i = 0; i < rts_n; i++) { rts_cmp[i] = rts_add[i]; nm_platform_ip_route_normalize(AF_INET, NM_PLATFORM_IP_ROUTE_CAST(&rts_cmp[i])); + rts_cmp[i].is_external = TRUE; } routes = nmtstp_ip4_route_get_all(NM_PLATFORM_GET, IFINDEX); @@ -880,6 +887,7 @@ test_ip6_route_options(gconstpointer test_data) for (i = 0; i < rts_n; i++) { rts_cmp[i] = rts_add[i]; nm_platform_ip_route_normalize(AF_INET6, NM_PLATFORM_IP_ROUTE_CAST(&rts_cmp[i])); + rts_cmp[i].is_external = TRUE; } routes = nmtstp_ip6_route_get_all(NM_PLATFORM_GET, IFINDEX); diff --git a/src/core/ppp/meson.build b/src/core/ppp/meson.build index ffeb0eba..9ee46113 100644 --- a/src/core/ppp/meson.build +++ b/src/core/ppp/meson.build @@ -14,6 +14,7 @@ nm_pppd_plugin = shared_module( ], link_with: [ libnm_core_impl, + libnm_log_null, libnm_glib_aux, libnm_std_aux, libc_siphash, diff --git a/src/core/settings/nm-settings-connection.c b/src/core/settings/nm-settings-connection.c index 641f3297..36ef6acb 100644 --- a/src/core/settings/nm-settings-connection.c +++ b/src/core/settings/nm-settings-connection.c @@ -11,6 +11,7 @@ #include "c-list/src/c-list.h" #include "libnm-glib-aux/nm-keyfile-aux.h" +#include "libnm-glib-aux/nm-c-list.h" #include "libnm-core-aux-intern/nm-common-macros.h" #include "nm-config.h" #include "nm-config-data.h" @@ -30,6 +31,8 @@ #define AUTOCONNECT_RETRIES_FOREVER -1 #define AUTOCONNECT_RESET_RETRIES_TIMER 300 +#define SEEN_BSSIDS_MAX 30 + #define _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES \ ((NMSettingsUpdate2Flags) (NM_SETTINGS_UPDATE2_FLAG_TO_DISK \ | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY \ @@ -59,6 +62,56 @@ nm_settings_connections_array_to_connections(NMSettingsConnection *const *connec /*****************************************************************************/ +typedef struct { + char bssid[sizeof(NMEtherAddr) * 3]; + CList seen_bssids_lst; +} SeenBssidEntry; + +static inline SeenBssidEntry * +_seen_bssid_entry_init_stale(SeenBssidEntry *entry, const NMEtherAddr *bssid_bin) +{ + _nm_utils_hwaddr_ntoa(bssid_bin, sizeof(NMEtherAddr), TRUE, entry->bssid, sizeof(entry->bssid)); + return entry; +} + +static inline SeenBssidEntry * +_seen_bssid_entry_new_stale_bin(const NMEtherAddr *bssid_bin) +{ + return _seen_bssid_entry_init_stale(g_slice_new(SeenBssidEntry), bssid_bin); +} + +static inline SeenBssidEntry * +_seen_bssid_entry_new_stale_copy(const SeenBssidEntry *src) +{ + SeenBssidEntry *entry; + + entry = g_slice_new(SeenBssidEntry); + memcpy(entry->bssid, src->bssid, sizeof(entry->bssid)); + return entry; +} + +static void +_seen_bssid_entry_free(gpointer data) +{ + SeenBssidEntry *entry = data; + + c_list_unlink_stale(&entry->seen_bssids_lst); + nm_g_slice_free(entry); +} + +/*****************************************************************************/ + +static GHashTable * +_seen_bssids_hash_new(void) +{ + return g_hash_table_new_full(nm_str_hash, + g_str_equal, + (GDestroyNotify) _seen_bssid_entry_free, + NULL); +} + +/*****************************************************************************/ + NM_GOBJECT_PROPERTIES_DEFINE(NMSettingsConnection, PROP_UNSAVED, PROP_FLAGS, PROP_FILENAME, ); enum { UPDATED_INTERNAL, FLAGS_CHANGED, LAST_SIGNAL }; @@ -99,7 +152,8 @@ typedef struct _NMSettingsConnectionPrivate { */ GVariant *agent_secrets; - GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */ + CList seen_bssids_lst_head; + GHashTable *seen_bssids_hash; guint64 timestamp; /* Up-to-date timestamp of connection use */ @@ -167,7 +221,9 @@ static const GDBusSignalInfo signal_info_updated; static const GDBusSignalInfo signal_info_removed; static const NMDBusInterfaceInfoExtended interface_info_settings_connection; -static void update_agent_secrets_cache(NMSettingsConnection *self, NMConnection *new); +static void update_agent_secrets_cache(NMSettingsConnection *self, NMConnection *new); +static guint _get_seen_bssids(NMSettingsConnection *self, + const char * strv_buf[static(SEEN_BSSIDS_MAX + 1)]); /*****************************************************************************/ @@ -246,14 +302,6 @@ nm_settings_connection_still_valid(NMSettingsConnection *self) /*****************************************************************************/ -static GHashTable * -_seen_bssids_hash_new(void) -{ - return g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, NULL); -} - -/*****************************************************************************/ - static void _getsettings_cached_clear(NMSettingsConnectionPrivate *priv) { @@ -1300,8 +1348,8 @@ get_settings_auth_cb(NMSettingsConnection * self, GError * error, gpointer data) { - gs_free const char ** seen_bssids = NULL; - NMConnectionSerializationOptions options = {}; + const char * seen_bssids_strv[SEEN_BSSIDS_MAX + 1]; + NMConnectionSerializationOptions options = {}; if (error) { g_dbus_method_invocation_return_gerror(context, error); @@ -1321,8 +1369,8 @@ get_settings_auth_cb(NMSettingsConnection * self, * from the same reason as timestamp. Thus we put it here to GetSettings() * return settings too. */ - seen_bssids = nm_settings_connection_get_seen_bssids(self); - options.seen_bssids = seen_bssids; + _get_seen_bssids(self, seen_bssids_strv); + options.seen_bssids = seen_bssids_strv; /* Secrets should *never* be returned by the GetSettings method, they * get returned by the GetSecrets method which can be better @@ -2304,68 +2352,66 @@ _nm_settings_connection_register_kf_dbs(NMSettingsConnection *self, if (priv->kf_db_seen_bssids != kf_db_seen_bssids) { gs_strfreev char **tmp_strv = NULL; - gsize i, len; + gsize len; + gsize i; + guint result_len; nm_key_file_db_unref(priv->kf_db_seen_bssids); priv->kf_db_seen_bssids = nm_key_file_db_ref(kf_db_seen_bssids); tmp_strv = nm_key_file_db_get_string_list(priv->kf_db_seen_bssids, connection_uuid, &len); - nm_clear_pointer(&priv->seen_bssids, g_hash_table_unref); + if (priv->seen_bssids_hash) + g_hash_table_remove_all(priv->seen_bssids_hash); - if (len > 0) { - _LOGT("read %zu seen-bssids from keyfile database \"%s\"", - len, - nm_key_file_db_get_filename(priv->kf_db_seen_bssids)); - priv->seen_bssids = _seen_bssids_hash_new(); - for (i = len; i > 0;) - g_hash_table_add(priv->seen_bssids, g_steal_pointer(&tmp_strv[--i])); - nm_clear_g_free(&tmp_strv); - } else { - NMSettingWireless *s_wifi; + for (result_len = 0, i = 0; i < len; i++) { + NMEtherAddr addr_bin; + SeenBssidEntry *entry; - _LOGT("no seen-bssids from keyfile database \"%s\"", - nm_key_file_db_get_filename(priv->kf_db_seen_bssids)); + nm_assert(result_len == nm_g_hash_table_size(priv->seen_bssids_hash)); + if (result_len >= SEEN_BSSIDS_MAX) + break; - /* If this connection didn't have an entry in the seen-bssids database, - * maybe this is the first time we've read it in, so populate the - * seen-bssids list from the deprecated seen-bssids property of the - * wifi setting. - */ - s_wifi = - nm_connection_get_setting_wireless(nm_settings_connection_get_connection(self)); - if (s_wifi) { - len = nm_setting_wireless_get_num_seen_bssids(s_wifi); - if (len > 0) { - priv->seen_bssids = _seen_bssids_hash_new(); - for (i = 0; i < len; i++) { - const char *bssid = nm_setting_wireless_get_seen_bssid(s_wifi, i); - - g_hash_table_add(priv->seen_bssids, g_strdup(bssid)); - } - } + if (!_nm_utils_hwaddr_aton_exact(tmp_strv[i], &addr_bin, sizeof(addr_bin))) + continue; + + if (!priv->seen_bssids_hash) + priv->seen_bssids_hash = _seen_bssids_hash_new(); + + entry = _seen_bssid_entry_new_stale_bin(&addr_bin); + if (!g_hash_table_insert(priv->seen_bssids_hash, entry, entry)) { + /* duplicate detected! The @entry key was freed by g_hash_table_insert(). */ + continue; } + c_list_link_tail(&priv->seen_bssids_lst_head, &entry->seen_bssids_lst); + result_len++; } + if (result_len > 0) { + _LOGT("read %u seen-bssids from keyfile database \"%s\"", + result_len, + nm_key_file_db_get_filename(priv->kf_db_seen_bssids)); + } else + nm_clear_pointer(&priv->seen_bssids_hash, g_hash_table_destroy); + + nm_assert(nm_g_hash_table_size(priv->seen_bssids_hash) == result_len); + nm_assert(result_len <= SEEN_BSSIDS_MAX); } } -/** - * nm_settings_connection_get_seen_bssids: - * @self: the #NMSettingsConnection - * - * Returns current list of seen BSSIDs for the connection. - * - * Returns: (transfer container) list of seen BSSIDs (in the standard hex-digits-and-colons notation). - * The caller is responsible for freeing the list, but not the content. - **/ -const char ** -nm_settings_connection_get_seen_bssids(NMSettingsConnection *self) +static guint +_get_seen_bssids(NMSettingsConnection *self, const char *strv_buf[static(SEEN_BSSIDS_MAX + 1)]) { - g_return_val_if_fail(NM_IS_SETTINGS_CONNECTION(self), NULL); + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self); + SeenBssidEntry * entry; + guint i; - return nm_utils_strdict_get_keys(NM_SETTINGS_CONNECTION_GET_PRIVATE(self)->seen_bssids, - TRUE, - NULL); + i = 0; + c_list_for_each_entry (entry, &priv->seen_bssids_lst_head, seen_bssids_lst) { + nm_assert(i <= SEEN_BSSIDS_MAX); + strv_buf[i++] = entry->bssid; + } + strv_buf[i] = NULL; + return i; } /** @@ -2379,14 +2425,17 @@ gboolean nm_settings_connection_has_seen_bssid(NMSettingsConnection *self, const char *bssid) { NMSettingsConnectionPrivate *priv; + NMEtherAddr addr_bin; g_return_val_if_fail(NM_IS_SETTINGS_CONNECTION(self), FALSE); g_return_val_if_fail(bssid, FALSE); + nm_assert(_nm_utils_hwaddr_aton_exact(bssid, &addr_bin, sizeof(addr_bin))); priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self); - return priv->seen_bssids - && g_hash_table_contains(NM_SETTINGS_CONNECTION_GET_PRIVATE(self)->seen_bssids, bssid); + return priv->seen_bssids_hash + && g_hash_table_contains(NM_SETTINGS_CONNECTION_GET_PRIVATE(self)->seen_bssids_hash, + bssid); } /** @@ -2401,15 +2450,47 @@ void nm_settings_connection_add_seen_bssid(NMSettingsConnection *self, const char *seen_bssid) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self); - gs_free const char ** strv = NULL; + const char * seen_bssids_strv[SEEN_BSSIDS_MAX + 1]; + NMEtherAddr addr_bin; const char * connection_uuid; + SeenBssidEntry entry_stack; + SeenBssidEntry * entry; + guint i; + + g_return_if_fail(seen_bssid); - g_return_if_fail(seen_bssid != NULL); + if (!_nm_utils_hwaddr_aton_exact(seen_bssid, &addr_bin, sizeof(addr_bin))) + g_return_if_reached(); - if (!priv->seen_bssids) - priv->seen_bssids = _seen_bssids_hash_new(); + _seen_bssid_entry_init_stale(&entry_stack, &addr_bin); - g_hash_table_add(priv->seen_bssids, g_strdup(seen_bssid)); + if (!priv->seen_bssids_hash) { + priv->seen_bssids_hash = _seen_bssids_hash_new(); + entry = NULL; + } else + entry = g_hash_table_lookup(priv->seen_bssids_hash, &entry_stack); + + if (entry) { + if (!nm_c_list_move_front(&priv->seen_bssids_lst_head, &entry->seen_bssids_lst)) { + /* no change. */ + return; + } + } else { + entry = _seen_bssid_entry_new_stale_copy(&entry_stack); + c_list_link_front(&priv->seen_bssids_lst_head, &entry->seen_bssids_lst); + if (!g_hash_table_add(priv->seen_bssids_hash, entry)) + nm_assert_not_reached(); + + if (g_hash_table_size(priv->seen_bssids_hash) > SEEN_BSSIDS_MAX) { + g_hash_table_remove( + priv->seen_bssids_hash, + c_list_last_entry(&priv->seen_bssids_lst_head, SeenBssidEntry, seen_bssids_lst)); + } + } + + nm_assert(g_hash_table_size(priv->seen_bssids_hash) <= SEEN_BSSIDS_MAX); + nm_assert(g_hash_table_size(priv->seen_bssids_hash) + == c_list_length(&priv->seen_bssids_lst_head)); if (!priv->kf_db_seen_bssids) return; @@ -2418,12 +2499,8 @@ nm_settings_connection_add_seen_bssid(NMSettingsConnection *self, const char *se if (!connection_uuid) return; - strv = nm_utils_strdict_get_keys(priv->seen_bssids, TRUE, NULL); - - nm_key_file_db_set_string_list(priv->kf_db_seen_bssids, - connection_uuid, - strv ?: NM_PTRARRAY_EMPTY(const char *), - -1); + i = _get_seen_bssids(self, seen_bssids_strv); + nm_key_file_db_set_string_list(priv->kf_db_seen_bssids, connection_uuid, seen_bssids_strv, i); } /*****************************************************************************/ @@ -2634,7 +2711,7 @@ nm_settings_connection_init(NMSettingsConnection *self) self->_priv = priv; c_list_init(&self->_connections_lst); - + c_list_init(&priv->seen_bssids_lst_head); c_list_init(&priv->call_ids_lst_head); c_list_init(&priv->auth_lst_head); @@ -2672,7 +2749,7 @@ dispose(GObject *object) nm_clear_pointer(&priv->agent_secrets, g_variant_unref); - nm_clear_pointer(&priv->seen_bssids, g_hash_table_destroy); + nm_clear_pointer(&priv->seen_bssids_hash, g_hash_table_destroy); g_clear_object(&priv->agent_mgr); diff --git a/src/core/settings/nm-settings-connection.h b/src/core/settings/nm-settings-connection.h index 83a6a7f6..fa3dbcfb 100644 --- a/src/core/settings/nm-settings-connection.h +++ b/src/core/settings/nm-settings-connection.h @@ -340,8 +340,6 @@ gboolean nm_settings_connection_get_timestamp(NMSettingsConnection *self, guint6 void nm_settings_connection_update_timestamp(NMSettingsConnection *self, guint64 timestamp); -const char **nm_settings_connection_get_seen_bssids(NMSettingsConnection *self); - gboolean nm_settings_connection_has_seen_bssid(NMSettingsConnection *self, const char *bssid); void nm_settings_connection_add_seen_bssid(NMSettingsConnection *self, const char *seen_bssid); diff --git a/src/core/settings/nm-settings.c b/src/core/settings/nm-settings.c index c876ea14..f9f98de7 100644 --- a/src/core/settings/nm-settings.c +++ b/src/core/settings/nm-settings.c @@ -388,6 +388,9 @@ typedef struct { guint kf_db_flush_idle_id_timestamps; guint kf_db_flush_idle_id_seen_bssids; + bool kf_db_pruned_timestamps; + bool kf_db_pruned_seen_bssid; + bool started : 1; /* Whether NMSettingsConnections changed in a way that affects the comparison @@ -495,6 +498,8 @@ _startup_complete_check_is_ready(NMSettings * self, conn = nm_settings_connection_get_connection(sett_conn); nm_manager_for_each_device (priv->manager, device, tmp_lst) { + gs_free_error GError *error = NULL; + if (!nm_device_is_real(device)) continue; @@ -505,7 +510,13 @@ _startup_complete_check_is_ready(NMSettings * self, continue; } - if (!nm_device_check_connection_compatible(device, conn, NULL)) + /* Check that device is compatible with the device. We are also happy + * with a device compatible but for which the connection is disallowed + * by NM configuration. */ + if (!nm_device_check_connection_compatible(device, conn, &error) + && !g_error_matches(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_DISALLOWED)) continue; return TRUE; @@ -3684,6 +3695,41 @@ again: /*****************************************************************************/ +static gboolean +_kf_db_prune_predicate(const char *uuid, gpointer user_data) +{ + return !!nm_settings_get_connection_by_uuid(user_data, uuid); +} + +static void +_kf_db_to_file(NMSettings *self, gboolean is_timestamps, gboolean force_write) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self); + NMKeyFileDB * kf_db; + bool * p_kf_db_pruned; + + if (is_timestamps) { + kf_db = priv->kf_db_timestamps; + p_kf_db_pruned = &priv->kf_db_pruned_timestamps; + } else { + kf_db = priv->kf_db_seen_bssids; + p_kf_db_pruned = &priv->kf_db_pruned_seen_bssid; + } + + if (!*p_kf_db_pruned) { + /* we only prune the DB once, because afterwards every + * add/remove of an connection will lead to a direct update. */ + *p_kf_db_pruned = TRUE; + nm_key_file_db_prune(kf_db, _kf_db_prune_predicate, self); + + /* once we also go over the directory, and see whether we + * have any left over temporary files to delete. */ + nm_key_file_db_prune_tmp_files(kf_db); + } + + nm_key_file_db_to_file(kf_db, force_write); +} + G_GNUC_PRINTF(4, 5) static void _kf_db_log_fcn(NMKeyFileDB *kf_db, int syslog_level, gpointer user_data, const char *fmt, ...) @@ -3732,7 +3778,7 @@ _kf_db_got_dirty_flush(NMSettings *self, gboolean is_timestamps) } if (nm_key_file_db_is_dirty(kf_db)) - nm_key_file_db_to_file(kf_db, FALSE); + _kf_db_to_file(self, is_timestamps, FALSE); else { _LOGT("[%s-keyfile]: skip saving changes to \"%s\"", prefix, @@ -3785,15 +3831,10 @@ _kf_db_got_dirty_fcn(NMKeyFileDB *kf_db, gpointer user_data) void nm_settings_kf_db_write(NMSettings *self) { - NMSettingsPrivate *priv; - g_return_if_fail(NM_IS_SETTINGS(self)); - priv = NM_SETTINGS_GET_PRIVATE(self); - if (priv->kf_db_timestamps) - nm_key_file_db_to_file(priv->kf_db_timestamps, TRUE); - if (priv->kf_db_seen_bssids) - nm_key_file_db_to_file(priv->kf_db_seen_bssids, TRUE); + _kf_db_to_file(self, TRUE, TRUE); + _kf_db_to_file(self, FALSE, TRUE); } /*****************************************************************************/ @@ -4031,8 +4072,8 @@ finalize(GObject *object) nm_clear_g_source(&priv->kf_db_flush_idle_id_timestamps); nm_clear_g_source(&priv->kf_db_flush_idle_id_seen_bssids); - nm_key_file_db_to_file(priv->kf_db_timestamps, FALSE); - nm_key_file_db_to_file(priv->kf_db_seen_bssids, FALSE); + _kf_db_to_file(self, TRUE, FALSE); + _kf_db_to_file(self, FALSE, FALSE); nm_key_file_db_destroy(priv->kf_db_timestamps); nm_key_file_db_destroy(priv->kf_db_seen_bssids); diff --git a/src/core/settings/plugins/ifcfg-rh/nm-ifdown b/src/core/settings/plugins/ifcfg-rh/nm-ifdown old mode 100644 new mode 100755 index e7bd73ae..3b05ef1c --- a/src/core/settings/plugins/ifcfg-rh/nm-ifdown +++ b/src/core/settings/plugins/ifcfg-rh/nm-ifdown @@ -1,3 +1,44 @@ -#!/bin/sh -nmcli connection load "/etc/sysconfig/network-scripts/ifcfg-$1" && -exec nmcli connection down filename "/etc/sysconfig/network-scripts/ifcfg-$1" +#!/bin/bash + +CONFIG="$1" + +if [ -z "${CONFIG}" ] ; then + echo $"Usage: ifdown " >&2 + exit 1 +fi + +usage_and_fail() { + cat <&2 +$1! + +See all profiles with \`nmcli connection\`. +Reload files from disk with \`nmcli connection reload\` +Deactivate the desired profile with \`nmcli connection down \"\$NAME\"\` +EOF + exit 1 +} + +OLD_IFS="$IFS" + +re='^/etc/sysconfig/network-scripts/ifcfg-[^/]+$' +for f in "/etc/sysconfig/network-scripts/ifcfg-$CONFIG" "/etc/sysconfig/network-scripts/$CONFIG" ; do + [[ "$f" =~ $re ]] || continue + [ -f "$f" ] || continue + + nmcli connection load "$f" \ + && nmcli connection down filename "$f" \ + && exit 0 + + usage_and_fail "Failure to deactivate file \"$CONFIG\"" +done + +IFS=$'\n ' +APATHS=( $(nmcli -g GENERAL.DBUS-PATH connection show --active "$CONFIG" 2>/dev/null) ) +IFS="$OLD_IFS" +if [ "${#APATHS[@]}" -eq 1 ] ; then + nmcli connection down apath "${APATHS[0]}" \ + && exit 0 + usage_and_fail "Failure to deactivate profile \"$CONFIG\" (apath ${APATHS[@]})" +fi + +usage_and_fail "No suitable profile \"$CONFIG\" found" diff --git a/src/core/settings/plugins/ifcfg-rh/nm-ifup b/src/core/settings/plugins/ifcfg-rh/nm-ifup old mode 100644 new mode 100755 index 96637a92..6b32fe1b --- a/src/core/settings/plugins/ifcfg-rh/nm-ifup +++ b/src/core/settings/plugins/ifcfg-rh/nm-ifup @@ -1,3 +1,44 @@ -#!/bin/sh -nmcli connection load "/etc/sysconfig/network-scripts/ifcfg-$1" && -exec nmcli connection up filename "/etc/sysconfig/network-scripts/ifcfg-$1" +#!/bin/bash + +CONFIG="$1" + +if [ -z "${CONFIG}" ] ; then + echo $"Usage: ifup " >&2 + exit 1 +fi + +usage_and_fail() { + cat <&2 +$1! + +See all profiles with \`nmcli connection\`. +Reload files from disk with \`nmcli connection reload\` +Activate the desired profile with \`nmcli connection up \"\$NAME\"\` +EOF + exit 1 +} + +OLD_IFS="$IFS" + +re='^/etc/sysconfig/network-scripts/ifcfg-[^/]+$' +for f in "/etc/sysconfig/network-scripts/ifcfg-$CONFIG" "/etc/sysconfig/network-scripts/$CONFIG" ; do + [[ "$f" =~ $re ]] || continue + [ -f "$f" ] || continue + + nmcli connection load "$f" \ + && nmcli connection up filename "$f" \ + && exit 0 + + usage_and_fail "Failure to activate file \"$CONFIG\"" +done + +IFS=$'\n ' +UUIDS=( $(nmcli -g connection.uuid connection show "$CONFIG" 2>/dev/null) ) +IFS="$OLD_IFS" +if [ "${#UUIDS[@]}" -eq 1 ] ; then + nmcli connection up uuid "${UUIDS[0]}" \ + && exit 0 + usage_and_fail "Failure to activate profile \"$CONFIG\" (uuid ${UUIDS[0]})" +fi + +usage_and_fail "No suitable profile \"$CONFIG\" found" diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 24d67003..03b347f9 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -1895,6 +1895,8 @@ make_ip4_setting(shvarFile *ifcfg, svGetValueBoolean(ifcfg, "DHCP_SEND_HOSTNAME", TRUE), NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, (int) svGetValueInt64(ifcfg, "IPV4_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0), + NM_SETTING_IP_CONFIG_REQUIRED_TIMEOUT, + (int) svGetValueInt64(ifcfg, "IPV4_REQUIRED_TIMEOUT", 10, 0, G_MAXINT32, -1), NULL); nm_clear_g_free(&value); @@ -1985,7 +1987,11 @@ make_ip4_setting(shvarFile *ifcfg, } else if (nm_utils_ipaddr_is_valid(AF_INET6, v)) { /* Ignore IPv6 addresses */ } else { - PARSE_WARNING("invalid DNS server address %s", v); + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid DNS server address '%s'", + v); return NULL; } } @@ -2152,6 +2158,8 @@ read_aliases(NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *filen continue; } + svWarnInvalid(parsed, "alias", _NMLOG_DOMAIN); + device = svGetValueStr(parsed, "DEVICE", &device_value); if (!device) { PARSE_WARNING("alias file '%s' has no DEVICE", full_path); @@ -2403,6 +2411,8 @@ make_ip6_setting(shvarFile *ifcfg, shvarFile *network_ifcfg, gboolean routes_rea svGetValueBoolean(ifcfg, "DHCPV6_SEND_HOSTNAME", TRUE), NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, (int) svGetValueInt64(ifcfg, "IPV6_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0), + NM_SETTING_IP_CONFIG_REQUIRED_TIMEOUT, + (int) svGetValueInt64(ifcfg, "IPV6_REQUIRED_TIMEOUT", 10, 0, G_MAXINT32, -1), NM_SETTING_IP6_CONFIG_RA_TIMEOUT, (int) svGetValueInt64(ifcfg, "IPV6_RA_TIMEOUT", 10, 0, G_MAXINT32, 0), NULL); @@ -2504,7 +2514,11 @@ make_ip6_setting(shvarFile *ifcfg, shvarFile *network_ifcfg, gboolean routes_rea } else if (nm_utils_ipaddr_is_valid(AF_INET, v)) { /* Ignore IPv4 addresses */ } else { - PARSE_WARNING("invalid DNS server address %s", v); + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid DNS server address '%s'", + v); return NULL; } } @@ -6298,6 +6312,7 @@ connection_from_file_full(const char *filename, NMSetting * s_ip4; NMSetting * s_ip6; const char * ifcfg_name = NULL; + gs_free char * s_tmp = NULL; gboolean has_ip4_defroute = FALSE; gboolean has_complex_routes_v4; gboolean has_complex_routes_v6; @@ -6325,8 +6340,6 @@ connection_from_file_full(const char *filename, if (!main_ifcfg) return NULL; - network_ifcfg = svOpenFile(network_file, NULL); - if (!svGetValueBoolean(main_ifcfg, "NM_CONTROLLED", TRUE)) { connection = create_unhandled_connection(filename, main_ifcfg, "unmanaged", out_unhandled); if (!connection) { @@ -6340,6 +6353,16 @@ connection_from_file_full(const char *filename, return g_steal_pointer(&connection); } + if (NM_IN_STRSET(svGetValueStr(main_ifcfg, "DEVICE", &s_tmp), "lo")) { + /* "lo" is not handled by NetworkManager and we ignore it. */ + } else + svWarnInvalid(main_ifcfg, "ifcfg", _NMLOG_DOMAIN); + nm_clear_g_free(&s_tmp); + + network_ifcfg = svOpenFile(network_file, NULL); + /* we don't call svWarnInvalid(network_ifcfg), because we will load this file for + * every profile. So we would get a large number of duplicate warnings. */ + /* iBFT is handled by nm-initrd-generator during boot. */ bootproto = svGetValueStr_cp(main_ifcfg, "BOOTPROTO"); if (bootproto && !g_ascii_strcasecmp(bootproto, "ibft")) { diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c index 6f6035e1..febfc120 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c @@ -958,6 +958,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = { _KEY_TYPE("IPV4_DHCP_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV4_DNS_PRIORITY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV4_FAILURE_FATAL", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV4_REQUIRED_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV4_ROUTE_METRIC", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV4_ROUTE_TABLE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV6ADDR", NMS_IFCFG_KEY_TYPE_IS_PLAIN), @@ -980,6 +981,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = { _KEY_TYPE("IPV6_PRIVACY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV6_PRIVACY_PREFER_PUBLIC_IP", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV6_RA_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_REQUIRED_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV6_RES_OPTIONS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV6_ROUTE_METRIC", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV6_ROUTE_TABLE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h index b61ce80a..b7751ec9 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h @@ -33,7 +33,7 @@ typedef struct { NMSIfcfgKeyTypeFlags key_flags; } NMSIfcfgKeyTypeInfo; -extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[249]; +extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[251]; const NMSIfcfgKeyTypeInfo *nms_ifcfg_well_known_key_find_info(const char *key, gssize *out_idx); diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index 1c5bbbb4..ef85b6bf 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -2840,6 +2840,9 @@ write_ip4_setting(NMConnection *connection, timeout = nm_setting_ip_config_get_dhcp_timeout(s_ip4); svSetValueInt64_cond(ifcfg, "IPV4_DHCP_TIMEOUT", timeout != 0, timeout); + timeout = nm_setting_ip_config_get_required_timeout(s_ip4); + svSetValueInt64_cond(ifcfg, "IPV4_REQUIRED_TIMEOUT", timeout != -1, timeout); + svSetValueBoolean(ifcfg, "IPV4_FAILURE_FATAL", !nm_setting_ip_config_get_may_fail(s_ip4)); route_metric = nm_setting_ip_config_get_route_metric(s_ip4); @@ -3037,6 +3040,9 @@ write_ip6_setting(NMConnection *connection, timeout = nm_setting_ip_config_get_dhcp_timeout(s_ip6); svSetValueInt64_cond(ifcfg, "IPV6_DHCP_TIMEOUT", timeout != 0, timeout); + timeout = nm_setting_ip_config_get_required_timeout(s_ip6); + svSetValueInt64_cond(ifcfg, "IPV6_REQUIRED_TIMEOUT", timeout != -1, timeout); + flags = nm_setting_ip_config_get_dhcp_hostname_flags(s_ip6); svSetValueInt64_cond(ifcfg, "DHCPV6_HOSTNAME_FLAGS", diff --git a/src/core/settings/plugins/ifcfg-rh/shvar.c b/src/core/settings/plugins/ifcfg-rh/shvar.c index 80644b64..d82efb3c 100644 --- a/src/core/settings/plugins/ifcfg-rh/shvar.c +++ b/src/core/settings/plugins/ifcfg-rh/shvar.c @@ -172,7 +172,7 @@ _escape_ansic(const char *source) n_alloc += 2; break; default: - if ((*p < ' ') || (*p >= 0177)) + if (!nm_ascii_is_regular(*p)) n_alloc += 4; else n_alloc += 1; @@ -221,7 +221,7 @@ _escape_ansic(const char *source) *q++ = *p; break; default: - if ((*p < ' ') || (*p >= 0177)) { + if (!nm_ascii_is_regular(*p)) { *q++ = '\\'; *q++ = '0' + (((*p) >> 6) & 07); *q++ = '0' + (((*p) >> 3) & 07); @@ -255,20 +255,37 @@ svEscape(const char *s, char **to_free) gsize slen; gsize i; gsize j; + gboolean all_ascii = TRUE; for (slen = 0; s[slen]; slen++) { if (_char_req_escape(s[slen])) mangle++; else if (_char_req_quotes(s[slen])) requires_quotes = TRUE; - else if (s[slen] < ' ') { - /* if the string contains newline we can only express it using ANSI C quotation - * (as we don't support line continuation). - * Additionally, ANSI control characters look odd with regular quotation, so handle - * them too. */ - return (*to_free = _escape_ansic(s)); + else if (!nm_ascii_is_regular(s[slen])) { + if (nm_ascii_is_ctrl_or_del(s[slen])) { + /* if the string contains newline we can only express it using ANSI C quotation + * (as we don't support line continuation). + * Additionally, ANSI control characters look odd with regular quotation, so handle + * them too. */ + return (*to_free = _escape_ansic(s)); + } + all_ascii = FALSE; + requires_quotes = TRUE; } } + + if (!all_ascii && !g_utf8_validate(s, -1, NULL)) { + /* The string is not valid ASCII/UTF-8. We can escape that via + * _escape_ansic(), however the reader might have a problem to + * do something sensible with the blob later. + * + * This is really a bug of the caller, which should not present us with + * non-text in the first place. But at this place, we cannot handle the + * error better, so just escape it. */ + return (*to_free = _escape_ansic(s)); + } + if (!mangle && !requires_quotes) { *to_free = NULL; return s; @@ -371,6 +388,12 @@ _strbuf_init(NMStrBuf *str, const char *value, gsize i) const char * svUnescape(const char *value, char **to_free) +{ + return svUnescape_full(value, to_free, TRUE); +} + +const char * +svUnescape_full(const char *value, char **to_free, gboolean check_utf8) { NMStrBuf str = NM_STR_BUF_INIT(0, FALSE); int looks_like_old_svescaped = -1; @@ -646,6 +669,8 @@ out_value: } if (str.allocated > 0) { + if (check_utf8 && !nm_str_buf_utf8_validate(&str)) + goto out_error; if (str.len == 0 || nm_str_buf_get_str_unsafe(&str)[0] == '\0') { nm_str_buf_destroy(&str); *to_free = NULL; @@ -656,6 +681,11 @@ out_value: } } + if (check_utf8 && !g_utf8_validate(value, i, NULL)) { + *to_free = NULL; + return NULL; + } + if (value[i] != '\0') { *to_free = g_strndup(value, i); return *to_free; @@ -1120,9 +1150,8 @@ _svGetValue(shvarFile *s, const char *key, char **to_free) if (line && line->line) { v = svUnescape(line->line, to_free); if (!v) { - /* a wrongly quoted value is treated like the empty string. - * See also svWriteFile(), which handles unparsable values - * that way. */ + /* a wrongly quoted value or non-UTF-8 is treated like the empty string. + * See also svWriteFile(), which handles unparsable values that way. */ nm_assert(!*to_free); return ""; } @@ -1495,6 +1524,91 @@ svUnsetValue(shvarFile *s, const char *key) /*****************************************************************************/ +void +svWarnInvalid(shvarFile *s, const char *file_type, NMLogDomain log_domain) +{ + shvarLine *line; + gsize n; + + if (!nm_logging_enabled(LOGL_WARN, log_domain)) + return; + + n = 0; + c_list_for_each_entry (line, &s->lst_head, lst) { + gs_free char *s_tmp = NULL; + + n++; + + if (!line->key) { + const char *str; + + nm_assert(line->line); + str = nm_str_skip_leading_spaces(line->line); + if (!NM_IN_SET(str[0], '\0', '#')) { + nm_log_warn(log_domain, + "ifcfg-rh: %s,%s:%zu: invalid line ignored", + file_type, + s->fileName, + n); + } + continue; + } + + if (g_hash_table_lookup(s->lst_idx, line) != line) { + nm_log_warn( + log_domain, + "ifcfg-rh: %s,%s:%zu: key %s is duplicated and the early occurrence ignored", + file_type, + s->fileName, + n, + line->key); + continue; + } + + if (!line->line) { + /* the line is deleted via svUnsetValue(). Ignore. */ + continue; + } + + if (!svUnescape(line->line, &s_tmp)) { + if (!svUnescape_full(line->line, &s_tmp, FALSE)) { + nm_log_warn(log_domain, + "ifcfg-rh: %s,%s:%zu: key %s is badly quoted and is treated as \"\"", + file_type, + s->fileName, + n, + line->key); + } else { + nm_log_warn(log_domain, + "ifcfg-rh: %s,%s:%zu: key %s does not contain valid UTF-8 and is " + "treated as \"\"", + file_type, + s->fileName, + n, + line->key); + } + continue; + } + + /* TODO: we read different shell scripts, and whether a key is recognized + * depends on the type. For example, alias files only accept a subset of + * known keys. + * + * Basically, depending on the @file_type, different keys are valid. */ + if (!nms_ifcfg_rh_utils_is_well_known_key(line->key)) { + nm_log_dbg(log_domain, + "ifcfg-rh: %s,%s:%zu: key %s is unknown and ignored", + file_type, + s->fileName, + n, + line->key); + continue; + } + } +} + +/*****************************************************************************/ + /* Write the current contents iff modified. Returns FALSE on error * and TRUE on success. Do not write if no values have been modified. * The mode argument is only used if creating the file, not if diff --git a/src/core/settings/plugins/ifcfg-rh/shvar.h b/src/core/settings/plugins/ifcfg-rh/shvar.h index 6965d873..cf91642f 100644 --- a/src/core/settings/plugins/ifcfg-rh/shvar.h +++ b/src/core/settings/plugins/ifcfg-rh/shvar.h @@ -107,6 +107,7 @@ void svCloseFile(shvarFile *s); const char *svEscape(const char *s, char **to_free); const char *svUnescape(const char *s, char **to_free); +const char *svUnescape_full(const char *value, char **to_free, gboolean check_utf8); static inline void _nm_auto_shvar_file_close(shvarFile **p_s) @@ -120,4 +121,6 @@ _nm_auto_shvar_file_close(shvarFile **p_s) } #define nm_auto_shvar_file_close nm_auto(_nm_auto_shvar_file_close) +void svWarnInvalid(shvarFile *s, const char *file_type, NMLogDomain log_domain); + #endif /* _SHVAR_H */ diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-suite-b-192-tls b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-suite-b-192-tls index 9a74bb4d..a21c301c 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-suite-b-192-tls +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-suite-b-192-tls @@ -3,7 +3,6 @@ DEVICE=eth2 HWADDR=00:16:41:11:22:33 BOOTPROTO=dhcp ONBOOT=yes -ONBOOT=yes USERCTL=yes IPV6INIT=no NM_CONTROLLED=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-ttls-tls b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-ttls-tls index 42ed1d68..bb63d5e8 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-ttls-tls +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-ttls-tls @@ -4,7 +4,6 @@ DEVICE=eth2 HWADDR=00:16:41:11:22:33 BOOTPROTO=dhcp ONBOOT=yes -ONBOOT=yes USERCTL=yes IPV6INIT=no NM_CONTROLLED=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected index 040ddc9d..92c03b12 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected @@ -13,8 +13,10 @@ #L2 METRIC1='' -METRIC2=$'\U0x' -METRIC3=$'x\U0' +METRIC2= +#NM: METRIC2=$'\U0x' +METRIC3= +#NM: METRIC3=$'x\U0' #L4 IPADDR=set-by-test1 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 6193b952..d9829f84 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -188,8 +188,8 @@ _assert_expected_content(NMConnection *connection, const char *filename, const c if (G_UNLIKELY(rewrite == 0)) { rewrite = (g_getenv("NMTST_IFCFG_RH_UPDATE_EXPECTED") || nm_streq0(g_getenv("NM_TEST_REGENERATE"), "1")) - ? -1 - : 1; + ? 1 + : -1; if (!g_atomic_int_compare_and_exchange(&rewrite_static, 0, rewrite)) g_assert_not_reached(); } @@ -738,10 +738,13 @@ test_read_variables_corner_cases(void) const char * mac; char expected_mac_address[ETH_ALEN] = {0x00, 0x16, 0x41, 0x11, 0x22, 0x33}; + NMTST_EXPECT_NM_WARN("*key NAME is badly quoted and is treated as \"\"*"); + NMTST_EXPECT_NM_WARN("*key ZONE is badly quoted and is treated as \"\"*"); connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-variables-corner-cases-1", NULL, TYPE_ETHERNET, NULL); + g_test_assert_expected_messages(); /* ===== CONNECTION SETTING ===== */ s_con = nm_connection_get_setting_connection(connection); @@ -830,10 +833,12 @@ test_read_unrecognized(void) gs_free char * unhandled_spec = NULL; guint64 expected_timestamp = 0; + NMTST_EXPECT_NM_WARN("*key NAME is badly quoted and is treated as \"\"*"); connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-unrecognized", NULL, NULL, &unhandled_spec); + g_test_assert_expected_messages(); g_assert_cmpstr(unhandled_spec, ==, "unrecognized:mac:00:11:22:33"); /* ===== CONNECTION SETTING ===== */ @@ -1004,10 +1009,12 @@ test_read_wired_dhcp(void) char expected_mac_address[ETH_ALEN] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xee}; const char * mac; + NMTST_EXPECT_NM_WARN("*key IPV6INIT is duplicated and the early occurrence ignored*"); connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-dhcp", NULL, TYPE_ETHERNET, &unmanaged); + g_test_assert_expected_messages(); g_assert(unmanaged == NULL); /* ===== CONNECTION SETTING ===== */ @@ -3583,10 +3590,12 @@ test_read_wifi_wpa_eap_tls(void) char * unmanaged = NULL; const char * expected_privkey_password = "test1"; + NMTST_EXPECT_NM_WARN("*key ONBOOT is duplicated and the early occurrence ignored*"); connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wpa-eap-tls", NULL, TYPE_ETHERNET, &unmanaged); + g_test_assert_expected_messages(); g_assert(!unmanaged); /* ===== WIRELESS SETTING ===== */ @@ -3791,10 +3800,12 @@ test_read_wifi_wep_eap_ttls_chap(void) NMSetting8021x * s_8021x; char * unmanaged = NULL; + NMTST_EXPECT_NM_WARN("*key ONBOOT is duplicated and the early occurrence ignored*"); connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wep-eap-ttls-chap", NULL, TYPE_WIRELESS, &unmanaged); + g_test_assert_expected_messages(); g_assert(!unmanaged); /* ===== WIRELESS SETTING ===== */ @@ -10476,7 +10487,7 @@ _svUnescape(const char *str, char **to_free) str = (str_free = g_strdup(str)); } - s = svUnescape(str, to_free); + s = svUnescape_full(str, to_free, FALSE); if (*to_free) { g_assert(s == *to_free); g_assert(s[0]); @@ -10484,6 +10495,37 @@ _svUnescape(const char *str, char **to_free) g_assert(s == NULL || (!s[0] && (s < str || s > strchr(str, '\0'))) || (s[0] && s >= str && s <= strchr(str, '\0'))); } + + { + const char * s2; + gs_free char *to_free2 = NULL; + + gboolean is_utf8 = s && g_utf8_validate(s, -1, NULL); + + s2 = svUnescape_full(str, &to_free2, TRUE); + if (NM_IN_STRSET(str, "$'\\U0x'", "$'\\x0'", "$'\\008'", "$'\\08'")) { + g_assert_cmpstr(s2, ==, NULL); + g_assert(!to_free2); + g_assert_cmpstr(s, ==, ""); + g_assert(!*to_free); + } else if (NM_IN_STRSET(str, "$'x\\U0'")) { + g_assert_cmpstr(s2, ==, NULL); + g_assert(!to_free2); + g_assert_cmpstr(s, ==, "x"); + g_assert(*to_free == s); + } else if (!is_utf8) { + g_assert(!s2); + g_assert(!to_free2); + } else if (!to_free2) { + g_assert_cmpstr(s, ==, s2); + g_assert(s == s2); + } else { + g_assert_cmpstr(s, ==, s2); + g_assert(s != s2); + g_assert(s2 == to_free2); + } + } + return s; } @@ -10665,6 +10707,9 @@ test_svUnescape(void) V1("\"\\'\"''", "\\'"), V0("\"b\\~b\" ", "b\\~b"), V1("\"b\\~b\"x", "b\\~bx"), + + V0("$'x\\U0'", "x"), + V0("$'\\U0x'", ""), }; const UnescapeTestData data_ansi[] = { /* strings inside $''. They cannot be compared directly, but must @@ -10851,7 +10896,7 @@ test_write_unknown(gconstpointer test_data) _svGetValue_check(sv, "METRIC", NULL); _svGetValue_check(sv, "METRIC1", ""); _svGetValue_check(sv, "METRIC2", ""); - _svGetValue_check(sv, "METRIC3", "x"); + _svGetValue_check(sv, "METRIC3", ""); _svGetValue_check(sv, "IPADDR", "set-by-test1"); _svGetValue_check(sv, "IPADDR2", "set-by-test2"); diff --git a/src/core/tests/meson.build b/src/core/tests/meson.build index 316f20cc..99fa0ae9 100644 --- a/src/core/tests/meson.build +++ b/src/core/tests/meson.build @@ -44,7 +44,9 @@ exe = executable( link_with: [ libnm_systemd_shared, libnm_systemd_core, + libnm_glib_aux, libnm_log_null, + libnm_std_aux, libc_siphash, ], ) diff --git a/src/libnm-base/nm-config-base.h b/src/libnm-base/nm-config-base.h index 7a23875a..c413e867 100644 --- a/src/libnm-base/nm-config-base.h +++ b/src/libnm-base/nm-config-base.h @@ -62,6 +62,8 @@ #define NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED "managed" #define NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER "ignore-carrier" #define NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS "sriov-num-vfs" +#define NM_CONFIG_KEYFILE_KEY_DEVICE_KEEP_CONFIGURATION "keep-configuration" +#define NM_CONFIG_KEYFILE_KEY_DEVICE_ALLOWED_CONNECTIONS "allowed-connections" #define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND "wifi.backend" #define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS "wifi.scan-rand-mac-address" #define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_GENERATE_MAC_ADDRESS_MASK \ diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver index 65d52b1a..89a99af8 100644 --- a/src/libnm-client-impl/libnm.ver +++ b/src/libnm-client-impl/libnm.ver @@ -1793,3 +1793,8 @@ global: nm_setting_match_new; nm_setting_wired_get_accept_all_mac_addresses; } libnm_1_30_0; + +libnm_1_32_4 { +global: + nm_setting_ip_config_get_required_timeout; +} libnm_1_32_0; diff --git a/src/libnm-client-impl/nm-libnm-utils.c b/src/libnm-client-impl/nm-libnm-utils.c index 3cc88ef4..671466cf 100644 --- a/src/libnm-client-impl/nm-libnm-utils.c +++ b/src/libnm-client-impl/nm-libnm-utils.c @@ -160,14 +160,17 @@ _fixup_string(const char * desc, p = q + 1; } - /* replace '_', ',', ASCII control characters and parentheses, with space. */ + /* replace '_', ',', ASCII control characters and everything inside parentheses, with space. */ for (p = desc_full; p[0]; p++) { if (*p == '(') in_paren = TRUE; - if (NM_IN_SET(*p, '_', ',') || *p < ' ' || in_paren) - *p = ' '; - if (*p == ')') + else if (*p == ')') in_paren = FALSE; + else if (NM_IN_SET(*p, '_', ',') || nm_ascii_is_ctrl_or_del(*p) || in_paren) { + /* pass */ + } else + continue; + *p = ' '; } /* Attempt to shorten ID by ignoring certain phrases */ diff --git a/src/libnm-client-impl/nm-property-infos-nmcli.xml b/src/libnm-client-impl/nm-property-infos-nmcli.xml index 49f2e220..b771b74c 100644 --- a/src/libnm-client-impl/nm-property-infos-nmcli.xml +++ b/src/libnm-client-impl/nm-property-infos-nmcli.xml @@ -34,10 +34,11 @@ - + + diff --git a/src/libnm-client-impl/nm-settings-docs-gir.xml b/src/libnm-client-impl/nm-settings-docs-gir.xml index 5f8a1d9f..cbbae7b6 100644 --- a/src/libnm-client-impl/nm-settings-docs-gir.xml +++ b/src/libnm-client-impl/nm-settings-docs-gir.xml @@ -214,6 +214,7 @@ + @@ -241,6 +242,7 @@ + diff --git a/src/libnm-client-impl/tests/test-libnm.c b/src/libnm-client-impl/tests/test-libnm.c index 6cb8076e..e470c63e 100644 --- a/src/libnm-client-impl/tests/test-libnm.c +++ b/src/libnm-client-impl/tests/test-libnm.c @@ -219,7 +219,7 @@ test_fixup_vendor_string(void) T_DATA("Memorex", "Memorex"), T_DATA("Micrel-Kendin", "Micrel-Kendin"), T_DATA("Microchip Technology, Inc.", "Microchip"), - T_DATA("Microcomputer Systems (M) Son", "Microcomputer"), + T_DATA("Microcomputer Systems (M) Son", "Microcomputer Son"), T_DATA("Microsoft Corp.", "Microsoft"), T_DATA("Microsoft Corporation", "Microsoft"), T_DATA("Micro-Star International Co., Ltd. [MSI]", "MSI"), @@ -594,11 +594,11 @@ test_fixup_product_string(void) T_DATA("82599 Ethernet Controller Virtual Function", "82599 Virtual Function"), T_DATA("82599 Virtual Function", "82599 Virtual Function"), T_DATA("82801BA/BAM/CA/CAM Ethernet Controller", "82801BA/BAM/CA/CAM"), - T_DATA("82801CAM (ICH3) PRO/100 VE Ethernet Controller", "82801CAM"), - T_DATA("82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller", "82801CAM"), - T_DATA("82801CAM (ICH3) PRO/100 VM Ethernet Controller", "82801CAM"), - T_DATA("82801CAM (ICH3) PRO/100 VM (KM) Ethernet Controller", "82801CAM"), - T_DATA("82801CAM (ICH3) PRO/100 VM (LOM) Ethernet Controller", "82801CAM"), + T_DATA("82801CAM (ICH3) PRO/100 VE Ethernet Controller", "82801CAM PRO/100 VE"), + T_DATA("82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller", "82801CAM PRO/100 VE"), + T_DATA("82801CAM (ICH3) PRO/100 VM Ethernet Controller", "82801CAM PRO/100 VM"), + T_DATA("82801CAM (ICH3) PRO/100 VM (KM) Ethernet Controller", "82801CAM PRO/100 VM"), + T_DATA("82801CAM (ICH3) PRO/100 VM (LOM) Ethernet Controller", "82801CAM PRO/100 VM"), T_DATA("82801DB PRO/100 VE (CNR) Ethernet Controller", "82801DB PRO/100 VE"), T_DATA("82801DB PRO/100 VE (LOM) Ethernet Controller", "82801DB PRO/100 VE"), T_DATA("82801DB PRO/100 VE (MOB) Ethernet Controller", "82801DB PRO/100 VE"), @@ -1009,25 +1009,25 @@ test_fixup_product_string(void) T_DATA("Ethernet Adapter", NULL), T_DATA("Ethernet adapter [U2L 100P-Y1]", "U2L 100P-Y1"), T_DATA("Ethernet Adaptive Virtual Function", "Adaptive Virtual Function"), - T_DATA("Ethernet Connection (2) I218-LM", NULL), - T_DATA("Ethernet Connection (2) I218-V", NULL), - T_DATA("Ethernet Connection (2) I219-LM", NULL), - T_DATA("Ethernet Connection (2) I219-V", NULL), - T_DATA("Ethernet Connection (3) I218-LM", NULL), - T_DATA("Ethernet Connection (3) I218-V", NULL), - T_DATA("Ethernet Connection (3) I219-LM", NULL), - T_DATA("Ethernet Connection (4) I219-LM", NULL), - T_DATA("Ethernet Connection (4) I219-V", NULL), - T_DATA("Ethernet Connection (5) I219-LM", NULL), - T_DATA("Ethernet Connection (5) I219-V", NULL), - T_DATA("Ethernet Connection (6) I219-LM", NULL), - T_DATA("Ethernet Connection (6) I219-V", NULL), - T_DATA("Ethernet Connection (7) I219-LM", NULL), - T_DATA("Ethernet Connection (7) I219-V", NULL), - T_DATA("Ethernet Connection (8) I219-LM", NULL), - T_DATA("Ethernet Connection (8) I219-V", NULL), - T_DATA("Ethernet Connection (9) I219-LM", NULL), - T_DATA("Ethernet Connection (9) I219-V", NULL), + T_DATA("Ethernet Connection (2) I218-LM", "I218-LM"), + T_DATA("Ethernet Connection (2 I218-V", NULL), + T_DATA("Ethernet Connection (2 I219-LM", NULL), + T_DATA("Ethernet Connection (2 I219-V", NULL), + T_DATA("Ethernet Connection (3) I218-LM", "I218-LM"), + T_DATA("Ethernet Connection (3) I218-V", "I218-V"), + T_DATA("Ethernet Connection (3 I219-LM", NULL), + T_DATA("Ethernet Connection (4 I219-LM", NULL), + T_DATA("Ethernet Connection (4 I219-V", NULL), + T_DATA("Ethernet Connection (5 I219-LM", NULL), + T_DATA("Ethernet Connection (5 I219-V", NULL), + T_DATA("Ethernet Connection (6 I219-LM", NULL), + T_DATA("Ethernet Connection (6 I219-V", NULL), + T_DATA("Ethernet Connection (7 I219-LM", NULL), + T_DATA("Ethernet Connection (7 I219-V", NULL), + T_DATA("Ethernet Connection (8 I219-LM", NULL), + T_DATA("Ethernet Connection (8 I219-V", NULL), + T_DATA("Ethernet Connection (9 I219-LM", NULL), + T_DATA("Ethernet Connection (9 I219-V", NULL), T_DATA("Ethernet Connection I217-LM", "I217-LM"), T_DATA("Ethernet Connection I217-V", "I217-V"), T_DATA("Ethernet Connection I218-LM", "I218-LM"), @@ -2160,12 +2160,12 @@ test_fixup_product_string(void) "WLM-20U2/GN-1080"), T_DATA("WLP-UC-AG300 Wireless LAN Adapter", "WLP-UC-AG300"), T_DATA("WM168g 802.11bg Wireless Adapter [Intersil ISL3886]", "WM168g"), - T_DATA("WN111(v2) RangeMax Next Wireless [Atheros AR9170+AR9101]", "WN111"), + T_DATA("WN111(v2) RangeMax Next Wireless [Atheros AR9170+AR9101]", "WN111 RangeMax Next"), T_DATA("WNA1000M 802.11bgn [Realtek RTL8188CUS]", "WNA1000M"), T_DATA("WNA1000Mv2 802.11bgn [Realtek RTL8188CUS?]", "WNA1000Mv2"), T_DATA("WNA1000 Wireless-N 150 [Atheros AR9170+AR9101]", "WNA1000 150"), T_DATA("WNA1100 Wireless-N 150 [Atheros AR9271]", "WNA1100 150"), - T_DATA("WNA3100M(v1) Wireless-N 300 [Realtek RTL8192CU]", "WNA3100M"), + T_DATA("WNA3100M(v1) Wireless-N 300 [Realtek RTL8192CU]", "WNA3100M 300"), T_DATA("WNDA3100v1 802.11abgn [Atheros AR9170+AR9104]", "WNDA3100v1"), T_DATA("WNDA3200 802.11abgn Wireless Adapter [Atheros AR7010+AR9280]", "WNDA3200"), T_DATA("WNDA4100 802.11abgn 3x3:3 [Ralink RT3573]", "WNDA4100"), diff --git a/src/libnm-core-aux-intern/nm-libnm-core-utils.c b/src/libnm-core-aux-intern/nm-libnm-core-utils.c index 78daac44..e53f840a 100644 --- a/src/libnm-core-aux-intern/nm-libnm-core-utils.c +++ b/src/libnm-core-aux-intern/nm-libnm-core-utils.c @@ -25,6 +25,7 @@ _nm_setting_bond_remove_options_miimon(NMSettingBond *s_bond) nm_setting_bond_remove_option(s_bond, NM_SETTING_BOND_OPTION_MIIMON); nm_setting_bond_remove_option(s_bond, NM_SETTING_BOND_OPTION_UPDELAY); nm_setting_bond_remove_option(s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY); + nm_setting_bond_remove_option(s_bond, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY); } void diff --git a/src/libnm-core-impl/meson.build b/src/libnm-core-impl/meson.build index 2b769616..f175ea3b 100644 --- a/src/libnm-core-impl/meson.build +++ b/src/libnm-core-impl/meson.build @@ -2,23 +2,27 @@ libnm_core_impl_inc = include_directories('.') -libnm_crypto_nss = static_library( - 'nm-crypto-nss', - sources: 'nm-crypto-nss.c', - dependencies: [ - libnm_core_public_dep, - crypto_nss_dep, - ], -) +if crypto_nss_dep.found() + libnm_crypto_nss = static_library( + 'nm-crypto-nss', + sources: 'nm-crypto-nss.c', + dependencies: [ + libnm_core_public_dep, + crypto_nss_dep, + ], + ) +endif -libnm_crypto_gnutls = static_library( - 'nm-crypto-gnutls', - sources: 'nm-crypto-gnutls.c', - dependencies: [ - libnm_core_public_dep, - crypto_gnutls_dep, - ], -) +if crypto_gnutls_dep.found() + libnm_crypto_gnutls = static_library( + 'nm-crypto-gnutls', + sources: 'nm-crypto-gnutls.c', + dependencies: [ + libnm_core_public_dep, + crypto_gnutls_dep, + ], + ) +endif if crypto == 'nss' libnm_crypto = libnm_crypto_nss diff --git a/src/libnm-core-impl/nm-connection.c b/src/libnm-core-impl/nm-connection.c index ed864db8..4ac7b322 100644 --- a/src/libnm-core-impl/nm-connection.c +++ b/src/libnm-core-impl/nm-connection.c @@ -649,7 +649,7 @@ nm_connection_replace_settings_from_connection(NMConnection *connection, { NMConnectionPrivate *priv; NMConnectionPrivate *new_priv; - gboolean changed; + gboolean changed = FALSE; int i; g_return_if_fail(NM_IS_CONNECTION(connection)); diff --git a/src/libnm-core-impl/nm-keyfile-utils.c b/src/libnm-core-impl/nm-keyfile-utils.c index 88f9c4f9..f8c2f387 100644 --- a/src/libnm-core-impl/nm-keyfile-utils.c +++ b/src/libnm-core-impl/nm-keyfile-utils.c @@ -531,7 +531,7 @@ _keyfile_key_encode(const char *name, char **out_to_free) if (ch == '\0') return name; - if (ch < 0x20 || ch >= 127 || NM_IN_SET(ch, '=', '[', ']') + if (!nm_ascii_is_regular(ch) || NM_IN_SET(ch, '=', '[', ']') || (ch == '\\' && g_ascii_isxdigit(name[i + 1]) && g_ascii_isxdigit(name[i + 2])) || (ch == ' ' && name[i + 1] == '\0')) break; @@ -557,7 +557,7 @@ _keyfile_key_encode(const char *name, char **out_to_free) if (ch == '\0') break; - if (ch < 0x20 || ch >= 127 || NM_IN_SET(ch, '=', '[', ']') + if (!nm_ascii_is_regular(ch) || NM_IN_SET(ch, '=', '[', ']') || (ch == '\\' && g_ascii_isxdigit(name[i + 1]) && g_ascii_isxdigit(name[i + 2])) || (ch == ' ' && name[i + 1] == '\0')) { nm_str_buf_append_c(&str, '\\'); diff --git a/src/libnm-core-impl/nm-keyfile.c b/src/libnm-core-impl/nm-keyfile.c index 88aa0d46..c6c4a2eb 100644 --- a/src/libnm-core-impl/nm-keyfile.c +++ b/src/libnm-core-impl/nm-keyfile.c @@ -2781,6 +2781,9 @@ static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = { .parser = mac_address_parser_ETHER_cloned, ), PARSE_INFO_PROPERTY(NM_SETTING_WIRELESS_MAC_ADDRESS, .parser = mac_address_parser_ETHER, ), + PARSE_INFO_PROPERTY(NM_SETTING_WIRELESS_SEEN_BSSIDS, + .parser_skip = TRUE, + .writer_skip = TRUE, ), PARSE_INFO_PROPERTY(NM_SETTING_WIRELESS_SSID, .parser = ssid_parser, .writer = ssid_writer, ), ), ), diff --git a/src/libnm-core-impl/nm-setting-bond.c b/src/libnm-core-impl/nm-setting-bond.c index 93baa9d6..252adc73 100644 --- a/src/libnm-core-impl/nm-setting-bond.c +++ b/src/libnm-core-impl/nm-setting-bond.c @@ -90,6 +90,7 @@ static const char *const valid_options_lst[] = { NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB, NM_SETTING_BOND_OPTION_LP_INTERVAL, + NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY, NULL, }; @@ -207,6 +208,7 @@ static NM_UTILS_STRING_TABLE_LOOKUP_STRUCT_DEFINE( {NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, {"1", NM_BOND_OPTION_TYPE_INT, 0, 255}}, {NM_SETTING_BOND_OPTION_NUM_UNSOL_NA, {"1", NM_BOND_OPTION_TYPE_INT, 0, 255}}, {NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, {"1", NM_BOND_OPTION_TYPE_INT, 0, 65535}}, + {NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY, {"0", NM_BOND_OPTION_TYPE_INT, 0, G_MAXINT}}, {NM_SETTING_BOND_OPTION_PRIMARY, {"", NM_BOND_OPTION_TYPE_IFNAME}}, {NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, {"always", NM_BOND_OPTION_TYPE_BOTH, 0, 2, _option_default_strv_primary_reselect}}, @@ -782,6 +784,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) int arp_interval; int num_grat_arp; int num_unsol_na; + int peer_notif_delay; const char * mode_str; const char * arp_ip_target = NULL; const char * lacp_rate; @@ -810,6 +813,8 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) arp_interval = _atoi(_bond_get_option_or_default(self, NM_SETTING_BOND_OPTION_ARP_INTERVAL)); num_grat_arp = _atoi(_bond_get_option_or_default(self, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP)); num_unsol_na = _atoi(_bond_get_option_or_default(self, NM_SETTING_BOND_OPTION_NUM_UNSOL_NA)); + peer_notif_delay = + _atoi(_bond_get_option_or_default(self, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY)); /* Option restrictions: * @@ -818,6 +823,8 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) * arp_validate does not work with [ BOND_MODE_8023AD, BOND_MODE_TLB, BOND_MODE_ALB ] * downdelay needs miimon * updelay needs miimon + * peer_notif_delay needs miimon enabled + * peer_notif_delay must be a miimon multiple * primary needs [ active-backup, tlb, alb ] */ @@ -926,6 +933,36 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } } + if (peer_notif_delay) { + if (miimon == 0) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' option requires '%s' option to be enabled"), + NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY, + NM_SETTING_BOND_OPTION_MIIMON); + g_prefix_error(error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + + /* The code disables miimon when arp is set, so they never occur together. + * But this occurs after this verification, so this check can occur in + * an invalid state, when both arp and miimon are enabled. To assure not + * dealing with an invalid state, this arp_interval == 0 condition, + * that is implicit, was made explicit. + */ + if ((peer_notif_delay % miimon) && (arp_interval == 0)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' option needs to be a value multiple of '%s' value"), + NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY, + NM_SETTING_BOND_OPTION_MIIMON); + g_prefix_error(error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + } + /* arp_ip_target can only be used with arp_interval, and must * contain a comma-separated list of IPv4 addresses. */ diff --git a/src/libnm-core-impl/nm-setting-ip-config.c b/src/libnm-core-impl/nm-setting-ip-config.c index 59ab5ca2..f37e9d35 100644 --- a/src/libnm-core-impl/nm-setting-ip-config.c +++ b/src/libnm-core-impl/nm-setting-ip-config.c @@ -3905,6 +3905,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingIPConfig, PROP_MAY_FAIL, PROP_DAD_TIMEOUT, PROP_DHCP_TIMEOUT, + PROP_REQUIRED_TIMEOUT, PROP_DHCP_IAID, PROP_DHCP_REJECT_SERVERS, ); @@ -3925,6 +3926,7 @@ typedef struct { int dns_priority; int dad_timeout; int dhcp_timeout; + int required_timeout; guint32 route_table; bool ignore_auto_routes : 1; bool ignore_auto_dns : 1; @@ -5205,6 +5207,25 @@ nm_setting_ip_config_get_dhcp_timeout(NMSettingIPConfig *setting) return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dhcp_timeout; } +/** + * nm_setting_ip_config_get_required_timeout: + * @setting: the #NMSettingIPConfig + * + * Returns the value contained in the #NMSettingIPConfig:required-timeout + * property. + * + * Returns: the required timeout for the address family + * + * Since: 1.34, 1.32.4 + **/ +int +nm_setting_ip_config_get_required_timeout(NMSettingIPConfig *setting) +{ + g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), -1); + + return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->required_timeout; +} + /** * nm_setting_ip_config_get_dhcp_iaid: * @setting: the #NMSettingIPConfig @@ -5908,6 +5929,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) case PROP_DHCP_TIMEOUT: g_value_set_int(value, nm_setting_ip_config_get_dhcp_timeout(setting)); break; + case PROP_REQUIRED_TIMEOUT: + g_value_set_int(value, nm_setting_ip_config_get_required_timeout(setting)); + break; case PROP_DHCP_IAID: g_value_set_string(value, nm_setting_ip_config_get_dhcp_iaid(setting)); break; @@ -6018,7 +6042,11 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps case PROP_DHCP_TIMEOUT: priv->dhcp_timeout = g_value_get_int(value); break; + case PROP_REQUIRED_TIMEOUT: + priv->required_timeout = g_value_get_int(value); + break; case PROP_DHCP_IAID: + g_free(priv->dhcp_iaid); priv->dhcp_iaid = g_value_dup_string(value); break; case PROP_DHCP_HOSTNAME_FLAGS: @@ -6048,6 +6076,7 @@ nm_setting_ip_config_init(NMSettingIPConfig *setting) priv->dhcp_send_hostname = TRUE; priv->may_fail = TRUE; priv->dad_timeout = -1; + priv->required_timeout = -1; } static void @@ -6482,6 +6511,38 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass) 0, G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS); + /** + * NMSettingIPConfig:required-timeout: + * + * The minimum time interval in milliseconds for which dynamic IP configuration + * should be tried before the connection succeeds. + * + * This property is useful for example if both IPv4 and IPv6 are enabled and + * are allowed to fail. Normally the connection succeeds as soon as one of + * the two address families completes; by setting a required timeout for + * e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, + * NetworkManager waits some time for IPv4 before the connection becomes + * active. + * + * Note that if #NMSettingIPConfig:may-fail is FALSE for the same address + * family, this property has no effect as NetworkManager needs to wait for + * the full DHCP timeout. + * + * A zero value means that no required timeout is present, -1 means the + * default value (either configuration ipvx.required-timeout override or + * zero). + * + * Since: 1.34, 1.32.4 + **/ + obj_properties[PROP_REQUIRED_TIMEOUT] = g_param_spec_int( + NM_SETTING_IP_CONFIG_REQUIRED_TIMEOUT, + "", + "", + -1, + G_MAXINT32, + -1, + G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS); + /** * NMSettingIPConfig:dhcp-iaid: * diff --git a/src/libnm-core-impl/nm-setting-ip4-config.c b/src/libnm-core-impl/nm-setting-ip4-config.c index 15bae0cd..b3a18bca 100644 --- a/src/libnm-core-impl/nm-setting-ip4-config.c +++ b/src/libnm-core-impl/nm-setting-ip4-config.c @@ -967,6 +967,8 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) * format: a comma separated list of addresses * description: A list of IPv4 addresses and their prefix length. Multiple addresses * can be separated by comma. For example "192.168.1.5/24, 10.1.0.5/24". + * The addresses are listed in increasing priority, meaning the last address will + * be the primary address. * ---end--- */ _nm_properties_override_gobj( diff --git a/src/libnm-core-impl/nm-setting-ip6-config.c b/src/libnm-core-impl/nm-setting-ip6-config.c index f6d59a46..ee72de57 100644 --- a/src/libnm-core-impl/nm-setting-ip6-config.c +++ b/src/libnm-core-impl/nm-setting-ip6-config.c @@ -1030,6 +1030,15 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) * that subnet. * ---end--- */ + /* ---nmcli--- + * property: addresses + * format: a comma separated list of addresses + * description: A list of IPv6 addresses and their prefix length. Multiple addresses + * can be separated by comma. For example "2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64". + * The addresses are listed in increasing priority, meaning the last address will + * be the primary address. + * ---end--- + */ _nm_properties_override_gobj( properties_override, g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_ADDRESSES), diff --git a/src/libnm-core-impl/nm-setting-wireless.c b/src/libnm-core-impl/nm-setting-wireless.c index 11111696..5bdedf9b 100644 --- a/src/libnm-core-impl/nm-setting-wireless.c +++ b/src/libnm-core-impl/nm-setting-wireless.c @@ -749,20 +749,55 @@ _to_dbus_fcn_seen_bssids(const NMSettInfoSetting * sett_info, NMSetting * setting, NMConnectionSerializationFlags flags, const NMConnectionSerializationOptions *options) +{ + if (options && options->seen_bssids) + return options->seen_bssids[0] ? g_variant_new_strv(options->seen_bssids, -1) : NULL; + + /* The seen-bssid property is special. It cannot be converted to D-Bus + * like regular properties, only via the "options". + * + * This basically means, that only the daemon can provide seen-bssids as GVariant, + * while when a client converts the property to GVariant, it gets lost. + * + * This has the odd effect, that when the client converts the setting to GVariant + * and back, the seen-bssids gets lost. That is kinda desired here, because the to_dbus_fcn() + * and from_dbus_fcn() have the meaning of how a setting gets transferred via D-Bus, + * and not necessarily a loss-less conversion into another format and back. And when + * transferring via D-Bus, then the option makes only sense when sending it from + * the daemon to the client, not otherwise. */ + return NULL; +} + +static gboolean +_from_dbus_fcn_seen_bssids(NMSetting * setting, + GVariant * connection_dict, + const char * property, + GVariant * value, + NMSettingParseFlags parse_flags, + GError ** error) { NMSettingWirelessPrivate *priv; + gs_free const char ** s = NULL; + gsize len; + gsize i; - if (options && options->seen_bssids) { - return options->seen_bssids[0] ? g_variant_new_strv(options->seen_bssids, -1) : NULL; + if (_nm_utils_is_manager_process) { + /* in the manager process, we don't accept seen-bssid from the client. + * Do nothing. */ + return TRUE; } priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting); - if (!priv->seen_bssids || priv->seen_bssids->len == 0) - return NULL; + nm_clear_pointer(&priv->seen_bssids, g_ptr_array_unref); - return g_variant_new_strv((const char *const *) priv->seen_bssids->pdata, - priv->seen_bssids->len); + s = g_variant_get_strv(value, &len); + if (len > 0) { + priv->seen_bssids = g_ptr_array_new_full(len, g_free); + for (i = 0; i < len; i++) + g_ptr_array_add(priv->seen_bssids, g_strdup(s[i])); + } + return TRUE; } /** @@ -1075,12 +1110,18 @@ compare_property(const NMSettInfoSetting *sett_info, NMSetting * set_b, NMSettingCompareFlags flags) { - if (nm_streq(sett_info->property_infos[property_idx].name, - NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS)) { + if (sett_info->property_infos[property_idx].param_spec + == obj_properties[PROP_CLONED_MAC_ADDRESS]) { return !set_b || nm_streq0(NM_SETTING_WIRELESS_GET_PRIVATE(set_a)->cloned_mac_address, NM_SETTING_WIRELESS_GET_PRIVATE(set_b)->cloned_mac_address); } + if (sett_info->property_infos[property_idx].param_spec == obj_properties[PROP_SEEN_BSSIDS]) { + return !set_b + || (nm_strv_ptrarray_cmp(NM_SETTING_WIRELESS_GET_PRIVATE(set_a)->seen_bssids, + NM_SETTING_WIRELESS_GET_PRIVATE(set_b)->seen_bssids) + == 0); + } return NM_SETTING_CLASS(nm_setting_wireless_parent_class) ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags); @@ -1744,7 +1785,8 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass) properties_override, obj_properties[PROP_SEEN_BSSIDS], NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING_ARRAY, - .to_dbus_fcn = _to_dbus_fcn_seen_bssids, )); + .to_dbus_fcn = _to_dbus_fcn_seen_bssids, + .from_dbus_fcn = _from_dbus_fcn_seen_bssids, )); /** * NMSettingWireless:mtu: diff --git a/src/libnm-core-impl/tests/meson.build b/src/libnm-core-impl/tests/meson.build index ca1d1c1a..988c60db 100644 --- a/src/libnm-core-impl/tests/meson.build +++ b/src/libnm-core-impl/tests/meson.build @@ -32,8 +32,8 @@ foreach test_unit: test_units libnm_core_impl, libnm_crypto, libnm_base, - libnm_log_null, libnm_systemd_shared, + libnm_log_null, libnm_glib_aux, libnm_std_aux, libc_siphash, diff --git a/src/libnm-core-impl/tests/test-general.c b/src/libnm-core-impl/tests/test-general.c index f9cc5e54..57864948 100644 --- a/src/libnm-core-impl/tests/test-general.c +++ b/src/libnm-core-impl/tests/test-general.c @@ -3868,6 +3868,7 @@ test_connection_diff_a_only(void) {NM_SETTING_IP_CONFIG_NEVER_DEFAULT, NM_SETTING_DIFF_RESULT_IN_A}, {NM_SETTING_IP_CONFIG_MAY_FAIL, NM_SETTING_DIFF_RESULT_IN_A}, {NM_SETTING_IP_CONFIG_DAD_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A}, + {NM_SETTING_IP_CONFIG_REQUIRED_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A}, {NM_SETTING_IP_CONFIG_DNS_PRIORITY, NM_SETTING_DIFF_RESULT_IN_A}, {NM_SETTING_IP_CONFIG_DHCP_IAID, NM_SETTING_DIFF_RESULT_IN_A}, {NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER, NM_SETTING_DIFF_RESULT_IN_A}, @@ -9136,6 +9137,12 @@ _do_test_utils_str_utf8safe(const char * str, ((nmtst_get_rand_bool()) ? NM_UTILS_STR_UTF8_SAFE_FLAG_NONE \ : NM_UTILS_STR_UTF8_SAFE_FLAG_SECRET) + if (expected && strlen(expected) == str_len && memcmp(str, expected, str_len) == 0) { + g_error("Test error: pass expected as NULL (instead of \"%s\", if the escaping will " + "produce no difference.", + expected); + } + buf_safe = nm_utils_buf_utf8safe_escape(str, str_len, flags | RND_FLAG, &str_free_1); str_safe = nm_utils_str_utf8safe_escape(str, flags | RND_FLAG, &str_free_2); @@ -9272,6 +9279,13 @@ test_utils_str_utf8safe(void) do_test_utils_str_utf8safe_unescape("\n\\012", "\n\012"); do_test_utils_str_utf8safe_unescape("\n\\.", "\n."); do_test_utils_str_utf8safe_unescape("\\n\\.3\\r", "\n.3\r"); + + do_test_utils_str_utf8safe("ab∞c", NULL, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL); + do_test_utils_str_utf8safe("ab\ab∞c", "ab\\007b∞c", NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL); + do_test_utils_str_utf8safe("ab\ab∞c", + "ab\\007b\\342\\210\\236c", + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL + | NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII); } /*****************************************************************************/ diff --git a/src/libnm-core-intern/nm-core-internal.h b/src/libnm-core-intern/nm-core-internal.h index 2ef25209..b1f084ed 100644 --- a/src/libnm-core-intern/nm-core-internal.h +++ b/src/libnm-core-intern/nm-core-internal.h @@ -60,6 +60,7 @@ #include "nm-setting-team-port.h" #include "nm-setting-team.h" #include "nm-setting-tun.h" +#include "nm-setting-user.h" #include "nm-setting-veth.h" #include "nm-setting-vlan.h" #include "nm-setting-vpn.h" @@ -79,6 +80,10 @@ #include "nm-vpn-editor-plugin.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h" +#define NM_USER_TAG_ORIGIN "org.freedesktop.NetworkManager.origin" + +/*****************************************************************************/ + /* NM_SETTING_COMPARE_FLAG_INFERRABLE: check whether a device-generated * connection can be replaced by a already-defined connection. This flag only * takes into account properties marked with the %NM_SETTING_PARAM_INFERRABLE diff --git a/src/libnm-core-public/nm-setting-bond.h b/src/libnm-core-public/nm-setting-bond.h index 8314c10e..c869e4c8 100644 --- a/src/libnm-core-public/nm-setting-bond.h +++ b/src/libnm-core-public/nm-setting-bond.h @@ -56,6 +56,7 @@ G_BEGIN_DECLS #define NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE "packets_per_slave" #define NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB "tlb_dynamic_lb" #define NM_SETTING_BOND_OPTION_LP_INTERVAL "lp_interval" +#define NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY "peer_notif_delay" typedef struct _NMSettingBondClass NMSettingBondClass; diff --git a/src/libnm-core-public/nm-setting-ip-config.h b/src/libnm-core-public/nm-setting-ip-config.h index de15dd54..9a745cb2 100644 --- a/src/libnm-core-public/nm-setting-ip-config.h +++ b/src/libnm-core-public/nm-setting-ip-config.h @@ -303,7 +303,7 @@ char *nm_ip_routing_rule_to_string(const NMIPRoutingRule * self, #define NM_SETTING_IP_CONFIG(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTING_IP_CONFIG, NMSettingIPConfig)) #define NM_SETTING_IP_CONFIG_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SETTING_IPCONFIG, NMSettingIPConfigClass)) + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SETTING_IP_CONFIG, NMSettingIPConfigClass)) #define NM_IS_SETTING_IP_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SETTING_IP_CONFIG)) #define NM_IS_SETTING_IP_CONFIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_SETTING_IP_CONFIG)) @@ -331,6 +331,7 @@ char *nm_ip_routing_rule_to_string(const NMIPRoutingRule * self, #define NM_SETTING_IP_CONFIG_MAY_FAIL "may-fail" #define NM_SETTING_IP_CONFIG_DAD_TIMEOUT "dad-timeout" #define NM_SETTING_IP_CONFIG_DHCP_TIMEOUT "dhcp-timeout" +#define NM_SETTING_IP_CONFIG_REQUIRED_TIMEOUT "required-timeout" #define NM_SETTING_IP_CONFIG_DHCP_IAID "dhcp-iaid" #define NM_SETTING_IP_CONFIG_DHCP_REJECT_SERVERS "dhcp-reject-servers" @@ -473,6 +474,8 @@ NM_AVAILABLE_IN_1_2 int nm_setting_ip_config_get_dad_timeout(NMSettingIPConfig *setting); NM_AVAILABLE_IN_1_2 int nm_setting_ip_config_get_dhcp_timeout(NMSettingIPConfig *setting); +NM_AVAILABLE_IN_1_32_4 +int nm_setting_ip_config_get_required_timeout(NMSettingIPConfig *setting); NM_AVAILABLE_IN_1_22 const char *nm_setting_ip_config_get_dhcp_iaid(NMSettingIPConfig *setting); diff --git a/src/libnm-core-public/nm-version-macros.h b/src/libnm-core-public/nm-version-macros.h index fcfbe6c6..f787188c 100644 --- a/src/libnm-core-public/nm-version-macros.h +++ b/src/libnm-core-public/nm-version-macros.h @@ -30,7 +30,7 @@ * Evaluates to the micro version number of NetworkManager which this source * compiled against. */ -#define NM_MICRO_VERSION (2) +#define NM_MICRO_VERSION (10) /** * NM_CHECK_VERSION: @@ -68,6 +68,7 @@ #define NM_VERSION_1_28 (NM_ENCODE_VERSION (1, 28, 0)) #define NM_VERSION_1_30 (NM_ENCODE_VERSION (1, 30, 0)) #define NM_VERSION_1_32 (NM_ENCODE_VERSION (1, 32, 0)) +#define NM_VERSION_1_32_4 (NM_ENCODE_VERSION (1, 32, 4)) /* For releases, NM_API_VERSION is equal to NM_VERSION. * diff --git a/src/libnm-core-public/nm-version-macros.h.in b/src/libnm-core-public/nm-version-macros.h.in index 58e877df..35e89287 100644 --- a/src/libnm-core-public/nm-version-macros.h.in +++ b/src/libnm-core-public/nm-version-macros.h.in @@ -68,6 +68,7 @@ #define NM_VERSION_1_28 (NM_ENCODE_VERSION (1, 28, 0)) #define NM_VERSION_1_30 (NM_ENCODE_VERSION (1, 30, 0)) #define NM_VERSION_1_32 (NM_ENCODE_VERSION (1, 32, 0)) +#define NM_VERSION_1_32_4 (NM_ENCODE_VERSION (1, 32, 4)) /* For releases, NM_API_VERSION is equal to NM_VERSION. * diff --git a/src/libnm-core-public/nm-version.h b/src/libnm-core-public/nm-version.h index 516870d1..10ceb581 100644 --- a/src/libnm-core-public/nm-version.h +++ b/src/libnm-core-public/nm-version.h @@ -285,6 +285,12 @@ #define NM_AVAILABLE_IN_1_32 #endif +#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_32_4 + #define NM_AVAILABLE_IN_1_32_4 G_UNAVAILABLE(1, 32.4) +#else + #define NM_AVAILABLE_IN_1_32_4 +#endif + /* * Synchronous API for calling D-Bus in libnm is deprecated. See * https://developer.gnome.org/libnm/stable/usage.html#sync-api diff --git a/src/libnm-glib-aux/nm-dedup-multi.h b/src/libnm-glib-aux/nm-dedup-multi.h index 9a995ec5..fb1cb863 100644 --- a/src/libnm-glib-aux/nm-dedup-multi.h +++ b/src/libnm-glib-aux/nm-dedup-multi.h @@ -309,8 +309,11 @@ guint nm_dedup_multi_index_dirty_remove_idx(NMDedupMultiIndex * self, /*****************************************************************************/ typedef struct _NMDedupMultiIter { - const CList * _head; - const CList * _next; + const CList *_head; + union { + const CList *_next; + const CList *_prev; + }; const NMDedupMultiEntry *current; } NMDedupMultiIter; @@ -329,13 +332,30 @@ nm_dedup_multi_iter_init(NMDedupMultiIter *iter, const NMDedupMultiHeadEntry *he iter->current = NULL; } +static inline void +nm_dedup_multi_iter_init_reverse(NMDedupMultiIter *iter, const NMDedupMultiHeadEntry *head) +{ + g_return_if_fail(iter); + + if (head && !c_list_is_empty(&head->lst_entries_head)) { + iter->_head = &head->lst_entries_head; + iter->_prev = head->lst_entries_head.prev; + } else { + iter->_head = NULL; + iter->_prev = NULL; + } + iter->current = NULL; +} + static inline gboolean nm_dedup_multi_iter_next(NMDedupMultiIter *iter) { g_return_val_if_fail(iter, FALSE); - if (!iter->_next) + if (!iter->_next) { + iter->current = NULL; return FALSE; + } /* we always look ahead for the next. This way, the user * may delete the current entry (but no other entries). */ @@ -347,6 +367,26 @@ nm_dedup_multi_iter_next(NMDedupMultiIter *iter) return TRUE; } +static inline gboolean +nm_dedup_multi_iter_prev(NMDedupMultiIter *iter) +{ + g_return_val_if_fail(iter, FALSE); + + if (!iter->_prev) { + iter->current = NULL; + return FALSE; + } + + /* we always look ahead for the prev. This way, the user + * may delete the current entry (but no other entries). */ + iter->current = c_list_entry(iter->_prev, NMDedupMultiEntry, lst_entries); + if (iter->_prev->prev == iter->_head) + iter->_prev = NULL; + else + iter->_prev = iter->_prev->prev; + return TRUE; +} + #define nm_dedup_multi_iter_for_each(iter, head_entry) \ for (nm_dedup_multi_iter_init((iter), (head_entry)); nm_dedup_multi_iter_next((iter));) diff --git a/src/libnm-glib-aux/nm-errno.c b/src/libnm-glib-aux/nm-errno.c index 283173e3..0426a21d 100644 --- a/src/libnm-glib-aux/nm-errno.c +++ b/src/libnm-glib-aux/nm-errno.c @@ -7,8 +7,6 @@ #include "nm-errno.h" -#include - /*****************************************************************************/ static NM_UTILS_LOOKUP_STR_DEFINE( @@ -162,19 +160,9 @@ nm_strerror_native(int errsv) buf = buf_static; if (G_UNLIKELY(!buf)) { - int errno_saved = errno; - pthread_key_t key; - buf = g_malloc(NM_STRERROR_BUFSIZE); buf_static = buf; - - if (pthread_key_create(&key, g_free) != 0 || pthread_setspecific(key, buf) != 0) { - /* Failure. We will leak the buffer when the thread exits. - * - * Nothing we can do about it really. For Debug builds we fail with an assertion. */ - nm_assert_not_reached(); - } - errno = errno_saved; + nm_utils_thread_local_register_destroy(buf, g_free); } return nm_strerror_native_r(errsv, buf, NM_STRERROR_BUFSIZE); diff --git a/src/libnm-glib-aux/nm-io-utils.c b/src/libnm-glib-aux/nm-io-utils.c index 894c8726..87478a2d 100644 --- a/src/libnm-glib-aux/nm-io-utils.c +++ b/src/libnm-glib-aux/nm-io-utils.c @@ -564,3 +564,67 @@ nm_g_subprocess_terminate_in_background(GSubprocess *subprocess, int timeout_mse NULL), main_context); } + +/*****************************************************************************/ + +char ** +nm_utils_find_mkstemp_files(const char *dirname, const char *filename) +{ + static const char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + DIR * dir; + struct dirent * entry; + GPtrArray * arr = NULL; + gsize l; + + /* We write files with g_file_set_contents() and nm_utils_file_set_contents(). + * These create temporary files using g_mkstemp_full(), with a random .XXXXXX suffix. + * + * If NetworkManager crashes while writing the file, then those temporary files are + * left over. We might want to find and delete such files. + * + * Beware: only delete such files if you are in full control about which files are + * supposed to be in the directory. For example, NetworkManager controls + * /var/lib/NetworkManager/timestamps files, and it thus takes the right to delete + * all files /var/lib/NetworkManager/timestamps.XXXXXX. That may not be appropriate + * in other cases! */ + + if (!dirname || !filename || !filename[0]) + return NULL; + + dir = opendir(dirname); + if (!dir) + return NULL; + + l = strlen(filename); + + while ((entry = readdir(dir))) { + const char *f = entry->d_name; + guint i; + + if (strncmp(f, filename, l) != 0) + goto next; + if (f[l] != '.') + goto next; + for (i = 1; i <= 6; i++) { + /* @letters is also what g_mkstemp_full() does! */ + if (!memchr(letters, f[l + i], G_N_ELEMENTS(letters))) + goto next; + } + if (f[l + 7] != '\0') + goto next; + + if (!arr) + arr = g_ptr_array_new(); + + g_ptr_array_add(arr, g_strdup(f)); +next:; + } + + closedir(dir); + + if (!arr) + return NULL; + + g_ptr_array_add(arr, NULL); + return (char **) g_ptr_array_free(arr, FALSE); +} diff --git a/src/libnm-glib-aux/nm-io-utils.h b/src/libnm-glib-aux/nm-io-utils.h index 98e63ac0..31ff6d05 100644 --- a/src/libnm-glib-aux/nm-io-utils.h +++ b/src/libnm-glib-aux/nm-io-utils.h @@ -58,4 +58,6 @@ int nm_utils_file_stat(const char *filename, struct stat *out_st); void nm_g_subprocess_terminate_in_background(GSubprocess *subprocess, int timeout_msec_before_kill); +char **nm_utils_find_mkstemp_files(const char *dirname, const char *filename); + #endif /* __NM_IO_UTILS_H__ */ diff --git a/src/libnm-glib-aux/nm-keyfile-aux.c b/src/libnm-glib-aux/nm-keyfile-aux.c index 75abe538..9cda1cf7 100644 --- a/src/libnm-glib-aux/nm-keyfile-aux.c +++ b/src/libnm-glib-aux/nm-keyfile-aux.c @@ -27,6 +27,8 @@ struct _NMKeyFileDB { bool dirty : 1; bool destroyed : 1; + bool groups_pruned : 1; + char filename[]; }; @@ -62,6 +64,16 @@ _IS_KEY_FILE_DB(NMKeyFileDB *self, gboolean require_is_started, gboolean allow_d return TRUE; } +static GKeyFile * +_key_file_new(void) +{ + GKeyFile *kf; + + kf = g_key_file_new(); + g_key_file_set_list_separator(kf, ','); + return kf; +} + /*****************************************************************************/ NMKeyFileDB * @@ -86,8 +98,7 @@ nm_key_file_db_new(const char * filename, self->log_fcn = log_fcn; self->got_dirty_fcn = got_dirty_fcn; self->user_data = user_data; - self->kf = g_key_file_new(); - g_key_file_set_list_separator(self->kf, ','); + self->kf = _key_file_new(); memcpy(self->filename, filename, l_filename + 1); self->group_name = &self->filename[l_filename + 1]; memcpy((char *) self->group_name, group_name, l_group + 1); @@ -371,3 +382,106 @@ nm_key_file_db_to_file(NMKeyFileDB *self, gboolean force) } else _LOGD("write keyfile: \"%s\"", self->filename); } + +/*****************************************************************************/ + +void +nm_key_file_db_prune_tmp_files(NMKeyFileDB *self) +{ + gs_free char * n_file = NULL; + gs_free char * n_dir = NULL; + gs_strfreev char **tmpfiles = NULL; + gsize i; + + n_file = g_path_get_basename(self->filename); + n_dir = g_path_get_dirname(self->filename); + + tmpfiles = nm_utils_find_mkstemp_files(n_dir, n_file); + if (!tmpfiles) + return; + + for (i = 0; tmpfiles[i]; i++) { + const char * tmpfile = tmpfiles[i]; + gs_free char *full_file = NULL; + int r; + + full_file = g_strdup_printf("%s/%s", n_dir, tmpfile); + + r = unlink(full_file); + if (r != 0) { + int errsv = errno; + + if (errsv != ENOENT) { + _LOGD("prune left over temp file %s failed: %s", + full_file, + nm_strerror_native(errsv)); + } + continue; + } + + _LOGD("prune left over temp file %s", full_file); + } +} + +/*****************************************************************************/ + +void +nm_key_file_db_prune(NMKeyFileDB *self, + gboolean (*predicate)(const char *key, gpointer user_data), + gpointer user_data) +{ + gs_strfreev char ** keys = NULL; + nm_auto_unref_keyfile GKeyFile *kf_to_free = NULL; + GKeyFile * kf_src = NULL; + GKeyFile * kf_dst = NULL; + guint k; + + g_return_if_fail(_IS_KEY_FILE_DB(self, TRUE, FALSE)); + nm_assert(predicate); + + _LOGD("prune keyfile of old entries: \"%s\"", self->filename); + + if (!self->groups_pruned) { + /* When we prune the first time, we swap the GKeyfile instance. + * The instance loaded from disk might have unrelated groups and + * comments. Let's get rid of them by creating a new instance. + * + * Otherwise, we know that self->kf only contains good keys, + * and at most we need to remove some of them. */ + kf_to_free = g_steal_pointer(&self->kf); + self->kf = _key_file_new(); + kf_src = kf_to_free; + self->groups_pruned = TRUE; + self->dirty = TRUE; + } else + kf_src = self->kf; + kf_dst = self->kf; + + keys = g_key_file_get_keys(kf_src, self->group_name, NULL, NULL); + if (keys) { + for (k = 0; keys[k]; k++) { + const char *key = keys[k]; + gboolean keep; + + keep = predicate(key, user_data); + + if (!keep) { + if (kf_dst == kf_src) { + g_key_file_remove_key(kf_dst, self->group_name, key, NULL); + self->dirty = TRUE; + } + continue; + } + + if (kf_dst != kf_src) { + gs_free char *value = NULL; + + value = g_key_file_get_value(kf_src, self->group_name, key, NULL); + if (value) + g_key_file_set_value(kf_dst, self->group_name, key, value); + else + self->dirty = TRUE; + } + } + } +} diff --git a/src/libnm-glib-aux/nm-keyfile-aux.h b/src/libnm-glib-aux/nm-keyfile-aux.h index 72d2f418..e756c57a 100644 --- a/src/libnm-glib-aux/nm-keyfile-aux.h +++ b/src/libnm-glib-aux/nm-keyfile-aux.h @@ -50,6 +50,12 @@ void nm_key_file_db_set_string_list(NMKeyFileDB * self, void nm_key_file_db_to_file(NMKeyFileDB *self, gboolean force); +void nm_key_file_db_prune_tmp_files(NMKeyFileDB *self); + +void nm_key_file_db_prune(NMKeyFileDB *self, + gboolean (*predicate)(const char *key, gpointer user_data), + gpointer user_data); + /*****************************************************************************/ #endif /* __NM_KEYFILE_AUX_H__ */ diff --git a/src/libnm-glib-aux/nm-macros-internal.h b/src/libnm-glib-aux/nm-macros-internal.h index 39197ecf..f2d81e1c 100644 --- a/src/libnm-glib-aux/nm-macros-internal.h +++ b/src/libnm-glib-aux/nm-macros-internal.h @@ -121,14 +121,6 @@ _nm_auto_free_gstring(GString **str) } #define nm_auto_free_gstring nm_auto(_nm_auto_free_gstring) -static inline void -_nm_auto_protect_errno(const int *p_saved_errno) -{ - errno = *p_saved_errno; -} -#define NM_AUTO_PROTECT_ERRNO(errsv_saved) \ - nm_auto(_nm_auto_protect_errno) _nm_unused const int errsv_saved = (errno) - NM_AUTO_DEFINE_FCN0(GSource *, _nm_auto_unref_gsource, g_source_unref); #define nm_auto_unref_gsource nm_auto(_nm_auto_unref_gsource) diff --git a/src/libnm-glib-aux/nm-random-utils.c b/src/libnm-glib-aux/nm-random-utils.c index 56b99d5e..b055bc3f 100644 --- a/src/libnm-glib-aux/nm-random-utils.c +++ b/src/libnm-glib-aux/nm-random-utils.c @@ -8,6 +8,8 @@ #include "nm-random-utils.h" #include +#include +#include #if USE_SYS_RANDOM_H #include @@ -16,9 +18,185 @@ #endif #include "nm-shared-utils.h" +#include "nm-time-utils.h" /*****************************************************************************/ +#if !defined(SYS_getrandom) && defined(__NR_getrandom) + #define SYS_getrandom __NR_getrandom +#endif + +#ifndef GRND_NONBLOCK + #define GRND_NONBLOCK 0x01 +#endif + +#ifndef GRND_INSECURE + #define GRND_INSECURE 0x04 +#endif + +#if !HAVE_GETRANDOM && defined(SYS_getrandom) +static int +getrandom(void *buf, size_t buflen, unsigned flags) +{ + return syscall(SYS_getrandom, buf, buflen, flags); +} + #undef HAVE_GETRANDOM + #define HAVE_GETRANDOM 1 +#endif + +/*****************************************************************************/ + +typedef struct _nm_packed { + uintptr_t heap_ptr; + uintptr_t stack_ptr; + gint64 now_bootime; + gint64 now_real; + pid_t pid; + pid_t ppid; + pid_t tid; + guint32 grand[16]; + guint8 auxval[16]; + guint8 getrandom_buf[20]; +} BadRandSeed; + +typedef struct _nm_packed { + guint64 counter; + union { + guint8 full[NM_UTILS_CHECKSUM_LENGTH_SHA256]; + struct { + guint8 half_1[NM_UTILS_CHECKSUM_LENGTH_SHA256 / 2]; + guint8 half_2[NM_UTILS_CHECKSUM_LENGTH_SHA256 / 2]; + }; + } sha_digest; + union { + guint8 u8[NM_UTILS_CHECKSUM_LENGTH_SHA256 / 2]; + guint32 u32[((NM_UTILS_CHECKSUM_LENGTH_SHA256 / 2) + 3) / 4]; + } rand_vals; + GRand *rand; +} BadRandState; + +static void +_bad_random_init_seed(BadRandSeed *seed) +{ + const guint8 *p_at_random; + int seed_idx; + GRand * rand; + + /* g_rand_new() reads /dev/urandom, but we already noticed that + * /dev/urandom fails to give us good randomness (which is why + * we hit the "bad randomness" code path). So this may not be as + * good as we wish, but let's hope that it it does something smart + * to give some extra entropy... */ + rand = g_rand_new(); + + /* Get some seed material from a GRand. */ + for (seed_idx = 0; seed_idx < (int) G_N_ELEMENTS(seed->grand); seed_idx++) + seed->grand[seed_idx] = g_rand_int(rand); + + /* Add an address from the heap and stack, maybe ASLR helps a bit? */ + seed->heap_ptr = (uintptr_t) ((gpointer) rand); + seed->stack_ptr = (uintptr_t) ((gpointer) &rand); + + g_rand_free(rand); + + /* Add the per-process, random number. */ + p_at_random = ((gpointer) getauxval(AT_RANDOM)); + if (p_at_random) { + G_STATIC_ASSERT(sizeof(seed->auxval) == 16); + memcpy(&seed->auxval, p_at_random, 16); + } + +#if HAVE_GETRANDOM + { + ssize_t r; + + /* This is likely to fail, because we already failed a moment earlier. Still, give + * it a try. */ + r = getrandom(seed->getrandom_buf, + sizeof(seed->getrandom_buf), + GRND_INSECURE | GRND_NONBLOCK); + (void) r; + } +#endif + + seed->now_bootime = nm_utils_clock_gettime_nsec(CLOCK_BOOTTIME); + seed->now_real = g_get_real_time(); + seed->pid = getpid(); + seed->ppid = getppid(); + seed->tid = nm_utils_gettid(); +} + +static void +_bad_random_bytes(guint8 *buf, gsize n) +{ + nm_auto_free_checksum GChecksum *sum = g_checksum_new(G_CHECKSUM_SHA256); + + nm_assert(n > 0); + + /* We are in the fallback code path, where getrandom() (and /dev/urandom) failed + * to give us good randomness. Try our best. + * + * Our ability to get entropy for the CPRNG is very limited and thus the overall + * result will not be good randomness. See _bad_random_init_seed(). + * + * Once we have some seed material, we combine GRand (which is not a cryptographically + * secure PRNG) with some iterative sha256 hashing. It would be nice if we had + * easy access to chacha20, but it's probably more cumbersome to fork those + * implementations than hack a bad CPRNG by using sha256 hashing. After all, this + * is fallback code to get *some* randomness. And with the inability to get a good + * seed, the CPRNG is not going to give us truly good randomness. */ + + { + static BadRandState gl_state; + static GRand * gl_rand; + static GMutex gl_mutex; + NM_G_MUTEX_LOCKED(&gl_mutex); + + if (G_UNLIKELY(!gl_rand)) { + union { + BadRandSeed d_seed; + guint32 d_u32[(sizeof(BadRandSeed) + 3) / 4]; + } data = { + .d_u32 = {0}, + }; + + _bad_random_init_seed(&data.d_seed); + + gl_rand = g_rand_new_with_seed_array(data.d_u32, G_N_ELEMENTS(data.d_u32)); + + g_checksum_update(sum, (const guchar *) &data, sizeof(data)); + nm_utils_checksum_get_digest(sum, gl_state.sha_digest.full); + } + + while (TRUE) { + int i; + + gl_state.counter++; + for (i = 0; i < G_N_ELEMENTS(gl_state.rand_vals.u32); i++) + gl_state.rand_vals.u32[i] = g_rand_int(gl_rand); + g_checksum_reset(sum); + g_checksum_update(sum, (const guchar *) &gl_state, sizeof(gl_state)); + nm_utils_checksum_get_digest(sum, gl_state.sha_digest.full); + + /* gl_state.sha_digest.full and gl_state.rand_vals contain now our + * random values, but they are also the state for the next iteration. + * We must not directly expose that state to the caller, so XOR the values. + * + * That means, per iteration we can generate 16 bytes of randomness. That + * is for example required to generate a random UUID. */ + for (i = 0; i < (int) (NM_UTILS_CHECKSUM_LENGTH_SHA256 / 2); i++) { + nm_assert(n > 0); + buf[0] = gl_state.sha_digest.half_1[i] ^ gl_state.sha_digest.half_2[i] + ^ gl_state.rand_vals.u8[i]; + buf++; + n--; + if (n == 0) + return; + } + } + } +} + /** * nm_utils_random_bytes: * @p: the buffer to fill @@ -46,9 +224,7 @@ nm_utils_random_bytes(void *p, size_t n) int fd; int r; gboolean has_high_quality = TRUE; - gboolean urandom_success; - guint8 * buf = p; - gboolean avoid_urandom = FALSE; + guint8 * buf = p; g_return_val_if_fail(p, FALSE); g_return_val_if_fail(n > 0, FALSE); @@ -58,91 +234,66 @@ nm_utils_random_bytes(void *p, size_t n) static gboolean have_syscall = TRUE; if (have_syscall) { - r = getrandom(buf, n, GRND_NONBLOCK); - if (r > 0) { - if ((size_t) r == n) + ssize_t r2; + int errsv; + + r2 = getrandom(buf, n, GRND_NONBLOCK); + if (r2 >= 0) { + if ((size_t) r2 == n) return TRUE; /* no or partial read. There is not enough entropy. - * Fill the rest reading from urandom, and remember that - * some bits are not high quality. */ - nm_assert(r < n); - buf += r; - n -= r; - has_high_quality = FALSE; + * Fill the rest reading with the fallback code and remember + * that some bits are not high quality. */ + nm_assert((size_t) r2 < n); + buf += r2; + n -= r2; /* At this point, we don't want to read /dev/urandom, because * the entropy pool is low (early boot?), and asking for more * entropy causes kernel messages to be logged. * - * We use our fallback via GRand. Note that g_rand_new() also - * tries to seed itself with data from /dev/urandom, but since - * we reuse the instance, it shouldn't matter. */ - avoid_urandom = TRUE; + * Note that we fall back to _bad_random_bytes(), which (among others) seeds + * itself with g_rand_new(). That also will read /dev/urandom, but as + * we do that only once, we don't care. But in general, we are here in + * a situation where we want to avoid reading /dev/urandom too much. */ + goto out_bad_random; + } + errsv = errno; + if (errsv == ENOSYS) { + /* no support for getrandom(). We don't know whether + * we /dev/urandom will give us good quality. Assume yes. */ + have_syscall = FALSE; + } else if (errsv == EAGAIN) { + /* No entropy. We avoid reading /dev/urandom. */ + goto out_bad_random; } else { - if (errno == ENOSYS) { - /* no support for getrandom(). We don't know whether - * we urandom will give us good quality. Assume yes. */ - have_syscall = FALSE; - } else { - /* unknown error. We'll read urandom below, but we don't have - * high-quality randomness. */ - has_high_quality = FALSE; - } + /* Unknown error, likely no entropy. We'll read /dev/urandom below, but we don't + * have high-quality randomness. */ + has_high_quality = FALSE; } } } #endif - urandom_success = FALSE; - if (!avoid_urandom) { fd_open: - fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY); - if (fd < 0) { - r = errno; - if (r == EINTR) - goto fd_open; - } else { - r = nm_utils_fd_read_loop_exact(fd, buf, n, TRUE); - nm_close(fd); - if (r >= 0) - urandom_success = TRUE; - } - } - - if (!urandom_success) { - static _nm_thread_local GRand *rand = NULL; - gsize i; - int j; - - /* we failed to fill the bytes reading from urandom. - * Fill the bits using GRand pseudo random numbers. - * - * We don't have good quality. - */ - has_high_quality = FALSE; - - if (G_UNLIKELY(!rand)) - rand = g_rand_new(); - - nm_assert(n > 0); - i = 0; - for (;;) { - const union { - guint32 v32; - guint8 v8[4]; - } v = { - .v32 = g_rand_int(rand), - }; - - for (j = 0; j < 4;) { - buf[i++] = v.v8[j++]; - if (i >= n) - goto done; - } - } -done:; + fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY); + if (fd < 0) { + if (errno == EINTR) + goto fd_open; + goto out_bad_random; } + r = nm_utils_fd_read_loop_exact(fd, buf, n, TRUE); + nm_close(fd); + if (r >= 0) + return has_high_quality; - return has_high_quality; +out_bad_random: + /* we failed to fill the bytes reading from /dev/urandom. + * Fill the bits using our pseudo random numbers. + * + * We don't have good quality. + */ + _bad_random_bytes(buf, n); + return FALSE; } diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c index e42e0fe5..81852aea 100644 --- a/src/libnm-glib-aux/nm-shared-utils.c +++ b/src/libnm-glib-aux/nm-shared-utils.c @@ -15,7 +15,9 @@ #include #include #include +#include +#include "c-list/src/c-list.h" #include "nm-errno.h" #include "nm-str-buf.h" @@ -2990,13 +2992,13 @@ nm_utils_buf_utf8safe_escape(gconstpointer buf, if (g_utf8_validate(str, buflen, &p) && nul_terminated) { /* note that g_utf8_validate() does not allow NUL character inside @str. Good. * We can treat @str like a NUL terminated string. */ - if (!NM_STRCHAR_ANY( - str, - ch, - (ch == '\\' - || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) && ch < ' ') - || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) - && ((guchar) ch) >= 127)))) + if (!NM_STRCHAR_ANY(str, + ch, + (ch == '\\' + || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) + && nm_ascii_is_ctrl_or_del(ch)) + || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) + && nm_ascii_is_non_ascii(ch))))) return str; } @@ -3013,9 +3015,10 @@ nm_utils_buf_utf8safe_escape(gconstpointer buf, nm_assert(ch); if (ch == '\\') nm_str_buf_append_c(&strbuf, '\\', '\\'); - else if ((NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) && ch < ' ') + else if ((NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) + && nm_ascii_is_ctrl_or_del(ch)) || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) - && ((guchar) ch) >= 127)) + && nm_ascii_is_non_ascii(ch))) _str_buf_append_c_escape_octal(&strbuf, ch); else nm_str_buf_append_c(&strbuf, ch); @@ -4932,14 +4935,14 @@ _nm_g_source_sentinel_get_init(GSource **p_source) }; GSource *source; -again: source = g_source_new((GSourceFuncs *) &source_funcs, sizeof(GSource)); g_source_set_priority(source, G_PRIORITY_DEFAULT_IDLE); g_source_set_name(source, "nm_g_source_sentinel"); if (!g_atomic_pointer_compare_and_exchange(p_source, NULL, source)) { g_source_unref(source); - goto again; + source = g_atomic_pointer_get(p_source); + nm_assert(source); } return source; @@ -6357,3 +6360,80 @@ nm_utils_get_process_exit_status_desc(int status) else return g_strdup_printf("exited with unknown status 0x%x", status); } + +/*****************************************************************************/ + +typedef struct { + CList lst; + gpointer tls_data; + GDestroyNotify destroy_notify; +} TlsRegData; + +static pthread_key_t _tls_reg_key; + +static void +_tls_reg_destroy(gpointer data) +{ + CList * lst_head = data; + TlsRegData *entry; + + if (!lst_head) + return; + + /* For no strong reason are we destroying the elements in reverse + * order than they were added. It seems a bit more sensible (but shouldn't + * matter nor should you rely on that). */ + while ((entry = c_list_last_entry(lst_head, TlsRegData, lst))) { + c_list_unlink_stale(&entry->lst); + entry->destroy_notify(entry->tls_data); + nm_g_slice_free(entry); + } + + nm_g_slice_free(lst_head); +} + +static void +_tls_reg_make_key(void) +{ + if (pthread_key_create(&_tls_reg_key, _tls_reg_destroy) != 0) + g_return_if_reached(); +} + +/** + * nm_utils_thread_local_register_destroy: + * @tls_data: the thread local storage data that should be destroyed when the thread + * exits. This pointer will be "owned" by the current thread. There is no way + * to un-register the destruction. + * @destroy_notify: the free function that will be called when the thread exits. + * + * If _nm_tread_local storage is heap allocated it requires freeing the pointer + * when the thread exits. Use this function to register the pointer to be + * released. + * + * This function does not change errno. + */ +void +nm_utils_thread_local_register_destroy(gpointer tls_data, GDestroyNotify destroy_notify) +{ + NM_AUTO_PROTECT_ERRNO(errsv); + static pthread_once_t key_once = PTHREAD_ONCE_INIT; + CList * lst_head; + TlsRegData * entry; + + nm_assert(destroy_notify); + + if (pthread_once(&key_once, _tls_reg_make_key) != 0) + g_return_if_reached(); + + if ((lst_head = pthread_getspecific(_tls_reg_key)) == NULL) { + lst_head = g_slice_new(CList); + c_list_init(lst_head); + if (pthread_setspecific(_tls_reg_key, lst_head) != 0) + g_return_if_reached(); + } + + entry = g_slice_new(TlsRegData); + entry->tls_data = tls_data; + entry->destroy_notify = destroy_notify; + c_list_link_tail(lst_head, &entry->lst); +} diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index f5b1d00c..dcf37cd3 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -1258,6 +1258,7 @@ typedef enum { NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE, NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_DISALLOWED, NM_UTILS_ERROR_SETTING_MISSING, @@ -1446,10 +1447,14 @@ GType nm_g_type_find_implementing_class_for_property(GType gtype, const char *pn typedef enum { NM_UTILS_STR_UTF8_SAFE_FLAG_NONE = 0, - /* This flag only has an effect during escaping. */ + /* This flag only has an effect during escaping. + * + * It will backslash escape ascii characters according to nm_ascii_is_ctrl_or_del(). */ NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL = 0x0001, - /* This flag only has an effect during escaping. */ + /* This flag only has an effect during escaping. + * + * It will backslash escape ascii characters according to nm_ascii_is_non_ascii(). */ NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII = 0x0002, /* This flag only has an effect during escaping to ensure we @@ -2438,6 +2443,40 @@ nm_hexchar(int x, gboolean upper_case) return upper_case ? _nm_hexchar_table_upper[x & 15] : _nm_hexchar_table_lower[x & 15]; } +static inline gboolean +nm_ascii_is_ctrl(char ch) +{ + /* 0 to ' '-1 is the C0 range. + * + * Other ranges may also be considered control characters, but NOT + * CONSIDERED by this function. For example: + * - DEL (127) is also a control character. + * - SP (' ', 0x20) is also considered a control character. + * - DEL+1 (0x80) to 0x9F is C1 range. + * - NBSP (0xA0) and SHY (0xAD) are ISO 8859 special characters + */ + return ((guchar) ch) < ' '; +} + +static inline gboolean +nm_ascii_is_ctrl_or_del(char ch) +{ + return ((guchar) ch) < ' ' || ch == 127; +} + +static inline gboolean +nm_ascii_is_non_ascii(char ch) +{ + return ((guchar) ch) > 127; +} + +static inline gboolean +nm_ascii_is_regular(char ch) +{ + /* same as(!nm_ascii_is_ctrl_or_del(ch) && !nm_ascii_is_non_ascii(ch)) */ + return ch >= ' ' && ch < 127; +} + char *nm_utils_bin2hexstr_full(gconstpointer addr, gsize length, char delimiter, @@ -2949,4 +2988,8 @@ void nm_crypto_md5_hash(const guint8 *salt, char *nm_utils_get_process_exit_status_desc(int status); +/*****************************************************************************/ + +void nm_utils_thread_local_register_destroy(gpointer tls_data, GDestroyNotify destroy_notify); + #endif /* __NM_SHARED_UTILS_H__ */ diff --git a/src/libnm-glib-aux/nm-str-buf.h b/src/libnm-glib-aux/nm-str-buf.h index b43b206f..7a7f580c 100644 --- a/src/libnm-glib-aux/nm-str-buf.h +++ b/src/libnm-glib-aux/nm-str-buf.h @@ -504,4 +504,11 @@ nm_str_buf_destroy(NMStrBuf *strbuf) #define nm_auto_str_buf nm_auto(nm_str_buf_destroy) +static inline gboolean +nm_str_buf_utf8_validate(NMStrBuf *strbuf) +{ + _nm_str_buf_assert(strbuf); + return strbuf->_priv_len == 0 || g_utf8_validate(strbuf->_priv_str, strbuf->_priv_len, NULL); +} + #endif /* __NM_STR_BUF_H__ */ diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index 6c7ab488..4a7b6790 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -1345,6 +1345,49 @@ test_nm_g_source_sentinel(void) /*****************************************************************************/ +static void +test_nm_ascii(void) +{ + int i; + + for (i = 0; i < 256; i++) { + const char ch = i; + gboolean is_space; + + if (ch == 127) { + g_assert(nm_ascii_is_ctrl_or_del(ch)); + g_assert(!nm_ascii_is_ctrl(ch)); + } else + g_assert(nm_ascii_is_ctrl_or_del(ch) == nm_ascii_is_ctrl(ch)); + g_assert(nm_ascii_is_ctrl_or_del(ch) == g_ascii_iscntrl(ch)); + + g_assert(nm_ascii_is_non_ascii(ch) == (i >= 128)); + + g_assert(!nm_ascii_is_ctrl_or_del(ch) || !nm_ascii_is_non_ascii(ch)); + + g_assert((nm_ascii_is_ctrl_or_del(ch) || nm_ascii_is_regular(ch)) + != nm_ascii_is_non_ascii(ch)); + + g_assert(nm_ascii_is_regular(ch) + == (!nm_ascii_is_ctrl_or_del(ch) && !nm_ascii_is_non_ascii(ch))); + + is_space = g_ascii_isspace(ch); + if (NM_IN_SET(ch, '\t', '\n', '\f', '\r')) { + /* hack is-space, so that the check below works to check for regular ASCII characters. */ + g_assert(!nm_ascii_is_regular(ch)); + g_assert(is_space); + is_space = FALSE; + } + g_assert(nm_ascii_is_regular(ch) + == (g_ascii_isalnum(ch) || g_ascii_isalpha(ch) || g_ascii_isdigit(ch) + || g_ascii_isgraph(ch) || g_ascii_islower(ch) || g_ascii_isprint(ch) + || g_ascii_ispunct(ch) || is_space || g_ascii_isupper(ch) + || g_ascii_isxdigit(ch))); + } +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -1376,6 +1419,7 @@ main(int argc, char **argv) g_test_add_func("/general/test_strv_dup_packed", test_strv_dup_packed); g_test_add_func("/general/test_utils_hashtable_cmp", test_utils_hashtable_cmp); g_test_add_func("/general/test_nm_g_source_sentinel", test_nm_g_source_sentinel); + g_test_add_func("/general/test_nm_ascii", test_nm_ascii); return g_test_run(); } diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index bdeae826..bcf94200 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -3519,6 +3519,7 @@ rta_multipath_done:; obj = nmp_object_new(is_v4 ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + obj->ip_route.is_external = TRUE; obj->ip_route.type_coerced = nm_platform_route_type_coerce(rtm->rtm_type); obj->ip_route.table_coerced = nm_platform_route_table_coerce( tb[RTA_TABLE] ? nla_get_u32(tb[RTA_TABLE]) : (guint32) rtm->rtm_table); diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index 089d0d42..6c0d0015 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -4489,6 +4489,20 @@ nm_platform_ip_route_sync(NMPlatform *self, conf_o = routes->pdata[i]; + if (NMP_OBJECT_CAST_IP_ROUTE(conf_o)->is_external) { + /* This route is added externally. We don't have our own agenda to + * add it, so skip. */ + continue; + } + + /* User space cannot add IPv6 routes with metric 0. However, kernel can, and we might track such + * routes in @route as they are present external. As we already skipped external routes above, + * we don't expect a user's choice to add such a route (it won't work anyway). */ + nm_assert( + IS_IPv4 + || nm_platform_ip6_route_get_effective_metric(NMP_OBJECT_CAST_IP6_ROUTE(conf_o)) + != 0); + #define VTABLE_IS_DEVICE_ROUTE(vt, o) \ (vt->is_ip4 ? (NMP_OBJECT_CAST_IP4_ROUTE(o)->gateway == 0) \ : IN6_IS_ADDR_UNSPECIFIED(&NMP_OBJECT_CAST_IP6_ROUTE(o)->gateway)) @@ -4505,7 +4519,7 @@ nm_platform_ip_route_sync(NMPlatform *self, routes_idx = g_hash_table_new((GHashFunc) nmp_object_id_hash, (GEqualFunc) nmp_object_id_equal); } - if (!g_hash_table_insert(routes_idx, (gpointer) conf_o, (gpointer) conf_o)) { + if (!g_hash_table_add(routes_idx, (gpointer) conf_o)) { _LOG3D("route-sync: skip adding duplicate route %s", nmp_object_to_string(conf_o, NMP_OBJECT_TO_STRING_PUBLIC, @@ -4514,14 +4528,6 @@ nm_platform_ip_route_sync(NMPlatform *self, continue; } - if (!IS_IPv4 - && nm_platform_ip6_route_get_effective_metric(NMP_OBJECT_CAST_IP6_ROUTE(conf_o)) - == 0) { - /* User space cannot add routes with metric 0. However, kernel can, and we might track such - * routes in @route as they are present external. Skip them silently. */ - continue; - } - plat_entry = nm_platform_lookup_entry(self, NMP_CACHE_ID_TYPE_OBJECT_TYPE, conf_o); if (plat_entry) { const NMPObject *plat_o; @@ -4684,6 +4690,24 @@ sync_route_add: } if (routes_prune) { + if (routes) { + for (i = 0; i < routes->len; i++) { + conf_o = routes->pdata[i]; + + if (NMP_OBJECT_CAST_IP_ROUTE(conf_o)->is_external) { + /* this is only to catch the case where an external route is + * both in @routes and @routes_prune list. In that case, + * @routes should win and we should not remove the address. */ + if (!routes_idx) { + routes_idx = g_hash_table_new((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal); + } + g_hash_table_add(routes_idx, (gpointer) conf_o); + continue; + } + } + } + for (i = 0; i < routes_prune->len; i++) { const NMPObject *prune_o; @@ -4694,7 +4718,7 @@ sync_route_add: || (!NM_IS_IPv4(addr_family) && NMP_OBJECT_GET_TYPE(prune_o) == NMP_OBJECT_TYPE_IP6_ROUTE)); - if (routes_idx && g_hash_table_lookup(routes_idx, prune_o)) + if (nm_g_hash_table_lookup(routes_idx, prune_o)) continue; if (!nm_platform_lookup_entry(self, NMP_CACHE_ID_TYPE_OBJECT_TYPE, prune_o)) @@ -6524,6 +6548,7 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz "%s" /* initcwnd */ "%s" /* initrwnd */ "%s" /* mtu */ + "%s" /* is_external */ "", nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced), str_type), @@ -6579,7 +6604,8 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz " mtu %s%" G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", route->mtu) - : ""); + : "", + route->is_external ? " (E)" : ""); return buf; } @@ -6649,6 +6675,7 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz "%s" /* initrwnd */ "%s" /* mtu */ "%s" /* pref */ + "%s" /* is_external */ "", nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced), str_type), @@ -6708,7 +6735,8 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz str_pref, " pref %s", nm_icmpv6_router_pref_to_string(route->rt_pref, str_pref2, sizeof(str_pref2))) - : ""); + : "", + route->is_external ? " (E)" : ""); return buf; } @@ -8005,7 +8033,8 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->lock_cwnd, obj->lock_initcwnd, obj->lock_initrwnd, - obj->lock_mtu)); + obj->lock_mtu, + obj->is_external)); break; } } @@ -8095,6 +8124,8 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, NM_CMP_FIELD(a, b, initcwnd); NM_CMP_FIELD(a, b, initrwnd); NM_CMP_FIELD(a, b, mtu); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) + NM_CMP_FIELD_UNSAFE(a, b, is_external); break; } return 0; @@ -8186,7 +8217,8 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, obj->lock_cwnd, obj->lock_initcwnd, obj->lock_initrwnd, - obj->lock_mtu), + obj->lock_mtu, + obj->is_external), obj->window, obj->cwnd, obj->initcwnd, @@ -8269,6 +8301,8 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, NM_CMP_DIRECT(_route_pref_normalize(a->rt_pref), _route_pref_normalize(b->rt_pref)); else NM_CMP_FIELD(a, b, rt_pref); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) + NM_CMP_FIELD_UNSAFE(a, b, is_external); break; } return 0; diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index e6279176..9d40cbbe 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -462,6 +462,14 @@ typedef union { * the "table_coerced" field is ignored (unlike for the metric). */ \ bool table_any : 1; \ \ + /* This route is tracked as external route, that is not a route that NetworkManager + * actively wants to add, but a route that was added externally. In some cases, such + * a route should be ignored. + * + * Note that unlike most other fields here, this flag only exists inside NetworkManager + * and is not reflected on netlink. */ \ + bool is_external : 1; \ + \ /* rtnh_flags * * Routes with rtm_flags RTM_F_CLONED are hidden by platform and diff --git a/src/libnm-platform/nmp-netns.c b/src/libnm-platform/nmp-netns.c index aea5b3b6..2b28a4cd 100644 --- a/src/libnm-platform/nmp-netns.c +++ b/src/libnm-platform/nmp-netns.c @@ -11,7 +11,6 @@ #include #include #include -#include #include "libnm-log-core/nm-logging.h" @@ -151,20 +150,13 @@ _netns_stack_get_impl(void) { gs_unref_object NMPNetns *netns = NULL; gs_free_error GError *error = NULL; - pthread_key_t key; GArray * s; s = g_array_new(FALSE, FALSE, sizeof(NetnsInfo)); g_array_set_clear_func(s, _netns_stack_clear_cb); _netns_stack = s; - /* register a destructor function to cleanup the array. If we fail - * to do so, we will leak NMPNetns instances (and their file descriptor) when the - * thread exits. */ - if (pthread_key_create(&key, (void (*)(void *)) g_array_unref) != 0) - _LOGE(NULL, "failure to initialize thread-local storage"); - else if (pthread_setspecific(key, s) != 0) - _LOGE(NULL, "failure to set thread-local storage"); + nm_utils_thread_local_register_destroy(s, (GDestroyNotify) g_array_unref); /* at the bottom of the stack we must try to create a netns instance * that we never pop. It's the base to which we need to return. */ diff --git a/src/libnm-platform/nmp-object.h b/src/libnm-platform/nmp-object.h index e3e101ee..021829db 100644 --- a/src/libnm-platform/nmp-object.h +++ b/src/libnm-platform/nmp-object.h @@ -777,6 +777,18 @@ nmp_cache_iter_next(NMDedupMultiIter *iter, const NMPObject **out_obj) return has_next; } +static inline gboolean +nmp_cache_iter_prev(NMDedupMultiIter *iter, const NMPObject **out_obj) +{ + gboolean has_prev; + + has_prev = nm_dedup_multi_iter_prev(iter); + nm_assert(!has_prev || NMP_OBJECT_IS_VALID(iter->current->obj)); + if (out_obj) + *out_obj = has_prev ? iter->current->obj : NULL; + return has_prev; +} + static inline gboolean nmp_cache_iter_next_link(NMDedupMultiIter *iter, const NMPlatformLink **out_obj) { @@ -792,6 +804,9 @@ nmp_cache_iter_next_link(NMDedupMultiIter *iter, const NMPlatformLink **out_obj) #define nmp_cache_iter_for_each(iter, head, obj) \ for (nm_dedup_multi_iter_init((iter), (head)); nmp_cache_iter_next((iter), (obj));) +#define nmp_cache_iter_for_each_reverse(iter, head, obj) \ + for (nm_dedup_multi_iter_init_reverse((iter), (head)); nmp_cache_iter_prev((iter), (obj));) + #define nmp_cache_iter_for_each_link(iter, head, obj) \ for (nm_dedup_multi_iter_init((iter), (head)); nmp_cache_iter_next_link((iter), (obj));) diff --git a/src/libnm-std-aux/nm-std-aux.h b/src/libnm-std-aux/nm-std-aux.h index b153866f..17b26881 100644 --- a/src/libnm-std-aux/nm-std-aux.h +++ b/src/libnm-std-aux/nm-std-aux.h @@ -695,16 +695,22 @@ nm_close(int fd) NM_AUTO_DEFINE_FCN_VOID0(void *, _nm_auto_free_impl, free); #define nm_auto_free nm_auto(_nm_auto_free_impl) +static inline void +_nm_auto_protect_errno(const int *p_saved_errno) +{ + errno = *p_saved_errno; +} +#define NM_AUTO_PROTECT_ERRNO(errsv_saved) \ + nm_auto(_nm_auto_protect_errno) _nm_unused const int errsv_saved = (errno) + /*****************************************************************************/ static inline void _nm_auto_close(int *pfd) { if (*pfd >= 0) { - int errsv = errno; - + NM_AUTO_PROTECT_ERRNO(errsv); (void) nm_close(*pfd); - errno = errsv; } } #define nm_auto_close nm_auto(_nm_auto_close) @@ -713,10 +719,8 @@ static inline void _nm_auto_fclose(FILE **pfd) { if (*pfd) { - int errsv = errno; - + NM_AUTO_PROTECT_ERRNO(errsv); (void) fclose(*pfd); - errno = errsv; } } #define nm_auto_fclose nm_auto(_nm_auto_fclose) diff --git a/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h b/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h index 9d4669c3..2f6188dc 100644 --- a/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h +++ b/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h @@ -75,12 +75,7 @@ raw_getpid(void) #endif } -static inline pid_t -_nm_gettid(void) -{ - return (pid_t) syscall(SYS_gettid); -} - #define gettid() _nm_gettid() + #define gettid() nm_utils_gettid() /* we build with C11 and thus provides char32_t,char16_t. */ #define HAVE_CHAR32_T 1 diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c index 3ac310bf..1cc5f9c1 100644 --- a/src/libnmc-setting/nm-meta-setting-desc.c +++ b/src/libnmc-setting/nm-meta-setting-desc.c @@ -6014,6 +6014,21 @@ static const NMMetaPropertyInfo *const property_infos_IP4_CONFIG[] = { PROPERTY_INFO (NM_SETTING_IP_CONFIG_MAY_FAIL, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_MAY_FAIL, .property_type = &_pt_gobject_bool, ), + PROPERTY_INFO (NM_SETTING_IP_CONFIG_REQUIRED_TIMEOUT, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_REQUIRED_TIMEOUT, + .property_type = &_pt_gobject_int, + .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int, + .value_infos = INT_VALUE_INFOS ( + { + .value.i64 = -1, + .nick = "default", + }, + { + .value.i64 = G_MAXINT32, + .nick = "infinity", + }, + ), + ), + ), PROPERTY_INFO (NM_SETTING_IP_CONFIG_DAD_TIMEOUT, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DAD_TIMEOUT, .property_type = &_pt_gobject_int, .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int, @@ -6217,6 +6232,21 @@ static const NMMetaPropertyInfo *const property_infos_IP6_CONFIG[] = { PROPERTY_INFO (NM_SETTING_IP_CONFIG_MAY_FAIL, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_MAY_FAIL, .property_type = &_pt_gobject_bool, ), + PROPERTY_INFO (NM_SETTING_IP_CONFIG_REQUIRED_TIMEOUT, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_REQUIRED_TIMEOUT, + .property_type = &_pt_gobject_int, + .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int, + .value_infos = INT_VALUE_INFOS ( + { + .value.i64 = -1, + .nick = "default", + }, + { + .value.i64 = G_MAXINT32, + .nick = "infinity", + }, + ), + ), + ), PROPERTY_INFO_WITH_DESC (NM_SETTING_IP6_CONFIG_IP6_PRIVACY, .property_type = &_pt_gobject_enum, .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_enum, diff --git a/src/libnmc-setting/settings-docs-input.xml b/src/libnmc-setting/settings-docs-input.xml index 126dbfb1..b434f2fd 100644 --- a/src/libnmc-setting/settings-docs-input.xml +++ b/src/libnmc-setting/settings-docs-input.xml @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/libnmc-setting/settings-docs.h b/src/libnmc-setting/settings-docs.h index da4f6b4c..12625d44 100644 --- a/src/libnmc-setting/settings-docs.h +++ b/src/libnmc-setting/settings-docs.h @@ -226,7 +226,7 @@ #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_REMOTE N_("The remote endpoint of the tunnel; the value must contain an IPv4 or IPv6 address.") #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_TOS N_("The type of service (IPv4) or traffic class (IPv6) field to be set on tunneled packets.") #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_TTL N_("The TTL to assign to tunneled packets. 0 is a special value meaning that packets inherit the TTL value.") -#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ADDRESSES N_("A list of IPv4 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"192.168.1.5/24, 10.1.0.5/24\".") +#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ADDRESSES N_("A list of IPv4 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"192.168.1.5/24, 10.1.0.5/24\". The addresses are listed in increasing priority, meaning the last address will be the primary address.") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DAD_TIMEOUT N_("Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4.") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID N_("A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string ('aa:bb:cc') it is interpreted as a binary client ID, in which case the first byte is assumed to be the 'type' field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the 'type' field is set to 0. The special values \"mac\" and \"perm-mac\" are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. The special value \"ipv6-duid\" uses the DUID from \"ipv6.dhcp-duid\" property as an RFC4361-compliant client identifier. As IAID it uses \"ipv4.dhcp-iaid\" and falls back to \"ipv6.dhcp-iaid\" if unset. The special value \"duid\" generates a RFC4361-compliant client identifier based on \"ipv4.dhcp-iaid\" and uses a DUID generated by hashing /etc/machine-id. The special value \"stable\" is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the \"${DEVICE}\" or \"${MAC}\" specifier to get a per-device key. If unset, a globally configured default is used. If still unset, the default depends on the DHCP plugin.") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_FQDN N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified FQDN will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-hostname\" are mutually exclusive and cannot be set at the same time.") @@ -247,11 +247,12 @@ #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_MAY_FAIL N_("If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out. Note that at least one IP configuration must succeed or overall network configuration will still fail. For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully.") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_METHOD N_("IP configuration method. NMSettingIP4Config and NMSettingIP6Config both support \"disabled\", \"auto\", \"manual\", and \"link-local\". See the subclass-specific documentation for other values. In general, for the \"auto\" method, properties such as \"dns\" and \"routes\" specify information that is added on to the information returned from automatic configuration. The \"ignore-auto-routes\" and \"ignore-auto-dns\" properties modify this behavior. For methods that imply no upstream network, such as \"shared\" or \"link-local\", these properties must be empty. For IPv4 method \"shared\", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared.") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_NEVER_DEFAULT N_("If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager.") +#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_REQUIRED_TIMEOUT N_("The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. Note that if \"may-fail\" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero).") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTE_METRIC N_("The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric.") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTE_TABLE N_("Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager.") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTES N_("A list of IPv4 destination addresses, prefix length, optional IPv4 next hop addresses, optional route metric, optional attribute. The valid syntax is: \"ip[/prefix] [next-hop] [metric] [attribute=val]...[,ip[/prefix]...]\". For example \"192.0.2.0/24 10.1.1.1 77, 198.51.100.0/24\".") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE N_("Configure method for creating the address for use with RFC4862 IPv6 Stateless Address Autoconfiguration. The permitted values are: NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 (0) or NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY (1). If the property is set to EUI64, the addresses will be generated using the interface tokens derived from hardware address. This makes the host part of the address to stay constant, making it possible to track host's presence when it changes networks. The address changes when the interface hardware is replaced. The value of stable-privacy enables use of cryptographically secure hash of a secret host-specific key along with the connection's stable-id and the network address as specified by RFC7217. This makes it impossible to use the address track host's presence, and makes the address stable when the network interface hardware is replaced. On D-Bus, the absence of an addr-gen-mode setting equals enabling stable-privacy. For keyfile plugin, the absence of the setting on disk means EUI64 so that the property doesn't change on upgrade from older versions. Note that this setting is distinct from the Privacy Extensions as configured by \"ip6-privacy\" property and it does not affect the temporary addresses configured with this option.") -#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDRESSES N_("Array of IP addresses.") +#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDRESSES N_("A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64\". The addresses are listed in increasing priority, meaning the last address will be the primary address.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DAD_TIMEOUT N_("Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_DUID N_("A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp client to identify itself to DHCPv6 servers (RFC 3315). The DUID is carried in the Client Identifier option. If the property is a hex string ('aa:bb:cc') it is interpreted as a binary DUID and filled as an opaque value in the Client Identifier option. The special value \"lease\" will retrieve the DUID previously used from the lease file belonging to the connection. If no DUID is found and \"dhclient\" is the configured dhcp client, the DUID is searched in the system-wide dhclient lease file. If still no DUID is found, or another dhcp client is used, a global and permanent DUID-UUID (RFC 6355) will be generated based on the machine-id. The special values \"llt\" and \"ll\" will generate a DUID of type LLT or LL (see RFC 3315) based on the current MAC address of the device. In order to try providing a stable DUID-LLT, the time field will contain a constant timestamp that is used globally (for all profiles) and persisted to disk. The special values \"stable-llt\", \"stable-ll\" and \"stable-uuid\" will generate a DUID of the corresponding type, derived from the connection's stable-id and a per-host unique key. You may want to include the \"${DEVICE}\" or \"${MAC}\" specifier in the stable-id, in case this profile gets activated on multiple devices. So, the link-layer address of \"stable-ll\" and \"stable-llt\" will be a generated address derived from the stable id. The DUID-LLT time value in the \"stable-llt\" option will be picked among a static timespan of three years (the upper bound of the interval is the same constant timestamp used in \"llt\"). When the property is unset, the global value provided for \"ipv6.dhcp-duid\" is used. If no global value is provided, the default \"lease\" value is assumed.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-fqdn\" are mutually exclusive and cannot be set at the same time.") @@ -272,6 +273,7 @@ #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_METHOD N_("IP configuration method. NMSettingIP4Config and NMSettingIP6Config both support \"disabled\", \"auto\", \"manual\", and \"link-local\". See the subclass-specific documentation for other values. In general, for the \"auto\" method, properties such as \"dns\" and \"routes\" specify information that is added on to the information returned from automatic configuration. The \"ignore-auto-routes\" and \"ignore-auto-dns\" properties modify this behavior. For methods that imply no upstream network, such as \"shared\" or \"link-local\", these properties must be empty. For IPv4 method \"shared\", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_NEVER_DEFAULT N_("If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_RA_TIMEOUT N_("A timeout for waiting Router Advertisements in seconds. If zero (the default), a globally configured default is used. If still unspecified, the timeout depends on the sysctl settings of the device. Set to 2147483647 (MAXINT32) for infinity.") +#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_REQUIRED_TIMEOUT N_("The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. Note that if \"may-fail\" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero).") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTE_METRIC N_("The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTE_TABLE N_("Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTES N_("Array of IP routes.") diff --git a/src/libnmc-setting/settings-docs.h.in b/src/libnmc-setting/settings-docs.h.in index da4f6b4c..12625d44 100644 --- a/src/libnmc-setting/settings-docs.h.in +++ b/src/libnmc-setting/settings-docs.h.in @@ -226,7 +226,7 @@ #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_REMOTE N_("The remote endpoint of the tunnel; the value must contain an IPv4 or IPv6 address.") #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_TOS N_("The type of service (IPv4) or traffic class (IPv6) field to be set on tunneled packets.") #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_TTL N_("The TTL to assign to tunneled packets. 0 is a special value meaning that packets inherit the TTL value.") -#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ADDRESSES N_("A list of IPv4 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"192.168.1.5/24, 10.1.0.5/24\".") +#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ADDRESSES N_("A list of IPv4 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"192.168.1.5/24, 10.1.0.5/24\". The addresses are listed in increasing priority, meaning the last address will be the primary address.") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DAD_TIMEOUT N_("Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4.") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID N_("A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string ('aa:bb:cc') it is interpreted as a binary client ID, in which case the first byte is assumed to be the 'type' field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the 'type' field is set to 0. The special values \"mac\" and \"perm-mac\" are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. The special value \"ipv6-duid\" uses the DUID from \"ipv6.dhcp-duid\" property as an RFC4361-compliant client identifier. As IAID it uses \"ipv4.dhcp-iaid\" and falls back to \"ipv6.dhcp-iaid\" if unset. The special value \"duid\" generates a RFC4361-compliant client identifier based on \"ipv4.dhcp-iaid\" and uses a DUID generated by hashing /etc/machine-id. The special value \"stable\" is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the \"${DEVICE}\" or \"${MAC}\" specifier to get a per-device key. If unset, a globally configured default is used. If still unset, the default depends on the DHCP plugin.") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_FQDN N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified FQDN will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-hostname\" are mutually exclusive and cannot be set at the same time.") @@ -247,11 +247,12 @@ #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_MAY_FAIL N_("If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out. Note that at least one IP configuration must succeed or overall network configuration will still fail. For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully.") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_METHOD N_("IP configuration method. NMSettingIP4Config and NMSettingIP6Config both support \"disabled\", \"auto\", \"manual\", and \"link-local\". See the subclass-specific documentation for other values. In general, for the \"auto\" method, properties such as \"dns\" and \"routes\" specify information that is added on to the information returned from automatic configuration. The \"ignore-auto-routes\" and \"ignore-auto-dns\" properties modify this behavior. For methods that imply no upstream network, such as \"shared\" or \"link-local\", these properties must be empty. For IPv4 method \"shared\", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared.") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_NEVER_DEFAULT N_("If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager.") +#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_REQUIRED_TIMEOUT N_("The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. Note that if \"may-fail\" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero).") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTE_METRIC N_("The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric.") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTE_TABLE N_("Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager.") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTES N_("A list of IPv4 destination addresses, prefix length, optional IPv4 next hop addresses, optional route metric, optional attribute. The valid syntax is: \"ip[/prefix] [next-hop] [metric] [attribute=val]...[,ip[/prefix]...]\". For example \"192.0.2.0/24 10.1.1.1 77, 198.51.100.0/24\".") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE N_("Configure method for creating the address for use with RFC4862 IPv6 Stateless Address Autoconfiguration. The permitted values are: NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 (0) or NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY (1). If the property is set to EUI64, the addresses will be generated using the interface tokens derived from hardware address. This makes the host part of the address to stay constant, making it possible to track host's presence when it changes networks. The address changes when the interface hardware is replaced. The value of stable-privacy enables use of cryptographically secure hash of a secret host-specific key along with the connection's stable-id and the network address as specified by RFC7217. This makes it impossible to use the address track host's presence, and makes the address stable when the network interface hardware is replaced. On D-Bus, the absence of an addr-gen-mode setting equals enabling stable-privacy. For keyfile plugin, the absence of the setting on disk means EUI64 so that the property doesn't change on upgrade from older versions. Note that this setting is distinct from the Privacy Extensions as configured by \"ip6-privacy\" property and it does not affect the temporary addresses configured with this option.") -#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDRESSES N_("Array of IP addresses.") +#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDRESSES N_("A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64\". The addresses are listed in increasing priority, meaning the last address will be the primary address.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DAD_TIMEOUT N_("Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_DUID N_("A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp client to identify itself to DHCPv6 servers (RFC 3315). The DUID is carried in the Client Identifier option. If the property is a hex string ('aa:bb:cc') it is interpreted as a binary DUID and filled as an opaque value in the Client Identifier option. The special value \"lease\" will retrieve the DUID previously used from the lease file belonging to the connection. If no DUID is found and \"dhclient\" is the configured dhcp client, the DUID is searched in the system-wide dhclient lease file. If still no DUID is found, or another dhcp client is used, a global and permanent DUID-UUID (RFC 6355) will be generated based on the machine-id. The special values \"llt\" and \"ll\" will generate a DUID of type LLT or LL (see RFC 3315) based on the current MAC address of the device. In order to try providing a stable DUID-LLT, the time field will contain a constant timestamp that is used globally (for all profiles) and persisted to disk. The special values \"stable-llt\", \"stable-ll\" and \"stable-uuid\" will generate a DUID of the corresponding type, derived from the connection's stable-id and a per-host unique key. You may want to include the \"${DEVICE}\" or \"${MAC}\" specifier in the stable-id, in case this profile gets activated on multiple devices. So, the link-layer address of \"stable-ll\" and \"stable-llt\" will be a generated address derived from the stable id. The DUID-LLT time value in the \"stable-llt\" option will be picked among a static timespan of three years (the upper bound of the interval is the same constant timestamp used in \"llt\"). When the property is unset, the global value provided for \"ipv6.dhcp-duid\" is used. If no global value is provided, the default \"lease\" value is assumed.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-fqdn\" are mutually exclusive and cannot be set at the same time.") @@ -272,6 +273,7 @@ #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_METHOD N_("IP configuration method. NMSettingIP4Config and NMSettingIP6Config both support \"disabled\", \"auto\", \"manual\", and \"link-local\". See the subclass-specific documentation for other values. In general, for the \"auto\" method, properties such as \"dns\" and \"routes\" specify information that is added on to the information returned from automatic configuration. The \"ignore-auto-routes\" and \"ignore-auto-dns\" properties modify this behavior. For methods that imply no upstream network, such as \"shared\" or \"link-local\", these properties must be empty. For IPv4 method \"shared\", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_NEVER_DEFAULT N_("If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_RA_TIMEOUT N_("A timeout for waiting Router Advertisements in seconds. If zero (the default), a globally configured default is used. If still unspecified, the timeout depends on the sysctl settings of the device. Set to 2147483647 (MAXINT32) for infinity.") +#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_REQUIRED_TIMEOUT N_("The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. Note that if \"may-fail\" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero).") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTE_METRIC N_("The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTE_TABLE N_("Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTES N_("Array of IP routes.") diff --git a/src/libnmc-setting/tests/meson.build b/src/libnmc-setting/tests/meson.build index 6c71f286..0ce39a94 100644 --- a/src/libnmc-setting/tests/meson.build +++ b/src/libnmc-setting/tests/meson.build @@ -13,6 +13,7 @@ exe = executable( libnm_core_aux_extern, libnm_core_aux_intern, libnm_base, + libnm_log_null, libnm_glib_aux, libnm_std_aux, libc_siphash, diff --git a/src/n-dhcp4/src/n-dhcp4-c-lease.c b/src/n-dhcp4/src/n-dhcp4-c-lease.c index 515814d4..bae2f674 100644 --- a/src/n-dhcp4/src/n-dhcp4-c-lease.c +++ b/src/n-dhcp4/src/n-dhcp4-c-lease.c @@ -249,9 +249,11 @@ _c_public_ void n_dhcp4_client_lease_get_lifetime(NDhcp4ClientLease *lease, uint * Gets the address contained in the server-identifier DHCP option, in network * byte order. * - * Return: 0 on success, negative error code on failure. + * Return: 0 on success, + * N_DHCP4_E_UNSET if the lease doesn't contain a server-identifier, or + * N_DCHP4_E_INTERNAL if the server-identifier is not valid. */ -_c_public_ int n_dhcp4_client_lease_get_server_identifier (NDhcp4ClientLease *lease, struct in_addr *addr) { +_c_public_ int n_dhcp4_client_lease_get_server_identifier(NDhcp4ClientLease *lease, struct in_addr *addr) { uint8_t *data; size_t n_data; int r; @@ -260,13 +262,49 @@ _c_public_ int n_dhcp4_client_lease_get_server_identifier (NDhcp4ClientLease *le if (r) return r; if (n_data < sizeof(struct in_addr)) - return N_DHCP4_E_MALFORMED; + return N_DHCP4_E_INTERNAL; memcpy(addr, data, sizeof(struct in_addr)); return 0; } +/** + * n_dhcp4_client_lease_get_file() - query the lease for the boot file name + * @lease: the lease to operate on + * @file: return argument for the file name + * + * Query the lease for the boot file name from the DHCP header. The file name + * is returned as a NULL-terminated string. + * + * Return: 0 on success, + * N_DHCP4_E_UNSET if the lease does not contain a file name, or + * N_DCHP4_E_INTERNAL if the file name is invalid. + */ +_c_public_ int n_dhcp4_client_lease_get_file(NDhcp4ClientLease *lease, const char **file) { + NDhcp4Message *message; + + if (lease->message->options[N_DHCP4_OPTION_OVERLOAD].size > 0 + && ((*lease->message->options[N_DHCP4_OPTION_OVERLOAD].value) & N_DHCP4_OVERLOAD_FILE)) { + /* The field is overloaded to contain other options */ + return N_DHCP4_E_UNSET; + } + + message = &lease->message->message; + + if (message->file[0] == '\0') + return N_DHCP4_E_UNSET; + + if (!memchr(message->file, '\0', sizeof(message->file))) { + /* The field is NULL-terminated (RFC 2131 section 2) */ + return N_DHCP4_E_INTERNAL; + } + + *file = (const char *) message->file; + + return 0; +} + /** * n_dhcp4_client_lease_query() - query the lease for an option * @lease: the lease to operate on diff --git a/src/n-dhcp4/src/n-dhcp4.h b/src/n-dhcp4/src/n-dhcp4.h index 435c5600..8493a487 100644 --- a/src/n-dhcp4/src/n-dhcp4.h +++ b/src/n-dhcp4/src/n-dhcp4.h @@ -171,7 +171,8 @@ void n_dhcp4_client_lease_get_siaddr(NDhcp4ClientLease *lease, struct in_addr *s void n_dhcp4_client_lease_get_basetime(NDhcp4ClientLease *lease, uint64_t *ns_basetimep); void n_dhcp4_client_lease_get_lifetime(NDhcp4ClientLease *lease, uint64_t *ns_lifetimep); int n_dhcp4_client_lease_query(NDhcp4ClientLease *lease, uint8_t option, uint8_t **datap, size_t *n_datap); -int n_dhcp4_client_lease_get_server_identifier (NDhcp4ClientLease *lease, struct in_addr *addr); +int n_dhcp4_client_lease_get_server_identifier(NDhcp4ClientLease *lease, struct in_addr *addr); +int n_dhcp4_client_lease_get_file(NDhcp4ClientLease *lease, const char **file); int n_dhcp4_client_lease_select(NDhcp4ClientLease *lease); int n_dhcp4_client_lease_accept(NDhcp4ClientLease *lease); diff --git a/src/nm-cloud-setup/main.c b/src/nm-cloud-setup/main.c index 0667532b..8dc67851 100644 --- a/src/nm-cloud-setup/main.c +++ b/src/nm-cloud-setup/main.c @@ -8,6 +8,7 @@ #include "nmcs-provider-ec2.h" #include "nmcs-provider-gcp.h" #include "nmcs-provider-azure.h" +#include "nmcs-provider-aliyun.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h" /*****************************************************************************/ @@ -85,6 +86,7 @@ _provider_detect(GCancellable *sigterm_cancellable) NMCS_TYPE_PROVIDER_EC2, NMCS_TYPE_PROVIDER_GCP, NMCS_TYPE_PROVIDER_AZURE, + NMCS_TYPE_PROVIDER_ALIYUN, }; int i; gulong cancellable_signal_id; @@ -338,11 +340,13 @@ _nmc_mangle_connection(NMDevice * device, if (entry) g_ptr_array_add(addrs_new, entry); } - - gateway = nm_utils_ip4_address_clear_host_address(config_data->cidr_addr, - config_data->cidr_prefix); - ((guint8 *) &gateway)[3] += 1; - + if (config_data->has_gateway && config_data->gateway) { + gateway = config_data->gateway; + } else { + gateway = nm_utils_ip4_address_clear_host_address(config_data->cidr_addr, + config_data->cidr_prefix); + ((guint8 *) &gateway)[3] += 1; + } rt_metric = 10; rt_table = 30400 + config_data->iface_idx; diff --git a/src/nm-cloud-setup/meson.build b/src/nm-cloud-setup/meson.build index 624a1043..ea4ad113 100644 --- a/src/nm-cloud-setup/meson.build +++ b/src/nm-cloud-setup/meson.build @@ -29,6 +29,7 @@ libnm_cloud_setup_core = static_library( 'nmcs-provider-ec2.c', 'nmcs-provider-gcp.c', 'nmcs-provider-azure.c', + 'nmcs-provider-aliyun.c', 'nmcs-provider.c', ), dependencies: [ diff --git a/src/nm-cloud-setup/nm-cloud-setup-utils.c b/src/nm-cloud-setup/nm-cloud-setup-utils.c index 1cb14a83..ed36b4dd 100644 --- a/src/nm-cloud-setup/nm-cloud-setup-utils.c +++ b/src/nm-cloud-setup/nm-cloud-setup-utils.c @@ -665,6 +665,20 @@ nmcs_utils_uri_build_concat_v(const char *base, const char **components, gsize n return nm_str_buf_finalize(&strbuf, NULL); } +const char * +nmcs_utils_uri_complete_interned(const char *uri) +{ + gs_free char *s = NULL; + + if (nm_str_is_empty(uri)) + return NULL; + if (NM_STR_HAS_PREFIX(uri, "http://") || NM_STR_HAS_PREFIX(uri, "https://") || strchr(uri, '/')) + return g_intern_string(uri); + + s = g_strconcat("http://", uri, NULL); + return g_intern_string(s); +} + /*****************************************************************************/ gboolean diff --git a/src/nm-cloud-setup/nm-cloud-setup-utils.h b/src/nm-cloud-setup/nm-cloud-setup-utils.h index d8631701..3ca7298f 100644 --- a/src/nm-cloud-setup/nm-cloud-setup-utils.h +++ b/src/nm-cloud-setup/nm-cloud-setup-utils.h @@ -121,6 +121,8 @@ char *nmcs_utils_uri_build_concat_v(const char *base, const char **components, g #define nmcs_utils_uri_build_concat(base, ...) \ nmcs_utils_uri_build_concat_v(base, ((const char *[]){__VA_ARGS__}), NM_NARG(__VA_ARGS__)) +const char *nmcs_utils_uri_complete_interned(const char *uri); + /*****************************************************************************/ gboolean nmcs_setting_ip_replace_ipv4_addresses(NMSettingIPConfig *s_ip, diff --git a/src/nm-cloud-setup/nm-cloud-setup.service.in b/src/nm-cloud-setup/nm-cloud-setup.service.in index 809f707d..f4b0e263 100644 --- a/src/nm-cloud-setup/nm-cloud-setup.service.in +++ b/src/nm-cloud-setup/nm-cloud-setup.service.in @@ -18,6 +18,7 @@ ExecStart=@libexecdir@/nm-cloud-setup #Environment=NM_CLOUD_SETUP_EC2=yes #Environment=NM_CLOUD_SETUP_GCP=yes #Environment=NM_CLOUD_SETUP_AZURE=yes +#Environment=NM_CLOUD_SETUP_ALIYUN=yes CapabilityBoundingSet= LockPersonality=yes diff --git a/src/nm-cloud-setup/nmcs-provider-aliyun.c b/src/nm-cloud-setup/nmcs-provider-aliyun.c new file mode 100644 index 00000000..01a4af0f --- /dev/null +++ b/src/nm-cloud-setup/nmcs-provider-aliyun.c @@ -0,0 +1,511 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmcs-provider-aliyun.h" + +#include + +#include "nm-cloud-setup-utils.h" + +/*****************************************************************************/ + +#define HTTP_TIMEOUT_MS 3000 + +#define NM_ALIYUN_HOST "100.100.100.200" +#define NM_ALIYUN_BASE "http://" NM_ALIYUN_HOST +#define NM_ALIYUN_API_VERSION "2016-01-01" +#define NM_ALIYUN_METADATA_URL_BASE /* $NM_ALIYUN_BASE/$NM_ALIYUN_API_VERSION */ \ + "/meta-data/network/interfaces/macs/" + +static const char * +_aliyun_base(void) +{ + static const char *base_cached = NULL; + const char * base; + +again: + base = g_atomic_pointer_get(&base_cached); + if (G_UNLIKELY(!base)) { + /* The base URI can be set via environment variable. + * This is mainly for testing, it's not usually supposed to be configured. + * Consider this private API! */ + base = g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_ALIYUN_HOST")); + + if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base)) + goto again; + } + base = nmcs_utils_uri_complete_interned(base) ?: ("" NM_ALIYUN_BASE); + return base; +} + +#define _aliyun_uri_concat(...) nmcs_utils_uri_build_concat(_aliyun_base(), __VA_ARGS__) +#define _aliyun_uri_interfaces(...) \ + _aliyun_uri_concat(NM_ALIYUN_API_VERSION, NM_ALIYUN_METADATA_URL_BASE, ##__VA_ARGS__) + +/*****************************************************************************/ + +struct _NMCSProviderAliyun { + NMCSProvider parent; +}; + +struct _NMCSProviderAliyunClass { + NMCSProviderClass parent; +}; + +G_DEFINE_TYPE(NMCSProviderAliyun, nmcs_provider_aliyun, NMCS_TYPE_PROVIDER); + +/*****************************************************************************/ + +static void +filter_chars(char *str, const char *chars) +{ + gsize i; + gsize j; + + for (i = 0, j = 0; str[i]; i++) { + if (!strchr(chars, str[i])) + str[j++] = str[i]; + } + str[j] = '\0'; +} + +static void +_detect_get_meta_data_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + gs_unref_object GTask *task = user_data; + gs_free_error GError *get_error = NULL; + gs_free_error GError *error = NULL; + + nm_http_client_poll_get_finish(NM_HTTP_CLIENT(source), result, NULL, NULL, &get_error); + + if (nm_utils_error_is_cancelled(get_error)) { + g_task_return_error(task, g_steal_pointer(&get_error)); + return; + } + + if (get_error) { + nm_utils_error_set(&error, + NM_UTILS_ERROR_UNKNOWN, + "failure to get ALIYUN metadata: %s", + get_error->message); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + g_task_return_boolean(task, TRUE); +} + +static void +detect(NMCSProvider *provider, GTask *task) +{ + NMHttpClient *http_client; + gs_free char *uri = NULL; + + http_client = nmcs_provider_get_http_client(provider); + + nm_http_client_poll_get(http_client, + (uri = _aliyun_uri_concat(NM_ALIYUN_API_VERSION "/meta-data/")), + HTTP_TIMEOUT_MS, + 256 * 1024, + 7000, + 1000, + NULL, + g_task_get_cancellable(task), + NULL, + NULL, + _detect_get_meta_data_done_cb, + task); +} + +/*****************************************************************************/ + +typedef enum { + GET_CONFIG_FETCH_DONE_TYPE_SUBNET_VPC_CIDR_BLOCK, + GET_CONFIG_FETCH_DONE_TYPE_PRIVATE_IPV4S, + GET_CONFIG_FETCH_DONE_TYPE_NETMASK, + GET_CONFIG_FETCH_DONE_TYPE_GATEWAY, +} GetConfigFetchDoneType; + +static void +_get_config_fetch_done_cb(NMHttpClient * http_client, + GAsyncResult * result, + gpointer user_data, + GetConfigFetchDoneType fetch_type) +{ + NMCSProviderGetConfigTaskData *get_config_data; + const char * hwaddr = NULL; + gs_unref_bytes GBytes *response = NULL; + gs_free_error GError * error = NULL; + NMCSProviderGetConfigIfaceData *config_iface_data; + in_addr_t tmp_addr; + int tmp_prefix; + in_addr_t netmask_bin; + in_addr_t gateway_bin; + gs_free const char ** s_addrs = NULL; + gsize i; + gsize len; + + nm_utils_user_data_unpack(user_data, &get_config_data, &hwaddr); + + nm_http_client_poll_get_finish(http_client, result, NULL, &response, &error); + + if (nm_utils_error_is_cancelled(error)) + return; + + if (error) + goto out; + + config_iface_data = g_hash_table_lookup(get_config_data->result_dict, hwaddr); + + switch (fetch_type) { + case GET_CONFIG_FETCH_DONE_TYPE_PRIVATE_IPV4S: + + s_addrs = nm_utils_strsplit_set_full(g_bytes_get_data(response, NULL), + ",", + NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP); + len = NM_PTRARRAY_LEN(s_addrs); + nm_assert(!config_iface_data->has_ipv4s); + nm_assert(!config_iface_data->ipv4s_arr); + config_iface_data->has_ipv4s = TRUE; + config_iface_data->ipv4s_len = 0; + if (len > 0) { + config_iface_data->ipv4s_arr = g_new(in_addr_t, len); + for (i = 0; i < len; i++) { + filter_chars((char *) s_addrs[i], "[]\""); + if (nm_utils_parse_inaddr_bin(AF_INET, s_addrs[i], NULL, &tmp_addr)) { + config_iface_data->ipv4s_arr[config_iface_data->ipv4s_len++] = tmp_addr; + } + } + } + break; + + case GET_CONFIG_FETCH_DONE_TYPE_SUBNET_VPC_CIDR_BLOCK: + + if (nm_utils_parse_inaddr_prefix_bin(AF_INET, + g_bytes_get_data(response, NULL), + NULL, + &tmp_addr, + &tmp_prefix)) { + nm_assert(!config_iface_data->has_cidr); + config_iface_data->has_cidr = TRUE; + config_iface_data->cidr_addr = tmp_addr; + } + break; + + case GET_CONFIG_FETCH_DONE_TYPE_NETMASK: + + if (nm_utils_parse_inaddr_bin(AF_INET, + g_bytes_get_data(response, NULL), + NULL, + &netmask_bin)) { + config_iface_data->cidr_prefix = nm_utils_ip4_netmask_to_prefix(netmask_bin); + }; + break; + + case GET_CONFIG_FETCH_DONE_TYPE_GATEWAY: + + if (nm_utils_parse_inaddr_bin(AF_INET, + g_bytes_get_data(response, NULL), + NULL, + &gateway_bin)) { + config_iface_data->has_gateway = TRUE; + config_iface_data->gateway = gateway_bin; + }; + break; + } + +out: + get_config_data->n_pending--; + _nmcs_provider_get_config_task_maybe_return(get_config_data, g_steal_pointer(&error)); +} + +static void +_get_config_fetch_done_cb_vpc_cidr_block(GObject *source, GAsyncResult *result, gpointer user_data) +{ + _get_config_fetch_done_cb(NM_HTTP_CLIENT(source), + result, + user_data, + GET_CONFIG_FETCH_DONE_TYPE_SUBNET_VPC_CIDR_BLOCK); +} + +static void +_get_config_fetch_done_cb_private_ipv4s(GObject *source, GAsyncResult *result, gpointer user_data) +{ + _get_config_fetch_done_cb(NM_HTTP_CLIENT(source), + result, + user_data, + GET_CONFIG_FETCH_DONE_TYPE_PRIVATE_IPV4S); +} + +static void +_get_config_fetch_done_cb_netmask(GObject *source, GAsyncResult *result, gpointer user_data) +{ + _get_config_fetch_done_cb(NM_HTTP_CLIENT(source), + result, + user_data, + GET_CONFIG_FETCH_DONE_TYPE_NETMASK); +} + +static void +_get_config_fetch_done_cb_gateway(GObject *source, GAsyncResult *result, gpointer user_data) +{ + _get_config_fetch_done_cb(NM_HTTP_CLIENT(source), + result, + user_data, + GET_CONFIG_FETCH_DONE_TYPE_GATEWAY); +} + +typedef struct { + gssize iface_idx; + char path[0]; +} GetConfigMetadataMac; + +static void +_get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMCSProviderGetConfigTaskData *get_config_data; + gs_unref_hashtable GHashTable *response_parsed = NULL; + gs_free_error GError *error = NULL; + GetConfigMetadataMac *v_mac_data; + const char * v_hwaddr; + GHashTableIter h_iter; + NMHttpClient * http_client; + + nm_http_client_poll_get_finish(NM_HTTP_CLIENT(source), result, NULL, NULL, &error); + + if (nm_utils_error_is_cancelled(error)) + return; + + get_config_data = user_data; + + response_parsed = g_steal_pointer(&get_config_data->extra_data); + get_config_data->extra_data_destroy = NULL; + + /* We ignore errors. Only if we got no response at all, it's a problem. + * Otherwise, we proceed with whatever we could fetch. */ + if (!response_parsed) { + _nmcs_provider_get_config_task_maybe_return( + get_config_data, + nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "meta data for interfaces not found")); + return; + } + + http_client = nmcs_provider_get_http_client(g_task_get_source_object(get_config_data->task)); + + g_hash_table_iter_init(&h_iter, response_parsed); + while (g_hash_table_iter_next(&h_iter, (gpointer *) &v_hwaddr, (gpointer *) &v_mac_data)) { + NMCSProviderGetConfigIfaceData *config_iface_data; + gs_free char * uri1 = NULL; + gs_free char * uri2 = NULL; + gs_free char * uri3 = NULL; + gs_free char * uri4 = NULL; + const char * hwaddr; + + if (!g_hash_table_lookup_extended(get_config_data->result_dict, + v_hwaddr, + (gpointer *) &hwaddr, + (gpointer *) &config_iface_data)) { + if (!get_config_data->any) { + _LOGD("get-config: skip fetching meta data for %s (%s)", + v_hwaddr, + v_mac_data->path); + continue; + } + config_iface_data = nmcs_provider_get_config_iface_data_new(FALSE); + g_hash_table_insert(get_config_data->result_dict, + (char *) (hwaddr = g_strdup(v_hwaddr)), + config_iface_data); + } + + nm_assert(config_iface_data->iface_idx == -1); + + config_iface_data->iface_idx = v_mac_data->iface_idx; + + _LOGD("get-config: start fetching meta data for #%" G_GSSIZE_FORMAT ", %s (%s)", + config_iface_data->iface_idx, + hwaddr, + v_mac_data->path); + + get_config_data->n_pending++; + nm_http_client_poll_get( + http_client, + (uri1 = _aliyun_uri_interfaces(v_mac_data->path, + NM_STR_HAS_SUFFIX(v_mac_data->path, "/") ? "" : "/", + "vpc-cidr-block")), + HTTP_TIMEOUT_MS, + 512 * 1024, + 10000, + 1000, + NULL, + get_config_data->intern_cancellable, + NULL, + NULL, + _get_config_fetch_done_cb_vpc_cidr_block, + nm_utils_user_data_pack(get_config_data, hwaddr)); + + get_config_data->n_pending++; + nm_http_client_poll_get( + http_client, + (uri2 = _aliyun_uri_interfaces(v_mac_data->path, + NM_STR_HAS_SUFFIX(v_mac_data->path, "/") ? "" : "/", + "private-ipv4s")), + HTTP_TIMEOUT_MS, + 512 * 1024, + 10000, + 1000, + NULL, + get_config_data->intern_cancellable, + NULL, + NULL, + _get_config_fetch_done_cb_private_ipv4s, + nm_utils_user_data_pack(get_config_data, hwaddr)); + + get_config_data->n_pending++; + nm_http_client_poll_get( + http_client, + (uri3 = _aliyun_uri_interfaces(v_mac_data->path, + NM_STR_HAS_SUFFIX(v_mac_data->path, "/") ? "" : "/", + "netmask")), + HTTP_TIMEOUT_MS, + 512 * 1024, + 10000, + 1000, + NULL, + get_config_data->intern_cancellable, + NULL, + NULL, + _get_config_fetch_done_cb_netmask, + nm_utils_user_data_pack(get_config_data, hwaddr)); + + get_config_data->n_pending++; + nm_http_client_poll_get( + http_client, + (uri4 = _aliyun_uri_interfaces(v_mac_data->path, + NM_STR_HAS_SUFFIX(v_mac_data->path, "/") ? "" : "/", + "gateway")), + HTTP_TIMEOUT_MS, + 512 * 1024, + 10000, + 1000, + NULL, + get_config_data->intern_cancellable, + NULL, + NULL, + _get_config_fetch_done_cb_gateway, + nm_utils_user_data_pack(get_config_data, hwaddr)); + } + + _nmcs_provider_get_config_task_maybe_return(get_config_data, NULL); +} + +static gboolean +_get_config_metadata_ready_check(long response_code, + GBytes * response, + gpointer check_user_data, + GError **error) +{ + NMCSProviderGetConfigTaskData *get_config_data = check_user_data; + gs_unref_hashtable GHashTable *response_parsed = NULL; + const guint8 * r_data; + const char * cur_line; + gsize r_len; + gsize cur_line_len; + GHashTableIter h_iter; + gboolean has_all; + const char * c_hwaddr; + gssize iface_idx_counter = 0; + + if (response_code != 200 || !response) { + /* we wait longer. */ + return FALSE; + } + + r_data = g_bytes_get_data(response, &r_len); + /* NMHttpClient guarantees that there is a trailing NUL after the data. */ + nm_assert(r_data[r_len] == 0); + + while (nm_utils_parse_next_line((const char **) &r_data, &r_len, &cur_line, &cur_line_len)) { + GetConfigMetadataMac *mac_data; + char * hwaddr; + + if (cur_line_len == 0) + continue; + + /* Truncate the string. It's safe to do, because we own @response an it has an + * extra NUL character after the buffer. */ + ((char *) cur_line)[cur_line_len] = '\0'; + + hwaddr = nmcs_utils_hwaddr_normalize( + cur_line, + cur_line[cur_line_len - 1u] == '/' ? (gssize) (cur_line_len - 1u) : -1); + if (!hwaddr) + continue; + + if (!response_parsed) + response_parsed = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); + + mac_data = g_malloc(sizeof(GetConfigMetadataMac) + 1u + cur_line_len); + mac_data->iface_idx = iface_idx_counter++; + memcpy(mac_data->path, cur_line, cur_line_len + 1u); + + /* here we will ignore duplicate responses. */ + g_hash_table_insert(response_parsed, hwaddr, mac_data); + } + + has_all = TRUE; + g_hash_table_iter_init(&h_iter, get_config_data->result_dict); + while (g_hash_table_iter_next(&h_iter, (gpointer *) &c_hwaddr, NULL)) { + if (!response_parsed || !g_hash_table_contains(response_parsed, c_hwaddr)) { + has_all = FALSE; + break; + } + } + + nm_clear_pointer(&get_config_data->extra_data, g_hash_table_unref); + if (response_parsed) { + get_config_data->extra_data = g_steal_pointer(&response_parsed); + get_config_data->extra_data_destroy = (GDestroyNotify) g_hash_table_unref; + } + return has_all; +} + +static void +get_config(NMCSProvider *provider, NMCSProviderGetConfigTaskData *get_config_data) +{ + gs_free char *uri = NULL; + + /* First we fetch the "macs/". If the caller requested some particular + * MAC addresses, then we poll until we see them. They might not yet be + * around from the start... + */ + nm_http_client_poll_get(nmcs_provider_get_http_client(provider), + (uri = _aliyun_uri_interfaces()), + HTTP_TIMEOUT_MS, + 256 * 1024, + 15000, + 1000, + NULL, + get_config_data->intern_cancellable, + _get_config_metadata_ready_check, + get_config_data, + _get_config_metadata_ready_cb, + get_config_data); +} + +/*****************************************************************************/ + +static void +nmcs_provider_aliyun_init(NMCSProviderAliyun *self) +{} + +static void +nmcs_provider_aliyun_class_init(NMCSProviderAliyunClass *klass) +{ + NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS(klass); + + provider_class->_name = "aliyun"; + provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_ALIYUN"); + provider_class->detect = detect; + provider_class->get_config = get_config; +} diff --git a/src/nm-cloud-setup/nmcs-provider-aliyun.h b/src/nm-cloud-setup/nmcs-provider-aliyun.h new file mode 100644 index 00000000..6e733a11 --- /dev/null +++ b/src/nm-cloud-setup/nmcs-provider-aliyun.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef __NMCS_PROVIDER_ALIYUN_H__ +#define __NMCS_PROVIDER_ALIYUN_H__ + +#include "nmcs-provider.h" + +/*****************************************************************************/ + +typedef struct _NMCSProviderAliyun NMCSProviderAliyun; +typedef struct _NMCSProviderAliyunClass NMCSProviderAliyunClass; + +#define NMCS_TYPE_PROVIDER_ALIYUN (nmcs_provider_aliyun_get_type()) +#define NMCS_PROVIDER_ALIYUN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMCS_TYPE_PROVIDER_ALIYUN, NMCSProviderAliyun)) +#define NMCS_PROVIDER_ALIYUN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMCS_TYPE_PROVIDER_ALIYUN, NMCSProviderAliyunClass)) +#define NMCS_IS_PROVIDER_ALIYUN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMCS_TYPE_PROVIDER_ALIYUN)) +#define NMCS_IS_PROVIDER_ALIYUN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NMCS_TYPE_PROVIDER_ALIYUN)) +#define NMCS_PROVIDER_ALIYUN_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMCS_TYPE_PROVIDER_ALIYUN, NMCSProviderAliyunClass)) + +GType nmcs_provider_aliyun_get_type(void); + +/*****************************************************************************/ + +#endif /* __NMCS_PROVIDER_ALIYUN_H__ */ diff --git a/src/nm-cloud-setup/nmcs-provider-azure.c b/src/nm-cloud-setup/nmcs-provider-azure.c index 9ced8c45..69785d64 100644 --- a/src/nm-cloud-setup/nmcs-provider-azure.c +++ b/src/nm-cloud-setup/nmcs-provider-azure.c @@ -41,10 +41,8 @@ _detect_get_meta_data_done_cb(GObject *source, GAsyncResult *result, gpointer us gs_unref_object GTask *task = user_data; gs_free_error GError *get_error = NULL; gs_free_error GError *error = NULL; - gboolean success; - success = - nm_http_client_poll_get_finish(NM_HTTP_CLIENT(source), result, NULL, NULL, &get_error); + nm_http_client_poll_get_finish(NM_HTTP_CLIENT(source), result, NULL, NULL, &get_error); if (nm_utils_error_is_cancelled(get_error)) { g_task_return_error(task, g_steal_pointer(&get_error)); @@ -60,12 +58,6 @@ _detect_get_meta_data_done_cb(GObject *source, GAsyncResult *result, gpointer us return; } - if (!success) { - nm_utils_error_set(&error, NM_UTILS_ERROR_UNKNOWN, "failure to detect azure metadata"); - g_task_return_error(task, g_steal_pointer(&error)); - return; - } - g_task_return_boolean(task, TRUE); } diff --git a/src/nm-cloud-setup/nmcs-provider-ec2.c b/src/nm-cloud-setup/nmcs-provider-ec2.c index 14b0b740..6f83238d 100644 --- a/src/nm-cloud-setup/nmcs-provider-ec2.c +++ b/src/nm-cloud-setup/nmcs-provider-ec2.c @@ -29,21 +29,7 @@ again: * This is mainly for testing, it's not usually supposed to be configured. * Consider this private API! */ base = g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_EC2_HOST")); - - if (base && base[0] && !strchr(base, '/')) { - if (NM_STR_HAS_PREFIX(base, "http://") || NM_STR_HAS_PREFIX(base, "https://")) - base = g_intern_string(base); - else { - gs_free char *s = NULL; - - s = g_strconcat("http://", base, NULL); - base = g_intern_string(s); - } - } - if (!base) - base = NM_EC2_BASE; - - nm_assert(!NM_STR_HAS_SUFFIX(base, "/")); + base = nmcs_utils_uri_complete_interned(base) ?: ("" NM_EC2_BASE); if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base)) goto again; diff --git a/src/nm-cloud-setup/nmcs-provider.h b/src/nm-cloud-setup/nmcs-provider.h index 3b0c2529..13212b8e 100644 --- a/src/nm-cloud-setup/nmcs-provider.h +++ b/src/nm-cloud-setup/nmcs-provider.h @@ -19,9 +19,11 @@ typedef struct { gssize iface_idx; in_addr_t cidr_addr; + in_addr_t gateway; guint8 cidr_prefix; bool has_ipv4s : 1; bool has_cidr : 1; + bool has_gateway : 1; NMIPRoute **iproutes_arr; gsize iproutes_len; diff --git a/src/nm-cloud-setup/tests/meson.build b/src/nm-cloud-setup/tests/meson.build index 77e25c81..c6881a57 100644 --- a/src/nm-cloud-setup/tests/meson.build +++ b/src/nm-cloud-setup/tests/meson.build @@ -11,6 +11,7 @@ exe = executable( link_with: [ libnm_cloud_setup_core, libnmc_base, + libnm_log_null, libnm_glib_aux, libnm_std_aux, libc_siphash, diff --git a/src/nm-dispatcher/meson.build b/src/nm-dispatcher/meson.build index 37ae9e82..eb3ea777 100644 --- a/src/nm-dispatcher/meson.build +++ b/src/nm-dispatcher/meson.build @@ -39,6 +39,7 @@ executable( link_with: [ libnm_core_aux_extern, libnm_dispatcher_core, + libnm_log_null, libnm_glib_aux, libnm_std_aux, libc_siphash, diff --git a/src/nm-dispatcher/tests/meson.build b/src/nm-dispatcher/tests/meson.build index ac96ef92..deaa8819 100644 --- a/src/nm-dispatcher/tests/meson.build +++ b/src/nm-dispatcher/tests/meson.build @@ -13,6 +13,7 @@ exe = executable( c_args: introspection_extra_cflags, link_with: [ libnm_dispatcher_core, + libnm_log_null, libnm_glib_aux, libnm_std_aux, libc_siphash, diff --git a/src/nm-initrd-generator/meson.build b/src/nm-initrd-generator/meson.build index 896ed3ec..080fe7e4 100644 --- a/src/nm-initrd-generator/meson.build +++ b/src/nm-initrd-generator/meson.build @@ -34,8 +34,8 @@ executable( libnm_platform, libnm_base, libnm_systemd_shared, - libnm_log_core, libnm_udev_aux, + libnm_log_core, libnm_glib_aux, libnm_std_aux, libc_siphash, diff --git a/src/nm-initrd-generator/nm-initrd-generator.c b/src/nm-initrd-generator/nm-initrd-generator.c index 490a4547..b78808b2 100644 --- a/src/nm-initrd-generator/nm-initrd-generator.c +++ b/src/nm-initrd-generator/nm-initrd-generator.c @@ -35,6 +35,14 @@ output_conn(gpointer key, gpointer value, gpointer user_data) gs_free char * data = NULL; gs_free_error GError *error = NULL; gsize len; + NMSetting * setting; + + setting = nm_setting_user_new(); + nm_connection_add_setting(connection, setting); + nm_setting_user_set_data(NM_SETTING_USER(setting), + NM_USER_TAG_ORIGIN, + "nm-initrd-generator", + NULL); if (!nm_connection_normalize(connection, NULL, NULL, &error)) goto err_out; diff --git a/src/nm-initrd-generator/nm-initrd-generator.h b/src/nm-initrd-generator/nm-initrd-generator.h index 56dcfd68..2ec52a01 100644 --- a/src/nm-initrd-generator/nm-initrd-generator.h +++ b/src/nm-initrd-generator/nm-initrd-generator.h @@ -9,7 +9,8 @@ #include "nm-connection.h" #include "nm-utils.h" -#define NMI_WAIT_DEVICE_TIMEOUT_MS 60000 +#define NMI_WAIT_DEVICE_TIMEOUT_MSEC 60000 +#define NMI_IP_REQUIRED_TIMEOUT_MSEC 20000 static inline int get_ip_address_family(const char *str, gboolean with_prefix) diff --git a/src/nm-initrd-generator/nmi-cmdline-reader.c b/src/nm-initrd-generator/nmi-cmdline-reader.c index 799fd5dc..5c42f83c 100644 --- a/src/nm-initrd-generator/nmi-cmdline-reader.c +++ b/src/nm-initrd-generator/nmi-cmdline-reader.c @@ -119,6 +119,8 @@ reader_create_connection(Reader * reader, reader->dhcp_timeout, NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER, reader->dhcp4_vci, + NM_SETTING_IP_CONFIG_REQUIRED_TIMEOUT, + NMI_IP_REQUIRED_TIMEOUT_MSEC, NULL); setting = nm_setting_ip6_config_new(); @@ -401,18 +403,19 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument) gs_unref_hashtable GHashTable *ibft = NULL; const char * tmp; const char * tmp2; - const char * kind = NULL; - const char * client_ip = NULL; - const char * peer = NULL; - const char * gateway_ip = NULL; - const char * netmask = NULL; - const char * client_hostname = NULL; - const char * iface_spec = NULL; - const char * mtu = NULL; - const char * macaddr = NULL; - int client_ip_family = AF_UNSPEC; - int client_ip_prefix = -1; - const char * dns[2] = { + const char * kind = NULL; + const char * client_ip = NULL; + const char * peer = NULL; + const char * gateway_ip = NULL; + const char * netmask = NULL; + const char * client_hostname = NULL; + const char * iface_spec = NULL; + const char * mtu = NULL; + const char * macaddr = NULL; + int client_ip_family = AF_UNSPEC; + int client_ip_prefix = -1; + gboolean clear_ip4_required_timeout = TRUE; + const char * dns[2] = { NULL, NULL, }; @@ -436,6 +439,7 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument) "none", "off", "dhcp", + "single-dhcp", "on" "any", "dhcp6", @@ -601,7 +605,7 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument) NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NULL); } - } else if (nm_streq0(kind, "dhcp")) { + } else if (NM_IN_STRSET(kind, "dhcp", "single-dhcp")) { g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, @@ -679,8 +683,13 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument) g_clear_error(&error); } } + } else { + clear_ip4_required_timeout = FALSE; } + if (clear_ip4_required_timeout) + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_REQUIRED_TIMEOUT, -1, NULL); + if (peer && *peer) _LOGW(LOGD_CORE, "Ignoring peer: %s (not implemented)\n", peer); @@ -907,6 +916,56 @@ reader_parse_vlan(Reader *reader, char *argument) g_ptr_array_add(reader->vlan_parents, g_strdup(phy)); } +static void +reader_parse_ib_pkey(Reader *reader, char *argument) +{ + NMConnection * connection; + NMSettingInfiniband *s_ib; + char * ifname; + gs_free char * parent = NULL; + char * pkey; + gint64 pkey_int; + + /* At the moment we only support ib.pkey=.; + * in the future we want to possibly support other options: + * ib.pkey=.: