diff options
| author | Michael Biebl <biebl@debian.org> | 2019-04-21 21:09:51 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2019-04-21 21:09:51 +0200 |
| commit | 85563b7fc7ec2cd21e38debb9b28db342e2e8e7c (patch) | |
| tree | cce7b0b02d28fae2df9fdf2c1804cacd1500f2d7 /src | |
| parent | 9a6dcbf895f9da01768e64b73cec88c16157d91e (diff) | |
New upstream version 1.18.0 upstream/1.18.0
Diffstat (limited to 'src')
129 files changed, 6240 insertions, 1697 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 71bfbf7c..13cb1dae 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -23,9 +23,11 @@ #include "NetworkManagerUtils.h" -#include "nm-utils/nm-c-list.h" +#include <linux/fib_rules.h> -#include "nm-common-macros.h" +#include "nm-glib-aux/nm-c-list.h" + +#include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-utils.h" #include "nm-setting-connection.h" #include "nm-setting-ip4-config.h" @@ -908,6 +910,48 @@ nm_match_spec_device_by_pllink (const NMPlatformLink *pllink, /*****************************************************************************/ +NMPlatformRoutingRule * +nm_ip_routing_rule_to_platform (const NMIPRoutingRule *rule, + NMPlatformRoutingRule *out_pl) +{ + nm_assert (rule); + nm_assert (nm_ip_routing_rule_validate (rule, NULL)); + nm_assert (out_pl); + + *out_pl = (NMPlatformRoutingRule) { + .addr_family = nm_ip_routing_rule_get_addr_family (rule), + .flags = ( nm_ip_routing_rule_get_invert (rule) + ? FIB_RULE_INVERT + : 0), + .priority = nm_ip_routing_rule_get_priority (rule), + .tos = nm_ip_routing_rule_get_tos (rule), + .ip_proto = nm_ip_routing_rule_get_ipproto (rule), + .fwmark = nm_ip_routing_rule_get_fwmark (rule), + .fwmask = nm_ip_routing_rule_get_fwmask (rule), + .sport_range = { + .start = nm_ip_routing_rule_get_source_port_start (rule), + .end = nm_ip_routing_rule_get_source_port_end (rule), + }, + .dport_range = { + .start = nm_ip_routing_rule_get_destination_port_start (rule), + .end = nm_ip_routing_rule_get_destination_port_end (rule), + }, + .src = *(nm_ip_routing_rule_get_from_bin (rule) ?: &nm_ip_addr_zero), + .dst = *(nm_ip_routing_rule_get_to_bin (rule) ?: &nm_ip_addr_zero), + .src_len = nm_ip_routing_rule_get_from_len (rule), + .dst_len = nm_ip_routing_rule_get_to_len (rule), + .action = nm_ip_routing_rule_get_action (rule), + .table = nm_ip_routing_rule_get_table (rule), + }; + + nm_ip_routing_rule_get_xifname_bin (rule, TRUE, out_pl->iifname); + nm_ip_routing_rule_get_xifname_bin (rule, FALSE, out_pl->oifname); + + return out_pl; +} + +/*****************************************************************************/ + struct _NMShutdownWaitObjHandle { CList lst; GObject *watched_obj; diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index 5e701224..2b1f5ed8 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -24,6 +24,9 @@ #include "nm-core-utils.h" +#include "nm-setting-ip-config.h" +#include "platform/nm-platform.h" + /*****************************************************************************/ const char *nm_utils_get_ip_config_method (NMConnection *connection, @@ -60,6 +63,11 @@ int nm_match_spec_device_by_pllink (const NMPlatformLink *pllink, /*****************************************************************************/ +NMPlatformRoutingRule *nm_ip_routing_rule_to_platform (const NMIPRoutingRule *rule, + NMPlatformRoutingRule *out_pl); + +/*****************************************************************************/ + /* during shutdown, there are two relevant timeouts. One is * NM_SHUTDOWN_TIMEOUT_MS which is plenty of time, that we give for all * actions to complete. Of course, during shutdown components should hurry diff --git a/src/devices/adsl/nm-atm-manager.c b/src/devices/adsl/nm-atm-manager.c index dddb8342..f312b5a3 100644 --- a/src/devices/adsl/nm-atm-manager.c +++ b/src/devices/adsl/nm-atm-manager.c @@ -27,7 +27,7 @@ #include "nm-device-adsl.h" #include "devices/nm-device-factory.h" #include "platform/nm-platform.h" -#include "nm-utils/nm-udev-utils.h" +#include "nm-udev-aux/nm-udev-utils.h" /*****************************************************************************/ diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c index dc85a8b2..9cd3a519 100644 --- a/src/devices/bluetooth/nm-bluez-manager.c +++ b/src/devices/bluetooth/nm-bluez-manager.c @@ -35,7 +35,7 @@ #include "nm-device-bt.h" #include "nm-core-internal.h" #include "platform/nm-platform.h" -#include "nm-dbus-compat.h" +#include "nm-std-aux/nm-dbus-compat.h" /*****************************************************************************/ diff --git a/src/devices/nm-acd-manager.c b/src/devices/nm-acd-manager.c index a8f7a63a..f437ce3a 100644 --- a/src/devices/nm-acd-manager.c +++ b/src/devices/nm-acd-manager.c @@ -113,6 +113,30 @@ acd_error_to_string (int error) g_return_val_if_reached (NULL); } +static int +acd_error_to_nmerr (int error, gboolean always_fail) +{ + if (error < 0) + return -nm_errno_native (error); + + if (always_fail) { + if (NM_IN_SET (error, N_ACD_E_PREEMPTED, + N_ACD_E_INVALID_ARGUMENT)) + return -NME_UNSPEC; + g_return_val_if_reached (-NME_UNSPEC); + } + + /* so, @error is either zero (indicating success) or one + * of the special status codes like N_ACD_E_*. In both cases, + * return the positive value here. */ + if (NM_IN_SET (error, _N_ACD_E_SUCCESS, + N_ACD_E_PREEMPTED, + N_ACD_E_INVALID_ARGUMENT)) + return error; + + g_return_val_if_reached (error); +} + /*****************************************************************************/ /** @@ -291,9 +315,9 @@ acd_init (NMAcdManager *self) * Start probing IP addresses for duplicates; when the probe terminates a * PROBE_TERMINATED signal is emitted. * - * Returns: %TRUE if at least one probe could be started, %FALSE otherwise + * Returns: 0 on success or a negative NetworkManager error code (NME_*). */ -gboolean +int nm_acd_manager_start_probe (NMAcdManager *self, guint timeout) { GHashTableIter iter; @@ -309,7 +333,7 @@ nm_acd_manager_start_probe (NMAcdManager *self, guint timeout) _LOGW ("couldn't init ACD for probing on interface '%s': %s", nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), acd_error_to_string (r)); - return FALSE; + return acd_error_to_nmerr (r, TRUE); } self->completed = 0; @@ -325,7 +349,7 @@ nm_acd_manager_start_probe (NMAcdManager *self, guint timeout) self->channel = g_io_channel_unix_new (fd); self->event_id = g_io_add_watch (self->channel, G_IO_IN, acd_event, self); - return success; + return success ? 0 : -NME_UNSPEC; } /** @@ -357,20 +381,23 @@ nm_acd_manager_check_address (NMAcdManager *self, in_addr_t address) * @self: a #NMAcdManager * * Start announcing addresses. + * + * Returns: a negative NetworkManager error number or zero on success. */ -void +int nm_acd_manager_announce_addresses (NMAcdManager *self) { GHashTableIter iter; AddressInfo *info; int r; + gboolean success = TRUE; r = acd_init (self); if (r) { _LOGW ("couldn't init ACD for announcing addresses on interface '%s': %s", nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), acd_error_to_string (r)); - return; + return acd_error_to_nmerr (r, TRUE); } if (self->state == STATE_INIT) { @@ -378,8 +405,10 @@ nm_acd_manager_announce_addresses (NMAcdManager *self) * start a fake probe with zero timeout and then perform * the announcement. */ g_hash_table_iter_init (&iter, self->addresses); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) - acd_probe_add (self, info, 0); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { + if (!acd_probe_add (self, info, 0)) + success = FALSE; + } self->state = STATE_ANNOUNCING; } else if (self->state == STATE_ANNOUNCING) { char sbuf[NM_UTILS_INET_ADDRSTRLEN]; @@ -394,10 +423,13 @@ nm_acd_manager_announce_addresses (NMAcdManager *self) nm_utils_inet4_ntop (info->address, sbuf), nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), acd_error_to_string (r)); + success = FALSE; } else _LOGD ("announcing address %s", nm_utils_inet4_ntop (info->address, sbuf)); } } + + return success ? 0 : -NME_UNSPEC; } static void diff --git a/src/devices/nm-acd-manager.h b/src/devices/nm-acd-manager.h index 75884846..08c0b798 100644 --- a/src/devices/nm-acd-manager.h +++ b/src/devices/nm-acd-manager.h @@ -36,8 +36,11 @@ NMAcdManager *nm_acd_manager_new (int ifindex, void nm_acd_manager_free (NMAcdManager *self); gboolean nm_acd_manager_add_address (NMAcdManager *self, in_addr_t address); -gboolean nm_acd_manager_start_probe (NMAcdManager *self, guint timeout); +int nm_acd_manager_start_probe (NMAcdManager *self, guint timeout); gboolean nm_acd_manager_check_address (NMAcdManager *self, in_addr_t address); -void nm_acd_manager_announce_addresses (NMAcdManager *self); +int nm_acd_manager_announce_addresses (NMAcdManager *self); + +NM_AUTO_DEFINE_FCN0 (NMAcdManager *, _nm_auto_free_acdmgr, nm_acd_manager_free); +#define nm_auto_free_acdmgr nm_auto (_nm_auto_free_acdmgr) #endif /* __NM_ACD_MANAGER__ */ diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 6dabdfe8..37159fca 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -185,20 +185,18 @@ set_arp_targets (NMDevice *device, const char *delim, const char *prefix) { - char **items, **iter, *tmp; + gs_free const char **value_v = NULL; + gsize i; - if (!value || !*value) + value_v = nm_utils_strsplit_set (value, delim); + if (!value_v) return; + for (i = 0; value_v[i]; i++) { + gs_free char *tmp = NULL; - items = g_strsplit_set (value, delim, 0); - for (iter = items; iter && *iter; iter++) { - if (*iter[0]) { - tmp = g_strdup_printf ("%s%s", prefix, *iter); - set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, tmp); - g_free (tmp); - } + tmp = g_strdup_printf ("%s%s", prefix, value_v[i]); + set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, tmp); } - g_strfreev (items); } static void diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 4c8921c0..4275af91 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -37,6 +37,7 @@ _LOG_DECLARE_SELF(NMDeviceBridge); struct _NMDeviceBridge { NMDevice parent; + bool vlan_configured:1; }; struct _NMDeviceBridgeClass { @@ -267,21 +268,45 @@ commit_option (NMDevice *device, NMSetting *setting, const Option *option, gbool nm_platform_sysctl_master_set_option (nm_device_get_platform (device), ifindex, option->sysname, value); } -static void -commit_master_options (NMDevice *device, NMSettingBridge *setting) +static const NMPlatformBridgeVlan ** +setting_vlans_to_platform (GPtrArray *array) { - const Option *option; - NMSetting *s = NM_SETTING (setting); - - for (option = master_options; option->name; option++) - commit_option (device, s, option, FALSE); + NMPlatformBridgeVlan **arr; + NMPlatformBridgeVlan *p_data; + guint i; + + if (!array || !array->len) + return NULL; + + G_STATIC_ASSERT_EXPR (_nm_alignof (NMPlatformBridgeVlan *) >= _nm_alignof (NMPlatformBridgeVlan)); + arr = g_malloc ( (sizeof (NMPlatformBridgeVlan *) * (array->len + 1)) + + (sizeof (NMPlatformBridgeVlan ) * (array->len ))); + p_data = (NMPlatformBridgeVlan *) &arr[array->len + 1]; + + for (i = 0; i < array->len; i++) { + NMBridgeVlan *vlan = array->pdata[i]; + guint16 vid_start, vid_end; + + nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end); + + p_data[i] = (NMPlatformBridgeVlan) { + .vid_start = vid_start, + .vid_end = vid_end, + .pvid = nm_bridge_vlan_is_pvid (vlan), + .untagged = nm_bridge_vlan_is_untagged (vlan), + }; + arr[i] = &p_data[i]; + } + arr[i] = NULL; + return (const NMPlatformBridgeVlan **) arr; } static void commit_slave_options (NMDevice *device, NMSettingBridgePort *setting) { const Option *option; - NMSetting *s, *s_clear = NULL; + NMSetting *s; + gs_unref_object NMSetting *s_clear = NULL; if (setting) s = NM_SETTING (setting); @@ -290,8 +315,6 @@ commit_slave_options (NMDevice *device, NMSettingBridgePort *setting) for (option = slave_options; option->name; option++) commit_option (device, s, option, TRUE); - - g_clear_object (&s_clear); } static void @@ -396,22 +419,112 @@ master_update_slave_connection (NMDevice *device, return TRUE; } +static gboolean +bridge_set_vlan_options (NMDevice *device, NMSettingBridge *s_bridge) +{ + NMDeviceBridge *self = NM_DEVICE_BRIDGE (device); + gconstpointer hwaddr; + size_t length; + gboolean enabled; + guint16 pvid; + NMPlatform *plat; + int ifindex; + gs_unref_ptrarray GPtrArray *vlans = NULL; + gs_free const NMPlatformBridgeVlan **plat_vlans = NULL; + + if (self->vlan_configured) + return TRUE; + + plat = nm_device_get_platform (device); + ifindex = nm_device_get_ifindex (device); + enabled = nm_setting_bridge_get_vlan_filtering (s_bridge); + + if (!enabled) { + nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "0"); + nm_platform_sysctl_master_set_option (plat, ifindex, "default_pvid", "1"); + nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE, NULL); + return TRUE; + } + + hwaddr = nm_platform_link_get_address (plat, ifindex, &length); + g_return_val_if_fail (length == ETH_ALEN, FALSE); + if (nm_utils_hwaddr_matches (hwaddr, ETH_ALEN, nm_ip_addr_zero.addr_eth, ETH_ALEN)) { + /* We need a non-zero MAC address to set the default pvid. + * Retry later. */ + return TRUE; + } + + self->vlan_configured = TRUE; + + /* Filtering must be disabled to change the default PVID */ + if (!nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "0")) + return FALSE; + + /* Clear the default PVID so that we later can force the re-creation of + * default PVID VLANs by writing the option again. */ + if (!nm_platform_sysctl_master_set_option (plat, ifindex, "default_pvid", "0")) + return FALSE; + + /* Clear all existing VLANs */ + if (!nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE, NULL)) + return FALSE; + + /* Now set the default PVID. After this point the kernel creates + * a PVID VLAN on each port, including the bridge itself. */ + pvid = nm_setting_bridge_get_vlan_default_pvid (s_bridge); + if (pvid) { + char value[32]; + + nm_sprintf_buf (value, "%u", pvid); + if (!nm_platform_sysctl_master_set_option (plat, ifindex, "default_pvid", value)) + return FALSE; + } + + /* Create VLANs only after setting the default PVID, so that + * any PVID VLAN overrides the bridge's default PVID. */ + g_object_get (s_bridge, NM_SETTING_BRIDGE_VLANS, &vlans, NULL); + plat_vlans = setting_vlans_to_platform (vlans); + if ( plat_vlans + && !nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE, plat_vlans)) + return FALSE; + + if (!nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "1")) + return FALSE; + + return TRUE; +} + static NMActStageReturn act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) { NMActStageReturn ret; - NMConnection *connection = nm_device_get_applied_connection (device); + NMConnection *connection; + NMSetting *s_bridge; + const Option *option; - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + NM_DEVICE_BRIDGE (device)->vlan_configured = FALSE; ret = NM_DEVICE_CLASS (nm_device_bridge_parent_class)->act_stage1_prepare (device, out_failure_reason); if (ret != NM_ACT_STAGE_RETURN_SUCCESS) return ret; - if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE)) + connection = nm_device_get_applied_connection (device); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + s_bridge = (NMSetting *) nm_connection_get_setting_bridge (connection); + g_return_val_if_fail (s_bridge, NM_ACT_STAGE_RETURN_FAILURE); + + if (!nm_device_hw_addr_set_cloned (device, connection, FALSE)) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); return NM_ACT_STAGE_RETURN_FAILURE; + } - commit_master_options (device, nm_connection_get_setting_bridge (connection)); + for (option = master_options; option->name; option++) + commit_option (device, s_bridge, option, FALSE); + + if (!bridge_set_vlan_options (device, (NMSettingBridge *) s_bridge)) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } return NM_ACT_STAGE_RETURN_SUCCESS; } @@ -457,12 +570,43 @@ enslave_slave (NMDevice *device, gboolean configure) { NMDeviceBridge *self = NM_DEVICE_BRIDGE (device); + NMConnection *master_connection; + NMSettingBridge *s_bridge; + NMSettingBridgePort *s_port; if (configure) { if (!nm_platform_link_enslave (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave))) return FALSE; - commit_slave_options (slave, nm_connection_get_setting_bridge_port (connection)); + master_connection = nm_device_get_applied_connection (device); + nm_assert (master_connection); + s_bridge = nm_connection_get_setting_bridge (master_connection); + nm_assert (s_bridge); + s_port = nm_connection_get_setting_bridge_port (connection); + + bridge_set_vlan_options (device, s_bridge); + + if (nm_setting_bridge_get_vlan_filtering (s_bridge)) { + gs_free const NMPlatformBridgeVlan **plat_vlans = NULL; + gs_unref_ptrarray GPtrArray *vlans = NULL; + + if (s_port) + g_object_get (s_port, NM_SETTING_BRIDGE_PORT_VLANS, &vlans, NULL); + + plat_vlans = setting_vlans_to_platform (vlans); + + /* Since the link was just enslaved, there are no existing VLANs + * (except for the default one) and so there's no need to flush. */ + + if ( plat_vlans + && !nm_platform_link_set_bridge_vlans (nm_device_get_platform (slave), + nm_device_get_ifindex (slave), + TRUE, + plat_vlans)) + return FALSE; + } + + commit_slave_options (slave, s_port); _LOGI (LOGD_BRIDGE, "attached bridge port %s", nm_device_get_ip_iface (slave)); diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 24c99f76..170c7e28 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -48,7 +48,7 @@ #include "nm-device-factory.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" -#include "nm-utils/nm-udev-utils.h" +#include "nm-udev-aux/nm-udev-utils.h" #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceEthernet); diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index 6e2372ab..dd98e92f 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -168,7 +168,8 @@ NMIPConfig *nm_device_ip_config_new (NMDevice *self, int addr_family); /*****************************************************************************/ gint64 nm_device_get_configured_mtu_from_connection_default (NMDevice *self, - const char *property_name); + const char *property_name, + guint32 max_mtu); guint32 nm_device_get_configured_mtu_from_connection (NMDevice *device, GType setting_type, diff --git a/src/devices/nm-device-wireguard.c b/src/devices/nm-device-wireguard.c index a9eb1ab4..f8c7d1c3 100644 --- a/src/devices/nm-device-wireguard.c +++ b/src/devices/nm-device-wireguard.c @@ -23,7 +23,7 @@ #include "nm-setting-wireguard.h" #include "nm-core-internal.h" -#include "nm-utils/nm-secret-utils.h" +#include "nm-glib-aux/nm-secret-utils.h" #include "nm-device-private.h" #include "platform/nm-platform.h" #include "platform/nmp-object.h" diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 7514fa78..bd4fbcc3 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -36,17 +36,18 @@ #include <linux/rtnetlink.h> #include <linux/pkt_sched.h> -#include "nm-utils/nm-dedup-multi.h" -#include "nm-utils/nm-random-utils.h" -#include "nm-utils/unaligned.h" +#include "nm-std-aux/unaligned.h" +#include "nm-glib-aux/nm-dedup-multi.h" +#include "nm-glib-aux/nm-random-utils.h" -#include "nm-ethtool-utils.h" -#include "nm-common-macros.h" +#include "nm-libnm-core-intern/nm-ethtool-utils.h" +#include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-device-private.h" #include "NetworkManagerUtils.h" #include "nm-manager.h" #include "platform/nm-platform.h" #include "platform/nmp-object.h" +#include "platform/nmp-rules-manager.h" #include "ndisc/nm-ndisc.h" #include "ndisc/nm-lndp-ndisc.h" #include "dhcp/nm-dhcp-manager.h" @@ -1545,8 +1546,7 @@ _set_ip_ifindex (NMDevice *self, priv->ip_ifindex, priv->ip_iface); - if (nm_platform_check_kernel_support (platform, - NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) + if (nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) nm_platform_link_set_user_ipv6ll_enabled (platform, priv->ip_ifindex, TRUE); if (!nm_platform_link_is_up (platform, priv->ip_ifindex)) @@ -2787,8 +2787,10 @@ nm_device_check_connectivity_update_interval (NMDevice *self) } static void -concheck_update_state (NMDevice *self, int addr_family, - NMConnectivityState state, gboolean allow_periodic_bump) +concheck_update_state (NMDevice *self, + int addr_family, + NMConnectivityState state, + gboolean allow_periodic_bump) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const gboolean IS_IPv4 = (addr_family == AF_INET); @@ -2821,6 +2823,14 @@ concheck_update_state (NMDevice *self, int addr_family, state = NM_CONNECTIVITY_LIMITED; } else state = NM_CONNECTIVITY_NONE; + } else if (state == NM_CONNECTIVITY_LIMITED) { + /* NMConnectivity cannot distinguish between NONE and LIMITED connectivity. In both + * cases, it just failed to fetch the URL. + * + * NMDevice coerces a LIMITED state to NONE here, if the logical state of the device + * is disconnected. */ + if (priv->state <= NM_DEVICE_STATE_DISCONNECTED) + state = NM_CONNECTIVITY_NONE; } if (priv->concheck_x[IS_IPv4].state == state) { @@ -4319,8 +4329,7 @@ realize_start_setup (NMDevice *self, if (priv->firmware_version) _notify (self, PROP_FIRMWARE_VERSION); - if (nm_platform_check_kernel_support (nm_device_get_platform (self), - NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) + if (nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) priv->ipv6ll_handle = nm_platform_link_get_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ifindex); if (nm_platform_link_supports_sriov (nm_device_get_platform (self), priv->ifindex)) @@ -5064,7 +5073,6 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection = nm_device_get_applied_connection (self); - NMDeviceState new_state; const char *master_status; g_return_if_fail (priv->master); @@ -5073,16 +5081,17 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason) && priv->state <= NM_DEVICE_STATE_ACTIVATED) { switch (nm_device_state_reason_check (reason)) { case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED: - new_state = NM_DEVICE_STATE_FAILED; master_status = "failed"; break; case NM_DEVICE_STATE_REASON_USER_REQUESTED: - new_state = NM_DEVICE_STATE_DEACTIVATING; reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED; master_status = "deactivated by user request"; break; + case NM_DEVICE_STATE_REASON_CONNECTION_REMOVED: + reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED; + master_status = "deactivated because master was removed"; + break; default: - new_state = NM_DEVICE_STATE_DISCONNECTED; master_status = "deactivated"; break; } @@ -5093,7 +5102,7 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason) /* Cancel any pending activation sources */ _cancel_activation (self); - nm_device_queue_state (self, new_state, reason); + nm_device_queue_state (self, NM_DEVICE_STATE_DEACTIVATING, reason); } else _LOGI (LOGD_DEVICE, "released from master device %s", nm_device_get_iface (priv->master)); @@ -5304,7 +5313,8 @@ nm_device_autoconnect_allowed (NMDevice *self) if (priv->state < NM_DEVICE_STATE_DISCONNECTED) return FALSE; } else { - /* Unrealized devices can always autoconnect. */ + if (!nm_device_check_unrealized_device_managed (self)) + return FALSE; } /* The 'autoconnect-allowed' signal is emitted on a device to allow @@ -6403,6 +6413,84 @@ lldp_init (NMDevice *self, gboolean restart) } } +/* set-mode can be: + * - TRUE: sync with new rules. + * - FALSE: sync, but remove all rules (== flush) + * - DEFAULT: forget about all the rules that we previously tracked, + * but don't actually remove them. This is when quitting NM + * we want to keep the rules. + * The problem is, after restart of NM, the rule manager will + * no longer remember that NM added these rules and treat them + * as externally added ones. Don't restart NetworkManager if + * you care about that. + */ +static void +_routing_rules_sync (NMDevice *self, + NMTernary set_mode) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMPRulesManager *rules_manager = nm_netns_get_rules_manager (nm_device_get_netns (self)); + gboolean untrack_only_dirty = FALSE; + gboolean keep_deleted_rules; + gpointer user_tag; + + user_tag = priv; + + if (set_mode == NM_TERNARY_TRUE) { + NMConnection *applied_connection; + NMSettingIPConfig *s_ip; + guint i, num; + int is_ipv4; + + untrack_only_dirty = TRUE; + nmp_rules_manager_set_dirty (rules_manager, user_tag); + + applied_connection = nm_device_get_applied_connection (self); + + for (is_ipv4 = 0; applied_connection && is_ipv4 < 2; is_ipv4++) { + int addr_family = is_ipv4 ? AF_INET : AF_INET6; + + s_ip = nm_connection_get_setting_ip_config (applied_connection, addr_family); + if (!s_ip) + continue; + + num = nm_setting_ip_config_get_num_routing_rules (s_ip); + for (i = 0; i < num; i++) { + NMPlatformRoutingRule plrule; + NMIPRoutingRule *rule; + + rule = nm_setting_ip_config_get_routing_rule (s_ip, i); + nm_ip_routing_rule_to_platform (rule, &plrule); + nmp_rules_manager_track (rules_manager, + &plrule, + 10, + user_tag); + } + } + } + + nmp_rules_manager_untrack_all (rules_manager, user_tag, !untrack_only_dirty); + + keep_deleted_rules = FALSE; + if (set_mode == NM_TERNARY_DEFAULT) { + /* when exiting NM, we leave the device up and the rules configured. + * We just all nmp_rules_manager_sync() to forget about the synced rules, + * but we don't actually delete them. + * + * FIXME: that is a problem after restart of NetworkManager, because these + * rules will look like externally added, and NM will no longer remove + * them. + * To fix that, we could during "assume" mark the rules of the profile + * as owned (and "added" by the device). The problem with that is that it + * wouldn't cover rules that devices add by internal decision (not because + * of a setting in the profile, e.g. WireGuard could setup policy routing). + * Maybe it would be better to remember these orphaned rules at exit in a + * file and track them after restart again. */ + keep_deleted_rules = TRUE; + } + nmp_rules_manager_sync (rules_manager, keep_deleted_rules); +} + static gboolean tc_commit (NMDevice *self) { @@ -6514,6 +6602,8 @@ activate_stage2_device_config (NMDevice *self) } } + _routing_rules_sync (self, NM_TERNARY_TRUE); + if (!nm_device_sys_iface_state_is_external_or_assume (self)) { if (!nm_device_bring_up (self, FALSE, &no_firmware)) { if (no_firmware) @@ -6747,7 +6837,8 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, AcdCallback cb) NMDedupMultiIter ipconf_iter; AcdData *data; guint timeout; - gboolean ret, addr_found; + gboolean addr_found; + int r; const guint8 *hwaddr_arr; size_t length; guint i; @@ -6801,9 +6892,8 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, AcdCallback cb) nm_acd_manager_add_address (acd_manager, address->address); } - ret = nm_acd_manager_start_probe (acd_manager, timeout); - - if (!ret) { + r = nm_acd_manager_start_probe (acd_manager, timeout); + if (r < 0) { _LOGW (LOGD_DEVICE, "acd probe failed"); /* DAD could not be started, signal success */ @@ -8830,12 +8920,13 @@ linklocal6_start (NMDevice *self) gint64 nm_device_get_configured_mtu_from_connection_default (NMDevice *self, - const char *property_name) + const char *property_name, + guint32 max_mtu) { return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, property_name, self, - 0, G_MAXUINT32, -1); + 0, max_mtu, -1); } guint32 @@ -8848,6 +8939,7 @@ nm_device_get_configured_mtu_from_connection (NMDevice *self, NMSetting *setting; gint64 mtu_default; guint32 mtu = 0; + guint32 max_mtu = G_MAXUINT32; nm_assert (NM_IS_DEVICE (self)); nm_assert (out_source); @@ -8870,6 +8962,7 @@ nm_device_get_configured_mtu_from_connection (NMDevice *self, if (setting) mtu = nm_setting_infiniband_get_mtu (NM_SETTING_INFINIBAND (setting)); global_property_name = NM_CON_DEFAULT ("infiniband.mtu"); + max_mtu = NM_INFINIBAND_MAX_MTU; } else if (setting_type == NM_TYPE_SETTING_IP_TUNNEL) { if (setting) mtu = nm_setting_ip_tunnel_get_mtu (NM_SETTING_IP_TUNNEL (setting)); @@ -8881,13 +8974,12 @@ nm_device_get_configured_mtu_from_connection (NMDevice *self, } else g_return_val_if_reached (0); - if (mtu) { *out_source = NM_DEVICE_MTU_SOURCE_CONNECTION; return mtu; } - mtu_default = nm_device_get_configured_mtu_from_connection_default (self, global_property_name); + mtu_default = nm_device_get_configured_mtu_from_connection_default (self, global_property_name, max_mtu); if (mtu_default >= 0) { *out_source = NM_DEVICE_MTU_SOURCE_CONNECTION; return (guint32) mtu_default; @@ -9140,8 +9232,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in * addresses as /128. The reason for the /128 is to prevent the kernel * from adding a prefix route for this address. */ ifa_flags = 0; - if (nm_platform_check_kernel_support (nm_device_get_platform (self), - NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS)) { + if (nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS)) { ifa_flags |= IFA_F_NOPREFIXROUTE; if (NM_IN_SET (priv->ndisc_use_tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)) @@ -9173,8 +9264,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in rdata->routes_n, nm_device_get_route_table (self, AF_INET6, TRUE), nm_device_get_route_metric (self, AF_INET6), - nm_platform_check_kernel_support (nm_device_get_platform (self), - NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF)); + nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF)); if (priv->ac_ip6_config.current) { nm_ip6_config_reset_routes_ndisc ((NMIP6Config *) priv->ac_ip6_config.current, rdata->gateways, @@ -9183,8 +9273,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in rdata->routes_n, nm_device_get_route_table (self, AF_INET6, TRUE), nm_device_get_route_metric (self, AF_INET6), - nm_platform_check_kernel_support (nm_device_get_platform (self), - NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF)); + nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF)); } } @@ -9372,8 +9461,7 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) priv->ndisc_use_tempaddr = use_tempaddr; if ( NM_IN_SET (use_tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) - && !nm_platform_check_kernel_support (nm_device_get_platform (self), - NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS)) { + && !nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS)) { _LOGW (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for " "IPv6 private addresses. This feature is not available"); } @@ -9480,8 +9568,7 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int ifindex = nm_device_get_ip_ifindex (self); - if (!nm_platform_check_kernel_support (nm_device_get_platform (self), - NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) + if (!nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) return; priv->ipv6ll_handle = enable; @@ -9854,7 +9941,7 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActStageReturn ret; NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; - NMIP6Config *ip6_config = NULL; + gs_unref_object NMIP6Config *ip6_config = NULL; g_assert (priv->ip_state_6 == NM_DEVICE_IP_STATE_WAIT); @@ -13424,6 +13511,32 @@ nm_device_set_unmanaged_by_flags_queue (NMDevice *self, _set_unmanaged_flags (self, flags, set_op, TRUE, FALSE, reason); } +/** + * nm_device_check_unrealized_device_managed: + * + * Checks if a unrealized device is managed from user settings + * or user configuration. + */ +gboolean +nm_device_check_unrealized_device_managed (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + nm_assert (!nm_device_is_real (self)); + + if (!nm_config_data_get_device_config_boolean (NM_CONFIG_GET_DATA, + NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, + self, + TRUE, + TRUE)) + return FALSE; + + if (nm_device_spec_match_list (self, nm_settings_get_unmanaged_specs (priv->settings))) + return FALSE; + + return TRUE; +} + void nm_device_set_unmanaged_by_user_settings (NMDevice *self) { @@ -14345,6 +14458,11 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean } } + _routing_rules_sync (self, + cleanup_type == CLEANUP_TYPE_KEEP + ? NM_TERNARY_DEFAULT + : NM_TERNARY_FALSE); + if (ifindex > 0) nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self), ifindex, NULL); @@ -15879,17 +15997,22 @@ nm_device_spec_match_list_full (NMDevice *self, const GSList *specs, int no_matc { NMDeviceClass *klass; NMMatchSpecMatchType m; + const char *hw_address = NULL; + gboolean is_fake; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); klass = NM_DEVICE_GET_CLASS (self); + hw_address = nm_device_get_permanent_hw_address_full (self, + !nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT), + &is_fake); m = nm_match_spec_device (specs, nm_device_get_iface (self), nm_device_get_type_description (self), nm_device_get_driver (self), nm_device_get_driver_version (self), - nm_device_get_permanent_hw_address (self), + is_fake ? NULL : hw_address, klass->get_s390_subchannels ? klass->get_s390_subchannels (self) : NULL, nm_dhcp_manager_get_config (nm_dhcp_manager_get ())); diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 45c9dda0..d11c33ab 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -691,6 +691,8 @@ void nm_device_set_unmanaged_by_user_udev (NMDevice *self); void nm_device_set_unmanaged_by_user_conf (NMDevice *self); void nm_device_set_unmanaged_by_quitting (NMDevice *device); +gboolean nm_device_check_unrealized_device_managed (NMDevice *self); + gboolean nm_device_is_nm_owned (NMDevice *device); gboolean nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps); diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c index cb3b5907..831054ba 100644 --- a/src/devices/nm-lldp-listener.c +++ b/src/devices/nm-lldp-listener.c @@ -24,7 +24,9 @@ #include <net/ethernet.h> +#include "nm-std-aux/unaligned.h" #include "platform/nm-platform.h" +#include "nm-glib-aux/nm-c-list.h" #include "nm-utils.h" #include "systemd/nm-sd.h" @@ -40,6 +42,8 @@ typedef enum { LLDP_ATTR_TYPE_NONE, LLDP_ATTR_TYPE_UINT32, LLDP_ATTR_TYPE_STRING, + LLDP_ATTR_TYPE_VARDICT, + LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS, } LldpAttrType; typedef enum { @@ -49,12 +53,18 @@ typedef enum { LLDP_ATTR_ID_SYSTEM_NAME, LLDP_ATTR_ID_SYSTEM_DESCRIPTION, LLDP_ATTR_ID_SYSTEM_CAPABILITIES, + LLDP_ATTR_ID_MANAGEMENT_ADDRESSES, LLDP_ATTR_ID_IEEE_802_1_PVID, LLDP_ATTR_ID_IEEE_802_1_PPVID, LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS, + LLDP_ATTR_ID_IEEE_802_1_PPVIDS, LLDP_ATTR_ID_IEEE_802_1_VID, LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME, - _LLDP_PROP_ID_COUNT, + LLDP_ATTR_ID_IEEE_802_1_VLANS, + LLDP_ATTR_ID_IEEE_802_3_MAC_PHY_CONF, + LLDP_ATTR_ID_IEEE_802_3_POWER_VIA_MDI, + LLDP_ATTR_ID_IEEE_802_3_MAX_FRAME_SIZE, + _LLDP_ATTR_ID_COUNT, } LldpAttrId; typedef struct { @@ -62,6 +72,8 @@ typedef struct { union { guint32 v_uint32; char *v_string; + GVariant *v_variant; + CList v_variant_list; }; } LldpAttrData; @@ -109,7 +121,7 @@ typedef struct { bool valid:1; - LldpAttrData attrs[_LLDP_PROP_ID_COUNT]; + LldpAttrData attrs[_LLDP_ATTR_ID_COUNT]; GVariant *variant; } LldpNeighbor; @@ -155,35 +167,26 @@ ether_addr_equal (const struct ether_addr *a1, const struct ether_addr *a2) return memcmp (a1, a2, ETH_ALEN) == 0; } -static guint32 -_access_uint8 (const void *data) -{ - return *((const guint8 *) data); -} - -static guint32 -_access_uint16 (const void *data) -{ - guint16 v; - - memcpy (&v, data, sizeof (v)); - return ntohs (v); -} - /*****************************************************************************/ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_lldp_attr_id_to_name, LldpAttrId, NM_UTILS_LOOKUP_DEFAULT_WARN (NULL), - NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_PORT_DESCRIPTION, NM_LLDP_ATTR_PORT_DESCRIPTION), - NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_NAME, NM_LLDP_ATTR_SYSTEM_NAME), - NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_DESCRIPTION, NM_LLDP_ATTR_SYSTEM_DESCRIPTION), - NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_CAPABILITIES, NM_LLDP_ATTR_SYSTEM_CAPABILITIES), - NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PVID, NM_LLDP_ATTR_IEEE_802_1_PVID), - NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID, NM_LLDP_ATTR_IEEE_802_1_PPVID), - NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS, NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS), - NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_VID, NM_LLDP_ATTR_IEEE_802_1_VID), - NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME, NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME), - NM_UTILS_LOOKUP_ITEM_IGNORE (_LLDP_PROP_ID_COUNT), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_PORT_DESCRIPTION, NM_LLDP_ATTR_PORT_DESCRIPTION), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_NAME, NM_LLDP_ATTR_SYSTEM_NAME), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_DESCRIPTION, NM_LLDP_ATTR_SYSTEM_DESCRIPTION), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_CAPABILITIES, NM_LLDP_ATTR_SYSTEM_CAPABILITIES), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_MANAGEMENT_ADDRESSES, NM_LLDP_ATTR_MANAGEMENT_ADDRESSES), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PVID, NM_LLDP_ATTR_IEEE_802_1_PVID), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID, NM_LLDP_ATTR_IEEE_802_1_PPVID), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS, NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVIDS, NM_LLDP_ATTR_IEEE_802_1_PPVIDS), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_VID, NM_LLDP_ATTR_IEEE_802_1_VID), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME, NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_VLANS, NM_LLDP_ATTR_IEEE_802_1_VLANS), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_3_MAC_PHY_CONF, NM_LLDP_ATTR_IEEE_802_3_MAC_PHY_CONF), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_3_POWER_VIA_MDI, NM_LLDP_ATTR_IEEE_802_3_POWER_VIA_MDI), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_3_MAX_FRAME_SIZE,NM_LLDP_ATTR_IEEE_802_3_MAX_FRAME_SIZE), + NM_UTILS_LOOKUP_ITEM_IGNORE (_LLDP_ATTR_ID_COUNT), ); _NM_UTILS_LOOKUP_DEFINE (static, _lldp_attr_id_to_type, LldpAttrId, LldpAttrType, @@ -192,12 +195,18 @@ _NM_UTILS_LOOKUP_DEFINE (static, _lldp_attr_id_to_type, LldpAttrId, LldpAttrType NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_SYSTEM_NAME, LLDP_ATTR_TYPE_STRING), NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_SYSTEM_DESCRIPTION, LLDP_ATTR_TYPE_STRING), NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_SYSTEM_CAPABILITIES, LLDP_ATTR_TYPE_UINT32), + NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_MANAGEMENT_ADDRESSES, LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS), NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_PVID, LLDP_ATTR_TYPE_UINT32), NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID, LLDP_ATTR_TYPE_UINT32), NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS, LLDP_ATTR_TYPE_UINT32), + NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVIDS, LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS), NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_VID, LLDP_ATTR_TYPE_UINT32), NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME, LLDP_ATTR_TYPE_STRING), - NM_UTILS_LOOKUP_ITEM_IGNORE (_LLDP_PROP_ID_COUNT), + NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_VLANS, LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS), + NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_3_MAC_PHY_CONF, LLDP_ATTR_TYPE_VARDICT), + NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_3_POWER_VIA_MDI, LLDP_ATTR_TYPE_VARDICT), + NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_3_MAX_FRAME_SIZE, LLDP_ATTR_TYPE_UINT32), + NM_UTILS_LOOKUP_ITEM_IGNORE (_LLDP_ATTR_ID_COUNT), ); static void @@ -216,13 +225,8 @@ _lldp_attr_set_str (LldpAttrData *pdata, LldpAttrId attr_id, const char *v_strin } static void -_lldp_attr_set_str_ptr (LldpAttrData *pdata, LldpAttrId attr_id, const void *str, gsize len) +_lldp_attr_take_str_ptr (LldpAttrData *pdata, LldpAttrId attr_id, char *str) { - const char *s = str; - const char *tmp; - gsize len0 = len; - gs_free char *str_free = NULL; - nm_assert (pdata); nm_assert (_lldp_attr_id_to_type (attr_id) == LLDP_ATTR_TYPE_STRING); @@ -233,23 +237,7 @@ _lldp_attr_set_str_ptr (LldpAttrData *pdata, LldpAttrId attr_id, const void *str return; pdata->attr_type = LLDP_ATTR_TYPE_STRING; - - /* truncate at first NUL, including removing trailing NULs*/ - tmp = memchr (s, '\0', len); - if (tmp) - len = tmp - s; - - if (!len) { - pdata->v_string = g_strdup (""); - return; - } - - if (len0 <= len || s[len] != '\0') { - /* hmpf, g_strescape needs a trailing NUL. Need to clone */ - s = str_free = g_strndup (s, len); - } - - pdata->v_string = g_strescape (s, NULL); + pdata->v_string = str; } static void @@ -267,6 +255,44 @@ _lldp_attr_set_uint32 (LldpAttrData *pdata, LldpAttrId attr_id, guint32 v_uint32 pdata->v_uint32 = v_uint32; } +static void +_lldp_attr_set_vardict (LldpAttrData *pdata, LldpAttrId attr_id, GVariant *variant) +{ + + nm_assert (pdata); + nm_assert (_lldp_attr_id_to_type (attr_id) == LLDP_ATTR_TYPE_VARDICT); + + pdata = &pdata[attr_id]; + + /* we ignore duplicate fields silently */ + if (pdata->attr_type != LLDP_ATTR_TYPE_NONE) { + if (g_variant_is_floating (variant)) + g_variant_unref (variant); + return; + } + + pdata->attr_type = LLDP_ATTR_TYPE_VARDICT; + pdata->v_variant = g_variant_ref_sink (variant); +} + +static void +_lldp_attr_add_vardict (LldpAttrData *pdata, LldpAttrId attr_id, GVariant *variant) +{ + nm_assert (pdata); + nm_assert (_lldp_attr_id_to_type (attr_id) == LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS); + + g_variant_ref_sink (variant); + pdata = &pdata[attr_id]; + + if (pdata->attr_type == LLDP_ATTR_TYPE_NONE) { + c_list_init (&pdata->v_variant_list); + pdata->attr_type = LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS; + } else + nm_assert (pdata->attr_type == LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS); + + c_list_link_tail (&pdata->v_variant_list, &nm_c_list_elem_new_stale (variant)->lst); +} + /*****************************************************************************/ static guint @@ -312,13 +338,28 @@ static void lldp_neighbor_free (LldpNeighbor *neighbor) { LldpAttrId attr_id; + LldpAttrType attr_type; if (neighbor) { g_free (neighbor->chassis_id); g_free (neighbor->port_id); - for (attr_id = 0; attr_id < _LLDP_PROP_ID_COUNT; attr_id++) { - if (neighbor->attrs[attr_id].attr_type == LLDP_ATTR_TYPE_STRING) + for (attr_id = 0; attr_id < _LLDP_ATTR_ID_COUNT; attr_id++) { + attr_type = neighbor->attrs[attr_id].attr_type; + + switch (attr_type) { + case LLDP_ATTR_TYPE_STRING: g_free (neighbor->attrs[attr_id].v_string); + break; + case LLDP_ATTR_TYPE_VARDICT: + g_variant_unref (neighbor->attrs[attr_id].v_variant); + break; + case LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS: + nm_c_list_elem_free_all (&neighbor->attrs[attr_id].v_variant_list, + (GDestroyNotify) g_variant_unref); + break; + default: + ; + } } g_clear_pointer (&neighbor->variant, g_variant_unref); g_slice_free (LldpNeighbor, neighbor); @@ -346,7 +387,7 @@ lldp_neighbor_equal (LldpNeighbor *a, LldpNeighbor *b) || !nm_streq0 (a->port_id, b->port_id)) return FALSE; - for (attr_id = 0; attr_id < _LLDP_PROP_ID_COUNT; attr_id++) { + for (attr_id = 0; attr_id < _LLDP_ATTR_ID_COUNT; attr_id++) { if (a->attrs[attr_id].attr_type != b->attrs[attr_id].attr_type) return FALSE; switch (a->attrs[attr_id].attr_type) { @@ -367,6 +408,75 @@ lldp_neighbor_equal (LldpNeighbor *a, LldpNeighbor *b) return TRUE; } +static GVariant * +parse_management_address_tlv (uint8_t *data, gsize len) +{ + GVariantDict dict; + GVariant *variant; + gsize addr_len, oid_len; + + /* 802.1AB-2009 - Figure 8-11 + * + * - TLV type / length (2 bytes) + * - address string length (1 byte) + * - address subtype (1 byte) + * - address (1 to 31 bytes) + * - interface number subtype (1 byte) + * - interface number (4 bytes) + * - OID string length (1 byte) + * - OID (0 to 128 bytes) + */ + + if (len < 11) + goto err; + + nm_assert ((data[0] >> 1) == SD_LLDP_TYPE_MGMT_ADDRESS); + nm_assert ((((data[0] & 1) << 8) + data[1]) + 2 == len); + + data += 2; + len -= 2; + addr_len = *data; /* length of (address subtype + address) */ + + if (addr_len < 2 || addr_len > 32) + goto err; + if (len < ( 1 /* address stringth length */ + + addr_len /* address subtype + address */ + + 5 /* interface */ + + 1)) /* oid */ + goto err; + + g_variant_dict_init (&dict, NULL); + + data++; + len--; + g_variant_dict_insert (&dict, "address-subtype", "u", (guint32) *data); + variant = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, data + 1, addr_len - 1, 1); + g_variant_dict_insert_value (&dict, "address", variant); + + data += addr_len; + len -= addr_len; + g_variant_dict_insert (&dict, "interface-number-subtype", "u", (guint32) *data); + + data++; + len--; + g_variant_dict_insert (&dict, "interface-number", "u", unaligned_read_be32 (data)); + + data += 4; + len -= 4; + oid_len = *data; + + if (len < (1 + oid_len)) + goto err; + + data++; + variant = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, data, oid_len, 1); + g_variant_dict_insert_value (&dict, "object-id", variant); + return g_variant_dict_end (&dict); +err: + g_variant_dict_clear (&dict); + return NULL; +} + static LldpNeighbor * lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error) { @@ -468,7 +578,29 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error) } do { guint8 oui[3]; - guint8 subtype; + guint8 type, subtype; + GVariant *variant; + + if (sd_lldp_neighbor_tlv_get_type (neighbor_sd, &type) < 0) + continue; + + if (sd_lldp_neighbor_tlv_get_raw (neighbor_sd, (void *) &data8, &len) < 0) + continue; + + switch (type) { + case SD_LLDP_TYPE_MGMT_ADDRESS: + variant = parse_management_address_tlv (data8, len); + if (variant) { + _lldp_attr_add_vardict (neigh->attrs, + LLDP_ATTR_ID_MANAGEMENT_ADDRESSES, + variant); + } + continue; + case SD_LLDP_TYPE_PRIVATE: + break; + default: + continue; + } r = sd_lldp_neighbor_tlv_get_oui (neighbor_sd, oui, &subtype); if (r < 0) { @@ -479,18 +611,12 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error) goto out; } - if (!( memcmp (oui, SD_LLDP_OUI_802_1, sizeof (oui)) == 0 - && NM_IN_SET (subtype, - SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID, - SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID, - SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME))) - continue; - - if (sd_lldp_neighbor_tlv_get_raw (neighbor_sd, (void *) &data8, &len) < 0) + if ( memcmp (oui, SD_LLDP_OUI_802_1, sizeof (oui)) != 0 + && memcmp (oui, SD_LLDP_OUI_802_3, sizeof (oui)) != 0) continue; /* skip over leading TLV, OUI and subtype */ -#ifdef WITH_MORE_ASSERTS +#if NM_MORE_ASSERTS > 5 { guint8 check_hdr[] = { 0xfe | (((len - 2) >> 8) & 0x01), ((len - 2) & 0xFF), @@ -507,25 +633,37 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error) data8 += 6; len -= 6; - /*if (memcmp (oui, SD_LLDP_OUI_802_1, sizeof (oui)) == 0)*/ - { + if (memcmp (oui, SD_LLDP_OUI_802_1, sizeof (oui)) == 0) { + GVariantDict dict; + switch (subtype) { case SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID: if (len != 2) continue; _lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_PVID, - _access_uint16 (data8)); + unaligned_read_be16 (data8)); break; case SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID: if (len != 3) continue; _lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS, - _access_uint8 (&data8[0])); + data8[0]); _lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_PPVID, - _access_uint16 (&data8[1])); + unaligned_read_be16 (&data8[1])); + + g_variant_dict_init (&dict, NULL); + g_variant_dict_insert (&dict, "ppvid", "u", (guint32) unaligned_read_be16 (&data8[1])); + g_variant_dict_insert (&dict, "flags", "u", (guint32) data8[0]); + + _lldp_attr_add_vardict (neigh->attrs, + LLDP_ATTR_ID_IEEE_802_1_PPVIDS, + g_variant_dict_end (&dict)); break; case SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME: { int l; + guint32 vid; + const char *name; + char *name_to_free; if (len <= 3) continue; @@ -536,14 +674,62 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error) if (l > 32) continue; - _lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_VID, - _access_uint16 (&data8[0])); - _lldp_attr_set_str_ptr (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME, - &data8[3], l); + name = nm_utils_buf_utf8safe_escape (&data8[3], l, 0, &name_to_free); + vid = unaligned_read_be16 (&data8[0]); + + g_variant_dict_init (&dict, NULL); + g_variant_dict_insert (&dict, "vid", "u", vid); + g_variant_dict_insert (&dict, "name", "s", name); + + _lldp_attr_add_vardict (neigh->attrs, + LLDP_ATTR_ID_IEEE_802_1_VLANS, + g_variant_dict_end (&dict)); + + _lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_VID, vid); + _lldp_attr_take_str_ptr (neigh->attrs, + LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME, + name_to_free ?: g_strdup (name)); break; } default: - g_assert_not_reached (); + continue; + } + } else if (memcmp (oui, SD_LLDP_OUI_802_3, sizeof (oui)) == 0) { + GVariantDict dict; + + switch (subtype) { + case SD_LLDP_OUI_802_3_SUBTYPE_MAC_PHY_CONFIG_STATUS: + if (len != 5) + continue; + + g_variant_dict_init (&dict, NULL); + g_variant_dict_insert (&dict, "autoneg", "u", (guint32) data8[0]); + g_variant_dict_insert (&dict, "pmd-autoneg-cap", "u", (guint32) unaligned_read_be16 (&data8[1])); + g_variant_dict_insert (&dict, "operational-mau-type", "u", (guint32) unaligned_read_be16 (&data8[3])); + + _lldp_attr_set_vardict (neigh->attrs, + LLDP_ATTR_ID_IEEE_802_3_MAC_PHY_CONF, + g_variant_dict_end (&dict)); + break; + case SD_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI: + if (len != 3) + continue; + + g_variant_dict_init (&dict, NULL); + g_variant_dict_insert (&dict, "mdi-power-support", "u", (guint32) data8[0]); + g_variant_dict_insert (&dict, "pse-power-pair", "u", (guint32) data8[1]); + g_variant_dict_insert (&dict, "power-class", "u", (guint32) data8[2]); + + _lldp_attr_set_vardict (neigh->attrs, + LLDP_ATTR_ID_IEEE_802_3_POWER_VIA_MDI, + g_variant_dict_end (&dict)); + break; + case SD_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE: + if (len != 2) + continue; + _lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_3_MAX_FRAME_SIZE, + unaligned_read_be16 (data8)); + break; } } } while (sd_lldp_neighbor_tlv_next (neighbor_sd) > 0); @@ -593,7 +779,7 @@ lldp_neighbor_to_variant (LldpNeighbor *neigh) g_variant_new_string (dest_str)); } - for (attr_id = 0; attr_id < _LLDP_PROP_ID_COUNT; attr_id++) { + for (attr_id = 0; attr_id < _LLDP_ATTR_ID_COUNT; attr_id++) { const LldpAttrData *data = &neigh->attrs[attr_id]; nm_assert (NM_IN_SET (data->attr_type, _lldp_attr_id_to_type (attr_id), LLDP_ATTR_TYPE_NONE)); @@ -608,7 +794,26 @@ lldp_neighbor_to_variant (LldpNeighbor *neigh) _lldp_attr_id_to_name (attr_id), g_variant_new_string (data->v_string)); break; - default: + case LLDP_ATTR_TYPE_VARDICT: + g_variant_builder_add (&builder, "{sv}", + _lldp_attr_id_to_name (attr_id), + data->v_variant); + break; + case LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS: { + NMCListElem *elem; + GVariantBuilder builder2; + + g_variant_builder_init (&builder2, G_VARIANT_TYPE ("aa{sv}")); + + c_list_for_each_entry (elem, &data->v_variant_list, lst) + g_variant_builder_add_value (&builder2, elem->data); + + g_variant_builder_add (&builder, "{sv}", + _lldp_attr_id_to_name (attr_id), + g_variant_builder_end (&builder2)); + break; + } + case LLDP_ATTR_TYPE_NONE: break; } } diff --git a/src/devices/ovs/nm-device-ovs-port.c b/src/devices/ovs/nm-device-ovs-port.c index b96eba68..35eb739f 100644 --- a/src/devices/ovs/nm-device-ovs-port.c +++ b/src/devices/ovs/nm-device-ovs-port.c @@ -20,6 +20,7 @@ #include "nm-default.h" #include "nm-device-ovs-port.h" +#include "nm-device-ovs-interface.h" #include "nm-ovsdb.h" #include "devices/nm-device-private.h" @@ -141,6 +142,11 @@ release_slave (NMDevice *device, NMDevice *slave, gboolean configure) { nm_ovsdb_del_interface (nm_ovsdb_get (), nm_device_get_iface (slave), del_iface_cb, g_object_ref (slave)); + + /* Open VSwitch is going to delete this one. We must ignore what happens + * next with the interface. */ + if (NM_IS_DEVICE_OVS_INTERFACE (slave)) + nm_device_update_from_platform_link (slave, NULL); } /*****************************************************************************/ diff --git a/src/devices/ovs/nm-ovs-factory.c b/src/devices/ovs/nm-ovs-factory.c index 766c650b..2124b2a0 100644 --- a/src/devices/ovs/nm-ovs-factory.c +++ b/src/devices/ovs/nm-ovs-factory.c @@ -132,7 +132,7 @@ ovsdb_device_removed (NMOvsdb *ovsdb, const char *name, NMDeviceType device_type && device_state < NM_DEVICE_STATE_DEACTIVATING) { nm_device_state_changed (device, NM_DEVICE_STATE_DEACTIVATING, - NM_DEVICE_STATE_REASON_REMOVED); + NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED); } else if (device_state == NM_DEVICE_STATE_UNMANAGED) { nm_device_unrealize (device, TRUE, NULL); } diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c index 9d73c3ac..5b50f840 100644 --- a/src/devices/ovs/nm-ovsdb.c +++ b/src/devices/ovs/nm-ovsdb.c @@ -24,7 +24,7 @@ #include <gmodule.h> #include <gio/gunixsocketaddress.h> -#include "nm-utils/nm-jansson.h" +#include "nm-glib-aux/nm-jansson.h" #include "devices/nm-device.h" #include "platform/nm-platform.h" #include "nm-core-internal.h" diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c index 38a6dd8c..287f4d1b 100644 --- a/src/devices/team/nm-device-team.c +++ b/src/devices/team/nm-device-team.c @@ -30,14 +30,14 @@ #include <teamdctl.h> #include <stdlib.h> -#include "nm-utils/nm-jansson.h" +#include "nm-glib-aux/nm-jansson.h" #include "NetworkManagerUtils.h" #include "devices/nm-device-private.h" #include "platform/nm-platform.h" #include "nm-config.h" #include "nm-core-internal.h" #include "nm-ip4-config.h" -#include "nm-dbus-compat.h" +#include "nm-std-aux/nm-dbus-compat.h" #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceTeam); @@ -408,8 +408,10 @@ teamd_dbus_appeared (GDBusConnection *connection, success = teamd_read_config (device); if (success) nm_device_activate_schedule_stage2_device_config (device); - else if (!nm_device_sys_iface_state_is_external_or_assume (device)) + else if (!nm_device_sys_iface_state_is_external_or_assume (device)) { + teamd_cleanup (device, TRUE); nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED); + } } } diff --git a/src/devices/tests/test-acd.c b/src/devices/tests/test-acd.c index aff71825..8b52ee2c 100644 --- a/src/devices/tests/test-acd.c +++ b/src/devices/tests/test-acd.c @@ -111,8 +111,8 @@ acd_manager_probe_terminated (NMAcdManager *acd_manager, gpointer user_data) static void test_acd_common (test_fixture *fixture, TestInfo *info) { - NMAcdManager *manager; - GMainLoop *loop; + nm_auto_free_acdmgr NMAcdManager *manager = NULL; + nm_auto_unref_gmainloop GMainLoop *loop = NULL; int i; const guint WAIT_TIME_OPTIMISTIC = 50; guint wait_time; @@ -120,6 +120,7 @@ test_acd_common (test_fixture *fixture, TestInfo *info) .probe_terminated_callback = acd_manager_probe_terminated, .user_data_destroy = (GDestroyNotify) g_main_loop_unref, }; + int r; if (_skip_acd_test ()) return; @@ -131,8 +132,10 @@ test_acd_common (test_fixture *fixture, TestInfo *info) wait_time = WAIT_TIME_OPTIMISTIC; again: + nm_clear_pointer (&loop, g_main_loop_unref); loop = g_main_loop_new (NULL, FALSE); + nm_clear_pointer (&manager, nm_acd_manager_free); manager = nm_acd_manager_new (fixture->ifindex0, fixture->hwaddr0, fixture->hwaddr0_len, @@ -148,9 +151,10 @@ again: 24, 0, 3600, 1800, 0, NULL); } - g_assert (nm_acd_manager_start_probe (manager, wait_time)); + r = nm_acd_manager_start_probe (manager, wait_time); + g_assert_cmpint (r, ==, 0); + g_assert (nmtst_main_loop_run (loop, 2000)); - g_main_loop_unref (loop); for (i = 0; info->addresses[i]; i++) { gboolean val; @@ -164,7 +168,6 @@ again: /* probably we just had a glitch and the system took longer than * expected. Re-verify with a large timeout this time. */ wait_time = 1000; - nm_clear_pointer (&manager, nm_acd_manager_free); goto again; } @@ -172,8 +175,6 @@ again: i, nm_utils_inet4_ntop (info->addresses[i], sbuf), info->expected_result[i] ? "detect no duplicated" : "detect a duplicate"); } - - nm_acd_manager_free (manager); } static void @@ -199,8 +200,9 @@ test_acd_probe_2 (test_fixture *fixture, gconstpointer user_data) static void test_acd_announce (test_fixture *fixture, gconstpointer user_data) { - NMAcdManager *manager; - GMainLoop *loop; + nm_auto_free_acdmgr NMAcdManager *manager = NULL; + nm_auto_unref_gmainloop GMainLoop *loop = NULL; + int r; if (_skip_acd_test ()) return; @@ -216,11 +218,9 @@ test_acd_announce (test_fixture *fixture, gconstpointer user_data) g_assert (nm_acd_manager_add_address (manager, ADDR2)); loop = g_main_loop_new (NULL, FALSE); - nm_acd_manager_announce_addresses (manager); + r = nm_acd_manager_announce_addresses (manager); + g_assert_cmpint (r, ==, 0); g_assert (!nmtst_main_loop_run (loop, 200)); - g_main_loop_unref (loop); - - nm_acd_manager_free (manager); } static void diff --git a/src/devices/tests/test-lldp.c b/src/devices/tests/test-lldp.c index 7227d082..7b135f58 100644 --- a/src/devices/tests/test-lldp.c +++ b/src/devices/tests/test-lldp.c @@ -231,8 +231,10 @@ TEST_RECV_FRAME_DEFINE (_test_recv_data1_frame0, static void _test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener) { - GVariant *neighbors, *attr; + GVariant *neighbors, *attr, *child; gs_unref_variant GVariant *neighbor = NULL; + guint v_uint = 0; + const char *v_str = NULL; neighbors = nm_lldp_listener_get_neighbors (listener); nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}")); @@ -242,7 +244,7 @@ _test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener) SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, "00:01:30:F9:AD:A0", SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME, "1/1"); g_assert (neighbor); - g_assert_cmpint (g_variant_n_children (neighbor), ==, 4 + 10); + g_assert_cmpint (g_variant_n_children (neighbor), ==, 4 + 16); attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_DESTINATION, G_VARIANT_TYPE_STRING); nmtst_assert_variant_string (attr, NM_LLDP_DEST_NEAREST_BRIDGE); @@ -270,11 +272,49 @@ _test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener) nmtst_assert_variant_uint32 (attr, 20); nm_clear_g_variant (&attr); - /* unsupported: Management Address */ - /* unsupported: IEEE 802.3 - Power Via MDI */ - /* unsupported: IEEE 802.3 - MAC/PHY Configuration/Status */ + /* Management Address */ + attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_MANAGEMENT_ADDRESSES, G_VARIANT_TYPE ("aa{sv}")); + g_assert (attr); + g_assert_cmpuint (g_variant_n_children (attr), ==, 1); + child = g_variant_get_child_value (attr, 0); + g_assert (child); + g_variant_lookup (child, "interface-number", "u", &v_uint); + g_assert_cmpint (v_uint, ==, 1001); + g_variant_lookup (child, "interface-number-subtype", "u", &v_uint); + g_assert_cmpint (v_uint, ==, 2); + g_variant_lookup (child, "address-subtype", "u", &v_uint); + g_assert_cmpint (v_uint, ==, 6); + nm_clear_g_variant (&child); + nm_clear_g_variant (&attr); + + /* IEEE 802.3 - Power Via MDI */ + attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_3_POWER_VIA_MDI, G_VARIANT_TYPE_VARDICT); + g_assert (attr); + g_variant_lookup (attr, "mdi-power-support", "u", &v_uint); + g_assert_cmpint (v_uint, ==, 7); + g_variant_lookup (attr, "pse-power-pair", "u", &v_uint); + g_assert_cmpint (v_uint, ==, 1); + g_variant_lookup (attr, "power-class", "u", &v_uint); + g_assert_cmpint (v_uint, ==, 0); + nm_clear_g_variant (&attr); + + /* IEEE 802.3 - MAC/PHY Configuration/Status */ + attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_3_MAC_PHY_CONF, G_VARIANT_TYPE_VARDICT); + g_assert (attr); + g_variant_lookup (attr, "autoneg", "u", &v_uint); + g_assert_cmpint (v_uint, ==, 3); + g_variant_lookup (attr, "pmd-autoneg-cap", "u", &v_uint); + g_assert_cmpint (v_uint, ==, 0x6c00); + g_variant_lookup (attr, "operational-mau-type", "u", &v_uint); + g_assert_cmpint (v_uint, ==, 16); + nm_clear_g_variant (&attr); + /* unsupported: IEEE 802.3 - Link Aggregation */ - /* unsupported: IEEE 802.3 - Maximum Frame Size*/ + + /* Maximum Frame Size */ + attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_3_MAX_FRAME_SIZE, G_VARIANT_TYPE_UINT32); + nmtst_assert_variant_uint32 (attr, 1522); + nm_clear_g_variant (&attr); /* IEEE 802.1 - Port VLAN ID */ attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_PVID, G_VARIANT_TYPE_UINT32); @@ -289,6 +329,18 @@ _test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener) nmtst_assert_variant_uint32 (attr, 1); nm_clear_g_variant (&attr); + /* new PPVID attributes */ + attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_PPVIDS, G_VARIANT_TYPE ("aa{sv}")); + g_assert_cmpuint (g_variant_n_children (attr), ==, 1); + child = g_variant_get_child_value (attr, 0); + g_assert (child); + g_variant_lookup (child, "ppvid", "u", &v_uint); + g_assert_cmpint (v_uint, ==, 0); + g_variant_lookup (child, "flags", "u", &v_uint); + g_assert_cmpint (v_uint, ==, 1); + nm_clear_g_variant (&child); + nm_clear_g_variant (&attr); + /* IEEE 802.1 - VLAN Name */ attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME, G_VARIANT_TYPE_STRING); nmtst_assert_variant_string (attr, "v2-0488-03-0505"); @@ -297,6 +349,18 @@ _test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener) nmtst_assert_variant_uint32 (attr, 488); nm_clear_g_variant (&attr); + /* new VLAN attributes */ + attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_VLANS, G_VARIANT_TYPE ("aa{sv}")); + g_assert_cmpuint (g_variant_n_children (attr), ==, 1); + child = g_variant_get_child_value (attr, 0); + g_assert (child); + g_variant_lookup (child, "vid", "u", &v_uint); + g_assert_cmpint (v_uint, ==, 488); + g_variant_lookup (child, "name", "&s", &v_str); + g_assert_cmpstr (v_str, ==, "v2-0488-03-0505"); + nm_clear_g_variant (&child); + nm_clear_g_variant (&attr); + /* unsupported: IEEE 802.1 - Protocol Identity */ } diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c index dcc161d2..da7d0685 100644 --- a/src/devices/wifi/nm-device-iwd.c +++ b/src/devices/wifi/nm-device-iwd.c @@ -22,7 +22,7 @@ #include "nm-device-iwd.h" -#include "nm-common-macros.h" +#include "nm-libnm-core-intern/nm-common-macros.h" #include "devices/nm-device.h" #include "devices/nm-device-private.h" #include "nm-utils.h" @@ -39,7 +39,7 @@ #include "nm-config.h" #include "nm-iwd-manager.h" #include "nm-dbus-manager.h" -#include "nm-dbus-compat.h" +#include "nm-std-aux/nm-dbus-compat.h" #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceIwd); diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 64869672..3a58c620 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -28,7 +28,7 @@ #include "nm-device-wifi-p2p.h" #include "nm-wifi-ap.h" -#include "nm-common-macros.h" +#include "nm-libnm-core-intern/nm-common-macros.h" #include "devices/nm-device.h" #include "devices/nm-device-private.h" #include "nm-dbus-manager.h" diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c index d668f0d8..494fca32 100644 --- a/src/devices/wifi/nm-iwd-manager.c +++ b/src/devices/wifi/nm-iwd-manager.c @@ -29,7 +29,7 @@ #include "nm-manager.h" #include "nm-device-iwd.h" #include "nm-wifi-utils.h" -#include "nm-utils/nm-random-utils.h" +#include "nm-glib-aux/nm-random-utils.h" #include "settings/nm-settings.h" /*****************************************************************************/ diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index 1cb549b0..ffdc61a6 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -679,19 +679,6 @@ complete_connection (NMModem *_self, } if (MODEM_CAPS_3GPP (modem_caps)) { - NMSettingGsm *s_gsm; - - s_gsm = nm_connection_get_setting_gsm (connection); - if (!s_gsm) { - /* Need a GSM setting at least */ - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_SETTING, - _("GSM mobile broadband connection requires a 'gsm' setting")); - g_prefix_error (error, "%s: ", NM_SETTING_GSM_SETTING_NAME); - return FALSE; - } - nm_utils_complete_generic (NM_PLATFORM_GET, connection, NM_SETTING_GSM_SETTING_NAME, diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c index fac14d69..4be089c0 100644 --- a/src/devices/wwan/nm-modem-manager.c +++ b/src/devices/wwan/nm-modem-manager.c @@ -32,7 +32,7 @@ #define sd_booted() FALSE #endif -#include "nm-dbus-compat.h" +#include "nm-std-aux/nm-dbus-compat.h" #include "nm-modem.h" #include "nm-modem-broadband.h" diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 2217f2a2..17794229 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -885,7 +885,7 @@ nm_modem_get_configured_mtu (NMDevice *self, NMDeviceMtuSource *out_source) } property_name = NM_IS_SETTING_GSM (setting) ? "gsm.mtu" : "cdma.mtu"; - mtu_default = nm_device_get_configured_mtu_from_connection_default (self, property_name); + mtu_default = nm_device_get_configured_mtu_from_connection_default (self, property_name, G_MAXUINT32); if (mtu_default >= 0) { *out_source = NM_DEVICE_MTU_SOURCE_CONNECTION; return (guint32) mtu_default; diff --git a/src/dhcp/meson.build b/src/dhcp/meson.build index a5dd3151..c1f28be0 100644 --- a/src/dhcp/meson.build +++ b/src/dhcp/meson.build @@ -8,7 +8,7 @@ cflags = [ executable( name, name + '.c', - dependencies: nm_core_dep, + dependencies: libnm_core_dep, c_args: cflags, link_args: ldflags_linker_script_binary, link_depends: linker_script_binary, diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c index a4fccce0..d494eff0 100644 --- a/src/dhcp/nm-dhcp-client.c +++ b/src/dhcp/nm-dhcp-client.c @@ -28,8 +28,8 @@ #include <stdlib.h> #include <linux/rtnetlink.h> -#include "nm-utils/nm-dedup-multi.h" -#include "nm-utils/nm-random-utils.h" +#include "nm-glib-aux/nm-dedup-multi.h" +#include "nm-glib-aux/nm-random-utils.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c index cbd706fa..85ca3704 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.c +++ b/src/dhcp/nm-dhcp-dhclient-utils.c @@ -25,7 +25,7 @@ #include <arpa/inet.h> #include <net/if.h> -#include "nm-utils/nm-dedup-multi.h" +#include "nm-glib-aux/nm-dedup-multi.h" #include "nm-dhcp-utils.h" #include "nm-ip4-config.h" @@ -50,10 +50,10 @@ static void add_request (GPtrArray *array, const char *item) { - int i; + guint i; for (i = 0; i < array->len; i++) { - if (!strcmp (g_ptr_array_index (array, i), item)) + if (nm_streq (array->pdata[i], item)) return; } g_ptr_array_add (array, g_strdup (item)); @@ -62,37 +62,48 @@ add_request (GPtrArray *array, const char *item) static gboolean grab_request_options (GPtrArray *store, const char* line) { - char **areq, **aiter; - gboolean end = FALSE; + gs_free const char **line_v = NULL; + gsize i; /* Grab each 'request' or 'also request' option and save for later */ - areq = g_strsplit_set (line, "\t ,", -1); - for (aiter = areq; aiter && *aiter; aiter++) { - if (!strlen (g_strstrip (*aiter))) - continue; + line_v = nm_utils_strsplit_set (line, "\t ,"); + for (i = 0; line_v && line_v[i]; i++) { + const char *ss = nm_str_skip_leading_spaces (line_v[i]); + gsize l; + gboolean end = FALSE; - if (*aiter[0] == ';') { + if (!ss[0]) + continue; + if (ss[0] == ';') { /* all done */ - end = TRUE; - break; + return TRUE; } - if (!g_ascii_isalnum ((*aiter)[0])) + if (!g_ascii_isalnum (ss[0])) continue; - if ((*aiter)[strlen (*aiter) - 1] == ';') { + l = strlen (ss); + + while ( l > 0 + && g_ascii_isspace (ss[l - 1])) { + ((char *) ss)[l - 1] = '\0'; + l--; + } + if ( l > 0 + && ss[l - 1] == ';') { /* Remove the EOL marker */ - (*aiter)[strlen (*aiter) - 1] = '\0'; + ((char *) ss)[l - 1] = '\0'; end = TRUE; } - add_request (store, *aiter); - } + if (ss[0]) + add_request (store, ss); - if (areq) - g_strfreev (areq); + if (end) + return TRUE; + } - return end; + return FALSE; } static void @@ -278,8 +289,9 @@ nm_dhcp_dhclient_create_config (const char *interface, const char *orig_contents, GBytes **out_new_client_id) { - GString *new_contents; - GPtrArray *fqdn_opts, *reqs; + nm_auto_free_gstring GString *new_contents = NULL; + gs_unref_ptrarray GPtrArray *fqdn_opts = NULL; + gs_unref_ptrarray GPtrArray *reqs = NULL; gboolean reset_reqlist = FALSE; int i; @@ -288,11 +300,11 @@ nm_dhcp_dhclient_create_config (const char *interface, nm_assert (!out_new_client_id || !*out_new_client_id); new_contents = g_string_new (_("# Created by NetworkManager\n")); - fqdn_opts = g_ptr_array_sized_new (5); reqs = g_ptr_array_new_full (5, g_free); if (orig_contents) { - char **lines, **line; + gs_free const char **lines = NULL; + gsize line_i; int nest = 0; gboolean in_alsoreq = FALSE; gboolean in_req = FALSE; @@ -301,19 +313,23 @@ nm_dhcp_dhclient_create_config (const char *interface, g_string_append_printf (new_contents, _("# Merged from %s\n\n"), orig_path); intf[0] = '\0'; - lines = g_strsplit_set (orig_contents, "\n\r", 0); - for (line = lines; lines && *line; line++) { - char *p = *line; + lines = nm_utils_strsplit_set (orig_contents, "\n\r"); + for (line_i = 0; lines && lines[line_i]; line_i++) { + const char *line = nm_str_skip_leading_spaces (lines[line_i]); + const char *p; - if (!strlen (g_strstrip (p))) + if (line[0] == '\0') continue; + g_strchomp ((char *) line); + + p = line; if (in_req) { /* pass */ } else if (strchr (p, '{')) { nest++; if ( !intf[0] - && g_str_has_prefix (p, "interface")) + && NM_STR_HAS_PREFIX (p, "interface")) if (read_interface (p, intf, sizeof (intf))) continue; } else if (strchr (p, '}')) { @@ -363,6 +379,8 @@ nm_dhcp_dhclient_create_config (const char *interface, * default ones set by NM, add them later */ if (!strncmp (p, FQDN_TAG_PREFIX, NM_STRLEN (FQDN_TAG_PREFIX))) { + if (!fqdn_opts) + fqdn_opts = g_ptr_array_new_full (5, g_free); g_ptr_array_add (fqdn_opts, g_strdup (p + NM_STRLEN (FQDN_TAG_PREFIX))); continue; } @@ -397,12 +415,9 @@ nm_dhcp_dhclient_create_config (const char *interface, } /* Existing configuration line is OK, add it to new configuration */ - g_string_append (new_contents, *line); + g_string_append (new_contents, line); g_string_append_c (new_contents, '\n'); } - - if (lines) - g_strfreev (lines); } else g_string_append_c (new_contents, '\n'); @@ -436,17 +451,16 @@ nm_dhcp_dhclient_create_config (const char *interface, /* And add it to the dhclient configuration */ for (i = 0; i < reqs->len; i++) g_string_append_printf (new_contents, "also request %s;\n", (char *) reqs->pdata[i]); - g_ptr_array_free (reqs, TRUE); - for (i = 0; i < fqdn_opts->len; i++) { - char *t = g_ptr_array_index (fqdn_opts, i); + if (fqdn_opts) { + for (i = 0; i < fqdn_opts->len; i++) { + const char *t = fqdn_opts->pdata[i]; - if (i == 0) - g_string_append_printf (new_contents, "\n# FQDN options from %s\n", orig_path); - g_string_append_printf (new_contents, FQDN_TAG_PREFIX "%s\n", t); - g_free (t); + if (i == 0) + g_string_append_printf (new_contents, "\n# FQDN options from %s\n", orig_path); + g_string_append_printf (new_contents, FQDN_TAG_PREFIX "%s\n", t); + } } - g_ptr_array_free (fqdn_opts, TRUE); g_string_append_c (new_contents, '\n'); @@ -458,7 +472,7 @@ nm_dhcp_dhclient_create_config (const char *interface, interface, anycast_addr); } - return g_string_free (new_contents, FALSE); + return g_string_free (g_steal_pointer (&new_contents), FALSE); } /* Roughly follow what dhclient's quotify_buf() and pretty_escape() functions do */ @@ -553,9 +567,9 @@ error: GBytes * nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error) { - GBytes *duid = NULL; - char *contents; - char **line, **split, *p, *e; + gs_free char *contents = NULL; + gs_free const char **contents_v = NULL; + gsize i; if (!g_file_test (leasefile, G_FILE_TEST_EXISTS)) return NULL; @@ -563,25 +577,29 @@ nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error) if (!g_file_get_contents (leasefile, &contents, NULL, error)) return NULL; - split = g_strsplit_set (contents, "\n\r", -1); - for (line = split; line && *line && (duid == NULL); line++) { - p = g_strstrip (*line); - if (g_str_has_prefix (p, DUID_PREFIX)) { - p += strlen (DUID_PREFIX); + contents_v = nm_utils_strsplit_set (contents, "\n\r"); + for (i = 0; contents_v && contents_v[i]; i++) { + const char *p = nm_str_skip_leading_spaces (contents_v[i]); + GBytes *duid; - /* look for trailing "; */ - e = p + strlen (p) - 2; - if (strcmp (e, "\";") != 0) - continue; - *e = '\0'; + if (!NM_STR_HAS_PREFIX (p, DUID_PREFIX)) + continue; - duid = nm_dhcp_dhclient_unescape_duid (p); - } + p += NM_STRLEN (DUID_PREFIX); + + g_strchomp ((char *) p); + + if (!NM_STR_HAS_SUFFIX (p, "\";")) + continue; + + ((char *) p)[strlen (p) - 2] = '\0'; + + duid = nm_dhcp_dhclient_unescape_duid (p); + if (duid) + return duid; } - g_free (contents); - g_strfreev (split); - return duid; + return NULL; } gboolean @@ -590,14 +608,12 @@ nm_dhcp_dhclient_save_duid (const char *leasefile, GError **error) { gs_free char *escaped_duid = NULL; - gs_strfreev char **lines = NULL; - char **iter, *l; - GString *s; - gboolean success; + gs_free const char **lines = NULL; + nm_auto_free_gstring GString *s = NULL; + const char *const*iter; gsize len = 0; g_return_val_if_fail (leasefile != NULL, FALSE); - if (!duid) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "missing duid"); @@ -605,19 +621,17 @@ nm_dhcp_dhclient_save_duid (const char *leasefile, } escaped_duid = nm_dhcp_dhclient_escape_duid (duid); - g_return_val_if_fail (escaped_duid != NULL, FALSE); + nm_assert (escaped_duid); if (g_file_test (leasefile, G_FILE_TEST_EXISTS)) { - char *contents = NULL; + gs_free char *contents = NULL; if (!g_file_get_contents (leasefile, &contents, &len, error)) { g_prefix_error (error, "failed to read lease file %s: ", leasefile); return FALSE; } - g_assert (contents); - lines = g_strsplit_set (contents, "\n\r", -1); - g_free (contents); + lines = nm_utils_strsplit_set_with_empty (contents, "\n\r"); } s = g_string_sized_new (len + 50); @@ -625,37 +639,38 @@ nm_dhcp_dhclient_save_duid (const char *leasefile, /* Preserve existing leasefile contents */ if (lines) { - for (iter = lines; iter && *iter; iter++) { - l = *iter; - while (g_ascii_isspace (*l)) - l++; + for (iter = lines; *iter; iter++) { + const char *str = *iter; + const char *l; + /* If we find an uncommented DUID in the file, check if * equal to the one we are going to write: if so, no need * to update the lease file, otherwise skip the old DUID. */ + l = nm_str_skip_leading_spaces (str); if (g_str_has_prefix (l, DUID_PREFIX)) { gs_strfreev char **split = NULL; split = g_strsplit (l, "\"", -1); - if (nm_streq0 (split[1], escaped_duid)) { - g_string_free (s, TRUE); + if ( split[0] + && nm_streq0 (split[1], escaped_duid)) return TRUE; - } + continue; } - if (*iter[0]) - g_string_append (s, *iter); + if (str) + g_string_append (s, str); /* avoid to add an extra '\n' at the end of file */ if ((iter[1]) != NULL) g_string_append_c (s, '\n'); } } - success = g_file_set_contents (leasefile, s->str, -1, error); - if (!success) + if (!g_file_set_contents (leasefile, s->str, -1, error)) { g_prefix_error (error, "failed to set DUID in lease file %s: ", leasefile); + return FALSE; + } - g_string_free (s, TRUE); - return success; + return TRUE; } diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c index af702cb4..b655a1eb 100644 --- a/src/dhcp/nm-dhcp-dhclient.c +++ b/src/dhcp/nm-dhcp-dhclient.c @@ -36,7 +36,7 @@ #include <arpa/inet.h> #include <ctype.h> -#include "nm-utils/nm-dedup-multi.h" +#include "nm-glib-aux/nm-dedup-multi.h" #include "nm-utils.h" #include "nm-config.h" diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c index 7063c82c..fe843a2c 100644 --- a/src/dhcp/nm-dhcp-manager.c +++ b/src/dhcp/nm-dhcp-manager.c @@ -32,7 +32,7 @@ #include <fcntl.h> #include <stdio.h> -#include "nm-utils/nm-dedup-multi.h" +#include "nm-glib-aux/nm-dedup-multi.h" #include "nm-config.h" #include "NetworkManagerUtils.h" diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c index 70ed8715..1cd5ba27 100644 --- a/src/dhcp/nm-dhcp-systemd.c +++ b/src/dhcp/nm-dhcp-systemd.c @@ -26,8 +26,8 @@ #include <ctype.h> #include <net/if_arp.h> -#include "nm-utils/nm-dedup-multi.h" -#include "nm-utils/unaligned.h" +#include "nm-glib-aux/nm-dedup-multi.h" +#include "nm-std-aux/unaligned.h" #include "nm-utils.h" #include "nm-config.h" diff --git a/src/dhcp/nm-dhcp-utils.c b/src/dhcp/nm-dhcp-utils.c index 5227eea7..7aa867c0 100644 --- a/src/dhcp/nm-dhcp-utils.c +++ b/src/dhcp/nm-dhcp-utils.c @@ -22,7 +22,7 @@ #include <unistd.h> #include <arpa/inet.h> -#include "nm-utils/nm-dedup-multi.h" +#include "nm-glib-aux/nm-dedup-multi.h" #include "nm-dhcp-utils.h" #include "nm-utils.h" @@ -100,67 +100,56 @@ out: return have_routes; } -static const char ** -process_dhclient_rfc3442_route (const char **octets, - NMPlatformIP4Route *route, - gboolean *success) +static gboolean +process_dhclient_rfc3442_route (const char *const**p_octets, + NMPlatformIP4Route *route) { - const char **o = octets; - int addr_len = 0, i = 0; - long int tmp; - char *next_hop; - guint32 tmp_addr; - - *success = FALSE; - - if (!*o) - return o; /* no prefix */ - - tmp = strtol (*o, NULL, 10); - if (tmp < 0 || tmp > 32) /* 32 == max IP4 prefix length */ - return o; - - memset (route, 0, sizeof (*route)); - route->plen = tmp; + const char *const*o = *p_octets; + gs_free char *next_hop = NULL; + int addr_len; + int v_plen; + in_addr_t tmp_addr; + in_addr_t v_network = 0; + + v_plen = _nm_utils_ascii_str_to_int64 (*o, 10, 0, 32, -1); + if (v_plen == -1) + return FALSE; o++; - if (tmp > 0) - addr_len = ((tmp - 1) / 8) + 1; + addr_len = v_plen > 0 + ? ((v_plen - 1) / 8) + 1 + : 0; /* ensure there's at least the address + next hop left */ - if (g_strv_length ((char **) o) < addr_len + 4) - goto error; + if (NM_PTRARRAY_LEN (o) < addr_len + 4) + return FALSE; - if (tmp) { + if (v_plen > 0) { const char *addr[4] = { "0", "0", "0", "0" }; - char *str_addr; + gs_free char *str_addr = NULL; + int i; for (i = 0; i < addr_len; i++) addr[i] = *o++; str_addr = g_strjoin (".", addr[0], addr[1], addr[2], addr[3], NULL); - if (inet_pton (AF_INET, str_addr, &tmp_addr) <= 0) { - g_free (str_addr); - goto error; - } - g_free (str_addr); - route->network = nm_utils_ip4_address_clear_host_address (tmp_addr, tmp); + if (inet_pton (AF_INET, str_addr, &tmp_addr) <= 0) + return FALSE; + v_network = nm_utils_ip4_address_clear_host_address (tmp_addr, v_plen); } - /* Handle next hop */ next_hop = g_strjoin (".", o[0], o[1], o[2], o[3], NULL); - if (inet_pton (AF_INET, next_hop, &tmp_addr) <= 0) { - g_free (next_hop); - goto error; - } - route->gateway = tmp_addr; - g_free (next_hop); - - *success = TRUE; - return o + 4; /* advance to past the next hop */ + o += 4; + if (inet_pton (AF_INET, next_hop, &tmp_addr) <= 0) + return FALSE; -error: - return o; + *route = (NMPlatformIP4Route) { + .network = v_network, + .plen = v_plen, + .gateway = tmp_addr, + }; + *p_octets = o; + return TRUE; } static gboolean @@ -171,23 +160,23 @@ ip4_process_dhclient_rfc3442_routes (const char *iface, NMIP4Config *ip4_config, guint32 *gwaddr) { - char **octets, **o; + gs_free const char **octets = NULL; + const char *const*o; gboolean have_routes = FALSE; - NMPlatformIP4Route route; - gboolean success; - o = octets = g_strsplit_set (str, " .", 0); - if (g_strv_length (octets) < 5) { + octets = nm_utils_strsplit_set_with_empty (str, " ."); + if (NM_PTRARRAY_LEN (octets) < 5) { _LOG2W (LOGD_DHCP4, iface, "ignoring invalid classless static routes '%s'", str); - goto out; + return FALSE; } + o = octets; while (*o) { - memset (&route, 0, sizeof (route)); - o = (char **) process_dhclient_rfc3442_route ((const char **) o, &route, &success); - if (!success) { + NMPlatformIP4Route route; + + if (!process_dhclient_rfc3442_route (&o, &route)) { _LOG2W (LOGD_DHCP4, iface, "ignoring invalid classless static routes"); - break; + return have_routes; } have_routes = TRUE; @@ -211,8 +200,6 @@ ip4_process_dhclient_rfc3442_routes (const char *iface, } } -out: - g_strfreev (octets); return have_routes; } diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c index 55d712b0..1eac3643 100644 --- a/src/dhcp/tests/test-dhcp-dhclient.c +++ b/src/dhcp/tests/test-dhcp-dhclient.c @@ -24,7 +24,7 @@ #include <arpa/inet.h> #include <linux/rtnetlink.h> -#include "nm-utils/nm-dedup-multi.h" +#include "nm-glib-aux/nm-dedup-multi.h" #include "NetworkManagerUtils.h" #include "dhcp/nm-dhcp-dhclient-utils.h" diff --git a/src/dhcp/tests/test-dhcp-utils.c b/src/dhcp/tests/test-dhcp-utils.c index 240d868c..118082a5 100644 --- a/src/dhcp/tests/test-dhcp-utils.c +++ b/src/dhcp/tests/test-dhcp-utils.c @@ -23,7 +23,7 @@ #include <arpa/inet.h> #include <linux/rtnetlink.h> -#include "nm-utils/nm-dedup-multi.h" +#include "nm-glib-aux/nm-dedup-multi.h" #include "nm-utils.h" #include "dhcp/nm-dhcp-utils.h" diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index c7c561c4..27c3e710 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -347,13 +347,20 @@ gboolean nm_dns_manager_has_systemd_resolved (NMDnsManager *self) { NMDnsManagerPrivate *priv; + NMDnsSystemdResolved *plugin = NULL; g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE); priv = NM_DNS_MANAGER_GET_PRIVATE (self); - return priv->sd_resolve_plugin - || NM_IS_DNS_SYSTEMD_RESOLVED (priv->plugin); + if (priv->sd_resolve_plugin) { + nm_assert (!NM_IS_DNS_SYSTEMD_RESOLVED (priv->plugin)); + plugin = NM_DNS_SYSTEMD_RESOLVED (priv->sd_resolve_plugin); + } else if (NM_IS_DNS_SYSTEMD_RESOLVED (priv->plugin)) + plugin = NM_DNS_SYSTEMD_RESOLVED (priv->plugin); + + return plugin + && nm_dns_systemd_resolved_is_running (plugin); } /*****************************************************************************/ diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c index 10797b86..5d262ba3 100644 --- a/src/dns/nm-dns-systemd-resolved.c +++ b/src/dns/nm-dns-systemd-resolved.c @@ -31,7 +31,7 @@ #include <sys/stat.h> #include <linux/if.h> -#include "nm-utils/nm-c-list.h" +#include "nm-glib-aux/nm-c-list.h" #include "nm-core-internal.h" #include "platform/nm-platform.h" #include "nm-utils.h" @@ -42,9 +42,11 @@ #include "nm-setting-connection.h" #include "devices/nm-device.h" #include "NetworkManagerUtils.h" +#include "nm-std-aux/nm-dbus-compat.h" -#define SYSTEMD_RESOLVED_DBUS_SERVICE "org.freedesktop.resolve1" -#define SYSTEMD_RESOLVED_DBUS_PATH "/org/freedesktop/resolve1" +#define SYSTEMD_RESOLVED_DBUS_SERVICE "org.freedesktop.resolve1" +#define SYSTEMD_RESOLVED_MANAGER_IFACE "org.freedesktop.resolve1.Manager" +#define SYSTEMD_RESOLVED_DBUS_PATH "/org/freedesktop/resolve1" /*****************************************************************************/ @@ -62,10 +64,14 @@ typedef struct { /*****************************************************************************/ typedef struct { - GDBusProxy *resolve; - GCancellable *init_cancellable; - GCancellable *update_cancellable; + GDBusConnection *dbus_connection; + GCancellable *cancellable; CList request_queue_lst_head; + guint name_owner_changed_id; + bool send_updates_warn_ratelimited:1; + bool try_start_blocked:1; + bool dbus_has_owner:1; + bool dbus_initied:1; } NMDnsSystemdResolvedPrivate; struct _NMDnsSystemdResolved { @@ -121,17 +127,26 @@ _interface_config_free (InterfaceConfig *config) static void call_done (GObject *source, GAsyncResult *r, gpointer user_data) { - GVariant *v; - GError *error = NULL; + gs_unref_variant GVariant *v = NULL; + gs_free_error GError *error = NULL; NMDnsSystemdResolved *self = (NMDnsSystemdResolved *) user_data; + NMDnsSystemdResolvedPrivate *priv; + + v = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), r, &error); + if ( !v + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); - v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), r, &error); if (!v) { - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - _LOGW ("Failed: %s", error->message); - g_error_free (error); - } + if (!priv->send_updates_warn_ratelimited) { + priv->send_updates_warn_ratelimited = TRUE; + _LOGW ("send-updates failed to update systemd-resolved: %s", error->message); + } else + _LOGD ("send-updates failed: %s", error->message); + } else + priv->send_updates_warn_ratelimited = FALSE; } static void @@ -175,12 +190,11 @@ static void free_pending_updates (NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); - RequestItem *request_item, *request_item_safe; + RequestItem *request_item; - c_list_for_each_entry_safe (request_item, - request_item_safe, - &priv->request_queue_lst_head, - request_queue_lst) + while ((request_item = c_list_first_entry (&priv->request_queue_lst_head, + RequestItem, + request_queue_lst))) _request_item_free (request_item); } @@ -267,27 +281,74 @@ static void send_updates (NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); - RequestItem *request_item, *request_item_safe; + RequestItem *request_item; + + if (c_list_is_empty (&priv->request_queue_lst_head)) { + /* nothing to do. */ + return; + } + + if (!priv->dbus_initied) { + _LOGT ("send-updates: D-Bus connection not ready"); + return; + } - nm_clear_g_cancellable (&priv->update_cancellable); + if (!priv->dbus_has_owner) { + if (priv->try_start_blocked) { + /* we have no name owner and we already tried poking the service to + * autostart. */ + _LOGT ("send-updates: no name owner"); + return; + } - if (!priv->resolve) + _LOGT ("send-updates: no name owner. Try start service..."); + priv->try_start_blocked = TRUE; + + g_dbus_connection_call (priv->dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "StartServiceByName", + g_variant_new ("(su)", SYSTEMD_RESOLVED_DBUS_SERVICE, 0u), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); return; + } - priv->update_cancellable = g_cancellable_new (); - - c_list_for_each_entry_safe (request_item, - request_item_safe, - &priv->request_queue_lst_head, - request_queue_lst) { - g_dbus_proxy_call (priv->resolve, - request_item->operation, - request_item->argument, - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->update_cancellable, - call_done, - self); + _LOGT ("send-updates: start %lu requests", + c_list_length (&priv->request_queue_lst_head)); + + nm_clear_g_cancellable (&priv->cancellable); + + priv->cancellable = g_cancellable_new (); + + while ((request_item = c_list_first_entry (&priv->request_queue_lst_head, + RequestItem, + request_queue_lst))) { + /* Above we explicitly call "StartServiceByName" trying to avoid D-Bus activating systmd-resolved + * multiple times. There is still a race, were we might hit this line although actually + * the service just quit this very moment. In that case, we would try to D-Bus activate the + * service multiple times during each call (something we wanted to avoid). + * + * But this is hard to avoid, because we'd have to check the error failure to detect the reason + * and retry. The race is not critical, because at worst it results in logging a warning + * about failure to start systemd.resolved. */ + g_dbus_connection_call (priv->dbus_connection, + SYSTEMD_RESOLVED_DBUS_SERVICE, + SYSTEMD_RESOLVED_DBUS_PATH, + SYSTEMD_RESOLVED_MANAGER_IFACE, + request_item->operation, + request_item->argument, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->cancellable, + call_done, + self); _request_item_free (request_item); } } @@ -361,28 +422,100 @@ get_name (NMDnsPlugin *plugin) /*****************************************************************************/ static void -resolved_proxy_created (GObject *source, GAsyncResult *r, gpointer user_data) +name_owner_changed (NMDnsSystemdResolved *self, + const char *owner) { - NMDnsSystemdResolved *self = (NMDnsSystemdResolved *) user_data; + NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); + + owner = nm_str_not_empty (owner); + + if (!owner) + _LOGT ("D-Bus name for systemd-resolved has no owner"); + else + _LOGT ("D-Bus name for systemd-resolved has owner %s", owner); + + priv->dbus_has_owner = !!owner; + if (owner) + priv->try_start_blocked = FALSE; + + send_updates (self); +} + +static void +name_owner_changed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + NMDnsSystemdResolved *self = user_data; + NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); + const char *new_owner; + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)"))) + return; + + g_variant_get (parameters, + "(&s&s&s)", + NULL, + NULL, + &new_owner); + + if (!priv->dbus_initied) { + /* There was a race and we got a NameOwnerChanged signal before GetNameOwner + * returns. */ + priv->dbus_initied = TRUE; + nm_clear_g_cancellable (&priv->cancellable); + } + + name_owner_changed (user_data, new_owner); +} + +static void +get_name_owner_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + NMDnsSystemdResolved *self; NMDnsSystemdResolvedPrivate *priv; + gs_unref_variant GVariant *ret = NULL; gs_free_error GError *error = NULL; - GDBusProxy *resolve; + const char *owner = NULL; - resolve = g_dbus_proxy_new_finish (r, &error); - if ( !resolve + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error); + if ( !ret && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; + if (ret) + g_variant_get (ret, "(&s)", &owner); + + self = user_data; priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); - g_clear_object (&priv->init_cancellable); - if (!resolve) { - _LOGW ("failed to connect to resolved via DBus: %s", error->message); - g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED); - return; - } - priv->resolve = resolve; - send_updates (self); + g_clear_object (&priv->cancellable); + + priv->dbus_initied = TRUE; + + name_owner_changed (self, owner); +} + +/*****************************************************************************/ + +gboolean +nm_dns_systemd_resolved_is_running (NMDnsSystemdResolved *self) +{ + NMDnsSystemdResolvedPrivate *priv; + + g_return_val_if_fail (NM_IS_DNS_SYSTEMD_RESOLVED (self), FALSE); + + priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); + + return priv->dbus_initied + && ( priv->dbus_has_owner + || !priv->try_start_blocked); } /*****************************************************************************/ @@ -394,17 +527,35 @@ nm_dns_systemd_resolved_init (NMDnsSystemdResolved *self) c_list_init (&priv->request_queue_lst_head); - priv->init_cancellable = g_cancellable_new (); - g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, - NULL, - SYSTEMD_RESOLVED_DBUS_SERVICE, - SYSTEMD_RESOLVED_DBUS_PATH, - SYSTEMD_RESOLVED_DBUS_SERVICE ".Manager", - priv->init_cancellable, - resolved_proxy_created, - self); + priv->dbus_connection = nm_g_object_ref (nm_dbus_manager_get_dbus_connection (nm_dbus_manager_get ())); + if (!priv->dbus_connection) { + _LOGD ("no D-Bus connection"); + return; + } + + priv->name_owner_changed_id = g_dbus_connection_signal_subscribe (priv->dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_INTERFACE_DBUS, + "NameOwnerChanged", + DBUS_PATH_DBUS, + SYSTEMD_RESOLVED_DBUS_SERVICE, + G_DBUS_SIGNAL_FLAGS_NONE, + name_owner_changed_cb, + self, + NULL); + priv->cancellable = g_cancellable_new (); + g_dbus_connection_call (priv->dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetNameOwner", + g_variant_new ("(s)", SYSTEMD_RESOLVED_DBUS_SERVICE), + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->cancellable, + get_name_owner_cb, + self); } NMDnsPlugin * @@ -420,9 +571,15 @@ dispose (GObject *object) NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); free_pending_updates (self); - g_clear_object (&priv->resolve); - nm_clear_g_cancellable (&priv->init_cancellable); - nm_clear_g_cancellable (&priv->update_cancellable); + + if (priv->name_owner_changed_id != 0) { + g_dbus_connection_signal_unsubscribe (priv->dbus_connection, + nm_steal_int (&priv->name_owner_changed_id)); + } + + nm_clear_g_cancellable (&priv->cancellable); + + g_clear_object (&priv->dbus_connection); G_OBJECT_CLASS (nm_dns_systemd_resolved_parent_class)->dispose (object); } diff --git a/src/dns/nm-dns-systemd-resolved.h b/src/dns/nm-dns-systemd-resolved.h index 800a60c1..b79ff5e4 100644 --- a/src/dns/nm-dns-systemd-resolved.h +++ b/src/dns/nm-dns-systemd-resolved.h @@ -36,4 +36,6 @@ GType nm_dns_systemd_resolved_get_type (void); NMDnsPlugin *nm_dns_systemd_resolved_new (void); +gboolean nm_dns_systemd_resolved_is_running (NMDnsSystemdResolved *self); + #endif /* __NETWORKMANAGER_DNS_SYSTEMD_RESOLVED_H__ */ diff --git a/src/initrd/meson.build b/src/initrd/meson.build index a12b718a..fa8c016b 100644 --- a/src/initrd/meson.build +++ b/src/initrd/meson.build @@ -10,7 +10,7 @@ libnmi_core = static_library( c_args: nm_cflags, sources: sources, include_directories: src_inc, - dependencies: nm_core_dep, + dependencies: libnm_core_dep, ) name = 'nm-initrd-generator' @@ -19,7 +19,7 @@ executable( name + '.c', c_args: nm_cflags, include_directories: src_inc, - dependencies: [ nm_core_dep ], + dependencies: [ libnm_core_dep ], link_with: [libnetwork_manager_base, libnmi_core], link_args: ldflags_linker_script_binary, link_depends: linker_script_binary, diff --git a/src/initrd/nm-initrd-generator.c b/src/initrd/nm-initrd-generator.c index b84543c4..4c1c4037 100644 --- a/src/initrd/nm-initrd-generator.c +++ b/src/initrd/nm-initrd-generator.c @@ -23,7 +23,7 @@ #include "nm-core-internal.h" #include "nm-keyfile-internal.h" #include "nm-initrd-generator.h" -#include "nm-utils/nm-io-utils.h" +#include "nm-glib-aux/nm-io-utils.h" /*****************************************************************************/ diff --git a/src/meson.build b/src/meson.build index 06a0dc57..a3ac4625 100644 --- a/src/meson.build +++ b/src/meson.build @@ -13,7 +13,7 @@ nm_cflags = ['-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_DAEMON' nm_dep = declare_dependency( include_directories: src_inc, - dependencies: nm_core_dep, + dependencies: libnm_core_dep, compile_args: nm_cflags, ) @@ -35,6 +35,7 @@ sources = files( 'platform/nm-platform-utils.c', 'platform/nmp-netns.c', 'platform/nmp-object.c', + 'platform/nmp-rules-manager.c', 'main-utils.c', 'NetworkManagerUtils.c', 'nm-core-utils.c', @@ -48,7 +49,7 @@ sources = files( deps = [ libsystemd_dep, libudev_dep, - nm_core_dep, + libnm_core_dep, ] if enable_wext @@ -148,7 +149,7 @@ nm_deps = [ dl_dep, libndp_dep, libudev_dep, - nm_core_dep, + libnm_core_dep, shared_n_acd_dep, logind_dep, ] @@ -185,7 +186,7 @@ deps = [ dl_dep, libndp_dep, libudev_dep, - nm_core_dep, + libnm_core_dep, ] name = 'nm-iface-helper' @@ -215,7 +216,7 @@ if enable_tests deps = [ libudev_dep, - nm_core_dep, + libnm_core_dep, ] test_cflags = ['-DNETWORKMANAGER_COMPILATION_TEST'] diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index 7b9f2cc7..17568c30 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -22,7 +22,7 @@ #include "nm-active-connection.h" -#include "nm-common-macros.h" +#include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-dbus-interface.h" #include "devices/nm-device.h" #include "settings/nm-settings-connection.h" diff --git a/src/nm-auth-subject.c b/src/nm-auth-subject.c index dff331a8..5599201e 100644 --- a/src/nm-auth-subject.c +++ b/src/nm-auth-subject.c @@ -173,9 +173,10 @@ _new_unix_process (GDBusMethodInvocation *context, GDBusMessage *message) { NMAuthSubject *self; - gboolean success = FALSE; - gulong pid = 0, uid = 0; - gs_free char *dbus_sender = NULL; + const char *dbus_sender = NULL; + gulong uid = 0; + gulong pid = 0; + gboolean success; g_return_val_if_fail (context || (connection && message), NULL); diff --git a/src/nm-auth-utils.c b/src/nm-auth-utils.c index 146f6883..a0ad84c1 100644 --- a/src/nm-auth-utils.c +++ b/src/nm-auth-utils.c @@ -22,7 +22,7 @@ #include "nm-auth-utils.h" -#include "nm-utils/nm-c-list.h" +#include "nm-glib-aux/nm-c-list.h" #include "nm-setting-connection.h" #include "nm-auth-subject.h" #include "nm-auth-manager.h" diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c index 9b1622ec..f715eea6 100644 --- a/src/nm-checkpoint.c +++ b/src/nm-checkpoint.c @@ -38,15 +38,19 @@ typedef struct { char *original_dev_path; + char *original_dev_name; + NMDeviceType dev_type; NMDevice *device; NMConnection *applied_connection; NMConnection *settings_connection; guint64 ac_version_id; NMDeviceState state; + bool is_software:1; bool realized:1; bool activation_lifetime_bound_to_profile_visiblity:1; NMUnmanFlagOp unmanaged_explicit; NMActivationReason activation_reason; + gulong dev_exported_change_id; } DeviceCheckpoint; NM_GOBJECT_PROPERTIES_DEFINE (NMCheckpoint, @@ -58,14 +62,15 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMCheckpoint, struct _NMCheckpointPrivate { /* properties */ GHashTable *devices; + GPtrArray *removed_devices; gint64 created_at_ms; guint32 rollback_timeout_s; guint timeout_id; /* private members */ - /* private members */ NMManager *manager; NMCheckpointCreateFlags flags; GHashTable *connection_uuids; + gulong dev_removed_id; NMCheckpointTimeoutCallback timeout_cb; gpointer timeout_data; @@ -204,28 +209,140 @@ find_settings_connection (NMCheckpoint *self, return sett_conn; } +static gboolean +restore_and_activate_connection (NMCheckpoint *self, + DeviceCheckpoint *dev_checkpoint) +{ + NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self); + NMSettingsConnection *connection; + gs_unref_object NMAuthSubject *subject = NULL; + GError *local_error = NULL; + gboolean need_update, need_activation; + + connection = find_settings_connection (self, + dev_checkpoint, + &need_update, + &need_activation); + if (connection) { + if (need_update) { + _LOGD ("rollback: updating connection %s", + nm_settings_connection_get_uuid (connection)); + nm_settings_connection_update (connection, + dev_checkpoint->settings_connection, + NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK, + NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, + "checkpoint-rollback", + NULL); + } + } else { + /* The connection was deleted, recreate it */ + _LOGD ("rollback: adding connection %s again", + nm_connection_get_uuid (dev_checkpoint->settings_connection)); + + connection = nm_settings_add_connection (nm_settings_get (), + dev_checkpoint->settings_connection, + TRUE, + &local_error); + if (!connection) { + _LOGD ("rollback: connection add failure: %s", local_error->message); + g_clear_error (&local_error); + return FALSE; + } + + /* If the device is software, a brand new NMDevice may have been created */ + if ( dev_checkpoint->is_software + && !dev_checkpoint->device) { + dev_checkpoint->device = nm_manager_get_device (priv->manager, + dev_checkpoint->original_dev_name, + dev_checkpoint->dev_type); + nm_g_object_ref (dev_checkpoint->device); + } + need_activation = TRUE; + } + + if (!dev_checkpoint->device) { + _LOGD ("rollback: device cannot be restored"); + return FALSE; + } + + if (need_activation) { + _LOGD ("rollback: reactivating connection %s", + nm_settings_connection_get_uuid (connection)); + subject = nm_auth_subject_new_internal (); + + /* Disconnect the device if needed. This necessary because now + * the manager prevents the reactivation of the same connection by + * an internal subject. */ + if ( nm_device_get_state (dev_checkpoint->device) > NM_DEVICE_STATE_DISCONNECTED + && nm_device_get_state (dev_checkpoint->device) < NM_DEVICE_STATE_DEACTIVATING) { + nm_device_state_changed (dev_checkpoint->device, + NM_DEVICE_STATE_DEACTIVATING, + NM_DEVICE_STATE_REASON_NEW_ACTIVATION); + } + + if (!nm_manager_activate_connection (priv->manager, + connection, + dev_checkpoint->applied_connection, + NULL, + dev_checkpoint->device, + subject, + NM_ACTIVATION_TYPE_MANAGED, + dev_checkpoint->activation_reason, + dev_checkpoint->activation_lifetime_bound_to_profile_visiblity + ? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY + : NM_ACTIVATION_STATE_FLAG_NONE, + &local_error)) { + _LOGW ("rollback: reactivation of connection %s/%s failed: %s", + nm_settings_connection_get_id (connection), + nm_settings_connection_get_uuid (connection), + local_error->message); + g_clear_error (&local_error); + return FALSE; + } + } + return TRUE; +} + GVariant * nm_checkpoint_rollback (NMCheckpoint *self) { NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self); DeviceCheckpoint *dev_checkpoint; GHashTableIter iter; - NMSettingsConnection *connection; NMDevice *device; - GError *local_error = NULL; GVariantBuilder builder; + uint i; _LOGI ("rollback of %s", nm_dbus_object_get_path (NM_DBUS_OBJECT (self))); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{su}")); + /* Start creating removed devices (if any and if possible) */ + if (priv->removed_devices) { + for (i = 0; i < priv->removed_devices->len; i++) { + guint32 result = NM_ROLLBACK_RESULT_OK; + + dev_checkpoint = priv->removed_devices->pdata[i]; + _LOGD ("rollback: restoring removed device %s (state %d, realized %d, explicitly unmanaged %d)", + dev_checkpoint->original_dev_name, + (int) dev_checkpoint->state, + dev_checkpoint->realized, + dev_checkpoint->unmanaged_explicit); + + if (dev_checkpoint->applied_connection) { + if (!restore_and_activate_connection (self, dev_checkpoint)) + result = NM_ROLLBACK_RESULT_ERR_FAILED; + } + g_variant_builder_add (&builder, "{su}", dev_checkpoint->original_dev_path, result); + } + } + /* Start rolling-back each device */ g_hash_table_iter_init (&iter, priv->devices); while (g_hash_table_iter_next (&iter, (gpointer *) &device, (gpointer *) &dev_checkpoint)) { - gs_unref_object NMAuthSubject *subject = NULL; guint32 result = NM_ROLLBACK_RESULT_OK; _LOGD ("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d)", - nm_device_get_iface (device), + dev_checkpoint->original_dev_name, (int) dev_checkpoint->state, dev_checkpoint->realized, dev_checkpoint->unmanaged_explicit); @@ -241,7 +358,7 @@ nm_checkpoint_rollback (NMCheckpoint *self) } } else { if (dev_checkpoint->realized) { - if (nm_device_is_software (device)) { + if (dev_checkpoint->is_software) { /* try to recreate software device */ _LOGD ("rollback: software device not realized, will re-activate"); goto activate; @@ -253,7 +370,6 @@ nm_checkpoint_rollback (NMCheckpoint *self) goto next_dev; } -activate: /* Manage the device again if needed */ if ( nm_device_get_unmanaged_flags (device, NM_UNMANAGED_USER_EXPLICIT) && dev_checkpoint->unmanaged_explicit != NM_UNMAN_FLAG_OP_SET_UNMANAGED) { @@ -276,76 +392,11 @@ activate: goto next_dev; } +activate: if (dev_checkpoint->applied_connection) { - gboolean need_update, need_activation; - - /* The device had an active connection: check if the - * connection still exists, is active and was changed */ - connection = find_settings_connection (self, dev_checkpoint, &need_update, &need_activation); - if (connection) { - if (need_update) { - _LOGD ("rollback: updating connection %s", - nm_settings_connection_get_uuid (connection)); - nm_settings_connection_update (connection, - dev_checkpoint->settings_connection, - NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - "checkpoint-rollback", - NULL); - } - } else { - /* The connection was deleted, recreate it */ - _LOGD ("rollback: adding connection %s again", - nm_connection_get_uuid (dev_checkpoint->settings_connection)); - - connection = nm_settings_add_connection (nm_settings_get (), - dev_checkpoint->settings_connection, - TRUE, - &local_error); - if (!connection) { - _LOGD ("rollback: connection add failure: %s", local_error->message); - g_clear_error (&local_error); - result = NM_ROLLBACK_RESULT_ERR_FAILED; - goto next_dev; - } - need_activation = TRUE; - } - - if (need_activation) { - _LOGD ("rollback: reactivating connection %s", - nm_settings_connection_get_uuid (connection)); - subject = nm_auth_subject_new_internal (); - - /* Disconnect the device if needed. This necessary because now - * the manager prevents the reactivation of the same connection by - * an internal subject. */ - if ( nm_device_get_state (device) > NM_DEVICE_STATE_DISCONNECTED - && nm_device_get_state (device) < NM_DEVICE_STATE_DEACTIVATING) { - nm_device_state_changed (device, - NM_DEVICE_STATE_DEACTIVATING, - NM_DEVICE_STATE_REASON_NEW_ACTIVATION); - } - - if (!nm_manager_activate_connection (priv->manager, - connection, - dev_checkpoint->applied_connection, - NULL, - device, - subject, - NM_ACTIVATION_TYPE_MANAGED, - dev_checkpoint->activation_reason, - dev_checkpoint->activation_lifetime_bound_to_profile_visiblity - ? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY - : NM_ACTIVATION_STATE_FLAG_NONE, - &local_error)) { - _LOGW ("rollback: reactivation of connection %s/%s failed: %s", - nm_settings_connection_get_id (connection), - nm_settings_connection_get_uuid (connection), - local_error->message); - g_clear_error (&local_error); - result = NM_ROLLBACK_RESULT_ERR_FAILED; - goto next_dev; - } + if (!restore_and_activate_connection (self, dev_checkpoint)) { + result = NM_ROLLBACK_RESULT_ERR_FAILED; + goto next_dev; } } else { /* The device was initially disconnected, deactivate any existing connection */ @@ -366,7 +417,6 @@ next_dev: if (NM_FLAGS_HAS (priv->flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) { NMSettingsConnection *con; gs_free NMSettingsConnection **list = NULL; - guint i; g_return_val_if_fail (priv->connection_uuids, NULL); list = nm_settings_get_connections_clone (nm_settings_get (), NULL, @@ -406,8 +456,56 @@ next_dev: return g_variant_new ("(a{su})", &builder); } +static void +device_checkpoint_destroy (gpointer data) +{ + DeviceCheckpoint *dev_checkpoint = data; + + nm_clear_g_signal_handler (dev_checkpoint->device, &dev_checkpoint->dev_exported_change_id); + g_clear_object (&dev_checkpoint->applied_connection); + g_clear_object (&dev_checkpoint->settings_connection); + g_clear_object (&dev_checkpoint->device); + g_free (dev_checkpoint->original_dev_path); + g_free (dev_checkpoint->original_dev_name); + + g_slice_free (DeviceCheckpoint, dev_checkpoint); +} + +static void +_move_dev_to_removed_devices (NMDevice *device, + NMCheckpoint *checkpoint) +{ + NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (checkpoint); + DeviceCheckpoint *dev_checkpoint; + + g_return_if_fail (device); + + dev_checkpoint = g_hash_table_lookup (priv->devices, device); + if (!dev_checkpoint) + return; + + g_hash_table_steal (priv->devices, dev_checkpoint->device); + nm_clear_g_signal_handler (dev_checkpoint->device, + &dev_checkpoint->dev_exported_change_id); + g_clear_object (&dev_checkpoint->device); + + if (!priv->removed_devices) + priv->removed_devices = g_ptr_array_new_with_free_func ((GDestroyNotify) device_checkpoint_destroy); + g_ptr_array_add (priv->removed_devices, dev_checkpoint); + + _notify (checkpoint, PROP_DEVICES); +} + +static void +_dev_exported_changed (NMDBusObject *obj, + NMCheckpoint *checkpoint) +{ + + _move_dev_to_removed_devices (NM_DEVICE (obj), checkpoint); +} + static DeviceCheckpoint * -device_checkpoint_create (NMDevice *device) +device_checkpoint_create (NMCheckpoint *checkpoint, NMDevice *device) { DeviceCheckpoint *dev_checkpoint; NMConnection *applied_connection; @@ -423,8 +521,15 @@ device_checkpoint_create (NMDevice *device) dev_checkpoint = g_slice_new0 (DeviceCheckpoint); dev_checkpoint->device = g_object_ref (device); dev_checkpoint->original_dev_path = g_strdup (path); + dev_checkpoint->original_dev_name = g_strdup (nm_device_get_iface (device)); + dev_checkpoint->dev_type = nm_device_get_device_type (device); dev_checkpoint->state = nm_device_get_state (device); + dev_checkpoint->is_software = nm_device_is_software (device); dev_checkpoint->realized = nm_device_is_real (device); + dev_checkpoint->dev_exported_change_id = g_signal_connect (device, + NM_DBUS_OBJECT_EXPORTED_CHANGED, + G_CALLBACK (_dev_exported_changed), + checkpoint); if (nm_device_get_unmanaged_mask (device, NM_UNMANAGED_USER_EXPLICIT)) { dev_checkpoint->unmanaged_explicit = !!nm_device_get_unmanaged_flags (device, @@ -448,19 +553,6 @@ device_checkpoint_create (NMDevice *device) return dev_checkpoint; } -static void -device_checkpoint_destroy (gpointer data) -{ - DeviceCheckpoint *dev_checkpoint = data; - - g_clear_object (&dev_checkpoint->applied_connection); - g_clear_object (&dev_checkpoint->settings_connection); - g_clear_object (&dev_checkpoint->device); - g_free (dev_checkpoint->original_dev_path); - - g_slice_free (DeviceCheckpoint, dev_checkpoint); -} - static gboolean _timeout_cb (gpointer user_data) { @@ -558,6 +650,12 @@ nm_checkpoint_init (NMCheckpoint *self) NULL, device_checkpoint_destroy); } +static void +_device_removed (NMManager *manager, NMDevice *device, gpointer user_data) +{ + _move_dev_to_removed_devices (device, NM_CHECKPOINT (user_data)); +} + NMCheckpoint * nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout_s, NMCheckpointCreateFlags flags) @@ -575,7 +673,7 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time self = g_object_new (NM_TYPE_CHECKPOINT, NULL); priv = NM_CHECKPOINT_GET_PRIVATE (self); - priv->manager = manager; + priv->manager = g_object_ref (manager); priv->rollback_timeout_s = rollback_timeout_s; priv->created_at_ms = nm_utils_get_monotonic_timestamp_ms (); priv->flags = flags; @@ -598,15 +696,18 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time for (i = 0; i < devices->len; i++) { NMDevice *device = devices->pdata[i]; - /* FIXME: as long as the check point instance exists, it won't let go - * of the device. That is a bug, for example, if you have a ethernet - * device that gets removed (rmmod), the checkpoint will reference - * a non-existing D-Bus path of a device. */ + /* As long as the check point instance exists, it will keep a reference + * to the device also if the device gets removed (by rmmod or by deleting + * a connection profile for a software device). */ g_hash_table_insert (priv->devices, device, - device_checkpoint_create (device)); + device_checkpoint_create (self, device)); } + priv->dev_removed_id = g_signal_connect (priv->manager, + NM_MANAGER_DEVICE_REMOVED, + G_CALLBACK (_device_removed), + self); return self; } @@ -620,6 +721,10 @@ dispose (GObject *object) g_clear_pointer (&priv->devices, g_hash_table_unref); g_clear_pointer (&priv->connection_uuids, g_hash_table_unref); + nm_clear_pointer (&priv->removed_devices, g_ptr_array_unref); + + nm_clear_g_signal_handler (priv->manager, &priv->dev_removed_id); + g_clear_object (&priv->manager); nm_clear_g_source (&priv->timeout_id); diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c index b72413d2..2816e76a 100644 --- a/src/nm-connectivity.c +++ b/src/nm-connectivity.c @@ -167,6 +167,7 @@ NM_DEFINE_SINGLETON_GETTER (NMConnectivity, nm_connectivity_get, NM_TYPE_CONNECT /*****************************************************************************/ +#if WITH_CONCHECK static ConConfig * _con_config_ref (ConConfig *con_config) { @@ -176,6 +177,7 @@ _con_config_ref (ConConfig *con_config) } return con_config; } +#endif static void _con_config_unref (ConConfig *con_config) @@ -195,11 +197,13 @@ _con_config_unref (ConConfig *con_config) g_slice_free (ConConfig, con_config); } +#if WITH_CONCHECK static const char * _con_config_get_response (const ConConfig *con_config) { return con_config->response ?: NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE; } +#endif /*****************************************************************************/ @@ -673,6 +677,7 @@ _idle_cb (gpointer user_data) return G_SOURCE_REMOVE; } +#if WITH_CONCHECK static void do_curl_request (NMConnectivityCheckHandle *cb_data) { @@ -791,6 +796,7 @@ resolve_cb (GObject *object, GAsyncResult *res, gpointer user_data) do_curl_request (cb_data); } +#endif #define SD_RESOLVED_DNS ((guint64) (1LL << 0)) @@ -819,13 +825,13 @@ nm_connectivity_check_start (NMConnectivity *self, cb_data->user_data = user_data; cb_data->completed_state = NM_CONNECTIVITY_UNKNOWN; cb_data->addr_family = addr_family; - cb_data->concheck.con_config = _con_config_ref (priv->con_config); - if (iface) cb_data->ifspec = g_strdup_printf ("if!%s", iface); #if WITH_CONCHECK + cb_data->concheck.con_config = _con_config_ref (priv->con_config); + if ( iface && ifindex > 0 && priv->enabled @@ -840,7 +846,18 @@ nm_connectivity_check_start (NMConnectivity *self, * * Yes, this makes NMConnectivity singleton dependent on NMDnsManager singleton. * Well, not really: it makes connectivity-check-start dependent on NMDnsManager - * which merely means, not to start a connectivity check, late during shutdown. */ + * which merely means, not to start a connectivity check, late during shutdown. + * + * NMDnsSystemdResolved tries to D-Bus activate systemd-resolved only once, + * to not spam syslog with failures messages from dbus-daemon. + * Note that unless NMDnsSystemdResolved tried and failed to start systemd-resolved, + * it guesses that systemd-resolved is activatable and returns %TRUE here. That + * means, while NMDnsSystemdResolved would not try to D-Bus activate systemd-resolved + * more than once, NMConnectivity might -- until NMDnsSystemdResolved tried itself + * and noticed that systemd-resolved is not available. + * This is relatively cumbersome to avoid, because we would have to go through + * NMDnsSystemdResolved trying to asynchronously start the service, to ensure there + * is only one attempt to start the service. */ has_systemd_resolved = nm_dns_manager_has_systemd_resolved (nm_dns_manager_get ()); if (has_systemd_resolved) { diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 6f55e62a..b0cc914e 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -37,10 +37,10 @@ #include <net/if_arp.h> #include <net/ethernet.h> -#include "nm-utils/nm-random-utils.h" -#include "nm-utils/nm-io-utils.h" -#include "nm-utils/unaligned.h" -#include "nm-utils/nm-secret-utils.h" +#include "nm-std-aux/unaligned.h" +#include "nm-glib-aux/nm-random-utils.h" +#include "nm-glib-aux/nm-io-utils.h" +#include "nm-glib-aux/nm-secret-utils.h" #include "nm-utils.h" #include "nm-core-internal.h" #include "nm-setting-connection.h" diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 1b0d39ed..1f9996ca 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -27,7 +27,7 @@ #include "nm-connection.h" -#include "nm-utils/nm-time-utils.h" +#include "nm-glib-aux/nm-time-utils.h" /*****************************************************************************/ diff --git a/src/nm-dbus-manager.c b/src/nm-dbus-manager.c index 00fa6617..1a455c42 100644 --- a/src/nm-dbus-manager.c +++ b/src/nm-dbus-manager.c @@ -28,9 +28,10 @@ #include <sys/types.h> #include "c-list/src/c-list.h" +#include "nm-glib-aux/nm-c-list.h" #include "nm-dbus-interface.h" #include "nm-core-internal.h" -#include "nm-dbus-compat.h" +#include "nm-std-aux/nm-dbus-compat.h" #include "nm-dbus-object.h" #include "NetworkManagerUtils.h" @@ -44,6 +45,17 @@ /*****************************************************************************/ typedef struct { + CList caller_info_lst; + gulong uid; + gulong pid; + gint64 uid_checked_at; + gint64 pid_checked_at; + bool uid_valid:1; + bool pid_valid:1; + char sender[0]; +} CallerInfo; + +typedef struct { GVariant *value; } PropertyCacheData; @@ -78,8 +90,10 @@ typedef struct { NMDBusManagerSetPropertyHandler set_property_handler; gpointer set_property_handler_data; - GDBusConnection *connection; - GDBusProxy *proxy; + GDBusConnection *main_dbus_connection; + + CList caller_info_lst_head; + guint objmgr_registration_id; bool started:1; bool shutting_down:1; @@ -441,21 +455,33 @@ private_server_get_connection_by_owner (PrivateServer *s, const char *owner) /*****************************************************************************/ +static void +_caller_info_free (CallerInfo *caller_info) +{ + c_list_unlink_stale (&caller_info->caller_info_lst); + g_free (caller_info); +} + static gboolean _bus_get_unix_pid (NMDBusManager *self, const char *sender, - gulong *out_pid, - GError **error) + gulong *out_pid) { + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); guint32 unix_pid = G_MAXUINT32; gs_unref_variant GVariant *ret = NULL; - ret = _nm_dbus_proxy_call_sync (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy, - "GetConnectionUnixProcessID", - g_variant_new ("(s)", sender), - G_VARIANT_TYPE ("(u)"), - G_DBUS_CALL_FLAGS_NONE, 2000, - NULL, error); + ret = g_dbus_connection_call_sync (priv->main_dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionUnixProcessID", + g_variant_new ("(s)", sender), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + 2000, + NULL, + NULL); if (!ret) return FALSE; @@ -468,18 +494,23 @@ _bus_get_unix_pid (NMDBusManager *self, static gboolean _bus_get_unix_user (NMDBusManager *self, const char *sender, - gulong *out_user, - GError **error) + gulong *out_user) { + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); guint32 unix_uid = G_MAXUINT32; gs_unref_variant GVariant *ret = NULL; - ret = _nm_dbus_proxy_call_sync (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy, - "GetConnectionUnixUser", - g_variant_new ("(s)", sender), - G_VARIANT_TYPE ("(u)"), - G_DBUS_CALL_FLAGS_NONE, 2000, - NULL, error); + ret = g_dbus_connection_call_sync (priv->main_dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionUnixUser", + g_variant_new ("(s)", sender), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + 2000, + NULL, + NULL); if (!ret) return FALSE; @@ -489,34 +520,102 @@ _bus_get_unix_user (NMDBusManager *self, return TRUE; } -/** - * _get_caller_info(): - * - * Given a GDBus method invocation, or a GDBusConnection + GDBusMessage, - * return the sender and the UID of the sender. - */ +static const CallerInfo * +_get_caller_info_ensure (NMDBusManager *self, + const char *sender, + gboolean ensure_uid, + gboolean ensure_pid) +{ + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + CallerInfo *caller_info; + CallerInfo *ci; + gint64 now_ns; + gsize num; + +#define CALLER_INFO_MAX_AGE (NM_UTILS_NS_PER_SECOND * 1) + + /* Linear search the cache for the sender. + * + * The number of cached caller-infos is limited. Hence, it's O(1) and + * the list is reasonably short. + * Also, the entire caching assumes that we repeatedly ask for the + * same sender. That means, we expect to find the right caller info + * at the front of the list. */ + num = 1; + caller_info = NULL; + c_list_for_each_entry (ci, &priv->caller_info_lst_head, caller_info_lst) { + if (nm_streq (sender, ci->sender)) { + caller_info = ci; + break; + } + num++; + } + + if (caller_info) + nm_c_list_move_front (&priv->caller_info_lst_head, &caller_info->caller_info_lst); + else { + gsize l = strlen (sender) + 1; + + caller_info = g_malloc (sizeof (CallerInfo) + l); + *caller_info = (CallerInfo) { + .uid_checked_at = - CALLER_INFO_MAX_AGE, + .pid_checked_at = - CALLER_INFO_MAX_AGE, + }; + memcpy (caller_info->sender, sender, l); + c_list_link_front (&priv->caller_info_lst_head, &caller_info->caller_info_lst); + + /* only cache the last few entries. */ + while (TRUE) { + nm_assert (num > 0 && num == c_list_length (&priv->caller_info_lst_head)); + if (num-- <= 5) + break; + _caller_info_free (c_list_last_entry (&priv->caller_info_lst_head, CallerInfo, caller_info_lst)); + } + } + + now_ns = nm_utils_get_monotonic_timestamp_ns (); + + if ( ensure_uid + && (now_ns - caller_info->uid_checked_at) > CALLER_INFO_MAX_AGE) { + caller_info->uid_checked_at = now_ns; + if (!(caller_info->uid_valid = _bus_get_unix_user (self, sender, &caller_info->uid))) + caller_info->uid = G_MAXULONG; + } + + if ( ensure_pid + && (now_ns - caller_info->pid_checked_at) > CALLER_INFO_MAX_AGE) { + caller_info->pid_checked_at = now_ns; + if (!(caller_info->pid_valid = _bus_get_unix_pid (self, sender, &caller_info->pid))) + caller_info->pid = G_MAXULONG; + } + + return caller_info; +} + static gboolean _get_caller_info (NMDBusManager *self, GDBusMethodInvocation *context, GDBusConnection *connection, GDBusMessage *message, - char **out_sender, + const char **out_sender, gulong *out_uid, gulong *out_pid) { NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + const CallerInfo *caller_info; const char *sender; if (context) { + nm_assert (G_IS_DBUS_METHOD_INVOCATION (context)); connection = g_dbus_method_invocation_get_connection (context); /* only bus connections will have a sender */ sender = g_dbus_method_invocation_get_sender (context); } else { - g_assert (message); + nm_assert (G_IS_DBUS_MESSAGE (message)); sender = g_dbus_message_get_sender (message); } - g_assert (connection); + nm_assert (G_IS_DBUS_CONNECTION (connection)); if (!sender) { PrivateServer *s; @@ -525,10 +624,8 @@ _get_caller_info (NMDBusManager *self, c_list_for_each_entry (s, &priv->private_servers_lst_head, private_servers_lst) { sender = private_server_get_connection_owner (s, connection); if (sender) { - if (out_uid) - *out_uid = 0; - if (out_sender) - *out_sender = g_strdup (sender); + NM_SET_OUT (out_uid, 0); + NM_SET_OUT (out_sender, sender); if (out_pid) { GCredentials *creds; @@ -547,35 +644,29 @@ _get_caller_info (NMDBusManager *self, return TRUE; } } + NM_SET_OUT (out_sender, NULL); + NM_SET_OUT (out_uid, G_MAXULONG); + NM_SET_OUT (out_pid, G_MAXULONG); return FALSE; } - /* Bus connections always have a sender */ - g_assert (sender); - if (out_uid) { - if (!_bus_get_unix_user (self, sender, out_uid, NULL)) { - *out_uid = G_MAXULONG; - return FALSE; - } - } - - if (out_pid) { - if (!_bus_get_unix_pid (self, sender, out_pid, NULL)) { - *out_pid = G_MAXULONG; - return FALSE; - } - } + caller_info = _get_caller_info_ensure (self, sender, !!out_uid, !!out_pid); - if (out_sender) - *out_sender = g_strdup (sender); + NM_SET_OUT (out_sender, caller_info->sender); + NM_SET_OUT (out_uid, caller_info->uid); + NM_SET_OUT (out_pid, caller_info->pid); + if (out_uid && !caller_info->uid_valid) + return FALSE; + if (out_pid && !caller_info->pid_valid) + return FALSE; return TRUE; } gboolean nm_dbus_manager_get_caller_info (NMDBusManager *self, GDBusMethodInvocation *context, - char **out_sender, + const char **out_sender, gulong *out_uid, gulong *out_pid) { @@ -586,7 +677,7 @@ gboolean nm_dbus_manager_get_caller_info_from_message (NMDBusManager *self, GDBusConnection *connection, GDBusMessage *message, - char **out_sender, + const char **out_sender, gulong *out_uid, gulong *out_pid) { @@ -647,8 +738,8 @@ nm_dbus_manager_get_unix_user (NMDBusManager *self, gulong *out_uid) { NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + const CallerInfo *caller_info; PrivateServer *s; - GError *error = NULL; g_return_val_if_fail (sender != NULL, FALSE); g_return_val_if_fail (out_uid != NULL, FALSE); @@ -665,13 +756,12 @@ nm_dbus_manager_get_unix_user (NMDBusManager *self, } /* Otherwise, a bus connection */ - if (!_bus_get_unix_user (self, sender, out_uid, &error)) { - _LOGW ("failed to get unix user for dbus sender '%s': %s", - sender, error->message); - g_error_free (error); + caller_info = _get_caller_info_ensure (self, sender, TRUE, FALSE); + *out_uid = caller_info->uid; + if (!caller_info->uid_valid) { + _LOGW ("failed to get unix user for dbus sender '%s'", sender); return FALSE; } - return TRUE; } @@ -757,16 +847,6 @@ nm_dbus_manager_new_proxy (NMDBusManager *self, /*****************************************************************************/ -GDBusConnection * -nm_dbus_manager_get_connection (NMDBusManager *self) -{ - g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL); - - return NM_DBUS_MANAGER_GET_PRIVATE (self)->connection; -} - -/*****************************************************************************/ - static const NMDBusInterfaceInfoExtended * _reg_data_get_interface_info (RegistrationData *reg_data) { @@ -943,7 +1023,7 @@ _obj_register (NMDBusManager *self, GVariantBuilder builder; nm_assert (c_list_is_empty (&obj->internal.registration_lst_head)); - nm_assert (priv->connection); + nm_assert (priv->main_dbus_connection); nm_assert (priv->started); n_klasses = 0; @@ -980,7 +1060,7 @@ _obj_register (NMDBusManager *self, reg_data = g_malloc0 (sizeof (RegistrationData) + (sizeof (PropertyCacheData) * prop_len)); - registration_id = g_dbus_connection_register_object (priv->connection, + registration_id = g_dbus_connection_register_object (priv->main_dbus_connection, obj->internal.path, NM_UNCONST_PTR (GDBusInterfaceInfo, &interface_info->parent), &dbus_vtable, @@ -1019,7 +1099,7 @@ _obj_register (NMDBusManager *self, * * In general, it's ok to export an object with frozen signals. But you better make sure * that all properties are in a self-consistent state when exporting the object. */ - g_dbus_connection_emit_signal (priv->connection, + g_dbus_connection_emit_signal (priv->main_dbus_connection, NULL, OBJECT_MANAGER_SERVER_BASE_PATH, interface_info_objmgr.name, @@ -1040,7 +1120,7 @@ _obj_unregister (NMDBusManager *self, nm_assert (NM_IS_DBUS_OBJECT (obj)); - if (!priv->connection) { + if (!priv->main_dbus_connection) { /* nothing to do for the moment. */ nm_assert (c_list_is_empty (&obj->internal.registration_lst_head)); return; @@ -1059,7 +1139,7 @@ _obj_unregister (NMDBusManager *self, "s", interface_info->parent.name); c_list_unlink_stale (®_data->registration_lst); - if (!g_dbus_connection_unregister_object (priv->connection, reg_data->registration_id)) + if (!g_dbus_connection_unregister_object (priv->main_dbus_connection, reg_data->registration_id)) nm_assert_not_reached (); if (interface_info->parent.properties) { @@ -1071,7 +1151,7 @@ _obj_unregister (NMDBusManager *self, g_free (reg_data); } - g_dbus_connection_emit_signal (priv->connection, + g_dbus_connection_emit_signal (priv->main_dbus_connection, NULL, OBJECT_MANAGER_SERVER_BASE_PATH, interface_info_objmgr.name, @@ -1122,7 +1202,7 @@ _nm_dbus_manager_obj_export (NMDBusObject *obj) nm_assert_not_reached (); c_list_link_tail (&priv->objects_lst_head, &obj->internal.objects_lst); - if (priv->connection && priv->started) + if (priv->main_dbus_connection && priv->started) _obj_register (self, obj); } @@ -1239,7 +1319,7 @@ _nm_dbus_manager_obj_notify (NMDBusObject *obj, } g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as")); - g_dbus_connection_emit_signal (priv->connection, + g_dbus_connection_emit_signal (priv->main_dbus_connection, NULL, obj->internal.path, "org.freedesktop.DBus.Properties", @@ -1255,7 +1335,7 @@ _nm_dbus_manager_obj_notify (NMDBusObject *obj, /* this is a special interface: it has a legacy PropertiesChanged signal, * however, contrary to other interfaces with ~regular~ legacy signals, * we only notify about properties that actually belong to this interface. */ - g_dbus_connection_emit_signal (priv->connection, + g_dbus_connection_emit_signal (priv->main_dbus_connection, NULL, obj->internal.path, nm_interface_info_device_statistics.parent.name, @@ -1292,7 +1372,7 @@ _nm_dbus_manager_obj_notify (NMDBusObject *obj, const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data); if (interface_info->legacy_property_changed) { - g_dbus_connection_emit_signal (priv->connection, + g_dbus_connection_emit_signal (priv->main_dbus_connection, NULL, obj->internal.path, interface_info->parent.name, @@ -1321,12 +1401,12 @@ _nm_dbus_manager_obj_emit_signal (NMDBusObject *obj, self = obj->internal.bus_manager; priv = NM_DBUS_MANAGER_GET_PRIVATE (self); - if (!priv->connection || !priv->started) { + if (!priv->main_dbus_connection || !priv->started) { nm_g_variant_unref_floating (args); return; } - g_dbus_connection_emit_signal (priv->connection, + g_dbus_connection_emit_signal (priv->main_dbus_connection, NULL, obj->internal.path, interface_info->parent.name, @@ -1473,7 +1553,7 @@ nm_dbus_manager_get_dbus_connection (NMDBusManager *self) { g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL); - return NM_DBUS_MANAGER_GET_PRIVATE (self)->connection; + return NM_DBUS_MANAGER_GET_PRIVATE (self)->main_dbus_connection; } void @@ -1487,7 +1567,7 @@ nm_dbus_manager_start (NMDBusManager *self, g_return_if_fail (NM_IS_DBUS_MANAGER (self)); priv = NM_DBUS_MANAGER_GET_PRIVATE (self); - if (!priv->connection) { + if (!priv->main_dbus_connection) { /* Do nothing. We're presumably in the configure-and-quit mode. */ return; } @@ -1507,7 +1587,6 @@ nm_dbus_manager_acquire_bus (NMDBusManager *self) gs_free_error GError *error = NULL; gs_unref_variant GVariant *ret = NULL; gs_unref_object GDBusConnection *connection = NULL; - gs_unref_object GDBusProxy *proxy = NULL; guint32 result; guint registration_id; @@ -1530,20 +1609,6 @@ nm_dbus_manager_acquire_bus (NMDBusManager *self) g_dbus_connection_set_exit_on_close (connection, FALSE); - proxy = g_dbus_proxy_new_sync (connection, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES - | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, - NULL, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - NULL, - &error); - if (!proxy) { - _LOGE ("fatal failure to initialize D-Bus: %s", error->message); - return FALSE; - } - registration_id = g_dbus_connection_register_object (connection, OBJECT_MANAGER_SERVER_BASE_PATH, NM_UNCONST_PTR (GDBusInterfaceInfo, &interface_info_objmgr), @@ -1556,15 +1621,22 @@ nm_dbus_manager_acquire_bus (NMDBusManager *self) return FALSE; } - ret = _nm_dbus_proxy_call_sync (proxy, - "RequestName", - g_variant_new ("(su)", - NM_DBUS_SERVICE, - DBUS_NAME_FLAG_DO_NOT_QUEUE), - G_VARIANT_TYPE ("(u)"), - G_DBUS_CALL_FLAGS_NONE, -1, - NULL, - &error); + ret = g_dbus_connection_call_sync (connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "RequestName", + g_variant_new ("(su)", + NM_DBUS_SERVICE, + DBUS_NAME_FLAG_DO_NOT_QUEUE), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (!ret) + return FALSE; + if (!ret) { _LOGE ("fatal failure to acquire D-Bus service \"%s"": %s", NM_DBUS_SERVICE, error->message); @@ -1581,8 +1653,7 @@ nm_dbus_manager_acquire_bus (NMDBusManager *self) } priv->objmgr_registration_id = registration_id; - priv->connection = g_steal_pointer (&connection); - priv->proxy = g_steal_pointer (&proxy); + priv->main_dbus_connection = g_steal_pointer (&connection); _LOGI ("acquired D-Bus service \"%s\"", NM_DBUS_SERVICE); @@ -1620,6 +1691,8 @@ nm_dbus_manager_init (NMDBusManager *self) c_list_init (&priv->private_servers_lst_head); c_list_init (&priv->objects_lst_head); priv->objects_by_path = g_hash_table_new ((GHashFunc) _objects_by_path_hash, (GEqualFunc) _objects_by_path_equal); + + c_list_init (&priv->caller_info_lst_head); } static void @@ -1628,6 +1701,7 @@ dispose (GObject *object) NMDBusManager *self = NM_DBUS_MANAGER (object); NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); PrivateServer *s, *s_safe; + CallerInfo *caller_info; /* All exported NMDBusObject instances keep the manager alive, so we don't * expect any remaining objects. */ @@ -1640,14 +1714,16 @@ dispose (GObject *object) private_server_free (s); if (priv->objmgr_registration_id) { - g_dbus_connection_unregister_object (priv->connection, + g_dbus_connection_unregister_object (priv->main_dbus_connection, nm_steal_int (&priv->objmgr_registration_id)); } - g_clear_object (&priv->proxy); - g_clear_object (&priv->connection); + g_clear_object (&priv->main_dbus_connection); G_OBJECT_CLASS (nm_dbus_manager_parent_class)->dispose (object); + + while ((caller_info = c_list_first_entry (&priv->caller_info_lst_head, CallerInfo, caller_info_lst))) + _caller_info_free (caller_info); } static void diff --git a/src/nm-dbus-manager.h b/src/nm-dbus-manager.h index 89acd7c8..2de6ff0b 100644 --- a/src/nm-dbus-manager.h +++ b/src/nm-dbus-manager.h @@ -61,8 +61,6 @@ void nm_dbus_manager_stop (NMDBusManager *self); gboolean nm_dbus_manager_is_stopping (NMDBusManager *self); -GDBusConnection *nm_dbus_manager_get_connection (NMDBusManager *self); - gpointer nm_dbus_manager_lookup_object (NMDBusManager *self, const char *path); void _nm_dbus_manager_obj_export (NMDBusObject *obj); @@ -77,7 +75,7 @@ void _nm_dbus_manager_obj_emit_signal (NMDBusObject *obj, gboolean nm_dbus_manager_get_caller_info (NMDBusManager *self, GDBusMethodInvocation *context, - char **out_sender, + const char **out_sender, gulong *out_uid, gulong *out_pid); @@ -97,7 +95,7 @@ gboolean nm_dbus_manager_get_unix_user (NMDBusManager *self, gboolean nm_dbus_manager_get_caller_info_from_message (NMDBusManager *self, GDBusConnection *connection, GDBusMessage *message, - char **out_sender, + const char **out_sender, gulong *out_uid, gulong *out_pid); diff --git a/src/nm-dbus-utils.c b/src/nm-dbus-utils.c index 8e7dd122..7b9c56b6 100644 --- a/src/nm-dbus-utils.c +++ b/src/nm-dbus-utils.c @@ -154,7 +154,7 @@ nm_dbus_utils_g_value_set_object_path_from_hash (GValue *value, while (g_hash_table_iter_next (&iter, (gpointer *) &obj, NULL)) { const char *path; - path = nm_dbus_object_get_path (obj); + path = nm_dbus_object_get_path_still_exported (obj); if (!path) { nm_assert (!expect_all_exported); continue; diff --git a/src/nm-dcb.c b/src/nm-dcb.c index a63fdf3d..fdcdd8dc 100644 --- a/src/nm-dcb.c +++ b/src/nm-dcb.c @@ -37,9 +37,11 @@ do_helper (const char *iface, const char *fmt, ...) { - char **argv = NULL, **split = NULL, *cmdline, *errmsg = NULL; - gboolean success = FALSE; - guint i, u; + gs_free const char **split = NULL; + gs_free char *cmdline = NULL; + gs_free const char **argv = NULL; + gsize i; + gsize u; va_list args; g_return_val_if_fail (fmt != NULL, FALSE); @@ -48,35 +50,31 @@ do_helper (const char *iface, cmdline = g_strdup_vprintf (fmt, args); va_end (args); - split = g_strsplit_set (cmdline, " ", 0); + split = nm_utils_strsplit_set_with_empty (cmdline, " "); if (!split) { g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, "failure parsing %s command line", helper_names[which]); - goto out; + return FALSE; } /* Allocate space for path, custom arg, interface name, arguments, and NULL */ - i = u = 0; - argv = g_new0 (char *, g_strv_length (split) + 4); + i = 0; + argv = g_new (const char *, NM_PTRARRAY_LEN (split) + 4); argv[i++] = NULL; /* Placeholder for dcbtool path */ if (which == DCBTOOL) { argv[i++] = "sc"; argv[i++] = (char *) iface; } - while (u < g_strv_length (split)) - argv[i++] = split[u++]; + for (u = 0; split[u]; u++) + argv[i++] = split[u]; argv[i++] = NULL; - success = run_func (argv, which, user_data, error); - if (!success && error) - g_assert (*error); - -out: - if (split) - g_strfreev (split); - g_free (argv); - g_free (cmdline); - g_free (errmsg); - return success; + + if (!run_func ((char **) argv, which, user_data, error)) { + g_assert (!error || *error); + return FALSE; + } + + return TRUE; } gboolean diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index c0ef1bb6..d656edc8 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -23,7 +23,7 @@ #include "nm-dispatcher.h" -#include "nm-dispatcher-api.h" +#include "nm-libnm-core-aux/nm-dispatcher-api.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-connectivity.h" diff --git a/src/nm-hostname-manager.c b/src/nm-hostname-manager.c index d405a320..3fcd8482 100644 --- a/src/nm-hostname-manager.c +++ b/src/nm-hostname-manager.c @@ -28,7 +28,7 @@ #include <selinux/selinux.h> #endif -#include "nm-common-macros.h" +#include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-dbus-interface.h" #include "nm-connection.h" #include "nm-utils.h" diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index 1ef9f5ae..812ab83e 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -31,7 +31,7 @@ #include <signal.h> #include <linux/rtnetlink.h> -#include "nm-utils/nm-c-list.h" +#include "nm-glib-aux/nm-c-list.h" #include "main-utils.h" #include "NetworkManagerUtils.h" @@ -186,8 +186,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in * addresses as /128. The reason for the /128 is to prevent the kernel * from adding a prefix route for this address. */ ifa_flags = 0; - if (nm_platform_check_kernel_support (NM_PLATFORM_GET, - NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS)) { + if (nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS)) { ifa_flags |= IFA_F_NOPREFIXROUTE; if (NM_IN_SET (global_opt.tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)) @@ -212,8 +211,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in rdata->routes_n, RT_TABLE_MAIN, global_opt.priority_v6, - nm_platform_check_kernel_support (NM_PLATFORM_GET, - NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF)); + nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF)); } if (changed & NM_NDISC_CONFIG_DHCP_LEVEL) { diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 1c06a42c..15088c60 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -27,7 +27,7 @@ #include <resolv.h> #include <linux/rtnetlink.h> -#include "nm-utils/nm-dedup-multi.h" +#include "nm-glib-aux/nm-dedup-multi.h" #include "nm-utils.h" #include "platform/nmp-object.h" @@ -115,13 +115,13 @@ _nm_ip_config_add_obj (NMDedupMultiIndex *multi_idx, if (!obj_new) { nm_assert (pl_new); obj_new = nmp_object_stackinit (&obj_new_stackinit, idx_type->obj_type, pl_new); - obj_new_stackinit.object.ifindex = ifindex; + NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (&obj_new_stackinit)->ifindex = ifindex; } else { nm_assert (!pl_new); nm_assert (NMP_OBJECT_GET_TYPE (obj_new) == idx_type->obj_type); - if (obj_new->object.ifindex != ifindex) { + if (NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj_new)->ifindex != ifindex) { obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); - obj_new_stackinit.object.ifindex = ifindex; + NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (&obj_new_stackinit)->ifindex = ifindex; } } nm_assert (NMP_OBJECT_GET_TYPE (obj_new) == idx_type->obj_type); @@ -859,15 +859,12 @@ _nm_ip_config_merge_route_attributes (int addr_family, GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TABLE, table, UINT32, uint32, 0); r->table_coerced = nm_platform_route_table_coerce (table ?: (route_table ?: RT_TABLE_MAIN)); - if (addr_family == AF_INET) { + if (addr_family == AF_INET) GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TOS, r4->tos, BYTE, byte, 0); - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_ONLINK, onlink, BOOLEAN, boolean, FALSE); - } else - onlink = FALSE; - r->r_rtm_flags = 0; - if (onlink) - r->r_rtm_flags = RTNH_F_ONLINK; + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_ONLINK, onlink, BOOLEAN, boolean, FALSE); + + r->r_rtm_flags = ((onlink) ? (unsigned) RTNH_F_ONLINK : 0u); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW, r->window, UINT32, uint32, 0); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_CWND, r->cwnd, UINT32, uint32, 0); diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index 6b4bfd64..6d89903f 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -25,7 +25,7 @@ #include "nm-setting-ip4-config.h" -#include "nm-utils/nm-dedup-multi.h" +#include "nm-glib-aux/nm-dedup-multi.h" #include "platform/nmp-object.h" /*****************************************************************************/ diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 99a9ff88..baef60bd 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -27,7 +27,7 @@ #include <resolv.h> #include <linux/rtnetlink.h> -#include "nm-utils/nm-dedup-multi.h" +#include "nm-glib-aux/nm-dedup-multi.h" #include "nm-utils.h" #include "platform/nmp-object.h" diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index 557e3796..86786ea8 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -25,7 +25,7 @@ #include "nm-setting-ip6-config.h" -#include "nm-utils/nm-dedup-multi.h" +#include "nm-glib-aux/nm-dedup-multi.h" #include "platform/nmp-object.h" /*****************************************************************************/ diff --git a/src/nm-logging.c b/src/nm-logging.c index 1ee94645..48e51421 100644 --- a/src/nm-logging.c +++ b/src/nm-logging.c @@ -37,7 +37,7 @@ #include <systemd/sd-journal.h> #endif -#include "nm-utils/nm-time-utils.h" +#include "nm-glib-aux/nm-time-utils.h" #include "nm-errors.h" /*****************************************************************************/ @@ -302,7 +302,8 @@ nm_logging_setup (const char *level, NMLogDomain new_log_state[_LOGL_N_REAL]; NMLogLevel cur_log_level; NMLogLevel new_log_level; - char **tmp, **iter; + gs_free const char **domains_v = NULL; + gsize i_d; int i; gboolean had_platform_debug; gs_free char *domains_free = NULL; @@ -337,28 +338,24 @@ nm_logging_setup (const char *level, } } - tmp = g_strsplit_set (domains, ", ", 0); - for (iter = tmp; iter && *iter; iter++) { + domains_v = nm_utils_strsplit_set (domains, ", "); + for (i_d = 0; domains_v && domains_v[i_d]; i_d++) { + const char *s = domains_v[i_d]; + const char *p; const LogDesc *diter; NMLogLevel domain_log_level; NMLogDomain bits; - char *p; /* LOGD_VPN_PLUGIN is protected, that is, when setting ALL or DEFAULT, * it does not enable the verbose levels DEBUG and TRACE, because that * may expose sensitive data. */ NMLogDomain protect = LOGD_NONE; - if (!strlen (*iter)) - continue; - - p = strchr (*iter, ':'); + p = strchr (s, ':'); if (p) { - *p = '\0'; - if (!match_log_level (p + 1, &domain_log_level, error)) { - g_strfreev (tmp); + *((char *) p) = '\0'; + if (!match_log_level (p + 1, &domain_log_level, error)) return FALSE; - } } else domain_log_level = new_log_level; @@ -372,26 +369,26 @@ nm_logging_setup (const char *level, } /* Check for combined domains */ - if (!g_ascii_strcasecmp (*iter, LOGD_ALL_STRING)) { + if (!g_ascii_strcasecmp (s, LOGD_ALL_STRING)) { bits = LOGD_ALL; protect = LOGD_VPN_PLUGIN; - } else if (!g_ascii_strcasecmp (*iter, LOGD_DEFAULT_STRING)) { + } else if (!g_ascii_strcasecmp (s, LOGD_DEFAULT_STRING)) { bits = LOGD_DEFAULT; protect = LOGD_VPN_PLUGIN; - } else if (!g_ascii_strcasecmp (*iter, LOGD_DHCP_STRING)) + } else if (!g_ascii_strcasecmp (s, LOGD_DHCP_STRING)) bits = LOGD_DHCP; - else if (!g_ascii_strcasecmp (*iter, LOGD_IP_STRING)) + else if (!g_ascii_strcasecmp (s, LOGD_IP_STRING)) bits = LOGD_IP; /* Check for compatibility domains */ - else if (!g_ascii_strcasecmp (*iter, "HW")) + else if (!g_ascii_strcasecmp (s, "HW")) bits = LOGD_PLATFORM; - else if (!g_ascii_strcasecmp (*iter, "WIMAX")) + else if (!g_ascii_strcasecmp (s, "WIMAX")) continue; else { for (diter = &domain_desc[0]; diter->name; diter++) { - if (!g_ascii_strcasecmp (diter->name, *iter)) { + if (!g_ascii_strcasecmp (diter->name, s)) { bits = diter->num; break; } @@ -400,7 +397,7 @@ nm_logging_setup (const char *level, if (!bits) { if (!bad_domains) { g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_LOG_DOMAIN, - _("Unknown log domain '%s'"), *iter); + _("Unknown log domain '%s'"), s); return FALSE; } @@ -408,7 +405,7 @@ nm_logging_setup (const char *level, g_string_append (unrecognized, ", "); else unrecognized = g_string_new (NULL); - g_string_append (unrecognized, *iter); + g_string_append (unrecognized, s); continue; } } @@ -429,7 +426,6 @@ nm_logging_setup (const char *level, } } } - g_strfreev (tmp); g_clear_pointer (&gl_main.logging_domains_to_string, g_free); diff --git a/src/nm-logging.h b/src/nm-logging.h index a824890a..a0f4ad73 100644 --- a/src/nm-logging.h +++ b/src/nm-logging.h @@ -26,7 +26,7 @@ #error nm-test-utils.h must be included as last header #endif -#include "nm-utils/nm-logging-fwd.h" +#include "nm-glib-aux/nm-logging-fwd.h" #define NM_LOG_CONFIG_BACKEND_DEBUG "debug" #define NM_LOG_CONFIG_BACKEND_SYSLOG "syslog" diff --git a/src/nm-manager.c b/src/nm-manager.c index 0bf6a751..40e068ac 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -27,9 +27,9 @@ #include <fcntl.h> #include <unistd.h> -#include "nm-utils/nm-c-list.h" +#include "nm-glib-aux/nm-c-list.h" -#include "nm-common-macros.h" +#include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-dbus-manager.h" #include "vpn/nm-vpn-manager.h" #include "devices/nm-device.h" @@ -54,7 +54,7 @@ #include "nm-core-internal.h" #include "nm-config.h" #include "nm-audit-manager.h" -#include "nm-dbus-compat.h" +#include "nm-std-aux/nm-dbus-compat.h" #include "nm-checkpoint.h" #include "nm-checkpoint-manager.h" #include "nm-dbus-object.h" @@ -303,6 +303,8 @@ static const GDBusSignalInfo signal_info_state_changed; static const GDBusSignalInfo signal_info_device_added; static const GDBusSignalInfo signal_info_device_removed; +static void update_connectivity_value (NMManager *self); + static gboolean add_device (NMManager *self, NMDevice *device, GError **error); static void _emit_device_added_removed (NMManager *self, @@ -1700,6 +1702,8 @@ remove_device (NMManager *self, g_signal_emit (self, signals[INTERNAL_DEVICE_REMOVED], 0, device); _notify (self, PROP_ALL_DEVICES); + update_connectivity_value (self); + nm_dbus_object_clear_and_unexport (&device); check_if_startup_complete (self); @@ -1991,6 +1995,13 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) g_object_unref (device); } + if (!nm_device_check_unrealized_device_managed (device)) { + _LOG3D (LOGD_DEVICE, connection, + "skip activation because virtual device '%s' is unmanaged", + nm_device_get_iface (device)); + return device; + } + /* Create backing resources if the device has any autoconnect connections */ connections = nm_settings_get_connections_clone (priv->settings, NULL, NULL, NULL, @@ -2924,6 +2935,12 @@ device_connectivity_changed (NMDevice *device, GParamSpec *pspec, NMManager *self) { + update_connectivity_value (self); +} + +static void +update_connectivity_value (NMManager *self) +{ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMConnectivityState best_state; @@ -8283,8 +8300,8 @@ nm_manager_class_init (NMManagerClass *manager_class) signals[INTERNAL_DEVICE_ADDED] = g_signal_new (NM_MANAGER_INTERNAL_DEVICE_ADDED, G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, 0, - NULL, NULL, NULL, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); /* emitted only for realized devices when a device @@ -8300,8 +8317,8 @@ nm_manager_class_init (NMManagerClass *manager_class) signals[INTERNAL_DEVICE_REMOVED] = g_signal_new (NM_MANAGER_INTERNAL_DEVICE_REMOVED, G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, 0, - NULL, NULL, NULL, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[ACTIVE_CONNECTION_ADDED] = diff --git a/src/nm-netns.c b/src/nm-netns.c index 5952a490..650578af 100644 --- a/src/nm-netns.c +++ b/src/nm-netns.c @@ -22,12 +22,13 @@ #include "nm-netns.h" -#include "nm-utils/nm-dedup-multi.h" +#include "nm-glib-aux/nm-dedup-multi.h" +#include "NetworkManagerUtils.h" +#include "nm-core-internal.h" #include "platform/nm-platform.h" #include "platform/nmp-netns.h" -#include "nm-core-internal.h" -#include "NetworkManagerUtils.h" +#include "platform/nmp-rules-manager.h" /*****************************************************************************/ @@ -38,6 +39,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( typedef struct { NMPlatform *platform; NMPNetns *platform_netns; + NMPRulesManager *rules_manager; } NMNetnsPrivate; struct _NMNetns { @@ -71,6 +73,12 @@ nm_netns_get_platform (NMNetns *self) return NM_NETNS_GET_PRIVATE (self)->platform; } +NMPRulesManager * +nm_netns_get_rules_manager (NMNetns *self) +{ + return NM_NETNS_GET_PRIVATE (self)->rules_manager; +} + NMDedupMultiIndex * nm_netns_get_multi_idx (NMNetns *self) { @@ -118,6 +126,20 @@ constructed (GObject *object) priv->platform_netns = nm_platform_netns_get (priv->platform); + priv->rules_manager = nmp_rules_manager_new (priv->platform); + + /* Weakly track the default rules and rules that were added + * outside of NetworkManager. */ + nmp_rules_manager_track_default (priv->rules_manager, + AF_UNSPEC, + 0, + nm_netns_parent_class /* static dummy user-tag */); + nmp_rules_manager_track_from_platform (priv->rules_manager, + NULL, + AF_UNSPEC, + 0, + nm_netns_parent_class /* static dummy user-tag */); + G_OBJECT_CLASS (nm_netns_parent_class)->constructed (object); } @@ -137,6 +159,8 @@ dispose (GObject *object) g_clear_object (&priv->platform); + nm_clear_pointer (&priv->rules_manager, nmp_rules_manager_unref); + G_OBJECT_CLASS (nm_netns_parent_class)->dispose (object); } diff --git a/src/nm-netns.h b/src/nm-netns.h index ae343cce..250d1149 100644 --- a/src/nm-netns.h +++ b/src/nm-netns.h @@ -40,6 +40,8 @@ NMNetns *nm_netns_new (NMPlatform *platform); NMPlatform *nm_netns_get_platform (NMNetns *self); NMPNetns *nm_netns_get_platform_netns (NMNetns *self); +struct _NMPRulesManager *nm_netns_get_rules_manager (NMNetns *self); + struct _NMDedupMultiIndex *nm_netns_get_multi_idx (NMNetns *self); #define NM_NETNS_GET (nm_netns_get ()) diff --git a/src/nm-policy.c b/src/nm-policy.c index 540f4065..1faba5c7 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -2588,7 +2588,7 @@ static void nm_policy_init (NMPolicy *self) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - const char *hostname_mode; + gs_free char *hostname_mode = NULL; c_list_init (&priv->pending_activation_checks); diff --git a/src/nm-rfkill-manager.c b/src/nm-rfkill-manager.c index 534ad369..cf2e7fe9 100644 --- a/src/nm-rfkill-manager.c +++ b/src/nm-rfkill-manager.c @@ -24,7 +24,7 @@ #include <libudev.h> -#include "nm-utils/nm-udev-utils.h" +#include "nm-udev-aux/nm-udev-utils.h" /*****************************************************************************/ diff --git a/src/nm-test-utils-core.h b/src/nm-test-utils-core.h index dbfe71ad..7f8b3327 100644 --- a/src/nm-test-utils-core.h +++ b/src/nm-test-utils-core.h @@ -310,7 +310,7 @@ nmtst_platform_ip6_routes_equal_aptr (const NMPObject *const*a, const NMPlatform #ifdef __NETWORKMANAGER_IP4_CONFIG_H__ -#include "nm-utils/nm-dedup-multi.h" +#include "nm-glib-aux/nm-dedup-multi.h" static inline NMIP4Config * nmtst_ip4_config_new (int ifindex) @@ -324,7 +324,7 @@ nmtst_ip4_config_new (int ifindex) #ifdef __NETWORKMANAGER_IP6_CONFIG_H__ -#include "nm-utils/nm-dedup-multi.h" +#include "nm-glib-aux/nm-dedup-multi.h" static inline NMIP6Config * nmtst_ip6_config_new (int ifindex) diff --git a/src/nm-types.h b/src/nm-types.h index b6b49028..03ee9929 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -119,15 +119,16 @@ NM_IS_IP_CONFIG_SOURCE_RTPROT (NMIPConfigSource source) } /* platform */ -typedef struct _NMPlatform NMPlatform; -typedef struct _NMPlatformObject NMPlatformObject; -typedef struct _NMPlatformIP4Address NMPlatformIP4Address; -typedef struct _NMPlatformIP4Route NMPlatformIP4Route; -typedef struct _NMPlatformIP6Address NMPlatformIP6Address; -typedef struct _NMPlatformIP6Route NMPlatformIP6Route; -typedef struct _NMPlatformLink NMPlatformLink; -typedef struct _NMPNetns NMPNetns; -typedef struct _NMPObject NMPObject; +typedef struct _NMPlatform NMPlatform; +typedef struct _NMPlatformObject NMPlatformObject; +typedef struct _NMPlatformObjWithIfindex NMPlatformObjWithIfindex; +typedef struct _NMPlatformIP4Address NMPlatformIP4Address; +typedef struct _NMPlatformIP4Route NMPlatformIP4Route; +typedef struct _NMPlatformIP6Address NMPlatformIP6Address; +typedef struct _NMPlatformIP6Route NMPlatformIP6Route; +typedef struct _NMPlatformLink NMPlatformLink; +typedef struct _NMPNetns NMPNetns; +typedef struct _NMPObject NMPObject; typedef enum { /* Please don't interpret type numbers outside nm-platform and use functions @@ -192,6 +193,7 @@ typedef enum { NMP_OBJECT_TYPE_IP6_ADDRESS, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE, + NMP_OBJECT_TYPE_ROUTING_RULE, NMP_OBJECT_TYPE_QDISC, diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 30466159..40a85390 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -154,10 +154,17 @@ static char * sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *path) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); + const char *v; ASSERT_SYSCTL_ARGS (pathid, dirfd, path); - return g_strdup (g_hash_table_lookup (priv->options, path)); + v = g_hash_table_lookup (priv->options, path); + if (!v) { + errno = ENOENT; + return NULL; + } + + return g_strdup (v); } static NMFakePlatformLink * @@ -1119,7 +1126,7 @@ ipx_route_delete (NMPlatform *platform, g_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); g_assert (ifindex == -1); - ifindex = obj->object.ifindex; + ifindex = NMP_OBJECT_CAST_IP_ROUTE (obj)->ifindex; obj_type = NMP_OBJECT_GET_TYPE (obj); } else { g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); @@ -1394,6 +1401,10 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); NMPlatformClass *platform_class = NM_PLATFORM_CLASS (klass); + NMPlatformKernelSupportType kernel_support; + + for (kernel_support = 0; kernel_support < _NM_PLATFORM_KERNEL_SUPPORT_NUM; kernel_support++) + _nm_platform_kernel_support_init (kernel_support, -1); object_class->finalize = finalize; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 2f5c75b0..d4b01152 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -26,8 +26,10 @@ #include <endian.h> #include <fcntl.h> #include <libudev.h> +#include <linux/fib_rules.h> #include <linux/ip.h> #include <linux/if_arp.h> +#include <linux/if_bridge.h> #include <linux/if_link.h> #include <linux/if_tun.h> #include <linux/if_tunnel.h> @@ -39,12 +41,14 @@ #include <sys/socket.h> #include <unistd.h> +#include "nm-std-aux/unaligned.h" + #include "nm-utils.h" #include "nm-core-internal.h" #include "nm-setting-vlan.h" -#include "nm-utils/nm-secret-utils.h" -#include "nm-utils/nm-c-list.h" +#include "nm-glib-aux/nm-secret-utils.h" +#include "nm-glib-aux/nm-c-list.h" #include "nm-netlink.h" #include "nm-core-utils.h" #include "nmp-object.h" @@ -54,9 +58,8 @@ #include "wifi/nm-wifi-utils.h" #include "wifi/nm-wifi-utils-wext.h" #include "wpan/nm-wpan-utils.h" -#include "nm-utils/unaligned.h" -#include "nm-utils/nm-io-utils.h" -#include "nm-utils/nm-udev-utils.h" +#include "nm-glib-aux/nm-io-utils.h" +#include "nm-udev-aux/nm-udev-utils.h" /*****************************************************************************/ @@ -140,8 +143,6 @@ enum { #define __IFLA_TUN_MAX 10 #define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1) -static const gboolean RTA_PREF_SUPPORTED_AT_COMPILETIME = (RTA_MAX >= 20 /* RTA_PREF */); - G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); #define RTA_PREF 20 #undef RTA_MAX @@ -164,6 +165,19 @@ G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); /*****************************************************************************/ +#define FRA_TUN_ID 12 +#define FRA_SUPPRESS_IFGROUP 13 +#define FRA_SUPPRESS_PREFIXLEN 14 +#define FRA_PAD 18 +#define FRA_L3MDEV 19 +#define FRA_UID_RANGE 20 +#define FRA_PROTOCOL 21 +#define FRA_IP_PROTO 22 +#define FRA_SPORT_RANGE 23 +#define FRA_DPORT_RANGE 24 + +/*****************************************************************************/ + #define IFLA_MACSEC_UNSPEC 0 #define IFLA_MACSEC_SCI 1 #define IFLA_MACSEC_PORT 2 @@ -263,6 +277,14 @@ struct _ifla_vf_vlan_info { /*****************************************************************************/ +/* Appeared in in kernel 4.0 dated April 12, 2015 */ +#ifndef BRIDGE_VLAN_INFO_RANGE_BEGIN +#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1 << 3) /* VLAN is start of vlan range */ +#define BRIDGE_VLAN_INFO_RANGE_END (1 << 4) /* VLAN is end of vlan range */ +#endif + +/*****************************************************************************/ + typedef enum { INFINIBAND_ACTION_CREATE_CHILD, INFINIBAND_ACTION_DELETE_CHILD, @@ -283,41 +305,64 @@ typedef struct { }; } ChangeLinkData; -enum { - DELAYED_ACTION_IDX_REFRESH_ALL_LINKS, - DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES, - DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ADDRESSES, - DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES, - DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES, - DELAYED_ACTION_IDX_REFRESH_ALL_QDISCS, - DELAYED_ACTION_IDX_REFRESH_ALL_TFILTERS, - _DELAYED_ACTION_IDX_REFRESH_ALL_NUM, -}; +typedef enum { + _REFRESH_ALL_TYPE_FIRST = 0, + + REFRESH_ALL_TYPE_LINKS = 0, + REFRESH_ALL_TYPE_IP4_ADDRESSES = 1, + REFRESH_ALL_TYPE_IP6_ADDRESSES = 2, + REFRESH_ALL_TYPE_IP4_ROUTES = 3, + REFRESH_ALL_TYPE_IP6_ROUTES = 4, + REFRESH_ALL_TYPE_ROUTING_RULES_IP4 = 5, + REFRESH_ALL_TYPE_ROUTING_RULES_IP6 = 6, + REFRESH_ALL_TYPE_QDISCS = 7, + REFRESH_ALL_TYPE_TFILTERS = 8, + + _REFRESH_ALL_TYPE_NUM, +} RefreshAllType; + +typedef struct { + NMPObjectType obj_type; + + /* for NLM_F_DUMP, which address family to request. */ + int addr_family; +} RefreshAllInfo; typedef enum { - DELAYED_ACTION_TYPE_NONE = 0, - DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS = (1LL << /* 0 */ DELAYED_ACTION_IDX_REFRESH_ALL_LINKS), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES = (1LL << /* 1 */ DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES = (1LL << /* 2 */ DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ADDRESSES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = (1LL << /* 3 */ DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = (1LL << /* 4 */ DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES), - DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = (1LL << /* 5 */ DELAYED_ACTION_IDX_REFRESH_ALL_QDISCS), - DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = (1LL << /* 6 */ DELAYED_ACTION_IDX_REFRESH_ALL_TFILTERS), - DELAYED_ACTION_TYPE_REFRESH_LINK = (1LL << 7), - DELAYED_ACTION_TYPE_MASTER_CONNECTED = (1LL << 11), - DELAYED_ACTION_TYPE_READ_NETLINK = (1LL << 12), - DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = (1LL << 13), + DELAYED_ACTION_TYPE_NONE = 0, + +#define F(val, name) ((sizeof (char[(((val)) == (name)) ? 1 : -1]) * 0) + (val)) + DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS = 1 << F (0, REFRESH_ALL_TYPE_LINKS), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES = 1 << F (1, REFRESH_ALL_TYPE_IP4_ADDRESSES), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES = 1 << F (2, REFRESH_ALL_TYPE_IP6_ADDRESSES), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = 1 << F (3, REFRESH_ALL_TYPE_IP4_ROUTES), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = 1 << F (4, REFRESH_ALL_TYPE_IP6_ROUTES), + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 = 1 << F (5, REFRESH_ALL_TYPE_ROUTING_RULES_IP4), + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6 = 1 << F (6, REFRESH_ALL_TYPE_ROUTING_RULES_IP6), + DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = 1 << F (7, REFRESH_ALL_TYPE_QDISCS), + DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = 1 << F (8, REFRESH_ALL_TYPE_TFILTERS), +#undef F + + DELAYED_ACTION_TYPE_REFRESH_LINK = 1 << 9, + DELAYED_ACTION_TYPE_MASTER_CONNECTED = 1 << 10, + DELAYED_ACTION_TYPE_READ_NETLINK = 1 << 11, + DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = 1 << 12, + __DELAYED_ACTION_TYPE_MAX, - DELAYED_ACTION_TYPE_REFRESH_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | - DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | - DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, - DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX -1, + DELAYED_ACTION_TYPE_REFRESH_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | + DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | + DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, + + DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX -1, } DelayedActionType; #define FOR_EACH_DELAYED_ACTION(iflags, flags_all) \ @@ -387,7 +432,7 @@ typedef struct { GIOChannel *event_channel; guint event_id; - bool pruning[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM]; + guint32 pruning[_REFRESH_ALL_TYPE_NUM]; GHashTable *sysctl_get_prev_values; CList sysctl_list; @@ -401,7 +446,7 @@ typedef struct { /* counter that a refresh all action is in progress, separated * by type. */ - int refresh_all_in_progress[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM]; + int refresh_all_in_progress[_REFRESH_ALL_TYPE_NUM]; GPtrArray *list_master_connected; GPtrArray *list_refresh_link; @@ -536,119 +581,6 @@ wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, return buf0; } -/***************************************************************************** - * Support IFLA_INET6_ADDR_GEN_MODE - *****************************************************************************/ - -static int _support_user_ipv6ll = 0; -#define _support_user_ipv6ll_still_undecided() (G_UNLIKELY (_support_user_ipv6ll == 0)) - -static void -_support_user_ipv6ll_detect (struct nlattr **tb) -{ - gboolean supported; - - nm_assert (_support_user_ipv6ll_still_undecided ()); - - /* IFLA_INET6_ADDR_GEN_MODE was added in kernel 3.17, dated 5 October, 2014. */ - supported = !!tb[IFLA_INET6_ADDR_GEN_MODE]; - _support_user_ipv6ll = supported ? 1 : -1; - _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", - supported ? "detected" : "not detected"); -} - -static gboolean -_support_user_ipv6ll_get (void) -{ - if (_support_user_ipv6ll_still_undecided ()) { - _support_user_ipv6ll = 1; - _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", "failed to detect; assume support"); - } - return _support_user_ipv6ll >= 0; -} - -/***************************************************************************** - * extended IFA_FLAGS support - *****************************************************************************/ - -static int _support_kernel_extended_ifa_flags = 0; - -#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == 0)) - -static void -_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) -{ - struct nlmsghdr *msg_hdr; - gboolean support; - - nm_assert (_support_kernel_extended_ifa_flags_still_undecided ()); - nm_assert (msg); - - msg_hdr = nlmsg_hdr (msg); - - nm_assert (msg_hdr && msg_hdr->nlmsg_type == RTM_NEWADDR); - - /* IFA_FLAGS is set for IPv4 and IPv6 addresses. It was added first to IPv6, - * but if we encounter an IPv4 address with IFA_FLAGS, we surely have support. */ - if (!NM_IN_SET (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family, AF_INET, AF_INET6)) - return; - - /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, - * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR - * and IFA_F_NOPREFIXROUTE for IPv6. They were added together in kernel 3.14, - * dated 30 March, 2014. - * - * For IPv4, IFA_F_NOPREFIXROUTE was added later, but there is no easy - * way to detect kernel support. */ - support = !!nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), IFA_FLAGS); - _support_kernel_extended_ifa_flags = support ? 1 : -1; - _LOG2D ("kernel-support: extended-ifa-flags: %s", support ? "detected" : "not detected"); -} - -static gboolean -_support_kernel_extended_ifa_flags_get (void) -{ - if (_support_kernel_extended_ifa_flags_still_undecided ()) { - _LOG2D ("kernel-support: extended-ifa-flags: %s", "unable to detect kernel support for handling IPv6 temporary addresses. Assume support"); - _support_kernel_extended_ifa_flags = 1; - } - return _support_kernel_extended_ifa_flags >= 0; -} - -/***************************************************************************** - * Support RTA_PREF - *****************************************************************************/ - -static int _support_rta_pref = 0; -#define _support_rta_pref_still_undecided() (G_UNLIKELY (_support_rta_pref == 0)) - -static void -_support_rta_pref_detect (struct nlattr **tb) -{ - gboolean supported; - - nm_assert (_support_rta_pref_still_undecided ()); - - /* RTA_PREF was added in kernel 4.1, dated 21 June, 2015. */ - supported = !!tb[RTA_PREF]; - _support_rta_pref = supported ? 1 : -1; - _LOG2D ("kernel-support: RTA_PREF: ability to set router preference for IPv6 routes: %s", - supported ? "detected" : "not detected"); -} - -static gboolean -_support_rta_pref_get (void) -{ - if (_support_rta_pref_still_undecided ()) { - /* if we couldn't detect support, we fallback on compile-time check, whether - * RTA_PREF is present in the kernel headers. */ - _support_rta_pref = RTA_PREF_SUPPORTED_AT_COMPILETIME ? 1 : -1; - _LOG2D ("kernel-support: RTA_PREF: ability to set router preference for IPv6 routes: %s", - RTA_PREF_SUPPORTED_AT_COMPILETIME ? "assume support" : "assume no support"); - } - return _support_rta_pref >= 0; -} - /****************************************************************** * Various utilities ******************************************************************/ @@ -1200,8 +1132,11 @@ _parse_af_inet6 (NMPlatform *platform, /* Hack to detect support addrgenmode of the kernel. We only parse * netlink messages that we receive from kernel, hence this check * is valid. */ - if (_support_user_ipv6ll_still_undecided ()) - _support_user_ipv6ll_detect (tb); + if (!_nm_platform_kernel_support_detected (NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) { + /* IFLA_INET6_ADDR_GEN_MODE was added in kernel 3.17, dated 5 October, 2014. */ + _nm_platform_kernel_support_init (NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL, + tb[IFLA_INET6_ADDR_GEN_MODE] ? 1 : -1); + } if (tb[IFLA_INET6_ADDR_GEN_MODE]) { i6_addr_gen_mode_inv = _nm_platform_uint8_inv (nla_get_u8 (tb[IFLA_INET6_ADDR_GEN_MODE])); @@ -3325,9 +3260,13 @@ rta_multipath_done: obj->ip_route.lock_mtu = NM_FLAGS_HAS (lock, 1 << RTAX_MTU); if (!is_v4) { - /* Detect support for RTA_PREF by inspecting the netlink message. */ - if (_support_rta_pref_still_undecided ()) - _support_rta_pref_detect (tb); + + if (!_nm_platform_kernel_support_detected (NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF)) { + /* Detect support for RTA_PREF by inspecting the netlink message. + * RTA_PREF was added in kernel 4.1, dated 21 June, 2015. */ + _nm_platform_kernel_support_init (NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF, + tb[RTA_PREF] ? 1 : -1); + } if (tb[RTA_PREF]) obj->ip6_route.rt_pref = nla_get_u8 (tb[RTA_PREF]); @@ -3340,6 +3279,204 @@ rta_multipath_done: } static NMPObject * +_new_from_nl_routing_rule (struct nlmsghdr *nlh, gboolean id_only) +{ + static const struct nla_policy policy[] = { + [FRA_UNSPEC] = { }, + [FRA_DST] = { /* struct in_addr, struct in6_addr */ }, + [FRA_SRC] = { /* struct in_addr, struct in6_addr */ }, + [FRA_IIFNAME] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ, }, + [FRA_GOTO] = { .type = NLA_U32, }, + [FRA_UNUSED2] = { }, + [FRA_PRIORITY] = { .type = NLA_U32, }, + [FRA_UNUSED3] = { }, + [FRA_UNUSED4] = { }, + [FRA_UNUSED5] = { }, + [FRA_FWMARK] = { .type = NLA_U32, }, + [FRA_FLOW] = { .type = NLA_U32, }, + [FRA_TUN_ID] = { .type = NLA_U64, }, + [FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32, }, + [FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32, }, + [FRA_TABLE] = { .type = NLA_U32, }, + [FRA_FWMASK] = { .type = NLA_U32, }, + [FRA_OIFNAME] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ, }, + [FRA_PAD] = { .type = NLA_U32, }, + [FRA_L3MDEV] = { .type = NLA_U8, }, + [FRA_UID_RANGE] = { .minlen = sizeof(NMFibRuleUidRange), + .maxlen = sizeof(NMFibRuleUidRange), }, + [FRA_PROTOCOL] = { .type = NLA_U8, }, + [FRA_IP_PROTO] = { .type = NLA_U8, }, + [FRA_SPORT_RANGE] = { .minlen = sizeof(NMFibRulePortRange), + .maxlen = sizeof(NMFibRulePortRange), }, + [FRA_DPORT_RANGE] = { .minlen = sizeof(NMFibRulePortRange), + .maxlen = sizeof(NMFibRulePortRange), }, + }; + struct nlattr *tb[G_N_ELEMENTS (policy)]; + const struct fib_rule_hdr *frh; + NMPlatformRoutingRule *props; + nm_auto_nmpobj NMPObject *obj = NULL; + int addr_family; + guint8 addr_size; + + if (nlmsg_parse_arr (nlh, sizeof (*frh), tb, policy) < 0) + return NULL; + + frh = nlmsg_data (nlh); + + addr_family = frh->family; + + if (!NM_IN_SET (addr_family, AF_INET, AF_INET6)) { + /* we don't care about other address families. */ + return NULL; + } + + addr_size = nm_utils_addr_family_to_size (addr_family); + + obj = nmp_object_new (NMP_OBJECT_TYPE_ROUTING_RULE, NULL); + props = &obj->routing_rule; + + props->addr_family = addr_family; + props->action = frh->action; + props->flags = frh->flags; + props->tos = frh->tos; + + props->table = tb[FRA_TABLE] + ? nla_get_u32 (tb[FRA_TABLE]) + : frh->table; + + if (tb[FRA_SUPPRESS_PREFIXLEN]) + props->suppress_prefixlen_inverse = ~nla_get_u32 (tb[FRA_SUPPRESS_PREFIXLEN]); + + if (tb[FRA_SUPPRESS_IFGROUP]) + props->suppress_ifgroup_inverse = ~nla_get_u32 (tb[FRA_SUPPRESS_IFGROUP]); + + if (tb[FRA_IIFNAME]) + nla_strlcpy (props->iifname, tb[FRA_IIFNAME], sizeof (props->iifname)); + + if (tb[FRA_OIFNAME]) + nla_strlcpy (props->oifname, tb[FRA_OIFNAME], sizeof (props->oifname)); + + if (tb[FRA_PRIORITY]) + props->priority = nla_get_u32 (tb[FRA_PRIORITY]); + + if (tb[FRA_FWMARK]) + props->fwmark = nla_get_u32 (tb[FRA_FWMARK]); + + if (tb[FRA_FWMASK]) + props->fwmask = nla_get_u32 (tb[FRA_FWMASK]); + + if (tb[FRA_GOTO]) + props->goto_target = nla_get_u32 (tb[FRA_GOTO]); + + props->src_len = frh->src_len; + if (props->src_len > addr_size * 8) + return NULL; + if (!tb[FRA_SRC]) { + if (props->src_len > 0) + return NULL; + } else if (!nm_ip_addr_set_from_untrusted (addr_family, + &props->src, + nla_data (tb[FRA_SRC]), + nla_len (tb[FRA_SRC]), + NULL)) + return NULL; + + props->dst_len = frh->dst_len; + if (props->dst_len > addr_size * 8) + return NULL; + if (!tb[FRA_DST]) { + if (props->dst_len > 0) + return NULL; + } else if (!nm_ip_addr_set_from_untrusted (addr_family, + &props->dst, + nla_data (tb[FRA_DST]), + nla_len (tb[FRA_DST]), + NULL)) + return NULL; + + if (tb[FRA_FLOW]) + props->flow = nla_get_u32 (tb[FRA_FLOW]); + + if (tb[FRA_TUN_ID]) + props->tun_id = nla_get_be64 (tb[FRA_TUN_ID]); + + if (tb[FRA_L3MDEV]) { + + if (!_nm_platform_kernel_support_detected (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV)) { + /* support for FRA_L3MDEV was added in 96c63fa7393d0a346acfe5a91e0c7d4c7782641b, + * kernel 4.8, 3 October 2017. + * + * We can only detect support if the attribute is present. A missing attribute + * is not conclusive. */ + _nm_platform_kernel_support_init (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV, 1); + } + + /* actually, kernel only allows this attribute to be missing or + * "1". Still, encode it as full uint8. + * + * Note that FRA_L3MDEV and FRA_TABLE are mutally exclusive. */ + props->l3mdev = nla_get_u8 (tb[FRA_L3MDEV]); + } + + if (tb[FRA_PROTOCOL]) + props->protocol = nla_get_u8 (tb[FRA_PROTOCOL]); + else + nm_assert (props->protocol == RTPROT_UNSPEC); + + if (!_nm_platform_kernel_support_detected (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL)) { + /* FRA_PROTOCOL was added in kernel 4.17, dated 3 June, 2018. + * See commit 1b71af6053af1bd2f849e9fda4f71c1e3f145dcf. */ + _nm_platform_kernel_support_init (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL, + tb[FRA_PROTOCOL] ? 1 : -1); + } + + if (tb[FRA_IP_PROTO]) + props->ip_proto = nla_get_u8 (tb[FRA_IP_PROTO]); + + G_STATIC_ASSERT_EXPR (sizeof (NMFibRulePortRange) == 4); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRulePortRange, start) == 0); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRulePortRange, end) == 2); + + nla_memcpy_checked_size (&props->sport_range, tb[FRA_SPORT_RANGE], sizeof (props->sport_range)); + nla_memcpy_checked_size (&props->dport_range, tb[FRA_DPORT_RANGE], sizeof (props->dport_range)); + + if (!_nm_platform_kernel_support_detected (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO)) { + /* support for FRA_IP_PROTO, FRA_SPORT_RANGE, and FRA_DPORT_RANGE was added together + * by bfff4862653bb96001ab57c1edd6d03f48e5f035, kernel 4.17, 4 June 2018. + * + * Unfortunately, a missing attribute does not tell us anything about support. + * We can only tell for sure when we have support, but not when we don't have. */ + if ( tb[FRA_IP_PROTO] + || tb[FRA_SPORT_RANGE] + || tb[FRA_DPORT_RANGE]) + _nm_platform_kernel_support_init (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO, 1); + } + + G_STATIC_ASSERT_EXPR (sizeof (NMFibRuleUidRange) == 8); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRuleUidRange, start) == 0); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRuleUidRange, end) == 4); + + if (tb[FRA_UID_RANGE]) { + + if (!_nm_platform_kernel_support_detected (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE)) { + /* support for FRA_UID_RANGE was added in 622ec2c9d52405973c9f1ca5116eb1c393adfc7d, + * kernel 4.10, 19 February 2017. + * + * We can only detect support if the attribute is present. A missing attribute + * is not conclusive. */ + _nm_platform_kernel_support_init (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE, 1); + } + + nla_memcpy_checked_size (&props->uid_range, tb[FRA_UID_RANGE], sizeof (props->uid_range)); + props->uid_range_has = TRUE; + } + + return g_steal_pointer (&obj); +} + +static NMPObject * _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) { static const struct nla_policy policy[] = { @@ -3438,6 +3575,10 @@ nmp_object_new_from_nl (NMPlatform *platform, const NMPCache *cache, struct nl_m case RTM_DELROUTE: case RTM_GETROUTE: return _new_from_nl_route (msghdr, id_only); + case RTM_NEWRULE: + case RTM_DELRULE: + case RTM_GETRULE: + return _new_from_nl_routing_rule (msghdr, id_only); case RTM_NEWQDISC: case RTM_DELQDISC: case RTM_GETQDISC: @@ -3642,21 +3783,23 @@ nla_put_failure: } static struct nl_msg * -_nl_msg_new_link (int nlmsg_type, - int nlmsg_flags, - int ifindex, - const char *ifname, - unsigned flags_mask, - unsigned flags_set) +_nl_msg_new_link_full (int nlmsg_type, + int nlmsg_flags, + int ifindex, + const char *ifname, + guint8 family, + unsigned flags_mask, + unsigned flags_set) { nm_auto_nlmsg struct nl_msg *msg = NULL; const struct ifinfomsg ifi = { + .ifi_family = family, .ifi_change = flags_mask, .ifi_flags = flags_set, .ifi_index = ifindex, }; - nm_assert (NM_IN_SET (nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK)); + nm_assert (NM_IN_SET (nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK, RTM_SETLINK)); msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); @@ -3672,6 +3815,15 @@ nla_put_failure: g_return_val_if_reached (NULL); } +static struct nl_msg * +_nl_msg_new_link (int nlmsg_type, + int nlmsg_flags, + int ifindex, + const char *ifname) +{ + return _nl_msg_new_link_full (nlmsg_type, nlmsg_flags, ifindex, ifname, AF_UNSPEC, 0, 0); +} + /* Copied and modified from libnl3's build_addr_msg(). */ static struct nl_msg * _nl_msg_new_address (int nlmsg_type, @@ -3796,9 +3948,7 @@ _nl_msg_new_route (int nlmsg_type, ? nm_platform_route_scope_inv (obj->ip4_route.scope_inv) : RT_SCOPE_NOWHERE, .rtm_type = RTN_UNICAST, - .rtm_flags = obj->ip_route.r_rtm_flags & (is_v4 - ? (unsigned) (RTNH_F_ONLINK) - : (unsigned) 0), + .rtm_flags = obj->ip_route.r_rtm_flags & ((unsigned) (RTNH_F_ONLINK)), .rtm_dst_len = obj->ip_route.plen, .rtm_src_len = is_v4 ? 0 @@ -3893,6 +4043,119 @@ nla_put_failure: } static struct nl_msg * +_nl_msg_new_routing_rule (int nlmsg_type, + int nlmsg_flags, + const NMPlatformRoutingRule *routing_rule) +{ + nm_auto_nlmsg struct nl_msg *msg = NULL; + const guint8 addr_size = nm_utils_addr_family_to_size (routing_rule->addr_family); + guint32 table; + + msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); + + table = routing_rule->table; + + if ( NM_IN_SET (routing_rule->addr_family, AF_INET, AF_INET6) + && routing_rule->action == FR_ACT_TO_TBL + && routing_rule->l3mdev == 0 + && table == RT_TABLE_UNSPEC) { + /* for IPv6, this setting is invalid and rejected by kernel. That's fine. + * + * for IPv4, kernel will automatically assign an unused table. That's not + * fine, because we don't know what we will get. + * + * The caller must not allow that to happen. */ + nm_assert_not_reached (); + } + + { + const struct fib_rule_hdr frh = { + .family = routing_rule->addr_family, + .src_len = routing_rule->src_len, + .dst_len = routing_rule->dst_len, + .tos = routing_rule->tos, + .table = table < 0x100u ? (guint8) table : (guint8) RT_TABLE_UNSPEC, + .action = routing_rule->action, + + /* we only allow setting the "not" flag. */ + .flags = routing_rule->flags & ((guint32) FIB_RULE_INVERT), + }; + + if (nlmsg_append_struct (msg, &frh) < 0) + goto nla_put_failure; + } + + if (table > G_MAXINT8) + NLA_PUT_U32 (msg, FRA_TABLE, table); + + if (routing_rule->suppress_prefixlen_inverse != 0) + NLA_PUT_U32 (msg, FRA_SUPPRESS_PREFIXLEN, ~routing_rule->suppress_prefixlen_inverse); + + if (routing_rule->suppress_ifgroup_inverse != 0) + NLA_PUT_U32 (msg, FRA_SUPPRESS_IFGROUP, ~routing_rule->suppress_ifgroup_inverse); + + if (routing_rule->iifname[0] != '\0') + NLA_PUT_STRING (msg, FRA_IIFNAME, routing_rule->iifname); + + if (routing_rule->oifname[0] != '\0') + NLA_PUT_STRING (msg, FRA_OIFNAME, routing_rule->oifname); + + /* we always set the priority and don't support letting kernel pick one. */ + NLA_PUT_U32 (msg, FRA_PRIORITY, routing_rule->priority); + + if ( routing_rule->fwmark != 0 + || routing_rule->fwmask != 0) { + NLA_PUT_U32 (msg, FRA_FWMARK, routing_rule->fwmark); + NLA_PUT_U32 (msg, FRA_FWMASK, routing_rule->fwmask); + } + + if (routing_rule->src_len > 0) + NLA_PUT (msg, FRA_SRC, addr_size, &routing_rule->src); + + if (routing_rule->dst_len > 0) + NLA_PUT (msg, FRA_DST, addr_size, &routing_rule->dst); + + if (routing_rule->flow != 0) { + /* only relevant for IPv4. */ + NLA_PUT_U32 (msg, FRA_FLOW, routing_rule->flow); + } + + if (routing_rule->tun_id != 0) + NLA_PUT_U64 (msg, FRA_TUN_ID, htobe64 (routing_rule->tun_id)); + + if (routing_rule->l3mdev) + NLA_PUT_U8 (msg, FRA_L3MDEV, routing_rule->l3mdev); + + if (routing_rule->protocol != RTPROT_UNSPEC) + NLA_PUT_U8 (msg, FRA_PROTOCOL, routing_rule->protocol); + + if (routing_rule->ip_proto != 0) + NLA_PUT_U8 (msg, FRA_IP_PROTO, routing_rule->ip_proto); + + if ( routing_rule->sport_range.start + || routing_rule->sport_range.end) + NLA_PUT (msg, FRA_SPORT_RANGE, sizeof (routing_rule->sport_range), &routing_rule->sport_range); + + if ( routing_rule->dport_range.start + || routing_rule->dport_range.end) + NLA_PUT (msg, FRA_DPORT_RANGE, sizeof (routing_rule->dport_range), &routing_rule->dport_range); + + if (routing_rule->uid_range_has) + NLA_PUT (msg, FRA_UID_RANGE, sizeof (routing_rule->uid_range), &routing_rule->uid_range); + + switch (routing_rule->action) { + case FR_ACT_GOTO: + NLA_PUT_U32 (msg, FRA_GOTO, routing_rule->goto_target); + break; + } + + return g_steal_pointer (&msg); + +nla_put_failure: + g_return_val_if_reached (NULL); +} + +static struct nl_msg * _nl_msg_new_qdisc (int nlmsg_type, int nlmsg_flags, const NMPlatformQdisc *qdisc) @@ -4285,22 +4548,31 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat ASSERT_SYSCTL_ARGS (pathid, dirfd, path); if (dirfd < 0) { - if (!nm_platform_netns_push (platform, &netns)) + if (!nm_platform_netns_push (platform, &netns)) { + errno = EBUSY; return NULL; + } pathid = path; } if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, &contents, NULL, &error) < 0) { - /* We assume FAILED means EOPNOTSUP */ - if ( g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) - || g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NODEV) - || g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_FAILED)) - _LOGD ("error reading %s: %s", pathid, error->message); - else - _LOGE ("error reading %s: %s", pathid, error->message); + NMLogLevel log_level = LOGL_ERR; + int errsv = EBUSY; + + if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { + errsv = ENOENT; + log_level = LOGL_DEBUG; + } else if ( g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NODEV) + || g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_FAILED)) { + /* We assume FAILED means EOPNOTSUP and don't log a error message. */ + log_level = LOGL_DEBUG; + } + + _NMLOG (log_level, "error reading %s: %s", pathid, error->message); g_clear_error (&error); + errno = errsv; return NULL; } @@ -4308,96 +4580,145 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat _log_dbg_sysctl_get (platform, pathid, contents); + /* errno is left undefined (as we don't return NULL). */ return contents; } /*****************************************************************************/ -static NMPlatformKernelSupportFlags -check_kernel_support (NMPlatform *platform, - NMPlatformKernelSupportFlags request_flags) +static void +process_events (NMPlatform *platform) { - NMPlatformKernelSupportFlags response = 0; + delayed_action_handle_all (platform, TRUE); +} - nm_assert (NM_IS_LINUX_PLATFORM (platform)); +/*****************************************************************************/ - if (NM_FLAGS_HAS (request_flags, NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS)) { - if (_support_kernel_extended_ifa_flags_get ()) - response |= NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS; - } +static const RefreshAllInfo * +refresh_all_type_get_info (RefreshAllType refresh_all_type) +{ + static const RefreshAllInfo infos[] = { +#define R(_refresh_all_type, _obj_type, _addr_family) [_refresh_all_type] = { .obj_type = _obj_type, .addr_family = _addr_family, } + R (REFRESH_ALL_TYPE_LINKS, NMP_OBJECT_TYPE_LINK, AF_UNSPEC), + R (REFRESH_ALL_TYPE_IP4_ADDRESSES, NMP_OBJECT_TYPE_IP4_ADDRESS, AF_UNSPEC), + R (REFRESH_ALL_TYPE_IP6_ADDRESSES, NMP_OBJECT_TYPE_IP6_ADDRESS, AF_UNSPEC), + R (REFRESH_ALL_TYPE_IP4_ROUTES, NMP_OBJECT_TYPE_IP4_ROUTE, AF_UNSPEC), + R (REFRESH_ALL_TYPE_IP6_ROUTES, NMP_OBJECT_TYPE_IP6_ROUTE, AF_UNSPEC), + R (REFRESH_ALL_TYPE_ROUTING_RULES_IP4, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET), + R (REFRESH_ALL_TYPE_ROUTING_RULES_IP6, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET6), + R (REFRESH_ALL_TYPE_QDISCS, NMP_OBJECT_TYPE_QDISC, AF_UNSPEC), + R (REFRESH_ALL_TYPE_TFILTERS, NMP_OBJECT_TYPE_TFILTER, AF_UNSPEC), +#undef R + }; - if (NM_FLAGS_HAS (request_flags, NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) { - if (_support_user_ipv6ll_get ()) - response |= NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL; - } + nm_assert (_NM_INT_NOT_NEGATIVE (refresh_all_type)); + nm_assert (refresh_all_type < G_N_ELEMENTS (infos)); + nm_assert (nmp_class_from_type (infos[refresh_all_type].obj_type)); - if (NM_FLAGS_HAS (request_flags, NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF)) { - if (_support_rta_pref_get ()) - response |= NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF; - } + return &infos[refresh_all_type]; +} + +_NM_UTILS_LOOKUP_DEFINE (static, delayed_action_type_to_refresh_all_type, DelayedActionType, RefreshAllType, + NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (0), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, REFRESH_ALL_TYPE_LINKS), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, REFRESH_ALL_TYPE_IP4_ADDRESSES), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, REFRESH_ALL_TYPE_IP6_ADDRESSES), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, REFRESH_ALL_TYPE_IP4_ROUTES), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, REFRESH_ALL_TYPE_IP6_ROUTES), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4, REFRESH_ALL_TYPE_ROUTING_RULES_IP4), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, REFRESH_ALL_TYPE_ROUTING_RULES_IP6), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, REFRESH_ALL_TYPE_QDISCS), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, REFRESH_ALL_TYPE_TFILTERS), + NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (), +); + +static DelayedActionType +delayed_action_type_from_refresh_all_type (RefreshAllType refresh_all_type) +{ + DelayedActionType t; + + nm_assert (refresh_all_type_get_info (refresh_all_type)); - return response; + t = (((DelayedActionType) 1) << refresh_all_type); + + nm_assert (refresh_all_type == delayed_action_type_to_refresh_all_type (t)); + + return t; } -static void -process_events (NMPlatform *platform) +static RefreshAllType +refresh_all_type_from_needle_object (const NMPObject *obj_needle) { - delayed_action_handle_all (platform, TRUE); + switch (NMP_OBJECT_GET_TYPE (obj_needle)) { + case NMP_OBJECT_TYPE_LINK: return REFRESH_ALL_TYPE_LINKS; + case NMP_OBJECT_TYPE_IP4_ADDRESS: return REFRESH_ALL_TYPE_IP4_ADDRESSES; + case NMP_OBJECT_TYPE_IP6_ADDRESS: return REFRESH_ALL_TYPE_IP6_ADDRESSES; + case NMP_OBJECT_TYPE_IP4_ROUTE: return REFRESH_ALL_TYPE_IP4_ROUTES; + case NMP_OBJECT_TYPE_IP6_ROUTE: return REFRESH_ALL_TYPE_IP6_ROUTES; + case NMP_OBJECT_TYPE_QDISC: return REFRESH_ALL_TYPE_QDISCS; + case NMP_OBJECT_TYPE_TFILTER: return REFRESH_ALL_TYPE_TFILTERS; + case NMP_OBJECT_TYPE_ROUTING_RULE: + switch (NMP_OBJECT_CAST_ROUTING_RULE (obj_needle)->addr_family) { + case AF_INET: return REFRESH_ALL_TYPE_ROUTING_RULES_IP4; + case AF_INET6: return REFRESH_ALL_TYPE_ROUTING_RULES_IP6; + } + nm_assert_not_reached (); + return 0; + default: + nm_assert_not_reached (); + return 0; + } } -/*****************************************************************************/ +static const NMPLookup * +refresh_all_type_init_lookup (RefreshAllType refresh_all_type, + NMPLookup *lookup) +{ + const RefreshAllInfo *refresh_all_info; -_NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_from_object_type, NMPObjectType, DelayedActionType, - NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (DELAYED_ACTION_TYPE_NONE), - NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_LINK, DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS), - NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP4_ADDRESS, DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES), - NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP6_ADDRESS, DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES), - NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP4_ROUTE, DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES), - NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP6_ROUTE, DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES), - NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_QDISC, DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS), - NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_TFILTER, DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS), - NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (), -); + nm_assert (lookup); -_NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_to_object_type, DelayedActionType, NMPObjectType, - NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (NMP_OBJECT_TYPE_UNKNOWN), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, NMP_OBJECT_TYPE_LINK), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, NMP_OBJECT_TYPE_IP4_ADDRESS), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, NMP_OBJECT_TYPE_IP6_ADDRESS), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, NMP_OBJECT_TYPE_IP4_ROUTE), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, NMP_OBJECT_TYPE_IP6_ROUTE), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, NMP_OBJECT_TYPE_QDISC), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NMP_OBJECT_TYPE_TFILTER), - NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (), -); + refresh_all_info = refresh_all_type_get_info (refresh_all_type); -_NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_all_to_idx, DelayedActionType, guint, - NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (0), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, DELAYED_ACTION_IDX_REFRESH_ALL_LINKS), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ADDRESSES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, DELAYED_ACTION_IDX_REFRESH_ALL_QDISCS), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, DELAYED_ACTION_IDX_REFRESH_ALL_TFILTERS), - NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (), -); + nm_assert (refresh_all_info); + + if (NM_IN_SET (refresh_all_info->obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)) { + return nmp_lookup_init_object_by_addr_family (lookup, + refresh_all_info->obj_type, + refresh_all_info->addr_family); + } + + /* not yet implemented. */ + nm_assert (refresh_all_info->addr_family == AF_UNSPEC); + + return nmp_lookup_init_obj_type (lookup, + refresh_all_info->obj_type); +} + +static DelayedActionType +delayed_action_refresh_from_needle_object (const NMPObject *obj_needle) +{ + return delayed_action_type_from_refresh_all_type (refresh_all_type_from_needle_object (obj_needle)); +} NM_UTILS_LOOKUP_STR_DEFINE_STATIC (delayed_action_to_string, DelayedActionType, NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, "refresh-all-links"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, "refresh-all-ip4-addresses"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, "refresh-all-ip6-addresses"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, "refresh-all-ip4-routes"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, "refresh-all-ip6-routes"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, "refresh-all-qdiscs"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, "refresh-all-tfilters"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_LINK, "refresh-link"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_MASTER_CONNECTED, "master-connected"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_READ_NETLINK, "read-netlink"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, "wait-for-nl-response"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, "refresh-all-links"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, "refresh-all-ip4-addresses"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, "refresh-all-ip6-addresses"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, "refresh-all-ip4-routes"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, "refresh-all-ip6-routes"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4, "refresh-all-routing-rules-ip4"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, "refresh-all-routing-rules-ip6"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, "refresh-all-qdiscs"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, "refresh-all-tfilters"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_LINK, "refresh-link"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_MASTER_CONNECTED, "master-connected"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_READ_NETLINK, "read-netlink"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, "wait-for-nl-response"), NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_NONE), NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_REFRESH_ALL), + NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL), NM_UTILS_LOOKUP_ITEM_IGNORE (__DELAYED_ACTION_TYPE_MAX), ); @@ -4455,6 +4776,7 @@ static gboolean delayed_action_refresh_all_in_progress (NMPlatform *platform, DelayedActionType action_type) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + RefreshAllType refresh_all_type; nm_assert (nm_utils_is_power_of_two (action_type)); nm_assert (NM_FLAGS_ANY (action_type, DELAYED_ACTION_TYPE_REFRESH_ALL)); @@ -4463,10 +4785,8 @@ delayed_action_refresh_all_in_progress (NMPlatform *platform, DelayedActionType if (NM_FLAGS_ANY (priv->delayed_action.flags, action_type)) return TRUE; - if (priv->delayed_action.refresh_all_in_progress[delayed_action_refresh_all_to_idx (action_type)] > 0) - return TRUE; - - return FALSE; + refresh_all_type = delayed_action_type_to_refresh_all_type (action_type); + return (priv->delayed_action.refresh_all_in_progress[refresh_all_type] > 0); } static void @@ -4764,25 +5084,33 @@ delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform, /*****************************************************************************/ static void -cache_prune_one_type (NMPlatform *platform, NMPObjectType obj_type) +cache_prune_one_type (NMPlatform *platform, + const NMPLookup *lookup) { NMDedupMultiIter iter; const NMPObject *obj; NMPCacheOpsType cache_op; - NMPLookup lookup; NMPCache *cache = nm_platform_get_cache (platform); - nmp_lookup_init_obj_type (&lookup, - obj_type); nm_dedup_multi_iter_init (&iter, nmp_cache_lookup (cache, - &lookup)); + lookup)); while (nm_dedup_multi_iter_next (&iter)) { - if (iter.current->dirty) { + const NMDedupMultiEntry *main_entry; + + /* we only track the dirty flag for the OBJECT-TYPE index. That means, + * for other lookup types we need to check the dirty flag of the main-entry. */ + main_entry = nmp_cache_reresolve_main_entry (cache, iter.current, lookup); + if (!main_entry->dirty) + continue; + + obj = main_entry->obj; + + _LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + + { nm_auto_nmpobj const NMPObject *obj_old = NULL; - obj = iter.current->obj; - _LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); cache_op = nmp_cache_remove (cache, obj, TRUE, TRUE, &obj_old); nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); cache_on_change (platform, cache_op, obj_old, NULL); @@ -4795,16 +5123,19 @@ static void cache_prune_all (NMPlatform *platform) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - DelayedActionType iflags, action_type; + RefreshAllType refresh_all_type; - action_type = DELAYED_ACTION_TYPE_REFRESH_ALL; - FOR_EACH_DELAYED_ACTION (iflags, action_type) { - bool *p = &priv->pruning[delayed_action_refresh_all_to_idx (iflags)]; + for (refresh_all_type = _REFRESH_ALL_TYPE_FIRST; refresh_all_type < _REFRESH_ALL_TYPE_NUM; refresh_all_type++) { + NMPLookup lookup; - if (*p) { - *p = FALSE; - cache_prune_one_type (platform, delayed_action_refresh_to_object_type (iflags)); - } + if (priv->pruning[refresh_all_type] == 0) + continue; + priv->pruning[refresh_all_type] -= 1; + if (priv->pruning[refresh_all_type] > 0) + continue; + refresh_all_type_init_lookup (refresh_all_type, + &lookup); + cache_prune_one_type (platform, &lookup); } } @@ -4876,6 +5207,7 @@ cache_on_change (NMPlatform *platform, DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NULL); @@ -5166,7 +5498,7 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha entry = nmp_cache_lookup_entry_link (nm_platform_get_cache (platform), ifindex); if (entry) { - priv->pruning[DELAYED_ACTION_IDX_REFRESH_ALL_LINKS] = TRUE; + priv->pruning[REFRESH_ALL_TYPE_LINKS] += 1; nm_dedup_multi_entry_set_dirty (entry, TRUE); } } @@ -5176,9 +5508,7 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha nlmsg = _nl_msg_new_link (RTM_GETLINK, 0, ifindex, - name, - 0, - 0); + name); if (nlmsg) { nle = _nl_send_nlmsg (platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { @@ -5234,6 +5564,7 @@ _nl_msg_new_dump (NMPObjectType obj_type, case NMP_OBJECT_TYPE_IP6_ADDRESS: case NMP_OBJECT_TYPE_IP4_ROUTE: case NMP_OBJECT_TYPE_IP6_ROUTE: + case NMP_OBJECT_TYPE_ROUTING_RULE: { const struct rtgenmsg gmsg = { .rtgen_family = preferred_addr_family, @@ -5254,42 +5585,68 @@ static void do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType action_type) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + DelayedActionType action_type_prune; DelayedActionType iflags; nm_assert (!NM_FLAGS_ANY (action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL)); action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL; - FOR_EACH_DELAYED_ACTION (iflags, action_type) { + action_type_prune = action_type; + + /* calling nmp_cache_dirty_set_all_main() with a non-main lookup-index requires an extra + * cache lookup for every entry. + * + * Avoid that, by special casing routing-rules here. */ + if (NM_FLAGS_ALL (action_type_prune, DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL)) { + NMPLookup lookup; + + priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP4] += 1; + priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP6] += 1; + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); + nmp_cache_dirty_set_all_main (nm_platform_get_cache (platform), + &lookup); + action_type_prune &= ~DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL; + } + + FOR_EACH_DELAYED_ACTION (iflags, action_type_prune) { + RefreshAllType refresh_all_type = delayed_action_type_to_refresh_all_type (iflags); NMPLookup lookup; - priv->pruning[delayed_action_refresh_all_to_idx (iflags)] = TRUE; - nmp_lookup_init_obj_type (&lookup, - delayed_action_refresh_to_object_type (iflags)); - nmp_cache_dirty_set_all (nm_platform_get_cache (platform), - &lookup); + priv->pruning[refresh_all_type] += 1; + refresh_all_type_init_lookup (refresh_all_type, + &lookup); + nmp_cache_dirty_set_all_main (nm_platform_get_cache (platform), + &lookup); } FOR_EACH_DELAYED_ACTION (iflags, action_type) { - NMPObjectType obj_type = delayed_action_refresh_to_object_type (iflags); + RefreshAllType refresh_all_type = delayed_action_type_to_refresh_all_type (iflags); + const RefreshAllInfo *refresh_all_info = refresh_all_type_get_info (refresh_all_type); nm_auto_nlmsg struct nl_msg *nlmsg = NULL; int *out_refresh_all_in_progress; - out_refresh_all_in_progress = &priv->delayed_action.refresh_all_in_progress[delayed_action_refresh_all_to_idx (iflags)]; + out_refresh_all_in_progress = &priv->delayed_action.refresh_all_in_progress[refresh_all_type]; nm_assert (*out_refresh_all_in_progress >= 0); *out_refresh_all_in_progress += 1; /* clear any delayed action that request a refresh of this object type. */ priv->delayed_action.flags &= ~iflags; _LOGt_delayed_action (iflags, NULL, "handle (do-request-all)"); - if (obj_type == NMP_OBJECT_TYPE_LINK) { - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK; - g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0); - _LOGt_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, NULL, "clear (do-request-all)"); + + if (refresh_all_type == REFRESH_ALL_TYPE_LINKS) { + nm_assert ( (priv->delayed_action.list_refresh_link->len > 0) + == NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)); + if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)) { + _LOGt_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, NULL, "clear (do-request-all)"); + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK; + g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0); + } } event_handler_read_netlink (platform, FALSE); - nlmsg = _nl_msg_new_dump (obj_type, AF_UNSPEC); + nlmsg = _nl_msg_new_dump (refresh_all_info->obj_type, + refresh_all_info->addr_family); if (!nlmsg) goto next_after_fail; @@ -5310,9 +5667,9 @@ next_after_fail: } static void -do_request_one_type (NMPlatform *platform, NMPObjectType obj_type) +do_request_one_type_by_needle_object (NMPlatform *platform, const NMPObject *obj_needle) { - do_request_all_no_delayed_actions (platform, delayed_action_refresh_from_object_type (obj_type)); + do_request_all_no_delayed_actions (platform, delayed_action_refresh_from_needle_object (obj_needle)); delayed_action_handle_all (platform, FALSE); } @@ -5398,9 +5755,27 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event msghdr = nlmsg_hdr (msg); - if ( _support_kernel_extended_ifa_flags_still_undecided () - && msghdr->nlmsg_type == RTM_NEWADDR) - _support_kernel_extended_ifa_flags_detect (msg); + if ( !_nm_platform_kernel_support_detected (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS) + && msghdr->nlmsg_type == RTM_NEWADDR) { + /* IFA_FLAGS is set for IPv4 and IPv6 addresses. It was added first to IPv6, + * but if we encounter an IPv4 address with IFA_FLAGS, we surely have support. */ + if ( nlmsg_valid_hdr (msghdr, sizeof (struct ifaddrmsg)) + && NM_IN_SET (((struct ifaddrmsg *) nlmsg_data (msghdr))->ifa_family, + AF_INET, + AF_INET6)) { + /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, + * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR + * and IFA_F_NOPREFIXROUTE for IPv6. They were added together in kernel 3.14, + * dated 30 March, 2014. + * + * For IPv4, IFA_F_NOPREFIXROUTE was added later, but there is no easy + * way to detect kernel support. */ + _nm_platform_kernel_support_init (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS, + !!nlmsg_find_attr (msghdr, sizeof (struct ifaddrmsg), IFA_FLAGS) + ? 1 + : -1); + } + } if (!handle_events) return; @@ -5408,6 +5783,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event if (NM_IN_SET (msghdr->nlmsg_type, RTM_DELLINK, RTM_DELADDR, RTM_DELROUTE, + RTM_DELRULE, RTM_DELQDISC, RTM_DELTFILTER)) { /* The event notifies about a deleted object. We don't need to initialize all @@ -5426,10 +5802,11 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event && NM_IN_SET (msghdr->nlmsg_type, RTM_NEWADDR, RTM_NEWLINK, RTM_NEWROUTE, + RTM_NEWRULE, RTM_NEWQDISC, RTM_NEWTFILTER)) { is_dump = delayed_action_refresh_all_in_progress (platform, - delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj))); + delayed_action_refresh_from_needle_object (obj)); } _LOGT ("event-notification: %s%s: %s", @@ -5445,10 +5822,11 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event switch (msghdr->nlmsg_type) { - case RTM_NEWLINK: - case RTM_NEWADDR: case RTM_GETLINK: + case RTM_NEWADDR: + case RTM_NEWLINK: case RTM_NEWQDISC: + case RTM_NEWRULE: case RTM_NEWTFILTER: cache_op = nmp_cache_update_netlink (cache, obj, is_dump, &obj_old, &obj_new); if (cache_op != NMP_CACHE_OPS_UNCHANGED) { @@ -5537,16 +5915,17 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event * netlink events. This needs investigation. */ _LOGT ("schedule resync of routes after RTM_NEWROUTE"); delayed_action_schedule (platform, - delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj)), + delayed_action_refresh_from_needle_object (obj), NULL); } break; } - case RTM_DELLINK: case RTM_DELADDR: - case RTM_DELROUTE: + case RTM_DELLINK: case RTM_DELQDISC: + case RTM_DELROUTE: + case RTM_DELRULE: case RTM_DELTFILTER: cache_op = nmp_cache_remove_netlink (cache, obj, &obj_old, &obj_new); if (cache_op != NMP_CACHE_OPS_UNCHANGED) { @@ -5657,7 +6036,7 @@ do_add_addrroute (NMPlatform *platform, * * rh#1484434 */ if (!nmp_cache_lookup_obj (nm_platform_get_cache (platform), obj_id)) - do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); + do_request_one_type_by_needle_object (platform, obj_id); } return wait_for_nl_response_to_nmerr (seq_result); @@ -5722,7 +6101,7 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * * * rh#1484434 */ if (nmp_cache_lookup_obj (nm_platform_get_cache (platform), obj_id)) - do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); + do_request_one_type_by_needle_object (platform, obj_id); } return success; @@ -5845,9 +6224,7 @@ link_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return -NME_UNSPEC; @@ -5876,9 +6253,7 @@ link_delete (NMPlatform *platform, int ifindex) nlmsg = _nl_msg_new_link (RTM_DELLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); nmp_object_stackinit_id_link (&obj_id, ifindex); return do_delete_object (platform, &obj_id, nlmsg); @@ -5891,12 +6266,6 @@ link_refresh (NMPlatform *platform, int ifindex) return !!nm_platform_link_get_obj (platform, ifindex, TRUE); } -static void -refresh_all (NMPlatform *platform, NMPObjectType obj_type) -{ - do_request_one_type (platform, obj_type); -} - static gboolean link_set_netns (NMPlatform *platform, int ifindex, @@ -5907,9 +6276,7 @@ link_set_netns (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if (!nlmsg) return FALSE; @@ -5936,12 +6303,13 @@ link_change_flags (NMPlatform *platform, nm_platform_link_flags2str (flags_set, s_flags, sizeof (s_flags)), nm_platform_link_flags2str (flags_mask, NULL, 0)); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, - 0, - ifindex, - NULL, - flags_mask, - flags_set); + nlmsg = _nl_msg_new_link_full (RTM_NEWLINK, + 0, + ifindex, + NULL, + AF_UNSPEC, + flags_mask, + flags_set); if (!nlmsg) return -NME_UNSPEC; return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); @@ -5997,7 +6365,7 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable ifindex, nm_platform_link_inet6_addrgenmode2str (mode, NULL, 0)); - if (!_support_user_ipv6ll_get ()) { + if (!nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) { _LOGD ("link: change %d: user-ipv6ll: not supported", ifindex); return -NME_PL_OPNOTSUPP; } @@ -6005,9 +6373,7 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if ( !nlmsg || !_nl_msg_new_link_set_afspec (nlmsg, mode, NULL)) g_return_val_if_reached (-NME_BUG); @@ -6024,7 +6390,7 @@ link_set_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId iid) _LOGD ("link: change %d: token: set IPv6 address generation token to %s", ifindex, nm_utils_inet6_interface_identifier_to_token (iid, sbuf)); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, NULL, 0, 0); + nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, NULL); if (!nlmsg || !_nl_msg_new_link_set_afspec (nlmsg, -1, &iid)) g_return_val_if_reached (FALSE); @@ -6105,9 +6471,7 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if (!nlmsg) g_return_val_if_reached (-NME_BUG); @@ -6126,9 +6490,7 @@ link_set_name (NMPlatform *platform, int ifindex, const char *name) nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if (!nlmsg) g_return_val_if_reached (-NME_BUG); @@ -6161,9 +6523,7 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if (!nlmsg) return FALSE; @@ -6224,6 +6584,14 @@ link_set_sriov_params (NMPlatform *platform, ifname, "device/sriov_drivers_autoprobe"), 10, 0, 1, -1); + + if ( current_autoprobe == -1 + && errno == ENOENT) { + /* older kernel versions don't have this sysctl. Assume the value is + * "1". */ + current_autoprobe = 1; + } + if ( current_num == num_vfs && (autoprobe == NM_TERNARY_DEFAULT || current_autoprobe == autoprobe)) return TRUE; @@ -6279,9 +6647,7 @@ link_set_sriov_vfs (NMPlatform *platform, int ifindex, const NMPlatformVF *const nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if (!nlmsg) g_return_val_if_reached (-NME_BUG); @@ -6364,6 +6730,74 @@ nla_put_failure: g_return_val_if_reached (FALSE); } +static gboolean +link_set_bridge_vlans (NMPlatform *platform, + int ifindex, + gboolean on_master, + const NMPlatformBridgeVlan *const *vlans) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct nlattr *list; + struct bridge_vlan_info vinfo = { }; + guint i; + + nlmsg = _nl_msg_new_link_full (vlans ? RTM_SETLINK : RTM_DELLINK, + 0, + ifindex, + NULL, + AF_BRIDGE, + 0, + 0); + if (!nlmsg) + g_return_val_if_reached (-NME_BUG); + + if (!(list = nla_nest_start (nlmsg, IFLA_AF_SPEC))) + goto nla_put_failure; + + NLA_PUT_U16 (nlmsg, + IFLA_BRIDGE_FLAGS, + on_master ? BRIDGE_FLAGS_MASTER : BRIDGE_FLAGS_SELF); + + if (vlans) { + /* Add VLANs */ + for (i = 0; vlans[i]; i++) { + const NMPlatformBridgeVlan *vlan = vlans[i]; + gboolean is_range = vlan->vid_start != vlan->vid_end; + + vinfo.vid = vlan->vid_start; + vinfo.flags = is_range ? BRIDGE_VLAN_INFO_RANGE_BEGIN : 0; + + if (vlan->untagged) + vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED; + if (vlan->pvid) + vinfo.flags |= BRIDGE_VLAN_INFO_PVID; + + NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo); + + if (is_range) { + vinfo.vid = vlan->vid_end; + vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END; + NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo); + } + } + } else { + /* Flush existing VLANs */ + vinfo.vid = 1; + vinfo.flags = BRIDGE_VLAN_INFO_RANGE_BEGIN; + NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo); + + vinfo.vid = 4094; + vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END; + NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo); + } + + nla_nest_end (nlmsg, list); + + return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); +nla_put_failure: + g_return_val_if_reached (FALSE); +} + static char * link_get_physical_port_id (NMPlatform *platform, int ifindex) { @@ -6409,9 +6843,7 @@ vlan_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -6445,9 +6877,7 @@ link_gre_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -6497,9 +6927,7 @@ link_ip6tnl_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -6553,9 +6981,7 @@ link_ip6gre_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -6612,9 +7038,7 @@ link_ipip_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -6656,9 +7080,7 @@ link_macsec_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -6713,9 +7135,7 @@ link_macvlan_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -6755,9 +7175,7 @@ link_sit_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -6857,9 +7275,7 @@ link_vxlan_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -6923,9 +7339,7 @@ link_6lowpan_add (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, - name, - 0, - 0); + name); if (!nlmsg) return FALSE; @@ -7073,9 +7487,7 @@ link_vlan_change (NMPlatform *platform, nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if ( !nlmsg || !_nl_msg_new_link_set_linkinfo_vlan (nlmsg, -1, @@ -7099,9 +7511,7 @@ link_enslave (NMPlatform *platform, int master, int slave) nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, - NULL, - 0, - 0); + NULL); if (!nlmsg) return FALSE; @@ -7606,6 +8016,9 @@ object_delete (NMPlatform *platform, case NMP_OBJECT_TYPE_IP6_ROUTE: nlmsg = _nl_msg_new_route (RTM_DELROUTE, 0, obj); break; + case NMP_OBJECT_TYPE_ROUTING_RULE: + nlmsg = _nl_msg_new_routing_rule (RTM_DELRULE, 0, NMP_OBJECT_CAST_ROUTING_RULE (obj)); + break; case NMP_OBJECT_TYPE_QDISC: nlmsg = _nl_msg_new_qdisc (RTM_DELQDISC, 0, NMP_OBJECT_CAST_QDISC (obj)); break; @@ -7703,6 +8116,47 @@ ip_route_get (NMPlatform *platform, /*****************************************************************************/ static int +routing_rule_add (NMPlatform *platform, + NMPNlmFlags flags, + const NMPlatformRoutingRule *routing_rule) +{ + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + nm_auto_nlmsg struct nl_msg *msg = NULL; + gs_free char *errmsg = NULL; + char s_buf[256]; + int nle; + + msg = _nl_msg_new_routing_rule (RTM_NEWRULE, flags, routing_rule); + + event_handler_read_netlink (platform, FALSE); + + nle = _nl_send_nlmsg (platform, msg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + if (nle < 0) { + _LOGE ("do-add-rule: failed sending netlink request \"%s\" (%d)", + nm_strerror (nle), -nle); + return -NME_PL_NETLINK; + } + + delayed_action_handle_all (platform, FALSE); + + nm_assert (seq_result); + + _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK + ? LOGL_DEBUG + : LOGL_WARN, + "do-add-rule: %s", + wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf))); + + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) + return 0; + if (seq_result < 0) + return seq_result; + return -NME_UNSPEC; +} + +/*****************************************************************************/ + +static int qdisc_add (NMPlatform *platform, NMPNlmFlags flags, const NMPlatformQdisc *qdisc) @@ -7736,7 +8190,8 @@ qdisc_add (NMPlatform *platform, if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) return 0; - + if (seq_result < 0) + return seq_result; return -NME_UNSPEC; } @@ -8053,6 +8508,7 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NULL); @@ -8318,9 +8774,13 @@ constructed (GObject *_object) g_assert (!nle); nle = nl_socket_add_memberships (priv->nlh, + RTNLGRP_IPV4_IFADDR, + RTNLGRP_IPV4_ROUTE, + RTNLGRP_IPV4_RULE, + RTNLGRP_IPV6_RULE, + RTNLGRP_IPV6_IFADDR, + RTNLGRP_IPV6_ROUTE, RTNLGRP_LINK, - RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, - RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, RTNLGRP_TC, 0); g_assert (!nle); @@ -8347,6 +8807,7 @@ constructed (GObject *_object) DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NULL); @@ -8457,7 +8918,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_add = link_add; platform_class->link_delete = link_delete; - platform_class->refresh_all = refresh_all; platform_class->link_refresh = link_refresh; platform_class->link_set_netns = link_set_netns; @@ -8478,6 +8938,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_name = link_set_name; platform_class->link_set_sriov_params = link_set_sriov_params; platform_class->link_set_sriov_vfs = link_set_sriov_vfs; + platform_class->link_set_bridge_vlans = link_set_bridge_vlans; platform_class->link_get_physical_port_id = link_get_physical_port_id; platform_class->link_get_dev_id = link_get_dev_id; @@ -8543,11 +9004,11 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->ip_route_add = ip_route_add; platform_class->ip_route_get = ip_route_get; + platform_class->routing_rule_add = routing_rule_add; + platform_class->qdisc_add = qdisc_add; platform_class->tfilter_add = tfilter_add; - platform_class->check_kernel_support = check_kernel_support; - platform_class->process_events = process_events; } diff --git a/src/platform/nm-netlink.h b/src/platform/nm-netlink.h index 094a3c6f..b12fd243 100644 --- a/src/platform/nm-netlink.h +++ b/src/platform/nm-netlink.h @@ -25,7 +25,7 @@ #include <linux/rtnetlink.h> #include <linux/genetlink.h> -#include "nm-utils/unaligned.h" +#include "nm-std-aux/unaligned.h" /*****************************************************************************/ diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c index 93cd0b3c..456871a7 100644 --- a/src/platform/nm-platform-utils.c +++ b/src/platform/nm-platform-utils.c @@ -35,7 +35,7 @@ #include "nm-utils.h" #include "nm-setting-wired.h" -#include "nm-ethtool-utils.h" +#include "nm-libnm-core-intern/nm-ethtool-utils.h" #include "nm-core-utils.h" diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h index bad77aee..f5491679 100644 --- a/src/platform/nm-platform-utils.h +++ b/src/platform/nm-platform-utils.h @@ -23,7 +23,7 @@ #include "nm-platform.h" #include "nm-setting-wired.h" -#include "nm-ethtool-utils.h" +#include "nm-libnm-core-intern/nm-ethtool-utils.h" /*****************************************************************************/ diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index fe0cb662..1fc0ccb7 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -28,6 +28,7 @@ #include <arpa/inet.h> #include <sys/socket.h> #include <netdb.h> +#include <linux/fib_rules.h> #include <linux/ip.h> #include <linux/if.h> #include <linux/if_tun.h> @@ -37,9 +38,9 @@ #include "nm-utils.h" #include "nm-core-internal.h" -#include "nm-utils/nm-dedup-multi.h" -#include "nm-utils/nm-udev-utils.h" -#include "nm-utils/nm-secret-utils.h" +#include "nm-glib-aux/nm-dedup-multi.h" +#include "nm-udev-aux/nm-udev-utils.h" +#include "nm-glib-aux/nm-secret-utils.h" #include "nm-core-utils.h" #include "nm-platform-utils.h" @@ -118,9 +119,6 @@ typedef struct _NMPlatformPrivate { bool use_udev:1; bool log_with_ptr:1; - NMPlatformKernelSupportFlags support_checked; - NMPlatformKernelSupportFlags support_present; - guint ip4_dev_route_blacklist_check_id; guint ip4_dev_route_blacklist_gc_timeout_id; GHashTable *ip4_dev_route_blacklist_hash; @@ -276,40 +274,99 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_nmp_nlm_flag_to_string_lookup, NMPNlmFlags, /*****************************************************************************/ -NMPlatformKernelSupportFlags -nm_platform_check_kernel_support (NMPlatform *self, - NMPlatformKernelSupportFlags request_flags) +volatile int _nm_platform_kernel_support_state[_NM_PLATFORM_KERNEL_SUPPORT_NUM] = { }; + +static const struct { + bool compile_time_default; + const char *name; + const char *desc; +} _nm_platform_kernel_support_info[_NM_PLATFORM_KERNEL_SUPPORT_NUM] = { + [NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS] = { + .compile_time_default = TRUE, + .name = "EXTENDED_IFA_FLAGS", + .desc = "IPv6 temporary addresses support", + }, + [NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL] = { + .compile_time_default = TRUE, + .name = "USER_IPV6LL", + .desc = "IFLA_INET6_ADDR_GEN_MODE support", + }, + [NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF] = { + .compile_time_default = (RTA_MAX >= 20 /* RTA_PREF */), + .name = "RTA_PREF", + .desc = "ability to set router preference for IPv6 routes", + }, + [NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV] = { + .compile_time_default = (FRA_MAX >= 19 /* FRA_L3MDEV */), + .name = "FRA_L3MDEV", + .desc = "FRA_L3MDEV attribute for policy routing rules", + }, + [NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE] = { + .compile_time_default = (FRA_MAX >= 20 /* FRA_UID_RANGE */), + .name = "FRA_UID_RANGE", + .desc = "FRA_UID_RANGE attribute for policy routing rules", + }, + [NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL] = { + .compile_time_default = (FRA_MAX >= 21 /* FRA_PROTOCOL */), + .name = "FRA_PROTOCOL", + .desc = "FRA_PROTOCOL attribute for policy routing rules", + }, + [NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO] = { + .compile_time_default = (FRA_MAX >= 22 /* FRA_IP_PROTO */), + .name = "FRA_IP_PROTO", + .desc = "FRA_IP_PROTO, FRA_SPORT_RANGE, FRA_DPORT_RANGE attributes for policy routing rules", + }, +}; + +int +_nm_platform_kernel_support_init (NMPlatformKernelSupportType type, + int value) { - NMPlatformPrivate *priv; + volatile int *p_state; + gboolean set_default = FALSE; - _CHECK_SELF (self, klass, TRUE); + nm_assert (_NM_INT_NOT_NEGATIVE (type) && type < G_N_ELEMENTS (_nm_platform_kernel_support_state)); - priv = NM_PLATFORM_GET_PRIVATE (self); + p_state = &_nm_platform_kernel_support_state[type]; - /* we cache the response from subclasses and only request it once. - * This probably gives better performance, but more importantly, - * we are guaranteed that the answer for a certain request_flag - * is always the same. */ - if (G_UNLIKELY (!NM_FLAGS_ALL (priv->support_checked, request_flags))) { - NMPlatformKernelSupportFlags checked, response; + if (value == 0) { + set_default = TRUE; + value = _nm_platform_kernel_support_info[type].compile_time_default + ? 1 + : -1; + } - checked = request_flags & ~priv->support_checked; - nm_assert (checked); + nm_assert (NM_IN_SET (value, -1, 1)); - if (klass->check_kernel_support) - response = klass->check_kernel_support (self, checked); - else { - /* fake platform. Pretend no support for anything. */ - response = 0; - } + if (!g_atomic_int_compare_and_exchange (p_state, 0, value)) { + value = g_atomic_int_get (p_state); + nm_assert (NM_IN_SET (value, -1, 1)); + return value; + } - priv->support_checked |= checked; - priv->support_present = (priv->support_present & ~checked) | (response & checked); +#undef NM_THREAD_SAFE_ON_MAIN_THREAD +#define NM_THREAD_SAFE_ON_MAIN_THREAD 0 + + if (set_default) { + nm_log_dbg (LOGD_PLATFORM, "platform: kernel-support for %s (%s) not detected: assume %ssupported", + _nm_platform_kernel_support_info[type].name, + _nm_platform_kernel_support_info[type].desc, + value >= 0 ? "" : "not "); + } else { + nm_log_dbg (LOGD_PLATFORM, "platform: kernel-support for %s (%s) detected: %ssupported", + _nm_platform_kernel_support_info[type].name, + _nm_platform_kernel_support_info[type].desc, + value >= 0 ? "" : "not "); } - return priv->support_present & request_flags; +#undef NM_THREAD_SAFE_ON_MAIN_THREAD +#define NM_THREAD_SAFE_ON_MAIN_THREAD 1 + + return value; } +/*****************************************************************************/ + /** * nm_platform_process_events: * @self: platform instance @@ -468,6 +525,8 @@ nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NMPlatform *self, * @path: Absolute path to sysctl * * Returns: (transfer full): Contents of the virtual sysctl file. + * + * If the path does not exist, %NULL is returned and %errno set to %ENOENT. */ char * nm_platform_sysctl_get (NMPlatform *self, const char *pathid, int dirfd, const char *path) @@ -517,6 +576,8 @@ nm_platform_sysctl_get_int32 (NMPlatform *self, const char *pathid, int dirfd, c * value. On success, %errno will be set to zero. The returned value * will always be in the range between @min and @max * (inclusive) or @fallback. + * If the file does not exist, the fallback is returned and %errno + * is set to ENOENT. */ gint64 nm_platform_sysctl_get_int_checked (NMPlatform *self, @@ -536,11 +597,17 @@ nm_platform_sysctl_get_int_checked (NMPlatform *self, g_return_val_if_fail (path, fallback); - if (path) - value = nm_platform_sysctl_get (self, pathid, dirfd, path); + if (!path) { + errno = EINVAL; + return fallback; + } + value = nm_platform_sysctl_get (self, pathid, dirfd, path); if (!value) { - errno = EINVAL; + /* nm_platform_sysctl_get() set errno to ENOENT if the file does not exist. + * Propagate/preserve that. */ + if (errno != ENOENT) + errno = EINVAL; return fallback; } @@ -1189,21 +1256,6 @@ nm_platform_link_supports_slaves (NMPlatform *self, int ifindex) } /** - * nm_platform_refresh_all: - * @self: platform instance - * @obj_type: The object type to request. - * - * Resync and re-request all objects from kernel of a certain @obj_type. - */ -void -nm_platform_refresh_all (NMPlatform *self, NMPObjectType obj_type) -{ - _CHECK_SELF_VOID (self, klass); - - klass->refresh_all (self, obj_type); -} - -/** * nm_platform_link_refresh: * @self: platform instance * @ifindex: Interface index @@ -1532,6 +1584,28 @@ nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformV return klass->link_set_sriov_vfs (self, ifindex, vfs); } +gboolean +nm_platform_link_set_bridge_vlans (NMPlatform *self, int ifindex, gboolean on_master, const NMPlatformBridgeVlan *const *vlans) +{ + guint i; + _CHECK_SELF (self, klass, FALSE); + + g_return_val_if_fail (ifindex > 0, FALSE); + + _LOG3D ("link: %s bridge VLANs on %s", + vlans ? "setting" : "clearing", + on_master ? "master" : "self"); + if (vlans) { + for (i = 0; vlans[i]; i++) { + const NMPlatformBridgeVlan *vlan = vlans[i]; + + _LOG3D ("link: bridge VLAN %s", nm_platform_bridge_vlan_to_string (vlan, NULL, 0)); + } + } + + return klass->link_set_bridge_vlans (self, ifindex, on_master, vlans); +} + /** * nm_platform_link_set_up: * @self: platform instance @@ -3894,7 +3968,7 @@ nm_platform_ip4_address_sync (NMPlatform *self, if (!known_addresses) return TRUE; - ifa_flags = nm_platform_check_kernel_support (self, NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS) + ifa_flags = nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS) ? IFA_F_NOPREFIXROUTE : 0; @@ -4094,7 +4168,7 @@ next_plat: if (!known_addresses) return TRUE; - ifa_flags = nm_platform_check_kernel_support (self, NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS) + ifa_flags = nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS) ? IFA_F_NOPREFIXROUTE : 0; @@ -4264,14 +4338,13 @@ nm_platform_ip_route_sync (NMPlatform *self, gboolean success = TRUE; char sbuf1[sizeof (_nm_utils_to_string_buffer)]; char sbuf2[sizeof (_nm_utils_to_string_buffer)]; + const gboolean IS_IPv4 = (addr_family == AF_INET); nm_assert (NM_IS_PLATFORM (self)); nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); nm_assert (ifindex > 0); - vt = addr_family == AF_INET - ? &nm_platform_vtable_route_v4 - : &nm_platform_vtable_route_v6; + vt = &nm_platform_vtable_route.vx[IS_IPv4]; for (i_type = 0; routes && i_type < 2; i_type++) { for (i = 0; i < routes->len; i++) { @@ -4578,7 +4651,7 @@ _ip_route_add (NMPlatform *self, nm_assert (route); nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - ifindex = ((NMPlatformObject *)route)->ifindex; + ifindex = ((const NMPlatformIPRoute *) route)->ifindex; _LOG3D ("route: %-10s IPv%c route: %s", _nmp_nlm_flag_to_string (flags & NMP_NLM_FLAG_FMASK), nm_utils_addr_family_to_char (addr_family), @@ -4630,18 +4703,28 @@ gboolean nm_platform_object_delete (NMPlatform *self, const NMPObject *obj) { - int ifindex = obj->object.ifindex; + int ifindex; + _CHECK_SELF (self, klass, FALSE); - if (!NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ROUTE, - NMP_OBJECT_TYPE_QDISC, - NMP_OBJECT_TYPE_TFILTER)) + switch (NMP_OBJECT_GET_TYPE (obj)) { + case NMP_OBJECT_TYPE_ROUTING_RULE: + _LOGD ("%s: delete %s", + NMP_OBJECT_GET_CLASS (obj)->obj_type_name, + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + case NMP_OBJECT_TYPE_QDISC: + case NMP_OBJECT_TYPE_TFILTER: + ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj)->ifindex; + _LOG3D ("%s: delete %s", + NMP_OBJECT_GET_CLASS (obj)->obj_type_name, + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + break; + default: g_return_val_if_reached (FALSE); - - _LOG3D ("%s: delete %s", - NMP_OBJECT_GET_CLASS (obj)->obj_type_name, - nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + } return klass->object_delete (self, obj); } @@ -4971,6 +5054,21 @@ nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, /*****************************************************************************/ int +nm_platform_routing_rule_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformRoutingRule *routing_rule) +{ + _CHECK_SELF (self, klass, -NME_BUG); + + g_return_val_if_fail (routing_rule, -NME_BUG); + + _LOGD ("routing-rule: adding or updating: %s", nm_platform_routing_rule_to_string (routing_rule, NULL, 0)); + return klass->routing_rule_add (self, flags, routing_rule); +} + +/*****************************************************************************/ + +int nm_platform_qdisc_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformQdisc *qdisc) @@ -5276,7 +5374,7 @@ nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len return buf; g_snprintf (buf, len, - lnk->is_tap ? "gretap" : "gre" + "gre%s" /* is_tap */ "%s" /* remote */ "%s" /* local */ "%s" /* parent_ifindex */ @@ -5288,6 +5386,7 @@ nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len "%s" /* ikey */ "%s" /* okey */ "", + lnk->is_tap ? "tap" : "", lnk->remote ? nm_sprintf_buf (str_remote, " remote %s", nm_utils_inet4_ntop (lnk->remote, str_remote1)) : "", lnk->local ? nm_sprintf_buf (str_local, " local %s", nm_utils_inet4_ntop (lnk->local, str_local1)) : "", lnk->parent_ifindex ? nm_sprintf_buf (str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "", @@ -6076,6 +6175,253 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi return buf; } +static void +_routing_rule_addr_to_string (char **buf, + gsize *len, + int addr_family, + const NMIPAddr *addr, + guint8 plen, + gboolean is_src) +{ + char s_addr[NM_UTILS_INET_ADDRSTRLEN]; + gboolean is_zero; + gsize addr_size; + + nm_assert_addr_family (addr_family); + nm_assert (addr); + + addr_size = nm_utils_addr_family_to_size (addr_family); + + is_zero = nm_utils_memeqzero (addr, addr_size); + + if ( plen == 0 + && is_zero) { + if (is_src) + nm_utils_strbuf_append_str (buf, len, " from all"); + else + nm_utils_strbuf_append_str (buf, len, ""); + return; + } + + nm_utils_strbuf_append_str (buf, len, is_src ? " from " : " to "); + + nm_utils_strbuf_append_str (buf, len, nm_utils_inet_ntop (addr_family, addr, s_addr)); + + if (plen != (addr_size * 8)) + nm_utils_strbuf_append (buf, len, "/%u", plen); +} + +static void +_routing_rule_port_range_to_string (char **buf, + gsize *len, + const NMFibRulePortRange *port_range, + const char *name) +{ + if ( port_range->start == 0 + && port_range->end == 0) + nm_utils_strbuf_append_str (buf, len, ""); + else { + nm_utils_strbuf_append (buf, len, " %s %u", name, port_range->start); + if (port_range->start != port_range->end) + nm_utils_strbuf_append (buf, len, "-%u", port_range->end); + } +} + +const char * +nm_platform_routing_rule_to_string (const NMPlatformRoutingRule *routing_rule, char *buf, gsize len) +{ + const char *buf0; + guint32 rr_flags; + + if (!nm_utils_to_string_buffer_init_null (routing_rule, &buf, &len)) + return buf; + + if (!NM_IN_SET (routing_rule->addr_family, AF_INET, AF_INET6)) { + /* invalid addr-family. The other fields are undefined. */ + if (routing_rule->addr_family == AF_UNSPEC) + g_snprintf (buf, len, "[routing-rule]"); + else + g_snprintf (buf, len, "[routing-rule family:%u]", routing_rule->addr_family); + return buf; + } + + buf0 = buf; + + rr_flags = routing_rule->flags; + + rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_INVERT); + nm_utils_strbuf_append (&buf, &len, + "[%c] " /* addr-family */ + "%u:" /* priority */ + "%s", /* not/FIB_RULE_INVERT */ + nm_utils_addr_family_to_char (routing_rule->addr_family), + routing_rule->priority, + ( NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_INVERT) + ? " not" + : "")); + + _routing_rule_addr_to_string (&buf, &len, + routing_rule->addr_family, + &routing_rule->src, + routing_rule->src_len, + TRUE); + + _routing_rule_addr_to_string (&buf, &len, + routing_rule->addr_family, + &routing_rule->dst, + routing_rule->dst_len, + FALSE); + + if (routing_rule->tos) + nm_utils_strbuf_append (&buf, &len, " tos 0x%02x", routing_rule->tos); + + if ( routing_rule->fwmark != 0 + || routing_rule->fwmask != 0) { + nm_utils_strbuf_append (&buf, &len, " fwmark %#x", (unsigned) routing_rule->fwmark); + if (routing_rule->fwmark != 0xFFFFFFFFu) + nm_utils_strbuf_append (&buf, &len, "/%#x", (unsigned) routing_rule->fwmask); + } + + if (routing_rule->iifname[0]) { + nm_utils_strbuf_append (&buf, &len, " iif %s", routing_rule->iifname); + rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_IIF_DETACHED); + if (NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_IIF_DETACHED)) + nm_utils_strbuf_append_str (&buf, &len, " [detached]"); + } + + if (routing_rule->oifname[0]) { + nm_utils_strbuf_append (&buf, &len, " oif %s", routing_rule->oifname); + rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_OIF_DETACHED); + if (NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_OIF_DETACHED)) + nm_utils_strbuf_append_str (&buf, &len, " [detached]"); + } + + if (routing_rule->l3mdev != 0) { + if (routing_rule->l3mdev == 1) + nm_utils_strbuf_append_str (&buf, &len, " lookup [l3mdev-table]"); + else { + nm_utils_strbuf_append (&buf, &len, " lookup [l3mdev-table/%u]", (unsigned) routing_rule->l3mdev); + } + } + + if ( routing_rule->uid_range_has + || routing_rule->uid_range.start + || routing_rule->uid_range.end) { + nm_utils_strbuf_append (&buf, &len, + " uidrange %u-%u%s", + routing_rule->uid_range.start, + routing_rule->uid_range.end, + routing_rule->uid_range_has ? "" : "(?)"); + } + + if (routing_rule->ip_proto != 0) { + /* we don't call getprotobynumber(), just print the numeric value. + * This differs from what ip-rule prints. */ + nm_utils_strbuf_append (&buf, &len, + " ipproto %u", + routing_rule->ip_proto); + } + + _routing_rule_port_range_to_string (&buf, &len, + &routing_rule->sport_range, + "sport"); + + _routing_rule_port_range_to_string (&buf, &len, + &routing_rule->dport_range, + "dport"); + + if (routing_rule->tun_id != 0) { + nm_utils_strbuf_append (&buf, &len, + " tun_id %"G_GUINT64_FORMAT, + routing_rule->tun_id); + } + + if (routing_rule->table != 0) { + nm_utils_strbuf_append (&buf, &len, + " lookup %u", + routing_rule->table); + } + + if (routing_rule->suppress_prefixlen_inverse != 0) { + nm_utils_strbuf_append (&buf, &len, + " suppress_prefixlen %d", + (int) (~routing_rule->suppress_prefixlen_inverse)); + } + + if (routing_rule->suppress_ifgroup_inverse != 0) { + nm_utils_strbuf_append (&buf, &len, + " suppress_ifgroup %d", + (int) (~routing_rule->suppress_ifgroup_inverse)); + } + + if (routing_rule->flow) { + /* FRA_FLOW is only for IPv4, but we want to print the value for all address-families, + * to see when it is set. In practice, this should not be set except for IPv4. + * + * We don't follow the style how ip-rule prints flow/realms. It's confusing. Just + * print the value hex. */ + nm_utils_strbuf_append (&buf, &len, + " realms 0x%08x", + routing_rule->flow); + } + + if (routing_rule->action == RTN_NAT) { + G_STATIC_ASSERT_EXPR (RTN_NAT == 10); + + /* NAT is deprecated for many years. We don't support RTA_GATEWAY/FRA_UNUSED2 + * for the gateway, and so do recent kernels ignore that parameter. */ + nm_utils_strbuf_append_str (&buf, &len, " masquerade"); + } else if (routing_rule->action == FR_ACT_GOTO) { + if (routing_rule->goto_target != 0) + nm_utils_strbuf_append (&buf, &len, " goto %u", routing_rule->goto_target); + else + nm_utils_strbuf_append_str (&buf, &len, " goto none"); + rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_UNRESOLVED); + if (NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_UNRESOLVED)) + nm_utils_strbuf_append_str (&buf, &len, " unresolved"); + } else if (routing_rule->action != FR_ACT_TO_TBL) { + const char *ss; + char ss_buf[60]; + +#define _V(v1, v2) ((sizeof (char[(((int) (v1)) == ((int) (v2))) ? 1 : -1]) * 0) + (v1)) + switch (routing_rule->action) { + case _V (FR_ACT_UNSPEC, RTN_UNSPEC) : ss = "none"; break; + case _V (FR_ACT_TO_TBL, RTN_UNICAST) : ss = "unicast"; break; + case _V (FR_ACT_GOTO, RTN_LOCAL) : ss = "local"; break; + case _V (FR_ACT_NOP, RTN_BROADCAST) : ss = "nop"; break; + case _V (FR_ACT_RES3, RTN_ANYCAST) : ss = "anycast"; break; + case _V (FR_ACT_RES4, RTN_MULTICAST) : ss = "multicast"; break; + case _V (FR_ACT_BLACKHOLE, RTN_BLACKHOLE) : ss = "blackhole"; break; + case _V (FR_ACT_UNREACHABLE, RTN_UNREACHABLE) : ss = "unreachable"; break; + case _V (FR_ACT_PROHIBIT, RTN_PROHIBIT) : ss = "prohibit"; break; + case RTN_THROW : ss = "throw"; break; + case RTN_NAT : ss = "nat"; break; + case RTN_XRESOLVE : ss = "xresolve"; break; + default: + ss = nm_sprintf_buf (ss_buf, "action-%u", routing_rule->action); + break; + } +#undef _V + nm_utils_strbuf_append (&buf, &len, " %s", ss); + } + + if (routing_rule->protocol != RTPROT_UNSPEC) + nm_utils_strbuf_append (&buf, &len, " protocol %u", routing_rule->protocol); + + if ( routing_rule->goto_target != 0 + && routing_rule->action != FR_ACT_GOTO) { + /* a trailing target is set for an unexpected action. Print it. */ + nm_utils_strbuf_append (&buf, &len, " goto-target %u", routing_rule->goto_target); + } + + if (rr_flags != 0) { + /* we have some flags we didn't print about yet. */ + nm_utils_strbuf_append (&buf, &len, " remaining-flags %x", rr_flags); + } + + return buf0; +} + const char * nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len) { @@ -6249,6 +6595,28 @@ nm_platform_vf_to_string (const NMPlatformVF *vf, char *buf, gsize len) return buf; } + +const char * +nm_platform_bridge_vlan_to_string (const NMPlatformBridgeVlan *vlan, char *buf, gsize len) +{ + char str_vid_end[64]; + + if (!nm_utils_to_string_buffer_init_null (vlan, &buf, &len)) + return buf; + + g_snprintf (buf, len, + "%u" + "%s" + "%s" + "%s", + vlan->vid_start, + vlan->vid_start != vlan->vid_end ? nm_sprintf_buf (str_vid_end, "-%u", vlan->vid_end) : "", + vlan->pvid ? " PVID" : "", + vlan->untagged ? " untagged" : ""); + + return buf; +} + void nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h) { @@ -7034,6 +7402,216 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route return 0; } +#define _ROUTING_RULE_FLAGS_IGNORE ( FIB_RULE_UNRESOLVED \ + | FIB_RULE_IIF_DETACHED \ + | FIB_RULE_OIF_DETACHED) + +#define _routing_rule_compare(cmp_type, kernel_support_type) \ + ( (cmp_type) == NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL \ + || nm_platform_kernel_support_get (kernel_support_type)) + +void +nm_platform_routing_rule_hash_update (const NMPlatformRoutingRule *obj, + NMPlatformRoutingRuleCmpType cmp_type, + NMHashState *h) +{ + gboolean cmp_full = TRUE; + gsize addr_size; + guint32 flags_mask = G_MAXUINT32; + + if (G_UNLIKELY (!NM_IN_SET (obj->addr_family, AF_INET, AF_INET6))) { + /* the address family is not one of the supported ones. That means, the + * instance will only compare equal to itself (pointer-equality). */ + nm_hash_update_val (h, (gconstpointer) obj); + return; + } + + switch (cmp_type) { + + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID: + + flags_mask &= ~_ROUTING_RULE_FLAGS_IGNORE; + + /* fall-through */ + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY: + + cmp_full = FALSE; + + /* fall-through */ + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL: + + nm_hash_update_vals (h, + obj->addr_family, + obj->tun_id, + obj->table, + obj->flags & flags_mask, + obj->priority, + obj->fwmark, + obj->fwmask, + ( ( cmp_full + || ( cmp_type == NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY + && obj->action == FR_ACT_GOTO)) + ? obj->goto_target + : (guint32) 0u), + ( ( cmp_full + || obj->addr_family == AF_INET) + ? obj->flow + : (guint32) 0u), + NM_HASH_COMBINE_BOOLS (guint8, + ( _routing_rule_compare (cmp_type, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE) + ? obj->uid_range_has + : FALSE)), + obj->suppress_prefixlen_inverse, + obj->suppress_ifgroup_inverse, + ( _routing_rule_compare (cmp_type, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV) + ? ( cmp_full + ? (guint16) obj->l3mdev + : (guint16) !!obj->l3mdev) + : G_MAXUINT16), + obj->action, + obj->tos, + obj->src_len, + obj->dst_len, + ( _routing_rule_compare (cmp_type, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL) + ? (guint16) obj->protocol + : G_MAXUINT16)); + addr_size = nm_utils_addr_family_to_size (obj->addr_family); + if (cmp_full || obj->src_len > 0) + nm_hash_update (h, &obj->src, addr_size); + if (cmp_full || obj->dst_len > 0) + nm_hash_update (h, &obj->dst, addr_size); + if (_routing_rule_compare (cmp_type, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE)) { + if (cmp_full || obj->uid_range_has) + nm_hash_update_valp (h, &obj->uid_range); + } + if (_routing_rule_compare (cmp_type, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO)) { + nm_hash_update_val (h, obj->ip_proto); + nm_hash_update_valp (h, &obj->sport_range); + nm_hash_update_valp (h, &obj->dport_range); + } + nm_hash_update_str (h, obj->iifname); + nm_hash_update_str (h, obj->oifname); + return; + } + + nm_assert_not_reached (); +} + +int +nm_platform_routing_rule_cmp (const NMPlatformRoutingRule *a, + const NMPlatformRoutingRule *b, + NMPlatformRoutingRuleCmpType cmp_type) +{ + gboolean cmp_full = TRUE; + gsize addr_size; + bool valid; + guint32 flags_mask = G_MAXUINT32; + + NM_CMP_SELF (a, b); + + valid = NM_IN_SET (a->addr_family, AF_INET, AF_INET6); + NM_CMP_DIRECT (valid, + (bool) NM_IN_SET (b->addr_family, AF_INET, AF_INET6)); + + if (G_UNLIKELY (!valid)) { + /* the address family is not one of the supported ones. That means, the + * instance will only compare equal to itself. */ + NM_CMP_DIRECT ((uintptr_t) a, (uintptr_t) b); + nm_assert_not_reached (); + return 0; + } + + switch (cmp_type) { + + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID: + + flags_mask &= ~_ROUTING_RULE_FLAGS_IGNORE; + + /* fall-through */ + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY: + + cmp_full = FALSE; + + /* fall-through */ + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL: + NM_CMP_FIELD (a, b, addr_family); + NM_CMP_FIELD (a, b, action); + NM_CMP_FIELD (a, b, priority); + NM_CMP_FIELD (a, b, tun_id); + + if (_routing_rule_compare (cmp_type, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV)) { + if (cmp_full) + NM_CMP_FIELD (a, b, l3mdev); + else + NM_CMP_FIELD_BOOL (a, b, l3mdev); + } + + NM_CMP_FIELD (a, b, table); + + NM_CMP_DIRECT (a->flags & flags_mask, b->flags & flags_mask); + + NM_CMP_FIELD (a, b, fwmark); + NM_CMP_FIELD (a, b, fwmask); + + if ( cmp_full + || ( cmp_type == NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY + && a->action == FR_ACT_GOTO)) + NM_CMP_FIELD (a, b, goto_target); + + NM_CMP_FIELD (a, b, suppress_prefixlen_inverse); + NM_CMP_FIELD (a, b, suppress_ifgroup_inverse); + NM_CMP_FIELD (a, b, tos); + + if (cmp_full || a->addr_family == AF_INET) + NM_CMP_FIELD (a, b, flow); + + if (_routing_rule_compare (cmp_type, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL)) + NM_CMP_FIELD (a, b, protocol); + + if (_routing_rule_compare (cmp_type, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO)) { + NM_CMP_FIELD (a, b, ip_proto); + NM_CMP_FIELD (a, b, sport_range.start); + NM_CMP_FIELD (a, b, sport_range.end); + NM_CMP_FIELD (a, b, dport_range.start); + NM_CMP_FIELD (a, b, dport_range.end); + } + + addr_size = nm_utils_addr_family_to_size (a->addr_family); + + NM_CMP_FIELD (a, b, src_len); + if (cmp_full || a->src_len > 0) + NM_CMP_FIELD_MEMCMP_LEN (a, b, src, addr_size); + + NM_CMP_FIELD (a, b, dst_len); + if (cmp_full || a->dst_len > 0) + NM_CMP_FIELD_MEMCMP_LEN (a, b, dst, addr_size); + + if (_routing_rule_compare (cmp_type, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE)) { + NM_CMP_FIELD_UNSAFE (a, b, uid_range_has); + if (cmp_full || a->uid_range_has) { + NM_CMP_FIELD (a, b, uid_range.start); + NM_CMP_FIELD (a, b, uid_range.end); + } + } + + NM_CMP_FIELD_STR (a, b, iifname); + NM_CMP_FIELD_STR (a, b, oifname); + return 0; + } + + nm_assert_not_reached (); + return 0; +} + /** * nm_platform_ip_address_cmp_expiry: * @a: a NMPlatformIPAddress to compare @@ -7131,6 +7709,13 @@ log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatform } static void +log_routing_rule (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformRoutingRule *routing_rule, NMPlatformSignalChangeType change_type, gpointer user_data) +{ + /* routing rules don't have an ifindex. We probably should refactor the signals that are emitted for platform changes. */ + _LOG3D ("signal: rt-rule %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_routing_rule_to_string (routing_rule, NULL, 0)); +} + +static void log_qdisc (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformQdisc *qdisc, NMPlatformSignalChangeType change_type, gpointer user_data) { _LOG3D ("signal: qdisc %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_qdisc_to_string (qdisc, NULL, 0)); @@ -7196,9 +7781,13 @@ nm_platform_cache_update_emit_signal (NMPlatform *self, return; } - ifindex = o->object.ifindex; klass = NMP_OBJECT_GET_CLASS (o); + if (klass->obj_type == NMP_OBJECT_TYPE_ROUTING_RULE) + ifindex = 0; + else + ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (o)->ifindex; + if ( klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE && NM_PLATFORM_GET_PRIVATE (self)->ip4_dev_route_blacklist_gc_timeout_id && NM_IN_SET (cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED)) @@ -7214,7 +7803,7 @@ nm_platform_cache_update_emit_signal (NMPlatform *self, _nm_platform_signal_id_get (klass->signal_type_id), 0, (int) klass->obj_type, - o->object.ifindex, + ifindex, &o->object, (int) cache_op); nmp_object_unref (o); @@ -7261,24 +7850,25 @@ _vtr_v4_metric_normalize (guint32 metric) /*****************************************************************************/ -const NMPlatformVTableRoute nm_platform_vtable_route_v4 = { - .is_ip4 = TRUE, - .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, - .addr_family = AF_INET, - .sizeof_route = sizeof (NMPlatformIP4Route), - .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip4_route_cmp, - .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip4_route_to_string, - .metric_normalize = _vtr_v4_metric_normalize, -}; - -const NMPlatformVTableRoute nm_platform_vtable_route_v6 = { - .is_ip4 = FALSE, - .obj_type = NMP_OBJECT_TYPE_IP6_ROUTE, - .addr_family = AF_INET6, - .sizeof_route = sizeof (NMPlatformIP6Route), - .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip6_route_cmp, - .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip6_route_to_string, - .metric_normalize = nm_utils_ip6_route_metric_normalize, +const _NMPlatformVTableRouteUnion nm_platform_vtable_route = { + .v4 = { + .is_ip4 = TRUE, + .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, + .addr_family = AF_INET, + .sizeof_route = sizeof (NMPlatformIP4Route), + .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip4_route_cmp, + .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip4_route_to_string, + .metric_normalize = _vtr_v4_metric_normalize, + }, + .v6 = { + .is_ip4 = FALSE, + .obj_type = NMP_OBJECT_TYPE_IP6_ROUTE, + .addr_family = AF_INET6, + .sizeof_route = sizeof (NMPlatformIP6Route), + .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip6_route_cmp, + .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip6_route_to_string, + .metric_normalize = nm_utils_ip6_route_metric_normalize, + }, }; /*****************************************************************************/ @@ -7338,8 +7928,9 @@ constructor (GType type, priv->multi_idx = nm_dedup_multi_index_new (); - priv->cache = nmp_cache_new (nm_platform_get_multi_idx (self), + priv->cache = nmp_cache_new (priv->multi_idx, priv->use_udev); + return object; } @@ -7411,11 +8002,12 @@ nm_platform_class_init (NMPlatformClass *platform_class) } G_STMT_END /* Signals */ - SIGNAL (NM_PLATFORM_SIGNAL_ID_LINK, NM_PLATFORM_SIGNAL_LINK_CHANGED, log_link); - SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address); - SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address); - SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route); - SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route); - SIGNAL (NM_PLATFORM_SIGNAL_ID_QDISC, NM_PLATFORM_SIGNAL_QDISC_CHANGED, log_qdisc); - SIGNAL (NM_PLATFORM_SIGNAL_ID_TFILTER, NM_PLATFORM_SIGNAL_TFILTER_CHANGED, log_tfilter); + SIGNAL (NM_PLATFORM_SIGNAL_ID_LINK, NM_PLATFORM_SIGNAL_LINK_CHANGED, log_link); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route); + SIGNAL (NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED, log_routing_rule); + SIGNAL (NM_PLATFORM_SIGNAL_ID_QDISC, NM_PLATFORM_SIGNAL_QDISC_CHANGED, log_qdisc); + SIGNAL (NM_PLATFORM_SIGNAL_ID_TFILTER, NM_PLATFORM_SIGNAL_TFILTER_CHANGED, log_tfilter); } diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 37aa58fd..97be8832 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -152,6 +152,14 @@ typedef enum { } NMPlatformIPRouteCmpType; typedef enum { + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, + + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY, + + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL, +} NMPlatformRoutingRuleCmpType; + +typedef enum { /* match-flags are strictly inclusive. That means, * by default nothing is matched, but if you enable a particular @@ -181,12 +189,22 @@ typedef enum { #define NM_PLATFORM_LINK_OTHER_NETNS (-1) -#define __NMPlatformObject_COMMON \ +struct _NMPlatformObject { + /* the object type has no fields of its own, it is only used to having + * a special pointer type that can be used to indicate "any" type. */ + char _dummy_don_t_use_me; +}; + +#define __NMPlatformObjWithIfindex_COMMON \ int ifindex; \ ; +struct _NMPlatformObjWithIfindex { + __NMPlatformObjWithIfindex_COMMON; +}; + struct _NMPlatformLink { - __NMPlatformObject_COMMON; + __NMPlatformObjWithIfindex_COMMON; char name[NMP_IFNAMSIZ]; NMLinkType type; @@ -246,6 +264,7 @@ typedef enum { /*< skip >*/ NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, + NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, NM_PLATFORM_SIGNAL_ID_QDISC, NM_PLATFORM_SIGNAL_ID_TFILTER, _NM_PLATFORM_SIGNAL_ID_LAST, @@ -260,15 +279,11 @@ typedef enum { NM_PLATFORM_SIGNAL_REMOVED, } NMPlatformSignalChangeType; -struct _NMPlatformObject { - __NMPlatformObject_COMMON; -}; - #define NM_PLATFORM_IP_ADDRESS_CAST(address) \ NM_CONSTCAST (NMPlatformIPAddress, (address), NMPlatformIPXAddress, NMPlatformIP4Address, NMPlatformIP6Address) #define __NMPlatformIPAddress_COMMON \ - __NMPlatformObject_COMMON; \ + __NMPlatformObjWithIfindex_COMMON; \ NMIPConfigSource addr_source; \ \ /* Timestamp in seconds in the reference system of nm_utils_get_monotonic_timestamp_*(). @@ -370,7 +385,7 @@ typedef union { #define NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE 0 #define __NMPlatformIPRoute_COMMON \ - __NMPlatformObject_COMMON; \ + __NMPlatformObjWithIfindex_COMMON; \ \ /* The NMIPConfigSource. For routes that we receive from cache this corresponds * to the rtm_protocol field (and is one of the NM_IP_CONFIG_SOURCE_RTPROT_* values). @@ -414,7 +429,7 @@ typedef union { * Such a route is not alive, according to nmp_object_is_alive(). * * NOTE: currently we ignore all flags except RTM_F_CLONED - * and RTNH_F_ONLINK for IPv4. + * and RTNH_F_ONLINK. * We also may not properly consider the flags as part of the ID * in route-cmp. */ \ unsigned r_rtm_flags; \ @@ -540,7 +555,49 @@ typedef union { #undef __NMPlatformIPRoute_COMMON typedef struct { - __NMPlatformObject_COMMON; + /* struct fib_rule_uid_range */ + guint32 start; + guint32 end; +} NMFibRuleUidRange; + +typedef struct { + /* struct fib_rule_port_range */ + guint16 start; + guint16 end; +} NMFibRulePortRange; + +typedef struct { + NMIPAddr src; /* FRA_SRC */ + NMIPAddr dst; /* FRA_DST */ + guint64 tun_id; /* betoh64(FRA_TUN_ID) */ + guint32 table; /* (struct fib_rule_hdr).table, FRA_TABLE */ + guint32 flags; /* (struct fib_rule_hdr).flags */ + guint32 priority; /* RA_PRIORITY */ + guint32 fwmark; /* FRA_FWMARK */ + guint32 fwmask; /* FRA_FWMASK */ + guint32 goto_target; /* FRA_GOTO */ + guint32 flow; /* FRA_FLOW */ + guint32 suppress_prefixlen_inverse; /* ~(FRA_SUPPRESS_PREFIXLEN) */ + guint32 suppress_ifgroup_inverse; /* ~(FRA_SUPPRESS_IFGROUP) */ + NMFibRuleUidRange uid_range; /* FRA_UID_RANGE */ + NMFibRulePortRange sport_range; /* FRA_SPORT_RANGE */ + NMFibRulePortRange dport_range; /* FRA_DPORT_RANGE */ + char iifname[NMP_IFNAMSIZ]; /* FRA_IIFNAME */ + char oifname[NMP_IFNAMSIZ]; /* FRA_OIFNAME */ + guint8 addr_family; /* (struct fib_rule_hdr).family */ + guint8 action; /* (struct fib_rule_hdr).action */ + guint8 tos; /* (struct fib_rule_hdr).tos */ + guint8 src_len; /* (struct fib_rule_hdr).src_len */ + guint8 dst_len; /* (struct fib_rule_hdr).dst_len */ + guint8 l3mdev; /* FRA_L3MDEV */ + guint8 protocol; /* FRA_PROTOCOL */ + guint8 ip_proto; /* FRA_IP_PROTO */ + + bool uid_range_has:1; /* has(FRA_UID_RANGE) */ +} NMPlatformRoutingRule; + +typedef struct { + __NMPlatformObjWithIfindex_COMMON; const char *kind; int addr_family; guint32 handle; @@ -562,7 +619,7 @@ typedef struct { #define NM_PLATFORM_ACTION_KIND_SIMPLE "simple" typedef struct { - __NMPlatformObject_COMMON; + __NMPlatformObjWithIfindex_COMMON; const char *kind; int addr_family; guint32 handle; @@ -571,7 +628,7 @@ typedef struct { NMPlatformAction action; } NMPlatformTfilter; -#undef __NMPlatformObject_COMMON +#undef __NMPlatformObjWithIfindex_COMMON typedef struct { gboolean is_ip4; @@ -583,8 +640,15 @@ typedef struct { guint32 (*metric_normalize) (guint32 metric); } NMPlatformVTableRoute; -extern const NMPlatformVTableRoute nm_platform_vtable_route_v4; -extern const NMPlatformVTableRoute nm_platform_vtable_route_v6; +typedef union { + struct { + NMPlatformVTableRoute v6; + NMPlatformVTableRoute v4; + }; + NMPlatformVTableRoute vx[2]; +} _NMPlatformVTableRouteUnion; + +extern const _NMPlatformVTableRouteUnion nm_platform_vtable_route; typedef struct { guint16 id; @@ -607,6 +671,13 @@ typedef struct { } NMPlatformVF; typedef struct { + guint16 vid_start; + guint16 vid_end; + bool untagged:1; + bool pvid:1; +} NMPlatformBridgeVlan; + +typedef struct { in_addr_t local; in_addr_t remote; int parent_ifindex; @@ -748,12 +819,6 @@ typedef enum { } NMPlatformLinkDuplexType; typedef enum { - NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS = (1LL << 0), - NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL = (1LL << 1), - NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF = (1LL << 2), -} NMPlatformKernelSupportFlags; - -typedef enum { NM_PLATFORM_WIREGUARD_CHANGE_FLAG_NONE = 0, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS = (1LL << 0), NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY = (1LL << 1), @@ -779,6 +844,51 @@ typedef enum { /*****************************************************************************/ +typedef enum { + NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL, + + /* this also includes FRA_SPORT_RANGE and FRA_DPORT_RANGE which + * were added at the same time. */ + NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO, + + _NM_PLATFORM_KERNEL_SUPPORT_NUM, +} NMPlatformKernelSupportType; + +extern volatile int _nm_platform_kernel_support_state[_NM_PLATFORM_KERNEL_SUPPORT_NUM]; + +int _nm_platform_kernel_support_init (NMPlatformKernelSupportType type, + int value); + +#define _nm_platform_kernel_support_detected(type) \ + G_LIKELY (({ \ + const NMPlatformKernelSupportType _type = (type); \ + \ + nm_assert (_NM_INT_NOT_NEGATIVE (_type) && _type < G_N_ELEMENTS (_nm_platform_kernel_support_state)); \ + \ + (_nm_platform_kernel_support_state[_type] != 0); \ + })) + +#define nm_platform_kernel_support_get(type) \ + ({ \ + const NMPlatformKernelSupportType _type = (type); \ + int _v; \ + \ + nm_assert (_NM_INT_NOT_NEGATIVE (_type) && _type < G_N_ELEMENTS (_nm_platform_kernel_support_state)); \ + \ + _v = _nm_platform_kernel_support_state[_type]; \ + if (G_UNLIKELY (_v == 0)) \ + _v = _nm_platform_kernel_support_init (_type, 0); \ + \ + (_v >= 0); \ + }) + +/*****************************************************************************/ + struct _NMPlatformPrivate; struct _NMPlatform { @@ -793,8 +903,6 @@ typedef struct { gboolean (*sysctl_set) (NMPlatform *, const char *pathid, int dirfd, const char *path, const char *value); char * (*sysctl_get) (NMPlatform *, const char *pathid, int dirfd, const char *path); - void (*refresh_all) (NMPlatform *self, NMPObjectType obj_type); - int (*link_add) (NMPlatform *, const char *name, NMLinkType type, @@ -831,6 +939,7 @@ typedef struct { gboolean (*link_set_name) (NMPlatform *, int ifindex, const char *name); gboolean (*link_set_sriov_params) (NMPlatform *, int ifindex, guint num_vfs, int autoprobe); gboolean (*link_set_sriov_vfs) (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); + gboolean (*link_set_bridge_vlans) (NMPlatform *self, int ifindex, gboolean on_master, const NMPlatformBridgeVlan *const *vlans); char * (*link_get_physical_port_id) (NMPlatform *, int ifindex); guint (*link_get_dev_id) (NMPlatform *, int ifindex); @@ -973,6 +1082,10 @@ typedef struct { int oif_ifindex, NMPObject **out_route); + int (*routing_rule_add) (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformRoutingRule *routing_rule); + int (*qdisc_add) (NMPlatform *self, NMPNlmFlags flags, const NMPlatformQdisc *qdisc); @@ -980,9 +1093,6 @@ typedef struct { int (*tfilter_add) (NMPlatform *self, NMPNlmFlags flags, const NMPlatformTfilter *tfilter); - - NMPlatformKernelSupportFlags (*check_kernel_support) (NMPlatform * self, - NMPlatformKernelSupportFlags request_flags); } NMPlatformClass; /* NMPlatform signals @@ -1001,6 +1111,7 @@ typedef struct { #define NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED "ip6-address-changed" #define NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED "ip4-route-changed" #define NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED "ip6-route-changed" +#define NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED "routing-rule-changed" #define NM_PLATFORM_SIGNAL_QDISC_CHANGED "qdisc-changed" #define NM_PLATFORM_SIGNAL_TFILTER_CHANGED "tfilter-changed" @@ -1167,8 +1278,6 @@ gboolean nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NMPlatform *self, const char *nm_platform_if_indextoname (NMPlatform *self, int ifindex, char *out_ifname/* of size IFNAMSIZ */); int nm_platform_if_nametoindex (NMPlatform *self, const char *ifname); -void nm_platform_refresh_all (NMPlatform *self, NMPObjectType obj_type); - const NMPObject *nm_platform_link_get_obj (NMPlatform *self, int ifindex, gboolean visible_only); @@ -1261,6 +1370,7 @@ int nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu); gboolean nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name); gboolean nm_platform_link_set_sriov_params (NMPlatform *self, int ifindex, guint num_vfs, int autoprobe); gboolean nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); +gboolean nm_platform_link_set_bridge_vlans (NMPlatform *self, int ifindex, gboolean on_master, const NMPlatformBridgeVlan *const *vlans); char *nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex); guint nm_platform_link_get_dev_id (NMPlatform *self, int ifindex); @@ -1482,6 +1592,10 @@ int nm_platform_ip_route_get (NMPlatform *self, int oif_ifindex, NMPObject **out_route); +int nm_platform_routing_rule_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformRoutingRule *routing_rule); + int nm_platform_qdisc_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformQdisc *qdisc); @@ -1512,9 +1626,11 @@ const char *nm_platform_ip4_address_to_string (const NMPlatformIP4Address *addre const char *nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *buf, gsize len); const char *nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsize len); const char *nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len); +const char *nm_platform_routing_rule_to_string (const NMPlatformRoutingRule *routing_rule, char *buf, gsize len); const char *nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len); const char *nm_platform_tfilter_to_string (const NMPlatformTfilter *tfilter, char *buf, gsize len); const char *nm_platform_vf_to_string (const NMPlatformVF *vf, char *buf, gsize len); +const char *nm_platform_bridge_vlan_to_string (const NMPlatformBridgeVlan *vlan, char *buf, gsize len); const char *nm_platform_vlan_qos_mapping_to_string (const char *name, const NMVlanQosMapping *map, @@ -1556,6 +1672,14 @@ nm_platform_ip6_route_cmp_full (const NMPlatformIP6Route *a, const NMPlatformIP6 return nm_platform_ip6_route_cmp (a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); } +int nm_platform_routing_rule_cmp (const NMPlatformRoutingRule *a, const NMPlatformRoutingRule *b, NMPlatformRoutingRuleCmpType cmp_type); + +static inline int +nm_platform_routing_rule_cmp_full (const NMPlatformRoutingRule *a, const NMPlatformRoutingRule *b) +{ + return nm_platform_routing_rule_cmp (a, b, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL); +} + int nm_platform_qdisc_cmp (const NMPlatformQdisc *a, const NMPlatformQdisc *b); int nm_platform_tfilter_cmp (const NMPlatformTfilter *a, const NMPlatformTfilter *b); @@ -1564,6 +1688,7 @@ void nm_platform_ip4_address_hash_update (const NMPlatformIP4Address *obj, NMHas void nm_platform_ip6_address_hash_update (const NMPlatformIP6Address *obj, NMHashState *h); void nm_platform_ip4_route_hash_update (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); void nm_platform_ip6_route_hash_update (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); +void nm_platform_routing_rule_hash_update (const NMPlatformRoutingRule *obj, NMPlatformRoutingRuleCmpType cmp_type, NMHashState *h); void nm_platform_lnk_gre_hash_update (const NMPlatformLnkGre *obj, NMHashState *h); void nm_platform_lnk_infiniband_hash_update (const NMPlatformLnkInfiniband *obj, NMHashState *h); void nm_platform_lnk_ip6tnl_hash_update (const NMPlatformLnkIp6Tnl *obj, NMHashState *h); @@ -1579,9 +1704,6 @@ void nm_platform_lnk_wireguard_hash_update (const NMPlatformLnkWireGuard *obj, N void nm_platform_qdisc_hash_update (const NMPlatformQdisc *obj, NMHashState *h); void nm_platform_tfilter_hash_update (const NMPlatformTfilter *obj, NMHashState *h); -NMPlatformKernelSupportFlags nm_platform_check_kernel_support (NMPlatform *self, - NMPlatformKernelSupportFlags request_flags); - const char *nm_platform_link_flags2str (unsigned flags, char *buf, gsize len); const char *nm_platform_link_inet6_addrgenmode2str (guint8 mode, char *buf, gsize len); const char *nm_platform_addr_flags2str (unsigned flags, char *buf, gsize len); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 6ec6fea1..0d20100c 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -28,7 +28,7 @@ #include <libudev.h> #include "nm-utils.h" -#include "nm-utils/nm-secret-utils.h" +#include "nm-glib-aux/nm-secret-utils.h" #include "nm-core-utils.h" #include "nm-platform-utils.h" @@ -389,16 +389,16 @@ _idx_obj_part (const DedupMultiIdxType *idx_type, nm_hash_update_val (h, obj_a); return 0; } - nm_assert (obj_a->object.ifindex > 0); + nm_assert (NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj_a)->ifindex > 0); if (obj_b) { return NMP_OBJECT_GET_TYPE (obj_a) == NMP_OBJECT_GET_TYPE (obj_b) - && obj_a->object.ifindex == obj_b->object.ifindex + && NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj_a)->ifindex == NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj_b)->ifindex && nmp_object_is_visible (obj_b); } if (h) { nm_hash_update_vals (h, idx_type->cache_id_type, - obj_a->object.ifindex); + NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj_a)->ifindex); } return 1; @@ -406,14 +406,14 @@ _idx_obj_part (const DedupMultiIdxType *idx_type, obj_type = NMP_OBJECT_GET_TYPE (obj_a); if ( !NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE) - || obj_a->object.ifindex <= 0) { + || NMP_OBJECT_CAST_IP_ROUTE (obj_a)->ifindex <= 0) { if (h) nm_hash_update_val (h, obj_a); return 0; } if (obj_b) { return obj_type == NMP_OBJECT_GET_TYPE (obj_b) - && obj_b->object.ifindex > 0 + && NMP_OBJECT_CAST_IP_ROUTE (obj_b)->ifindex > 0 && (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE ? (nm_platform_ip4_route_cmp (&obj_a->ip4_route, &obj_b->ip4_route, NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID) == 0) : (nm_platform_ip6_route_cmp (&obj_a->ip6_route, &obj_b->ip6_route, NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID) == 0)); @@ -427,6 +427,25 @@ _idx_obj_part (const DedupMultiIdxType *idx_type, } return 1; + case NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY: + obj_type = NMP_OBJECT_GET_TYPE (obj_a); + /* currently, only routing rules are supported for this cache-id-type. */ + if ( obj_type != NMP_OBJECT_TYPE_ROUTING_RULE + || !NM_IN_SET (obj_a->routing_rule.addr_family, AF_INET, AF_INET6)) { + if (h) + nm_hash_update_val (h, obj_a); + return 0; + } + if (obj_b) { + return NMP_OBJECT_GET_TYPE (obj_b) == NMP_OBJECT_TYPE_ROUTING_RULE + && obj_a->routing_rule.addr_family == obj_b->routing_rule.addr_family; + } + if (h) { + nm_hash_update_vals (h, idx_type->cache_id_type, + obj_a->routing_rule.addr_family); + } + return 1; + case NMP_CACHE_ID_TYPE_NONE: case __NMP_CACHE_ID_TYPE_MAX: break; @@ -712,16 +731,6 @@ _nmp_object_fixup_link_master_connected (NMPObject **obj_new, NMPObject *obj_ori /*****************************************************************************/ -const NMPClass * -nmp_class_from_type (NMPObjectType obj_type) -{ - g_return_val_if_fail (obj_type > NMP_OBJECT_TYPE_UNKNOWN && obj_type <= NMP_OBJECT_TYPE_MAX, NULL); - - return &_nmp_classes[obj_type - 1]; -} - -/*****************************************************************************/ - static void _vt_cmd_obj_dispose_link (NMPObject *obj) { @@ -1369,6 +1378,10 @@ _vt_cmd_plobj_id_copy (ip6_route, NMPlatformIP6Route, { *dst = *src; nm_assert (nm_platform_ip6_route_cmp (dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0); }); +_vt_cmd_plobj_id_copy (routing_rule, NMPlatformRoutingRule, { + *dst = *src; + nm_assert (nm_platform_routing_rule_cmp (dst, src, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0); +}); /* Uses internally nmp_object_copy(), hence it also violates the const * promise for @obj. @@ -1469,6 +1482,12 @@ _vt_cmd_plobj_id_cmp_ip6_route (const NMPlatformObject *obj1, const NMPlatformOb return nm_platform_ip6_route_cmp ((NMPlatformIP6Route *) obj1, (NMPlatformIP6Route *) obj2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); } +static int +_vt_cmd_plobj_id_cmp_routing_rule (const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_routing_rule_cmp ((NMPlatformRoutingRule *) obj1, (NMPlatformRoutingRule *) obj2, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID); +} + void nmp_object_id_hash_update (const NMPObject *obj, NMHashState *h) { @@ -1534,6 +1553,9 @@ _vt_cmd_plobj_id_hash_update (ip4_route, NMPlatformIP4Route, { _vt_cmd_plobj_id_hash_update (ip6_route, NMPlatformIP6Route, { nm_platform_ip6_route_hash_update (obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); }) +_vt_cmd_plobj_id_hash_update (routing_rule, NMPlatformRoutingRule, { + nm_platform_routing_rule_hash_update (obj, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, h); +}) _vt_cmd_plobj_id_hash_update (qdisc, NMPlatformQdisc, { nm_hash_update_vals (h, obj->ifindex, @@ -1557,6 +1579,12 @@ _vt_cmd_plobj_hash_update_ip6_route (const NMPlatformObject *obj, NMHashState *h return nm_platform_ip6_route_hash_update ((const NMPlatformIP6Route *) obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, h); } +static void +_vt_cmd_plobj_hash_update_routing_rule (const NMPlatformObject *obj, NMHashState *h) +{ + return nm_platform_routing_rule_hash_update ((const NMPlatformRoutingRule *) obj, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL, h); +} + gboolean nmp_object_is_alive (const NMPObject *obj) { @@ -1574,13 +1602,14 @@ nmp_object_is_alive (const NMPObject *obj) static gboolean _vt_cmd_obj_is_alive_link (const NMPObject *obj) { - return obj->object.ifindex > 0 && (obj->_link.netlink.is_in_netlink || obj->_link.udev.device); + return NMP_OBJECT_CAST_LINK (obj)->ifindex > 0 + && (obj->_link.netlink.is_in_netlink || obj->_link.udev.device); } static gboolean _vt_cmd_obj_is_alive_ipx_address (const NMPObject *obj) { - return obj->object.ifindex > 0; + return NMP_OBJECT_CAST_IP_ADDRESS (obj)->ifindex > 0; } static gboolean @@ -1601,20 +1630,26 @@ _vt_cmd_obj_is_alive_ipx_route (const NMPObject *obj) * Instead we create a dead object, and nmp_cache_update_netlink() * will remove the old version of the update. **/ - return obj->object.ifindex > 0 + return NMP_OBJECT_CAST_IP_ROUTE (obj)->ifindex > 0 && !NM_FLAGS_HAS (obj->ip_route.r_rtm_flags, RTM_F_CLONED); } static gboolean +_vt_cmd_obj_is_alive_routing_rule (const NMPObject *obj) +{ + return NM_IN_SET (obj->routing_rule.addr_family, AF_INET, AF_INET6); +} + +static gboolean _vt_cmd_obj_is_alive_qdisc (const NMPObject *obj) { - return obj->object.ifindex > 0; + return NMP_OBJECT_CAST_QDISC (obj)->ifindex > 0; } static gboolean _vt_cmd_obj_is_alive_tfilter (const NMPObject *obj) { - return obj->object.ifindex > 0; + return NMP_OBJECT_CAST_TFILTER (obj)->ifindex > 0; } gboolean @@ -1672,6 +1707,12 @@ static const guint8 _supported_cache_ids_ipx_route[] = { 0, }; +static const guint8 _supported_cache_ids_routing_rules[] = { + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY, + 0, +}; + /*****************************************************************************/ static void @@ -1949,6 +1990,7 @@ nmp_lookup_init_obj_type (NMPLookup *lookup, case NMP_OBJECT_TYPE_IP6_ADDRESS: case NMP_OBJECT_TYPE_IP4_ROUTE: case NMP_OBJECT_TYPE_IP6_ROUTE: + case NMP_OBJECT_TYPE_ROUTING_RULE: case NMP_OBJECT_TYPE_QDISC: case NMP_OBJECT_TYPE_TFILTER: _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); @@ -1998,7 +2040,7 @@ nmp_lookup_init_object (NMPLookup *lookup, } o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); - o->object.ifindex = ifindex; + o->obj_with_ifindex.ifindex = ifindex; lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX; return _L (lookup); } @@ -2014,7 +2056,7 @@ nmp_lookup_init_route_default (NMPLookup *lookup, NMP_OBJECT_TYPE_IP6_ROUTE)); o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); - o->object.ifindex = 1; + o->ip_route.ifindex = 1; lookup->cache_id_type = NMP_CACHE_ID_TYPE_DEFAULT_ROUTES; return _L (lookup); } @@ -2062,9 +2104,9 @@ nmp_lookup_init_ip4_route_by_weak_id (NMPLookup *lookup, nm_assert (lookup); o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_IP4_ROUTE); - o->object.ifindex = 1; - o->ip_route.plen = plen; - o->ip_route.metric = metric; + o->ip4_route.ifindex = 1; + o->ip4_route.plen = plen; + o->ip4_route.metric = metric; if (network) o->ip4_route.network = network; o->ip4_route.tos = tos; @@ -2085,9 +2127,9 @@ nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup, nm_assert (lookup); o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_IP6_ROUTE); - o->object.ifindex = 1; - o->ip_route.plen = plen; - o->ip_route.metric = metric; + o->ip6_route.ifindex = 1; + o->ip6_route.plen = plen; + o->ip6_route.metric = metric; if (network) o->ip6_route.network = *network; if (src) @@ -2097,6 +2139,23 @@ nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup, return _L (lookup); } +const NMPLookup * +nmp_lookup_init_object_by_addr_family (NMPLookup *lookup, + NMPObjectType obj_type, + int addr_family) +{ + NMPObject *o; + + nm_assert (lookup); + nm_assert_addr_family (addr_family); + nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)); + + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); + NMP_OBJECT_CAST_ROUTING_RULE (o)->addr_family = addr_family; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY; + return _L (lookup); +} + /*****************************************************************************/ GArray * @@ -2920,15 +2979,25 @@ nmp_cache_update_link_master_connected (NMPCache *cache, /*****************************************************************************/ void -nmp_cache_dirty_set_all (NMPCache *cache, - const NMPLookup *lookup) +nmp_cache_dirty_set_all_main (NMPCache *cache, + const NMPLookup *lookup) { + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + nm_assert (cache); nm_assert (lookup); - nm_dedup_multi_index_dirty_set_head (cache->multi_idx, - _idx_type_get (cache, lookup->cache_id_type), - &lookup->selector_obj); + head_entry = nmp_cache_lookup (cache, lookup); + + nm_dedup_multi_iter_init (&iter, head_entry); + while (nm_dedup_multi_iter_next (&iter)) { + const NMDedupMultiEntry *main_entry; + + main_entry = nmp_cache_reresolve_main_entry (cache, iter.current, lookup); + + nm_dedup_multi_entry_set_dirty (main_entry, TRUE); + } } /*****************************************************************************/ @@ -3076,6 +3145,25 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip6_route, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp_full, }, + [NMP_OBJECT_TYPE_ROUTING_RULE - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_ROUTING_RULE, + .sizeof_data = sizeof (NMPObjectRoutingRule), + .sizeof_public = sizeof (NMPlatformRoutingRule), + .obj_type_name = "routing-rule", + .rtm_gettype = RTM_GETRULE, + .signal_type_id = NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, + .signal_type = NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED, + .supported_cache_ids = _supported_cache_ids_routing_rules, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_routing_rule, + .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_routing_rule, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_routing_rule, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_routing_rule, + .cmd_plobj_to_string_id = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_routing_rule_to_string, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_routing_rule_to_string, + .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_routing_rule, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_routing_rule_cmp_full, + }, [NMP_OBJECT_TYPE_QDISC - 1] = { .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_QDISC, diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 525e1440..3ce9db36 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -23,8 +23,8 @@ #include <netinet/in.h> -#include "nm-utils/nm-obj.h" -#include "nm-utils/nm-dedup-multi.h" +#include "nm-glib-aux/nm-obj.h" +#include "nm-glib-aux/nm-dedup-multi.h" #include "nm-platform.h" struct udev_device; @@ -174,6 +174,11 @@ typedef enum { /*< skip >*/ * cache-resync. */ NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + /* a filter for objects that track an explicit address family. + * + * Note that currently on NMPObjectRoutingRule is indexed by this filter. */ + NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY, + __NMP_CACHE_ID_TYPE_MAX, NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1, } NMPCacheIdType; @@ -325,6 +330,10 @@ typedef struct { } NMPObjectIP6Route; typedef struct { + NMPlatformRoutingRule _public; +} NMPObjectRoutingRule; + +typedef struct { NMPlatformQdisc _public; } NMPObjectQdisc; @@ -340,6 +349,8 @@ struct _NMPObject { union { NMPlatformObject object; + NMPlatformObjWithIfindex obj_with_ifindex; + NMPlatformLink link; NMPObjectLink _link; @@ -390,6 +401,9 @@ struct _NMPObject { NMPObjectIP4Route _ip4_route; NMPObjectIP6Route _ip6_route; + NMPlatformRoutingRule routing_rule; + NMPObjectRoutingRule _routing_rule; + NMPlatformQdisc qdisc; NMPObjectQdisc _qdisc; NMPlatformTfilter tfilter; @@ -397,6 +411,8 @@ struct _NMPObject { }; }; +/*****************************************************************************/ + static inline gboolean NMP_CLASS_IS_VALID (const NMPClass *klass) { @@ -405,6 +421,17 @@ NMP_CLASS_IS_VALID (const NMPClass *klass) && ((((char *) klass) - ((char *) _nmp_classes)) % (sizeof (_nmp_classes[0]))) == 0; } +static inline const NMPClass * +nmp_class_from_type (NMPObjectType obj_type) +{ + nm_assert (obj_type > 0); + nm_assert (obj_type <= G_N_ELEMENTS (_nmp_classes)); + nm_assert (_nmp_classes[obj_type - 1].obj_type == obj_type); + nm_assert (NMP_CLASS_IS_VALID (&_nmp_classes[obj_type - 1])); + + return &_nmp_classes[obj_type - 1]; +} + static inline NMPObject * NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj) { @@ -454,11 +481,65 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) return obj ? obj->_class->obj_type : NMP_OBJECT_TYPE_UNKNOWN; } +static inline gboolean +_NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMPObjectType obj_type) +{ + switch (obj_type) { + case NMP_OBJECT_TYPE_LINK: + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + + case NMP_OBJECT_TYPE_QDISC: + + case NMP_OBJECT_TYPE_TFILTER: + + case NMP_OBJECT_TYPE_LNK_GRE: + case NMP_OBJECT_TYPE_LNK_GRETAP: + case NMP_OBJECT_TYPE_LNK_INFINIBAND: + case NMP_OBJECT_TYPE_LNK_IP6TNL: + case NMP_OBJECT_TYPE_LNK_IP6GRE: + case NMP_OBJECT_TYPE_LNK_IP6GRETAP: + case NMP_OBJECT_TYPE_LNK_IPIP: + case NMP_OBJECT_TYPE_LNK_MACSEC: + case NMP_OBJECT_TYPE_LNK_MACVLAN: + case NMP_OBJECT_TYPE_LNK_MACVTAP: + case NMP_OBJECT_TYPE_LNK_SIT: + case NMP_OBJECT_TYPE_LNK_TUN: + case NMP_OBJECT_TYPE_LNK_VLAN: + case NMP_OBJECT_TYPE_LNK_VXLAN: + case NMP_OBJECT_TYPE_LNK_WIREGUARD: + return TRUE; + default: + nm_assert (nmp_class_from_type (obj_type)); + return FALSE; + } +} + +#define NMP_OBJECT_CAST_OBJECT(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert ( !_obj \ + || nmp_class_from_type (NMP_OBJECT_GET_TYPE (_obj)))); \ + _obj ? &NM_CONSTCAST (NMPObject, _obj)->object : NULL; \ + }) + +#define NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert ( !_obj \ + || _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMP_OBJECT_GET_TYPE (_obj))); \ + _obj ? &NM_CONSTCAST (NMPObject, _obj)->obj_with_ifindex : NULL; \ + }) + #define NMP_OBJECT_CAST_LINK(obj) \ ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_LINK); \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_LINK); \ _obj ? &NM_CONSTCAST (NMPObject, _obj)->link : NULL; \ }) @@ -482,7 +563,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP4_ADDRESS); \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP4_ADDRESS); \ _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip4_address : NULL; \ }) @@ -490,7 +571,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP6_ADDRESS); \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP6_ADDRESS); \ _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip6_address : NULL; \ }) @@ -514,7 +595,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP4_ROUTE); \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP4_ROUTE); \ _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip4_route : NULL; \ }) @@ -522,15 +603,23 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP6_ROUTE); \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP6_ROUTE); \ _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip6_route : NULL; \ }) +#define NMP_OBJECT_CAST_ROUTING_RULE(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_ROUTING_RULE); \ + _obj ? &NM_CONSTCAST (NMPObject, _obj)->routing_rule : NULL; \ + }) + #define NMP_OBJECT_CAST_QDISC(obj) \ ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_QDISC); \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_QDISC); \ _obj ? &NM_CONSTCAST (NMPObject, _obj)->qdisc : NULL; \ }) @@ -538,12 +627,10 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_TFILTER); \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_TFILTER); \ _obj ? &NM_CONSTCAST (NMPObject, _obj)->tfilter : NULL; \ }) -const NMPClass *nmp_class_from_type (NMPObjectType obj_type); - static inline const NMPObject * nmp_object_ref (const NMPObject *obj) { @@ -565,9 +652,11 @@ nmp_object_ref (const NMPObject *obj) static inline void nmp_object_unref (const NMPObject *obj) { - nm_assert (!obj || NMP_OBJECT_IS_VALID (obj)); + if (obj) { + nm_assert (NMP_OBJECT_IS_VALID (obj)); - nm_dedup_multi_obj_unref ((const NMDedupMultiObj *) obj); + nm_dedup_multi_obj_unref ((const NMDedupMultiObj *) obj); + } } #define nm_clear_nmp_object(ptr) \ @@ -695,6 +784,9 @@ const NMPLookup *nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup, guint32 metric, const struct in6_addr *src, guint8 src_plen); +const NMPLookup *nmp_lookup_init_object_by_addr_family (NMPLookup *lookup, + NMPObjectType obj_type, + int addr_family); GArray *nmp_cache_lookup_to_array (const NMDedupMultiHeadEntry *head_entry, NMPObjectType obj_type, @@ -784,8 +876,34 @@ NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, const NMPObject **out_obj_old, const NMPObject **out_obj_new); -void nmp_cache_dirty_set_all (NMPCache *cache, - const NMPLookup *lookup); +static inline const NMDedupMultiEntry * +nmp_cache_reresolve_main_entry (NMPCache *cache, + const NMDedupMultiEntry *entry, + const NMPLookup *lookup) +{ + const NMDedupMultiEntry *main_entry; + + nm_assert (cache); + nm_assert (entry); + nm_assert (lookup); + + if (lookup->cache_id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE) { + nm_assert (entry == nmp_cache_lookup_entry (cache, entry->obj)); + return entry; + } + + /* we only track the dirty flag for the OBJECT-TYPE index. That means, + * for other lookup types we need to check the dirty flag of the main-entry. */ + main_entry = nmp_cache_lookup_entry (cache, entry->obj); + + nm_assert (main_entry); + nm_assert (main_entry->obj == entry->obj); + + return main_entry; +} + +void nmp_cache_dirty_set_all_main (NMPCache *cache, + const NMPLookup *lookup); NMPCache *nmp_cache_new (NMDedupMultiIndex *multi_idx, gboolean use_udev); void nmp_cache_free (NMPCache *cache); diff --git a/src/platform/nmp-rules-manager.c b/src/platform/nmp-rules-manager.c new file mode 100644 index 00000000..970afcde --- /dev/null +++ b/src/platform/nmp-rules-manager.c @@ -0,0 +1,737 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#include "nm-default.h" + +#include "nmp-rules-manager.h" + +#include <linux/fib_rules.h> +#include <linux/rtnetlink.h> + +#include "nm-std-aux/c-list-util.h" +#include "nmp-object.h" + +/*****************************************************************************/ + +struct _NMPRulesManager { + NMPlatform *platform; + GHashTable *by_obj; + GHashTable *by_user_tag; + GHashTable *by_data; + guint ref_count; +}; + +/*****************************************************************************/ + +static void _rules_init (NMPRulesManager *self); + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_PLATFORM +#define _NMLOG_PREFIX_NAME "rules-manager" + +#define _NMLOG(level, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + \ + if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \ + _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \ + "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + _NMLOG_PREFIX_NAME \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END + +/*****************************************************************************/ + +static gboolean +NMP_IS_RULES_MANAGER (gpointer self) +{ + return self + && ((NMPRulesManager *) self)->ref_count > 0 + && NM_IS_PLATFORM (((NMPRulesManager *) self)->platform); +} + +#define _USER_TAG_LOG(user_tag) nm_hash_obfuscate_ptr (1240261787u, (user_tag)) + +/*****************************************************************************/ + +typedef struct { + const NMPObject *obj; + gconstpointer user_tag; + CList obj_lst; + CList user_tag_lst; + + /* track_priority_val zero is special: those are weakly tracked rules. + * That means: NetworkManager will restore them only if it removed them earlier. + * But it will not remove or add them otherwise. + * + * Otherwise, the track_priority_val goes together with track_priority_present. + * In case of one rule being tracked multile times (with different priorities), + * the one with higher priority wins. See _rules_obj_get_best_data(). + * Then, the winning present state either enforces that the rule is present + * or absent. + * + * If a rules is not tracked at all, it is ignored by NetworkManager. Assuming + * that it was added externally by the user. But unlike weakly tracked rules, + * NM will *not* restore such rules if NetworkManager themself removed them. */ + guint32 track_priority_val; + bool track_priority_present:1; + + bool dirty:1; +} RulesData; + +typedef enum { + CONFIG_STATE_NONE = 0, + CONFIG_STATE_ADDED_BY_US = 1, + CONFIG_STATE_REMOVED_BY_US = 2, +} ConfigState; + +typedef struct { + const NMPObject *obj; + CList obj_lst_head; + + /* indicates whether we configured/removed the rule (during sync()). We need that, so + * if the rule gets untracked, that we know to remove/restore it. + * + * This makes NMPRulesManager stateful (beyond the configuration that indicates + * which rules are tracked). + * After a restart, NetworkManager would no longer remember which rules were added + * by us. That would need to be fixed by persisting the state and reloading it after + * restart. */ + ConfigState config_state; +} RulesObjData; + +typedef struct { + gconstpointer user_tag; + CList user_tag_lst_head; +} RulesUserTagData; + +static void +_rules_data_assert (const RulesData *rules_data, gboolean linked) +{ + nm_assert (rules_data); + nm_assert (NMP_OBJECT_GET_TYPE (rules_data->obj) == NMP_OBJECT_TYPE_ROUTING_RULE); + nm_assert (nmp_object_is_visible (rules_data->obj)); + nm_assert (rules_data->user_tag); + nm_assert (!linked || !c_list_is_empty (&rules_data->obj_lst)); + nm_assert (!linked || !c_list_is_empty (&rules_data->user_tag_lst)); +} + +static guint +_rules_data_hash (gconstpointer data) +{ + const RulesData *rules_data = data; + NMHashState h; + + _rules_data_assert (rules_data, FALSE); + + nm_hash_init (&h, 269297543u); + nm_platform_routing_rule_hash_update (NMP_OBJECT_CAST_ROUTING_RULE (rules_data->obj), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, + &h); + nm_hash_update_val (&h, rules_data->user_tag); + return nm_hash_complete (&h); +} + +static gboolean +_rules_data_equal (gconstpointer data_a, gconstpointer data_b) +{ + const RulesData *rules_data_a = data_a; + const RulesData *rules_data_b = data_b; + + _rules_data_assert (rules_data_a, FALSE); + _rules_data_assert (rules_data_b, FALSE); + + return rules_data_a->user_tag == rules_data_b->user_tag + && (nm_platform_routing_rule_cmp (NMP_OBJECT_CAST_ROUTING_RULE (rules_data_a->obj), + NMP_OBJECT_CAST_ROUTING_RULE (rules_data_b->obj), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0); +} + +static void +_rules_data_destroy (gpointer data) +{ + RulesData *rules_data = data; + + _rules_data_assert (rules_data, FALSE); + + c_list_unlink_stale (&rules_data->obj_lst); + c_list_unlink_stale (&rules_data->user_tag_lst); + nmp_object_unref (rules_data->obj); + g_slice_free (RulesData, rules_data); +} + +static const RulesData * +_rules_obj_get_best_data (RulesObjData *obj_data) +{ + RulesData *rules_data; + const RulesData *rd_best = NULL; + + c_list_for_each_entry (rules_data, &obj_data->obj_lst_head, obj_lst) { + + _rules_data_assert (rules_data, TRUE); + + if (rd_best) { + if (rd_best->track_priority_val > rules_data->track_priority_val) + continue; + if (rd_best->track_priority_val == rules_data->track_priority_val) { + if ( rd_best->track_priority_present + || !rules_data->track_priority_present) { + /* if the priorities are identical, then "present" wins over + * "!present" (absent). */ + continue; + } + } + } + + rd_best = rules_data; + } + + return rd_best; +} + +static guint +_rules_obj_hash (gconstpointer data) +{ + const RulesObjData *obj_data = data; + NMHashState h; + + nm_hash_init (&h, 432817559u); + nm_platform_routing_rule_hash_update (NMP_OBJECT_CAST_ROUTING_RULE (obj_data->obj), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, + &h); + return nm_hash_complete (&h); +} + +static gboolean +_rules_obj_equal (gconstpointer data_a, gconstpointer data_b) +{ + const RulesObjData *obj_data_a = data_a; + const RulesObjData *obj_data_b = data_b; + + return (nm_platform_routing_rule_cmp (NMP_OBJECT_CAST_ROUTING_RULE (obj_data_a->obj), + NMP_OBJECT_CAST_ROUTING_RULE (obj_data_b->obj), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0); +} + +static void +_rules_obj_destroy (gpointer data) +{ + RulesObjData *obj_data = data; + + c_list_unlink_stale (&obj_data->obj_lst_head); + nmp_object_unref (obj_data->obj); + g_slice_free (RulesObjData, obj_data); +} + +static guint +_rules_user_tag_hash (gconstpointer data) +{ + const RulesUserTagData *user_tag_data = data; + + return nm_hash_val (644693447u, user_tag_data->user_tag); +} + +static gboolean +_rules_user_tag_equal (gconstpointer data_a, gconstpointer data_b) +{ + const RulesUserTagData *user_tag_data_a = data_a; + const RulesUserTagData *user_tag_data_b = data_b; + + return user_tag_data_a->user_tag == user_tag_data_b->user_tag; +} + +static void +_rules_user_tag_destroy (gpointer data) +{ + RulesUserTagData *user_tag_data = data; + + c_list_unlink_stale (&user_tag_data->user_tag_lst_head); + g_slice_free (RulesUserTagData, user_tag_data); +} + +static RulesData * +_rules_data_lookup (GHashTable *by_data, + const NMPObject *obj, + gconstpointer user_tag) +{ + RulesData rules_data_needle = { + .obj = obj, + .user_tag = user_tag, + }; + + return g_hash_table_lookup (by_data, &rules_data_needle); +} + +void +nmp_rules_manager_track (NMPRulesManager *self, + const NMPlatformRoutingRule *routing_rule, + gint32 track_priority, + gconstpointer user_tag) +{ + NMPObject obj_stack; + const NMPObject *p_obj_stack; + RulesData *rules_data; + RulesObjData *obj_data; + RulesUserTagData *user_tag_data; + gboolean changed = FALSE; + guint32 track_priority_val; + gboolean track_priority_present; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + g_return_if_fail (routing_rule); + g_return_if_fail (user_tag); + nm_assert (track_priority != G_MININT32); + + _rules_init (self); + + p_obj_stack = nmp_object_stackinit (&obj_stack, NMP_OBJECT_TYPE_ROUTING_RULE, routing_rule); + + nm_assert (nmp_object_is_visible (p_obj_stack)); + + if (track_priority >= 0) { + track_priority_val = track_priority; + track_priority_present = TRUE; + } else { + track_priority_val = -track_priority; + track_priority_present = FALSE; + } + + rules_data = _rules_data_lookup (self->by_data, p_obj_stack, user_tag); + + if (!rules_data) { + rules_data = g_slice_new (RulesData); + *rules_data = (RulesData) { + .obj = nm_dedup_multi_index_obj_intern (nm_platform_get_multi_idx (self->platform), + p_obj_stack), + .user_tag = user_tag, + .track_priority_val = track_priority_val, + .track_priority_present = track_priority_present, + .dirty = FALSE, + }; + g_hash_table_add (self->by_data, rules_data); + + obj_data = g_hash_table_lookup (self->by_obj, &rules_data->obj); + if (!obj_data) { + obj_data = g_slice_new (RulesObjData); + *obj_data = (RulesObjData) { + .obj = nmp_object_ref (rules_data->obj), + .obj_lst_head = C_LIST_INIT (obj_data->obj_lst_head), + .config_state = CONFIG_STATE_NONE, + }; + g_hash_table_add (self->by_obj, obj_data); + } + c_list_link_tail (&obj_data->obj_lst_head, &rules_data->obj_lst); + + user_tag_data = g_hash_table_lookup (self->by_user_tag, &rules_data->user_tag); + if (!user_tag_data) { + user_tag_data = g_slice_new (RulesUserTagData); + *user_tag_data = (RulesUserTagData) { + .user_tag = user_tag, + .user_tag_lst_head = C_LIST_INIT (user_tag_data->user_tag_lst_head), + }; + g_hash_table_add (self->by_user_tag, user_tag_data); + } + c_list_link_tail (&user_tag_data->user_tag_lst_head, &rules_data->user_tag_lst); + changed = TRUE; + } else { + rules_data->dirty = FALSE; + if ( rules_data->track_priority_val != track_priority_val + || rules_data->track_priority_present != track_priority_present) { + rules_data->track_priority_val = track_priority_val; + rules_data->track_priority_present = track_priority_present; + changed = TRUE; + } + } + + _rules_data_assert (rules_data, TRUE); + + if (changed) { + _LOGD ("routing-rule: track ["NM_HASH_OBFUSCATE_PTR_FMT",%s%u] \"%s\")", + _USER_TAG_LOG (rules_data->user_tag), + ( rules_data->track_priority_val == 0 + ? "" + : ( rules_data->track_priority_present + ? "+" + : "-")), + (guint) rules_data->track_priority_val, + nmp_object_to_string (rules_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + } +} + +static void +_rules_data_untrack (NMPRulesManager *self, + RulesData *rules_data, + gboolean remove_user_tag_data) +{ + RulesObjData *obj_data; + + nm_assert (NMP_IS_RULES_MANAGER (self)); + _rules_data_assert (rules_data, TRUE); + nm_assert (self->by_data); + nm_assert (g_hash_table_lookup (self->by_data, rules_data) == rules_data); + + _LOGD ("routing-rule: untrack ["NM_HASH_OBFUSCATE_PTR_FMT"] \"%s\"", + _USER_TAG_LOG (rules_data->user_tag), + nmp_object_to_string (rules_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + +#if NM_MORE_ASSERTS + { + RulesUserTagData *user_tag_data; + + user_tag_data = g_hash_table_lookup (self->by_user_tag, &rules_data->user_tag); + nm_assert (user_tag_data); + nm_assert (c_list_contains (&user_tag_data->user_tag_lst_head, &rules_data->user_tag_lst)); + } +#endif + + nm_assert (!c_list_is_empty (&rules_data->user_tag_lst)); + if ( remove_user_tag_data + && c_list_length_is (&rules_data->user_tag_lst, 1)) + g_hash_table_remove (self->by_user_tag, &rules_data->user_tag); + + obj_data = g_hash_table_lookup (self->by_obj, &rules_data->obj); + nm_assert (obj_data); + nm_assert (c_list_contains (&obj_data->obj_lst_head, &rules_data->obj_lst)); + nm_assert (obj_data == g_hash_table_lookup (self->by_obj, &rules_data->obj)); + + /* if obj_data is marked to be "added_by_us" or "removed_by_us", we need to keep this entry + * around for the next sync -- so that we can undo what we did earlier. */ + if ( obj_data->config_state == CONFIG_STATE_NONE + && c_list_length_is (&rules_data->obj_lst, 1)) + g_hash_table_remove (self->by_obj, &rules_data->obj); + + g_hash_table_remove (self->by_data, rules_data); +} + +void +nmp_rules_manager_untrack (NMPRulesManager *self, + const NMPlatformRoutingRule *routing_rule, + gconstpointer user_tag) +{ + NMPObject obj_stack; + const NMPObject *p_obj_stack; + RulesData *rules_data; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + g_return_if_fail (routing_rule); + g_return_if_fail (user_tag); + + _rules_init (self); + + p_obj_stack = nmp_object_stackinit (&obj_stack, NMP_OBJECT_TYPE_ROUTING_RULE, routing_rule); + + nm_assert (nmp_object_is_visible (p_obj_stack)); + + rules_data = _rules_data_lookup (self->by_data, p_obj_stack, user_tag); + if (rules_data) + _rules_data_untrack (self, rules_data, TRUE); +} + +void +nmp_rules_manager_set_dirty (NMPRulesManager *self, + gconstpointer user_tag) +{ + RulesData *rules_data; + RulesUserTagData *user_tag_data; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + g_return_if_fail (user_tag); + + if (!self->by_data) + return; + + user_tag_data = g_hash_table_lookup (self->by_user_tag, &user_tag); + if (!user_tag_data) + return; + + c_list_for_each_entry (rules_data, &user_tag_data->user_tag_lst_head, user_tag_lst) + rules_data->dirty = TRUE; +} + +void +nmp_rules_manager_untrack_all (NMPRulesManager *self, + gconstpointer user_tag, + gboolean all /* or only dirty */) +{ + RulesData *rules_data; + RulesData *rules_data_safe; + RulesUserTagData *user_tag_data; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + g_return_if_fail (user_tag); + + if (!self->by_data) + return; + + user_tag_data = g_hash_table_lookup (self->by_user_tag, &user_tag); + if (!user_tag_data) + return; + + c_list_for_each_entry_safe (rules_data, rules_data_safe, &user_tag_data->user_tag_lst_head, user_tag_lst) { + if ( all + || rules_data->dirty) + _rules_data_untrack (self, rules_data, FALSE); + } + if (c_list_is_empty (&user_tag_data->user_tag_lst_head)) + g_hash_table_remove (self->by_user_tag, user_tag_data); +} + +void +nmp_rules_manager_sync (NMPRulesManager *self, + gboolean keep_deleted_rules) +{ + const NMDedupMultiHeadEntry *pl_head_entry; + NMDedupMultiIter pl_iter; + const NMPObject *plobj; + gs_unref_ptrarray GPtrArray *rules_to_delete = NULL; + RulesObjData *obj_data; + GHashTableIter h_iter; + guint i; + const RulesData *rd_best; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + + if (!self->by_data) + return; + + _LOGD ("sync%s", keep_deleted_rules ? " (don't remove any rules)" : ""); + + pl_head_entry = nm_platform_lookup_obj_type (self->platform, NMP_OBJECT_TYPE_ROUTING_RULE); + if (pl_head_entry) { + nmp_cache_iter_for_each (&pl_iter, pl_head_entry, &plobj) { + obj_data = g_hash_table_lookup (self->by_obj, &plobj); + + if (!obj_data) { + /* this rule is not tracked. It was externally added, hence we + * ignore it. */ + continue; + } + + rd_best = _rules_obj_get_best_data (obj_data); + if (rd_best) { + if (rd_best->track_priority_present) + continue; + if (rd_best->track_priority_val == 0) { + if (obj_data->config_state != CONFIG_STATE_ADDED_BY_US) + continue; + obj_data->config_state = CONFIG_STATE_NONE; + } + } + + if (keep_deleted_rules) { + _LOGD ("forget/leak rule added by us: %s", nmp_object_to_string (plobj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + continue; + } + + if (!rules_to_delete) + rules_to_delete = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + + g_ptr_array_add (rules_to_delete, (gpointer) nmp_object_ref (plobj)); + + obj_data->config_state = CONFIG_STATE_REMOVED_BY_US; + } + } + + if (rules_to_delete) { + for (i = 0; i < rules_to_delete->len; i++) + nm_platform_object_delete (self->platform, rules_to_delete->pdata[i]); + } + + g_hash_table_iter_init (&h_iter, self->by_obj); + while (g_hash_table_iter_next (&h_iter, (gpointer *) &obj_data, NULL)) { + + rd_best = _rules_obj_get_best_data (obj_data); + + if (!rd_best) { + g_hash_table_iter_remove (&h_iter); + continue; + } + + if (!rd_best->track_priority_present) + continue; + if (rd_best->track_priority_val == 0) { + if (obj_data->config_state != CONFIG_STATE_REMOVED_BY_US) + continue; + obj_data->config_state = CONFIG_STATE_NONE; + } + + plobj = nm_platform_lookup_obj (self->platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj_data->obj); + if (plobj) + continue; + + obj_data->config_state = CONFIG_STATE_ADDED_BY_US; + nm_platform_routing_rule_add (self->platform, NMP_NLM_FLAG_ADD, NMP_OBJECT_CAST_ROUTING_RULE (obj_data->obj)); + } +} + +void +nmp_rules_manager_track_from_platform (NMPRulesManager *self, + NMPlatform *platform, + int addr_family, + gint32 tracking_priority, + gconstpointer user_tag) +{ + NMPLookup lookup; + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *o; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + + if (!platform) + platform = self->platform; + else + g_return_if_fail (NM_IS_PLATFORM (platform)); + + nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); + head_entry = nm_platform_lookup (platform, &lookup); + nmp_cache_iter_for_each (&iter, head_entry, &o) { + const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE (o); + + if ( addr_family != AF_UNSPEC + && rr->addr_family != addr_family) + continue; + + nmp_rules_manager_track (self, rr, tracking_priority, user_tag); + } +} + +/*****************************************************************************/ + +void +nmp_rules_manager_track_default (NMPRulesManager *self, + int addr_family, + gint32 track_priority, + gconstpointer user_tag) +{ + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + + nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + + /* track the default rules. See also `man ip-rule`. */ + + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) { + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET, + .priority = 0, + .table = RT_TABLE_LOCAL, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag); + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET, + .priority = 32766, + .table = RT_TABLE_MAIN, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag); + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET, + .priority = 32767, + .table = RT_TABLE_DEFAULT, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag); + } + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) { + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET6, + .priority = 0, + .table = RT_TABLE_LOCAL, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag); + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET6, + .priority = 32766, + .table = RT_TABLE_MAIN, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag); + } +} + +static void +_rules_init (NMPRulesManager *self) +{ + if (self->by_data) + return; + + self->by_data = g_hash_table_new_full (_rules_data_hash, _rules_data_equal, NULL, _rules_data_destroy); + self->by_obj = g_hash_table_new_full (_rules_obj_hash, _rules_obj_equal, NULL, _rules_obj_destroy); + self->by_user_tag = g_hash_table_new_full (_rules_user_tag_hash, _rules_user_tag_equal, NULL, _rules_user_tag_destroy); +} + +/*****************************************************************************/ + +NMPRulesManager * +nmp_rules_manager_new (NMPlatform *platform) +{ + NMPRulesManager *self; + + g_return_val_if_fail (NM_IS_PLATFORM (platform), NULL); + + self = g_slice_new (NMPRulesManager); + *self = (NMPRulesManager) { + .ref_count = 1, + .platform = g_object_ref (platform), + }; + return self; +} + +void +nmp_rules_manager_ref (NMPRulesManager *self) +{ + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + + self->ref_count++; +} + +void nmp_rules_manager_unref (NMPRulesManager *self) +{ + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + + if (--self->ref_count > 0) + return; + + if (self->by_data) { + g_hash_table_destroy (self->by_user_tag); + g_hash_table_destroy (self->by_obj); + g_hash_table_destroy (self->by_data); + } + g_object_unref (self->platform); + g_slice_free (NMPRulesManager, self); +} diff --git a/src/platform/nmp-rules-manager.h b/src/platform/nmp-rules-manager.h new file mode 100644 index 00000000..310c7971 --- /dev/null +++ b/src/platform/nmp-rules-manager.h @@ -0,0 +1,67 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifndef __NMP_RULES_MANAGER_H__ +#define __NMP_RULES_MANAGER_H__ + +#include "nm-platform.h" + +/*****************************************************************************/ + +typedef struct _NMPRulesManager NMPRulesManager; + +NMPRulesManager *nmp_rules_manager_new (NMPlatform *platform); + +void nmp_rules_manager_ref (NMPRulesManager *self); +void nmp_rules_manager_unref (NMPRulesManager *self); + +#define nm_auto_unref_rules_manager nm_auto (_nmp_rules_manager_unref) +NM_AUTO_DEFINE_FCN0 (NMPRulesManager *, _nmp_rules_manager_unref, nmp_rules_manager_unref) + +void nmp_rules_manager_track (NMPRulesManager *self, + const NMPlatformRoutingRule *routing_rule, + gint32 track_priority, + gconstpointer user_tag); + +void nmp_rules_manager_track_default (NMPRulesManager *self, + int addr_family, + gint32 track_priority, + gconstpointer user_tag); + +void nmp_rules_manager_track_from_platform (NMPRulesManager *self, + NMPlatform *platform, + int addr_family, + gint32 tracking_priority, + gconstpointer user_tag); + +void nmp_rules_manager_untrack (NMPRulesManager *self, + const NMPlatformRoutingRule *routing_rule, + gconstpointer user_tag); + +void nmp_rules_manager_set_dirty (NMPRulesManager *self, + gconstpointer user_tag); + +void nmp_rules_manager_untrack_all (NMPRulesManager *self, + gconstpointer user_tag, + gboolean all /* or only dirty */); + +void nmp_rules_manager_sync (NMPRulesManager *self, + gboolean keep_deleted_rules); + +/*****************************************************************************/ + +#endif /* __NMP_RULES_MANAGER_H__ */ diff --git a/src/platform/tests/monitor.c b/src/platform/tests/monitor.c index f0e3e6cf..fff5967b 100644 --- a/src/platform/tests/monitor.c +++ b/src/platform/tests/monitor.c @@ -77,8 +77,6 @@ main (int argc, char **argv) nm_linux_platform_setup (); - nm_platform_check_kernel_support (NM_PLATFORM_GET, ~((NMPlatformKernelSupportFlags) 0)); - if (global_opt.persist) g_main_loop_run (loop); diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index c510710b..7ebda9ff 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -900,7 +900,7 @@ _ip_address_add (NMPlatform *platform, } /* Let's wait until we see the address. */ - end_time = nm_utils_get_monotonic_timestamp_ms () + 250; + end_time = nm_utils_get_monotonic_timestamp_ms () + 500; do { if (external_command) diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index aa1f5460..e4eaec29 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -273,6 +273,58 @@ nmtstp_ip6_route_get_all (NMPlatform *platform, GArray *nmtstp_platform_ip4_address_get_all (NMPlatform *self, int ifindex); GArray *nmtstp_platform_ip6_address_get_all (NMPlatform *self, int ifindex); +/*****************************************************************************/ + +static inline gboolean +_nmtstp_platform_routing_rules_get_all_predicate (const NMPObject *obj, + gpointer user_data) +{ + int addr_family = GPOINTER_TO_INT (user_data); + + g_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_ROUTING_RULE); + + return addr_family == AF_UNSPEC + || NMP_OBJECT_CAST_ROUTING_RULE (obj)->addr_family == addr_family; +} + +static inline GPtrArray * +nmtstp_platform_routing_rules_get_all (NMPlatform *platform, int addr_family) +{ + NMPLookup lookup; + + g_assert (NM_IS_PLATFORM (platform)); + g_assert (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); + return nm_platform_lookup_clone (platform, + &lookup, + _nmtstp_platform_routing_rules_get_all_predicate, + GINT_TO_POINTER (addr_family)); +} + +static inline guint +nmtstp_platform_routing_rules_get_count (NMPlatform *platform, int addr_family) +{ + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *obj; + NMPLookup lookup; + guint n; + + g_assert (NM_IS_PLATFORM (platform)); + g_assert (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); + head_entry = nm_platform_lookup (platform, &lookup); + + n = 0; + nmp_cache_iter_for_each (&iter, head_entry, &obj) { + if (_nmtstp_platform_routing_rules_get_all_predicate (obj, GINT_TO_POINTER (addr_family))) + n++; + } + return n; +} + gboolean nmtstp_platform_ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric); gboolean nmtstp_platform_ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 71324301..a0650cc1 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -25,7 +25,7 @@ #include <sys/types.h> #include <linux/if_tun.h> -#include "nm-utils/nm-io-utils.h" +#include "nm-glib-aux/nm-io-utils.h" #include "platform/nmp-object.h" #include "platform/nmp-netns.h" #include "platform/nm-platform-utils.h" @@ -521,8 +521,7 @@ test_bridge_addr (void) plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); g_assert (plink); - if (nm_platform_check_kernel_support (NM_PLATFORM_GET, - NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) { + if (nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) { g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); @@ -2963,6 +2962,37 @@ test_sysctl_netns_switch (void) g_assert_cmpint (ifindex, ==, (gint32) nm_platform_sysctl_get_int32 (PL, NMP_SYSCTL_PATHID_NETDIR (dirfd, s ?: "<unknown>", "ifindex"), -1)); g_assert_cmpint (ifindex, ==, (gint32) nm_platform_sysctl_get_int32 (PL, NMP_SYSCTL_PATHID_ABSOLUTE (nm_sprintf_bufa (100, "/sys/class/net/%s/ifindex", IFNAME)), -1)); + /* also test that nm_platform_sysctl_get() sets errno to ENOENT for non-existing paths. */ + { + gint64 i64; + int errsv; + char *v; + + errno = ESRCH; + v = nm_platform_sysctl_get (PL, NMP_SYSCTL_PATHID_ABSOLUTE ("/sys/devices/virtual/net/not-existing/ifindex")); + errsv = errno; + g_assert (!v); + g_assert_cmpint (errsv, ==, ENOENT); + + errno = ESRCH; + i64 = nm_platform_sysctl_get_int_checked (PL, NMP_SYSCTL_PATHID_ABSOLUTE ("/sys/devices/virtual/net/not-existing/ifindex"), 10, 1, G_MAXINT, -1); + errsv = errno; + g_assert_cmpint (i64, ==, -1); + g_assert_cmpint (errsv, ==, ENOENT); + + errno = ESRCH; + v = nm_platform_sysctl_get (PL, NMP_SYSCTL_PATHID_ABSOLUTE ("/sys/devices/virtual/net/lo/not-existing")); + errsv = errno; + g_assert (!v); + g_assert_cmpint (errsv, ==, ENOENT); + + errno = ESRCH; + i64 = nm_platform_sysctl_get_int_checked (PL, NMP_SYSCTL_PATHID_ABSOLUTE ("/sys/devices/virtual/net/lo/not-existing"), 10, 1, G_MAXINT, -1); + errsv = errno; + g_assert_cmpint (i64, ==, -1); + g_assert_cmpint (errsv, ==, ENOENT); + } + /* accessing the path directly, only succeeds iff the current namespace happens to be the namespace * in which we created the link. */ { diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c index 12acf4a5..e2019e96 100644 --- a/src/platform/tests/test-nmp-object.c +++ b/src/platform/tests/test-nmp-object.c @@ -24,7 +24,7 @@ #include <linux/pkt_sched.h> #include "platform/nmp-object.h" -#include "nm-utils/nm-udev-utils.h" +#include "nm-udev-aux/nm-udev-utils.h" #include "nm-test-utils-core.h" @@ -223,7 +223,7 @@ _nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, const NMPObject **ou g_assert (cache); g_assert (NMP_OBJECT_IS_VALID (obj)); - obj_prev = nmp_cache_lookup_link (cache, obj->object.ifindex); + obj_prev = nmp_cache_lookup_link (cache, NMP_OBJECT_CAST_LINK (obj)->ifindex); obj_new_expected = nmp_object_clone (obj, FALSE); if (obj_prev && obj_prev->_link.udev.device) obj_new_expected->_link.udev.device = udev_device_ref (obj_prev->_link.udev.device); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 2619ec52..e6ffe1ae 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -20,9 +20,11 @@ #include "nm-default.h" #include <linux/rtnetlink.h> +#include <linux/fib_rules.h> #include "nm-core-utils.h" #include "platform/nm-platform-utils.h" +#include "platform/nmp-rules-manager.h" #include "test-common.h" @@ -873,6 +875,861 @@ again_find_idx: /*****************************************************************************/ +#define FRA_SUPPRESS_IFGROUP 13 +#define FRA_SUPPRESS_PREFIXLEN 14 +#define FRA_L3MDEV 19 +#define FRA_UID_RANGE 20 +#define FRA_PROTOCOL 21 +#define FRA_IP_PROTO 22 +#define FRA_SPORT_RANGE 23 +#define FRA_DPORT_RANGE 24 + +static const NMPObject * +_rule_find_by_priority (NMPlatform *platform, + guint32 priority) +{ + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *o; + const NMPObject *obj = NULL; + NMPLookup lookup; + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); + head_entry = nm_platform_lookup (platform, &lookup); + nmp_cache_iter_for_each (&iter, head_entry, &o) { + if (NMP_OBJECT_CAST_ROUTING_RULE (o)->priority != priority) + continue; + g_assert (!obj); + obj = o; + } + return obj; +} + +static const NMPObject * +_rule_check_kernel_support_one (NMPlatform *platform, + const NMPlatformRoutingRule *rr) +{ + nm_auto_nmpobj const NMPObject *obj = NULL; + int r; + + g_assert (!_rule_find_by_priority (platform, rr->priority)); + + r = nm_platform_routing_rule_add (platform, NMP_NLM_FLAG_ADD, rr); + g_assert_cmpint (r, ==, 0); + + obj = nmp_object_ref (_rule_find_by_priority (platform, rr->priority)); + g_assert (obj); + + r = nm_platform_object_delete (platform, obj); + g_assert_cmpint (r, ==, TRUE); + + g_assert (!_rule_find_by_priority (platform, rr->priority)); + + return g_steal_pointer (&obj); +} + +static gboolean +_rule_check_kernel_support (NMPlatform *platform, + int attribute) +{ + static int support[] = { + [FRA_SUPPRESS_IFGROUP] = 0, + [FRA_SUPPRESS_PREFIXLEN] = 0, + [FRA_L3MDEV] = 0, + [FRA_UID_RANGE] = 0, + [FRA_PROTOCOL] = 0, + [FRA_IP_PROTO] = 0, + [FRA_SPORT_RANGE] = 0, + [FRA_DPORT_RANGE] = 0, + }; + const guint32 PROBE_PRORITY = 12033; + gboolean sup; + int i; + + g_assert (NM_IS_PLATFORM (platform)); + + if (attribute == -1) { + for (i = 0; i < G_N_ELEMENTS (support); i++) { + if (support[i] < 0) { + /* indicate that some test was skipped. */ + return FALSE; + } + } + return TRUE; + } + + g_assert (attribute >= 0 && attribute < G_N_ELEMENTS (support)); + + if (support[attribute] != 0) + return support[attribute] >= 0; + + switch (attribute) { + case FRA_SUPPRESS_IFGROUP: { + nm_auto_nmpobj const NMPObject *obj = NULL; + const NMPlatformRoutingRule rr = { + .addr_family = AF_INET, + .priority = PROBE_PRORITY, + .suppress_ifgroup_inverse = ~((guint32) 1245), + }; + + obj = _rule_check_kernel_support_one (platform, &rr); + + sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->suppress_prefixlen_inverse == rr.suppress_ifgroup_inverse; + break; + } + case FRA_SUPPRESS_PREFIXLEN: { + nm_auto_nmpobj const NMPObject *obj = NULL; + const NMPlatformRoutingRule rr = { + .addr_family = AF_INET, + .priority = PROBE_PRORITY, + .suppress_prefixlen_inverse = ~((guint32) 1245), + }; + + obj = _rule_check_kernel_support_one (platform, &rr); + + sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->suppress_prefixlen_inverse == rr.suppress_prefixlen_inverse; + break; + } + case FRA_L3MDEV: { + nm_auto_nmpobj const NMPObject *obj = NULL; + const NMPlatformRoutingRule rr = { + .addr_family = AF_INET, + .priority = PROBE_PRORITY, + .l3mdev = TRUE, + }; + + obj = _rule_check_kernel_support_one (platform, &rr); + + sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->l3mdev != 0; + break; + } + case FRA_UID_RANGE: { + nm_auto_nmpobj const NMPObject *obj = NULL; + const NMPlatformRoutingRule rr = { + .addr_family = AF_INET, + .priority = PROBE_PRORITY, + .uid_range = { + .start = 0, + .end = 0, + }, + .uid_range_has = TRUE, + }; + + obj = _rule_check_kernel_support_one (platform, &rr); + + sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->uid_range_has; + break; + } + case FRA_PROTOCOL: { + nm_auto_nmpobj const NMPObject *obj = NULL; + const NMPlatformRoutingRule rr = { + .addr_family = AF_INET, + .priority = PROBE_PRORITY, + .protocol = 30, + }; + + obj = _rule_check_kernel_support_one (platform, &rr); + + sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->protocol == 30; + break; + } + case FRA_IP_PROTO: { + nm_auto_nmpobj const NMPObject *obj = NULL; + const NMPlatformRoutingRule rr = { + .addr_family = AF_INET, + .priority = PROBE_PRORITY, + .ip_proto = 30, + }; + + obj = _rule_check_kernel_support_one (platform, &rr); + + sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->ip_proto == 30; + break; + } + case FRA_SPORT_RANGE: + case FRA_DPORT_RANGE: + /* these were added at the same time as FRA_IP_PROTO. */ + sup = _rule_check_kernel_support (platform, FRA_IP_PROTO); + break; + default: + g_assert_not_reached (); + return FALSE; + } + + support[attribute] = sup ? 1 : -1; + + _LOGD ("kernel support for routing rule attribute #%d %s", attribute, sup ? "detected" : "not detected"); + return sup; +} + +static const NMPObject * +_platform_has_routing_rule (NMPlatform *platform, + const NMPObject *obj) +{ + const NMPObject *o; + + g_assert (NM_IS_PLATFORM (platform)); + g_assert (NMP_OBJECT_IS_VALID (obj)); + g_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_ROUTING_RULE); + + o = nm_platform_lookup_obj (platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj); + if (o) + g_assert (nm_platform_routing_rule_cmp (NMP_OBJECT_CAST_ROUTING_RULE (obj), NMP_OBJECT_CAST_ROUTING_RULE (o), NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0); + + return o; +} + +static guint32 +_rr_rand_choose_u32 (guint32 p) +{ + /* mostly, we just return zero. We want that each rule only has few + * fields set -- having most fields at zero. */ + if ((p % 10000u) < 7500u) + return 0; + + /* give 0xFFFFFFFFu extra probability. */ + if ((p % 10000u) < 8250u) + return 0xFFFFFFFFu; + + /* choose a small number. */ + if ((p % 10000u) < 9125u) + return (~p) % 10; + + /* finally, full random number. */ + return ~p; +} + +#define _rr_rand_choose_u8(p) ((guint8) _rr_rand_choose_u32 ((p))) + +static const NMPObject * +_rule_create_random (NMPlatform *platform) +{ + NMPObject *obj; + NMPlatformRoutingRule *rr; + guint32 p; + int addr_size; + guint i; + char saddr[NM_UTILS_INET_ADDRSTRLEN]; + static struct { + guint32 uid; + guint32 euid; + bool initialized; + } uids; + + if (G_UNLIKELY (!uids.initialized)) { + uids.uid = getuid (); + uids.euid = geteuid (); + uids.initialized = TRUE; + } + + obj = nmp_object_new (NMP_OBJECT_TYPE_ROUTING_RULE, NULL); + rr = NMP_OBJECT_CAST_ROUTING_RULE (obj); + + rr->addr_family = nmtst_rand_select (AF_INET, AF_INET6); + + addr_size = nm_utils_addr_family_to_size (rr->addr_family); + + p = nmtst_get_rand_int (); + if ((p % 1000u) < 50) + rr->priority = 10000 + ((~p) % 20u); + + p = nmtst_get_rand_int (); + if ((p % 1000u) < 40) + nm_sprintf_buf (rr->iifname, "t-iif-%u", (~p) % 20); + else if ((p % 1000u) < 80) + nm_sprintf_buf (rr->iifname, "%s", DEVICE_NAME); + + p = nmtst_get_rand_int (); + if ((p % 1000u) < 40) + nm_sprintf_buf (rr->oifname, "t-oif-%d", (~p) % 20); + else if ((p % 1000u) < 80) + nm_sprintf_buf (rr->oifname, "%s", DEVICE_NAME); + + for (i = 0; i < 2; i++) { + NMIPAddr *p_addr = i ? &rr->src : &rr->dst; + guint8 *p_len = i ? &rr->src_len : &rr->dst_len; + + p = nmtst_get_rand_int (); + if ((p % 1000u) < 100) { + /* if we set src_len/dst_len to zero, the src/dst is actually ignored. + * + * For fuzzying, still set the address. It shall have no further effect. + * */ + *p_len = (~p) % (addr_size * 8 + 1); + p = nmtst_get_rand_int (); + if ((p % 3u) == 0) { + if (rr->addr_family == AF_INET) + p_addr->addr4 = nmtst_inet4_from_string (nm_sprintf_buf (saddr, "192.192.5.%u", (~p) % 256u)); + else + p_addr->addr6 = *nmtst_inet6_from_string (nm_sprintf_buf (saddr, "1:2:3:4::f:%02x", (~p) % 256u)); + } else if ((p % 3u) == 1) + nmtst_rand_buf (NULL, p_addr, addr_size); + } + } + + p = nmtst_get_rand_int (); + if ((p % 1000u) < 50) + rr->tun_id = 10000 + ((~p) % 20); + +again_action: + p = nmtst_get_rand_int (); + if ((p % 1000u) < 500) + rr->action = FR_ACT_UNSPEC; + else if ((p % 1000u) < 750) + rr->action = (~p) % 12u; + else + rr->action = (~p) % 0x100u; + + rr->priority = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + + if ( rr->action == FR_ACT_GOTO + && rr->priority == G_MAXINT32) + goto again_action; + + p = nmtst_get_rand_int (); + if ((p % 10000u) < 100) + rr->goto_target = rr->priority + 1; + else + rr->goto_target = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + if ( rr->action == FR_ACT_GOTO + && rr->goto_target <= rr->priority) + goto again_action; + + p = nmtst_get_rand_int (); + if ((p % 1000u) < 25) { + if (_rule_check_kernel_support (platform, FRA_L3MDEV)) { + rr->l3mdev = TRUE; + rr->table = RT_TABLE_UNSPEC; + } + } + +again_table: + if (!rr->l3mdev) { + p = nmtst_get_rand_int (); + if ((p % 1000u) < 700) + rr->table = RT_TABLE_UNSPEC; + else if ((p % 1000u) < 850) + rr->table = RT_TABLE_MAIN; + else + rr->table = 10000 + ((~p) % 10); + if ( rr->action == FR_ACT_TO_TBL + && rr->table == RT_TABLE_UNSPEC) + goto again_table; + } + + rr->fwmark = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + rr->fwmask = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + + rr->flow = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + + if (_rule_check_kernel_support (platform, FRA_PROTOCOL)) + rr->protocol = _rr_rand_choose_u8 (nmtst_get_rand_int ()); + +#define IPTOS_TOS_MASK 0x1E + +again_tos: + rr->tos = _rr_rand_choose_u8 (nmtst_get_rand_int ()); + if ( rr->addr_family == AF_INET + && rr->tos & ~IPTOS_TOS_MASK) + goto again_tos; + + if (_rule_check_kernel_support (platform, FRA_IP_PROTO)) + rr->ip_proto = _rr_rand_choose_u8 (nmtst_get_rand_int ()); + + if (_rule_check_kernel_support (platform, FRA_SUPPRESS_PREFIXLEN)) + rr->suppress_prefixlen_inverse = ~_rr_rand_choose_u32 (nmtst_get_rand_int ()); + + if (_rule_check_kernel_support (platform, FRA_SUPPRESS_IFGROUP)) + rr->suppress_ifgroup_inverse = ~_rr_rand_choose_u32 (nmtst_get_rand_int ()); + + if (_rule_check_kernel_support (platform, FRA_UID_RANGE)) { + p = nmtst_get_rand_int (); + rr->uid_range_has = (p % 10000u) < 200; + } + +again_uid_range: + rr->uid_range.start = nmtst_rand_select (0u, uids.uid, uids.euid); + rr->uid_range.end = nmtst_rand_select (0u, uids.uid, uids.euid); + if (rr->uid_range_has) { + if (rr->uid_range.end < rr->uid_range.start) + NMTST_SWAP (rr->uid_range.start, rr->uid_range.end); + if ( rr->uid_range.start == ((guint32) -1) + || rr->uid_range.end == ((guint32) -1)) + goto again_uid_range; + } + + for (i = 0; i < 2; i++) { + NMFibRulePortRange *range = i ? &rr->sport_range : &rr->dport_range; + int attribute = i ? FRA_SPORT_RANGE : FRA_DPORT_RANGE; + + if (!_rule_check_kernel_support (platform, attribute)) + continue; + + p = nmtst_get_rand_int (); + if ((p % 10000u) < 300) { + while (range->start == 0) { + p = p ^ nmtst_get_rand_int (); + range->start = nmtst_rand_select (1u, 0xFFFEu, ((p ) % 0xFFFEu) + 1); + range->end = nmtst_rand_select (1u, 0xFFFEu, ((p >> 16) % 0xFFFEu) + 1, range->start); + if (range->end < range->start) + NMTST_SWAP (range->start, range->end); + } + } + } + + p = nmtst_get_rand_int () % 1000u; + if (p < 100) + rr->flags |= FIB_RULE_INVERT; + + return obj; +} + +static gboolean +_rule_fuzzy_equal (const NMPObject *obj, + const NMPObject *obj_comp, + int op_type) +{ + const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE (obj); + NMPlatformRoutingRule rr_co = *NMP_OBJECT_CAST_ROUTING_RULE (obj_comp); + + switch (op_type) { + case RTM_NEWRULE: + /* when adding rules with RTM_NEWRULE, kernel checks whether an existing + * rule already exists and may fail with EEXIST. This check has issues + * and reject legitimate rules (rh#1686075). + * + * Work around that. */ + if (rr->src_len == 0) + rr_co.src_len = 0; + if (rr->dst_len == 0) + rr_co.dst_len = 0; + if (rr->flow == 0) + rr_co.flow = 0; + if (rr->tos == 0) + rr_co.tos = 0; + rr_co.suppress_prefixlen_inverse = rr->suppress_prefixlen_inverse; + rr_co.suppress_ifgroup_inverse = rr->suppress_ifgroup_inverse; + if (!NM_FLAGS_HAS (rr->flags, FIB_RULE_INVERT)) + rr_co.flags &= ~((guint32) FIB_RULE_INVERT); + else + rr_co.flags |= ((guint32) FIB_RULE_INVERT); + break; + case RTM_DELRULE: + /* when deleting a rule with RTM_DELRULE, kernel tries to find the + * cadidate to delete. It might delete the wrong rule (rh#1685816). */ + if (rr->action == FR_ACT_UNSPEC) + rr_co.action = FR_ACT_UNSPEC; + if (rr->iifname[0] == '\0') + rr_co.iifname[0] = '\0'; + if (rr->oifname[0] == '\0') + rr_co.oifname[0] = '\0'; + if (rr->src_len == 0) + rr_co.src_len = 0; + if (rr->dst_len == 0) + rr_co.dst_len = 0; + if (rr->tun_id == 0) + rr_co.tun_id = 0; + if (rr->fwmark == 0) + rr_co.fwmark = 0; + if (rr->fwmask == 0) + rr_co.fwmask = 0; + if (rr->flow == 0) + rr_co.flow = 0; + if (rr->protocol == 0) + rr_co.protocol = 0; + if (rr->table == RT_TABLE_UNSPEC) + rr_co.table = RT_TABLE_UNSPEC; + if (rr->l3mdev == 0) + rr_co.l3mdev = 0; + if (rr->tos == 0) + rr_co.tos = 0; + if (rr->ip_proto == 0) + rr_co.ip_proto = 0; + if (rr->suppress_prefixlen_inverse == 0) + rr_co.suppress_prefixlen_inverse = 0; + if (rr->suppress_ifgroup_inverse == 0) + rr_co.suppress_ifgroup_inverse = 0; + if (!rr->uid_range_has) + rr_co.uid_range_has = FALSE; + if (rr->sport_range.start == 0 && rr->sport_range.end == 0) { + rr_co.sport_range.start = 0; + rr_co.sport_range.end = 0; + } + if (rr->dport_range.start == 0 && rr->dport_range.end == 0) { + rr_co.dport_range.start = 0; + rr_co.dport_range.end = 0; + } + if (!NM_FLAGS_HAS (rr->flags, FIB_RULE_INVERT)) + rr_co.flags &= ~((guint32) FIB_RULE_INVERT); + else + rr_co.flags |= ((guint32) FIB_RULE_INVERT); + break; + default: + g_assert_not_reached (); + break; + } + + return nm_platform_routing_rule_cmp (rr, &rr_co, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0; +} + +static void +test_rule (gconstpointer test_data) +{ + const int TEST_IDX = GPOINTER_TO_INT (test_data); + const gboolean TEST_SYNC = (TEST_IDX == 4); + gs_unref_ptrarray GPtrArray *objs = NULL; + gs_unref_ptrarray GPtrArray *objs_initial = NULL; + NMPlatform *platform = NM_PLATFORM_GET; + guint i, j, n; + int r; + gboolean had_an_issue_exist = FALSE; + + nm_platform_process_events (platform); + + objs_initial = nmtstp_platform_routing_rules_get_all (platform, AF_UNSPEC); + g_assert (objs_initial); + g_assert_cmpint (objs_initial->len, ==, 5); + + nmtstp_run_command_check ("ip rule add table 766"); + nm_platform_process_events (platform); + + for (i = 6; i > 0; i--) { + gs_unref_ptrarray GPtrArray *objs_extern = NULL; + const NMPObject *obj; + + objs_extern = nmtstp_platform_routing_rules_get_all (platform, AF_UNSPEC); + + g_assert (objs_extern); + g_assert_cmpint (objs_extern->len, ==, i); + + if (TEST_IDX != 1) + nmtst_rand_perm (NULL, objs_extern->pdata, NULL, sizeof (gpointer), objs_extern->len); + + obj = objs_extern->pdata[0]; + + r = nm_platform_object_delete (platform, obj); + g_assert_cmpint (r, ==, TRUE); + + g_assert (!_platform_has_routing_rule (platform, obj)); + } + + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, 0); + +#define RR(...) \ + nmp_object_new (NMP_OBJECT_TYPE_ROUTING_RULE, \ + (const NMPlatformObject *) &((NMPlatformRoutingRule) { \ + __VA_ARGS__ \ + })) + + objs = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET, + .priority = 10, + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET, + .priority = 400, + .action = FR_ACT_GOTO, + .goto_target = 10000, + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET6, + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET6, + .action = FR_ACT_TO_TBL, + .table = RT_TABLE_MAIN, + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET6, + .priority = 30, + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET6, + .priority = 50, + .iifname = "t-iif-1", + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET6, + .priority = 50, + .iifname = "t-oif-1", + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET, + .priority = 50, + .iifname = "t-oif-2", + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET, + .priority = 51, + .iifname = DEVICE_NAME, + )); + + if (TEST_IDX == 1) { + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET, + .table = 10000, + )); + } + + if (TEST_IDX != 1) { + nmtst_rand_perm (NULL, objs->pdata, NULL, sizeof (gpointer), objs->len); + g_ptr_array_set_size (objs, nmtst_get_rand_int () % (objs->len + 1)); + } + + n = (TEST_IDX != 1) ? nmtst_get_rand_int () % 50u : 0u; + for (i = 0; i < n; i++) { + nm_auto_nmpobj const NMPObject *o = NULL; + guint try = 0; + +again: + o = _rule_create_random (platform); + for (j = 0; j < objs->len; j++) { + if (nm_platform_routing_rule_cmp (NMP_OBJECT_CAST_ROUTING_RULE (o), + NMP_OBJECT_CAST_ROUTING_RULE (objs->pdata[j]), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0) { + try++; + g_assert (try < 200); + nm_clear_pointer (&o, nmp_object_unref); + goto again; + } + } + g_ptr_array_add (objs, (gpointer) g_steal_pointer (&o)); + } + + if (TEST_IDX != 1) + nmtst_rand_perm (NULL, objs->pdata, NULL, sizeof (gpointer), objs->len); + + if (TEST_SYNC) { + gs_unref_hashtable GHashTable *unique_priorities = g_hash_table_new (NULL, NULL); + nm_auto_unref_rules_manager NMPRulesManager *rules_manager = nmp_rules_manager_new (platform); + gs_unref_ptrarray GPtrArray *objs_sync = NULL; + gconstpointer USER_TAG_1 = &platform; + gconstpointer USER_TAG_2 = &unique_priorities; + + objs_sync = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + + /* ensure that priorities are unique. Otherwise it confuses the test, because + * kernel may wrongly be unable to add/delete routes based on a wrong match + * (rh#1685816, rh#1685816). */ + for (i = 0; i < objs->len; i++) { + const NMPObject *obj = objs->pdata[i]; + guint32 prio = NMP_OBJECT_CAST_ROUTING_RULE (obj)->priority; + + if ( !NM_IN_SET (prio, 0, 32766, 32767) + && !g_hash_table_contains (unique_priorities, GUINT_TO_POINTER (prio))) { + g_hash_table_add (unique_priorities, GUINT_TO_POINTER (prio)); + g_ptr_array_add (objs_sync, (gpointer) nmp_object_ref (obj)); + } + } + + for (i = 0; i < objs_sync->len; i++) { + nmp_rules_manager_track (rules_manager, + NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), + 1, + USER_TAG_1); + if (nmtst_get_rand_bool ()) { + /* this has no effect, because a negative priority (of same absolute value) + * has lower priority than the positive priority above. */ + nmp_rules_manager_track (rules_manager, + NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), + -1, + USER_TAG_2); + } + if (nmtst_get_rand_int () % objs_sync->len == 0) { + nmp_rules_manager_sync (rules_manager, FALSE); + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, i + 1); + } + } + + nmp_rules_manager_sync (rules_manager, FALSE); + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs_sync->len); + + for (i = 0; i < objs_sync->len; i++) { + switch (nmtst_get_rand_int () % 3) { + case 0: + nmp_rules_manager_untrack (rules_manager, + NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), + USER_TAG_1); + nmp_rules_manager_untrack (rules_manager, + NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), + USER_TAG_1); + break; + case 1: + nmp_rules_manager_track (rules_manager, + NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), + -1, + USER_TAG_1); + break; + case 2: + nmp_rules_manager_track (rules_manager, + NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), + -2, + USER_TAG_2); + break; + } + if (nmtst_get_rand_int () % objs_sync->len == 0) { + nmp_rules_manager_sync (rules_manager, FALSE); + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs_sync->len - i - 1); + } + } + + nmp_rules_manager_sync (rules_manager, FALSE); + + } else { + for (i = 0; i < objs->len;) { + const NMPObject *obj = objs->pdata[i]; + + for (j = 0; j < objs->len; j++) + g_assert ((j < i) == (!!_platform_has_routing_rule (platform, objs->pdata[j]))); + + r = nm_platform_routing_rule_add (platform, NMP_NLM_FLAG_ADD, NMP_OBJECT_CAST_ROUTING_RULE (obj)); + + if (r == -EEXIST) { + g_assert (!_platform_has_routing_rule (platform, obj)); + /* this should not happen, but there are bugs in kernel (rh#1686075). */ + for (j = 0; j < i; j++) { + const NMPObject *obj2 = objs->pdata[j]; + + g_assert (_platform_has_routing_rule (platform, obj2)); + + if (_rule_fuzzy_equal (obj, obj2, RTM_NEWRULE)) { + r = 0; + break; + } + } + if (r == 0) { + /* OK, the rule is shadowed by another rule, and kernel does not allow + * us to add this one (rh#1686075). Drop this from the test. */ + g_ptr_array_remove_index (objs, i); + had_an_issue_exist = TRUE; + continue; + } + } + + if (r != 0) { + NMPLookup lookup; + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *o; + + g_print (">>> failing... errno=%d, rule=%s\n", r, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); + head_entry = nm_platform_lookup (platform, &lookup); + nmp_cache_iter_for_each (&iter, head_entry, &o) { + char ch = ' '; + + if ( NMP_OBJECT_CAST_ROUTING_RULE (o)->addr_family == NMP_OBJECT_CAST_ROUTING_RULE (obj)->addr_family + && NMP_OBJECT_CAST_ROUTING_RULE (o)->priority == NMP_OBJECT_CAST_ROUTING_RULE (obj)->priority) + ch = '*'; + g_print (">>> existing rule: %c %s\n", ch, nmp_object_to_string (o, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + } + + nmtstp_run_command_check ("ip rule"); + nmtstp_run_command_check ("ip -6 rule"); + g_assert_cmpint (r, ==, 0); + } + + g_assert (_platform_has_routing_rule (platform, obj)); + + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, i + 1); + + i++; + } + + if (TEST_IDX != 1) + nmtst_rand_perm (NULL, objs->pdata, NULL, sizeof (gpointer), objs->len); + + if (_LOGD_ENABLED ()) { + nmtstp_run_command_check ("ip rule"); + nmtstp_run_command_check ("ip -6 rule"); + } + + for (i = 0; i < objs->len; i++) { + const NMPObject *obj = objs->pdata[i]; + const NMPObject *obj2; + + for (j = 0; j < objs->len; j++) + g_assert ((j < i) == (!_platform_has_routing_rule (platform, objs->pdata[j]))); + + g_assert (_platform_has_routing_rule (platform, obj)); + + r = nm_platform_object_delete (platform, obj); + g_assert_cmpint (r, ==, TRUE); + + obj2 = _platform_has_routing_rule (platform, obj); + + if (obj2) { + guint k; + + /* When deleting a rule, kernel does a fuzzy match, ignoring for example: + * - action, if it is FR_ACT_UNSPEC + * - iifname,oifname if it is unspecified + * rh#1685816 + * + * That means, we may have deleted the wrong rule. Which one? */ + k = i; + for (j = i + 1; j < objs->len; j++) { + if (!_platform_has_routing_rule (platform, objs->pdata[j])) { + g_assert_cmpint (k, ==, i); + k = j; + } + } + g_assert_cmpint (k, >, i); + + if (!_rule_fuzzy_equal (obj, objs->pdata[k], RTM_DELRULE)) { + g_print (">>> failing...\n"); + g_print (">>> no fuzzy match between: %s\n", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + g_print (">>> and: %s\n", nmp_object_to_string (objs->pdata[k], NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + g_assert_not_reached (); + } + + objs->pdata[i] = objs->pdata[k]; + objs->pdata[k] = (gpointer) obj; + obj2 = NULL; + } + + g_assert (!obj2); + + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs->len -i - 1); + } + } + + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, 0); + + for (i = 0; i < objs_initial->len; i++) { + const NMPObject *obj = objs_initial->pdata[i]; + + for (j = 0; j < objs_initial->len; j++) + g_assert ((j < i) == (!!_platform_has_routing_rule (platform, objs_initial->pdata[j]))); + + r = nm_platform_routing_rule_add (platform, NMP_NLM_FLAG_ADD, NMP_OBJECT_CAST_ROUTING_RULE (obj)); + g_assert_cmpint (r, ==, 0); + } + for (j = 0; j < objs_initial->len; j++) + g_assert (_platform_has_routing_rule (platform, objs_initial->pdata[j])); + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs_initial->len); + + /* the tests passed as good as we could (as good as we implemented workarounds for them). + * Still, with this kernel, not all features were fully tested. Mark the test as skipped. */ + if (had_an_issue_exist) + g_test_skip ("adding a rule failed with EEXIST although it should not (rh#1686075)"); + else if (!_rule_check_kernel_support (platform, -1)) + g_test_skip ("some kernel features were not available and skipped for the test"); +} + +/*****************************************************************************/ + NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; void @@ -902,4 +1759,11 @@ _nmtstp_setup_tests (void) add_test_func ("/route/ip6_route_get", test_ip6_route_get); add_test_func ("/route/ip4_zero_gateway", test_ip4_zero_gateway); } + + if (nmtstp_is_root_test ()) { + add_test_func_data ("/route/rule/1", test_rule, GINT_TO_POINTER (1)); + add_test_func_data ("/route/rule/2", test_rule, GINT_TO_POINTER (2)); + add_test_func_data ("/route/rule/3", test_rule, GINT_TO_POINTER (3)); + add_test_func_data ("/route/rule/4", test_rule, GINT_TO_POINTER (4)); + } } diff --git a/src/ppp/meson.build b/src/ppp/meson.build index a0f2df50..04c539e3 100644 --- a/src/ppp/meson.build +++ b/src/ppp/meson.build @@ -2,7 +2,7 @@ name = 'nm-pppd-plugin' deps = [ dl_dep, - nm_core_dep, + libnm_core_dep, ] nm_pppd_plugin = shared_module( diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c index 8924c39f..814dee85 100644 --- a/src/settings/nm-agent-manager.c +++ b/src/settings/nm-agent-manager.c @@ -24,7 +24,7 @@ #include <pwd.h> -#include "nm-common-macros.h" +#include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-dbus-interface.h" #include "nm-secret-agent.h" #include "nm-auth-utils.h" diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 8d1f9583..3fdaa598 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -25,7 +25,7 @@ #include "c-list/src/c-list.h" -#include "nm-common-macros.h" +#include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-config.h" #include "nm-config-data.h" #include "nm-dbus-interface.h" diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index fd1d316a..8e18a33e 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -36,7 +36,7 @@ #include <selinux/selinux.h> #endif -#include "nm-common-macros.h" +#include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-dbus-interface.h" #include "nm-connection.h" #include "nm-setting-8021x.h" @@ -60,7 +60,7 @@ #include "nm-utils.h" #include "nm-core-internal.h" -#include "nm-utils/nm-c-list.h" +#include "nm-glib-aux/nm-c-list.h" #include "nm-dbus-object.h" #include "devices/nm-device-ethernet.h" #include "nm-settings-connection.h" @@ -1138,7 +1138,7 @@ pk_add_cb (NMAuthChain *chain, NMAuthCallResult result; GError *error = NULL; NMConnection *connection = NULL; - NMSettingsConnection *added = NULL; + gs_unref_object NMSettingsConnection *added = NULL; NMSettingsAddCallback callback; gpointer callback_data; NMAuthSubject *subject; @@ -1169,6 +1169,12 @@ pk_add_cb (NMAuthChain *chain, save_to_disk = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "save-to-disk")); added = nm_settings_add_connection (self, connection, save_to_disk, &error); + + /* The callback may remove the connection from the settings manager (e.g. + * because it's found to be incompatible with the device on AddAndActivate). + * But we need to keep it alive for a bit longer, precisely to check wehther + * it's still known to the setting manager. */ + g_object_ref (added); } callback = nm_auth_chain_get_data (chain, "callback"); diff --git a/src/settings/plugins/README b/src/settings/plugins/README deleted file mode 100644 index 307d0bb1..00000000 --- a/src/settings/plugins/README +++ /dev/null @@ -1,33 +0,0 @@ -Plugins generally have three components: - -1) plugin object: manages the individual "connections", which are - just objects wrapped around on-disk config data. The plugin handles requests - to add new connections via the NM D-Bus API, and also watches config - directories for changes to configuration data. Plugins implement the - NMSettingsPlugin interface. See plugin.c. - -2) "connections": subclasses of NMSettingsConnection. They handle updates to - configuration data, deletion, etc. See NMKeyfileConnection. - -3) reader/writer code: typically a separate static library that gets linked - into the main plugin shared object, so they can be unit tested separately - from the plugin. This code should read config data from disk and create - an NMConnection from it, and be capable of taking an NMConnection and writing - out appropriate configuration data to disk. - -NM will first call the "factory" function that every module must provide, which -is nm_settings_plugin_factory(). That function creates and returns a singleton -instance of the plugin's main object, which implements NMSettingsPlugin. -That interface is implemented via the object definition in G_DEFINE_TYPE_EXTENDED -in plugin.c, which registers the interface setup function -settings_plugin_interface_init(), which when called actually sets up the vtables -for the functions defined by NMSettingsPluginInterface. Thus there are two -entry points into the plugin: nm_settings_plugin_factory() and -the NMSettingsPluginInterface methods. - -The plugin also emits various signals (defined by NMSettingsPluginInterface) -which NetworkManager listens for. These include notifications of new -connections if they were created via changes to the on-disk files. The -"connection" objects can also emit signals (defined by the NMSettingsConnection -and NMConnection superclasses) when the connections' backing storage gets -changed or deleted. diff --git a/src/settings/plugins/ibft/nms-ibft-plugin.c b/src/settings/plugins/ibft/nms-ibft-plugin.c index 00b25068..47051995 100644 --- a/src/settings/plugins/ibft/nms-ibft-plugin.c +++ b/src/settings/plugins/ibft/nms-ibft-plugin.c @@ -64,31 +64,30 @@ static void read_connections (NMSIbftPlugin *self) { NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - GSList *blocks = NULL, *iter; - GError *error = NULL; + nm_auto_free_ibft_blocks GSList *blocks = NULL; + GSList *iter; + gs_free_error GError *error = NULL; NMSIbftConnection *connection; if (!nms_ibft_reader_load_blocks ("/sbin/iscsiadm", &blocks, &error)) { nm_log_dbg (LOGD_SETTINGS, "ibft: failed to read iscsiadm records: %s", error->message); - g_error_free (error); return; } for (iter = blocks; iter; iter = iter->next) { connection = nms_ibft_connection_new (iter->data, &error); - if (connection) { - nm_log_info (LOGD_SETTINGS, "ibft: read connection '%s'", - nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (connection))); - g_hash_table_insert (priv->connections, - g_strdup (nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection))), - connection); - } else { + if (!connection) { nm_log_warn (LOGD_SETTINGS, "ibft: failed to read iscsiadm record: %s", error->message); g_clear_error (&error); + continue; } - } - g_slist_free_full (blocks, (GDestroyNotify) g_ptr_array_unref); + nm_log_info (LOGD_SETTINGS, "ibft: read connection '%s'", + nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (connection))); + g_hash_table_insert (priv->connections, + g_strdup (nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection))), + connection); + } } static GSList * diff --git a/src/settings/plugins/ibft/nms-ibft-reader.c b/src/settings/plugins/ibft/nms-ibft-reader.c index c6c14376..fde30383 100644 --- a/src/settings/plugins/ibft/nms-ibft-reader.c +++ b/src/settings/plugins/ibft/nms-ibft-reader.c @@ -46,8 +46,7 @@ remove_most_whitespace (const char *src) char *s_new, *s2; const char *svalue; - while (*src && g_ascii_isspace (*src)) - src++; + src = nm_str_skip_leading_spaces (src); svalue = strchr (src, '='); if (!svalue || svalue == src) @@ -94,24 +93,25 @@ nms_ibft_reader_load_blocks (const char *iscsiadm_path, { const char *argv[4] = { iscsiadm_path, "-m", "fw", NULL }; const char *envp[1] = { NULL }; - GSList *blocks = NULL; - char *out = NULL, *err = NULL; - int status = 0; - char **lines = NULL, **iter; + nm_auto_free_ibft_blocks GSList *blocks = NULL; + gs_free char *out = NULL; + gs_free char *err = NULL; + gs_free const char **lines = NULL; GPtrArray *block_lines = NULL; - gboolean success = FALSE; + gsize i; + int status = 0; g_return_val_if_fail (iscsiadm_path != NULL, FALSE); g_return_val_if_fail (out_blocks != NULL && *out_blocks == NULL, FALSE); if (!g_spawn_sync ("/", (char **) argv, (char **) envp, 0, NULL, NULL, &out, &err, &status, error)) - goto done; + return FALSE; if (!WIFEXITED (status)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "iBFT: %s exited abnormally.", iscsiadm_path); - goto done; + return FALSE; } if (WEXITSTATUS (status) != 0) { @@ -127,59 +127,47 @@ nms_ibft_reader_load_blocks (const char *iscsiadm_path, g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "iBFT: %s exited with error %d. Message: '%s'", iscsiadm_path, WEXITSTATUS (status), err ?: "(none)"); - goto done; + return FALSE; } nm_log_dbg (LOGD_SETTINGS, "iBFT records:\n%s", out); - lines = g_strsplit_set (out, "\n\r", -1); - for (iter = lines; iter && *iter; iter++) { - if (!*iter[0]) - continue; + lines = nm_utils_strsplit_set (out, "\n\r"); + for (i = 0; lines && lines[i]; i++) { + const char *ss = lines[i]; - if (!g_ascii_strncasecmp (*iter, TAG_BEGIN, NM_STRLEN (TAG_BEGIN))) { + if (!g_ascii_strncasecmp (ss, TAG_BEGIN, NM_STRLEN (TAG_BEGIN))) { if (block_lines) { PARSE_WARNING ("malformed iscsiadm record: missing END RECORD."); - g_ptr_array_unref (block_lines); + nm_clear_pointer (&block_lines, g_ptr_array_unref); } /* Start new record */ block_lines = g_ptr_array_new_full (15, g_free); - } else if (!g_ascii_strncasecmp (*iter, TAG_END, NM_STRLEN (TAG_END))) { + } else if (!g_ascii_strncasecmp (ss, TAG_END, NM_STRLEN (TAG_END))) { if (block_lines) { if (block_lines->len) - blocks = g_slist_prepend (blocks, block_lines); + blocks = g_slist_prepend (blocks, g_steal_pointer (&block_lines)); else - g_ptr_array_unref (block_lines); - block_lines = NULL; + g_ptr_array_unref (g_steal_pointer (&block_lines)); } } else if (block_lines) { - char *s = remove_most_whitespace (*iter); + char *s = remove_most_whitespace (ss); - if (s) + if (!s) { + PARSE_WARNING ("malformed iscsiadm record: no = in '%s'.", ss); + nm_clear_pointer (&block_lines, g_ptr_array_unref); + } else g_ptr_array_add (block_lines, s); - else { - PARSE_WARNING ("malformed iscsiadm record: no = in '%s'.", *iter); - g_clear_pointer (&block_lines, g_ptr_array_unref); - } } } if (block_lines) { PARSE_WARNING ("malformed iscsiadm record: missing # END RECORD."); - g_clear_pointer (&block_lines, g_ptr_array_unref); + nm_clear_pointer (&block_lines, g_ptr_array_unref); } - success = TRUE; - -done: - if (lines) - g_strfreev (lines); - g_free (out); - g_free (err); - if (success) - *out_blocks = blocks; - else - g_slist_free_full (blocks, (GDestroyNotify) g_ptr_array_unref); - return success; + + *out_blocks = g_steal_pointer (&blocks); + return TRUE; } #define ISCSI_HWADDR_TAG "iface.hwaddress" diff --git a/src/settings/plugins/ibft/nms-ibft-reader.h b/src/settings/plugins/ibft/nms-ibft-reader.h index 27500cc5..baa81e99 100644 --- a/src/settings/plugins/ibft/nms-ibft-reader.h +++ b/src/settings/plugins/ibft/nms-ibft-reader.h @@ -23,6 +23,14 @@ #include "nm-connection.h" +static inline void +_nm_auto_free_ibft_blocks (GSList **p_blocks) +{ + if (*p_blocks) + g_slist_free_full (*p_blocks, (GDestroyNotify) g_ptr_array_unref); +} +#define nm_auto_free_ibft_blocks nm_auto (_nm_auto_free_ibft_blocks) + gboolean nms_ibft_reader_load_blocks (const char *iscsiadm_path, GSList **out_blocks, GError **error); diff --git a/src/settings/plugins/ibft/tests/test-ibft.c b/src/settings/plugins/ibft/tests/test-ibft.c index 4c45f574..f5b584a1 100644 --- a/src/settings/plugins/ibft/tests/test-ibft.c +++ b/src/settings/plugins/ibft/tests/test-ibft.c @@ -40,16 +40,16 @@ static GPtrArray * read_block (const char *iscsiadm_path, const char *expected_mac) { - GSList *blocks = NULL, *iter; + nm_auto_free_ibft_blocks GSList *blocks = NULL; + GSList *iter; GPtrArray *block = NULL; GError *error = NULL; gboolean success; success = nms_ibft_reader_load_blocks (iscsiadm_path, &blocks, &error); - g_assert_no_error (error); - g_assert (success); - g_assert (blocks); + nmtst_assert_success (success, error); + g_assert (blocks); for (iter = blocks; iter; iter = iter->next) { const char *s_hwaddr = NULL; @@ -63,7 +63,6 @@ read_block (const char *iscsiadm_path, const char *expected_mac) } g_assert (block); - g_slist_free_full (blocks, (GDestroyNotify) g_ptr_array_unref); return block; } @@ -176,7 +175,7 @@ static void test_read_ibft_malformed (gconstpointer user_data) { const char *iscsiadm_path = user_data; - GSList *blocks = NULL; + nm_auto_free_ibft_blocks GSList *blocks = NULL; GError *error = NULL; gboolean success; @@ -185,9 +184,9 @@ test_read_ibft_malformed (gconstpointer user_data) NMTST_EXPECT_NM_WARN ("*malformed iscsiadm record*"); success = nms_ibft_reader_load_blocks (iscsiadm_path, &blocks, &error); - g_assert_no_error (error); - g_assert (success); - g_assert (blocks == NULL); + nmtst_assert_success (success, error); + + g_assert (!blocks); g_test_assert_expected_messages (); } diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c index 89272edb..5160dbf0 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c @@ -30,7 +30,7 @@ #include <sys/stat.h> #include <gmodule.h> -#include "nm-dbus-compat.h" +#include "nm-std-aux/nm-dbus-compat.h" #include "nm-setting-connection.h" #include "settings/nm-settings-plugin.h" #include "nm-config.h" diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 7c1db225..e5423b18 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -31,7 +31,7 @@ #include <sys/ioctl.h> #include <unistd.h> -#include "nm-utils/nm-secret-utils.h" +#include "nm-glib-aux/nm-secret-utils.h" #include "nm-connection.h" #include "nm-dbus-interface.h" #include "nm-setting-connection.h" @@ -53,7 +53,7 @@ #include "nm-setting-generic.h" #include "nm-core-internal.h" #include "nm-utils.h" -#include "nm-ethtool-utils.h" +#include "nm-libnm-core-intern/nm-ethtool-utils.h" #include "platform/nm-platform.h" #include "NetworkManagerUtils.h" @@ -82,7 +82,8 @@ static char * get_full_file_path (const char *ifcfg_path, const char *file_path) { const char *base = file_path; - char *p, *ret, *dirname; + gs_free char *dirname = NULL; + char *p; g_return_val_if_fail (ifcfg_path != NULL, NULL); g_return_val_if_fail (file_path != NULL, NULL); @@ -95,9 +96,7 @@ get_full_file_path (const char *ifcfg_path, const char *file_path) base = p + 1; dirname = g_path_get_dirname (ifcfg_path); - ret = g_build_path ("/", dirname, base, NULL); - g_free (dirname); - return ret; + return g_build_path ("/", dirname, base, NULL); } /*****************************************************************************/ @@ -207,7 +206,7 @@ _cert_get_cert_bytes (const char *ifcfg_path, { gs_free char *path = NULL; - if (g_str_has_prefix (value, "pkcs11:")) + if (NM_STR_HAS_PREFIX (value, "pkcs11:")) return _nm_setting_802_1x_cert_value_to_bytes (NM_SETTING_802_1X_CK_SCHEME_PKCS11, (guint8 *) value, -1, error); path = get_full_file_path (ifcfg_path, value); @@ -379,7 +378,7 @@ make_connection_setting (const char *file, NMSettingConnection *s_con; NMSettingConnectionLldp lldp; const char *ifcfg_name = NULL; - char *new_id; + gs_free char *new_id = NULL; const char *uuid; gs_free char *uuid_free = NULL; gs_free char *value = NULL; @@ -396,7 +395,6 @@ make_connection_setting (const char *file, new_id = make_connection_name (ifcfg, ifcfg_name, suggested, prefix); g_object_set (s_con, NM_SETTING_CONNECTION_ID, new_id, NULL); - g_free (new_id); /* Try for a UUID key before falling back to hashing the file name */ uuid = svGetValueStr (ifcfg, "UUID", &uuid_free); @@ -457,7 +455,7 @@ make_connection_setting (const char *file, if (v) { gs_free const char **items = NULL; - items = nm_utils_strsplit_set (v, " ", FALSE); + items = nm_utils_strsplit_set (v, " "); for (iter = items; iter && *iter; iter++) { if (!nm_setting_connection_add_permission (s_con, "user", *iter, NULL)) PARSE_WARNING ("invalid USERS item '%s'", *iter); @@ -473,7 +471,7 @@ make_connection_setting (const char *file, if (v) { gs_free const char **items = NULL; - items = nm_utils_strsplit_set (v, " \t", FALSE); + items = nm_utils_strsplit_set (v, " \t"); for (iter = items; iter && *iter; iter++) { if (!nm_setting_connection_add_secondary (s_con, *iter)) PARSE_WARNING ("secondary connection UUID '%s' already added", *iter); @@ -562,7 +560,6 @@ make_connection_setting (const char *file, return NM_SETTING (s_con); } -/* Returns TRUE on missing address or valid address */ static gboolean read_ip4_address (shvarFile *ifcfg, const char *tag, @@ -572,7 +569,7 @@ read_ip4_address (shvarFile *ifcfg, { gs_free char *value_to_free = NULL; const char *value; - guint32 a; + in_addr_t a; nm_assert (ifcfg); nm_assert (tag); @@ -820,7 +817,7 @@ parse_route_line (const char *line, NMIPRoute **out_route, GError **error) { - nm_auto_ip_route_unref NMIPRoute *route = NULL; + nm_auto_unref_ip_route NMIPRoute *route = NULL; gs_free const char **words_free = NULL; const char *const*words; const char *s; @@ -889,7 +886,7 @@ parse_route_line (const char *line, * Maybe later we want to support some form of quotation here. * Which of course, would be incompatible with initscripts. */ - words_free = nm_utils_strsplit_set (line, " \t\n", FALSE); + words_free = nm_utils_strsplit_set (line, " \t\n"); words = words_free ?: NM_PTRARRAY_EMPTY (const char *); @@ -1284,7 +1281,7 @@ read_route_file (int addr_family, for (line = strtok_r (contents, "\n", &contents_rest); line; line = strtok_r (NULL, "\n", &contents_rest)) { - nm_auto_ip_route_unref NMIPRoute *route = NULL; + nm_auto_unref_ip_route NMIPRoute *route = NULL; gs_free_error GError *local = NULL; int e; @@ -1327,7 +1324,7 @@ parse_dns_options (NMSettingIPConfig *ip_config, const char *value) if (!nm_setting_ip_config_has_dns_options (ip_config)) nm_setting_ip_config_clear_dns_options (ip_config, TRUE); - options = nm_utils_strsplit_set (value, " ", FALSE); + options = nm_utils_strsplit_set (value, " "); if (options) { for (item = options; *item; item++) { if (!nm_setting_ip_config_add_dns_option (ip_config, *item)) @@ -1343,46 +1340,33 @@ parse_full_ip6_address (shvarFile *ifcfg, NMIPAddress **out_address, GError **error) { - char **list; - char *ip_val, *prefix_val; + NMIPAddress *addr; + NMIPAddr addr_bin; int prefix; - gboolean success = FALSE; - g_return_val_if_fail (addr_str != NULL, FALSE); - g_return_val_if_fail (out_address != NULL, FALSE); - g_return_val_if_fail (*out_address == NULL, FALSE); - g_return_val_if_fail (!error || !*error, FALSE); + nm_assert (addr_str); + nm_assert (out_address && !*out_address); + nm_assert (!error || !*error); - /* Split the address and prefix */ - list = g_strsplit_set (addr_str, "/", 2); - if (g_strv_length (list) < 1) { + if (!nm_utils_parse_inaddr_prefix_bin (AF_INET6, + addr_str, + NULL, + &addr_bin, + &prefix)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Invalid IP6 address '%s'", addr_str); - goto error; + return FALSE; } - ip_val = list[0]; - - prefix_val = list[1]; - if (prefix_val) { - prefix = _nm_utils_ascii_str_to_int64 (prefix_val, 10, 0, 128, -1); - if (prefix < 0) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP6 prefix '%s'", prefix_val); - goto error; - } - } else { - /* Missing prefix is treated as prefix of 64 */ + if (prefix < 0) prefix = 64; - } - *out_address = nm_ip_address_new (AF_INET6, ip_val, prefix, error); - if (*out_address) - success = TRUE; + addr = nm_ip_address_new_binary (AF_INET6, &addr_bin, prefix, error); + if (!addr) + return FALSE; -error: - g_strfreev (list); - return success; + *out_address = addr; + return TRUE; } static NMSetting * @@ -1443,13 +1427,12 @@ make_match_setting (shvarFile *ifcfg) if (!v) return NULL; - strv = nm_utils_strsplit_set (v, " \t", TRUE); + strv = nm_utils_escaped_tokens_split (v, NM_ASCII_SPACES); if (strv) { for (i = 0; strv[i]; i++) { if (!s_match) s_match = (NMSettingMatch *) nm_setting_match_new (); - nm_setting_match_add_interface_name (s_match, - _nm_utils_unescape_spaces ((char *) strv[i])); + nm_setting_match_add_interface_name (s_match, strv[i]); } } @@ -1722,7 +1705,7 @@ make_ip4_setting (shvarFile *ifcfg, if (v) { gs_free const char **searches = NULL; - searches = nm_utils_strsplit_set (v, " ", FALSE); + searches = nm_utils_strsplit_set (v, " "); if (searches) { for (item = searches; *item; item++) { if (!nm_setting_ip_config_add_dns_search (s_ip4, *item)) @@ -1783,7 +1766,7 @@ make_ip4_setting (shvarFile *ifcfg, if (v) { gs_free const char **searches = NULL; - searches = nm_utils_strsplit_set (v, " ", FALSE); + searches = nm_utils_strsplit_set (v, " "); if (searches) { for (item = searches; *item; item++) { if (!nm_setting_ip_config_add_dns_search (s_ip4, *item)) @@ -1809,7 +1792,8 @@ static void read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *filename) { GDir *dir; - char *dirname, *base; + gs_free char *dirname = NULL; + gs_free char *base = NULL; NMIPAddress *base_addr = NULL; GError *err = NULL; @@ -1820,9 +1804,9 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file base_addr = nm_setting_ip_config_get_address (s_ip4, 0); dirname = g_path_get_dirname (filename); - g_return_if_fail (dirname != NULL); + nm_assert (dirname != NULL); base = g_path_get_basename (filename); - g_return_if_fail (base != NULL); + nm_assert (base != NULL); dir = g_dir_open (dirname, 0, &err); if (dir) { @@ -1911,9 +1895,6 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file PARSE_WARNING ("can not read directory '%s': %s", dirname, err->message); g_error_free (err); } - - g_free (base); - g_free (dirname); } static NMSetting * @@ -1922,10 +1903,9 @@ make_ip6_setting (shvarFile *ifcfg, gboolean routes_read, GError **error) { - NMSettingIPConfig *s_ip6 = NULL; + gs_unref_object NMSettingIPConfig *s_ip6 = NULL; const char *v; gs_free char *value = NULL; - char *route6_path = NULL; gboolean ipv6init, ipv6forwarding, dhcp6 = FALSE; char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; const char *ipv6addr, *ipv6addr_secondaries; @@ -2060,7 +2040,7 @@ make_ip6_setting (shvarFile *ifcfg, /* Don't bother to read IP, DNS and routes when IPv6 is disabled */ if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) - return NM_SETTING (s_ip6); + return NM_SETTING (g_steal_pointer (&s_ip6)); nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "DHCPV6_DUID", &value); @@ -2083,7 +2063,7 @@ make_ip6_setting (shvarFile *ifcfg, g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, v, NULL); g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, - svGetValueBoolean (ifcfg, "DHCPV6_SEND_HOSTNAME", TRUE), NULL); + svGetValueBoolean (ifcfg, "DHCPV6_SEND_HOSTNAME", TRUE), NULL); /* Read static IP addresses. * Read them even for AUTO and DHCP methods - in this case the addresses are @@ -2099,12 +2079,12 @@ make_ip6_setting (shvarFile *ifcfg, ipv6addr_secondaries ?: "", NULL); - list = nm_utils_strsplit_set (value, " ", FALSE); + list = nm_utils_strsplit_set (value, " "); for (iter = list, i = 0; iter && *iter; iter++, i++) { NMIPAddress *addr = NULL; if (!parse_full_ip6_address (ifcfg, *iter, i, &addr, error)) - goto error; + return NULL; if (!nm_setting_ip_config_add_address (s_ip6, addr)) PARSE_WARNING ("duplicate IP6 address"); @@ -2129,7 +2109,7 @@ make_ip6_setting (shvarFile *ifcfg, if (!nm_utils_ipaddr_valid (AF_INET6, v)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Invalid IP6 address '%s'", v); - goto error; + return NULL; } g_object_set (s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, v, NULL); @@ -2172,18 +2152,19 @@ make_ip6_setting (shvarFile *ifcfg, /* Ignore IPv4 addresses */ } else { PARSE_WARNING ("invalid DNS server address %s", v); - goto error; + return NULL; } } if (!routes_read) { /* NOP */ } else { + gs_free char *route6_path = NULL; + /* Read static routes from route6-<interface> file */ route6_path = utils_get_route6_path (svFileGetName (ifcfg)); if (!read_route_file (AF_INET6, route6_path, s_ip6, error)) - goto error; - g_free (route6_path); + return NULL; } /* DNS searches */ @@ -2192,7 +2173,7 @@ make_ip6_setting (shvarFile *ifcfg, if (v) { gs_free const char **searches = NULL; - searches = nm_utils_strsplit_set (v, " ", FALSE); + searches = nm_utils_strsplit_set (v, " "); if (searches) { for (iter = searches; *iter; iter++) { if (!nm_setting_ip_config_add_dns_search (s_ip6, *iter)) @@ -2212,12 +2193,7 @@ make_ip6_setting (shvarFile *ifcfg, priority, NULL); - return NM_SETTING (s_ip6); - -error: - g_free (route6_path); - g_object_unref (s_ip6); - return NULL; + return NM_SETTING (g_steal_pointer (&s_ip6)); } static NMSetting * @@ -2551,7 +2527,7 @@ read_dcb_percent_array (shvarFile *ifcfg, return TRUE; } - split = nm_utils_strsplit_set (val, ",", FALSE); + split = nm_utils_strsplit_set (val, ","); if (NM_PTRARRAY_LEN (split) != 8) { PARSE_WARNING ("invalid %s percentage list value '%s'", prop, val); g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -2588,10 +2564,9 @@ make_dcb_setting (shvarFile *ifcfg, NMSetting **out_setting, GError **error) { - NMSettingDcb *s_dcb = NULL; + gs_unref_object NMSettingDcb *s_dcb = NULL; gboolean dcb_on; NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE; - char *val; g_return_val_if_fail (out_setting != NULL, FALSE); @@ -2600,31 +2575,28 @@ make_dcb_setting (shvarFile *ifcfg, return TRUE; s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); - g_assert (s_dcb); /* FCOE */ if (!read_dcb_app (ifcfg, s_dcb, "FCOE", &dcb_flags_props[DCB_APP_FCOE_FLAGS], NM_SETTING_DCB_APP_FCOE_PRIORITY, error)) { - g_object_unref (s_dcb); return FALSE; } if (nm_setting_dcb_get_app_fcoe_flags (s_dcb) & NM_SETTING_DCB_FLAG_ENABLE) { + gs_free char *val = NULL; + val = svGetValueStr_cp (ifcfg, KEY_DCB_APP_FCOE_MODE); if (val) { - if (strcmp (val, NM_SETTING_DCB_FCOE_MODE_FABRIC) == 0 || - strcmp (val, NM_SETTING_DCB_FCOE_MODE_VN2VN) == 0) + if (NM_IN_STRSET (val, NM_SETTING_DCB_FCOE_MODE_FABRIC, + NM_SETTING_DCB_FCOE_MODE_VN2VN)) g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_FCOE_MODE, val, NULL); else { PARSE_WARNING ("invalid FCoE mode '%s'", val); g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "invalid FCoE mode"); - g_free (val); - g_object_unref (s_dcb); return FALSE; } - g_free (val); } } @@ -2633,7 +2605,6 @@ make_dcb_setting (shvarFile *ifcfg, &dcb_flags_props[DCB_APP_ISCSI_FLAGS], NM_SETTING_DCB_APP_ISCSI_PRIORITY, error)) { - g_object_unref (s_dcb); return FALSE; } @@ -2642,7 +2613,6 @@ make_dcb_setting (shvarFile *ifcfg, &dcb_flags_props[DCB_APP_FIP_FLAGS], NM_SETTING_DCB_APP_FIP_PRIORITY, error)) { - g_object_unref (s_dcb); return FALSE; } @@ -2657,7 +2627,6 @@ make_dcb_setting (shvarFile *ifcfg, "PFC", nm_setting_dcb_set_priority_flow_control, error)) { - g_object_unref (s_dcb); return FALSE; } @@ -2673,7 +2642,6 @@ make_dcb_setting (shvarFile *ifcfg, TRUE, nm_setting_dcb_set_priority_group_id, error)) { - g_object_unref (s_dcb); return FALSE; } @@ -2686,7 +2654,6 @@ make_dcb_setting (shvarFile *ifcfg, TRUE, nm_setting_dcb_set_priority_group_bandwidth, error)) { - g_object_unref (s_dcb); return FALSE; } @@ -2699,7 +2666,6 @@ make_dcb_setting (shvarFile *ifcfg, FALSE, nm_setting_dcb_set_priority_bandwidth, error)) { - g_object_unref (s_dcb); return FALSE; } @@ -2711,7 +2677,6 @@ make_dcb_setting (shvarFile *ifcfg, "STRICT", nm_setting_dcb_set_priority_strict_bandwidth, error)) { - g_object_unref (s_dcb); return FALSE; } @@ -2723,11 +2688,10 @@ make_dcb_setting (shvarFile *ifcfg, FALSE, nm_setting_dcb_set_priority_traffic_class, error)) { - g_object_unref (s_dcb); return FALSE; } - *out_setting = NM_SETTING (s_dcb); + *out_setting = NM_SETTING (g_steal_pointer (&s_dcb)); return TRUE; } @@ -2834,7 +2798,7 @@ make_wep_setting (shvarFile *ifcfg, GError **error) { gs_unref_object NMSettingWirelessSecurity *s_wsec = NULL; - char *value; + gs_free char *value = NULL; shvarFile *keys_ifcfg = NULL; int default_key_idx = 0; gboolean has_default_key = FALSE; @@ -2849,13 +2813,12 @@ make_wep_setting (shvarFile *ifcfg, if (default_key_idx == 0) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Invalid default WEP key '%s'", value); - g_free (value); return NULL; } has_default_key = TRUE; default_key_idx--; /* convert to [0...3] */ g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, (guint) default_key_idx, NULL); - g_free (value); + nm_clear_g_free (&value); } /* Read WEP key flags */ @@ -2902,23 +2865,21 @@ make_wep_setting (shvarFile *ifcfg, value = svGetValueStr_cp (ifcfg, "SECURITYMODE"); if (value) { - char *lcase; + gs_free char *lcase = NULL; lcase = g_ascii_strdown (value, -1); - g_free (value); + nm_clear_g_free (&value); - if (!strcmp (lcase, "open")) { + if (nm_streq (lcase, "open")) { g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", NULL); - } else if (!strcmp (lcase, "restricted")) { + } else if (nm_streq (lcase, "restricted")) { g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "shared", NULL); } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Invalid WEP authentication algorithm '%s'", lcase); - g_free (lcase); return NULL; } - g_free (lcase); } /* If no WEP keys were given, and the keys are not agent-owned, and no @@ -2963,7 +2924,7 @@ fill_wpa_ciphers (shvarFile *ifcfg, if (!p) return TRUE; - list = nm_utils_strsplit_set (p, " ", FALSE); + list = nm_utils_strsplit_set (p, " "); for (iter = list; iter && *iter; iter++, i++) { /* Ad-Hoc configurations cannot have pairwise ciphers, and can only * have one group cipher. Ignore any additional group ciphers and @@ -3233,7 +3194,7 @@ eap_peap_reader (const char *eap_method, } /* Handle options for the inner auth method */ - list = nm_utils_strsplit_set (v, " ", FALSE); + list = nm_utils_strsplit_set (v, " "); iter = list; if (iter) { if (NM_IN_STRSET (*iter, "MSCHAPV2", @@ -3311,7 +3272,7 @@ eap_ttls_reader (const char *eap_method, inner_auth = g_ascii_strdown (v, -1); /* Handle options for the inner auth method */ - list = nm_utils_strsplit_set (inner_auth, " ", FALSE); + list = nm_utils_strsplit_set (inner_auth, " "); iter = list; if (iter) { if (NM_IN_STRSET (*iter, "mschapv2", @@ -3372,7 +3333,7 @@ eap_fast_reader (const char *eap_method, if (fast_provisioning) { gs_free const char **list1 = NULL; - list1 = nm_utils_strsplit_set (fast_provisioning, " \t", FALSE); + list1 = nm_utils_strsplit_set (fast_provisioning, " \t"); for (iter = list1; iter && *iter; iter++) { if (strcmp (*iter, "allow-unauth") == 0) allow_unauth = TRUE; @@ -3406,7 +3367,7 @@ eap_fast_reader (const char *eap_method, } /* Handle options for the inner auth method */ - list = nm_utils_strsplit_set (inner_auth, " ", FALSE); + list = nm_utils_strsplit_set (inner_auth, " "); iter = list; if (iter) { if ( !strcmp (*iter, "MSCHAPV2") @@ -3486,7 +3447,7 @@ read_8021x_list_value (shvarFile *ifcfg, if (!v) return; - strv = nm_utils_strsplit_set (v, " \t", FALSE); + strv = nm_utils_strsplit_set (v, " \t"); if (strv) g_object_set (setting, prop_name, strv, NULL); } @@ -3515,7 +3476,7 @@ fill_8021x (shvarFile *ifcfg, return NULL; } - list = nm_utils_strsplit_set (v, " ", FALSE); + list = nm_utils_strsplit_set (v, " "); s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); @@ -3829,7 +3790,7 @@ transform_hwaddr_blacklist (const char *blacklist) const char **strv; gsize i, j; - strv = nm_utils_strsplit_set (blacklist, " \t", FALSE); + strv = nm_utils_strsplit_set (blacklist, " \t"); if (!strv) return NULL; for (i = 0, j = 0; strv[j]; j++) { @@ -4147,7 +4108,7 @@ parse_ethtool_option (const char *value, gs_free const char **words = NULL; guint i; - words = nm_utils_strsplit_set (value, NULL, FALSE); + words = nm_utils_strsplit_set (value, " \t\n"); if (!words) return; @@ -4316,6 +4277,84 @@ parse_ethtool_option (const char *value, } } +static GPtrArray * +read_routing_rules_parse (shvarFile *ifcfg, + gboolean routes_read) +{ + gs_unref_ptrarray GPtrArray *arr = NULL; + gs_free const char **keys = NULL; + guint i, len; + + keys = svGetKeysSorted (ifcfg, SV_KEY_TYPE_ROUTING_RULE4 | SV_KEY_TYPE_ROUTING_RULE6, &len); + if (len == 0) + return NULL; + + if (!routes_read) { + PARSE_WARNING ("'rule-' or 'rule6-' files are present; Policy routing rules (ROUTING_RULE*) settings are ignored"); + return NULL; + } + + arr = g_ptr_array_new_full (len, (GDestroyNotify) nm_ip_routing_rule_unref); + for (i = 0; i < len; i++) { + const char *key = keys[i]; + nm_auto_unref_ip_routing_rule NMIPRoutingRule *rule = NULL; + gs_free_error GError *local = NULL; + gs_free char *value_to_free = NULL; + const char *value; + gboolean key_is_ipv4; + + key_is_ipv4 = (key[NM_STRLEN ("ROUTING_RULE")] == '_'); + nm_assert ( key_is_ipv4 == NM_STR_HAS_PREFIX (key, "ROUTING_RULE_")); + nm_assert ((!key_is_ipv4) == NM_STR_HAS_PREFIX (key, "ROUTING_RULE6_")); + + value = svGetValueStr (ifcfg, key, &value_to_free); + if (!value) + continue; + + rule = nm_ip_routing_rule_from_string (value, + NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE + | (key_is_ipv4 + ? NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET + : NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6), + NULL, + &local); + if (!rule) { + PARSE_WARNING ("invalid routing rule %s=\"%s\": %s", key, value, local->message); + continue; + } + + g_ptr_array_add (arr, g_steal_pointer (&rule)); + } + + if (arr->len == 0) + return NULL; + + return g_steal_pointer (&arr); +} + +static void +read_routing_rules (shvarFile *ifcfg, + gboolean routes_read, + NMSettingIPConfig *s_ip4, + NMSettingIPConfig *s_ip6) +{ + gs_unref_ptrarray GPtrArray *routing_rules = NULL; + guint i; + + routing_rules = read_routing_rules_parse (ifcfg, routes_read); + if (!routing_rules) + return; + + for (i = 0; i < routing_rules->len; i++) { + NMIPRoutingRule *rule = routing_rules->pdata[i]; + + nm_setting_ip_config_add_routing_rule ( (nm_ip_routing_rule_get_addr_family (rule) == AF_INET) + ? s_ip4 + : s_ip6, + rule); + } +} + static void parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection) { @@ -4340,7 +4379,7 @@ parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection) gs_free const char **opts = NULL; const char *const *iter; - opts = nm_utils_strsplit_set (ethtool_opts, ";", FALSE); + opts = nm_utils_strsplit_set (ethtool_opts, ";"); for (iter = opts; iter && iter[0]; iter++) { /* in case of repeated wol_passwords, parse_ethtool_option() * will do the right thing and clear wol_password before resetting. */ @@ -4436,7 +4475,7 @@ make_wired_setting (shvarFile *ifcfg, gs_free const char **chans = NULL; guint32 num_chans; - chans = nm_utils_strsplit_set (value, ",", FALSE); + chans = nm_utils_strsplit_set (value, ","); num_chans = NM_PTRARRAY_LEN (chans); if (num_chans < 2 || num_chans > 3) { PARSE_WARNING ("invalid SUBCHANNELS '%s' (%u channels, 2 or 3 expected)", @@ -4470,22 +4509,23 @@ make_wired_setting (shvarFile *ifcfg, value = svGetValueStr_cp (ifcfg, "OPTIONS"); if (value) { - char **options, **iter; + gs_free const char **options = NULL; + gsize i; - iter = options = g_strsplit_set (value, " ", 0); - while (iter && *iter) { - char *equals = strchr (*iter, '='); + options = nm_utils_strsplit_set_with_empty (value, " "); + for (i = 0; options && options[i]; i++) { + const char *line = options[i]; + const char *equals; gboolean valid = FALSE; + equals = strchr (line, '='); if (equals) { - *equals = '\0'; - valid = nm_setting_wired_add_s390_option (s_wired, *iter, equals + 1); + ((char *) equals)[0] = '\0'; + valid = nm_setting_wired_add_s390_option (s_wired, line, equals + 1); } if (!valid) - PARSE_WARNING ("invalid s390 OPTION '%s'", *iter); - iter++; + PARSE_WARNING ("invalid s390 OPTION '%s'", line); } - g_strfreev (options); nm_clear_g_free (&value); } @@ -4759,18 +4799,18 @@ make_bond_setting (shvarFile *ifcfg, gs_free const char **items = NULL; const char *const *iter; - items = nm_utils_strsplit_set (v, " ", FALSE); + items = nm_utils_strsplit_set (v, " "); for (iter = items; iter && *iter; iter++) { - gs_strfreev char **keys = NULL; - const char *key, *val; - - keys = g_strsplit_set (*iter, "=", 2); - if (keys && *keys) { - key = *keys; - val = *(keys + 1); - if (val && key[0] && val[0]) - handle_bond_option (s_bond, key, val); - } + gs_free char *key = NULL; + const char *val; + + val = strchr (*iter, '='); + if (!val) + continue; + key = g_strndup (*iter, val - *iter); + val++; + if (key[0] && val[0]) + handle_bond_option (s_bond, key, val); } } @@ -4958,6 +4998,8 @@ handle_bridge_option (NMSetting *setting, { "max_age", NM_SETTING_BRIDGE_MAX_AGE, BRIDGE_OPT_TYPE_OPTION, .only_with_stp = TRUE }, { "ageing_time", NM_SETTING_BRIDGE_AGEING_TIME, BRIDGE_OPT_TYPE_OPTION }, { "multicast_snooping", NM_SETTING_BRIDGE_MULTICAST_SNOOPING, BRIDGE_OPT_TYPE_OPTION }, + { "vlan_filtering", NM_SETTING_BRIDGE_VLAN_FILTERING, BRIDGE_OPT_TYPE_OPTION }, + { "default_pvid", NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, BRIDGE_OPT_TYPE_OPTION }, { "group_fwd_mask", NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, BRIDGE_OPT_TYPE_OPTION }, { "priority", NM_SETTING_BRIDGE_PORT_PRIORITY, BRIDGE_OPT_TYPE_PORT_OPTION }, { "path_cost", NM_SETTING_BRIDGE_PORT_PATH_COST, BRIDGE_OPT_TYPE_PORT_OPTION }, @@ -5037,19 +5079,56 @@ handle_bridging_opts (NMSetting *setting, gs_free const char **items = NULL; const char *const *iter; - items = nm_utils_strsplit_set (value, " ", FALSE); + items = nm_utils_strsplit_set (value, " "); for (iter = items; iter && *iter; iter++) { - gs_strfreev char **keys = NULL; - const char *key, *val; - - keys = g_strsplit_set (*iter, "=", 2); - if (keys && *keys) { - key = *keys; - val = *(keys + 1); - if (val && key[0] && val[0]) - func (setting, stp, key, val, opt_type); + gs_free char *key = NULL; + const char *val; + + val = strchr (*iter, '='); + if (!val) + continue; + key = g_strndup (*iter, val - *iter); + val++; + if (key[0] && val[0]) + func (setting, stp, key, val, opt_type); + } +} + +static void +read_bridge_vlans (shvarFile *ifcfg, + const char *key, + NMSetting *setting, + const char *property) +{ + gs_unref_ptrarray GPtrArray *array = NULL; + gs_free char *value_to_free = NULL; + const char *value; + + value = svGetValueStr (ifcfg, key, &value_to_free); + if (value) { + gs_free const char **strv = NULL; + const char *const *iter; + GError *local = NULL; + NMBridgeVlan *vlan; + + array = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); + + strv = nm_utils_escaped_tokens_split (value, ","); + if (strv) { + for (iter = strv; *iter; iter++) { + vlan = nm_bridge_vlan_from_str (*iter, &local); + if (!vlan) { + PARSE_WARNING ("invalid bridge VLAN: %s", local->message); + g_clear_error (&local); + continue; + } + g_ptr_array_add (array, vlan); + } } + nm_clear_g_free (&value_to_free); } + + g_object_set (setting, property, array, NULL); } static NMSetting * @@ -5110,6 +5189,11 @@ make_bridge_setting (shvarFile *ifcfg, nm_clear_g_free (&value_to_free); } + read_bridge_vlans (ifcfg, + "BRIDGE_VLANS", + NM_SETTING (s_bridge), + NM_SETTING_BRIDGE_VLANS); + return (NMSetting *) g_steal_pointer (&s_bridge); } @@ -5179,6 +5263,11 @@ make_bridge_port_setting (shvarFile *ifcfg) handle_bridging_opts (s_port, FALSE, value, handle_bridge_option, BRIDGE_OPT_TYPE_PORT_OPTION); nm_clear_g_free (&value_to_free); } + + read_bridge_vlans (ifcfg, + "BRIDGE_PORT_VLANS", + s_port, + NM_SETTING_BRIDGE_PORT_VLANS); } return s_port; @@ -5255,7 +5344,7 @@ parse_prio_map_list (NMSettingVlan *s_vlan, v = svGetValueStr (ifcfg, key, &value); if (!v) return; - list = nm_utils_strsplit_set (v, ",", FALSE); + list = nm_utils_strsplit_set (v, ","); for (iter = list; iter && *iter; iter++) { if (!strchr (*iter, ':')) @@ -5360,7 +5449,7 @@ make_vlan_setting (shvarFile *ifcfg, gs_free const char **strv = NULL; const char *const *ptr; - strv = nm_utils_strsplit_set (v, ", ", FALSE); + strv = nm_utils_strsplit_set (v, ", "); for (ptr = strv; ptr && *ptr; ptr++) { if (nm_streq (*ptr, "GVRP") && gvrp == -1) vlan_flags |= NM_VLAN_FLAG_GVRP; @@ -5502,7 +5591,7 @@ check_dns_search_domains (shvarFile *ifcfg, NMSetting *s_ip4, NMSetting *s_ip6) gs_free const char **searches = NULL; const char *const *item; - searches = nm_utils_strsplit_set (v, " ", FALSE); + searches = nm_utils_strsplit_set (v, " "); if (searches) { for (item = searches; *item; item++) { if (!nm_setting_ip_config_add_dns_search (NM_SETTING_IP_CONFIG (s_ip6), *item)) @@ -5768,8 +5857,7 @@ connection_from_file_full (const char *filename, error); if (!s_ip6) return NULL; - else - nm_connection_add_setting (connection, s_ip6); + nm_connection_add_setting (connection, s_ip6); s_ip4 = make_ip4_setting (main_ifcfg, network_ifcfg, @@ -5778,12 +5866,15 @@ connection_from_file_full (const char *filename, error); if (!s_ip4) return NULL; - else { - read_aliases (NM_SETTING_IP_CONFIG (s_ip4), - !has_ip4_defroute && !nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (s_ip4)), - filename); - nm_connection_add_setting (connection, s_ip4); - } + read_aliases (NM_SETTING_IP_CONFIG (s_ip4), + !has_ip4_defroute && !nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (s_ip4)), + filename); + nm_connection_add_setting (connection, s_ip4); + + read_routing_rules (main_ifcfg, + !has_complex_routes_v4 && !has_complex_routes_v6, + NM_SETTING_IP_CONFIG (s_ip4), + NM_SETTING_IP_CONFIG (s_ip6)); s_sriov = make_sriov_setting (main_ifcfg); if (s_sriov) diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h index 84c22094..c7729df5 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h @@ -22,7 +22,7 @@ #define _UTILS_H_ #include "nm-connection.h" -#include "nm-ethtool-utils.h" +#include "nm-libnm-core-intern/nm-ethtool-utils.h" #include "shvar.h" diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index ee7fd161..80b1bffe 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -30,8 +30,8 @@ #include <unistd.h> #include <stdio.h> -#include "nm-utils/nm-enum-utils.h" -#include "nm-utils/nm-io-utils.h" +#include "nm-glib-aux/nm-enum-utils.h" +#include "nm-glib-aux/nm-io-utils.h" #include "nm-manager.h" #include "nm-setting-connection.h" #include "nm-setting-wired.h" @@ -50,7 +50,7 @@ #include "nm-core-internal.h" #include "NetworkManagerUtils.h" #include "nm-meta-setting.h" -#include "nm-ethtool-utils.h" +#include "nm-libnm-core-intern/nm-ethtool-utils.h" #include "nms-ifcfg-rh-common.h" #include "nms-ifcfg-rh-reader.h" @@ -1464,6 +1464,43 @@ get_setting_default_boolean (NMSetting *setting, const char *prop) } static gboolean +write_bridge_vlans (NMSetting *setting, + const char *property_name, + shvarFile *ifcfg, + const char *key, + GError **error) +{ + gs_unref_ptrarray GPtrArray *vlans = NULL; + NMBridgeVlan *vlan; + GString *string; + guint i; + + g_object_get (setting, property_name, &vlans, NULL); + + if (!vlans || !vlans->len) { + svUnsetValue (ifcfg, key); + return TRUE; + } + + string = g_string_new (""); + for (i = 0; i < vlans->len; i++) { + gs_free char *vlan_str = NULL; + + vlan = vlans->pdata[i]; + vlan_str = nm_bridge_vlan_to_str (vlan, error); + if (!vlan_str) + return FALSE; + if (string->len > 0) + g_string_append (string, ","); + nm_utils_escaped_tokens_escape_gstr_assert (vlan_str, ",", string); + } + + svSetValueStr (ifcfg, key, string->str); + g_string_free (string, TRUE); + return TRUE; +} + +static gboolean write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) { NMSettingBridge *s_bridge; @@ -1534,10 +1571,31 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wire g_string_append_printf (opts, "multicast_snooping=%u", (guint32) b); } + b = nm_setting_bridge_get_vlan_filtering (s_bridge); + if (b != get_setting_default_boolean (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_VLAN_FILTERING)) { + if (opts->len) + g_string_append_c (opts, ' '); + g_string_append_printf (opts, "vlan_filtering=%u", (guint32) b); + } + + i = nm_setting_bridge_get_vlan_default_pvid (s_bridge); + if (i != get_setting_default_uint (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID)) { + if (opts->len) + g_string_append_c (opts, ' '); + g_string_append_printf (opts, "default_pvid=%u", i); + } + if (opts->len) svSetValueStr (ifcfg, "BRIDGING_OPTS", opts->str); g_string_free (opts, TRUE); + if (!write_bridge_vlans ((NMSetting *) s_bridge, + NM_SETTING_BRIDGE_VLANS, + ifcfg, + "BRIDGE_VLANS", + error)) + return FALSE; + svSetValueStr (ifcfg, "TYPE", TYPE_BRIDGE); *wired = write_wired_for_virtual (connection, ifcfg); @@ -1550,7 +1608,7 @@ write_bridge_port_setting (NMConnection *connection, shvarFile *ifcfg, GError ** { NMSettingBridgePort *s_port; guint32 i; - GString *opts; + GString *string; s_port = nm_connection_get_setting_bridge_port (connection); if (!s_port) @@ -1559,28 +1617,35 @@ write_bridge_port_setting (NMConnection *connection, shvarFile *ifcfg, GError ** svUnsetValue (ifcfg, "BRIDGING_OPTS"); /* Bridge options */ - opts = g_string_sized_new (32); + string = g_string_sized_new (32); i = nm_setting_bridge_port_get_priority (s_port); if (i != get_setting_default_uint (NM_SETTING (s_port), NM_SETTING_BRIDGE_PORT_PRIORITY)) - g_string_append_printf (opts, "priority=%u", i); + g_string_append_printf (string, "priority=%u", i); i = nm_setting_bridge_port_get_path_cost (s_port); if (i != get_setting_default_uint (NM_SETTING (s_port), NM_SETTING_BRIDGE_PORT_PATH_COST)) { - if (opts->len) - g_string_append_c (opts, ' '); - g_string_append_printf (opts, "path_cost=%u", i); + if (string->len) + g_string_append_c (string, ' '); + g_string_append_printf (string, "path_cost=%u", i); } if (nm_setting_bridge_port_get_hairpin_mode (s_port)) { - if (opts->len) - g_string_append_c (opts, ' '); - g_string_append_printf (opts, "hairpin_mode=1"); + if (string->len) + g_string_append_c (string, ' '); + g_string_append_printf (string, "hairpin_mode=1"); } - if (opts->len) - svSetValueStr (ifcfg, "BRIDGING_OPTS", opts->str); - g_string_free (opts, TRUE); + if (string->len) + svSetValueStr (ifcfg, "BRIDGING_OPTS", string->str); + g_string_free (string, TRUE); + + if (!write_bridge_vlans ((NMSetting *) s_port, + NM_SETTING_BRIDGE_PORT_VLANS, + ifcfg, + "BRIDGE_PORT_VLANS", + error)) + return FALSE; return TRUE; } @@ -2310,15 +2375,17 @@ write_match_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) num = nm_setting_match_get_num_interface_names (s_match); for (i = 0; i < num; i++) { - gs_free char *to_free = NULL; const char *name; - if (i == 0) + name = nm_setting_match_get_interface_name (s_match, i); + if (!name || !name[0]) + continue; + + if (!str) str = g_string_new (""); else g_string_append_c (str, ' '); - name = nm_setting_match_get_interface_name (s_match, i); - g_string_append (str, _nm_utils_escape_spaces (name, &to_free)); + nm_utils_escaped_tokens_escape_gstr (name, NM_ASCII_SPACES, str); } if (str) @@ -2889,6 +2956,52 @@ write_ip6_setting (NMConnection *connection, return TRUE; } +static void +write_ip_routing_rules (NMConnection *connection, + shvarFile *ifcfg, + gboolean route_ignore) +{ + gsize idx; + int is_ipv4; + + svUnsetAll (ifcfg, SV_KEY_TYPE_ROUTING_RULE4 | SV_KEY_TYPE_ROUTING_RULE6); + + if (route_ignore) + return; + + idx = 0; + + for (is_ipv4 = 1; is_ipv4 >= 0; is_ipv4--) { + const int addr_family = is_ipv4 ? AF_INET : AF_INET6; + NMSettingIPConfig *s_ip; + guint i, num; + + s_ip = nm_connection_get_setting_ip_config (connection, addr_family); + if (!s_ip) + continue; + + num = nm_setting_ip_config_get_num_routing_rules (s_ip); + for (i = 0; i < num; i++) { + NMIPRoutingRule *rule = nm_setting_ip_config_get_routing_rule (s_ip, i); + gs_free const char *s = NULL; + char key[64]; + + s = nm_ip_routing_rule_to_string (rule, + NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE, + NULL, + NULL); + if (!s) + continue; + + if (is_ipv4) + numbered_tag (key, "ROUTING_RULE_", ++idx); + else + numbered_tag (key, "ROUTING_RULE6_", ++idx); + svSetValueStr (ifcfg, key, s); + } + } +} + static char * escape_id (const char *id) { @@ -3111,6 +3224,15 @@ do_write_construct (NMConnection *connection, has_complex_routes_v4 ? "" : "6"); return FALSE; } + if ( ( s_ip4 + && nm_setting_ip_config_get_num_routing_rules (s_ip4) > 0) + || ( s_ip6 + && nm_setting_ip_config_get_num_routing_rules (s_ip6) > 0)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Cannot configure routing rules on a connection that has an associated 'rule%s-' file", + has_complex_routes_v4 ? "" : "6"); + return FALSE; + } route_ignore = TRUE; } else route_ignore = FALSE; @@ -3128,6 +3250,10 @@ do_write_construct (NMConnection *connection, error)) return FALSE; + write_ip_routing_rules (connection, + ifcfg, + route_ignore); + write_connection_setting (s_con, ifcfg); NM_SET_OUT (out_ifcfg, g_steal_pointer (&ifcfg)); diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index f3d58e26..b399a17f 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -36,8 +36,8 @@ #include "nm-core-internal.h" #include "nm-core-utils.h" -#include "nm-utils/nm-enum-utils.h" -#include "nm-utils/nm-io-utils.h" +#include "nm-glib-aux/nm-enum-utils.h" +#include "nm-glib-aux/nm-io-utils.h" #include "c-list/src/c-list.h" /*****************************************************************************/ @@ -879,10 +879,25 @@ _is_all_digits (const char *str) #define IS_NUMBERED_TAG(key, tab_name) \ ({ \ + const char *_key2 = (key); \ + \ + ( (strncmp (_key2, tab_name, NM_STRLEN (tab_name)) == 0) \ + && _is_all_digits (&_key2[NM_STRLEN (tab_name)])); \ + }) + +#define IS_NUMBERED_TAG_PARSE(key, tab_name, out_idx) \ + ({ \ const char *_key = (key); \ + gint64 _idx; \ + gboolean _good = FALSE; \ + gint64 *_out_idx = (out_idx); \ \ - ( (strncmp (_key, tab_name, NM_STRLEN (tab_name)) == 0) \ - && _is_all_digits (&_key[NM_STRLEN (tab_name)])); \ + if ( IS_NUMBERED_TAG (_key, ""tab_name"") \ + && (_idx = _nm_utils_ascii_str_to_int64 (&_key[NM_STRLEN (tab_name)], 10, 0, G_MAXINT64, -1)) != -1) { \ + NM_SET_OUT (_out_idx, _idx); \ + _good = TRUE; \ + } \ + _good; \ }) static gboolean @@ -919,10 +934,30 @@ _svKeyMatchesType (const char *key, SvKeyType match_key_type) if (IS_NUMBERED_TAG (key, "SRIOV_VF")) return TRUE; } + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTING_RULE4)) { + if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE_", NULL)) + return TRUE; + } + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTING_RULE6)) { + if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE6_", NULL)) + return TRUE; + } return FALSE; } +gint64 +svNumberedParseKey (const char *key) +{ + gint64 idx; + + if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE_", &idx)) + return idx; + if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE6_", &idx)) + return idx; + return -1; +} + GHashTable * svGetKeys (shvarFile *s, SvKeyType match_key_type) { @@ -947,6 +982,42 @@ svGetKeys (shvarFile *s, SvKeyType match_key_type) return keys; } +static int +_get_keys_sorted_cmp (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + const char *k_a = *((const char *const*) a); + const char *k_b = *((const char *const*) b); + gint64 n_a; + gint64 n_b; + + n_a = svNumberedParseKey (k_a); + n_b = svNumberedParseKey (k_b); + NM_CMP_DIRECT (n_a, n_b); + NM_CMP_RETURN (strcmp (k_a, k_b)); + nm_assert_not_reached (); + return 0; +} + +const char ** +svGetKeysSorted (shvarFile *s, + SvKeyType match_key_type, + guint *out_len) +{ + gs_unref_hashtable GHashTable *keys_hash = NULL; + + keys_hash = svGetKeys (s, match_key_type); + if (!keys_hash) { + NM_SET_OUT (out_len, 0); + return NULL; + } + return (const char **) nm_utils_hash_keys_to_array (keys_hash, + _get_keys_sorted_cmp, + NULL, + out_len); +} + /*****************************************************************************/ const char * diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index 622bb474..b38a8557 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -40,6 +40,8 @@ typedef enum { SV_KEY_TYPE_TC = (1LL << 3), SV_KEY_TYPE_USER = (1LL << 4), SV_KEY_TYPE_SRIOV_VF = (1LL << 5), + SV_KEY_TYPE_ROUTING_RULE4 = (1LL << 6), + SV_KEY_TYPE_ROUTING_RULE6 = (1LL << 7), } SvKeyType; const char *svFileGetName (const shvarFile *s); @@ -67,8 +69,14 @@ char *svGetValueStr_cp (shvarFile *s, const char *key); int svParseBoolean (const char *value, int def); +gint64 svNumberedParseKey (const char *key); + GHashTable *svGetKeys (shvarFile *s, SvKeyType match_key_type); +const char **svGetKeysSorted (shvarFile *s, + SvKeyType match_key_type, + guint *out_len); + /* return TRUE if <key> resolves to any truth value (e.g. "yes", "y", "true") * return FALSE if <key> resolves to any non-truth value (e.g. "no", "n", "false") * return <def> otherwise diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected index c478db38..a8ff8df3 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected @@ -2,6 +2,7 @@ HWADDR=31:33:33:37:BE:CD MTU=1492 TYPE=Ethernet BRIDGING_OPTS="priority=50 path_cost=33" +BRIDGE_PORT_VLANS="1 untagged,2 pvid,4-4094 untagged" NAME="Test Write Bridge Component" UUID=${UUID} ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected new file mode 100644 index 00000000..0c2fa035 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected @@ -0,0 +1,19 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +ROUTING_RULE_1="priority 10 from 0.0.0.0/0 table 1" +ROUTING_RULE_2="priority 10 to 192.167.8.0/24 table 2" +ROUTING_RULE6_3="priority 10 from ::/0 table 10" +ROUTING_RULE6_4="priority 10 to 1:2:3::5/24 table 22" +ROUTING_RULE6_5="priority 10 to 1:3:3::5 table 55" +NAME="Test Write Routing Rules" +UUID=${UUID} +ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main index 2bc987c2..1788efe2 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main @@ -4,5 +4,5 @@ TYPE=Bridge BOOTPROTO=dhcp STP=on DELAY=2 -BRIDGING_OPTS="priority=32744 hello_time=7 max_age=39 ageing_time=235352 multicast_snooping=0 group_fwd_mask=24" +BRIDGING_OPTS="priority=32744 hello_time=7 max_age=39 ageing_time=235352 multicast_snooping=0 group_fwd_mask=24 vlan_filtering=1 default_pvid=99" MACADDR=00:16:41:11:22:33 diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index b352fbfc..49ab04d4 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -49,7 +49,7 @@ #include "nm-setting-vlan.h" #include "nm-setting-dcb.h" #include "nm-core-internal.h" -#include "nm-ethtool-utils.h" +#include "nm-libnm-core-intern/nm-ethtool-utils.h" #include "NetworkManagerUtils.h" @@ -4465,6 +4465,101 @@ test_write_wired_dhcp (void) nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); } +static NMIPRoutingRule * +_ip_routing_rule_new (int addr_family, + const char *str) +{ + NMIPRoutingRuleAsStringFlags flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE; + gs_free_error GError *local = NULL; + NMIPRoutingRule *rule; + + if (addr_family != AF_UNSPEC) { + if (addr_family == AF_INET) + flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET; + else { + g_assert (addr_family == AF_INET6); + flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6; + } + } + + rule = nm_ip_routing_rule_from_string (str, + NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE + | flags, + NULL, + nmtst_get_rand_bool () ? &local : NULL); + nmtst_assert_success (rule, local); + + if (addr_family != AF_UNSPEC) + g_assert_cmpint (nm_ip_routing_rule_get_addr_family (rule), ==, addr_family); + return rule; +} + +static void +_ip_routing_rule_add_to_setting (NMSettingIPConfig *s_ip, + const char *str) +{ + nm_auto_unref_ip_routing_rule NMIPRoutingRule *rule = NULL; + + rule = _ip_routing_rule_new (nm_setting_ip_config_get_addr_family (s_ip), str); + nm_setting_ip_config_add_routing_rule (s_ip, rule); +} + +static void +test_write_routing_rules (void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + + connection = nm_simple_connection_new (); + + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write Routing Rules", + NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (), + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NULL); + + _ip_routing_rule_add_to_setting (s_ip4, "pref 10 from 0.0.0.0/0 table 1"); + _ip_routing_rule_add_to_setting (s_ip4, "priority 10 to 192.167.8.0/24 table 2"); + _ip_routing_rule_add_to_setting (s_ip6, "pref 10 from ::/0 table 10"); + _ip_routing_rule_add_to_setting (s_ip6, "pref 10 from ::/0 to 1:2:3::5/24 table 22"); + _ip_routing_rule_add_to_setting (s_ip6, "pref 10 from ::/0 to 1:3:3::5/128 table 55"); + + nmtst_assert_connection_verifies (connection); + + _writer_new_connec_exp (connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_Routing_Rules.cexpected", + &testfile); + reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); + nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); +} + static void test_write_wired_match (void) { @@ -7507,6 +7602,8 @@ test_read_bridge_main (void) g_assert_cmpuint (nm_setting_bridge_get_ageing_time (s_bridge), ==, 235352); g_assert_cmpuint (nm_setting_bridge_get_group_forward_mask (s_bridge), ==, 24); g_assert (!nm_setting_bridge_get_multicast_snooping (s_bridge)); + g_assert_cmpint (nm_setting_bridge_get_vlan_filtering (s_bridge), ==, TRUE); + g_assert_cmpint (nm_setting_bridge_get_vlan_default_pvid (s_bridge), ==, 99); /* MAC address */ s_wired = nm_connection_get_setting_wired (connection); @@ -7531,6 +7628,8 @@ test_write_bridge_main (void) NMIPAddress *addr; static const char *mac = "31:33:33:37:be:cd"; GError *error = NULL; + gs_unref_ptrarray GPtrArray *vlans = NULL; + NMBridgeVlan *vlan; connection = nm_simple_connection_new (); g_assert (connection); @@ -7551,9 +7650,23 @@ test_write_bridge_main (void) s_bridge = (NMSettingBridge *) nm_setting_bridge_new (); nm_connection_add_setting (connection, NM_SETTING (s_bridge)); + vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); + vlan = nm_bridge_vlan_new (10, 16); + nm_bridge_vlan_set_untagged (vlan, TRUE); + g_ptr_array_add (vlans, vlan); + vlan = nm_bridge_vlan_new (22, 22); + nm_bridge_vlan_set_pvid (vlan, TRUE); + nm_bridge_vlan_set_untagged (vlan, TRUE); + g_ptr_array_add (vlans, vlan); + vlan = nm_bridge_vlan_new (44, 0); + g_ptr_array_add (vlans, vlan); + g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac, NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, 19008, + NM_SETTING_BRIDGE_VLAN_FILTERING, TRUE, + NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, 4000, + NM_SETTING_BRIDGE_VLANS, vlans, NULL); /* IP4 setting */ @@ -7631,6 +7744,8 @@ test_write_bridge_component (void) NMSetting *s_port; static const char *mac = "31:33:33:37:be:cd"; guint32 mtu = 1492; + gs_unref_ptrarray GPtrArray *vlans = NULL; + NMBridgeVlan *vlan; connection = nm_simple_connection_new (); g_assert (connection); @@ -7658,11 +7773,23 @@ test_write_bridge_component (void) NULL); /* Bridge port */ + vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); + vlan = nm_bridge_vlan_new (1, 0); + nm_bridge_vlan_set_untagged (vlan, TRUE); + g_ptr_array_add (vlans, vlan); + vlan = nm_bridge_vlan_new (4, 4094); + nm_bridge_vlan_set_untagged (vlan, TRUE); + g_ptr_array_add (vlans, vlan); + vlan = nm_bridge_vlan_new (2, 2); + nm_bridge_vlan_set_pvid (vlan, TRUE); + g_ptr_array_add (vlans, vlan); + s_port = nm_setting_bridge_port_new (); nm_connection_add_setting (connection, s_port); g_object_set (s_port, NM_SETTING_BRIDGE_PORT_PRIORITY, 50, NM_SETTING_BRIDGE_PORT_PATH_COST, 33, + NM_SETTING_BRIDGE_PORT_VLANS, vlans, NULL); nmtst_assert_connection_verifies (connection); @@ -10168,6 +10295,7 @@ int main (int argc, char **argv) g_test_add_func (TPATH "wired/write-dhcp-plus-ip", test_write_wired_dhcp_plus_ip); g_test_add_func (TPATH "wired/write/dhcp-8021x-peap-mschapv2", test_write_wired_dhcp_8021x_peap_mschapv2); g_test_add_func (TPATH "wired/write/match", test_write_wired_match); + g_test_add_func (TPATH "wired/write/routing-rules", test_write_routing_rules); #define _add_test_write_wired_8021x_tls(testpath, scheme, flags) \ nmtst_add_test_func (testpath, test_write_wired_8021x_tls, GINT_TO_POINTER (scheme), GINT_TO_POINTER (flags)) diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c index fd5561ae..239f6415 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c @@ -419,17 +419,15 @@ update_wired_setting_from_if_block (NMConnection *connection, static void ifupdown_ip4_add_dns (NMSettingIPConfig *s_ip4, const char *dns) { + gs_free const char **list = NULL; + const char **iter; guint32 addr; - gs_strfreev char **list = NULL; - char **iter; if (dns == NULL) return; - list = g_strsplit_set (dns, " \t", -1); + list = nm_utils_strsplit_set (dns, " \t"); for (iter = list; iter && *iter; iter++) { - if ((*iter)[0] == '\0') - continue; if (!inet_pton (AF_INET, *iter, &addr)) { _LOGW (" ignoring invalid nameserver '%s'", *iter); continue; @@ -524,13 +522,11 @@ update_ip4_setting_from_if_block (NMConnection *connection, /* DNS searches */ search_v = ifparser_getkey (block, "dns-search"); if (search_v) { - gs_strfreev char **list = NULL; - char **iter; + gs_free const char **list = NULL; + const char **iter; - list = g_strsplit_set (search_v, " \t", -1); + list = nm_utils_strsplit_set (search_v, " \t"); for (iter = list; iter && *iter; iter++) { - if ((*iter)[0] == '\0') - continue; if (!nm_setting_ip_config_add_dns_search (s_ip4, *iter)) _LOGW (" duplicate DNS domain '%s'", *iter); } @@ -546,17 +542,15 @@ update_ip4_setting_from_if_block (NMConnection *connection, static void ifupdown_ip6_add_dns (NMSettingIPConfig *s_ip6, const char *dns) { + gs_free const char **list = NULL; + const char **iter; struct in6_addr addr; - gs_strfreev char **list = NULL; - char **iter; if (dns == NULL) return; - list = g_strsplit_set (dns, " \t", -1); + list = nm_utils_strsplit_set (dns, " \t"); for (iter = list; iter && *iter; iter++) { - if ((*iter)[0] == '\0') - continue; if (!inet_pton (AF_INET6, *iter, &addr)) { _LOGW (" ignoring invalid nameserver '%s'", *iter); continue; @@ -640,13 +634,11 @@ update_ip6_setting_from_if_block (NMConnection *connection, /* DNS searches */ search_v = ifparser_getkey (block, "dns-search"); if (search_v) { - gs_strfreev char **list = NULL; - char **iter; + gs_free const char **list = NULL; + const char **iter; - list = g_strsplit_set (search_v, " \t", -1); + list = nm_utils_strsplit_set (search_v, " \t"); for (iter = list; iter && *iter; iter++) { - if ((*iter)[0] == '\0') - continue; if (!nm_setting_ip_config_add_dns_search (s_ip6, *iter)) _LOGW (" duplicate DNS domain '%s'", *iter); } diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c index 99a59477..04281f00 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c @@ -175,14 +175,14 @@ initialize (NMSettingsPlugin *plugin) const char *ports = ifparser_getkey (block, "bridge-ports"); if (ports) { - guint i; int state = 0; - gs_strfreev char **port_ifaces = NULL; + gs_free const char **port_ifaces = NULL; + gsize i; _LOGD ("parse: found bridge ports %s for %s", ports, block->name); - port_ifaces = g_strsplit_set (ports, " \t", -1); - for (i = 0; port_ifaces[i]; i++) { + port_ifaces = nm_utils_strsplit_set (ports, " \t"); + for (i = 0; port_ifaces && port_ifaces[i]; i++) { const char *token = port_ifaces[i]; /* Skip crazy stuff like regex or all */ @@ -200,7 +200,7 @@ initialize (NMSettingsPlugin *plugin) } if (nm_streq (token, "none")) continue; - if (state == 0 && strlen (token) > 0) { + if (state == 0) { conn = g_hash_table_lookup (priv->eni_ifaces, block->name); if (!conn) { _LOGD ("parse: adding bridge port \"%s\"", token); @@ -280,7 +280,7 @@ initialize (NMSettingsPlugin *plugin) GHashTableIter iter; g_hash_table_iter_init (&iter, priv->eni_ifaces); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) conn)) { + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &conn)) { if (conn) { _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self), NM_SETTINGS_CONNECTION (conn)); diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c index 8c75d8c7..2e06aeb9 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-writer.c +++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c @@ -32,7 +32,7 @@ #include "nms-keyfile-utils.h" #include "nms-keyfile-reader.h" -#include "nm-utils/nm-io-utils.h" +#include "nm-glib-aux/nm-io-utils.h" /*****************************************************************************/ diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c index f46689eb..12d21aba 100644 --- a/src/supplicant/nm-supplicant-interface.c +++ b/src/supplicant/nm-supplicant-interface.c @@ -29,7 +29,7 @@ #include "NetworkManagerUtils.h" #include "nm-supplicant-config.h" #include "nm-core-internal.h" -#include "nm-dbus-compat.h" +#include "nm-std-aux/nm-dbus-compat.h" #define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" #define WPAS_DBUS_IFACE_INTERFACE_WPS WPAS_DBUS_INTERFACE ".Interface.WPS" diff --git a/src/systemd/meson.build b/src/systemd/meson.build index 9dea4fb5..257a9462 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -38,7 +38,7 @@ libnm_systemd_core = static_library( ) ], dependencies: [ - nm_core_dep, + libnm_core_dep, ], c_args: [ '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD', diff --git a/src/systemd/sd-adapt-core/stat-util.h b/src/systemd/sd-adapt-core/udev-util.h index 637892c2..637892c2 100644 --- a/src/systemd/sd-adapt-core/stat-util.h +++ b/src/systemd/sd-adapt-core/udev-util.h diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c index 7fc8248a..e3af362d 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.c +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c @@ -14,6 +14,7 @@ #include "siphash24.h" #include "sparse-endian.h" #include "stdio-util.h" +#include "udev-util.h" #include "virt.h" #define SYSTEMD_PEN 43793 @@ -189,6 +190,13 @@ int dhcp_identifier_set_iaid( /* not yet ready */ return -EBUSY; + r = device_is_renaming(device); + if (r < 0) + return r; + if (r > 0) + /* device is under renaming */ + return -EBUSY; + name = net_get_name(device); } } diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c index 810a2633..45d2ed89 100644 --- a/src/systemd/src/libsystemd-network/dhcp-network.c +++ b/src/systemd/src/libsystemd-network/dhcp-network.c @@ -154,7 +154,6 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) { .in.sin_addr.s_addr = address, }; _cleanup_close_ int s = -1; - char ifname[IF_NAMESIZE] = ""; int r; s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); @@ -170,12 +169,9 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) { return r; if (ifindex > 0) { - if (if_indextoname(ifindex, ifname) == 0) - return -errno; - - r = setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)); + r = socket_bind_to_ifindex(s, ifindex); if (r < 0) - return -errno; + return r; } if (address == INADDR_ANY) { diff --git a/src/systemd/src/libsystemd-network/dhcp-option.c b/src/systemd/src/libsystemd-network/dhcp-option.c index c5fbe749..eaf044bf 100644 --- a/src/systemd/src/libsystemd-network/dhcp-option.c +++ b/src/systemd/src/libsystemd-network/dhcp-option.c @@ -10,10 +10,10 @@ #include <string.h> #include "alloc-util.h" -#include "utf8.h" -#include "strv.h" - #include "dhcp-internal.h" +#include "memory-util.h" +#include "strv.h" +#include "utf8.h" static int option_append(uint8_t options[], size_t size, size_t *offset, uint8_t code, size_t optlen, const void *optval) { diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c index 3d5abe64..6172c368 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-option.c +++ b/src/systemd/src/libsystemd-network/dhcp6-option.c @@ -15,10 +15,10 @@ #include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" #include "dns-domain.h" +#include "memory-util.h" #include "sparse-endian.h" #include "strv.h" #include "unaligned.h" -#include "util.h" typedef struct DHCP6StatusOption { struct DHCP6Option option; diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c index bbe7bf9d..530af034 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.c +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c @@ -9,9 +9,9 @@ #include "in-addr-util.h" #include "lldp-internal.h" #include "lldp-neighbor.h" +#include "memory-util.h" #include "missing.h" #include "unaligned.h" -#include "util.h" static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) { siphash24_compress(id->chassis_id, id->chassis_id_size, state); diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c index f85db47b..de219a6d 100644 --- a/src/systemd/src/libsystemd-network/network-internal.c +++ b/src/systemd/src/libsystemd-network/network-internal.c @@ -103,32 +103,12 @@ bool net_match_config(Set *match_mac, char * const *match_drivers, char * const *match_types, char * const *match_names, - Condition *match_host, - Condition *match_virt, - Condition *match_kernel_cmdline, - Condition *match_kernel_version, - Condition *match_arch, const struct ether_addr *dev_mac, const char *dev_path, const char *dev_driver, const char *dev_type, const char *dev_name) { - if (match_host && condition_test(match_host) <= 0) - return false; - - if (match_virt && condition_test(match_virt) <= 0) - return false; - - if (match_kernel_cmdline && condition_test(match_kernel_cmdline) <= 0) - return false; - - if (match_kernel_version && condition_test(match_kernel_version) <= 0) - return false; - - if (match_arch && condition_test(match_arch) <= 0) - return false; - if (match_mac && (!dev_mac || !set_contains(match_mac, dev_mac))) return false; @@ -159,32 +139,31 @@ int config_parse_net_condition(const char *unit, void *userdata) { ConditionType cond = ltype; - Condition **ret = data; + Condition **list = data, *c; bool negate; - Condition *c; - _cleanup_free_ char *s = NULL; assert(filename); assert(lvalue); assert(rvalue); assert(data); + if (isempty(rvalue)) { + *list = condition_free_list_type(*list, cond); + return 0; + } + negate = rvalue[0] == '!'; if (negate) rvalue++; - s = strdup(rvalue); - if (!s) - return log_oom(); - - c = condition_new(cond, s, false, negate); + c = condition_new(cond, rvalue, false, negate); if (!c) return log_oom(); - if (*ret) - condition_free(*ret); + /* Drop previous assignment. */ + *list = condition_free_list_type(*list, cond); - *ret = c; + LIST_PREPEND(conditions, *list, c); return 0; } diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h index 9119d9a4..d895103b 100644 --- a/src/systemd/src/libsystemd-network/network-internal.h +++ b/src/systemd/src/libsystemd-network/network-internal.h @@ -6,7 +6,6 @@ #include "sd-device.h" #include "sd-dhcp-lease.h" -#include "condition.h" #include "conf-parser.h" #include "def.h" #include "set.h" @@ -20,11 +19,6 @@ bool net_match_config(Set *match_mac, char * const *match_driver, char * const *match_type, char * const *match_name, - Condition *match_host, - Condition *match_virt, - Condition *match_kernel_cmdline, - Condition *match_kernel_version, - Condition *match_arch, const struct ether_addr *dev_mac, const char *dev_path, const char *dev_driver, diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c index 27f79638..cadcc8a8 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -25,10 +25,10 @@ #include "event-util.h" #include "hostname-util.h" #include "io-util.h" +#include "memory-util.h" #include "random-util.h" #include "string-util.h" #include "strv.h" -#include "util.h" #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */ #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN) diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index 39d2a6d0..6c7aecc8 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -10,6 +10,9 @@ #include <stdio_ext.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> #include "sd-dhcp-lease.h" diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c index 6de4adb5..53ce8929 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4acd.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c @@ -22,7 +22,7 @@ #include "random-util.h" #include "siphash24.h" #include "string-util.h" -#include "util.h" +#include "time-util.h" /* Constants from the RFC */ #define PROBE_WAIT_USEC (1U * USEC_PER_SEC) diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c index 8b1e9665..4f186762 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c @@ -219,28 +219,21 @@ static int ipv4ll_pick_address(sd_ipv4ll *ll) { return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr }); } -int sd_ipv4ll_restart(sd_ipv4ll *ll) { - ll->address = 0; - - return sd_ipv4ll_start(ll); -} - #define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2) -int sd_ipv4ll_start(sd_ipv4ll *ll) { +static int ipv4ll_start_internal(sd_ipv4ll *ll, bool reset_generation) { int r; bool picked_address = false; assert_return(ll, -EINVAL); assert_return(!ether_addr_is_null(&ll->mac), -EINVAL); - assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); /* If no random seed is set, generate some from the MAC address */ if (!ll->seed_set) ll->seed.value = htole64(siphash24(ll->mac.ether_addr_octet, ETH_ALEN, MAC_HASH_KEY.bytes)); - /* Restart the generation counter. */ - ll->seed.generation = 0; + if (reset_generation) + ll->seed.generation = 0; if (ll->address == 0) { r = ipv4ll_pick_address(ll); @@ -264,6 +257,19 @@ int sd_ipv4ll_start(sd_ipv4ll *ll) { return 0; } +int sd_ipv4ll_start(sd_ipv4ll *ll) { + assert_return(ll, -EINVAL); + assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); + + return ipv4ll_start_internal(ll, true); +} + +int sd_ipv4ll_restart(sd_ipv4ll *ll) { + ll->address = 0; + + return ipv4ll_start_internal(ll, false); +} + static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) { assert(ll); @@ -299,11 +305,7 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) { ll->claimed_address = 0; } else { - r = ipv4ll_pick_address(ll); - if (r < 0) - goto error; - - r = sd_ipv4acd_start(ll->acd); + r = sd_ipv4ll_restart(ll); if (r < 0) goto error; } diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c index 741128e1..bdf58dac 100644 --- a/src/systemd/src/libsystemd-network/sd-lldp.c +++ b/src/systemd/src/libsystemd-network/sd-lldp.c @@ -15,7 +15,9 @@ #include "lldp-internal.h" #include "lldp-neighbor.h" #include "lldp-network.h" +#include "memory-util.h" #include "socket-util.h" +#include "sort-util.h" #include "string-table.h" #define LLDP_DEFAULT_NEIGHBORS_MAX 128U diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c index 6f77421b..dad0801b 100644 --- a/src/systemd/src/libsystemd/sd-event/sd-event.c +++ b/src/systemd/src/libsystemd/sd-event/sd-event.c @@ -17,6 +17,7 @@ #include "hashmap.h" #include "list.h" #include "macro.h" +#include "memory-util.h" #include "missing.h" #include "prioq.h" #include "process-util.h" @@ -25,7 +26,6 @@ #include "string-table.h" #include "string-util.h" #include "time-util.h" -#include "util.h" #define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC) @@ -3120,7 +3120,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { timeout = 0; m = epoll_wait(e->epoll_fd, ev_queue, ev_queue_max, - timeout == (uint64_t) -1 ? -1 : (int) ((timeout + USEC_PER_MSEC - 1) / USEC_PER_MSEC)); + timeout == (uint64_t) -1 ? -1 : (int) DIV_ROUND_UP(timeout, USEC_PER_MSEC)); if (m < 0) { if (errno == EINTR) { e->state = SD_EVENT_PENDING; diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.h b/src/systemd/src/libsystemd/sd-id128/id128-util.h index 65f14ab2..82a69a77 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.h +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.h @@ -10,9 +10,6 @@ char *id128_to_uuid_string(sd_id128_t id, char s[37]); -/* Like SD_ID128_FORMAT_STR, but formats as UUID, not in plain format */ -#define ID128_UUID_FORMAT_STR "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" - bool id128_is_valid(const char *s) _pure_; typedef enum Id128Format { diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c index 13a28291..d0cae981 100644 --- a/src/systemd/src/libsystemd/sd-id128/sd-id128.c +++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c @@ -121,7 +121,6 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) { #if 0 /* NM_IGNORED */ static int get_invocation_from_keyring(sd_id128_t *ret) { - _cleanup_free_ char *description = NULL; char *d, *p, *g, *u, *e; unsigned long perms; @@ -140,7 +139,7 @@ static int get_invocation_from_keyring(sd_id128_t *ret) { if (key == -1) { /* Keyring support not available? No invocation key stored? */ if (IN_SET(errno, ENOSYS, ENOKEY)) - return 0; + return -ENXIO; return -errno; } @@ -216,7 +215,19 @@ static int get_invocation_from_keyring(sd_id128_t *ret) { if (c != sizeof(sd_id128_t)) return -EIO; - return 1; + return 0; +} + +static int get_invocation_from_environment(sd_id128_t *ret) { + const char *e; + + assert(ret); + + e = secure_getenv("INVOCATION_ID"); + if (!e) + return -ENXIO; + + return sd_id128_from_string(e, ret); } _public_ int sd_id128_get_invocation(sd_id128_t *ret) { @@ -226,31 +237,17 @@ _public_ int sd_id128_get_invocation(sd_id128_t *ret) { assert_return(ret, -EINVAL); if (sd_id128_is_null(saved_invocation_id)) { + /* We first check the environment. The environment variable is primarily relevant for user + * services, and sufficiently safe as long as no privilege boundary is involved. */ + r = get_invocation_from_environment(&saved_invocation_id); + if (r < 0 && r != -ENXIO) + return r; - /* We first try to read the invocation ID from the kernel keyring. This has the benefit that it is not - * fakeable by unprivileged code. If the information is not available in the keyring, we use - * $INVOCATION_ID but ignore the data if our process was called by less privileged code - * (i.e. secure_getenv() instead of getenv()). - * - * The kernel keyring is only relevant for system services (as for user services we don't store the - * invocation ID in the keyring, as there'd be no trust benefit in that). The environment variable is - * primarily relevant for user services, and sufficiently safe as no privilege boundary is involved. */ - + /* The kernel keyring is relevant for system services (as for user services we don't store + * the invocation ID in the keyring, as there'd be no trust benefit in that). */ r = get_invocation_from_keyring(&saved_invocation_id); if (r < 0) return r; - - if (r == 0) { - const char *e; - - e = secure_getenv("INVOCATION_ID"); - if (!e) - return -ENXIO; - - r = sd_id128_from_string(e, &saved_invocation_id); - if (r < 0) - return r; - } } *ret = saved_invocation_id; diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h index 787a12f2..7bb86093 100644 --- a/src/systemd/src/systemd/sd-event.h +++ b/src/systemd/src/systemd/sd-event.h @@ -71,7 +71,7 @@ typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata); typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata); typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata); typedef int (*sd_event_signal_handler_t)(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata); -#if defined _GNU_SOURCE || _POSIX_C_SOURCE >= 199309L +#if defined _GNU_SOURCE || (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L) typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, void *userdata); #else typedef void* sd_event_child_handler_t; diff --git a/src/systemd/src/systemd/sd-id128.h b/src/systemd/src/systemd/sd-id128.h index bdf88ed5..c5fbe0a4 100644 --- a/src/systemd/src/systemd/sd-id128.h +++ b/src/systemd/src/systemd/sd-id128.h @@ -63,6 +63,9 @@ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret); #define SD_ID128_FORMAT_STR "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" #define SD_ID128_FORMAT_VAL(x) (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15] +/* Like SD_ID128_FORMAT_STR, but formats as UUID, not in plain format */ +#define SD_ID128_UUID_FORMAT_STR "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" + #define SD_ID128_CONST_STR(x) \ ((const char[SD_ID128_STRING_MAX]) { \ ((x).bytes[0] >> 4) >= 10 ? 'a' + ((x).bytes[0] >> 4) - 10 : '0' + ((x).bytes[0] >> 4), \ diff --git a/src/tests/meson.build b/src/tests/meson.build index 153128f2..17857676 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -36,7 +36,7 @@ exe = executable( test_unit, [test_unit + '.c'], include_directories: src_inc, - dependencies: nm_core_dep, + dependencies: libnm_core_dep, c_args: cflags, link_with: [ libnm_systemd_core, diff --git a/src/tests/test-systemd.c b/src/tests/test-systemd.c index 91edcab2..20cbd50e 100644 --- a/src/tests/test-systemd.c +++ b/src/tests/test-systemd.c @@ -269,12 +269,12 @@ _test_unbase64mem_mem (const char *base64, const guint8 *expected_arr, gsize exp for (i = 0; expected_base64[i]; i++) _test_unbase64char (expected_base64[i], FALSE); - r = nm_sd_utils_unbase64mem (expected_base64, strlen (expected_base64), &exp2_arr, &exp2_len); + r = nm_sd_utils_unbase64mem (expected_base64, strlen (expected_base64), TRUE, &exp2_arr, &exp2_len); g_assert_cmpint (r, ==, 0); g_assert_cmpmem (expected_arr, expected_len, exp2_arr, exp2_len); if (!nm_streq (base64, expected_base64)) { - r = nm_sd_utils_unbase64mem (base64, strlen (base64), &exp3_arr, &exp3_len); + r = nm_sd_utils_unbase64mem (base64, strlen (base64), TRUE, &exp3_arr, &exp3_len); g_assert_cmpint (r, ==, 0); g_assert_cmpmem (expected_arr, expected_len, exp3_arr, exp3_len); } @@ -289,7 +289,7 @@ _test_unbase64mem_inval (const char *base64) gsize exp_len = 0; int r; - r = nm_sd_utils_unbase64mem (base64, strlen (base64), &exp_arr, &exp_len); + r = nm_sd_utils_unbase64mem (base64, strlen (base64), TRUE, &exp_arr, &exp_len); g_assert_cmpint (r, <, 0); g_assert (!exp_arr); g_assert (exp_len == 0); |