diff options
Diffstat (limited to 'src')
284 files changed, 9174 insertions, 6286 deletions
diff --git a/src/contrib/nm-vpn-editor-plugin-call.h b/src/contrib/nm-vpn-editor-plugin-call.h index 713ddf08..bf6036ee 100644 --- a/src/contrib/nm-vpn-editor-plugin-call.h +++ b/src/contrib/nm-vpn-editor-plugin-call.h @@ -27,9 +27,9 @@ * @NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_CAN_ADD: whether the plugin can * add a new connection for the given service-type. **/ -typedef enum { /*< skip >*/ - NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_NONE = 0x00, - NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_CAN_ADD = 0x01, +typedef enum /*< skip >*/ { + NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_NONE = 0x00, + NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_CAN_ADD = 0x01, } NMVpnEditorPluginServiceFlags; struct _NMVpnEditorPluginVT { diff --git a/src/core/NetworkManagerUtils.c b/src/core/NetworkManagerUtils.c index c50dbada..5727aac8 100644 --- a/src/core/NetworkManagerUtils.c +++ b/src/core/NetworkManagerUtils.c @@ -34,6 +34,11 @@ /*****************************************************************************/ +G_STATIC_ASSERT(NM_SHUTDOWN_TIMEOUT_1500_MSEC <= NM_SHUTDOWN_TIMEOUT_MAX_MSEC); +G_STATIC_ASSERT(NM_SHUTDOWN_TIMEOUT_5000_MSEC <= NM_SHUTDOWN_TIMEOUT_MAX_MSEC); + +/*****************************************************************************/ + /** * nm_utils_get_shared_wifi_permission: * @connection: the NMConnection to lookup the permission. @@ -1037,7 +1042,7 @@ _shutdown_waitobj_cb(gpointer user_data, GObject *where_the_object_was) * is still used. * * If @wait_type is %NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE, then during shutdown - * (after %NM_SHUTDOWN_TIMEOUT_MS), the cancellable will be cancelled to notify + * (after %NM_SHUTDOWN_TIMEOUT_MAX_MSEC), the cancellable will be cancelled to notify * the source of the shutdown. Note that otherwise, in this mode also @watched_obj * is only tracked with a weak-pointer. Especially, it does not register to the * "cancelled" signal to automatically unregister (otherwise, you would never @@ -1046,7 +1051,7 @@ _shutdown_waitobj_cb(gpointer user_data, GObject *where_the_object_was) * FIXME(shutdown): proper shutdown is not yet implemented, and registering * an object (currently) has no effect. * - * FIXME(shutdown): during shutdown, after %NM_SHUTDOWN_TIMEOUT_MS timeout, cancel + * FIXME(shutdown): during shutdown, after %NM_SHUTDOWN_TIMEOUT_MAX_MSEC timeout, cancel * all remaining %NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE instances. Also, when somebody * enqueues a cancellable after that point, cancel it right away on an idle handler. * @@ -1347,8 +1352,13 @@ nm_utils_ip_route_attribute_to_platform(int addr_family, int type; type = nm_net_aux_rtnl_rtntype_a2n(g_variant_get_string(variant, NULL)); - nm_assert( - NM_IN_SET(type, RTN_UNICAST, RTN_LOCAL, RTN_BLACKHOLE, RTN_UNREACHABLE, RTN_PROHIBIT)); + nm_assert(NM_IN_SET(type, + RTN_UNICAST, + RTN_LOCAL, + RTN_BLACKHOLE, + RTN_UNREACHABLE, + RTN_PROHIBIT, + RTN_THROW)); r->type_coerced = nm_platform_route_type_coerce(type); } else diff --git a/src/core/NetworkManagerUtils.h b/src/core/NetworkManagerUtils.h index c9210ba8..67c9cba4 100644 --- a/src/core/NetworkManagerUtils.h +++ b/src/core/NetworkManagerUtils.h @@ -103,23 +103,37 @@ NMPlatformRoutingRule *nm_ip_routing_rule_to_platform(const NMIPRoutingRule *rul /*****************************************************************************/ /* during shutdown, there are two relevant timeouts. One is - * NM_SHUTDOWN_TIMEOUT_MS which is plenty of time, that we give for all + * NM_SHUTDOWN_TIMEOUT_MAX_MSEC which is plenty of time, that we give for all * actions to complete. Of course, during shutdown components should hurry * to cleanup. * * When we initiate shutdown, we should start killing child processes - * with SIGTERM. If they don't complete within NM_SHUTDOWN_TIMEOUT_MS, we send + * with SIGTERM. If they don't complete within NM_SHUTDOWN_TIMEOUT_MAX_MSEC, we send * SIGKILL. * - * After NM_SHUTDOWN_TIMEOUT_MS, NetworkManager will however not yet terminate right - * away. It iterates the mainloop for another NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG. This + * After NM_SHUTDOWN_TIMEOUT_MAX_MSEC, NetworkManager will however not yet terminate right + * away. It iterates the mainloop for another NM_SHUTDOWN_TIMEOUT_ADDITIONAL_MSEC. This * should give time to reap the child process (after SIGKILL). * * So, the maximum time we should wait before sending SIGKILL should be at most - * NM_SHUTDOWN_TIMEOUT_MS. + * NM_SHUTDOWN_TIMEOUT_MAX_MSEC. */ -#define NM_SHUTDOWN_TIMEOUT_MS 1500 -#define NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG 500 +#define NM_SHUTDOWN_TIMEOUT_MAX_MSEC 5000 +#define NM_SHUTDOWN_TIMEOUT_ADDITIONAL_MSEC 500 + +/** + * NM_SHUTDOWN_TIMEOUT_1500_MSEC: this is just 1500 msec. The special + * thing about the define is that you are guaranteed that this is not + * longer than NM_SHUTDOWN_TIMEOUT_MAX_MSEC. + * When you perform an async operation, it must either be cancellable + * (and complete fast) or never take longer than NM_SHUTDOWN_TIMEOUT_MAX_MSEC. + * The usage of this macro makes that relation to NM_SHUTDOWN_TIMEOUT_MAX_MSEC + * explicit. + */ +#define NM_SHUTDOWN_TIMEOUT_1500_MSEC 1500 + +/* See NM_SHUTDOWN_TIMEOUT_1500_MSEC. */ +#define NM_SHUTDOWN_TIMEOUT_5000_MSEC 5000 typedef enum { /* There is no watched_obj argument, and the shutdown is delayed until the user @@ -131,7 +145,7 @@ typedef enum { NM_SHUTDOWN_WAIT_TYPE_OBJECT, /* The watched_obj argument is a GCancellable, and shutdown is delayed until the object - * gets destroyed (or unregistered). Note that after NM_SHUTDOWN_TIMEOUT_MS, the + * gets destroyed (or unregistered). Note that after NM_SHUTDOWN_TIMEOUT_MAX_MSEC, the * cancellable will be cancelled to notify listeners about the shutdown. */ NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE, } NMShutdownWaitType; diff --git a/src/core/devices/bluetooth/nm-bluez-manager.c b/src/core/devices/bluetooth/nm-bluez-manager.c index 05f85297..6bc00faf 100644 --- a/src/core/devices/bluetooth/nm-bluez-manager.c +++ b/src/core/devices/bluetooth/nm-bluez-manager.c @@ -1352,6 +1352,7 @@ _conn_create_panu_connection(NMBluezManager *self, BzDBusObj *bzobj) bzobj->d_device.address); nm_settings_add_connection(priv->settings, + NULL, connection, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, NM_SETTINGS_CONNECTION_ADD_REASON_NONE, diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c index 56b729e9..16896d57 100644 --- a/src/core/devices/nm-device-bond.c +++ b/src/core/devices/nm-device-bond.c @@ -37,17 +37,17 @@ NM_SETTING_BOND_OPTION_USE_CARRIER, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, \ NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY -#define OPTIONS_REAPPLY_SUBSET \ - NM_SETTING_BOND_OPTION_MIIMON, NM_SETTING_BOND_OPTION_UPDELAY, \ - NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_ARP_INTERVAL, \ - NM_SETTING_BOND_OPTION_ARP_VALIDATE, NM_SETTING_BOND_OPTION_PRIMARY, \ - NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO, \ - NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS, \ - NM_SETTING_BOND_OPTION_FAIL_OVER_MAC, NM_SETTING_BOND_OPTION_LP_INTERVAL, \ - NM_SETTING_BOND_OPTION_MIN_LINKS, NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, \ - NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, NM_SETTING_BOND_OPTION_RESEND_IGMP, \ - NM_SETTING_BOND_OPTION_USE_CARRIER, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, \ - NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY +#define OPTIONS_REAPPLY_SUBSET \ + NM_SETTING_BOND_OPTION_MIIMON, NM_SETTING_BOND_OPTION_UPDELAY, \ + NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_ARP_INTERVAL, \ + NM_SETTING_BOND_OPTION_ARP_VALIDATE, NM_SETTING_BOND_OPTION_PRIMARY, \ + NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO, \ + NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS, \ + NM_SETTING_BOND_OPTION_LP_INTERVAL, NM_SETTING_BOND_OPTION_MIN_LINKS, \ + NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, \ + NM_SETTING_BOND_OPTION_RESEND_IGMP, NM_SETTING_BOND_OPTION_USE_CARRIER, \ + NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, \ + NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY #define OPTIONS_REAPPLY_FULL \ OPTIONS_REAPPLY_SUBSET, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, \ @@ -373,7 +373,7 @@ apply_bonding_config(NMDeviceBond *self) NM_SETTING_BOND_OPTION_MODE); /* Need to release all slaves before we can change bond mode */ if (!nm_streq0(device_bond_mode, mode_str)) - nm_device_master_release_slaves(device); + nm_device_master_release_slaves_all(device); set_bond_attr_or_default(device, s_bond, NM_SETTING_BOND_OPTION_MODE); diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c index 4e4c2637..f11c172a 100644 --- a/src/core/devices/nm-device-bridge.c +++ b/src/core/devices/nm-device-bridge.c @@ -22,11 +22,17 @@ /*****************************************************************************/ +enum _NMBtCbState { + _NM_BT_CB_STATE_NONE = 0, /* Registration not done */ + _NM_BT_CB_STATE_WAIT = 1, /* Waiting for the callback */ + _NM_BT_CB_STATE_SUCCESS = 2, /* Callback succeeded */ +}; + struct _NMDeviceBridge { NMDevice parent; GCancellable *bt_cancellable; bool vlan_configured : 1; - bool bt_registered : 1; + unsigned bt_cb_state : 2; }; struct _NMDeviceBridgeClass { @@ -76,7 +82,8 @@ check_connection_available(NMDevice *device, if (!nm_bt_vtable_network_server->is_available( nm_bt_vtable_network_server, bdaddr, - (self->bt_cancellable || self->bt_registered) ? device : NULL)) { + (self->bt_cancellable || self->bt_cb_state != _NM_BT_CB_STATE_NONE) ? device + : NULL)) { if (bdaddr) nm_utils_error_set(error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, @@ -791,23 +798,68 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge) return TRUE; } +static void +_platform_lnk_bridge_init_from_setting(NMSettingBridge *s_bridge, NMPlatformLnkBridge *props) +{ + *props = (NMPlatformLnkBridge){ + .forward_delay = _DEFAULT_IF_ZERO(nm_setting_bridge_get_forward_delay(s_bridge) * 100u, + NM_BRIDGE_FORWARD_DELAY_DEF_SYS), + .hello_time = _DEFAULT_IF_ZERO(nm_setting_bridge_get_hello_time(s_bridge) * 100u, + NM_BRIDGE_HELLO_TIME_DEF_SYS), + .max_age = _DEFAULT_IF_ZERO(nm_setting_bridge_get_max_age(s_bridge) * 100u, + NM_BRIDGE_MAX_AGE_DEF_SYS), + .ageing_time = nm_setting_bridge_get_ageing_time(s_bridge) * 100u, + .stp_state = nm_setting_bridge_get_stp(s_bridge), + .priority = nm_setting_bridge_get_priority(s_bridge), + .vlan_protocol = to_sysfs_vlan_protocol_sys(nm_setting_bridge_get_vlan_protocol(s_bridge)), + .vlan_stats_enabled = nm_setting_bridge_get_vlan_stats_enabled(s_bridge), + .group_fwd_mask = nm_setting_bridge_get_group_forward_mask(s_bridge), + .mcast_snooping = nm_setting_bridge_get_multicast_snooping(s_bridge), + .mcast_router = + to_sysfs_multicast_router_sys(nm_setting_bridge_get_multicast_router(s_bridge)), + .mcast_query_use_ifaddr = nm_setting_bridge_get_multicast_query_use_ifaddr(s_bridge), + .mcast_querier = nm_setting_bridge_get_multicast_querier(s_bridge), + .mcast_hash_max = nm_setting_bridge_get_multicast_hash_max(s_bridge), + .mcast_last_member_count = nm_setting_bridge_get_multicast_last_member_count(s_bridge), + .mcast_startup_query_count = nm_setting_bridge_get_multicast_startup_query_count(s_bridge), + .mcast_last_member_interval = + nm_setting_bridge_get_multicast_last_member_interval(s_bridge), + .mcast_membership_interval = nm_setting_bridge_get_multicast_membership_interval(s_bridge), + .mcast_querier_interval = nm_setting_bridge_get_multicast_querier_interval(s_bridge), + .mcast_query_interval = nm_setting_bridge_get_multicast_query_interval(s_bridge), + .mcast_query_response_interval = + nm_setting_bridge_get_multicast_query_response_interval(s_bridge), + .mcast_startup_query_interval = + nm_setting_bridge_get_multicast_startup_query_interval(s_bridge), + }; + + to_sysfs_group_address_sys(nm_setting_bridge_get_group_address(s_bridge), &props->group_addr); +} + static NMActStageReturn act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) { - NMConnection *connection; - NMSetting *s_bridge; - const Option *option; + NMConnection *connection; + NMSettingBridge *s_bridge; + NMPlatformLnkBridge props; + int r; + int ifindex = nm_device_get_ifindex(device); 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); + s_bridge = nm_connection_get_setting_bridge(connection); g_return_val_if_fail(s_bridge, NM_ACT_STAGE_RETURN_FAILURE); - for (option = master_options; option->name; option++) - commit_option(device, s_bridge, option, FALSE); + _platform_lnk_bridge_init_from_setting(s_bridge, &props); + + r = nm_platform_link_bridge_change(nm_device_get_platform(device), ifindex, &props); + if (r < 0) { + NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } - if (!bridge_set_vlan_options(device, (NMSettingBridge *) s_bridge)) { + if (!bridge_set_vlan_options(device, s_bridge)) { NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); return NM_ACT_STAGE_RETURN_FAILURE; } @@ -835,6 +887,7 @@ _bt_register_bridge_cb(GError *error, gpointer user_data) return; } + self->bt_cb_state = _NM_BT_CB_STATE_SUCCESS; nm_device_activate_schedule_stage2_device_config(NM_DEVICE(self), FALSE); } @@ -846,12 +899,12 @@ _nm_device_bridge_notify_unregister_bt_nap(NMDevice *device, const char *reason) _LOGD(LOGD_DEVICE, "bluetooth NAP server unregistered from bridge: %s%s", reason, - self->bt_registered ? "" : " (was no longer registered)"); + self->bt_cb_state != _NM_BT_CB_STATE_NONE ? "" : " (was no longer registered)"); nm_clear_g_cancellable(&self->bt_cancellable); - if (self->bt_registered) { - self->bt_registered = FALSE; + if (self->bt_cb_state != _NM_BT_CB_STATE_NONE) { + self->bt_cb_state = _NM_BT_CB_STATE_NONE; nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BT_FAILED); } } @@ -879,9 +932,12 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) if (self->bt_cancellable) return NM_ACT_STAGE_RETURN_POSTPONE; - if (self->bt_registered) + if (self->bt_cb_state == _NM_BT_CB_STATE_WAIT) return NM_ACT_STAGE_RETURN_POSTPONE; + if (self->bt_cb_state == _NM_BT_CB_STATE_SUCCESS) + return NM_ACT_STAGE_RETURN_SUCCESS; + self->bt_cancellable = g_cancellable_new(); if (!nm_bt_vtable_network_server->register_bridge(nm_bt_vtable_network_server, nm_setting_bluetooth_get_bdaddr(s_bt), @@ -895,7 +951,7 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) return NM_ACT_STAGE_RETURN_FAILURE; } - self->bt_registered = TRUE; + self->bt_cb_state = _NM_BT_CB_STATE_WAIT; return NM_ACT_STAGE_RETURN_POSTPONE; } @@ -906,14 +962,14 @@ deactivate(NMDevice *device) _LOGD(LOGD_DEVICE, "deactivate bridge%s", - self->bt_registered ? " (registered as NAP bluetooth device)" : ""); + self->bt_cb_state != _NM_BT_CB_STATE_NONE ? " (registered as NAP bluetooth device)" : ""); self->vlan_configured = FALSE; nm_clear_g_cancellable(&self->bt_cancellable); - if (self->bt_registered) { - self->bt_registered = FALSE; + if (self->bt_cb_state != _NM_BT_CB_STATE_NONE) { + self->bt_cb_state = _NM_BT_CB_STATE_NONE; nm_bt_vtable_network_server->unregister_bridge(nm_bt_vtable_network_server, device); } } @@ -1054,39 +1110,7 @@ create_and_realize(NMDevice *device, } } - props = (NMPlatformLnkBridge){ - .forward_delay = _DEFAULT_IF_ZERO(nm_setting_bridge_get_forward_delay(s_bridge) * 100u, - NM_BRIDGE_FORWARD_DELAY_DEF_SYS), - .hello_time = _DEFAULT_IF_ZERO(nm_setting_bridge_get_hello_time(s_bridge) * 100u, - NM_BRIDGE_HELLO_TIME_DEF_SYS), - .max_age = _DEFAULT_IF_ZERO(nm_setting_bridge_get_max_age(s_bridge) * 100u, - NM_BRIDGE_MAX_AGE_DEF_SYS), - .ageing_time = nm_setting_bridge_get_ageing_time(s_bridge) * 100u, - .stp_state = nm_setting_bridge_get_stp(s_bridge), - .priority = nm_setting_bridge_get_priority(s_bridge), - .vlan_protocol = to_sysfs_vlan_protocol_sys(nm_setting_bridge_get_vlan_protocol(s_bridge)), - .vlan_stats_enabled = nm_setting_bridge_get_vlan_stats_enabled(s_bridge), - .group_fwd_mask = nm_setting_bridge_get_group_forward_mask(s_bridge), - .mcast_snooping = nm_setting_bridge_get_multicast_snooping(s_bridge), - .mcast_router = - to_sysfs_multicast_router_sys(nm_setting_bridge_get_multicast_router(s_bridge)), - .mcast_query_use_ifaddr = nm_setting_bridge_get_multicast_query_use_ifaddr(s_bridge), - .mcast_querier = nm_setting_bridge_get_multicast_querier(s_bridge), - .mcast_hash_max = nm_setting_bridge_get_multicast_hash_max(s_bridge), - .mcast_last_member_count = nm_setting_bridge_get_multicast_last_member_count(s_bridge), - .mcast_startup_query_count = nm_setting_bridge_get_multicast_startup_query_count(s_bridge), - .mcast_last_member_interval = - nm_setting_bridge_get_multicast_last_member_interval(s_bridge), - .mcast_membership_interval = nm_setting_bridge_get_multicast_membership_interval(s_bridge), - .mcast_querier_interval = nm_setting_bridge_get_multicast_querier_interval(s_bridge), - .mcast_query_interval = nm_setting_bridge_get_multicast_query_interval(s_bridge), - .mcast_query_response_interval = - nm_setting_bridge_get_multicast_query_response_interval(s_bridge), - .mcast_startup_query_interval = - nm_setting_bridge_get_multicast_startup_query_interval(s_bridge), - }; - - to_sysfs_group_address_sys(nm_setting_bridge_get_group_address(s_bridge), &props.group_addr); + _platform_lnk_bridge_init_from_setting(s_bridge, &props); /* If mtu != 0, we set the MTU of the new bridge at creation time. However, kernel will still * automatically adjust the MTU of the bridge based on the minimum of the slave's MTU. diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c index 407f8ab8..32f2fbf9 100644 --- a/src/core/devices/nm-device-ethernet.c +++ b/src/core/devices/nm-device-ethernet.c @@ -1014,7 +1014,7 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) * get confused and fail to negotiate the new connection. (rh #1023503) * * FIXME(shutdown): when exiting, we also need to wait before quitting, - * at least for additional NM_SHUTDOWN_TIMEOUT_MS seconds because + * at least for additional NM_SHUTDOWN_TIMEOUT_MAX_MSEC seconds because * otherwise after restart the device won't work for the first seconds. */ if (priv->ppp_data.last_pppoe_time_msec != 0) { diff --git a/src/core/devices/nm-device-private.h b/src/core/devices/nm-device-private.h index 790bb823..b54aed6a 100644 --- a/src/core/devices/nm-device-private.h +++ b/src/core/devices/nm-device-private.h @@ -61,7 +61,7 @@ void nm_device_recheck_available_connections(NMDevice *device); void nm_device_master_check_slave_physical_port(NMDevice *self, NMDevice *slave, NMLogDomain log_domain); -void nm_device_master_release_slaves(NMDevice *self); +void nm_device_master_release_slaves_all(NMDevice *self); void nm_device_set_carrier(NMDevice *self, gboolean carrier); diff --git a/src/core/devices/nm-device-tun.c b/src/core/devices/nm-device-tun.c index b6c4f88b..e623e9c0 100644 --- a/src/core/devices/nm-device-tun.c +++ b/src/core/devices/nm-device-tun.c @@ -542,6 +542,12 @@ create_device(NMDeviceFactory *factory, NM_SETTING_TUN_SETTING_NAME), NULL); + /* OpenvSwitch will create a tun device named ovs-netdev when the datapath selected is netdev */ + if (nm_streq0(iface, "ovs-netdev")) { + *out_ignore = TRUE; + return NULL; + } + return g_object_new(NM_TYPE_DEVICE_TUN, NM_DEVICE_IFACE, iface, diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index a11486d5..1d9f235a 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -101,6 +101,12 @@ typedef void (*ActivationHandleFunc)(NMDevice *self); typedef enum { + RELEASE_SLAVE_TYPE_NO_CONFIG, + RELEASE_SLAVE_TYPE_CONFIG, + RELEASE_SLAVE_TYPE_CONFIG_FORCE, +} ReleaseSlaveType; + +typedef enum { CLEANUP_TYPE_KEEP, CLEANUP_TYPE_REMOVED, CLEANUP_TYPE_DECONFIGURE, @@ -362,7 +368,6 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDevice, PROP_FIRMWARE_MISSING, PROP_NM_PLUGIN_MISSING, PROP_TYPE_DESC, - PROP_RFKILL_TYPE, PROP_IFINDEX, PROP_AVAILABLE_CONNECTIONS, PROP_PHYSICAL_PORT_ID, @@ -474,7 +479,6 @@ typedef struct _NMDevicePrivate { char *driver; char *driver_version; char *firmware_version; - RfKillType rfkill_type; bool firmware_missing : 1; bool nm_plugin_missing : 1; bool @@ -1135,7 +1139,7 @@ _prop_get_ipv6_dhcp_duid(NMDevice *self, duid_out = nm_utils_generate_duid_llt(arp_type, hwaddr_bin, hwaddr_len, - nm_utils_host_id_get_timestamp_ns() + nm_utils_host_id_get_timestamp_nsec() / NM_UTILS_NSEC_PER_SEC); } @@ -1236,7 +1240,7 @@ _prop_get_ipv6_dhcp_duid(NMDevice *self, * before. Let's compute the time (in seconds) from 0 to 3 years; then we'll * subtract it from the host_id timestamp. */ - time = nm_utils_host_id_get_timestamp_ns() / NM_UTILS_NSEC_PER_SEC; + time = nm_utils_host_id_get_timestamp_nsec() / NM_UTILS_NSEC_PER_SEC; /* don't use too old timestamps. They cannot be expressed in DUID-LLT and * would all be truncated to zero. */ @@ -3855,6 +3859,7 @@ update_external_connection(NMDevice *self) if (connection_new) { nm_settings_connection_update(settings_connection, + NULL, connection_new, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, @@ -3893,12 +3898,15 @@ _dev_l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, N case NM_L3_CONFIG_NOTIFY_TYPE_PRE_COMMIT: { const NML3ConfigData *l3cd; + NMDeviceState state = nm_device_get_state(self); - /* FIXME(l3cfg): MTU handling should be moved to l3cfg. */ - l3cd = nm_l3cfg_get_combined_l3cd(l3cfg, TRUE); - if (l3cd) - priv->ip6_mtu = nm_l3_config_data_get_ip6_mtu(l3cd); - _commit_mtu(self); + if (state >= NM_DEVICE_STATE_IP_CONFIG && state < NM_DEVICE_STATE_DEACTIVATING) { + /* FIXME(l3cfg): MTU handling should be moved to l3cfg. */ + l3cd = nm_l3cfg_get_combined_l3cd(l3cfg, TRUE); + if (l3cd) + priv->ip6_mtu = nm_l3_config_data_get_ip6_mtu(l3cd); + _commit_mtu(self); + } return; } case NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT: @@ -3976,7 +3984,9 @@ _dev_l3_cfg_commit_type_reset(NMDevice *self) commit_type = NM_L3_CFG_COMMIT_TYPE_NONE; goto do_set; case NM_DEVICE_SYS_IFACE_STATE_ASSUME: - commit_type = NM_L3_CFG_COMMIT_TYPE_ASSUME; + /* TODO: NM_DEVICE_SYS_IFACE_STATE_ASSUME, will be dropped from the code. + * Meanwhile, the commit type must be updated. */ + commit_type = NM_L3_CFG_COMMIT_TYPE_UPDATE; goto do_set; case NM_DEVICE_SYS_IFACE_STATE_MANAGED: commit_type = NM_L3_CFG_COMMIT_TYPE_UPDATE; @@ -4455,9 +4465,9 @@ _parent_set_ifindex(NMDevice *self, int parent_ifindex, gboolean force_check) _LOGD(LOGD_DEVICE, "parent: ifindex %d, no device", priv->parent_ifindex); else { _LOGD(LOGD_DEVICE, - "parent: ifindex %d, device %p, %s", + "parent: ifindex %d, device " NM_HASH_OBFUSCATE_PTR_FMT ", %s", priv->parent_ifindex, - priv->parent_device.obj, + NM_HASH_OBFUSCATE_PTR(priv->parent_device.obj), nm_device_get_iface(priv->parent_device.obj)); } @@ -4719,7 +4729,7 @@ nm_device_get_ip_iface_identifier(NMDevice *self, gboolean *out_is_token) { NMSettingIP6Config *s_ip6; - const char *token = NULL; + const char *token; g_return_val_if_fail(NM_IS_DEVICE(self), FALSE); @@ -4731,13 +4741,12 @@ nm_device_get_ip_iface_identifier(NMDevice *self, g_return_val_if_fail(s_ip6, FALSE); token = nm_setting_ip6_config_get_token(s_ip6); - if (token) + if (token) { NM_SET_OUT(out_is_token, TRUE); + return nm_utils_ipv6_interface_identifier_get_from_token(iid, token); + } } - if (token) - return nm_utils_ipv6_interface_identifier_get_from_token(iid, token); - else - return NM_DEVICE_GET_CLASS(self)->get_ip_iface_identifier(self, iid); + return NM_DEVICE_GET_CLASS(self)->get_ip_iface_identifier(self, iid); } const char * @@ -5162,12 +5171,17 @@ nm_device_get_applied_setting(NMDevice *self, GType setting_type) return connection ? nm_connection_get_setting(connection, setting_type) : NULL; } -RfKillType +NMRfkillType nm_device_get_rfkill_type(NMDevice *self) { + NMRfkillType t; + g_return_val_if_fail(NM_IS_DEVICE(self), FALSE); - return NM_DEVICE_GET_PRIVATE(self)->rfkill_type; + t = NM_DEVICE_GET_CLASS(self)->rfkill_type; + + nm_assert(NM_IN_SET(t, NM_RFKILL_TYPE_UNKNOWN, NM_RFKILL_TYPE_WLAN, NM_RFKILL_TYPE_WWAN)); + return t; } static const char * @@ -5875,11 +5889,9 @@ static SlaveInfo * find_slave_info(NMDevice *self, NMDevice *slave) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - CList *iter; SlaveInfo *info; - c_list_for_each (iter, &priv->slaves) { - info = c_list_entry(iter, SlaveInfo, lst_slave); + c_list_for_each_entry (info, &priv->slaves, lst_slave) { if (info->slave == slave) return info; } @@ -5947,12 +5959,12 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co } /** - * nm_device_master_release_one_slave: + * nm_device_master_release_slave: * @self: the master device * @slave: the slave device to release * @configure: whether @self needs to actually release @slave - * @force: force the release of @slave even if it wasn't added - * to @master by NetworkManager + * @release_type: whether @self needs to actually release slave + * and whether that is forced. * @reason: the state change reason for the @slave * * If @self is capable of enslaving other devices (ie it's a bridge, bond, team, @@ -5960,11 +5972,10 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co * updates the state of @self and @slave to reflect its release. */ static void -nm_device_master_release_one_slave(NMDevice *self, - NMDevice *slave, - gboolean configure, - gboolean force, - NMDeviceStateReason reason) +nm_device_master_release_slave(NMDevice *self, + NMDevice *slave, + ReleaseSlaveType release_type, + NMDeviceStateReason reason) { NMDevicePrivate *priv; NMDevicePrivate *slave_priv; @@ -5973,17 +5984,22 @@ nm_device_master_release_one_slave(NMDevice *self, g_return_if_fail(NM_DEVICE(self)); g_return_if_fail(NM_DEVICE(slave)); - g_return_if_fail(!force || configure); + nm_assert(NM_IN_SET(release_type, + RELEASE_SLAVE_TYPE_NO_CONFIG, + RELEASE_SLAVE_TYPE_CONFIG, + RELEASE_SLAVE_TYPE_CONFIG_FORCE)); g_return_if_fail(NM_DEVICE_GET_CLASS(self)->release_slave != NULL); info = find_slave_info(self, slave); _LOGT(LOGD_CORE, - "master: release one slave %p/%s %s%s", - slave, + "master: release one slave " NM_HASH_OBFUSCATE_PTR_FMT "/%s %s%s", + NM_HASH_OBFUSCATE_PTR(slave), nm_device_get_iface(slave), !info ? "(not registered)" : (info->slave_is_enslaved ? "(enslaved)" : "(not enslaved)"), - force ? " (force-configure)" : (configure ? " (configure)" : "")); + release_type == RELEASE_SLAVE_TYPE_CONFIG_FORCE + ? " (force-configure)" + : (release_type == RELEASE_SLAVE_TYPE_CONFIG ? " (configure)" : "(no-config)")); if (!info) g_return_if_reached(); @@ -5995,8 +6011,11 @@ nm_device_master_release_one_slave(NMDevice *self, nm_assert(slave == info->slave); /* first, let subclasses handle the release ... */ - if (info->slave_is_enslaved || nm_device_sys_iface_state_is_external(slave) || force) - NM_DEVICE_GET_CLASS(self)->release_slave(self, slave, configure); + if (info->slave_is_enslaved || nm_device_sys_iface_state_is_external(slave) + || release_type >= RELEASE_SLAVE_TYPE_CONFIG_FORCE) + NM_DEVICE_GET_CLASS(self)->release_slave(self, + slave, + release_type >= RELEASE_SLAVE_TYPE_CONFIG); /* raise notifications about the release, including clearing is_enslaved. */ nm_device_slave_notify_release(slave, reason); @@ -6314,7 +6333,8 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink) plink_master = nm_platform_link_get(nm_device_get_platform(self), plink->master); plink_master_keep_alive = nmp_object_ref(NMP_OBJECT_UP_CAST(plink_master)); - if (master == NULL && plink_master && nm_streq0(plink_master->name, "ovs-system") + if (master == NULL && plink_master + && NM_IN_STRSET(plink_master->name, "ovs-system", "ovs-netdev") && plink_master->type == NM_LINK_TYPE_OPENVSWITCH) { _LOGD(LOGD_DEVICE, "the device claimed by openvswitch"); goto out; @@ -6330,11 +6350,10 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink) goto out; } - nm_device_master_release_one_slave(priv->master, - self, - FALSE, - FALSE, - NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + nm_device_master_release_slave(priv->master, + self, + RELEASE_SLAVE_TYPE_NO_CONFIG, + NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); } if (master && NM_DEVICE_GET_CLASS(master)->enslave_slave) { @@ -6638,7 +6657,7 @@ static void link_changed_cb(NMPlatform *platform, int obj_type_i, int ifindex, - NMPlatformLink *info, + NMPlatformLink *pllink, int change_type_i, NMDevice *self) { @@ -6651,7 +6670,7 @@ link_changed_cb(NMPlatform *platform, priv = NM_DEVICE_GET_PRIVATE(self); if (ifindex == nm_device_get_ifindex(self)) { - if (!(info->n_ifi_flags & IFF_UP)) + if (!(pllink->n_ifi_flags & IFF_UP)) priv->device_link_changed_down = TRUE; if (!priv->device_link_changed_id) { priv->device_link_changed_id = g_idle_add(device_link_changed, self); @@ -7004,7 +7023,7 @@ sriov_op_queue(NMDevice *self, * * FIXME(shutdown): However, during shutdown we don't have a follow-up write request to cancel * this operation and we have to give it at least some time to complete. The solution is that - * we register a way to abort the last call during shutdown, and after NM_SHUTDOWN_TIMEOUT_MS + * we register a way to abort the last call during shutdown, and after NM_SHUTDOWN_TIMEOUT_MAX_MSEC * grace period we pull the plug and cancel it. */ op = g_slice_new(SriovOp); @@ -7527,7 +7546,11 @@ slave_state_changed(NMDevice *slave, configure = priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED && nm_device_sys_iface_state_get(slave) != NM_DEVICE_SYS_IFACE_STATE_EXTERNAL; - nm_device_master_release_one_slave(self, slave, configure, FALSE, reason); + nm_device_master_release_slave(self, + slave, + configure ? RELEASE_SLAVE_TYPE_CONFIG + : RELEASE_SLAVE_TYPE_NO_CONFIG, + reason); /* Bridge/bond/team interfaces are left up until manually deactivated */ if (c_list_is_empty(&priv->slaves) && priv->state == NM_DEVICE_STATE_ACTIVATED) _LOGD(LOGD_DEVICE, "last slave removed; remaining activated"); @@ -7565,8 +7588,8 @@ nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure) info = find_slave_info(self, slave); _LOGT(LOGD_CORE, - "master: add one slave %p/%s%s", - slave, + "master: add one slave " NM_HASH_OBFUSCATE_PTR_FMT "/%s%s", + NM_HASH_OBFUSCATE_PTR(slave), nm_device_get_iface(slave), info ? " (already registered)" : ""); @@ -7624,14 +7647,12 @@ nm_device_master_check_slave_physical_port(NMDevice *self, NMDevice *slave, NMLo NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); const char *slave_physical_port_id, *existing_physical_port_id; SlaveInfo *info; - CList *iter; slave_physical_port_id = nm_device_get_physical_port_id(slave); if (!slave_physical_port_id) return; - c_list_for_each (iter, &priv->slaves) { - info = c_list_entry(iter, SlaveInfo, lst_slave); + c_list_for_each_entry (info, &priv->slaves, lst_slave) { if (info->slave == slave) continue; @@ -7651,13 +7672,13 @@ nm_device_master_check_slave_physical_port(NMDevice *self, NMDevice *slave, NMLo } } -/* release all slaves */ void -nm_device_master_release_slaves(NMDevice *self) +nm_device_master_release_slaves_all(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); NMDeviceStateReason reason; - CList *iter, *safe; + SlaveInfo *info; + SlaveInfo *safe; /* Don't release the slaves if this connection doesn't belong to NM. */ if (nm_device_sys_iface_state_is_external(self)) @@ -7667,9 +7688,7 @@ nm_device_master_release_slaves(NMDevice *self) if (priv->state == NM_DEVICE_STATE_FAILED) reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED; - c_list_for_each_safe (iter, safe, &priv->slaves) { - SlaveInfo *info = c_list_entry(iter, SlaveInfo, lst_slave); - + c_list_for_each_entry_safe (info, safe, &priv->slaves, lst_slave) { if (priv->activation_state_preserve_external_ports && nm_device_sys_iface_state_is_external(info->slave)) { _LOGT(LOGD_DEVICE, @@ -7677,7 +7696,7 @@ nm_device_master_release_slaves(NMDevice *self) nm_device_get_iface(info->slave)); continue; } - nm_device_master_release_one_slave(self, info->slave, TRUE, FALSE, reason); + nm_device_master_release_slave(self, info->slave, RELEASE_SLAVE_TYPE_CONFIG, reason); } /* We only need this flag for a short time. It served its purpose. Clear @@ -7852,11 +7871,10 @@ nm_device_removed(NMDevice *self, gboolean unconfigure_ip_config) if (priv->master) { /* this is called when something externally messes with the slave or during shut-down. * Release the slave from master, but don't touch the device. */ - nm_device_master_release_one_slave(priv->master, - self, - FALSE, - FALSE, - NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + nm_device_master_release_slave(priv->master, + self, + RELEASE_SLAVE_TYPE_NO_CONFIG, + NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); } _dev_l3_register_l3cds(self, priv->l3cfg, FALSE, unconfigure_ip_config); @@ -8928,11 +8946,10 @@ master_ready(NMDevice *self, NMActiveConnection *active) _LOGD(LOGD_DEVICE, "master connection ready; master device %s", nm_device_get_iface(master)); if (priv->master && priv->master != master) - nm_device_master_release_one_slave(priv->master, - self, - FALSE, - FALSE, - NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + nm_device_master_release_slave(priv->master, + self, + RELEASE_SLAVE_TYPE_NO_CONFIG, + NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); /* If the master didn't change, add-slave only rechecks whether to assume a connection. */ nm_device_master_add_slave(master, @@ -9214,11 +9231,10 @@ activate_stage1_device_prepare(NMDevice *self) if (master) master_ready(self, active); else if (priv->master) { - nm_device_master_release_one_slave(priv->master, - self, - TRUE, - TRUE, - NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + nm_device_master_release_slave(priv->master, + self, + RELEASE_SLAVE_TYPE_CONFIG_FORCE, + NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); } nm_device_activate_schedule_stage2_device_config(self, TRUE); @@ -9434,7 +9450,7 @@ activate_stage2_device_config(NMDevice *self) NMActStageReturn ret; NMSettingWired *s_wired; gboolean no_firmware = FALSE; - CList *iter; + SlaveInfo *info; NMTernary accept_all_mac_addresses; nm_device_state_changed(self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE); @@ -9481,8 +9497,7 @@ activate_stage2_device_config(NMDevice *self) } /* If we have slaves that aren't yet enslaved, do that now */ - c_list_for_each (iter, &priv->slaves) { - SlaveInfo *info = c_list_entry(iter, SlaveInfo, lst_slave); + c_list_for_each_entry (info, &priv->slaves, lst_slave) { NMDeviceState slave_state = nm_device_get_state(info->slave); if (slave_state == NM_DEVICE_STATE_IP_CONFIG) @@ -9517,8 +9532,6 @@ activate_stage2_device_config(NMDevice *self) lldp_setup(self, NM_TERNARY_DEFAULT); - _commit_mtu(self); - nm_device_activate_schedule_stage3_ip_config(self, TRUE); } @@ -10410,14 +10423,12 @@ have_any_ready_slaves(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); SlaveInfo *info; - CList *iter; /* Any enslaved slave is "ready" in the generic case as it's - * at least >= NM_DEVCIE_STATE_IP_CONFIG and has had Layer 2 + * at least >= NM_DEVICE_STATE_IP_CONFIG and has had Layer 2 * properties set up. */ - c_list_for_each (iter, &priv->slaves) { - info = c_list_entry(iter, SlaveInfo, lst_slave); + c_list_for_each_entry (info, &priv->slaves, lst_slave) { if (NM_DEVICE_GET_PRIVATE(info->slave)->is_enslaved) return TRUE; } @@ -10440,7 +10451,7 @@ void nm_device_use_ip6_subnet(NMDevice *self, const NMPlatformIP6Address *subnet) { nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; - char sbuf[sizeof(_nm_utils_to_string_buffer)]; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatformIP6Address address; l3cd = nm_device_create_l3_config_data(self, NM_IP_CONFIG_SOURCE_SHARED); @@ -11864,6 +11875,15 @@ activate_stage3_ip_config(NMDevice *self) nm_device_get_ip_iface(self)); } + /* We currently will attach ports in the state change NM_DEVICE_STATE_IP_CONFIG above. + * Note that kernel changes the MTU of bond ports, so we want to commit the MTU + * afterwards! + * + * This might reset the MTU to something different from the bond controller and + * it might not be a working configuration. But it's what the user asked for, so + * let's do it! */ + _commit_mtu(self); + ipv4_method = nm_device_get_effective_ip_config_method(self, AF_INET); if (nm_streq(ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { /* "auto" usually means DHCPv4 or autoconf6, but it doesn't have to be. Subclasses @@ -12455,6 +12475,7 @@ can_reapply_change(NMDevice *self, NM_SETTING_CONNECTION_UUID, NM_SETTING_CONNECTION_STABLE_ID, NM_SETTING_CONNECTION_AUTOCONNECT, + NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES, NM_SETTING_CONNECTION_ZONE, NM_SETTING_CONNECTION_METERED, NM_SETTING_CONNECTION_LLDP, @@ -15085,7 +15106,7 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { /* master: release slaves */ - nm_device_master_release_slaves(self); + nm_device_master_release_slaves_all(self); /* Take out any entries in the routing table and any IP address the device had. */ if (ifindex > 0) { @@ -15109,12 +15130,12 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu /* slave: mark no longer enslaved */ if (priv->master && priv->ifindex > 0 - && nm_platform_link_get_master(nm_device_get_platform(self), priv->ifindex) <= 0) - nm_device_master_release_one_slave(priv->master, - self, - FALSE, - FALSE, - NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + && nm_platform_link_get_master(nm_device_get_platform(self), priv->ifindex) <= 0) { + nm_device_master_release_slave(priv->master, + self, + RELEASE_SLAVE_TYPE_NO_CONFIG, + NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + } lldp_setup(self, NM_TERNARY_FALSE); @@ -15614,7 +15635,7 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, sett_conn ? nm_settings_connection_get_id(sett_conn) : "<unknown>"); /* Notify any slaves of the unexpected failure */ - nm_device_master_release_slaves(self); + nm_device_master_release_slaves_all(self); /* If the connection doesn't yet have a timestamp, set it to zero so that * we can distinguish between connections we've tried to activate and have @@ -17070,9 +17091,6 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) case PROP_TYPE_DESC: g_value_set_string(value, priv->type_desc); break; - case PROP_RFKILL_TYPE: - g_value_set_uint(value, priv->rfkill_type); - break; case PROP_AVAILABLE_CONNECTIONS: nm_dbus_utils_g_value_set_object_path_from_hash(value, priv->available_connections, TRUE); break; @@ -17208,10 +17226,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps /* construct-only */ priv->type_desc = g_value_dup_string(value); break; - case PROP_RFKILL_TYPE: - /* construct-only */ - priv->rfkill_type = g_value_get_uint(value); - break; case PROP_PERM_HW_ADDRESS: /* construct-only */ priv->hw_addr_perm = g_value_dup_string(value); @@ -17261,7 +17275,6 @@ nm_device_init(NMDevice *self) priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED; priv->state = NM_DEVICE_STATE_UNMANAGED; priv->state_reason = NM_DEVICE_STATE_REASON_NONE; - priv->rfkill_type = RFKILL_TYPE_UNKNOWN; priv->unmanaged_flags = NM_UNMANAGED_PLATFORM_INIT; priv->unmanaged_mask = priv->unmanaged_flags; priv->available_connections = g_hash_table_new_full(nm_direct_hash, NULL, g_object_unref, NULL); @@ -17664,6 +17677,8 @@ nm_device_class_init(NMDeviceClass *klass) klass->reapply_connection = reapply_connection; klass->set_platform_mtu = set_platform_mtu; + klass->rfkill_type = NM_RFKILL_TYPE_UNKNOWN; + obj_properties[PROP_UDI] = g_param_spec_string(NM_DEVICE_UDI, "", @@ -17821,14 +17836,6 @@ nm_device_class_init(NMDeviceClass *klass) "", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_RFKILL_TYPE] = - g_param_spec_uint(NM_DEVICE_RFKILL_TYPE, - "", - "", - RFKILL_TYPE_WLAN, - RFKILL_TYPE_MAX, - RFKILL_TYPE_UNKNOWN, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_properties[PROP_IFINDEX] = g_param_spec_int(NM_DEVICE_IFINDEX, "", "", diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h index a7badb86..80def125 100644 --- a/src/core/devices/nm-device.h +++ b/src/core/devices/nm-device.h @@ -65,7 +65,6 @@ #define NM_DEVICE_SLAVES "slaves" /* partially internal */ #define NM_DEVICE_TYPE_DESC "type-desc" /* Internal only */ -#define NM_DEVICE_RFKILL_TYPE "rfkill-type" /* Internal only */ #define NM_DEVICE_IFINDEX "ifindex" /* Internal only */ #define NM_DEVICE_MASTER "master" /* Internal only */ #define NM_DEVICE_HAS_PENDING_ACTION "has-pending-action" /* Internal only */ @@ -105,38 +104,38 @@ typedef enum NMActStageReturn NMActStageReturn; * a condition, so that adding a flag might make a connection available that would * not be available otherwise. Adding a flag should never make a connection * not available if it would be available otherwise. */ -typedef enum { /*< skip >*/ - NM_DEVICE_CHECK_CON_AVAILABLE_NONE = 0, +typedef enum { + NM_DEVICE_CHECK_CON_AVAILABLE_NONE = 0, - /* since NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST is a collection of flags with more fine grained + /* since NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST is a collection of flags with more fine grained * parts, this flag in general indicates that this is a user-request. */ - _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST = (1L << 0), + _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST = (1L << 0), - /* we also consider devices which have no carrier but are still waiting for the driver + /* we also consider devices which have no carrier but are still waiting for the driver * to detect carrier. Usually, such devices are not yet available, however for a user-request * they are. They might fail later if carrier doesn't come. */ - _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER = (1L << 1), + _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER = (1L << 1), - /* usually, a profile is only available if the Wi-Fi AP is in range. For an + /* usually, a profile is only available if the Wi-Fi AP is in range. For an * explicit user request, we also consider profiles for APs that are not (yet) * visible. */ - _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP = (1L << 2), + _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP = (1L << 2), - /* a device can be marked as unmanaged for various reasons. Some of these reasons + /* a device can be marked as unmanaged for various reasons. Some of these reasons * are authoritative, others not. Non-authoritative reasons can be overruled by * `nmcli device set $DEVICE managed yes`. Also, for an explicit user activation * request we may want to consider the device as managed. This flag makes devices * that are unmanaged appear available. */ - _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED = (1L << 3), + _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED = (1L << 3), - /* a collection of flags, that are commonly set for an explicit user-request. */ - NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST = - _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST - | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER - | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP - | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED, + /* a collection of flags, that are commonly set for an explicit user-request. */ + NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST = + _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST + | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER + | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP + | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED, - NM_DEVICE_CHECK_CON_AVAILABLE_ALL = (1L << 4) - 1, + NM_DEVICE_CHECK_CON_AVAILABLE_ALL = (1L << 4) - 1, } NMDeviceCheckConAvailableFlags; struct _NMDevicePrivate; @@ -149,19 +148,18 @@ struct _NMDevice { /* The flags have an relaxing meaning, that means, specifying more flags, can make * a device appear more available. It can never make a device less available. */ -typedef enum { /*< skip >*/ - NM_DEVICE_CHECK_DEV_AVAILABLE_NONE = 0, +typedef enum { + NM_DEVICE_CHECK_DEV_AVAILABLE_NONE = 0, - /* the device is considered available, even if it has no carrier. + /* the device is considered available, even if it has no carrier. * * For various device types (software devices) we ignore carrier based * on the type. So, for them, this flag has no effect anyway. */ - _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER = (1L << 0), + _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER = (1L << 0), - NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST = - _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER, + NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST = _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER, - NM_DEVICE_CHECK_DEV_AVAILABLE_ALL = (1L << 1) - 1, + NM_DEVICE_CHECK_DEV_AVAILABLE_ALL = (1L << 1) - 1, } NMDeviceCheckDevAvailableFlags; typedef void (*NMDeviceDeactivateCallback)(NMDevice *self, GError *error, gpointer user_data); @@ -209,6 +207,8 @@ typedef struct _NMDeviceClass { bool can_reapply_change_ovs_external_ids : 1; + NMRfkillType rfkill_type : 4; + void (*state_changed)(NMDevice *device, NMDeviceState new_state, NMDeviceState old_state, @@ -410,7 +410,6 @@ typedef struct _NMDeviceClass { gboolean (*set_platform_mtu)(NMDevice *self, guint32 mtu); const char *(*get_dhcp_anycast_address)(NMDevice *self); - } NMDeviceClass; GType nm_device_get_type(void); @@ -538,7 +537,7 @@ gboolean nm_device_get_enabled(NMDevice *device); void nm_device_set_enabled(NMDevice *device, gboolean enabled); -RfKillType nm_device_get_rfkill_type(NMDevice *device); +NMRfkillType nm_device_get_rfkill_type(NMDevice *device); /* IPv6 prefix delegation */ diff --git a/src/core/devices/ovs/nm-device-ovs-interface.c b/src/core/devices/ovs/nm-device-ovs-interface.c index 74707d24..6b9d3d7e 100644 --- a/src/core/devices/ovs/nm-device-ovs-interface.c +++ b/src/core/devices/ovs/nm-device-ovs-interface.c @@ -13,6 +13,7 @@ #include "devices/nm-device-private.h" #include "nm-active-connection.h" #include "nm-setting-connection.h" +#include "nm-setting-ovs-bridge.h" #include "nm-setting-ovs-interface.h" #include "nm-setting-ovs-port.h" @@ -23,7 +24,10 @@ typedef struct { NMOvsdb *ovsdb; - bool waiting_for_interface : 1; + GSource *wait_link_idle_source; + gulong wait_link_signal_id; + int wait_link_ifindex; + bool wait_link_is_waiting : 1; } NMDeviceOvsInterfacePrivate; struct _NMDeviceOvsInterface { @@ -115,10 +119,10 @@ link_changed(NMDevice *device, const NMPlatformLink *pllink) { NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(device); - if (!pllink || !priv->waiting_for_interface) + if (!pllink || !priv->wait_link_is_waiting) return; - priv->waiting_for_interface = FALSE; + priv->wait_link_is_waiting = FALSE; if (nm_device_get_state(device) == NM_DEVICE_STATE_IP_CONFIG) { if (!nm_device_hw_addr_set_cloned(device, @@ -199,17 +203,83 @@ ready_for_ip_config(NMDevice *device) return nm_device_get_ip_ifindex(device) > 0; } +static gboolean +_set_ip_ifindex_tun(gpointer user_data) +{ + NMDevice *device = user_data; + NMDeviceOvsInterface *self = NM_DEVICE_OVS_INTERFACE(device); + NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self); + + nm_clear_g_source_inst(&priv->wait_link_idle_source); + + priv->wait_link_is_waiting = FALSE; + nm_device_set_ip_ifindex(device, priv->wait_link_ifindex); + + nm_device_devip_set_state(device, AF_INET, NM_DEVICE_IP_STATE_PENDING, NULL); + nm_device_devip_set_state(device, AF_INET6, NM_DEVICE_IP_STATE_PENDING, NULL); + nm_device_activate_schedule_stage3_ip_config(device, FALSE); + + return G_SOURCE_CONTINUE; +} + +static void +_netdev_tun_link_cb(NMPlatform *platform, + int obj_type_i, + int ifindex, + NMPlatformLink *pllink, + int change_type_i, + NMDevice *device) +{ + const NMPlatformSignalChangeType change_type = change_type_i; + NMDeviceOvsInterface *self = NM_DEVICE_OVS_INTERFACE(device); + NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self); + + if (change_type == NM_PLATFORM_SIGNAL_ADDED) { + if (pllink->type == NM_LINK_TYPE_TUN + && nm_streq0(pllink->name, nm_device_get_iface(device))) { + nm_clear_g_signal_handler(platform, &priv->wait_link_signal_id); + + priv->wait_link_ifindex = ifindex; + + priv->wait_link_idle_source = nm_g_idle_add_source(_set_ip_ifindex_tun, device); + } + } +} + static void act_stage3_ip_config(NMDevice *device, int addr_family) { - NMDeviceOvsInterface *self = NM_DEVICE_OVS_INTERFACE(device); - NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self); + NMActiveConnection *controller_act = NULL; + NMSettingOvsBridge *s_ovs_bridge = NULL; + NMDeviceOvsInterface *self = NM_DEVICE_OVS_INTERFACE(device); + NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self); if (!_is_internal_interface(device)) { nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, NULL); return; } + /* When the ovs-bridge controller is using netdev datapath, the interface + * link created is a tun device instead of a ovs-interface. NetworkManager must + * detect the creation of the tun link and attach the ifindex to the + * ovs-interface device. */ + controller_act = NM_ACTIVE_CONNECTION(nm_device_get_act_request(device)); + if (controller_act && nm_device_get_ip_ifindex(device) <= 0 && priv->wait_link_signal_id == 0) { + controller_act = nm_active_connection_get_master(controller_act); + if (controller_act) { + controller_act = nm_active_connection_get_master(controller_act); + if (controller_act) + s_ovs_bridge = nm_connection_get_setting_ovs_bridge( + nm_active_connection_get_applied_connection(controller_act)); + if (s_ovs_bridge + && nm_streq0(nm_setting_ovs_bridge_get_datapath_type(s_ovs_bridge), "netdev")) + priv->wait_link_signal_id = g_signal_connect(nm_device_get_platform(device), + NM_PLATFORM_SIGNAL_LINK_CHANGED, + G_CALLBACK(_netdev_tun_link_cb), + self); + } + } + /* FIXME(l3cfg): we should create the IP ifindex before stage3 start. * * For now it's here because when the ovs-interface enters stage3, then it's added to the @@ -219,11 +289,15 @@ act_stage3_ip_config(NMDevice *device, int addr_family) * This should change. */ if (nm_device_get_ip_ifindex(device) <= 0) { _LOGT(LOGD_DEVICE, "waiting for link to appear"); - priv->waiting_for_interface = TRUE; + priv->wait_link_is_waiting = TRUE; nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_PENDING, NULL); return; } + priv->wait_link_is_waiting = FALSE; + nm_clear_g_source_inst(&priv->wait_link_idle_source); + nm_clear_g_signal_handler(nm_device_get_platform(device), &priv->wait_link_signal_id); + if (!nm_device_hw_addr_set_cloned(device, nm_device_get_applied_connection(device), FALSE)) { nm_device_devip_set_failed(device, addr_family, NM_DEVICE_STATE_REASON_CONFIG_FAILED); return; @@ -244,7 +318,8 @@ deactivate(NMDevice *device) NMDeviceOvsInterface *self = NM_DEVICE_OVS_INTERFACE(device); NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self); - priv->waiting_for_interface = FALSE; + priv->wait_link_is_waiting = FALSE; + nm_clear_g_source_inst(&priv->wait_link_idle_source); } typedef struct { @@ -351,7 +426,7 @@ deactivate_async(NMDevice *device, .callback_user_data = callback_user_data, }; - if (!priv->waiting_for_interface + if (!priv->wait_link_is_waiting && !nm_platform_link_get_by_ifname(nm_device_get_platform(device), nm_device_get_iface(device))) { _LOGT(LOGD_CORE, "deactivate: link not present, proceeding"); @@ -360,7 +435,9 @@ deactivate_async(NMDevice *device, return; } - if (priv->waiting_for_interface) { + nm_clear_g_source_inst(&priv->wait_link_idle_source); + + if (priv->wait_link_is_waiting) { /* At this point we have issued an INSERT and a DELETE * command for the interface to ovsdb. We don't know if * vswitchd will see the two updates or only one. We diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c index 7c45e0e4..44e16cb7 100644 --- a/src/core/devices/ovs/nm-ovsdb.c +++ b/src/core/devices/ovs/nm-ovsdb.c @@ -104,6 +104,7 @@ typedef struct { OvsdbMethodCallback callback; gpointer user_data; OvsdbMethodPayload payload; + GObject *shutdown_wait_obj; } OvsdbMethodCall; /*****************************************************************************/ @@ -245,6 +246,8 @@ static NM_UTILS_LOOKUP_STR_DEFINE(_device_type_to_table, static void _call_complete(OvsdbMethodCall *call, json_t *response, GError *error) { + g_clear_object(&call->shutdown_wait_obj); + if (response) { gs_free char *str = NULL; @@ -378,12 +381,14 @@ ovsdb_call_method(NMOvsdb *self, call = g_slice_new(OvsdbMethodCall); *call = (OvsdbMethodCall){ - .self = self, - .call_id = CALL_ID_UNSPEC, - .command = command, - .callback = callback, - .user_data = user_data, + .self = self, + .call_id = CALL_ID_UNSPEC, + .command = command, + .callback = callback, + .user_data = user_data, + .shutdown_wait_obj = g_object_new(G_TYPE_OBJECT, NULL), }; + nm_shutdown_wait_obj_register_object(call->shutdown_wait_obj, "ovsdb-call"); if (add_first) c_list_link_front(&priv->calls_lst_head, &call->calls_lst); @@ -2481,29 +2486,27 @@ typedef struct { static void _transact_cb(NMOvsdb *self, json_t *result, GError *error, gpointer user_data) { - OvsdbCall *call = user_data; - const char *err; - const char *err_details; - size_t index; - json_t *value; - - if (error) - goto out; - - json_array_foreach (result, index, value) { - if (json_unpack(value, "{s:s, s:s}", "error", &err, "details", &err_details) == 0) { - g_set_error(&error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "Error running the transaction: %s: %s", - err, - err_details); - goto out; + OvsdbCall *call = user_data; + gs_free_error GError *local = NULL; + const char *err; + const char *err_details; + size_t index; + json_t *value; + + if (!error) { + json_array_foreach (result, index, value) { + if (json_unpack(value, "{s:s, s:s}", "error", &err, "details", &err_details) == 0) { + local = g_error_new(G_IO_ERROR, + G_IO_ERROR_FAILED, + "Error running the transaction: %s: %s", + err, + err_details); + break; + } } } -out: - call->callback(error, call->user_data); + call->callback(local ?: error, call->user_data); nm_g_slice_free(call); } diff --git a/src/core/devices/wifi/nm-device-iwd-p2p.c b/src/core/devices/wifi/nm-device-iwd-p2p.c index 01774b12..40e38321 100644 --- a/src/core/devices/wifi/nm-device-iwd-p2p.c +++ b/src/core/devices/wifi/nm-device-iwd-p2p.c @@ -1196,8 +1196,6 @@ nm_device_iwd_p2p_new(GDBusObject *dbus_obj) NM_DEVICE_TYPE_WIFI_P2P, NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI, - NM_DEVICE_RFKILL_TYPE, - RFKILL_TYPE_WLAN, NULL); if (!self || !nm_device_iwd_p2p_set_dbus_obj(self, dbus_obj)) @@ -1252,6 +1250,8 @@ nm_device_iwd_p2p_class_init(NMDeviceIwdP2PClass *klass) device_class->state_changed = device_state_changed; + device_class->rfkill_type = NM_RFKILL_TYPE_WLAN; + obj_properties[PROP_PEERS] = g_param_spec_boxed(NM_DEVICE_IWD_P2P_PEERS, "", "", diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c index 7543d9da..ab37cbec 100644 --- a/src/core/devices/wifi/nm-device-iwd.c +++ b/src/core/devices/wifi/nm-device-iwd.c @@ -3426,8 +3426,6 @@ nm_device_iwd_new(const char *iface) NM_DEVICE_TYPE_WIFI, NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI, - NM_DEVICE_RFKILL_TYPE, - RFKILL_TYPE_WLAN, NULL); } @@ -3490,6 +3488,8 @@ nm_device_iwd_class_init(NMDeviceIwdClass *klass) device_class->state_changed = device_state_changed; + device_class->rfkill_type = NM_RFKILL_TYPE_WLAN; + obj_properties[PROP_MODE] = g_param_spec_uint(NM_DEVICE_IWD_MODE, "", "", diff --git a/src/core/devices/wifi/nm-device-wifi-p2p.c b/src/core/devices/wifi/nm-device-wifi-p2p.c index dfbf8978..4576af95 100644 --- a/src/core/devices/wifi/nm-device-wifi-p2p.c +++ b/src/core/devices/wifi/nm-device-wifi-p2p.c @@ -1229,8 +1229,6 @@ nm_device_wifi_p2p_new(const char *iface) NM_DEVICE_TYPE_WIFI_P2P, NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI, - NM_DEVICE_RFKILL_TYPE, - RFKILL_TYPE_WLAN, NULL); } @@ -1293,6 +1291,8 @@ nm_device_wifi_p2p_class_init(NMDeviceWifiP2PClass *klass) device_class->state_changed = device_state_changed; + device_class->rfkill_type = NM_RFKILL_TYPE_WLAN; + obj_properties[PROP_PEERS] = g_param_spec_boxed(NM_DEVICE_WIFI_P2P_PEERS, "", "", diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c index 010cee08..d83b1f35 100644 --- a/src/core/devices/wifi/nm-device-wifi.c +++ b/src/core/devices/wifi/nm-device-wifi.c @@ -978,14 +978,16 @@ deactivate_reset_hw_addr(NMDevice *device) static gboolean check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) { - NMDeviceWifi *self = NM_DEVICE_WIFI(device); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(self); - NMSettingWireless *s_wireless; - const char *mac; - const char *const *mac_blacklist; - int i; - const char *mode; - const char *perm_hw_addr; + NMDeviceWifi *self = NM_DEVICE_WIFI(device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(self); + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wsec; + const char *mac; + const char *const *mac_blacklist; + int i; + const char *mode; + const char *perm_hw_addr; + const char *key_mgmt; if (!NM_DEVICE_CLASS(nm_device_wifi_parent_class) ->check_connection_compatible(device, connection, error)) @@ -1070,6 +1072,20 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError * } } + s_wsec = nm_connection_get_setting_wireless_security(connection); + if (s_wsec) { + key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec); + + if (nm_supplicant_interface_get_capability(priv->sup_iface, NM_SUPPL_CAP_TYPE_WEP) + == NM_TERNARY_FALSE + && NM_IN_STRSET(key_mgmt, "ieee8021x", "none")) { + nm_utils_error_set_literal(error, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "wpa_supplicant does not support WEP encryption"); + return FALSE; + } + } + // FIXME: check channel/freq/band against bands the hardware supports // FIXME: check encryption against device capabilities // FIXME: check bitrate against device capabilities @@ -2936,14 +2952,16 @@ build_supplicant_config(NMDeviceWifi *self, } s_8021x = nm_connection_get_setting_802_1x(connection); - if (!nm_supplicant_config_add_setting_wireless_security(config, - s_wireless_sec, - s_8021x, - con_uuid, - mtu, - pmf, - fils, - error)) { + if (!nm_supplicant_config_add_setting_wireless_security( + config, + s_wireless_sec, + s_8021x, + con_uuid, + nm_setting_wireless_get_mode(s_wireless), + mtu, + pmf, + fils, + error)) { g_prefix_error(error, "802-11-wireless-security: "); goto error; } @@ -3103,28 +3121,75 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) static void ensure_hotspot_frequency(NMDeviceWifi *self, NMSettingWireless *s_wifi, NMWifiAP *ap) { - NMDevice *device = NM_DEVICE(self); - const char *band = nm_setting_wireless_get_band(s_wifi); - const guint32 a_freqs[] = {5180, 5200, 5220, 5745, 5765, 5785, 5805, 0}; - const guint32 bg_freqs[] = {2412, 2437, 2462, 2472, 0}; - guint32 freq = 0; + guint32 a_freqs[] = {5180, 5200, 5220, 5745, 5765, 5785, 5805, 0}; + guint32 bg_freqs[] = {2412, 2437, 2462, 2472, 0}; + guint32 *rnd_freqs; + guint rnd_freqs_len; + NMDevice *device = NM_DEVICE(self); + const char *band = nm_setting_wireless_get_band(s_wifi); + guint32 freq; + guint64 rnd; + guint i; + guint l; - g_assert(ap); + nm_assert(ap); + nm_assert(NM_IN_STRSET(band, NULL, "a", "bg")); if (nm_wifi_ap_get_freq(ap)) return; - if (g_strcmp0(band, "a") == 0) - freq = nm_platform_wifi_find_frequency(nm_device_get_platform(device), - nm_device_get_ifindex(device), - a_freqs); - else - freq = nm_platform_wifi_find_frequency(nm_device_get_platform(device), - nm_device_get_ifindex(device), - bg_freqs); + { + GBytes *ssid; + gsize ssid_len; + const guint8 *ssid_data; + const guint8 random_seed[16] = {0x9a, + 0xdc, + 0x86, + 0x9a, + 0xa8, + 0xa2, + 0x07, + 0x97, + 0xbe, + 0x6d, + 0xe6, + 0x99, + 0x9f, + 0xa8, + 0x09, + 0x2b}; + + /* Calculate a stable "random" number based on the SSID. */ + ssid = nm_setting_wireless_get_ssid(s_wifi); + ssid_data = g_bytes_get_data(ssid, &ssid_len); + rnd = c_siphash_hash(random_seed, ssid_data, ssid_len); + } + + if (nm_streq0(band, "a")) { + rnd_freqs = a_freqs; + rnd_freqs_len = G_N_ELEMENTS(a_freqs) - 1; + } else { + rnd_freqs = bg_freqs; + rnd_freqs_len = G_N_ELEMENTS(bg_freqs) - 1; + } + + /* shuffle the frequencies (inplace). The idea is to choose + * a different frequency depending on the SSID. */ + for (i = 0, l = rnd_freqs_len; l > 1; i++, l--) { + /* Add an arbitrary chosen (prime) number to rnd, to get more "random" + * numbers. Since we only shuffle a handful of elements, that's good + * enough (and stable). */ + rnd += 5630246189u; + NM_SWAP(&rnd_freqs[i], &rnd_freqs[i + (rnd % l)]); + } + + freq = nm_platform_wifi_find_frequency(nm_device_get_platform(device), + nm_device_get_ifindex(device), + rnd_freqs); + if (freq == 0) + freq = rnd_freqs[0]; - if (!freq) - freq = (g_strcmp0(band, "a") == 0) ? 5180 : 2462; + _LOGD(LOGD_WIFI, "set frequency for hotspot AP to %u", freq); if (nm_wifi_ap_set_freq(ap, freq)) _ap_dump(self, LOGL_DEBUG, ap, "updated", 0); @@ -3663,8 +3728,6 @@ nm_device_wifi_new(const char *iface, _NMDeviceWifiCapabilities capabilities) NM_DEVICE_TYPE_WIFI, NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI, - NM_DEVICE_RFKILL_TYPE, - RFKILL_TYPE_WLAN, NM_DEVICE_WIFI_CAPABILITIES, (guint) capabilities, NULL); @@ -3756,6 +3819,8 @@ nm_device_wifi_class_init(NMDeviceWifiClass *klass) device_class->state_changed = device_state_changed; + device_class->rfkill_type = NM_RFKILL_TYPE_WLAN; + obj_properties[PROP_MODE] = g_param_spec_uint(NM_DEVICE_WIFI_MODE, "", "", diff --git a/src/core/devices/wifi/nm-iwd-manager.c b/src/core/devices/wifi/nm-iwd-manager.c index 5563ebf8..27222aae 100644 --- a/src/core/devices/wifi/nm-iwd-manager.c +++ b/src/core/devices/wifi/nm-iwd-manager.c @@ -948,6 +948,7 @@ mirror_connection(NMIwdManager *self, if (!nm_settings_add_connection( priv->settings, + NULL, connection, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, NM_SETTINGS_CONNECTION_ADD_REASON_NONE, diff --git a/src/core/devices/wifi/nm-wifi-ap.c b/src/core/devices/wifi/nm-wifi-ap.c index ac0b748a..825a5aa8 100644 --- a/src/core/devices/wifi/nm-wifi-ap.c +++ b/src/core/devices/wifi/nm-wifi-ap.c @@ -1037,9 +1037,10 @@ nm_wifi_ap_lookup_for_device(NMDevice *device, const char *exported_path) g_return_val_if_fail(NM_IS_DEVICE(device), NULL); - ap = nm_dbus_manager_lookup_object(nm_dbus_object_get_manager(NM_DBUS_OBJECT(device)), - exported_path); - if (!ap || !NM_IS_WIFI_AP(ap) || ap->wifi_device != device) + ap = nm_dbus_manager_lookup_object_with_type(nm_dbus_object_get_manager(NM_DBUS_OBJECT(device)), + NM_TYPE_WIFI_AP, + exported_path); + if (!ap || ap->wifi_device != device) return NULL; return ap; diff --git a/src/core/devices/wifi/nm-wifi-p2p-peer.c b/src/core/devices/wifi/nm-wifi-p2p-peer.c index 0a174270..8ccb2a4d 100644 --- a/src/core/devices/wifi/nm-wifi-p2p-peer.c +++ b/src/core/devices/wifi/nm-wifi-p2p-peer.c @@ -139,10 +139,11 @@ nm_wifi_p2p_peer_lookup_for_device(NMDevice *device, const char *exported_path) g_return_val_if_fail(NM_IS_DEVICE(device), NULL); - peer = (NMWifiP2PPeer *) nm_dbus_manager_lookup_object( - nm_dbus_object_get_manager(NM_DBUS_OBJECT(device)), - exported_path); - if (!peer || !NM_IS_WIFI_P2P_PEER(peer) || peer->wifi_device != device) + peer = + nm_dbus_manager_lookup_object_with_type(nm_dbus_object_get_manager(NM_DBUS_OBJECT(device)), + NM_TYPE_WIFI_P2P_PEER, + exported_path); + if (!peer || peer->wifi_device != device) return NULL; return peer; diff --git a/src/core/devices/wwan/nm-device-modem.c b/src/core/devices/wwan/nm-device-modem.c index ef802a44..77ba1b24 100644 --- a/src/core/devices/wwan/nm-device-modem.c +++ b/src/core/devices/wwan/nm-device-modem.c @@ -181,8 +181,10 @@ modem_new_config(NMModem *modem, return; } - if (!IS_IPv4) + if (!IS_IPv4) { priv->iid = iid ? *iid : ((NMUtilsIPv6IfaceId) NM_UTILS_IPV6_IFACE_ID_INIT); + nm_device_sysctl_ip_conf_set(device, AF_INET6, "disable_ipv6", "0"); + } if (do_auto) { if (IS_IPv4) @@ -757,8 +759,6 @@ nm_device_modem_new(NMModem *modem) "Broadband", NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_MODEM, - NM_DEVICE_RFKILL_TYPE, - RFKILL_TYPE_WWAN, NM_DEVICE_MODEM_MODEM, modem, NM_DEVICE_MODEM_CAPABILITIES, @@ -840,6 +840,8 @@ nm_device_modem_class_init(NMDeviceModemClass *klass) device_class->state_changed = device_state_changed; + device_class->rfkill_type = NM_RFKILL_TYPE_WWAN; + obj_properties[PROP_MODEM] = g_param_spec_object(NM_DEVICE_MODEM_MODEM, "", diff --git a/src/core/devices/wwan/nm-modem-broadband.c b/src/core/devices/wwan/nm-modem-broadband.c index b585652e..cbf30f56 100644 --- a/src/core/devices/wwan/nm-modem-broadband.c +++ b/src/core/devices/wwan/nm-modem-broadband.c @@ -957,7 +957,7 @@ stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_metho const int IS_IPv4 = NM_IS_IPv4(addr_family); NMModemBroadband *self = NM_MODEM_BROADBAND(modem); nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; - char sbuf[sizeof(_nm_utils_to_string_buffer)]; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; gs_free_error GError *error = NULL; const char *data_port; const char *address_string; diff --git a/src/core/devices/wwan/nm-modem-ofono.c b/src/core/devices/wwan/nm-modem-ofono.c index cac90d32..0db30046 100644 --- a/src/core/devices/wwan/nm-modem-ofono.c +++ b/src/core/devices/wwan/nm-modem-ofono.c @@ -734,7 +734,7 @@ handle_settings(GVariant *v_dict, gpointer user_data) { NMModemOfono *self = NM_MODEM_OFONO(user_data); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); - char sbuf[sizeof(_nm_utils_to_string_buffer)]; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatformIP4Address address; gboolean ret = FALSE; const char *interface; diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c index 85e48ff7..2bfd7e01 100644 --- a/src/core/dhcp/nm-dhcp-client.c +++ b/src/core/dhcp/nm-dhcp-client.c @@ -109,25 +109,22 @@ nm_dhcp_client_get_pid(NMDhcpClient *self) return NM_DHCP_CLIENT_GET_PRIVATE(self)->pid; } -static void -_set_effective_client_id(NMDhcpClient *self, GBytes *client_id, gboolean take) +void +nm_dhcp_client_set_effective_client_id(NMDhcpClient *self, GBytes *client_id) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - nm_assert(!client_id || g_bytes_get_size(client_id) >= 2); + g_return_if_fail(NM_IS_DHCP_CLIENT(self)); + g_return_if_fail(!client_id || g_bytes_get_size(client_id) >= 2); + + priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - if (priv->effective_client_id == client_id - || (priv->effective_client_id && client_id - && g_bytes_equal(priv->effective_client_id, client_id))) { - if (take && client_id) - g_bytes_unref(client_id); + if (nm_g_bytes_equal0(priv->effective_client_id, client_id)) return; - } - if (priv->effective_client_id) - g_bytes_unref(priv->effective_client_id); + g_bytes_unref(priv->effective_client_id); priv->effective_client_id = client_id; - if (!take && client_id) + if (client_id) g_bytes_ref(client_id); { @@ -141,15 +138,6 @@ _set_effective_client_id(NMDhcpClient *self, GBytes *client_id, gboolean take) } } -void -nm_dhcp_client_set_effective_client_id(NMDhcpClient *self, GBytes *client_id) -{ - g_return_if_fail(NM_IS_DHCP_CLIENT(self)); - g_return_if_fail(!client_id || g_bytes_get_size(client_id) >= 2); - - _set_effective_client_id(self, client_id, FALSE); -} - /*****************************************************************************/ NM_UTILS_LOOKUP_STR_DEFINE(nm_dhcp_state_to_string, @@ -678,7 +666,7 @@ nm_dhcp_client_start_ip6(NMDhcpClient *self, GError **error) if (!priv->config.v6.enforce_duid) own_client_id = NM_DHCP_CLIENT_GET_CLASS(self)->get_duid(self); - _set_effective_client_id(self, own_client_id ?: priv->config.client_id, FALSE); + nm_dhcp_client_set_effective_client_id(self, own_client_id ?: priv->config.client_id); addr = ipv6_lladdr_find(self); if (!addr) { diff --git a/src/core/dhcp/nm-dhcp-dhcpcd.c b/src/core/dhcp/nm-dhcp-dhcpcd.c index 4b3429ef..280d9ce6 100644 --- a/src/core/dhcp/nm-dhcp-dhcpcd.c +++ b/src/core/dhcp/nm-dhcp-dhcpcd.c @@ -70,7 +70,7 @@ ip4_start(NMDhcpClient *client, GError **error) const NMDhcpClientConfig *client_config; gs_unref_ptrarray GPtrArray *argv = NULL; pid_t pid; - GError *local = NULL; + gs_free_error GError *local = NULL; gs_free char *cmd_str = NULL; const char *dhcpcd_path; @@ -143,7 +143,6 @@ ip4_start(NMDhcpClient *client, GError **error) NM_UTILS_ERROR_UNKNOWN, "dhcpcd failed to start: %s", local->message); - g_error_free(local); return FALSE; } diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c index d7fbe356..aac18967 100644 --- a/src/core/dhcp/nm-dhcp-nettools.c +++ b/src/core/dhcp/nm-dhcp-nettools.c @@ -154,6 +154,7 @@ static gboolean lease_parse_address(NDhcp4ClientLease *lease, NML3ConfigData *l3cd, GHashTable *options, + in_addr_t *out_address, GError **error) { struct in_addr a_address; @@ -268,6 +269,8 @@ lease_parse_address(NDhcp4ClientLease *lease, .preferred = a_lifetime, })); + NM_SET_OUT(out_address, a_address.s_addr); + return TRUE; } @@ -326,6 +329,7 @@ lease_parse_address_list(NDhcp4ClientLease *lease, static void lease_parse_routes(NDhcp4ClientLease *lease, NML3ConfigData *l3cd, + in_addr_t lease_address, GHashTable *options, NMStrBuf *sbuf) { @@ -343,6 +347,17 @@ lease_parse_routes(NDhcp4ClientLease *lease, int r; guint i; + /* Routes can be in option 33 (static-route), 121 (classless-static-route) and 249 (a non-standard classless-static-route). + * Option 249 (Microsoft Classless Static Route), is described here: + * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dhcpe/f9c19c79-1c7f-4746-b555-0c0fc523f3f9 + * + * We will anyway parse all these 3 options and add them to the "options" hash (as distinct entries). + * We will however also parse one of the options into the "l3cd" for configuring routing. + * Thereby we prefer 121 over 249 over 33. + * + * Preferring 121 over 33 is defined by RFC 3443. + * Preferring 121 over 249 over 33 is made up as it makes sense (the MS docs are not very clear). + */ for (i = 0; i < 2; i++) { const guint8 option_code = (i == 0) ? NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE : NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE; @@ -373,10 +388,11 @@ lease_parse_routes(NDhcp4ClientLease *lease, nm_l3_config_data_add_route_4(l3cd, &((const NMPlatformIP4Route){ + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, .network = dest, .plen = plen, .gateway = gateway, - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .pref_src = lease_address, .table_any = TRUE, .table_coerced = 0, .metric_any = TRUE, @@ -416,10 +432,11 @@ lease_parse_routes(NDhcp4ClientLease *lease, nm_l3_config_data_add_route_4(l3cd, &((const NMPlatformIP4Route){ + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, .network = dest, .plen = plen, .gateway = gateway, - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .pref_src = lease_address, .table_any = TRUE, .table_coerced = 0, .metric_any = TRUE, @@ -464,6 +481,7 @@ lease_parse_routes(NDhcp4ClientLease *lease, &((const NMPlatformIP4Route){ .rt_source = NM_IP_CONFIG_SOURCE_DHCP, .gateway = gateway, + .pref_src = lease_address, .table_any = TRUE, .table_coerced = 0, .metric_any = TRUE, @@ -547,6 +565,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, const char *v_str; guint16 v_u16; in_addr_t v_inaddr; + in_addr_t lease_address; struct in_addr v_inaddr_s; int r; @@ -556,7 +575,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, options = nm_dhcp_option_create_options_dict(); - if (!lease_parse_address(lease, l3cd, options, error)) + if (!lease_parse_address(lease, l3cd, options, &lease_address, error)) return NULL; r = n_dhcp4_client_lease_get_server_identifier(lease, &v_inaddr_s); @@ -575,7 +594,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, v_inaddr); } - lease_parse_routes(lease, l3cd, options, &sbuf); + lease_parse_routes(lease, l3cd, lease_address, options, &sbuf); lease_parse_address_list(lease, l3cd, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, options, &sbuf); diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c index 0884def3..14a121e7 100644 --- a/src/core/dhcp/nm-dhcp-systemd.c +++ b/src/core/dhcp/nm-dhcp-systemd.c @@ -81,16 +81,20 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, const struct in_addr *addr_list; char addr_str[NM_UTILS_INET_ADDRSTRLEN]; const char *s; - nm_auto_free_gstring GString *str = NULL; - nm_auto_free sd_dhcp_route **routes = NULL; - const char *const *search_domains = NULL; + nm_auto_free_gstring GString *str = NULL; + nm_auto_free sd_dhcp_route **routes_static = NULL; + nm_auto_free sd_dhcp_route **routes_classless = NULL; + const char *const *search_domains = NULL; + guint32 default_route_metric_offset; guint16 mtu; - int i, num; + int i; + int num; + int is_classless; + int n_routes_static; + int n_routes_classless; const void *data; gsize data_len; gboolean has_router_from_classless = FALSE; - gboolean has_classless_route = FALSE; - gboolean has_static_route = FALSE; const gint32 ts = nm_utils_get_monotonic_timestamp_sec(); gint64 ts_time = time(NULL); struct in_addr a_address; @@ -226,44 +230,27 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_HOST_NAME, s); } - num = sd_dhcp_lease_get_routes(lease, &routes); - if (num > 0) { - nm_auto_free_gstring GString *str_classless = NULL; - nm_auto_free_gstring GString *str_static = NULL; - guint32 default_route_metric_offset = 0; + default_route_metric_offset = 0; + n_routes_static = sd_dhcp_lease_get_static_routes(lease, &routes_static); + n_routes_classless = sd_dhcp_lease_get_classless_routes(lease, &routes_classless); + for (is_classless = 1; is_classless >= 0; is_classless--) { + int n_routes = (is_classless ? n_routes_classless : n_routes_static); + sd_dhcp_route *const *routes = (is_classless ? routes_classless : routes_static); - for (i = 0; i < num; i++) { - switch (sd_dhcp_route_get_option(routes[i])) { - case NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE: - has_classless_route = TRUE; - break; - case NM_DHCP_OPTION_DHCP4_STATIC_ROUTE: - has_static_route = TRUE; - break; - } - } + if (n_routes <= 0) + continue; - if (has_classless_route) - str_classless = g_string_sized_new(30); - if (has_static_route) - str_static = g_string_sized_new(30); + nm_gstring_prepare(&str); - for (i = 0; i < num; i++) { + for (i = 0; i < n_routes; i++) { char network_net_str[NM_UTILS_INET_ADDRSTRLEN]; char gateway_str[NM_UTILS_INET_ADDRSTRLEN]; guint8 r_plen; struct in_addr r_network; struct in_addr r_gateway; in_addr_t network_net; - int option; guint32 m; - option = sd_dhcp_route_get_option(routes[i]); - if (!NM_IN_SET(option, - NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE, - NM_DHCP_OPTION_DHCP4_STATIC_ROUTE)) - continue; - if (sd_dhcp_route_get_destination(routes[i], &r_network) < 0) continue; if (sd_dhcp_route_get_destination_prefix_length(routes[i], &r_plen) < 0 || r_plen > 32) @@ -275,31 +262,28 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, _nm_utils_inet4_ntop(network_net, network_net_str); _nm_utils_inet4_ntop(r_gateway.s_addr, gateway_str); - g_string_append_printf( - nm_gstring_add_space_delimiter(option == NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE - ? str_classless - : str_static), - "%s/%d %s", - network_net_str, - (int) r_plen, - gateway_str); + g_string_append_printf(nm_gstring_add_space_delimiter(str), + "%s/%d %s", + network_net_str, + (int) r_plen, + gateway_str); - if (option == NM_DHCP_OPTION_DHCP4_STATIC_ROUTE && has_classless_route) { + if (!is_classless && n_routes_classless > 0) { /* RFC 3443: if the DHCP server returns both a Classless Static Routes * option and a Static Routes option, the DHCP client MUST ignore the * Static Routes option. */ continue; } - if (r_plen == 0 && option == NM_DHCP_OPTION_DHCP4_STATIC_ROUTE) { - /* for option 33 (static route), RFC 2132 says: - * - * The default route (0.0.0.0) is an illegal destination for a static - * route. */ - continue; - } - if (r_plen == 0) { + if (!is_classless) { + /* for option 33 (static route), RFC 2132 says: + * + * The default route (0.0.0.0) is an illegal destination for a static + * route. */ + continue; + } + /* if there are multiple default routes, we add them with differing * metrics. */ m = default_route_metric_offset++; @@ -309,10 +293,11 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, nm_l3_config_data_add_route_4(l3cd, &((const NMPlatformIP4Route){ + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, .network = network_net, .plen = r_plen, .gateway = r_gateway.s_addr, - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .pref_src = a_address.s_addr, .metric_any = TRUE, .metric = m, .table_any = TRUE, @@ -320,21 +305,18 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, })); } - if (str_classless && str_classless->len > 0) - nm_dhcp_option_add_option(options, - AF_INET, - NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE, - str_classless->str); - if (str_static && str_static->len > 0) + if (str->len > 0) { nm_dhcp_option_add_option(options, AF_INET, - NM_DHCP_OPTION_DHCP4_STATIC_ROUTE, - str_static->str); + is_classless ? NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE + : NM_DHCP_OPTION_DHCP4_STATIC_ROUTE, + str->str); + } } num = sd_dhcp_lease_get_router(lease, &a_router); if (num > 0) { - guint32 default_route_metric_offset = 0; + default_route_metric_offset = 0; nm_gstring_prepare(&str); for (i = 0; i < num; i++) { @@ -366,6 +348,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, &((const NMPlatformIP4Route){ .rt_source = NM_IP_CONFIG_SOURCE_DHCP, .gateway = a_router[i].s_addr, + .pref_src = a_address.s_addr, .table_any = TRUE, .table_coerced = 0, .metric_any = TRUE, diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c index 214e94cd..a0eec6e0 100644 --- a/src/core/dhcp/nm-dhcp-utils.c +++ b/src/core/dhcp/nm-dhcp-utils.c @@ -28,7 +28,8 @@ static gboolean ip4_process_dhcpcd_rfc3442_routes(const char *iface, const char *str, NML3ConfigData *l3cd, - guint32 *gwaddr) + in_addr_t address, + guint32 *out_gwaddr) { gs_free const char **routes = NULL; const char **r; @@ -79,7 +80,7 @@ ip4_process_dhcpcd_rfc3442_routes(const char *iface, have_routes = TRUE; if (rt_cidr == 0 && rt_addr == 0) { /* FIXME: how to handle multiple routers? */ - *gwaddr = rt_route; + *out_gwaddr = rt_route; } else { _LOG2I(LOGD_DHCP4, iface, @@ -91,13 +92,13 @@ ip4_process_dhcpcd_rfc3442_routes(const char *iface, nm_l3_config_data_add_route_4( l3cd, &((const NMPlatformIP4Route){ + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, .network = nm_utils_ip4_address_clear_host_address(rt_addr, rt_cidr), .plen = rt_cidr, .gateway = rt_route, - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .pref_src = address, .metric_any = TRUE, .table_any = TRUE, - })); } } @@ -158,7 +159,8 @@ static gboolean ip4_process_dhclient_rfc3442_routes(const char *iface, const char *str, NML3ConfigData *l3cd, - guint32 *gwaddr) + in_addr_t address, + guint32 *out_gwaddr) { gs_free const char **octets = NULL; const char *const *o; @@ -182,13 +184,14 @@ ip4_process_dhclient_rfc3442_routes(const char *iface, have_routes = TRUE; if (!route.plen) { /* gateway passed as classless static route */ - *gwaddr = route.gateway; + *out_gwaddr = route.gateway; } else { char b1[INET_ADDRSTRLEN]; char b2[INET_ADDRSTRLEN]; /* normal route */ route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; + route.pref_src = address; route.table_any = TRUE; route.table_coerced = 0; route.metric_any = TRUE; @@ -212,14 +215,15 @@ static gboolean ip4_process_classless_routes(const char *iface, GHashTable *options, NML3ConfigData *l3cd, - guint32 *gwaddr) + in_addr_t address, + guint32 *out_gwaddr) { const char *str, *p; g_return_val_if_fail(options != NULL, FALSE); g_return_val_if_fail(l3cd != NULL, FALSE); - *gwaddr = 0; + *out_gwaddr = 0; /* dhcpd/dhclient in Fedora has support for rfc3442 implemented using a * slightly different format: @@ -266,14 +270,17 @@ ip4_process_classless_routes(const char *iface, if (strchr(str, '/')) { /* dhcpcd format */ - return ip4_process_dhcpcd_rfc3442_routes(iface, str, l3cd, gwaddr); + return ip4_process_dhcpcd_rfc3442_routes(iface, str, l3cd, address, out_gwaddr); } - return ip4_process_dhclient_rfc3442_routes(iface, str, l3cd, gwaddr); + return ip4_process_dhclient_rfc3442_routes(iface, str, l3cd, address, out_gwaddr); } static void -process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData *l3cd) +process_classful_routes(const char *iface, + GHashTable *options, + NML3ConfigData *l3cd, + in_addr_t address) { gs_free const char **searches = NULL; const char **s; @@ -293,6 +300,7 @@ process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData * } for (s = searches; *s; s += 2) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatformIP4Route route; guint32 rt_addr, rt_route; @@ -321,6 +329,7 @@ process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData * route.plen = 32; } route.gateway = rt_route; + route.pref_src = address; route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; route.table_any = TRUE; route.table_coerced = 0; @@ -334,7 +343,7 @@ process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData * _LOG2I(LOGD_DHCP, iface, " static route %s", - nm_platform_ip4_route_to_string(&route, NULL, 0)); + nm_platform_ip4_route_to_string(&route, sbuf, sizeof(sbuf))); } } @@ -422,8 +431,8 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, /* Routes: if the server returns classless static routes, we MUST ignore * the 'static_routes' option. */ - if (!ip4_process_classless_routes(iface, options, l3cd, &gateway)) - process_classful_routes(iface, options, l3cd); + if (!ip4_process_classless_routes(iface, options, l3cd, address.address, &gateway)) + process_classful_routes(iface, options, l3cd, address.address); if (gateway) { _LOG2I(LOGD_DHCP4, iface, " gateway %s", _nm_utils_inet4_ntop(gateway, sbuf)); @@ -453,6 +462,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, const NMPlatformIP4Route r = { .rt_source = NM_IP_CONFIG_SOURCE_DHCP, .gateway = gateway, + .pref_src = address.address, .table_any = TRUE, .table_coerced = 0, .metric_any = TRUE, diff --git a/src/core/dns/nm-dns-dnsmasq.c b/src/core/dns/nm-dns-dnsmasq.c index f0abf564..43426882 100644 --- a/src/core/dns/nm-dns-dnsmasq.c +++ b/src/core/dns/nm-dns-dnsmasq.c @@ -39,10 +39,10 @@ #define _NMLOG(level, ...) __NMLOG_DEFAULT(level, _NMLOG_DOMAIN, "dnsmasq", __VA_ARGS__) #define WAIT_MSEC_AFTER_SIGTERM 1000 -G_STATIC_ASSERT(WAIT_MSEC_AFTER_SIGTERM <= NM_SHUTDOWN_TIMEOUT_MS); +G_STATIC_ASSERT(WAIT_MSEC_AFTER_SIGTERM <= NM_SHUTDOWN_TIMEOUT_MAX_MSEC); #define WAIT_MSEC_AFTER_SIGKILL 400 -G_STATIC_ASSERT(WAIT_MSEC_AFTER_SIGKILL + 100 <= NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG); +G_STATIC_ASSERT(WAIT_MSEC_AFTER_SIGKILL + 100 <= NM_SHUTDOWN_TIMEOUT_ADDITIONAL_MSEC); typedef void (*GlPidSpawnAsyncNotify)(GCancellable *cancellable, GPid pid, @@ -848,7 +848,7 @@ static GVariant * create_update_args(NMDnsDnsmasq *self, const NMGlobalDnsConfig *global_config, const CList *ip_data_lst_head, - const char *hostname) + const char *hostdomain) { GVariantBuilder servers; const NMDnsConfigIPData *ip_data; @@ -1124,7 +1124,7 @@ static gboolean update(NMDnsPlugin *plugin, const NMGlobalDnsConfig *global_config, const CList *ip_data_lst_head, - const char *hostname, + const char *hostdomain, GError **error) { NMDnsDnsmasq *self = NM_DNS_DNSMASQ(plugin); @@ -1135,7 +1135,7 @@ update(NMDnsPlugin *plugin, nm_clear_pointer(&priv->set_server_ex_args, g_variant_unref); priv->set_server_ex_args = - g_variant_ref_sink(create_update_args(self, global_config, ip_data_lst_head, hostname)); + g_variant_ref_sink(create_update_args(self, global_config, ip_data_lst_head, hostdomain)); send_dnsmasq_update(self); return TRUE; diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c index efbf847b..566f3d66 100644 --- a/src/core/dns/nm-dns-manager.c +++ b/src/core/dns/nm-dns-manager.c @@ -26,6 +26,7 @@ #include "libnm-core-intern/nm-core-internal.h" #include "libnm-glib-aux/nm-str-buf.h" +#include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "NetworkManagerUtils.h" #include "devices/nm-device.h" @@ -97,7 +98,7 @@ typedef struct { bool config_changed : 1; - char *hostname; + char *hostdomain; guint updates_queue; guint8 hash[HASH_LEN]; /* SHA1 hash of current DNS config */ @@ -1260,24 +1261,8 @@ _collect_resolv_conf_data(NMDnsManager *self, } } - /* If the hostname is a FQDN ("dcbw.example.com"), then add the domain part of it - * ("example.com") to the searches list, to ensure that we can still resolve its - * non-FQ form ("dcbw") too. (Also, if there are no other search domains specified, - * this makes a good default.) However, if the hostname is the top level of a domain - * (eg, "example.com"), then use the hostname itself as the search (since the user is - * unlikely to want "com" as a search domain). - */ - if (priv->hostname) { - const char *hostdomain = strchr(priv->hostname, '.'); - - if (hostdomain && !nm_utils_ipaddr_is_valid(AF_UNSPEC, priv->hostname)) { - hostdomain++; - if (domain_is_valid(hostdomain, TRUE)) - add_string_item(rc.searches, hostdomain, TRUE); - else if (domain_is_valid(priv->hostname, TRUE)) - add_string_item(rc.searches, priv->hostname, TRUE); - } - } + if (priv->hostdomain) + add_string_item(rc.searches, priv->hostdomain, TRUE); if (rc.has_trust_ad == NM_TERNARY_TRUE) g_ptr_array_add(rc.options, g_strdup(NM_SETTING_DNS_OPTION_TRUST_AD)); @@ -1695,7 +1680,7 @@ update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError nm_dns_plugin_update(priv->sd_resolve_plugin, global_config, _mgr_get_ip_data_lst_head(self), - priv->hostname, + priv->hostdomain, NULL); } @@ -1717,7 +1702,7 @@ update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError if (!nm_dns_plugin_update(plugin, global_config, _mgr_get_ip_data_lst_head(self), - priv->hostname, + priv->hostdomain, &plugin_error)) { _LOGW("update-dns: plugin %s update failed: %s", plugin_name, plugin_error->message); @@ -2003,27 +1988,39 @@ done: } void -nm_dns_manager_set_initial_hostname(NMDnsManager *self, const char *hostname) -{ - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); - - g_free(priv->hostname); - priv->hostname = g_strdup(hostname); -} - -void nm_dns_manager_set_hostname(NMDnsManager *self, const char *hostname, gboolean skip_update) { - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); + const char *domain = NULL; /* Certain hostnames we don't want to include in resolv.conf 'searches' */ - if (hostname && nm_utils_is_specific_hostname(hostname) && !strstr(hostname, ".in-addr.arpa") - && strchr(hostname, '.')) { - /* pass */ - } else - hostname = NULL; + if (hostname && nm_utils_is_specific_hostname(hostname) + && !g_str_has_suffix(hostname, ".in-addr.arpa") + && !nm_utils_ipaddr_is_valid(AF_UNSPEC, hostname)) { + domain = strchr(hostname, '.'); + if (domain) { + domain++; + /* If the hostname is a FQDN ("dcbw.example.com"), then add + * the domain part of it ("example.com") to the searches list, + * to ensure that we can still resolve its non-FQ form + * ("dcbw") too. (Also, if there are no other search domains + * specified, this makes a good default.) However, if the + * hostname is the top level of a domain (eg, "example.com"), + * then use the hostname itself as the search (since the user + * is unlikely to want "com" as a search domain).a + */ + if (domain_is_valid(domain, TRUE)) { + /* pass */ + } else if (domain_is_valid(hostname, TRUE)) { + domain = hostname; + } + + if (!nm_sd_hostname_is_valid(domain, FALSE)) + domain = NULL; + } + } - if (!nm_strdup_reset(&priv->hostname, hostname)) + if (!nm_strdup_reset(&priv->hostdomain, domain)) return; if (skip_update) @@ -2668,7 +2665,7 @@ finalize(GObject *object) NMDnsManager *self = NM_DNS_MANAGER(object); NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); - g_free(priv->hostname); + g_free(priv->hostdomain); g_free(priv->mode); G_OBJECT_CLASS(nm_dns_manager_parent_class)->finalize(object); diff --git a/src/core/dns/nm-dns-manager.h b/src/core/dns/nm-dns-manager.h index 84821ece..c30d4b3a 100644 --- a/src/core/dns/nm-dns-manager.h +++ b/src/core/dns/nm-dns-manager.h @@ -105,7 +105,6 @@ gboolean nm_dns_manager_set_ip_config(NMDnsManager *self, NMDnsIPConfigType ip_config_type, gboolean replace_all); -void nm_dns_manager_set_initial_hostname(NMDnsManager *self, const char *hostname); void nm_dns_manager_set_hostname(NMDnsManager *self, const char *hostname, gboolean skip_update); /** diff --git a/src/core/dns/nm-dns-plugin.c b/src/core/dns/nm-dns-plugin.c index 2126da77..847d7839 100644 --- a/src/core/dns/nm-dns-plugin.c +++ b/src/core/dns/nm-dns-plugin.c @@ -60,7 +60,7 @@ gboolean nm_dns_plugin_update(NMDnsPlugin *self, const NMGlobalDnsConfig *global_config, const CList *ip_config_lst_head, - const char *hostname, + const char *hostdomain, GError **error) { g_return_val_if_fail(NM_DNS_PLUGIN_GET_CLASS(self)->update != NULL, FALSE); @@ -68,7 +68,7 @@ nm_dns_plugin_update(NMDnsPlugin *self, return NM_DNS_PLUGIN_GET_CLASS(self)->update(self, global_config, ip_config_lst_head, - hostname, + hostdomain, error); } diff --git a/src/core/dns/nm-dns-plugin.h b/src/core/dns/nm-dns-plugin.h index bc854cce..f9c424ab 100644 --- a/src/core/dns/nm-dns-plugin.h +++ b/src/core/dns/nm-dns-plugin.h @@ -34,7 +34,7 @@ typedef struct { gboolean (*update)(NMDnsPlugin *self, const NMGlobalDnsConfig *global_config, const CList *ip_config_lst_head, - const char *hostname, + const char *hostdomain, GError **error); void (*stop)(NMDnsPlugin *self); diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c index a043e855..c4993884 100644 --- a/src/core/dns/nm-dns-systemd-resolved.c +++ b/src/core/dns/nm-dns-systemd-resolved.c @@ -551,7 +551,7 @@ static gboolean update(NMDnsPlugin *plugin, const NMGlobalDnsConfig *global_config, const CList *ip_data_lst_head, - const char *hostname, + const char *hostdomain, GError **error) { NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(plugin); diff --git a/src/core/dns/nm-dns-unbound.c b/src/core/dns/nm-dns-unbound.c index f5a53332..8a75cf08 100644 --- a/src/core/dns/nm-dns-unbound.c +++ b/src/core/dns/nm-dns-unbound.c @@ -28,7 +28,7 @@ static gboolean update(NMDnsPlugin *plugin, const NMGlobalDnsConfig *global_config, const CList *ip_config_lst_head, - const char *hostname, + const char *hostdomain, GError **error) { char *argv[] = {DNSSEC_TRIGGER_PATH, "--async", "--update", NULL}; diff --git a/src/core/dnsmasq/nm-dnsmasq-manager.c b/src/core/dnsmasq/nm-dnsmasq-manager.c index 4ab91e7e..be463da3 100644 --- a/src/core/dnsmasq/nm-dnsmasq-manager.c +++ b/src/core/dnsmasq/nm-dnsmasq-manager.c @@ -67,9 +67,12 @@ dm_watch_cb(GPid pid, int status, gpointer user_data) guint err; if (WIFEXITED(status)) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + err = WEXITSTATUS(status); if (err != 0) { - _LOGW("dnsmasq exited with error: %s", nm_utils_dnsmasq_status_to_string(err, NULL, 0)); + _LOGW("dnsmasq exited with error: %s", + nm_utils_dnsmasq_status_to_string(err, sbuf, sizeof(sbuf))); } } else if (WIFSTOPPED(status)) { _LOGW("dnsmasq stopped unexpectedly with signal %d", WSTOPSIG(status)); @@ -188,8 +191,6 @@ create_dm_cmd_line(const char *iface, nm_strv_ptrarray_add_string_dup(cmd, "--dhcp-option-force=43,ANDROID_METERED"); } - nm_strv_ptrarray_add_string_dup(cmd, "--dhcp-lease-max=50"); - nm_strv_ptrarray_add_string_printf(cmd, "--dhcp-leasefile=%s/dnsmasq-%s.leases", NMSTATEDIR, diff --git a/src/core/main.c b/src/core/main.c index e39a9614..1a9c3a65 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -302,8 +302,6 @@ main(int argc, char *argv[]) g_type_ensure(G_TYPE_DBUS_CONNECTION); g_type_ensure(NM_TYPE_DBUS_MANAGER); - main_loop = g_main_loop_new(NULL, FALSE); - /* we determine a first-start (contrary to a restart during the same boot) * based on the existence of NM_CONFIG_DEVICE_STATE_DIR directory. */ config_cli = nm_config_cmd_line_options_new( @@ -403,6 +401,8 @@ main(int argc, char *argv[]) wrote_pidfile = nm_main_utils_write_pidfile(global_opt.pidfile); } + main_loop = g_main_loop_new(NULL, FALSE); + /* Set up unix signal handling - before creating threads, but after daemonizing! */ nm_main_utils_setup_signals(main_loop); diff --git a/src/core/ndisc/nm-ndisc.c b/src/core/ndisc/nm-ndisc.c index 969eacfa..04b673e5 100644 --- a/src/core/ndisc/nm-ndisc.c +++ b/src/core/ndisc/nm-ndisc.c @@ -996,6 +996,7 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd) const NMPObject *obj; guint len; guint i; + gint32 fake_now = NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000; nm_assert(NM_IS_NDISC(ndisc)); nm_assert(nm_ndisc_get_node_type(ndisc) == NM_NDISC_NODE_TYPE_ROUTER); @@ -1018,7 +1019,7 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd) lifetime = nmp_utils_lifetime_get(addr->timestamp, addr->lifetime, addr->preferred, - NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000, + &fake_now, &preferred); if (!lifetime) continue; diff --git a/src/core/nm-checkpoint-manager.c b/src/core/nm-checkpoint-manager.c index 4a3e7aaf..6c9ea2a6 100644 --- a/src/core/nm-checkpoint-manager.c +++ b/src/core/nm-checkpoint-manager.c @@ -259,10 +259,11 @@ nm_checkpoint_manager_lookup_by_path(NMCheckpointManager *self, const char *path g_return_val_if_fail(self, NULL); - checkpoint = - nm_dbus_manager_lookup_object(nm_dbus_object_get_manager(NM_DBUS_OBJECT(GET_MANAGER(self))), - path); - if (!checkpoint || !NM_IS_CHECKPOINT(checkpoint)) { + checkpoint = nm_dbus_manager_lookup_object_with_type( + nm_dbus_object_get_manager(NM_DBUS_OBJECT(GET_MANAGER(self))), + NM_TYPE_CHECKPOINT, + path); + if (!checkpoint) { g_set_error(error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS, diff --git a/src/core/nm-checkpoint.c b/src/core/nm-checkpoint.c index 5b48f91a..15667332 100644 --- a/src/core/nm-checkpoint.c +++ b/src/core/nm-checkpoint.c @@ -231,6 +231,7 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP; nm_settings_connection_update( connection, + NULL, dev_checkpoint->settings_connection, persist_mode, sett_flags, @@ -247,6 +248,7 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; if (!nm_settings_add_connection(NM_SETTINGS_GET, + NULL, dev_checkpoint->settings_connection, persist_mode, NM_SETTINGS_CONNECTION_ADD_REASON_NONE, diff --git a/src/core/nm-config.c b/src/core/nm-config.c index 60a2f1df..ea1c2ab3 100644 --- a/src/core/nm-config.c +++ b/src/core/nm-config.c @@ -2870,6 +2870,7 @@ _set_config_data(NMConfig *self, NMConfigData *new_data, NMConfigChangeFlags rel NMConfigData *old_data = priv->config_data; NMConfigChangeFlags changes, changes_diff; gboolean had_new_data = !!new_data; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; nm_assert(reload_flags); nm_assert(!NM_FLAGS_ANY(reload_flags, ~NM_CONFIG_CHANGE_CAUSES)); @@ -2901,15 +2902,15 @@ _set_config_data(NMConfig *self, NMConfigData *new_data, NMConfigChangeFlags rel if (new_data) { _LOGI("signal: %s (%s)", - nm_config_change_flags_to_string(changes, NULL, 0), + nm_config_change_flags_to_string(changes, sbuf, sizeof(sbuf)), nm_config_data_get_config_description(new_data)); nm_config_data_log(new_data, "CONFIG: ", " ", priv->no_auto_default_file, NULL); priv->config_data = new_data; } else if (had_new_data) _LOGI("signal: %s (no changes from disk)", - nm_config_change_flags_to_string(changes, NULL, 0)); + nm_config_change_flags_to_string(changes, sbuf, sizeof(sbuf))); else - _LOGI("signal: %s", nm_config_change_flags_to_string(changes, NULL, 0)); + _LOGI("signal: %s", nm_config_change_flags_to_string(changes, sbuf, sizeof(sbuf))); g_signal_emit(self, signals[SIGNAL_CONFIG_CHANGED], 0, new_data ?: old_data, changes, old_data); if (new_data) g_object_unref(old_data); diff --git a/src/core/nm-connectivity.c b/src/core/nm-connectivity.c index c84d8a56..eea8adb2 100644 --- a/src/core/nm-connectivity.c +++ b/src/core/nm-connectivity.c @@ -622,6 +622,75 @@ _timeout_cb(gpointer user_data) cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "timeout"); return G_SOURCE_REMOVE; } + +static gboolean +easy_debug_enabled(void) +{ + static int enabled = 0; + int e; + + /* libcurl debug logging can be useful, but is very verbose. + * Only enable it when we have a certain environment variable set. */ + +again: + e = g_atomic_int_get(&enabled); + if (G_UNLIKELY(e == 0)) { + e = _nm_utils_ascii_str_to_bool(g_getenv("NM_LOG_CONCHECK"), FALSE) ? 1 : -1; + if (G_UNLIKELY(!g_atomic_int_compare_and_exchange(&enabled, 0, e))) + goto again; + } + return e >= 0; +} + +static int +easy_debug_cb(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr) +{ + NMConnectivityCheckHandle *cb_data = userptr; + gs_free char *data_escaped = NULL; + const char *msg; + gboolean print_data = FALSE; + + switch (type) { + case CURLINFO_TEXT: + print_data = TRUE; + msg = "== Info: "; + break; + case CURLINFO_DATA_OUT: + msg = "=> Send data"; + break; + case CURLINFO_SSL_DATA_OUT: + msg = "=> Send SSL data"; + break; + case CURLINFO_HEADER_IN: + msg = "<= Recv header"; + break; + case CURLINFO_DATA_IN: + msg = "<= Recv data"; + break; + case CURLINFO_SSL_DATA_IN: + msg = "<= Recv SSL data"; + break; + case CURLINFO_HEADER_OUT: + print_data = TRUE; + msg = "=> Send header: "; + return 0; + default: + return 0; + } + + _LOG2T("libcurl %s%s%s%s", + msg, + NM_PRINT_FMT_QUOTED(print_data, + "[", + (data_escaped = nm_utils_buf_utf8safe_escape_cp( + data, + size, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)) + ?: "", + "]", + "")); + return 0; +} #endif static gboolean @@ -641,12 +710,14 @@ _idle_cb(gpointer user_data) #if WITH_CONCHECK static void -do_curl_request(NMConnectivityCheckHandle *cb_data) +do_curl_request(NMConnectivityCheckHandle *cb_data, const char *hosts) { CURLM *mhandle; CURL *ehandle; long resolve; + _LOG2T("set curl resolve list to '%s'", hosts); + mhandle = curl_multi_init(); if (!mhandle) { cb_data_complete(cb_data, NM_CONNECTIVITY_ERROR, "curl error"); @@ -660,6 +731,8 @@ do_curl_request(NMConnectivityCheckHandle *cb_data) return; } + cb_data->concheck.hosts = curl_slist_append(NULL, hosts); + cb_data->concheck.curl_mhandle = mhandle; cb_data->concheck.curl_ehandle = ehandle; cb_data->concheck.request_headers = curl_slist_append(NULL, "Connection: close"); @@ -696,12 +769,103 @@ do_curl_request(NMConnectivityCheckHandle *cb_data) curl_easy_setopt(ehandle, CURLOPT_RESOLVE, cb_data->concheck.hosts); curl_easy_setopt(ehandle, CURLOPT_IPRESOLVE, resolve); curl_easy_setopt(ehandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + if (_LOGT_ENABLED() && easy_debug_enabled()) { + curl_easy_setopt(ehandle, CURLOPT_DEBUGFUNCTION, easy_debug_cb); + curl_easy_setopt(ehandle, CURLOPT_DEBUGDATA, cb_data); + curl_easy_setopt(ehandle, CURLOPT_VERBOSE, 1L); + } curl_multi_add_handle(mhandle, ehandle); } static void -resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data) +system_resolver_resolve_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + nm_auto_str_buf NMStrBuf strbuf_hosts = NM_STR_BUF_INIT(0, FALSE); + gs_free_error GError *resolv_error = NULL; + GList *list; + GList *iter; + NMConnectivityCheckHandle *cb_data; + + list = g_resolver_lookup_by_name_finish(G_RESOLVER(source_object), res, &resolv_error); + + if (nm_utils_error_is_cancelled(resolv_error)) + return; + + cb_data = user_data; + + nm_assert(cb_data); + nm_assert(NM_IS_CONNECTIVITY(cb_data->self)); + + if (resolv_error) { + _LOG2D("failure to resolve name: %s", resolv_error->message); + cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "resolve-error"); + return; + } + + for (iter = list; iter; iter = iter->next) { + GInetAddress *a = iter->data; + char str_addr[NM_UTILS_INET_ADDRSTRLEN]; + int addr_family; + + switch (g_inet_address_get_family(a)) { + case G_SOCKET_FAMILY_IPV4: + addr_family = AF_INET; + break; + case G_SOCKET_FAMILY_IPV6: + addr_family = AF_INET6; + break; + default: + addr_family = AF_UNSPEC; + break; + } + if (cb_data->addr_family != AF_UNSPEC && cb_data->addr_family != addr_family) + continue; + + if (strbuf_hosts.len == 0) { + nm_str_buf_append_printf(&strbuf_hosts, + "%s:%s:", + cb_data->concheck.con_config->host, + cb_data->concheck.con_config->port ?: "80"); + } else + nm_str_buf_append_c(&strbuf_hosts, ','); + + nm_str_buf_append(&strbuf_hosts, + nm_utils_inet_ntop(addr_family, g_inet_address_to_bytes(a), str_addr)); + } + + g_list_free_full(list, g_object_unref); + + if (strbuf_hosts.len == 0) { + _LOG2D("system resolver returned no usable IPv%c addresses", + nm_utils_addr_family_to_char(cb_data->addr_family)); + cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "resolve-error"); + return; + } + + do_curl_request(cb_data, nm_str_buf_get_str(&strbuf_hosts)); +} + +static void +system_resolver_resolve(NMConnectivityCheckHandle *cb_data) +{ + gs_unref_object GResolver *resolver = NULL; + + _LOG2D("start request to '%s' (try resolving '%s' using system resolver)", + cb_data->concheck.con_config->uri, + cb_data->concheck.con_config->host); + + resolver = g_resolver_get_default(); + + g_resolver_lookup_by_name_async(resolver, + cb_data->concheck.con_config->host, + cb_data->concheck.resolve_cancellable, + system_resolver_resolve_cb, + cb_data); +} + +static void +systemd_resolved_resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data) { NMConnectivityCheckHandle *cb_data; gs_unref_variant GVariant *result = NULL; @@ -715,17 +879,24 @@ resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data) nm_auto_str_buf NMStrBuf strbuf_hosts = NM_STR_BUF_INIT(0, FALSE); result = g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), res, &error); - if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + if (nm_utils_error_is_cancelled(error)) return; cb_data = user_data; - g_clear_object(&cb_data->concheck.resolve_cancellable); - if (!result) { - /* Never mind. Just let do curl do its own resolving. */ + gs_free char *dbus_error = NULL; + _LOG2D("can't resolve a name via systemd-resolved: %s", error->message); - do_curl_request(cb_data); + + dbus_error = g_dbus_error_get_remote_error(error); + if (nm_streq0(dbus_error, "org.freedesktop.resolve1.NoNameServers")) { + cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "resolve-error"); + return; + } + + /* Never mind. Fallback to the system resolver. */ + system_resolver_resolve(cb_data); return; } @@ -759,14 +930,14 @@ resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data) nm_str_buf_append(&strbuf_hosts, nm_utils_inet_ntop(addr_family, address_buf, str_addr)); } - if (strbuf_hosts.len > 0) { - const char *s = nm_str_buf_get_str(&strbuf_hosts); - - cb_data->concheck.hosts = curl_slist_append(NULL, s); - _LOG2T("set curl resolve list to '%s'", s); + if (strbuf_hosts.len == 0) { + _LOG2D("systemd-resolve returned no usable IPv%c addresses", + nm_utils_addr_family_to_char(cb_data->addr_family)); + cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "resolve-error"); + return; } - do_curl_request(cb_data); + do_curl_request(cb_data, nm_str_buf_get_str(&strbuf_hosts)); } #endif @@ -892,6 +1063,8 @@ nm_connectivity_check_start(NMConnectivity *self, } } + cb_data->concheck.resolve_cancellable = g_cancellable_new(); + /* note that we pick up support for systemd-resolved right away when we need it. * We don't need to remember the setting, because we can (cheaply) check anew * on each request. @@ -927,8 +1100,6 @@ nm_connectivity_check_start(NMConnectivity *self, return cb_data; } - cb_data->concheck.resolve_cancellable = g_cancellable_new(); - g_dbus_connection_call(dbus_connection, "org.freedesktop.resolve1", "/org/freedesktop/resolve1", @@ -943,17 +1114,15 @@ nm_connectivity_check_start(NMConnectivity *self, G_DBUS_CALL_FLAGS_NONE, -1, cb_data->concheck.resolve_cancellable, - resolve_cb, + systemd_resolved_resolve_cb, cb_data); _LOG2D("start request to '%s' (try resolving '%s' using systemd-resolved)", cb_data->concheck.con_config->uri, cb_data->concheck.con_config->host); - } else { - _LOG2D("start request to '%s' (systemd-resolved not available)", - cb_data->concheck.con_config->uri); - do_curl_request(cb_data); + return cb_data; } + system_resolver_resolve(cb_data); return cb_data; } #endif diff --git a/src/core/nm-core-utils.c b/src/core/nm-core-utils.c index e05638e4..c8b789b3 100644 --- a/src/core/nm-core-utils.c +++ b/src/core/nm-core-utils.c @@ -30,6 +30,7 @@ #include "libnm-glib-aux/nm-secret-utils.h" #include "libnm-glib-aux/nm-time-utils.h" #include "libnm-glib-aux/nm-str-buf.h" +#include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "nm-utils.h" #include "libnm-core-intern/nm-core-internal.h" #include "nm-setting-connection.h" @@ -2692,8 +2693,9 @@ _host_id_read_timestamp(gboolean use_secret_key_file, * timestamp. It is wrong to worry about using a fake timestamp (which is tied to * the secret_key) if we are unable to access the secret_key file in the first place. * - * Pick a random timestamp from the past two years. Yes, this timestamp - * is not stable across restarts, but apparently neither is the host-id + * Pick a timestamp from the past two years, by using a generated timespan + * by hashing the host-id. Yes, this timestamp counts back from @now, and is + * thus not stable across restarts. But apparently neither is the host-id * nor the secret_key itself. */ #define EPOCH_TWO_YEARS (G_GINT64_CONSTANT(2 * 365 * 24 * 3600) * NM_UTILS_NSEC_PER_SEC) @@ -2874,9 +2876,15 @@ out: typedef struct { guint8 *host_id; gsize host_id_len; - gint64 timestamp_ns; - bool is_good : 1; - bool timestamp_is_good : 1; + + /* The timestamp (in nsec since the Epoch) returned by nm_utils_host_id_get_timestamp_nsec(). + * It is associated with the host (and the host-id). We currently use this for the LLT DUID + * generation for IPv6. Instead of persisting the timestamp separately to disk, we re-use the + * file timestamp of the secret_key file. */ + gint64 timestamp_nsec; + + bool is_good : 1; + bool timestamp_is_good : 1; } HostIdData; static const HostIdData *volatile host_id_static; @@ -2900,7 +2908,7 @@ again: host_id_data.timestamp_is_good = _host_id_read_timestamp(host_id_data.is_good, host_id_data.host_id, host_id_data.host_id_len, - &host_id_data.timestamp_ns); + &host_id_data.timestamp_nsec); if (!host_id_data.timestamp_is_good && host_id_data.is_good) nm_log_warn(LOGD_CORE, "secret-key: failure reading host timestamp (use fake one)"); @@ -2939,9 +2947,9 @@ nm_utils_host_id_get(const guint8 **out_host_id, gsize *out_host_id_len) } gint64 -nm_utils_host_id_get_timestamp_ns(void) +nm_utils_host_id_get_timestamp_nsec(void) { - return _host_id_get()->timestamp_ns; + return _host_id_get()->timestamp_nsec; } static GArray *nmtst_host_id_stack = NULL; @@ -2952,7 +2960,7 @@ void nmtst_utils_host_id_push(const guint8 *host_id, gssize host_id_len, gboolean is_good, - const gint64 *timestamp_ns) + const gint64 *p_timestamp_nsec) { NM_G_MUTEX_LOCKED(&nmtst_host_id_lock); gs_free char *str1_to_free = NULL; @@ -2971,8 +2979,8 @@ nmtst_utils_host_id_push(const guint8 *host_id, &str1_to_free), (gsize) host_id_len, !!is_good, - timestamp_ns ? *timestamp_ns : 0, - timestamp_ns ? "" : " (not-good)"); + p_timestamp_nsec ? *p_timestamp_nsec : 0, + p_timestamp_nsec ? "" : " (not-good)"); if (!nmtst_host_id_stack) { nmtst_host_id_stack = g_array_new(FALSE, FALSE, sizeof(HostIdData)); @@ -2984,9 +2992,9 @@ nmtst_utils_host_id_push(const guint8 *host_id, *h = (HostIdData){ .host_id = nm_memdup(host_id, host_id_len), .host_id_len = host_id_len, - .timestamp_ns = timestamp_ns ? *timestamp_ns : 0, + .timestamp_nsec = p_timestamp_nsec ? *p_timestamp_nsec : 0, .is_good = is_good, - .timestamp_is_good = !!timestamp_ns, + .timestamp_is_good = !!p_timestamp_nsec, }; g_atomic_pointer_set(&host_id_static, h); @@ -3342,8 +3350,13 @@ nm_utils_stable_id_generated_complete(const char *stable_id_generated) static void _stable_id_append(GString *str, const char *substitution) { - if (!substitution) + if (!substitution) { + /* Would have been nicer to append "=NIL;" to differentiate between + * empty and NULL. + * + * Can't do that now, as it would change behavior. */ substitution = ""; + } g_string_append_printf(str, "=%zu{%s}", strlen(substitution), substitution); } @@ -3413,7 +3426,7 @@ nm_utils_stable_id_parse(const char *stable_id, ({ \ gboolean _match = FALSE; \ \ - if (g_str_has_prefix(&stable_id[i], "" prefix "")) { \ + if (NM_STR_HAS_PREFIX(&stable_id[i], "" prefix "")) { \ _match = TRUE; \ if (!str) \ str = g_string_sized_new(256); \ @@ -5201,3 +5214,50 @@ again: return g; } + +/*****************************************************************************/ + +/** + * nm_utils_shorten_hostname: + * @hostname: the input hostname + * @shortened: (out) (transfer full): on return, the shortened hostname + * + * Checks whether the input hostname is valid. If not, tries to shorten it + * to HOST_NAME_MAX or to the first dot, whatever comes earlier. + * The new hostname is returned in @shortened. + * + * Returns: %TRUE if the input hostname was already valid or if was shortened + * successfully; %FALSE otherwise + */ +gboolean +nm_utils_shorten_hostname(const char *hostname, char **shortened) +{ + gs_free char *s = NULL; + const char *dot; + gsize l; + + nm_assert(hostname); + nm_assert(shortened); + + if (nm_sd_hostname_is_valid(hostname, FALSE)) { + *shortened = NULL; + return TRUE; + } + + dot = strchr(hostname, '.'); + if (dot) + l = (dot - hostname); + else + l = strlen(hostname); + l = MIN(l, (gsize) HOST_NAME_MAX); + + s = g_strndup(hostname, l); + + if (!nm_sd_hostname_is_valid(s, FALSE)) { + *shortened = NULL; + return FALSE; + } + + *shortened = g_steal_pointer(&s); + return TRUE; +} diff --git a/src/core/nm-core-utils.h b/src/core/nm-core-utils.h index 7079ab2f..d1a62a60 100644 --- a/src/core/nm-core-utils.h +++ b/src/core/nm-core-utils.h @@ -233,6 +233,7 @@ void nm_utils_log_connection_diff(NMConnection *connection, const char *dbus_path); gboolean nm_utils_is_specific_hostname(const char *name); +gboolean nm_utils_shorten_hostname(const char *hostname, char **shortened); struct _NMUuid; @@ -246,7 +247,7 @@ const char *nm_utils_proc_cmdline(void); const char *const *nm_utils_proc_cmdline_split(void); gboolean nm_utils_host_id_get(const guint8 **out_host_id, gsize *out_host_id_len); -gint64 nm_utils_host_id_get_timestamp_ns(void); +gint64 nm_utils_host_id_get_timestamp_nsec(void); void nmtst_utils_host_id_push(const guint8 *host_id, gssize host_id_len, diff --git a/src/core/nm-dbus-manager.c b/src/core/nm-dbus-manager.c index 2c5f7ace..7fcbf6ca 100644 --- a/src/core/nm-dbus-manager.c +++ b/src/core/nm-dbus-manager.c @@ -1082,6 +1082,21 @@ nm_dbus_manager_lookup_object(NMDBusManager *self, const char *path) return obj; } +gpointer +nm_dbus_manager_lookup_object_with_type(NMDBusManager *self, GType gtype, const char *path) +{ + gpointer ptr; + + nm_assert(g_type_is_a(gtype, NM_TYPE_DBUS_OBJECT)); + nm_assert(gtype != NM_TYPE_DBUS_OBJECT); + + ptr = nm_dbus_manager_lookup_object(self, path); + if (!ptr || !G_TYPE_CHECK_INSTANCE_TYPE(ptr, gtype)) + return NULL; + + return ptr; +} + void _nm_dbus_manager_obj_export(NMDBusObject *obj) { diff --git a/src/core/nm-dbus-manager.h b/src/core/nm-dbus-manager.h index d7ca78d1..d977f99f 100644 --- a/src/core/nm-dbus-manager.h +++ b/src/core/nm-dbus-manager.h @@ -52,6 +52,9 @@ gboolean nm_dbus_manager_is_stopping(NMDBusManager *self); gpointer nm_dbus_manager_lookup_object(NMDBusManager *self, const char *path); +gpointer +nm_dbus_manager_lookup_object_with_type(NMDBusManager *self, GType gtype, const char *path); + void _nm_dbus_manager_obj_export(NMDBusObject *obj); void _nm_dbus_manager_obj_unexport(NMDBusObject *obj); void diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index ddac1137..8fa8059e 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -422,7 +422,7 @@ _fw_nft_call_communicate_cb(GObject *source, GAsyncResult *result, gpointer user nm_g_main_context_push_thread_default_if_necessary(NULL); nm_shutdown_wait_obj_register_object(call_data->subprocess, "nft-terminate"); - G_STATIC_ASSERT_EXPR(200 < NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG * 2 / 3); + G_STATIC_ASSERT_EXPR(200 < NM_SHUTDOWN_TIMEOUT_ADDITIONAL_MSEC * 2 / 3); nm_g_subprocess_terminate_in_background(call_data->subprocess, 200); } } else if (g_subprocess_get_successful(call_data->subprocess)) { @@ -546,7 +546,7 @@ _fw_nft_call(GBytes *stdin_buf, call_data); call_data->timeout_source = - nm_g_source_attach(nm_g_timeout_source_new((NM_SHUTDOWN_TIMEOUT_MS * 2) / 3, + nm_g_source_attach(nm_g_timeout_source_new((NM_SHUTDOWN_TIMEOUT_1500_MSEC * 2) / 3, G_PRIORITY_DEFAULT, _fw_nft_call_timeout_cb, call_data, diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c index 03593ea2..5ab2f6b8 100644 --- a/src/core/nm-l3-config-data.c +++ b/src/core/nm-l3-config-data.c @@ -349,7 +349,7 @@ nm_l3_config_data_log(const NML3ConfigData *self, NMLogLevel log_level, NMLogDomain log_domain) { - char sbuf[sizeof(_nm_utils_to_string_buffer)]; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; int IS_IPv4; guint i; @@ -1172,13 +1172,6 @@ _l3_config_data_add_obj(NMDedupMultiIndex *multi_idx, modified = TRUE; } - /* OR assume_config_once flag */ - if (obj_new->ip_address.a_assume_config_once - && !obj_old->ip_address.a_assume_config_once) { - obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new); - obj_new_stackinit.ip_address.a_assume_config_once = TRUE; - modified = TRUE; - } break; case NMP_OBJECT_TYPE_IP4_ROUTE: case NMP_OBJECT_TYPE_IP6_ROUTE: @@ -1189,13 +1182,6 @@ _l3_config_data_add_obj(NMDedupMultiIndex *multi_idx, modified = TRUE; } - /* OR assume_config_once flag */ - if (obj_new->ip_route.r_assume_config_once - && !obj_old->ip_route.r_assume_config_once) { - obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new); - obj_new_stackinit.ip_route.r_assume_config_once = TRUE; - modified = TRUE; - } break; default: nm_assert_not_reached(); @@ -3056,9 +3042,8 @@ nm_l3_config_data_merge(NML3ConfigData *self, const NMPlatformIPAddress *a_src = NMP_OBJECT_CAST_IP_ADDRESS(obj); NMPlatformIPXAddress a; NML3ConfigMergeHookResult hook_result = { - .ip4acd_not_ready = NM_OPTION_BOOL_DEFAULT, - .assume_config_once = NM_OPTION_BOOL_DEFAULT, - .force_commit = NM_OPTION_BOOL_DEFAULT, + .ip4acd_not_ready = NM_OPTION_BOOL_DEFAULT, + .force_commit = NM_OPTION_BOOL_DEFAULT, }; #define _ensure_a() \ @@ -3091,12 +3076,6 @@ nm_l3_config_data_merge(NML3ConfigData *self, a.a4.a_acd_not_ready = (!!hook_result.ip4acd_not_ready); } - if (hook_result.assume_config_once != NM_OPTION_BOOL_DEFAULT - && (!!hook_result.assume_config_once) != a_src->a_assume_config_once) { - _ensure_a(); - a.ax.a_assume_config_once = (!!hook_result.assume_config_once); - } - if (hook_result.force_commit != NM_OPTION_BOOL_DEFAULT && (!!hook_result.force_commit) != a_src->a_force_commit) { _ensure_a(); @@ -3121,9 +3100,8 @@ nm_l3_config_data_merge(NML3ConfigData *self, const NMPlatformIPRoute *r_src = NMP_OBJECT_CAST_IP_ROUTE(obj); NMPlatformIPXRoute r; NML3ConfigMergeHookResult hook_result = { - .ip4acd_not_ready = NM_OPTION_BOOL_DEFAULT, - .assume_config_once = NM_OPTION_BOOL_DEFAULT, - .force_commit = NM_OPTION_BOOL_DEFAULT, + .ip4acd_not_ready = NM_OPTION_BOOL_DEFAULT, + .force_commit = NM_OPTION_BOOL_DEFAULT, }; #define _ensure_r() \ @@ -3149,12 +3127,6 @@ nm_l3_config_data_merge(NML3ConfigData *self, r.rx.ifindex = self->ifindex; } - if (hook_result.assume_config_once != NM_OPTION_BOOL_DEFAULT - && (!!hook_result.assume_config_once) != r_src->r_assume_config_once) { - _ensure_r(); - r.rx.r_assume_config_once = (!!hook_result.assume_config_once); - } - if (hook_result.force_commit != NM_OPTION_BOOL_DEFAULT && (!!hook_result.force_commit) != r_src->r_force_commit) { _ensure_r(); diff --git a/src/core/nm-l3-config-data.h b/src/core/nm-l3-config-data.h index b7a1bb32..20a32c62 100644 --- a/src/core/nm-l3-config-data.h +++ b/src/core/nm-l3-config-data.h @@ -137,7 +137,6 @@ NML3ConfigData *nm_l3_config_data_new_from_platform(NMDedupMultiIndex *mu typedef struct { NMOptionBool ip4acd_not_ready; - NMOptionBool assume_config_once; NMOptionBool force_commit; } NML3ConfigMergeHookResult; diff --git a/src/core/nm-l3-ipv4ll.c b/src/core/nm-l3-ipv4ll.c index 68cb17fb..2aedab56 100644 --- a/src/core/nm-l3-ipv4ll.c +++ b/src/core/nm-l3-ipv4ll.c @@ -600,7 +600,7 @@ _l3cd_config_add(NML3IPv4LL *self) nm_assert_not_reached(); self->l3cfg_commit_handle = nm_l3cfg_commit_type_register(self->l3cfg, - NM_L3_CFG_COMMIT_TYPE_ASSUME, + NM_L3_CFG_COMMIT_TYPE_UPDATE, self->l3cfg_commit_handle, "ipv4ll"); nm_l3cfg_commit_on_idle_schedule(self->l3cfg, NM_L3_CFG_COMMIT_TYPE_AUTO); @@ -748,12 +748,8 @@ _ipv4ll_set_timed_out_update(NML3IPv4LL *self, TimedOutState new_state) if (self->timed_out_expiry_msec == 0 || self->timed_out_expiry_msec < expiry_msec) { self->timed_out_expiry_msec = expiry_msec; nm_clear_g_source_inst(&self->timed_out_source); - self->timed_out_source = nm_g_timeout_source_new(timeout_msec, - G_PRIORITY_DEFAULT, - _ipv4ll_set_timed_out_timeout_cb, - self, - NULL); - g_source_attach(self->timed_out_source, NULL); + self->timed_out_source = + nm_g_timeout_add_source(timeout_msec, _ipv4ll_set_timed_out_timeout_cb, self); } break; } @@ -922,8 +918,7 @@ _ipv4ll_state_change_on_idle(NML3IPv4LL *self) if (!self->state_change_on_idle_source) { self->state_change_on_idle_source = - nm_g_idle_source_new(G_PRIORITY_DEFAULT, _ipv4ll_state_change_on_idle_cb, self, NULL); - g_source_attach(self->state_change_on_idle_source, NULL); + nm_g_idle_add_source(_ipv4ll_state_change_on_idle_cb, self); } } diff --git a/src/core/nm-l3-ipv6ll.c b/src/core/nm-l3-ipv6ll.c index 60da4ee8..2b9a1a0e 100644 --- a/src/core/nm-l3-ipv6ll.c +++ b/src/core/nm-l3-ipv6ll.c @@ -398,10 +398,7 @@ _lladdr_handle_changed(NML3IPv6LL *self) * NML3IPv4LL, where we use NM_L3_CONFIG_MERGE_FLAGS_ONLY_FOR_ACD. The difference * is that for IPv6 we let kernel do DAD, so we need to actually configure the * address. For IPv4, we can run ACD without configuring anything in kernel, - * and let the user decide how to proceed. - * - * Also in this case, we use the most graceful commit-type (NM_L3_CFG_COMMIT_TYPE_ASSUME), - * but for that to work, we also need NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE flag. */ + * and let the user decide how to proceed. */ l3cd = nm_l3_ipv6ll_get_l3cd(self); @@ -421,7 +418,7 @@ _lladdr_handle_changed(NML3IPv6LL *self) NM_DNS_PRIORITY_DEFAULT_NORMAL, NM_L3_ACD_DEFEND_TYPE_ALWAYS, 0, - NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE, + NM_L3CFG_CONFIG_FLAGS_NONE, NM_L3_CONFIG_MERGE_FLAGS_NONE)) changed = TRUE; } else { @@ -430,7 +427,7 @@ _lladdr_handle_changed(NML3IPv6LL *self) } self->l3cfg_commit_handle = nm_l3cfg_commit_type_register(self->l3cfg, - l3cd ? NM_L3_CFG_COMMIT_TYPE_ASSUME + l3cd ? NM_L3_CFG_COMMIT_TYPE_UPDATE : NM_L3_CFG_COMMIT_TYPE_NONE, self->l3cfg_commit_handle, "ipv6ll"); diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index eeb041d0..6b7e6f4b 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -363,7 +363,6 @@ static NM_UTILS_ENUM2STR_DEFINE(_l3_cfg_commit_type_to_string, NML3CfgCommitType, NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_AUTO, "auto"), NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_NONE, "none"), - NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_ASSUME, "assume"), NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_UPDATE, "update"), NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_REAPPLY, "reapply"), ); @@ -585,7 +584,7 @@ _l3_config_notify_data_to_string(const NML3ConfigNotifyData *notify_data, void _nm_l3cfg_emit_signal_notify(NML3Cfg *self, const NML3ConfigNotifyData *notify_data) { - char sbuf[sizeof(_nm_utils_to_string_buffer)]; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; nm_assert(notify_data); nm_assert(_NM_INT_NOT_NEGATIVE(notify_data->notify_type)); @@ -768,14 +767,6 @@ _nm_n_acd_data_probe_new(NML3Cfg *self, in_addr_t addr, guint32 timeout_msec, gp } \ G_STMT_END -static gboolean -_obj_state_data_get_assume_config_once(const ObjStateData *obj_state) -{ - nm_assert_obj_state(NULL, obj_state); - - return nmp_object_get_assume_config_once(obj_state->obj); -} - static ObjStateData * _obj_state_data_new(const NMPObject *obj, const NMPObject *plobj) { @@ -885,7 +876,7 @@ _obj_state_data_update(ObjStateData *obj_state, const NMPObject *obj) static void _obj_states_externally_removed_track(NML3Cfg *self, const NMPObject *obj, gboolean in_platform) { - char sbuf[sizeof(_nm_utils_to_string_buffer)]; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; ObjStateData *obj_state; nm_assert(NM_IS_L3CFG(self)); @@ -946,7 +937,7 @@ _obj_states_update_all(NML3Cfg *self) NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE, }; - char sbuf[sizeof(_nm_utils_to_string_buffer)]; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; ObjStateData *obj_state; int i; gboolean any_dirty = FALSE; @@ -1037,7 +1028,7 @@ typedef struct { static gboolean _obj_states_sync_filter(NML3Cfg *self, const NMPObject *obj, NML3CfgCommitType commit_type) { - char sbuf[sizeof(_nm_utils_to_string_buffer)]; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPObjectType obj_type; ObjStateData *obj_state; @@ -1054,10 +1045,6 @@ _obj_states_sync_filter(NML3Cfg *self, const NMPObject *obj, NML3CfgCommitType c nm_assert(c_list_is_empty(&obj_state->os_zombie_lst)); if (!obj_state->os_nm_configured) { - if (commit_type == NM_L3_CFG_COMMIT_TYPE_ASSUME - && !_obj_state_data_get_assume_config_once(obj_state)) - return FALSE; - obj_state->os_nm_configured = TRUE; _LOGD("obj-state: configure-first-time: %s", @@ -1152,7 +1139,7 @@ _obj_state_zombie_lst_get_prune_lists(NML3Cfg *self, const int IS_IPv4 = NM_IS_IPv4(addr_family); const NMPObjectType obj_type_route = NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4); const NMPObjectType obj_type_address = NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4); - char sbuf[sizeof(_nm_utils_to_string_buffer)]; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; ObjStateData *obj_state; ObjStateData *obj_state_safe; @@ -1198,7 +1185,7 @@ _obj_state_zombie_lst_get_prune_lists(NML3Cfg *self, static void _obj_state_zombie_lst_prune_all(NML3Cfg *self, int addr_family) { - char sbuf[sizeof(_nm_utils_to_string_buffer)]; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; ObjStateData *obj_state; ObjStateData *obj_state_safe; @@ -1743,9 +1730,7 @@ again: n_acd_get_fd(self->priv.p->nacd, &fd); - self->priv.p->nacd_source = - nm_g_unix_fd_source_new(fd, G_IO_IN, G_PRIORITY_DEFAULT, _l3_acd_nacd_event, self, NULL); - nm_g_source_attach(self->priv.p->nacd_source, NULL); + self->priv.p->nacd_source = nm_g_unix_fd_add_source(fd, G_IO_IN, _l3_acd_nacd_event, self); NM_SET_OUT(out_acd_not_supported, FALSE); return self->priv.p->nacd; @@ -3088,7 +3073,6 @@ nm_l3cfg_commit_on_idle_schedule(NML3Cfg *self, NML3CfgCommitType commit_type) nm_assert(NM_IS_L3CFG(self)); nm_assert(NM_IN_SET(commit_type, NM_L3_CFG_COMMIT_TYPE_AUTO, - NM_L3_CFG_COMMIT_TYPE_ASSUME, NM_L3_CFG_COMMIT_TYPE_UPDATE, NM_L3_CFG_COMMIT_TYPE_REAPPLY)); @@ -3503,7 +3487,6 @@ out_clear: typedef struct { NML3Cfg *self; gconstpointer tag; - bool assume_config_once; bool to_commit; bool force_commit_once; } L3ConfigMergeHookAddObjData; @@ -3523,11 +3506,9 @@ _l3_hook_add_obj_cb(const NML3ConfigData *l3cd, nm_assert(obj); nm_assert(hook_result); nm_assert(hook_result->ip4acd_not_ready == NM_OPTION_BOOL_DEFAULT); - nm_assert(hook_result->assume_config_once == NM_OPTION_BOOL_DEFAULT); nm_assert(hook_result->force_commit == NM_OPTION_BOOL_DEFAULT); - hook_result->assume_config_once = hook_data->assume_config_once; - hook_result->force_commit = hook_data->force_commit_once; + hook_result->force_commit = hook_data->force_commit_once; switch (NMP_OBJECT_GET_TYPE(obj)) { case NMP_OBJECT_TYPE_IP4_ADDRESS: @@ -3683,9 +3664,7 @@ _l3cfg_update_combined_config(NML3Cfg *self, if (NM_FLAGS_HAS(l3cd_data->config_flags, NM_L3CFG_CONFIG_FLAGS_ONLY_FOR_ACD)) continue; - hook_data.tag = l3cd_data->tag_confdata; - hook_data.assume_config_once = - NM_FLAGS_HAS(l3cd_data->config_flags, NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE); + hook_data.tag = l3cd_data->tag_confdata; hook_data.force_commit_once = l3cd_data->force_commit_once; nm_l3_config_data_merge(l3cd, @@ -3859,7 +3838,7 @@ _routes_temporary_not_available_update(NML3Cfg *self, for (i = 0; i < routes_temporary_not_available_arr->len; i++) { const NMPObject *o = routes_temporary_not_available_arr->pdata[i]; - char sbuf[sizeof(_nm_utils_to_string_buffer)]; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; nm_assert(NMP_OBJECT_GET_TYPE(o) == NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family))); @@ -4212,8 +4191,7 @@ _l3_commit_one(NML3Cfg *self, nm_assert(NM_IN_SET(commit_type, NM_L3_CFG_COMMIT_TYPE_NONE, NM_L3_CFG_COMMIT_TYPE_REAPPLY, - NM_L3_CFG_COMMIT_TYPE_UPDATE, - NM_L3_CFG_COMMIT_TYPE_ASSUME)); + NM_L3_CFG_COMMIT_TYPE_UPDATE)); nm_assert_addr_family(addr_family); _LOGT("committing IPv%c configuration (%s)", @@ -4299,7 +4277,6 @@ _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle) nm_assert(NM_IN_SET(commit_type, NM_L3_CFG_COMMIT_TYPE_NONE, NM_L3_CFG_COMMIT_TYPE_AUTO, - NM_L3_CFG_COMMIT_TYPE_ASSUME, NM_L3_CFG_COMMIT_TYPE_UPDATE, NM_L3_CFG_COMMIT_TYPE_REAPPLY)); nm_assert(self->priv.p->commit_reentrant_count == 0); @@ -4423,10 +4400,7 @@ nm_l3cfg_commit_type_register(NML3Cfg *self, char buf[64]; nm_assert(NM_IS_L3CFG(self)); - nm_assert(NM_IN_SET(commit_type, - NM_L3_CFG_COMMIT_TYPE_NONE, - NM_L3_CFG_COMMIT_TYPE_ASSUME, - NM_L3_CFG_COMMIT_TYPE_UPDATE)); + nm_assert(NM_IN_SET(commit_type, NM_L3_CFG_COMMIT_TYPE_NONE, NM_L3_CFG_COMMIT_TYPE_UPDATE)); /* It would be easy (and maybe convenient) to allow that @existing_handle * can currently be registered on another NML3Cfg instance. But then we couldn't diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h index 0ea68646..f6ec39ce 100644 --- a/src/core/nm-l3cfg.h +++ b/src/core/nm-l3cfg.h @@ -363,15 +363,6 @@ typedef enum _nm_packed { /* Don't touch the interface. */ NM_L3_CFG_COMMIT_TYPE_NONE, - /* ASSUME means to keep any pre-existing extra routes/addresses, while - * also not adding routes/addresses that are not present yet. This is to - * gracefully take over after restart, where the existing IP configuration - * should not change. - * - * The flag NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE can make certain addresses/ - * routes commitable also during "assume". */ - NM_L3_CFG_COMMIT_TYPE_ASSUME, - /* UPDATE means to add new addresses/routes, while also removing addresses/routes * that are no longer present (but were previously configured by NetworkManager). * Routes/addresses that were removed externally won't be re-added, and routes/addresses diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c index 6c73d237..52153697 100644 --- a/src/core/nm-manager.c +++ b/src/core/nm-manager.c @@ -54,15 +54,18 @@ /*****************************************************************************/ typedef struct { - gboolean user_enabled; - gboolean sw_enabled; - gboolean hw_enabled; - RfKillType rtype; + guint prop_id; + guint hw_prop_id; NMConfigRunStatePropertyType key; - const char *desc; - const char *prop; - const char *hw_prop; -} RadioState; +} RfkillTypeDesc; + +typedef struct { + bool available : 1; + bool user_enabled : 1; + bool sw_enabled : 1; + bool hw_enabled : 1; + bool os_owner : 1; +} RfkillRadioState; typedef enum { ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_INTERNAL, @@ -120,6 +123,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMManager, PROP_WWAN_HARDWARE_ENABLED, PROP_WIMAX_ENABLED, PROP_WIMAX_HARDWARE_ENABLED, + PROP_RADIO_FLAGS, PROP_ACTIVE_CONNECTIONS, PROP_CONNECTIVITY, PROP_CONNECTIVITY_CHECK_AVAILABLE, @@ -169,7 +173,8 @@ typedef struct { NMSettings *settings; - RadioState radio_states[RFKILL_TYPE_MAX]; + RfkillRadioState radio_states[NM_RFKILL_TYPE_MAX]; + NMVpnManager *vpn_manager; NMSleepMonitor *sleep_monitor; @@ -190,6 +195,8 @@ typedef struct { guint devices_inited_id; + guint radio_flags; + NMConnectivityState connectivity_state; guint8 device_state_prune_ratelimit_count; @@ -393,6 +400,8 @@ static void _activation_auth_done(NMManager *self, gboolean success, const char *error_desc); +static void _rfkill_update(NMManager *self, NMRfkillType rtype); + /*****************************************************************************/ static NM_CACHED_QUARK_FCN("autoconnect-root", autoconnect_root_quark); @@ -1156,8 +1165,10 @@ active_connection_get_by_path(NMManager *self, const char *path) NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); NMActiveConnection *ac; - ac = nm_dbus_manager_lookup_object(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)), path); - if (!ac || !NM_IS_ACTIVE_CONNECTION(ac) || c_list_is_empty(&ac->active_connections_lst)) + ac = nm_dbus_manager_lookup_object_with_type(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)), + NM_TYPE_ACTIVE_CONNECTION, + path); + if (!ac || c_list_is_empty(&ac->active_connections_lst)) return NULL; nm_assert(c_list_contains(&priv->active_connections_lst_head, &ac->active_connections_lst)); @@ -1289,8 +1300,11 @@ nm_manager_get_device_by_path(NMManager *self, const char *path) g_return_val_if_fail(path, NULL); - device = nm_dbus_manager_lookup_object(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)), path); - if (!device || !NM_IS_DEVICE(device) || c_list_is_empty(&device->devices_lst)) + device = + nm_dbus_manager_lookup_object_with_type(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)), + NM_TYPE_DEVICE, + path); + if (!device || c_list_is_empty(&device->devices_lst)) return NULL; nm_assert(c_list_contains(&priv->devices_lst_head, &device->devices_lst)); @@ -1637,6 +1651,9 @@ check_if_startup_complete(NMManager *self) return; } + /* Most of our logging is not API/stable, but this line is kinda important and + * what people will look for when debugging NetworkManager-wait-online.service. + * Take care before rewording this message. */ _LOGI(LOGD_CORE, "startup complete"); priv->startup = FALSE; @@ -1704,6 +1721,7 @@ remove_device(NMManager *self, NMDevice *device, gboolean quitting) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); gboolean unmanage = FALSE; + NMRfkillType rtype; _LOG2D(LOGD_DEVICE, device, @@ -1744,6 +1762,10 @@ remove_device(NMManager *self, NMDevice *device, gboolean quitting) _parent_notify_changed(self, device, TRUE); + rtype = nm_device_get_rfkill_type(device); + if (rtype != NM_RFKILL_TYPE_UNKNOWN) + _rfkill_update(self, rtype); + if (nm_device_is_real(device)) { gboolean unconfigure_ip_config = !quitting || unmanage; @@ -2276,39 +2298,104 @@ _static_hostname_changed_cb(NMHostnameManager *hostname_manager, GParamSpec *psp } /*****************************************************************************/ -/* General NMManager stuff */ -/*****************************************************************************/ + +static const RfkillTypeDesc _rfkill_type_desc[NM_RFKILL_TYPE_MAX] = { + [NM_RFKILL_TYPE_WLAN] = + { + .prop_id = PROP_WIRELESS_ENABLED, + .hw_prop_id = PROP_WIRELESS_HARDWARE_ENABLED, + .key = NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED, + }, + [NM_RFKILL_TYPE_WWAN] = + { + .prop_id = PROP_WWAN_ENABLED, + .hw_prop_id = PROP_WWAN_HARDWARE_ENABLED, + .key = NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED, + }, +}; static gboolean -radio_enabled_for_rstate(RadioState *rstate, gboolean check_changeable) +_rfkill_radio_state_get_enabled(const RfkillRadioState *rstate, gboolean check_changeable) { gboolean enabled; + /* If the device is not owned by the os, hw_enabled will be FALSE, hence + * we don't need to consider os_owner here. + */ enabled = rstate->user_enabled && rstate->hw_enabled; if (check_changeable) enabled &= rstate->sw_enabled; return enabled; } +static void +_rfkill_radio_state_set_from_manager(NMManager *self, NMRfkillType rtype, RfkillRadioState *rstate) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); + NMDevice *device; + + switch (nm_rfkill_manager_get_rfkill_state(priv->rfkill_mgr, rtype)) { + case NM_RFKILL_STATE_UNAVAILABLE: + rstate->sw_enabled = TRUE; + rstate->hw_enabled = TRUE; + rstate->os_owner = TRUE; + + /* A rfkill-type is available when there is a compatible + * killswitch or a compatible device. */ + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { + if (nm_device_get_rfkill_type(device) == rtype) { + rstate->available = TRUE; + return; + } + } + rstate->available = FALSE; + return; + case NM_RFKILL_STATE_UNBLOCKED: + rstate->available = TRUE; + rstate->sw_enabled = TRUE; + rstate->hw_enabled = TRUE; + rstate->os_owner = TRUE; + return; + case NM_RFKILL_STATE_SOFT_BLOCKED: + rstate->available = TRUE; + rstate->sw_enabled = FALSE; + rstate->hw_enabled = TRUE; + rstate->os_owner = TRUE; + return; + case NM_RFKILL_STATE_HARD_BLOCKED: + rstate->available = TRUE; + rstate->sw_enabled = FALSE; + rstate->hw_enabled = FALSE; + /* In case the OS doesn't own the NIC, we would be in NM_RFKILL_STATE_HARD_BLOCKED */ + rstate->os_owner = TRUE; + return; + case NM_RFKILL_STATE_HARD_BLOCKED_OS_NOT_OWNER: + rstate->available = TRUE; + rstate->sw_enabled = FALSE; + rstate->hw_enabled = FALSE; + rstate->os_owner = FALSE; + return; + } + nm_assert_not_reached(); +} + static gboolean -radio_enabled_for_type(NMManager *self, RfKillType rtype, gboolean check_changeable) +_rfkill_radio_state_get(NMManager *self, NMRfkillType rtype) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - return radio_enabled_for_rstate(&priv->radio_states[rtype], check_changeable); + nm_assert(_NM_INT_NOT_NEGATIVE(rtype) && rtype < G_N_ELEMENTS(priv->radio_states)); + + return _rfkill_radio_state_get_enabled(&priv->radio_states[rtype], TRUE); } static void -manager_update_radio_enabled(NMManager *self, RadioState *rstate, gboolean enabled) +_rfkill_update_devices(NMManager *self, NMRfkillType rtype, gboolean enabled) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); NMDevice *device; - /* Do nothing for radio types not yet implemented */ - if (!rstate->prop) - return; - - g_object_notify(G_OBJECT(self), rstate->prop); + _notify(self, _rfkill_type_desc[rtype].prop_id); /* Don't touch devices if asleep/networking disabled */ if (manager_sleeping(self)) @@ -2316,7 +2403,7 @@ manager_update_radio_enabled(NMManager *self, RadioState *rstate, gboolean enabl /* enable/disable wireless devices as required */ c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - if (nm_device_get_rfkill_type(device) == rstate->rtype) { + if (nm_device_get_rfkill_type(device) == rtype) { _LOG2D(LOGD_RFKILL, device, "rfkill: setting radio %s", @@ -2327,89 +2414,195 @@ manager_update_radio_enabled(NMManager *self, RadioState *rstate, gboolean enabl } static void -update_rstate_from_rfkill(NMRfkillManager *rfkill_mgr, RadioState *rstate) +_rfkill_update_one_type(NMManager *self, NMRfkillType rtype) { - switch (nm_rfkill_manager_get_rfkill_state(rfkill_mgr, rstate->rtype)) { - case RFKILL_UNBLOCKED: - rstate->sw_enabled = TRUE; - rstate->hw_enabled = TRUE; - break; - case RFKILL_SOFT_BLOCKED: - rstate->sw_enabled = FALSE; - rstate->hw_enabled = TRUE; - break; - case RFKILL_HARD_BLOCKED: - rstate->sw_enabled = FALSE; - rstate->hw_enabled = FALSE; - break; - default: - g_warn_if_reached(); - break; - } -} + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); + RfkillRadioState *rstate = &priv->radio_states[rtype]; + gboolean old_enabled; + gboolean new_enabled; + gboolean old_rfkilled; + gboolean new_rfkilled; + gboolean old_hwe; + guint old_radio_flags; -static void -manager_rfkill_update_one_type(NMManager *self, RadioState *rstate, RfKillType rtype) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gboolean old_enabled, new_enabled, old_rfkilled, new_rfkilled, old_hwe; + nm_assert(_NM_INT_NOT_NEGATIVE(rtype) && rtype < G_N_ELEMENTS(priv->radio_states)); - old_enabled = radio_enabled_for_rstate(rstate, TRUE); - old_rfkilled = rstate->hw_enabled && rstate->sw_enabled; - old_hwe = rstate->hw_enabled; + old_enabled = _rfkill_radio_state_get_enabled(rstate, TRUE); + old_rfkilled = rstate->hw_enabled && rstate->sw_enabled; + old_hwe = rstate->hw_enabled; + old_radio_flags = priv->radio_flags; /* recheck kernel rfkill state */ - update_rstate_from_rfkill(priv->rfkill_mgr, rstate); + _rfkill_radio_state_set_from_manager(self, rtype, rstate); /* Print out all states affecting device enablement */ - if (rstate->desc) { - _LOGD(LOGD_RFKILL, - "rfkill: %s hw-enabled %d sw-enabled %d", - rstate->desc, - rstate->hw_enabled, - rstate->sw_enabled); - } + _LOGD(LOGD_RFKILL, + "rfkill: %s available %d hw-enabled %d sw-enabled %d os-owner %d", + nm_rfkill_type_to_string(rtype), + rstate->available, + rstate->hw_enabled, + rstate->sw_enabled, + rstate->os_owner); /* Log new killswitch state */ new_rfkilled = rstate->hw_enabled && rstate->sw_enabled; if (old_rfkilled != new_rfkilled) { _LOGI(LOGD_RFKILL, "rfkill: %s now %s by radio killswitch", - rstate->desc, + nm_rfkill_type_to_string(rtype), new_rfkilled ? "enabled" : "disabled"); } - /* Send out property changed signal for HW enabled */ - if (rstate->hw_enabled != old_hwe) { - if (rstate->hw_prop) - g_object_notify(G_OBJECT(self), rstate->hw_prop); - } + priv->radio_flags = NM_FLAGS_ASSIGN(priv->radio_flags, + (guint) nm_rfkill_type_to_radio_available_flag(rtype), + rstate->available); + + /* Send out property changed signal for HW available and enabled */ + nm_gobject_notify_together(self, + rstate->hw_enabled != old_hwe ? _rfkill_type_desc[rtype].hw_prop_id + : PROP_0, + priv->radio_flags != old_radio_flags ? PROP_RADIO_FLAGS : PROP_0); /* And finally update the actual device radio state itself; respect the * daemon state here because this is never called from user-triggered * radio changes and we only want to ignore the daemon enabled state when * handling user radio change requests. */ - new_enabled = radio_enabled_for_rstate(rstate, TRUE); + new_enabled = _rfkill_radio_state_get_enabled(rstate, TRUE); if (new_enabled != old_enabled) - manager_update_radio_enabled(self, rstate, new_enabled); + _rfkill_update_devices(self, rtype, new_enabled); } static void -nm_manager_rfkill_update(NMManager *self, RfKillType rtype) +_rfkill_update(NMManager *self, NMRfkillType rtype) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - guint i; + guint i; - if (rtype != RFKILL_TYPE_UNKNOWN) - manager_rfkill_update_one_type(self, &priv->radio_states[rtype], rtype); + if (rtype != NM_RFKILL_TYPE_UNKNOWN) + _rfkill_update_one_type(self, rtype); else { /* Otherwise, sync all radio types */ - for (i = 0; i < RFKILL_TYPE_MAX; i++) - manager_rfkill_update_one_type(self, &priv->radio_states[i], i); + for (i = 0; i < NM_RFKILL_TYPE_MAX; i++) + _rfkill_update_one_type(self, i); } } +/*****************************************************************************/ + +#define KERN_RFKILL_OP_CHANGE_ALL 3 +#define KERN_RFKILL_TYPE_WLAN 1 +#define KERN_RFKILL_TYPE_WWAN 5 + +struct rfkill_event { + uint32_t idx; + uint8_t type; + uint8_t op; + uint8_t soft; + uint8_t hard; +} _nm_packed; + +static void +_rfkill_update_system(NMManager *self, NMRfkillType rtype, gboolean enabled) +{ + nm_auto_close int fd = -1; + struct rfkill_event event; + ssize_t len; + int errsv; + + nm_assert(NM_IN_SET(rtype, NM_RFKILL_TYPE_WLAN, NM_RFKILL_TYPE_WWAN)); + + fd = open("/dev/rfkill", O_RDWR | O_CLOEXEC); + if (fd < 0) { + if (errno == EACCES) + _LOGW(LOGD_RFKILL, + "rfkill: (%s): failed to open killswitch device", + nm_rfkill_type_to_string(rtype)); + return; + } + + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { + _LOGW(LOGD_RFKILL, + "rfkill: (%s): failed to set killswitch device for " + "non-blocking operation", + nm_rfkill_type_to_string(rtype)); + return; + } + + memset(&event, 0, sizeof(event)); + event.op = KERN_RFKILL_OP_CHANGE_ALL; + switch (rtype) { + case NM_RFKILL_TYPE_WLAN: + event.type = KERN_RFKILL_TYPE_WLAN; + break; + case NM_RFKILL_TYPE_WWAN: + event.type = KERN_RFKILL_TYPE_WWAN; + break; + default: + nm_assert_not_reached(); + } + event.soft = enabled ? 0 : 1; + + len = write(fd, &event, sizeof(event)); + if (len < 0) { + errsv = errno; + _LOGW(LOGD_RFKILL, + "rfkill: (%s): failed to change Wi-Fi killswitch state: (%d) %s", + nm_rfkill_type_to_string(rtype), + errsv, + nm_strerror_native(errsv)); + } else if (len == sizeof(event)) { + _LOGI(LOGD_RFKILL, + "rfkill: %s hardware radio set %s", + nm_rfkill_type_to_string(rtype), + enabled ? "enabled" : "disabled"); + } else { + /* Failed to write full structure */ + _LOGW(LOGD_RFKILL, + "rfkill: (%s): failed to change Wi-Fi killswitch state", + nm_rfkill_type_to_string(rtype)); + } +} + +static void +_rfkill_update_from_user(NMManager *self, NMRfkillType rtype, gboolean enabled) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); + RfkillRadioState *rstate = &priv->radio_states[rtype]; + gboolean old_enabled, new_enabled; + + /* Don't touch devices if asleep/networking disabled */ + if (manager_sleeping(self)) + return; + + _LOGD(LOGD_RFKILL, + "rfkill: (%s): setting radio %s by user", + nm_rfkill_type_to_string(rtype), + enabled ? "enabled" : "disabled"); + + /* Update enabled key in state file */ + nm_config_state_set(priv->config, TRUE, FALSE, _rfkill_type_desc[rtype].key, enabled); + + /* When the user toggles the radio, their request should override any + * daemon (like ModemManager) enabled state that can be changed. For WWAN + * for example, we want the WwanEnabled property to reflect the daemon state + * too so that users can toggle the modem powered, but we don't want that + * daemon state to affect whether or not the user *can* turn it on, which is + * what the kernel rfkill state does. So we ignore daemon enabled state + * when determining what the new state should be since it shouldn't block + * the user's request. + */ + old_enabled = _rfkill_radio_state_get_enabled(rstate, TRUE); + rstate->user_enabled = enabled; + new_enabled = _rfkill_radio_state_get_enabled(rstate, FALSE); + if (new_enabled != old_enabled) { + /* Try to change the kernel rfkill state */ + _rfkill_update_system(self, rtype, new_enabled); + _rfkill_update_devices(self, rtype, new_enabled); + } +} + +/*****************************************************************************/ + static void device_auth_done_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data) { @@ -2599,7 +2792,7 @@ get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generat int master_ifindex = nm_platform_link_get_master(priv->platform, ifindex); /* Check that the master is activating before assuming a - * slave connection. However, ignore ovs-system master as + * slave connection. However, ignore ovs-system/ovs-netdev master as * we never manage it. */ if (master_ifindex @@ -2786,6 +2979,7 @@ get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generat nm_device_assume_state_reset(device); if (!nm_settings_add_connection(priv->settings, + NULL, connection, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, NM_SETTINGS_CONNECTION_ADD_REASON_NONE, @@ -2906,6 +3100,7 @@ recheck_assume_connection(NMManager *self, NMDevice *device) nm_settings_connection_update( sett_conn, + NULL, con2, NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP, NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, @@ -3207,7 +3402,7 @@ add_device(NMManager *self, NMDevice *device, GError **error) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); const char *iface, *type_desc; - RfKillType rtype; + NMRfkillType rtype; GSList *iter, *remove = NULL; int ifindex; const char *dbus_path; @@ -3294,9 +3489,9 @@ add_device(NMManager *self, NMDevice *device, GError **error) * global state. */ rtype = nm_device_get_rfkill_type(device); - if (rtype != RFKILL_TYPE_UNKNOWN) { - nm_manager_rfkill_update(self, rtype); - nm_device_set_enabled(device, radio_enabled_for_type(self, rtype, TRUE)); + if (rtype != NM_RFKILL_TYPE_UNKNOWN) { + _rfkill_update(self, rtype); + nm_device_set_enabled(device, _rfkill_radio_state_get(self, rtype)); } iface = nm_device_get_iface(device); @@ -3671,12 +3866,14 @@ platform_query_devices(NMManager *self) } static void -rfkill_manager_rfkill_changed_cb(NMRfkillManager *rfkill_mgr, - RfKillType rtype, - RfKillState udev_state, - gpointer user_data) +rfkill_manager_rfkill_changed_cb(NMRfkillManager *rfkill_mgr, + /* NMRfkillType */ guint rtype, + /* NMRfkillState */ guint udev_state, + gpointer user_data) { - nm_manager_rfkill_update(NM_MANAGER(user_data), rtype); + nm_assert(rtype < NM_RFKILL_TYPE_MAX); + + _rfkill_update(NM_MANAGER(user_data), rtype); } const CList * @@ -5760,6 +5957,7 @@ _add_and_activate_auth_done(NMManager *self, * shutdown. */ nm_settings_add_connection_dbus( priv->settings, + NULL, connection, persist_mode, NM_SETTINGS_CONNECTION_ADD_REASON_NONE, @@ -6313,7 +6511,7 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed) /* Ensure rfkill state is up-to-date since we don't respond to state * changes during sleep. */ - nm_manager_rfkill_update(self, RFKILL_TYPE_UNKNOWN); + _rfkill_update(self, NM_RFKILL_TYPE_UNKNOWN); /* Re-manage managed devices */ c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { @@ -6330,20 +6528,21 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed) /* enable/disable wireless devices since that we don't respond * to killswitch changes during sleep. */ - for (i = 0; i < RFKILL_TYPE_MAX; i++) { - RadioState *rstate = &priv->radio_states[i]; - gboolean enabled = radio_enabled_for_rstate(rstate, TRUE); - - if (rstate->desc) { - _LOGD(LOGD_RFKILL, - "rfkill: %s %s devices (hw_enabled %d, sw_enabled %d, user_enabled %d)", - enabled ? "enabling" : "disabling", - rstate->desc, - rstate->hw_enabled, - rstate->sw_enabled, - rstate->user_enabled); - } - if (nm_device_get_rfkill_type(device) == rstate->rtype) + for (i = 0; i < NM_RFKILL_TYPE_MAX; i++) { + const NMRfkillType rtype = i; + const RfkillRadioState *rstate = &priv->radio_states[rtype]; + gboolean enabled = _rfkill_radio_state_get_enabled(rstate, TRUE); + + _LOGD(LOGD_RFKILL, + "rfkill: %s %s devices (hw_enabled %d, sw_enabled %d, user_enabled %d, " + "os_owner %d)", + enabled ? "enabling" : "disabling", + nm_rfkill_type_to_string(rtype), + rstate->hw_enabled, + rstate->sw_enabled, + rstate->user_enabled, + rstate->os_owner); + if (nm_device_get_rfkill_type(device) == rtype) nm_device_set_enabled(device, enabled); } @@ -6929,25 +7128,21 @@ nm_manager_start(NMManager *self, GError **error) nm_device_factory_manager_for_each_factory(start_factory, NULL); /* Set initial radio enabled/disabled state */ - for (i = 0; i < RFKILL_TYPE_MAX; i++) { - RadioState *rstate = &priv->radio_states[i]; - gboolean enabled; - - if (!rstate->desc) - continue; + for (i = 0; i < NM_RFKILL_TYPE_MAX; i++) { + const NMRfkillType rtype = i; + RfkillRadioState *rstate = &priv->radio_states[rtype]; + gboolean enabled; /* recheck kernel rfkill state */ - update_rstate_from_rfkill(priv->rfkill_mgr, rstate); - - if (rstate->desc) { - _LOGI(LOGD_RFKILL, - "rfkill: %s %s by radio killswitch; %s by state file", - rstate->desc, - (rstate->hw_enabled && rstate->sw_enabled) ? "enabled" : "disabled", - rstate->user_enabled ? "enabled" : "disabled"); - } - enabled = radio_enabled_for_rstate(rstate, TRUE); - manager_update_radio_enabled(self, rstate, enabled); + _rfkill_radio_state_set_from_manager(self, rtype, rstate); + + _LOGI(LOGD_RFKILL, + "rfkill: %s %s by radio killswitch; %s by state file", + nm_rfkill_type_to_string(rtype), + (rstate->hw_enabled && rstate->sw_enabled) ? "enabled" : "disabled", + rstate->user_enabled ? "enabled" : "disabled"); + enabled = _rfkill_radio_state_get_enabled(rstate, TRUE); + _rfkill_update_devices(self, rtype, enabled); } _LOGI(LOGD_CORE, "Networking is %s by state file", priv->net_enabled ? "enabled" : "disabled"); @@ -7605,141 +7800,7 @@ auth_mgr_changed(NMAuthManager *auth_manager, gpointer user_data) "()"); } -#define KERN_RFKILL_OP_CHANGE_ALL 3 -#define KERN_RFKILL_TYPE_WLAN 1 -#define KERN_RFKILL_TYPE_WWAN 5 -struct rfkill_event { - uint32_t idx; - uint8_t type; - uint8_t op; - uint8_t soft; - uint8_t hard; -} _nm_packed; - -static void -rfkill_change(NMManager *self, const char *desc, RfKillType rtype, gboolean enabled) -{ - int fd; - struct rfkill_event event; - ssize_t len; - int errsv; - - g_return_if_fail(rtype == RFKILL_TYPE_WLAN || rtype == RFKILL_TYPE_WWAN); - - fd = open("/dev/rfkill", O_RDWR | O_CLOEXEC); - if (fd < 0) { - if (errno == EACCES) - _LOGW(LOGD_RFKILL, "rfkill: (%s): failed to open killswitch device", desc); - return; - } - - if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { - _LOGW(LOGD_RFKILL, - "rfkill: (%s): failed to set killswitch device for " - "non-blocking operation", - desc); - nm_close(fd); - return; - } - - memset(&event, 0, sizeof(event)); - event.op = KERN_RFKILL_OP_CHANGE_ALL; - switch (rtype) { - case RFKILL_TYPE_WLAN: - event.type = KERN_RFKILL_TYPE_WLAN; - break; - case RFKILL_TYPE_WWAN: - event.type = KERN_RFKILL_TYPE_WWAN; - break; - default: - g_assert_not_reached(); - } - event.soft = enabled ? 0 : 1; - - len = write(fd, &event, sizeof(event)); - if (len < 0) { - errsv = errno; - _LOGW(LOGD_RFKILL, - "rfkill: (%s): failed to change Wi-Fi killswitch state: (%d) %s", - desc, - errsv, - nm_strerror_native(errsv)); - } else if (len == sizeof(event)) { - _LOGI(LOGD_RFKILL, - "rfkill: %s hardware radio set %s", - desc, - enabled ? "enabled" : "disabled"); - } else { - /* Failed to write full structure */ - _LOGW(LOGD_RFKILL, "rfkill: (%s): failed to change Wi-Fi killswitch state", desc); - } - - nm_close(fd); -} - -static void -manager_radio_user_toggled(NMManager *self, RadioState *rstate, gboolean enabled) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gboolean old_enabled, new_enabled; - - /* Don't touch devices if asleep/networking disabled */ - if (manager_sleeping(self)) - return; - - if (rstate->desc) { - _LOGD(LOGD_RFKILL, - "rfkill: (%s): setting radio %s by user", - rstate->desc, - enabled ? "enabled" : "disabled"); - } - - /* Update enabled key in state file */ - nm_config_state_set(priv->config, TRUE, FALSE, rstate->key, enabled); - - /* When the user toggles the radio, their request should override any - * daemon (like ModemManager) enabled state that can be changed. For WWAN - * for example, we want the WwanEnabled property to reflect the daemon state - * too so that users can toggle the modem powered, but we don't want that - * daemon state to affect whether or not the user *can* turn it on, which is - * what the kernel rfkill state does. So we ignore daemon enabled state - * when determining what the new state should be since it shouldn't block - * the user's request. - */ - old_enabled = radio_enabled_for_rstate(rstate, TRUE); - rstate->user_enabled = enabled; - new_enabled = radio_enabled_for_rstate(rstate, FALSE); - if (new_enabled != old_enabled) { - /* Try to change the kernel rfkill state */ - if (rstate->rtype == RFKILL_TYPE_WLAN || rstate->rtype == RFKILL_TYPE_WWAN) - rfkill_change(self, rstate->desc, rstate->rtype, new_enabled); - - manager_update_radio_enabled(self, rstate, new_enabled); - } -} - -static gboolean -periodic_update_active_connection_timestamps(gpointer user_data) -{ - NMManager *manager = NM_MANAGER(user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(manager); - NMActiveConnection *ac; - gboolean has_time = FALSE; - guint64 t = 0; - - c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) { - if (nm_active_connection_get_state(ac) != NM_ACTIVE_CONNECTION_STATE_ACTIVATED) - continue; - - if (!has_time) { - t = time(NULL); - has_time = TRUE; - } - nm_settings_connection_update_timestamp(nm_active_connection_get_settings_connection(ac), - t); - } - return G_SOURCE_CONTINUE; -} +/*****************************************************************************/ void nm_manager_unblock_failed_ovs_interfaces(NMManager *self) @@ -7893,8 +7954,8 @@ constructed(GObject *object) priv->net_enabled = state->net_enabled; - priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = state->wifi_enabled; - priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = state->wwan_enabled; + priv->radio_states[NM_RFKILL_TYPE_WLAN].user_enabled = state->wifi_enabled; + priv->radio_states[NM_RFKILL_TYPE_WWAN].user_enabled = state->wwan_enabled; priv->rfkill_mgr = nm_rfkill_manager_new(); g_signal_connect(priv->rfkill_mgr, @@ -7907,21 +7968,18 @@ constructed(GObject *object) * changes to the WirelessEnabled/WWANEnabled properties which toggle kernel * rfkill. */ - rfkill_change(self, - priv->radio_states[RFKILL_TYPE_WLAN].desc, - RFKILL_TYPE_WLAN, - priv->radio_states[RFKILL_TYPE_WLAN].user_enabled); - rfkill_change(self, - priv->radio_states[RFKILL_TYPE_WWAN].desc, - RFKILL_TYPE_WWAN, - priv->radio_states[RFKILL_TYPE_WWAN].user_enabled); + _rfkill_update_system(self, + NM_RFKILL_TYPE_WLAN, + priv->radio_states[NM_RFKILL_TYPE_WLAN].user_enabled); + _rfkill_update_system(self, + NM_RFKILL_TYPE_WWAN, + priv->radio_states[NM_RFKILL_TYPE_WWAN].user_enabled); } static void nm_manager_init(NMManager *self) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - guint i; GFile *file; c_list_init(&priv->auth_lst_head); @@ -7935,25 +7993,18 @@ nm_manager_init(NMManager *self) priv->capabilities = g_array_new(FALSE, FALSE, sizeof(guint32)); - /* Initialize rfkill structures and states */ - memset(priv->radio_states, 0, sizeof(priv->radio_states)); - - priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = TRUE; - priv->radio_states[RFKILL_TYPE_WLAN].key = NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED; - priv->radio_states[RFKILL_TYPE_WLAN].prop = NM_MANAGER_WIRELESS_ENABLED; - priv->radio_states[RFKILL_TYPE_WLAN].hw_prop = NM_MANAGER_WIRELESS_HARDWARE_ENABLED; - priv->radio_states[RFKILL_TYPE_WLAN].desc = "Wi-Fi"; - priv->radio_states[RFKILL_TYPE_WLAN].rtype = RFKILL_TYPE_WLAN; - - priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = TRUE; - priv->radio_states[RFKILL_TYPE_WWAN].key = NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED; - priv->radio_states[RFKILL_TYPE_WWAN].prop = NM_MANAGER_WWAN_ENABLED; - priv->radio_states[RFKILL_TYPE_WWAN].hw_prop = NM_MANAGER_WWAN_HARDWARE_ENABLED; - priv->radio_states[RFKILL_TYPE_WWAN].desc = "WWAN"; - priv->radio_states[RFKILL_TYPE_WWAN].rtype = RFKILL_TYPE_WWAN; - - for (i = 0; i < RFKILL_TYPE_MAX; i++) - priv->radio_states[i].hw_enabled = TRUE; + priv->radio_states[NM_RFKILL_TYPE_WLAN] = (RfkillRadioState){ + .user_enabled = TRUE, + .sw_enabled = FALSE, + .hw_enabled = TRUE, + .os_owner = TRUE, + }; + priv->radio_states[NM_RFKILL_TYPE_WWAN] = (RfkillRadioState){ + .user_enabled = TRUE, + .sw_enabled = FALSE, + .hw_enabled = TRUE, + .os_owner = TRUE, + }; priv->sleeping = FALSE; priv->state = NM_STATE_DISCONNECTED; @@ -7984,12 +8035,6 @@ nm_manager_init(NMManager *self) _LOGW(LOGD_CORE, "failed to monitor kernel firmware directory '%s'.", KERNEL_FIRMWARE_DIR); } - /* Update timestamps in active connections */ - priv->timestamp_update_id = - g_timeout_add_seconds(300, - (GSourceFunc) periodic_update_active_connection_timestamps, - self); - priv->metered = NM_METERED_UNKNOWN; priv->sleep_devices = g_hash_table_new(nm_direct_hash, NULL); } @@ -8025,16 +8070,16 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) g_value_set_boolean(value, priv->net_enabled); break; case PROP_WIRELESS_ENABLED: - g_value_set_boolean(value, radio_enabled_for_type(self, RFKILL_TYPE_WLAN, TRUE)); + g_value_set_boolean(value, _rfkill_radio_state_get(self, NM_RFKILL_TYPE_WLAN)); break; case PROP_WIRELESS_HARDWARE_ENABLED: - g_value_set_boolean(value, priv->radio_states[RFKILL_TYPE_WLAN].hw_enabled); + g_value_set_boolean(value, priv->radio_states[NM_RFKILL_TYPE_WLAN].hw_enabled); break; case PROP_WWAN_ENABLED: - g_value_set_boolean(value, radio_enabled_for_type(self, RFKILL_TYPE_WWAN, TRUE)); + g_value_set_boolean(value, _rfkill_radio_state_get(self, NM_RFKILL_TYPE_WWAN)); break; case PROP_WWAN_HARDWARE_ENABLED: - g_value_set_boolean(value, priv->radio_states[RFKILL_TYPE_WWAN].hw_enabled); + g_value_set_boolean(value, priv->radio_states[NM_RFKILL_TYPE_WWAN].hw_enabled); break; case PROP_WIMAX_ENABLED: g_value_set_boolean(value, FALSE); @@ -8042,6 +8087,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) case PROP_WIMAX_HARDWARE_ENABLED: g_value_set_boolean(value, FALSE); break; + case PROP_RADIO_FLAGS: + g_value_set_uint(value, priv->radio_flags); + break; case PROP_ACTIVE_CONNECTIONS: ptrarr = g_ptr_array_new(); c_list_for_each_entry_prev (ac, @@ -8125,14 +8173,14 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps switch (prop_id) { case PROP_WIRELESS_ENABLED: - manager_radio_user_toggled(NM_MANAGER(object), - &priv->radio_states[RFKILL_TYPE_WLAN], - g_value_get_boolean(value)); + _rfkill_update_from_user(NM_MANAGER(object), + NM_RFKILL_TYPE_WLAN, + g_value_get_boolean(value)); break; case PROP_WWAN_ENABLED: - manager_radio_user_toggled(NM_MANAGER(object), - &priv->radio_states[RFKILL_TYPE_WWAN], - g_value_get_boolean(value)); + _rfkill_update_from_user(NM_MANAGER(object), + NM_RFKILL_TYPE_WWAN, + g_value_get_boolean(value)); break; case PROP_WIMAX_ENABLED: /* WIMAX is deprecated. This does nothing. */ @@ -8497,6 +8545,9 @@ static const NMDBusInterfaceInfoExtended interface_info_manager = { NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WimaxHardwareEnabled", "b", NM_MANAGER_WIMAX_HARDWARE_ENABLED), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("RadioFlags", + "u", + NM_MANAGER_RADIO_FLAGS), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ActiveConnections", "ao", NM_MANAGER_ACTIVE_CONNECTIONS), @@ -8641,6 +8692,14 @@ nm_manager_class_init(NMManagerClass *manager_class) TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_RADIO_FLAGS] = g_param_spec_uint(NM_MANAGER_RADIO_FLAGS, + "", + "", + 0, + G_MAXUINT32, + NM_RADIO_FLAG_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_ACTIVE_CONNECTIONS] = g_param_spec_boxed(NM_MANAGER_ACTIVE_CONNECTIONS, "", diff --git a/src/core/nm-manager.h b/src/core/nm-manager.h index 385cdfe0..f8563c3a 100644 --- a/src/core/nm-manager.h +++ b/src/core/nm-manager.h @@ -30,6 +30,7 @@ #define NM_MANAGER_WWAN_HARDWARE_ENABLED "wwan-hardware-enabled" #define NM_MANAGER_WIMAX_ENABLED "wimax-enabled" #define NM_MANAGER_WIMAX_HARDWARE_ENABLED "wimax-hardware-enabled" +#define NM_MANAGER_RADIO_FLAGS "radio-flags" #define NM_MANAGER_ACTIVE_CONNECTIONS "active-connections" #define NM_MANAGER_CONNECTIVITY "connectivity" #define NM_MANAGER_CONNECTIVITY_CHECK_AVAILABLE "connectivity-check-available" diff --git a/src/core/nm-pacrunner-manager.c b/src/core/nm-pacrunner-manager.c index 5ef425bb..86a40632 100644 --- a/src/core/nm-pacrunner-manager.c +++ b/src/core/nm-pacrunner-manager.c @@ -291,7 +291,7 @@ _call_destroy_proxy_configuration(NMPacrunnerManager *self, g_variant_new("(o)", path), G_VARIANT_TYPE("()"), G_DBUS_CALL_FLAGS_NO_AUTO_START, - NM_SHUTDOWN_TIMEOUT_MS, + NM_SHUTDOWN_TIMEOUT_1500_MSEC, priv->cancellable, _call_destroy_proxy_configuration_cb, conf_id_ref(conf_id)); @@ -315,7 +315,7 @@ _call_create_proxy_configuration(NMPacrunnerManager *self, conf_id->parameters, G_VARIANT_TYPE("(o)"), G_DBUS_CALL_FLAGS_NO_AUTO_START, - NM_SHUTDOWN_TIMEOUT_MS, + NM_SHUTDOWN_TIMEOUT_1500_MSEC, priv->cancellable, _call_create_proxy_configuration_cb, conf_id_ref(conf_id)); diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c index d77fc0a0..2d47772d 100644 --- a/src/core/nm-policy.c +++ b/src/core/nm-policy.c @@ -75,8 +75,9 @@ typedef struct { guint schedule_activate_all_id; /* idle handler for schedule_activate_all(). */ NMPolicyHostnameMode hostname_mode; - char *orig_hostname; /* hostname at NM start time */ - char *cur_hostname; /* hostname we want to assign */ + char *orig_hostname; /* hostname at NM start time */ + char *cur_hostname; /* hostname we want to assign */ + char *cur_hostname_full; /* similar to @last_hostname, but before shortening */ char * last_hostname; /* last hostname NM set (to detect if someone else changed it in the meanwhile) */ @@ -560,6 +561,7 @@ _set_hostname(NMPolicy *self, const char *new_hostname, const char *msg) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self); gs_free char *old_hostname = NULL; + gboolean cur_hostname_full_changed; const char *name; /* The incoming hostname *can* be NULL, which will get translated to @@ -568,32 +570,50 @@ _set_hostname(NMPolicy *self, const char *new_hostname, const char *msg) * there was no valid hostname to start with. */ + if (nm_strdup_reset(&priv->cur_hostname_full, new_hostname)) { + gs_free char *shortened = NULL; + + cur_hostname_full_changed = TRUE; + + if (priv->cur_hostname_full + && !nm_utils_shorten_hostname(priv->cur_hostname_full, &shortened)) { + _LOGW(LOGD_DNS, + "set-hostname: hostname '%s' %s is invalid", + priv->cur_hostname_full, + msg); + return; + } + + if (shortened) { + _LOGI(LOGD_DNS, + "set-hostname: shortened hostname %s from '%s' to '%s'", + msg, + priv->cur_hostname_full, + shortened); + nm_strdup_reset_take(&priv->cur_hostname, g_steal_pointer(&shortened)); + } else + nm_strdup_reset(&priv->cur_hostname, priv->cur_hostname_full); + } else + cur_hostname_full_changed = FALSE; + /* Update the DNS only if the hostname is actually * going to change. */ - if (!nm_streq0(priv->cur_hostname, new_hostname)) { - g_free(priv->cur_hostname); - priv->cur_hostname = g_strdup(new_hostname); - + if (cur_hostname_full_changed) { /* Notify the DNS manager of the hostname change so that the domain part, if * present, can be added to the search list. Set the @updating_dns flag * so that dns_config_changed() doesn't try again to restart DNS lookup. */ priv->updating_dns = TRUE; nm_dns_manager_set_hostname(priv->dns_manager, - priv->cur_hostname, + priv->cur_hostname_full, all_devices_not_active(self)); priv->updating_dns = FALSE; } /* Finally, set kernel hostname */ - if (!new_hostname) - name = FALLBACK_HOSTNAME4; - else if (!new_hostname[0]) { - g_warn_if_reached(); - name = FALLBACK_HOSTNAME4; - } else - name = new_hostname; + nm_assert(!priv->cur_hostname || priv->cur_hostname[0]); + name = priv->cur_hostname ?: FALLBACK_HOSTNAME4; /* Don't set the hostname if it isn't actually changing */ if ((old_hostname = _get_hostname(self)) && (nm_streq(name, old_hostname))) { @@ -602,14 +622,16 @@ _set_hostname(NMPolicy *self, const char *new_hostname, const char *msg) } /* Keep track of the last set hostname */ - g_free(priv->last_hostname); - priv->last_hostname = g_strdup(name); + nm_strdup_reset(&priv->last_hostname, name); priv->changing_hostname = TRUE; _LOGI(LOGD_DNS, "set-hostname: set hostname to '%s' (%s)", name, msg); /* Ask NMSettings to update the transient hostname using its - * systemd-hostnamed proxy */ + * systemd-hostnamed proxy. + * + * FIXME(shutdown): an async request must be cancellable, so we are guaranteed + * to tear down in a reasonable time.*/ nm_hostname_manager_set_transient_hostname(priv->hostname_manager, name, settings_set_hostname_cb, @@ -2761,7 +2783,7 @@ constructed(GObject *object) self); priv->dns_manager = g_object_ref(nm_dns_manager_get()); - nm_dns_manager_set_initial_hostname(priv->dns_manager, priv->orig_hostname); + nm_dns_manager_set_hostname(priv->dns_manager, priv->orig_hostname, TRUE); priv->config_changed_id = g_signal_connect(priv->dns_manager, NM_DNS_MANAGER_CONFIG_CHANGED, G_CALLBACK(dns_config_changed), @@ -2892,6 +2914,7 @@ dispose(GObject *object) nm_clear_g_free(&priv->orig_hostname); nm_clear_g_free(&priv->cur_hostname); + nm_clear_g_free(&priv->cur_hostname_full); nm_clear_g_free(&priv->last_hostname); if (priv->hostname_manager) { diff --git a/src/core/nm-rfkill-manager.c b/src/core/nm-rfkill-manager.c index f3851e5c..03333899 100644 --- a/src/core/nm-rfkill-manager.c +++ b/src/core/nm-rfkill-manager.c @@ -9,6 +9,7 @@ #include <libudev.h> +#include "c-list/src/c-list.h" #include "libnm-udev-aux/nm-udev-utils.h" /*****************************************************************************/ @@ -24,8 +25,9 @@ typedef struct { NMUdevClient *udev_client; /* Authoritative rfkill state (RFKILL_* enum) */ - RfKillState rfkill_states[RFKILL_TYPE_MAX]; - GSList *killswitches; + NMRfkillState rfkill_states[NM_RFKILL_TYPE_MAX]; + + CList killswitch_lst_head; } NMRfkillManagerPrivate; struct _NMRfkillManager { @@ -45,60 +47,81 @@ G_DEFINE_TYPE(NMRfkillManager, nm_rfkill_manager, G_TYPE_OBJECT) /*****************************************************************************/ typedef struct { - char *name; - guint64 seqnum; - char *path; - char *driver; - RfKillType rtype; - int state; - gboolean platform; + CList killswitch_lst; + char *name; + char *path; + char *driver; + guint64 seqnum; + NMRfkillType rtype; + int state; + bool platform : 1; } Killswitch; -RfKillState -nm_rfkill_manager_get_rfkill_state(NMRfkillManager *self, RfKillType rtype) +NMRfkillState +nm_rfkill_manager_get_rfkill_state(NMRfkillManager *self, NMRfkillType rtype) { - g_return_val_if_fail(self != NULL, RFKILL_UNBLOCKED); - g_return_val_if_fail(rtype < RFKILL_TYPE_MAX, RFKILL_UNBLOCKED); + g_return_val_if_fail(self != NULL, NM_RFKILL_STATE_UNBLOCKED); + g_return_val_if_fail(rtype < NM_RFKILL_TYPE_MAX, NM_RFKILL_STATE_UNBLOCKED); return NM_RFKILL_MANAGER_GET_PRIVATE(self)->rfkill_states[rtype]; } -static const char * -rfkill_type_to_desc(RfKillType rtype) +NMRadioFlags +nm_rfkill_type_to_radio_available_flag(NMRfkillType type) +{ + switch (type) { + case NM_RFKILL_TYPE_WLAN: + return NM_RADIO_FLAG_WLAN_AVAILABLE; + case NM_RFKILL_TYPE_WWAN: + return NM_RADIO_FLAG_WWAN_AVAILABLE; + case NM_RFKILL_TYPE_UNKNOWN: + break; + } + return nm_assert_unreachable_val(NM_RADIO_FLAG_NONE); +} + +const char * +nm_rfkill_type_to_string(NMRfkillType type) { - if (rtype == 0) + switch (type) { + case NM_RFKILL_TYPE_WLAN: return "Wi-Fi"; - else if (rtype == 1) + case NM_RFKILL_TYPE_WWAN: return "WWAN"; - else if (rtype == 2) - return "WiMAX"; - return "unknown"; + case NM_RFKILL_TYPE_UNKNOWN: + break; + } + return nm_assert_unreachable_val("unknown"); } static const char * -rfkill_state_to_desc(RfKillState rstate) +nm_rfkill_state_to_string(NMRfkillState state) { - if (rstate == 0) + switch (state) { + case NM_RFKILL_STATE_UNAVAILABLE: + return "unavailable"; + case NM_RFKILL_STATE_UNBLOCKED: return "unblocked"; - else if (rstate == 1) + case NM_RFKILL_STATE_SOFT_BLOCKED: return "soft-blocked"; - else if (rstate == 2) + case NM_RFKILL_STATE_HARD_BLOCKED: return "hard-blocked"; - return "unknown"; + case NM_RFKILL_STATE_HARD_BLOCKED_OS_NOT_OWNER: + return "hard-blocked-os-not-owner"; + } + return nm_assert_unreachable_val("unknown"); } static Killswitch * -killswitch_new(struct udev_device *device, RfKillType rtype) +killswitch_new(struct udev_device *device, NMRfkillType rtype) { Killswitch *ks; - struct udev_device *parent = NULL, *grandparent = NULL; - const char *driver, *subsys, *parent_subsys = NULL; - - ks = g_malloc0(sizeof(Killswitch)); - ks->name = g_strdup(udev_device_get_sysname(device)); - ks->seqnum = udev_device_get_seqnum(device); - ks->path = g_strdup(udev_device_get_syspath(device)); - ks->rtype = rtype; + struct udev_device *parent = NULL; + struct udev_device *grandparent = NULL; + const char *driver; + const char *subsys; + const char *parent_subsys = NULL; + gboolean platform; driver = udev_device_get_property_value(device, "DRIVER"); subsys = udev_device_get_subsystem(device); @@ -116,14 +139,23 @@ killswitch_new(struct udev_device *device, RfKillType rtype) driver = udev_device_get_property_value(grandparent, "DRIVER"); } } - if (!driver) driver = "(unknown)"; - ks->driver = g_strdup(driver); - if (g_strcmp0(subsys, "platform") == 0 || g_strcmp0(parent_subsys, "platform") == 0 - || g_strcmp0(subsys, "acpi") == 0 || g_strcmp0(parent_subsys, "acpi") == 0) - ks->platform = TRUE; + platform = FALSE; + if (nm_streq0(subsys, "platform") || nm_streq0(parent_subsys, "platform") + || nm_streq0(subsys, "acpi") || nm_streq0(parent_subsys, "acpi")) + platform = TRUE; + + ks = g_slice_new(Killswitch); + *ks = (Killswitch){ + .name = g_strdup(udev_device_get_sysname(device)), + .seqnum = udev_device_get_seqnum(device), + .path = g_strdup(udev_device_get_syspath(device)), + .rtype = rtype, + .driver = g_strdup(driver), + .platform = platform, + }; return ks; } @@ -131,55 +163,58 @@ killswitch_new(struct udev_device *device, RfKillType rtype) static void killswitch_destroy(Killswitch *ks) { - g_return_if_fail(ks != NULL); - + c_list_unlink_stale(&ks->killswitch_lst); g_free(ks->name); g_free(ks->path); g_free(ks->driver); - memset(ks, 0, sizeof(Killswitch)); - g_free(ks); + nm_g_slice_free(ks); } -static RfKillState -sysfs_state_to_nm_state(int sysfs_state) +static NMRfkillState +sysfs_state_to_nm_state(int sysfs_state, int sysfs_reason) { switch (sysfs_state) { case 0: - return RFKILL_SOFT_BLOCKED; + return NM_RFKILL_STATE_SOFT_BLOCKED; case 1: - return RFKILL_UNBLOCKED; + return NM_RFKILL_STATE_UNBLOCKED; case 2: - return RFKILL_HARD_BLOCKED; + /* sysfs reason is a bitmap, in case we have both reasons (SIGNAL and NOT_OWNER), we want + * to consider the device as not owned. + */ + if (sysfs_reason & 2) + return NM_RFKILL_STATE_HARD_BLOCKED_OS_NOT_OWNER; + return NM_RFKILL_STATE_HARD_BLOCKED; default: nm_log_warn(LOGD_RFKILL, "unhandled rfkill state %d", sysfs_state); break; } - return RFKILL_UNBLOCKED; + return NM_RFKILL_STATE_UNBLOCKED; } static void recheck_killswitches(NMRfkillManager *self) { NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE(self); - GSList *iter; - RfKillState poll_states[RFKILL_TYPE_MAX]; - RfKillState platform_states[RFKILL_TYPE_MAX]; - gboolean platform_checked[RFKILL_TYPE_MAX]; + Killswitch *ks; + NMRfkillState poll_states[NM_RFKILL_TYPE_MAX]; + NMRfkillState platform_states[NM_RFKILL_TYPE_MAX]; + gboolean platform_checked[NM_RFKILL_TYPE_MAX]; int i; /* Default state is unblocked */ - for (i = 0; i < RFKILL_TYPE_MAX; i++) { - poll_states[i] = RFKILL_UNBLOCKED; - platform_states[i] = RFKILL_UNBLOCKED; + for (i = 0; i < NM_RFKILL_TYPE_MAX; i++) { + poll_states[i] = NM_RFKILL_STATE_UNAVAILABLE; + platform_states[i] = NM_RFKILL_STATE_UNAVAILABLE; platform_checked[i] = FALSE; } /* Poll the states of all killswitches */ - for (iter = priv->killswitches; iter; iter = g_slist_next(iter)) { - Killswitch *ks = iter->data; + c_list_for_each_entry (ks, &priv->killswitch_lst_head, killswitch_lst) { struct udev_device *device; - RfKillState dev_state; + NMRfkillState dev_state; int sysfs_state; + int sysfs_reason; device = udev_device_new_from_subsystem_sysname(nm_udev_client_get_udev(priv->udev_client), "rfkill", @@ -192,15 +227,24 @@ recheck_killswitches(NMRfkillManager *self) G_MININT, G_MAXINT, -1); - dev_state = sysfs_state_to_nm_state(sysfs_state); + + sysfs_reason = _nm_utils_ascii_str_to_int64( + udev_device_get_property_value(device, "RFKILL_HW_BLOCK_REASON"), + 16, + G_MININT, + G_MAXINT, + 1); /* defaults to SIGNAL in case the kernel does not support this */ + + dev_state = sysfs_state_to_nm_state(sysfs_state, sysfs_reason); nm_log_dbg(LOGD_RFKILL, - "%s rfkill%s switch %s state now %d/%u", - rfkill_type_to_desc(ks->rtype), + "%s rfkill%s switch %s state now %d/%s reason: 0x%x", + nm_rfkill_type_to_string(ks->rtype), ks->platform ? " platform" : "", ks->name, sysfs_state, - dev_state); + nm_rfkill_state_to_string(dev_state), + sysfs_reason); if (ks->platform == FALSE) { if (dev_state > poll_states[ks->rtype]) @@ -210,27 +254,32 @@ recheck_killswitches(NMRfkillManager *self) if (dev_state > platform_states[ks->rtype]) platform_states[ks->rtype] = dev_state; } + udev_device_unref(device); } /* Log and emit change signal for final rfkill states */ - for (i = 0; i < RFKILL_TYPE_MAX; i++) { + for (i = 0; i < NM_RFKILL_TYPE_MAX; i++) { if (platform_checked[i] == TRUE) { /* blocked platform switch state overrides device state, otherwise * let the device state stand. (bgo #655773) */ - if (platform_states[i] != RFKILL_UNBLOCKED) + if (platform_states[i] > NM_RFKILL_STATE_UNBLOCKED) poll_states[i] = platform_states[i]; } if (poll_states[i] != priv->rfkill_states[i]) { nm_log_dbg(LOGD_RFKILL, "%s rfkill state now '%s'", - rfkill_type_to_desc(i), - rfkill_state_to_desc(poll_states[i])); + nm_rfkill_type_to_string(i), + nm_rfkill_state_to_string(poll_states[i])); priv->rfkill_states[i] = poll_states[i]; - g_signal_emit(self, signals[RFKILL_CHANGED], 0, i, priv->rfkill_states[i]); + g_signal_emit(self, + signals[RFKILL_CHANGED], + 0, + (guint) i, + (guint) priv->rfkill_states[i]); } } } @@ -239,52 +288,48 @@ static Killswitch * killswitch_find_by_name(NMRfkillManager *self, const char *name) { NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE(self); - GSList *iter; - - g_return_val_if_fail(name != NULL, NULL); + Killswitch *ks; - for (iter = priv->killswitches; iter; iter = g_slist_next(iter)) { - Killswitch *candidate = iter->data; + nm_assert(name); - if (!strcmp(name, candidate->name)) - return candidate; + c_list_for_each_entry (ks, &priv->killswitch_lst_head, killswitch_lst) { + if (nm_streq(name, ks->name)) + return ks; } return NULL; } -static RfKillType +static NMRfkillType rfkill_type_to_enum(const char *str) { - g_return_val_if_fail(str != NULL, RFKILL_TYPE_UNKNOWN); - - if (!strcmp(str, "wlan")) - return RFKILL_TYPE_WLAN; - else if (!strcmp(str, "wwan")) - return RFKILL_TYPE_WWAN; + if (str) { + if (nm_streq(str, "wlan")) + return NM_RFKILL_TYPE_WLAN; + if (nm_streq(str, "wwan")) + return NM_RFKILL_TYPE_WWAN; + } - return RFKILL_TYPE_UNKNOWN; + return NM_RFKILL_TYPE_UNKNOWN; } static void add_one_killswitch(NMRfkillManager *self, struct udev_device *device) { NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE(self); - const char *str_type; - RfKillType rtype; + NMRfkillType rtype; Killswitch *ks; - str_type = udev_device_get_property_value(device, "RFKILL_TYPE"); - rtype = rfkill_type_to_enum(str_type); - if (rtype == RFKILL_TYPE_UNKNOWN) + rtype = rfkill_type_to_enum(udev_device_get_property_value(device, "RFKILL_TYPE")); + if (rtype == NM_RFKILL_TYPE_UNKNOWN) return; - ks = killswitch_new(device, rtype); - priv->killswitches = g_slist_prepend(priv->killswitches, ks); + ks = killswitch_new(device, rtype); + c_list_link_front(&priv->killswitch_lst_head, &ks->killswitch_lst); nm_log_info(LOGD_RFKILL, "%s: found %s radio killswitch (at %s) (%sdriver %s)", ks->name, - rfkill_type_to_desc(rtype), + nm_rfkill_type_to_string(rtype), ks->path, ks->platform ? "platform " : "", ks->driver ?: "<unknown>"); @@ -297,6 +342,7 @@ rfkill_add(NMRfkillManager *self, struct udev_device *device) g_return_if_fail(device != NULL); name = udev_device_get_sysname(device); + g_return_if_fail(name != NULL); if (!killswitch_find_by_name(self, name)) @@ -307,21 +353,20 @@ static void rfkill_remove(NMRfkillManager *self, struct udev_device *device) { NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE(self); - GSList *iter; + Killswitch *ks; const char *name; g_return_if_fail(device != NULL); + name = udev_device_get_sysname(device); - g_return_if_fail(name != NULL); - for (iter = priv->killswitches; iter; iter = g_slist_next(iter)) { - Killswitch *ks = iter->data; + g_return_if_fail(name != NULL); - if (!strcmp(ks->name, name)) { + c_list_for_each_entry (ks, &priv->killswitch_lst_head, killswitch_lst) { + if (nm_streq(ks->name, name)) { nm_log_info(LOGD_RFKILL, "radio killswitch %s disappeared", ks->path); - priv->killswitches = g_slist_remove(priv->killswitches, ks); killswitch_destroy(ks); - break; + return; } } } @@ -339,16 +384,16 @@ handle_uevent(NMUdevClient *client, struct udev_device *device, gpointer user_da /* A bit paranoid */ subsys = udev_device_get_subsystem(device); - g_return_if_fail(!g_strcmp0(subsys, "rfkill")); + g_return_if_fail(nm_streq0(subsys, "rfkill")); nm_log_dbg(LOGD_PLATFORM, "udev rfkill event: action '%s' device '%s'", action, udev_device_get_sysname(device)); - if (!strcmp(action, "add")) + if (nm_streq(action, "add")) rfkill_add(self, device); - else if (!strcmp(action, "remove")) + else if (nm_streq(action, "remove")) rfkill_remove(self, device); recheck_killswitches(self); @@ -364,8 +409,10 @@ nm_rfkill_manager_init(NMRfkillManager *self) struct udev_list_entry *iter; guint i; - for (i = 0; i < RFKILL_TYPE_MAX; i++) - priv->rfkill_states[i] = RFKILL_UNBLOCKED; + c_list_init(&priv->killswitch_lst_head); + + for (i = 0; i < NM_RFKILL_TYPE_MAX; i++) + priv->rfkill_states[i] = NM_RFKILL_STATE_UNAVAILABLE; priv->udev_client = nm_udev_client_new(NM_MAKE_STRV("rfkill"), handle_uevent, self); @@ -399,11 +446,10 @@ dispose(GObject *object) { NMRfkillManager *self = NM_RFKILL_MANAGER(object); NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE(self); + Killswitch *ks; - if (priv->killswitches) { - g_slist_free_full(priv->killswitches, (GDestroyNotify) killswitch_destroy); - priv->killswitches = NULL; - } + while ((ks = c_list_first_entry(&priv->killswitch_lst_head, Killswitch, killswitch_lst))) + killswitch_destroy(ks); priv->udev_client = nm_udev_client_destroy(priv->udev_client); @@ -426,6 +472,6 @@ nm_rfkill_manager_class_init(NMRfkillManagerClass *klass) NULL, G_TYPE_NONE, 2, - G_TYPE_UINT, - G_TYPE_UINT); + G_TYPE_UINT /* NMRfkillType */, + G_TYPE_UINT /* NMRfkillState */); } diff --git a/src/core/nm-rfkill-manager.h b/src/core/nm-rfkill-manager.h index d3f66349..88d0421a 100644 --- a/src/core/nm-rfkill-manager.h +++ b/src/core/nm-rfkill-manager.h @@ -7,23 +7,33 @@ #ifndef __NM_RFKILL_MANAGER_H__ #define __NM_RFKILL_MANAGER_H__ -typedef enum { /*< skip >*/ - RFKILL_UNBLOCKED = 0, - RFKILL_SOFT_BLOCKED = 1, - RFKILL_HARD_BLOCKED = 2 -} RfKillState; - -typedef enum { /*< skip >*/ - RFKILL_TYPE_WLAN = 0, - RFKILL_TYPE_WWAN = 1, - - /* UNKNOWN and MAX should always be 1 more than - * the last rfkill type since RFKILL_TYPE_MAX is +typedef enum { + NM_RFKILL_STATE_UNAVAILABLE = 0, + NM_RFKILL_STATE_UNBLOCKED = 1, + NM_RFKILL_STATE_SOFT_BLOCKED = 2, + NM_RFKILL_STATE_HARD_BLOCKED = 3, + /* NM_RFKILL_STATE_HARD_BLOCKED_OS_NOT_OWNER means that the CSME firmware + * is currently controlling the device. This feature is implmented on Intel + * wifi devices only. + * The NetworkManager can get ownership on the device, but it requires to + * first ask ownership through the iwlmei kernel module. + */ + NM_RFKILL_STATE_HARD_BLOCKED_OS_NOT_OWNER = 4, +} NMRfkillState; + +typedef enum { + NM_RFKILL_TYPE_WLAN = 0, + NM_RFKILL_TYPE_WWAN = 1, + + /* UNKNOWN and MAX should always be 1 more than + * the last rfkill type since NM_RFKILL_TYPE_MAX is * used as an array size. */ - RFKILL_TYPE_UNKNOWN, /* KEEP LAST */ - RFKILL_TYPE_MAX = RFKILL_TYPE_UNKNOWN -} RfKillType; + NM_RFKILL_TYPE_UNKNOWN, /* KEEP LAST */ + NM_RFKILL_TYPE_MAX = NM_RFKILL_TYPE_UNKNOWN, +} NMRfkillType; + +const char *nm_rfkill_type_to_string(NMRfkillType rtype); #define NM_TYPE_RFKILL_MANAGER (nm_rfkill_manager_get_type()) #define NM_RFKILL_MANAGER(obj) \ @@ -43,6 +53,8 @@ GType nm_rfkill_manager_get_type(void); NMRfkillManager *nm_rfkill_manager_new(void); -RfKillState nm_rfkill_manager_get_rfkill_state(NMRfkillManager *manager, RfKillType rtype); +NMRfkillState nm_rfkill_manager_get_rfkill_state(NMRfkillManager *manager, NMRfkillType rtype); + +NMRadioFlags nm_rfkill_type_to_radio_available_flag(NMRfkillType type); #endif /* __NM_RFKILL_MANAGER_H__ */ diff --git a/src/core/nm-session-monitor.c b/src/core/nm-session-monitor.c index 08bfedca..a2e9373c 100644 --- a/src/core/nm-session-monitor.c +++ b/src/core/nm-session-monitor.c @@ -114,13 +114,10 @@ st_sd_init(NMSessionMonitor *monitor) return; } - monitor->sd.watch = nm_g_unix_fd_source_new(sd_login_monitor_get_fd(monitor->sd.monitor), + monitor->sd.watch = nm_g_unix_fd_add_source(sd_login_monitor_get_fd(monitor->sd.monitor), G_IO_IN, - G_PRIORITY_DEFAULT, st_sd_changed, - monitor, - NULL); - g_source_attach(monitor->sd.watch, NULL); + monitor); } static void diff --git a/src/core/nm-test-utils-core.h b/src/core/nm-test-utils-core.h index 6f571972..467f9bb2 100644 --- a/src/core/nm-test-utils-core.h +++ b/src/core/nm-test-utils-core.h @@ -211,12 +211,13 @@ nmtst_platform_ip4_routes_equal(const NMPlatformIP4Route *a, for (i = 0; i < len; i++) { if (nm_platform_ip4_route_cmp_full(&a[i], &b[i]) != 0) { - char buf[sizeof(_nm_utils_to_string_buffer)]; + char buf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; + char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; g_error("Error comparing IPv4 route[%lu]: %s vs %s", (unsigned long) i, - nm_platform_ip4_route_to_string(&a[i], NULL, 0), - nm_platform_ip4_route_to_string(&b[i], buf, sizeof(buf))); + nm_platform_ip4_route_to_string(&a[i], buf1, sizeof(buf1)), + nm_platform_ip4_route_to_string(&b[i], buf2, sizeof(buf2))); g_assert_not_reached(); } } @@ -280,12 +281,13 @@ nmtst_platform_ip6_routes_equal(const NMPlatformIP6Route *a, for (i = 0; i < len; i++) { if (nm_platform_ip6_route_cmp_full(&a[i], &b[i]) != 0) { - char buf[sizeof(_nm_utils_to_string_buffer)]; + char buf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; + char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; g_error("Error comparing IPv6 route[%lu]: %s vs %s", (unsigned long) i, - nm_platform_ip6_route_to_string(&a[i], NULL, 0), - nm_platform_ip6_route_to_string(&b[i], buf, sizeof(buf))); + nm_platform_ip6_route_to_string(&a[i], buf1, sizeof(buf1)), + nm_platform_ip6_route_to_string(&b[i], buf2, sizeof(buf2))); g_assert_not_reached(); } } diff --git a/src/core/platform/tests/test-common.c b/src/core/platform/tests/test-common.c index 9053d351..d7fed220 100644 --- a/src/core/platform/tests/test-common.c +++ b/src/core/platform/tests/test-common.c @@ -239,8 +239,9 @@ _nmtstp_platform_ip_addresses_assert(const char *filename, } } if ((IS_IPv4 ? force_exact_4 : force_exact_6) && nm_g_ptr_array_len(plat_addrs) > 0) { - char sbuf[sizeof(_nm_utils_to_string_buffer)]; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("%s:%d: %u IPv%c addresses found on ifindex %d that should not be there (one " "is %s)", filename, @@ -252,6 +253,7 @@ _nmtstp_platform_ip_addresses_assert(const char *filename, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); + NM_PRAGMA_WARNING_REENABLE } } } @@ -580,6 +582,7 @@ _nmtstp_assert_ip4_route_exists(const char *file, if (c != c_exists && c_exists != -1) { char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("[%s:%u] %s(): The ip4 route %s/%d metric %u tos %u shall exist %u times, but " "platform has it %u times", file, @@ -591,6 +594,7 @@ _nmtstp_assert_ip4_route_exists(const char *file, tos, c_exists, c); + NM_PRAGMA_WARNING_REENABLE } return r; @@ -673,6 +677,7 @@ _nmtstp_assert_ip6_route_exists(const char *file, char s_src[NM_UTILS_INET_ADDRSTRLEN]; char s_network[NM_UTILS_INET_ADDRSTRLEN]; + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("[%s:%u] %s(): The ip6 route %s/%d metric %u src %s/%d shall exist %u times, but " "platform has it %u times", file, @@ -685,6 +690,7 @@ _nmtstp_assert_ip6_route_exists(const char *file, src_plen, c_exists, c); + NM_PRAGMA_WARNING_REENABLE } return r; @@ -2772,13 +2778,7 @@ nmtstp_acd_defender_new(int ifindex, in_addr_t ip_addr, const NMEtherAddr *mac_a n_acd_get_fd(defender->nacd, &fd); g_assert_cmpint(fd, >=, 0); - defender->source = nm_g_source_attach(nm_g_unix_fd_source_new(fd, - G_IO_IN, - G_PRIORITY_DEFAULT, - _l3_acd_nacd_event, - defender, - NULL), - NULL); + defender->source = nm_g_unix_fd_add_source(fd, G_IO_IN, _l3_acd_nacd_event, defender); return defender; } diff --git a/src/core/platform/tests/test-platform-general.c b/src/core/platform/tests/test-platform-general.c index 9629326a..93e525dd 100644 --- a/src/core/platform/tests/test-platform-general.c +++ b/src/core/platform/tests/test-platform-general.c @@ -101,9 +101,9 @@ static void test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data) { static const char *const EXPECTED_BUFFER[3] = { - [0] = ("5b1aea34648cabfec7c3523f76cf1ce34ca17a9a32f3f0f218424e48836dd1cb504e03d53e1124c5" + [0] = ("5b1aea34648cabfec7c3523f76cf1ce34ca17a9a32f3f0f218024e48836dd1cb504e03d53e1124c5" "0065aeb2e6fbf952902383028e3b47f280f062ea1a7e0b7be218d067530e1b0487b8c3b99f2b8a1a" - "8982c42f0000003437c5156e072b2f2f0037c9cfe07c34ddb3980deb14ab7b5af84a034703000000" + "8907c42f0000003437c5156e072b2f2f0037c9cfe07c34ddb3980deb14ab7b5af84a034703000000" "883b0f3fd6ed84d6c959e553b887edcd6101f5d200000043b809d259e499db7d00f1853bdcb0e4bc" "0e2b00b667b7b16d8d1e69c803000000b973972c17a47631c169f11ff9119c40b403b6630000007a" "034f43340d01683c0045097aea4a849f060ddf57b24a5be9636360d603000000ad7c499dd538d345" @@ -115,50 +115,50 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data) "6663861eea1bf42c12ea3b9503000000fb95e8332fdfff658483a2d039a7bf148e02481e00000060" "e89f7abdb682380a00eae374835b4a49a2b980b6aba92da6409969aa03000000e00473755d31e5b2" "de252167c1c91b3a36020c700000007740318db913a353ed006efc068829c0e66ad0143a0554efb4" - "40e55b8b03000000c0cfb2b4386bec092fa5757ecde9348bda002ebd000000ab667224dae775e5cc" - "0041aca2ff0f576767d3648102b61886d149f07403000000153ece68ade15cec25a59273e7519f34" - "c4458d70000000f3819aa46fbe1439340033ae6dec0fb124f264af67eed7c9a8ecc8fb1103000000" + "d149f07403000000153ece68ade15cec25a59273e7519f34c40d8d70000000f3819aa46fbe143934" + "0033ae6dec0fb124f264af67eed7c9a840e55b8b03000000c0cfb2b4386bec092fa5757ecde9348b" + "da002ebd000000ab667224dae775e5cc0041aca2ff0f576767d3648102b61886ecc8fb1103000000" "fcebbaeb0c56535923f14874042a8aff1d028e5ec3cc13cc36bbe3c9bb0ec36f00e007bb64a2827b" "7cdd38d0314c178e5a06c40b03000000ab90135fa636af4464d210a256be75e0500244770000004b" "2e69220d6c0fc09c25d6534c809829af4a9df58dbfef186d416f3a1e030000002c932e655203d82a" "3c84c4eb274ed18687030281000000f2235376239daeaacb3cae864b437baaae91921681c2162b9b" "69e66142030000004fda8a3e0b841cf76391dd68269b53eccb02a831000000b78c54dda9ac3bb1b1" "d43e6505621b9a7f0422ae3fc8979ee0416f95e70300000057d6249b652ba98c7dc7f17f666969e4" - "5c02af7a000000ee0b06fa9e988f80f0de6dc8dfcf2a3ad3bbcc0fa3b314f695111d891d03000000" - "cd897619f51f44e644d7cf1d06b2b115d800549e62c12fba5b1cdec48d10bbb13b8313d8cd2a24d3" - "4fc812bd2f8a59d90fcc00ac030000005292cd32dc096cd5d8a4c5cf3351ee1cfc03056c00000051" - "bbce426cfa4b861cc78592be7b14e7ba9c15acb881ae55f0e5fe7d360300000066a3ae3939762df3" - "3a2d55060c78d551cb0010fd00000041b9aca07b6e4925dd27943a272c171ed15abbbe1cd911db7b" - "86ed271803000000a5edc511c1507a141e0f515638c7ba31f50209450000003357ae79989870ccec" - "3def0ad92749e016663fe6ee0228c1da82d1595603000000348352d715cf9d411ea012e5307294b6" - "e301dac4000000075efee38dd16f8ee4ccd2f50c30706cae3fdcc2f0ee3d5e26bb20413203000000" - "862573c2303dd1d65c7b2cffeca6d1adaccae11f0000000f855ebf3b772eb2b1c896c9a7304f6645" - "0a5f6abd850b06e3b10123e6030000001dff045298cfee0636674cdefb57b9ae54cfe8f400000038" - "1ba2c4396de60f032bc7f34de2959871c0d4c0d4eb720c4ab550c5db03000000f32f4af595d785d5" - "c1b5aad487c192f08bd7a09b00000043288cd9bf66ec305a225a0c71b2ce78bb16104c8eaf18c565" - "b16c7376030000005219061ab4c5c79489b2cc6a883c146972decb8b000000f5e6d66df46ea13910" - "7754dee62c36d2fc70ccc567df7a49b8585287dc03000000cfb18b2b2cb749e2e03e544d0eb4f73e" - "75039fcf251b32fc79685b05ddd3aa9ba511d2e40edb4d758fb554158ae5c7c0beb42b3403000000" + "5c02af7a000000ee0b06fa9e988f80f0de6dc8dfcf2a3ad3bbcc0fa3b314f695b550c5db03000000" + "f32f4af595d785d5c1b5aad487c192f08b12a09b00000043288cd9bf66ec305a225a0c71b2ce78bb" + "16104c8eaf18c565111d891d03000000cd897619f51f44e644d7cf1d06b2b115d800549e62c12fba" + "5b1cdec48d10bbb13b8313d8cd2a24d34fc812bd2f8a59d90fcc00ac030000005292cd32dc096cd5" + "d8a4c5cf3351ee1cfc03056c00000051bbce426cfa4b861cc78592be7b14e7ba9c15acb881ae55f0" + "b16c7376030000005219061ab4c5c79489b2cc6a883c14697205cb8b000000f5e6d66df46ea13910" + "7754dee62c36d2fc70ccc567df7a49b8e5fe7d360300000066a3ae3939762df33a2d55060c78d551" + "cb0010fd00000041b9aca07b6e4925dd27943a272c171ed15abbbe1cd911db7b86ed271803000000" + "a5edc511c1507a141e0f515638c7ba31f50209450000003357ae79989870ccec3def0ad92749e016" + "663fe6ee0228c1dabb20413203000000862573c2303dd1d65c7b2cffeca6d1adac11e11f0000000f" + "855ebf3b772eb2b1c896c9a7304f66450a5f6abd850b06e382d1595603000000348352d715cf9d41" + "1ea012e5307294b6e301dac4000000075efee38dd16f8ee4ccd2f50c30706cae3fdcc2f0ee3d5e26" + "b10123e6030000001dff045298cfee0636674cdefb57b9ae541de8f4000000381ba2c4396de60f03" + "2bc7f34de2959871c0d4c0d4eb720c4a585287dc03000000cfb18b2b2cb749e2e03e544d0eb4f73e" + "751d9fcf251b32fc79685b05ddd3aa9ba511d2e40edb4d758fb554158ae5c7c0beb42b3403000000" "895d5f24037d233302ad3b82d639272e4a02eadfbd2146bf8cfdb205f90e54b58a6ee136a779f37c" - "30d2c5053c40ecaec38b6b8e03000000bea73223e59bf0193432e9fa7a899f2d8ec7e4b89bf5a5d0" + "30d2c5053c40ecaec38b6b8e03000000bea73223e59bf0193432e9fa7a899f2d8e0ae4b89bf5a5d0" "6776e66a9d80ab132e1ac921eb76adbb229df32e561fa80a0fc4676703000000a23eb66e720da9e0" "7ac998b5690807d52602369ee1af4ca5a6a95581af5fd7ceacdca10f47d7b351a36b178aabc78a4a" "1a0dd8c003000000e2815a1a37a52bddd1c2f1018b587eed720358f0e9201f17bd99fcf72909ac9e" "7a55299e9bb4fd53bc7417940fcffe3f81cafd6302000000d6732578acd14320aefd4503189f7630" - "2403501c0000002b9f3c39f24b0572b100745cb25851429b3bbfb50168dfd04eb62f22ca02000000" - "891715df7fc6a902edae579e2e10c7f7a202a0340436242cbeb0248cee3fbc160032d4f28aa28c08" - "f80dd50c6712dfb4abba4a32020000008ffe423d01883918039249f398f9b37ea091465100000064" - "3722d9b707c0d8a400b7c8307f06b4b29088f20d9ac676d5e4bafc4e02000000fde69eec3af2e6d0" - "bd68ab722af14548b29572e504265f6c72923e22594f3f790008ed2e2ebb0771db46a54cadb245ea" + "2403501c0000002b9f3c39f24b0572b100745cb25851429b3bbfb50168dfd04eabba4a3202000000" + "8ffe423d01883918039249f398f9b37ea0104651000000643722d9b707c0d8a400b7c8307f06b4b2" + "9088f20d9ac676d5b62f22ca02000000891715df7fc6a902edae579e2e10c7f7a202a0340436242c" + "beb0248cee3fbc160032d4f28aa28c08f80dd50c6712dfb4e4bafc4e02000000fde69eec3af2e6d0" + "bd68ab722af14548b20672e504265f6c72923e22594f3f790008ed2e2ebb0771db46a54cadb245ea" "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c01a56dfdcfc1155f236c8b9c79a620" "00660bbf024b03ff0a8e27c405e64244e36f90d402000000fd41fe47684b370b6ec6584d64496089" - "570968ead4d1ae91c819bb068196d59900de3246e43f5e7945aaf95e2ffa3a11d1e1b34902000000" - "bcd7be07d78e6222e45aaf61814f703b40125e6b000000cbaaa37b861e6d46dafe7d6ec4ac1ea051" - "010911915ddb05f29c64ed0402000000a660ac824b7fae389861419c50da49bf2b02258300000025" + "570568ead4d1ae91c819bb068196d59900de3246e43f5e7945aaf95e2ffa3a11641e447502000000" + "b9b68c08a5e4351ea349e1ccf662e058b819a45100000045fcb6a035339d504c9726d80d9c2d89df" + "765b4d9a130257dc9c64ed0402000000a660ac824b7fae389861419c50da49bf2b02258300000025" "9f9f0251becc987907879cca68fec7bbbb5f8edf248b4995d184e82002000000a19cdf6dd1c173f6" "078a806d329c9b008b00c972000000f5b2cd3dbddc74e26de958e48d2ab8b0313e7f8933e315130f" - "641e447502000000b9b68c08a5e4351ea349e1ccf662e058b879a45100000045fcb6a035339d504c" - "9726d80d9c2d89df765b4d9a130257dcc43bd794010000005bfe47c6f53a54e01b0c1d89414d94d0" + "d1e1b34902000000bcd7be07d78e6222e45aaf61814f703b401f5e6b000000cbaaa37b861e6d46da" + "fe7d6ec4ac1ea051010911915ddb05f2c43bd794010000005bfe47c6f53a54e01b0c1d89414d94d0" "e0032ec50000000103781b0f294a2b7300421398f4de67e9cee64b38b56e03e01539ce4101000000" "18238487a417f3da01d99dae5f190096cc012cab0000005b2363d13edc5aa115005eb914d8fbe9a4" "fdb3d117d76b0de5bd82e9ea010000003d1b91caae8cb60b49ba9be338d856a40c02c3d400000064" @@ -172,49 +172,49 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data) "b900b2061d0c334b009f2dd1cdf64b0f9a60e0e289f08db3fde6b0250100000039dd8f88152a5845" "4d9ca9d20f45dfa774028604000000fbcd7db68b9ae586da00b4070c50320427c4dd3d031e33f22c" "210aeb09010000007000b96d06992b6a58acd3995b9663d2df02f333ef467092818aa77d6732b678" - "00844ef5a943825fcd743f59bd14c89b955e1a13010000007acbce3e3bcdf3824f1b134847ff26ba" - "4400774900000017ec852d59f3d17232edd86ad6c3103a68843a9aef34983882d3d3878501000000" - "5e0dfe491d1ba96742c7b5e02b2271229c01b02a06d0dc5504b0595daf37deb499996bfb667f072d" - "ec1e5d9cdc8a11f4409bcfb30100000099d90a8543961b2ccd47724a3c460ba85103f4c500000063" - "eafb1ac4c0982b283aa9986700b2a2b3ed257b8b0489f48f053ec8ca0100000027335a25a364d101" - "5ffac03089f45539e1021784000000d7b83579b8da27345a72437f9b6245de39ec9e71ee4b951507" - "f121014a01000000efc67bce716c856e3973dc42a1003be94f89d8ee0000009b5d5bbe6c10085f3d" - "6176f78a19bb8df1804c122fce5078c156e8f3fe010000000ea8042602a1f8e6f5657f3f9e3eb807" - "cbad7645000000b8df6f628a70456d79f25d5895fb57fa60d9279fb2b8fcbac65ad47b8a01000000" - "da40d88d40a6d75bc404156225b7eedefc2b44574b15e2ae496ad01bf007eacb0a28aec868282510" - "b60291ea6480e356925b568a0000000087bb24e5264fd3ebe9cf9f6df9615189f701e815000000a7" - "5c9555876b6a3f13002b6cb8360feaac1d5c302df59dd32a7a859db500000000362956cd46646a0e" - "222160e5f769bb295703ed370000005b6a813387e99bb834009da86c64fefab2548759d313a5b92d" - "8e47935e0000000034f0386a253c21d94064f6b021281e235f00ae20000000d71fd050bf8d85055b" - "00e3756ccdb3455c60ca7b11c66af76e594f24a100000000e143fd52599364e13468f80fd514573f" - "b572671c0000006932d1d5f5d0ce2cf6007a70ba5193a162bc92ec1b11d9172c857ae81200000000" - "3e29535402e9b690c628d048eacce745ea213cb1000000b632ef3be6070dafa200187470e9da5570" - "9427c226d324d9a08487fd0d00000000b7a350f9fc1519defa7db4532545666937c22a3b000000dc" + "00844ef5a943825fcd743f59bd14c89bf121014a01000000efc67bce716c856e3973dc42a1003be9" + "4f0ed8ee0000009b5d5bbe6c10085f3d6176f78a19bb8df1804c122fce5078c1955e1a1301000000" + "7acbce3e3bcdf3824f1b134847ff26ba4400774900000017ec852d59f3d17232edd86ad6c3103a68" + "843a9aef34983882d3d38785010000005e0dfe491d1ba96742c7b5e02b2271229c01b02a06d0dc55" + "04b0595daf37deb499996bfb667f072dec1e5d9cdc8a11f4409bcfb30100000099d90a8543961b2c" + "cd47724a3c460ba85103f4c500000063eafb1ac4c0982b283aa9986700b2a2b3ed257b8b0489f48f" + "053ec8ca0100000027335a25a364d1015ffac03089f45539e1021784000000d7b83579b8da27345a" + "72437f9b6245de39ec9e71ee4b95150756e8f3fe010000000ea8042602a1f8e6f5657f3f9e3eb807" + "cb067645000000b8df6f628a70456d79f25d5895fb57fa60d9279fb2b8fcbac65ad47b8a01000000" + "da40d88d40a6d75bc404156225b7eedefc1e44574b15e2ae496ad01bf007eacb0a28aec868282510" + "b60291ea6480e356594f24a100000000e143fd52599364e13468f80fd514573fb517671c00000069" + "32d1d5f5d0ce2cf6007a70ba5193a162bc92ec1b11d9172c925b568a0000000087bb24e5264fd3eb" + "e9cf9f6df9615189f701e815000000a75c9555876b6a3f13002b6cb8360feaac1d5c302df59dd32a" + "7a859db500000000362956cd46646a0e222160e5f769bb295703ed370000005b6a813387e99bb834" + "009da86c64fefab2548759d313a5b92d8e47935e0000000034f0386a253c21d94064f6b021281e23" + "5f00ae20000000d71fd050bf8d85055b00e3756ccdb3455c60ca7b11c66af76e857ae81200000000" + "3e29535402e9b690c628d048eacce745ea053cb1000000b632ef3be6070dafa200187470e9da5570" + "9427c226d324d9a08487fd0d00000000b7a350f9fc1519defa7db45325456669371e2a3b000000dc" "c405cbee5016c25200d8901d7a0165fe20744edb6ba04f14a4c73cf500000000a4bab14874afdf54" - "e6aae816430607ca0675e09818e9bbec5918c59068baf76a008940f6fc3bbdc7f6090f756aae660b" + "e6aae816430607ca061fe09818e9bbec5918c59068baf76a008940f6fc3bbdc7f6090f756aae660b" "6e4c699300000000d9c1e67743efb54e54270e46042e91186a034e38000000376feecb80ac245409" "c0becc271d9c2f67179bff0644399ae7df6b3542000000004c5cf8107ba282f4f983821918f93e74" - "2d08f0550000006f2292362e5d68265d9f98c82d9b7a559be3acf4fc36fa6b51e3c9472e00000000" + "2d00f0550000006f2292362e5d68265d9f98c82d9b7a559be3acf4fc36fa6b51e3c9472e00000000" "c5cfd9f2343b21362c19a0921dce2f839200fa45000000270b9977e166bee737fe73670c439a644c" - "323b59b4cd20eb7dabea74f700000000f6989d2d6a909e986ff7add5df2c93e05459507b0000000f" - "466554d2ae4d52a8c67b2e48b47003c81785d3ffdbd9a61759747cf200000000ab2dadc5a39411fd" - "4ff1116d478987316a553fc2000000cfc6ebe434a7ae8ff040483e310819e3b10db116431ec6f769" - "438a72e1000000002495a609675344f7e2e3a5ebaec3c85f0a1742f70abe95c50345132a61eda239" - "d9d083c3bf085387046ef8a36f0e9e696b382ab0000000009a6ce5d906837dbca6a5ee19d6f63fe9" + "323b59b4cd20eb7dabea74f700000000f6989d2d6a909e986ff7add5df2c93e05412507b0000000f" + "466554d2ae4d52a8c67b2e48b47003c81785d3ffdbd9a617438a72e1000000002495a609675344f7" + "e2e3a5ebaec3c85f0a0042f70abe95c50345132a61eda239d9d083c3bf085387046ef8a36f0e9e69" + "59747cf200000000ab2dadc5a39411fd4ff1116d478987316a1f3fc2000000cfc6ebe434a7ae8ff0" + "40483e310819e3b10db116431ec6f7696b382ab0000000009a6ce5d906837dbca6a5ee19d6f63fe9" "cb0301f7246f13b2050424a2b3a45ef7a029c896b4132bd895072cfeffe9d6815997069500000000" "0d3c723b91adb0da7c4aa7e7eb5a15bcde035fb98b841fd84cc43c510385b9a4c2aac1d67a909b29" - "7c703915312e9c3cae02dfa000000000dd603bd35e7fa0f02f2f3313d8469d09a92409c0b7f0318a" + "7c703915312e9c3cae02dfa000000000dd603bd35e7fa0f02f2f3313d8469d09a91709c0b7f0318a" "575a4f8e061db3dd7fde25654a4059d565dbc8a91e3b4457b077ddad3108be69f9b97d05c917ad6b" - "10e693bb6e26f2ba90c8e909a9fe20e5c7a4c656482a9b0d00625009a40aeb62a42b6a62548e3c38" + "10e693bb6e26f2ba900fe909a9fe20e5c7a4c656482a9b0d00625009a40aeb62a42b6a62548e3c38" "cd3c72f203000000ca82ac5180101be4f85cef468ea086ea9a01fdc3a9fe1ec787bc45db7c52a52d" "00bd39a44e8e8bc17c01ac63eca0c1cf5ff7f03a03000000c9a89192c1c8be55281a59d1fd338f35" - "7e00f8cea9fec34573654ea6624f138ef9531cd9367a02e4d241989477a363d53b02239e03000000" - "24438387def0f4c6544e4b275d9b7146a70010d2a9fec17647176b7c07d856e3b883efebc09dd9d6" - "1966b7ae7412041d57393c6f03000000182c0287822a272bec4501a1e27acfee7a0188ffa9fe6cae" - "426de59560fad65d67c624f285d7174177a47579dda0b6eaa9a84c820300000070b1646d8026e9f1" - "704f1b16286ba2dabc01f082a9feed33ef60a8b540b26f66761d1f13badfad0fe8fa8f3c1aad2a82" - "fa40546c03000000df2d7c2790d3119a051bb2ee8192ac0cfa3abc1ea9fe3e7d75a2f42b50c6a363" - "40132378b95c59313bacba64dbe996206e6904f50200000047150b9b14010469823acb72bb89182f" + "7e00f8cea9fec34573654ea6624f138ef9531cd9367a02e4d241989477a363d5fa40546c03000000" + "df2d7c2790d3119a051bb2ee8192ac0cfa00bc1ea9fe3e7d75a2f42b50c6a36340132378b95c5931" + "3bacba64dbe996203b02239e0300000024438387def0f4c6544e4b275d9b7146a70010d2a9fec176" + "47176b7c07d856e3b883efebc09dd9d61966b7ae7412041d57393c6f03000000182c0287822a272b" + "ec4501a1e27acfee7a0188ffa9fe6cae426de59560fad65d67c624f285d7174177a47579dda0b6ea" + "a9a84c820300000070b1646d8026e9f1704f1b16286ba2dabc01f082a9feed33ef60a8b540b26f66" + "761d1f13badfad0fe8fa8f3c1aad2a826e6904f50200000047150b9b14010469823acb72bb89182f" "93002196a9feb9153b36bc60be5b534e006527f67485ab35aca0c7ee419733853cf09e8b02000000" "e79c10acfce165e332a62384ec04e5bab40085ada9fe0070a36dd51323b2c54200154d12f86c260a" "9edfa7a74c1c83c1050f63f802000000443cacf59c6379a44b7892f487afa98cb102c19ca9fe942f" @@ -222,7 +222,7 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data) "36506ebd455e679cef009bb8a9fecec3f8c8fa6867a982be8a934f852cc3d4d82bc0ec7303f99f8f" "def85b7502000000a0bef8675b29a197b7b3cceaf5f1bb12c503256aa9fe6e5d58099ffc4a503a71" "2350acbd48411f0dc15d2f0f49dad345d966279502000000e06302aba042aaa218dc091e9aa1477f" - "6fdc9830a9fec95829a8838314dff34d24c332219a1b163a732d803e0e2f4f916d06412601000000" + "6f059830a9fec95829a8838314dff34d24c332219a1b163a732d803e0e2f4f916d06412601000000" "98c39e7cc282208fefc57ff447036b955101cb22a9fe793f797a3c7dadd1c86e009d0c90bc512e13" "7dcef5e4a27985bd5cfd5ce601000000152f2b70eaef7443e0f79ab6902dde533601ec71a9fe9f25" "4ac95883195580410062ed564153e17478f8c3344d89c0bbfaa100fd01000000be184524a6bdc878" @@ -230,7 +230,7 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data) "0d80684e01000000ba8110fb9733cc24904f288262e6ea77a203a5f8a9feeefa701d120523bd98f2" "00098b43cd68be6e3f81268193fd637e9037d7a701000000c47cf0f551e96770a754ac19ef820fe0" "2900f2d3a9fe049150b8d10ab700cc3a7cf51be0403b654ba2f56808092069af5f5b481b01000000" - "68cb3bc873b04d937a6ed8f7bc51e54066fed098a9fe048a92d3adc69a84eb47622400207799416a" + "68cb3bc873b04d937a6ed8f7bc51e5406617d098a9fe048a92d3adc69a84eb47622400207799416a" "f1f0a086fbd7e2f7dea0077a00000000c386e9c6e6a2cbfa10ee58bdc75183609900d627a9feb1cb" "e491cbbbf9443fd6007eb3c5bf64b671d6f18dbf463f9b83f512dc1c00000000fbab244735d67c61" "283031667b2d74a10302b1b1a9fe2aa590a2312e17f1a35900459582f4ef43c780908872746e39ef" @@ -241,306 +241,306 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data) "3215abf8fbdf17c3677e5a3a00000000608df061d45d864d09f4ecf17625f82da1034828a9fe1e37" "1051852c972ea7954079884af257b044fd13a6826a4c619f3d136cac000000009402a4c216772167" "3f2b02b3256ead1f2f039bc1a9fefca162fb81e733cff620ca7feefe1933631e8e69f6d9d6962d2c"), - [1] = ("54270e46020000006a894e387625da376feecb80ac245409c0becc271d9c2f67179bff0644399ae7" + [1] = ("54270e46020000006a894e387625da376feecb80ac245409c058cc271d9c2f67179bff0644399ae7" "9c64ed0432d599eaa660ac824b7fae389861419c7a899f2d010000009bf5a5d06776e66a9d80ab13" - "2e1ac921eb76adbb229df32e561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b" - "c4e1966800000000b3121cd5ef51e696c816290dbaee0e7726d082e1530ff5397c9125f59577d71c" + "2e1ac921eb04adbb229df32e561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b" + "c4e1966800000000b3121cd5ef51e696c816290dbaee0e77264c82e1530ff5397c9125f59577d71c" "4b258a005116d11354edff62ceaa458fc75a91c425a5927300000000c4458d70911714f1819aa46f" - "be143934c933ae6dec0fb124f264af67eed7c9a8c38b6b8ee2614344bea73223e59bf0193432e9fa" - "4cc43c5103000000c2aac1d67a909b297c703915312e9c3c5a06c40b20ea99d5ab90135fa636af44" + "be143934c915ae6dec0fb124f264af67eed7c9a8c38b6b8ee2614344bea73223e59bf0193432e9fa" + "4cc43c5103000000c2aac1d67a909b297c703915312e9c3c5a64c40b20ea99d5ab90135fa636af44" "64d210a256be75e0509c4477d19ac64b2e69220d12ea3b9501000000fb95e8332fdfff658483a2d0" - "39a7bf148ed3481e0bccc460e89f7abdb682380a62eae374835b4a49a2b980b6aba92da6409969aa" - "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ecc8fb112087b97cfcebbaeb0c565359" + "39a7bf148e4c481e0bccc460e89f7abdb682380a62eae374835b4a49a2b980b6aba92da6409969aa" + "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ec5ffb112087b97cfcebbaeb0c565359" "23f14874042a8aff1d1f8e5ec3cc13cc36bbe3c9bb0ec36f0000000064a2827b7cdd38d0314c178e" - "925b568ae56bc4e9fefb24e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13" - "02e9b69000000000eacce745ea213cb1a84035b632ef3be6070dafa2fed87470e9da55709427c226" + "925b568ae566c4e9fefb24e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13" + "02e9b69000000000eacce745ea213cb1a84035b632ef3be60755afa2fed87470e9da55709427c226" "d324d9a086ed27184b443181a5edc511c1507a142fea9bc10300000062fb81e733cff620ca7feefe" - "1933631e8e69f6d9fed62d2c6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196" - "9985d62700000000e491cbbbf9443fd6a77eb3c5bf64b671d6f18dbffeff9b8306c83bc026977911" + "1933631e8e18f6d9fed62d2c6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196" + "9985d62700000000e491cbbbf9443fd6a77eb3c5bf64b671d6098dbffeff9b8306c83bc026977911" "4bdade862c224f6f36506ebd455e679cef369bb8d514573f03000000f6932f6932d1d5f5d0ce2cf6" - "bf7a70ba5193a162fe92ec1b11d9172cf84a03478b82cdd8883b0f3fd6ed84d6c959e553b887edcd" - "6297dfa700000000fd0d53bb6215de4bb6f6f3d031a790287e45e9c1feb836c0a25ea71f5daaed4d" + "bf7a70ba5111a162fe92ec1b11d9172cf84a03478b82cdd8883b0f3fd6ed84d6c959e553b887edcd" + "6297dfa700000000fd0d53bb6215de4bb6f6f3d031a790287e7be9c1feb836c0a25ea71f5daaed4d" "99eb2ebebe3c432fec7d3abc4ccf30d3aaaf02a41e0f515601000000f57609453df7803357ae7998" - "9870ccec3def0ad9000000000000000000000000000000019f05a16768cb3bc873b04d937a6ed8f7" - "ae8cb60b0300000038d856a40c67c3d4afa9c8649da90bd2fff2f256000000000000000000000000" + "9870ccec3d4f0ad9000000000000000000000000000000019f05a16768cb3bc873b04d937a6ed8f7" + "ae8cb60b0300000038d856a40c67c3d4afa9c8649da90bd2ff4cf256000000000000000000000000" "00000001955e1a13d1111b067acbce3e3bcdf38200154d12010000009edfa7a74c1c83c18e47935e" - "8354846e34f0386a00000000000000000000000000000001ac26c7d41fd050bf8d85055bcfe3756c" - "a2f56808092069affaa100fdfdd68d34be184524a6bdc8789cf85178000000000000000000000000" + "8354846e346e386a00000000000000000000000000000001ac26c7d41fd050bf8d85055bcfe3756c" + "a2f56808092069affaa100fdfdd68d34be184524a6bdc8789c575178000000000000000000000000" "00000000020ffdb0e612098610e52bb2a16a40086f0e9e6903000000ec2e2924348352d715cf9d41" - "1ea012e5307294b600000000000000000000000000000000ccd2f50c30706cae3fdcc2f0ee3d5e26" - "81ae55f0030000000aa20624fbab244735d67c61283031667b2d74a1000000000000000000000000" + "1ea012e5305a94b600000000000000000000000000000000ccd2f50c30706cae3fdcc2f0ee3d5e26" + "81ae55f0030000000aa20624fbab244735d67c61283031667b2274a1000000000000000000000000" "0000000000459582f4ef43c780908872746e39efaefd545b020000007ced4f8b362956cd46646a0e" - "222160e5f769bb2900000000000000000000000000000000a19da86c64fefab2548759d313a5b92d" - "f05d7308020000009d82c856ba942455050f63f8282f3464443cacf5000000000000000000000000" + "222160e5f749bb2900000000000000000000000000000000a19da86c64fefab2548759d313a5b92d" + "f05d7308020000009d82c856ba942455050f63f8282f34644466acf5000000000000000000000000" "0000000069d9942f460bcea75481f25e307d0de96e91b91501000000be5b534e0a6527f67485ab35" - "aca0c7ee4197338500000000000000000000000000000000aefd4503189f7630248e501c9052c22b" - "5d68265d010000009b7a559be3acf4fc36fa6b513cf09e8b99289462000000000000000000000000" + "aca0c7ee4167338500000000000000000000000000000000aefd4503189f7630248e501c9052c22b" + "5d68265d010000009b7a559be3acf4fc36fa6b513cf09e8b99729462000000000000000000000000" "00000000b49185ad9b7d0070a36dd51323b2c542e5fe7d360100000066a3ae3939762df33a2d5506" - "0c78d551cbb110fd000000000000000000000000000000002c171ed15abbbe1cd911db7b56e8f3fe" - "9a6ce5d900000000a6a5ee19d6f63fe9cbdd01f7246f13b2050424a2000000000000000000000000" + "0c78d551cb7410fd000000000000000000000000000000002c171ed15abbbe1cd911db7b56e8f3fe" + "9a6ce5d900000000a6a5ee19d6f63fe9cbdd01f7246f13b2055624a2000000000000000000000000" "00000000ffe9d6812231bb02f922259b173c333cbae87d60000000005d31e5b2de252167c1c91b3a" - "36ba0c700ee78477000000000000000000000000000000006ad0143a0554efb46b382ab0404cdf02" - "7c52a52d030000004e8e8bc17c01ac63eca0c1cfbb204132fb7213e4000000000000000000000000" + "36ba0c700e538477000000000000000000000000000000006ad0143a0554efb46b382ab0404cdf02" + "7c52a52d030000004e8e8bc17c01ac63eca0c1cfbb204132fb0813e4000000000000000000000000" "00000000accae11f8045a80f855ebf3b772eb2b1d03bc90503000000eb760e3f004a2efc8ffc96b8" - "9b1bc8f415bd4e7700000000000000000000000000000000d149f074a378b881153ece68ade15cec" - "7666c05303000000ffa8e89871fb9510b900b2061d0c334b819f2dd1000000000000000000000000" + "9b1bc8f415174e7700000000000000000000000000000000d149f074a378b881153ece68ade15cec" + "7666c05303000000ffa8e89871fb9510b900b2061d0c334b816a2dd1000000000000000000000000" "00000000095a3230fde69eec3af2e6d0bd68ab7210613fc90200000016a31dc2e46a234558817151" - "b38fc9c909cf8d7100000000000000000000000000000000ae17bb4515856092b4d9e89b676761b2" - "57d6249b020000007dc7f17f666969e45c3baf7a119a65ee0b06fa9e000000000000000000000000" + "b38fc9c9093c8d7100000000000000000000000000000000ae17bb4515856092b4d9e89b676761b2" + "57d6249b020000007dc7f17f666969e45c3baf7a119a65ee0b08fa9e000000000000000000000000" "00000000b314f695bd82e9ea8891ed0a3d1b91cab56e03e0010000002f70249eefc67bce716c856e" "3973dc42a1003be9000000000000000000000000000000006176f78a19bb8df1804c122fce5078c1" - "eecc89f601000000d635edea8ffe423d01883918039249f398f9b37e000000000000000000000000" + "eecc89f601000000d635edea8ffe423d01883918039249f3984ab37e000000000000000000000000" "000000001fb7c8307f06b4b29088f20d9ac676d5f90e54b501000000a779f37c30d2c5053c40ecae" - "210aeb0934cb2a2400000000000000000000000000000000df48f333ef467092818aa77d6732b678" - "bcd7be0700000000e45aaf61814f703b40125e6baf4648cbaaa37b86000000000000000000000000" + "210aeb0934392a2400000000000000000000000000000000df48f333ef467092818aa77d6732b678" + "bcd7be0700000000e45aaf61814f703b40125e6baf4648cbaa557b86000000000000000000000000" "000000005ddb05f2a84e27c18a06e2c9cb29efdf50da49bf00000000d94beb259f9f0251becc9879" - "07879cca68fec7bb000000000000000000000000000000009402a4c2167721673f2b02b3256ead1f" - "a349e1cc00000000b879a45176740744fcb6a035339d504c9726d80d000000000000000000000000" + "07879cca6857c7bb000000000000000000000000000000009402a4c2167721673f2b02b3256ead1f" + "a349e1cc00000000b879a45176740744fcb6a035339d504c972bd80d000000000000000000000000" "000000009518862bc9a89192c1c8be55281a59d1ebfdcec30000000067a982be8a934f852cc3d4d8" - "2bc0ec7303f99f8f00000000000000000000000000000000901f55f960c55c22f32099a8020bea2a" - "5b1aea3401000000c7c3523f76cf1ce34ca17a9a32f3f0f218424e48000000000000000000000000" + "2bc0ec7303579f8f00000000000000000000000000000000901f55f960c55c22f32099a8020bea2a" + "5b1aea3401000000c7c3523f76cf1ce34ca17a9a32f3f0f2187c4e48000000000000000000000000" "00000002e6fbf952902383028e3b47f2111d891d536c75bb01000000720da9e07ac998b5690807d5" - "2617369ee1af4ca500000000000000000000ffff47d7b351a36b178aabc78a4a0d80684ee5dbf45b" - "17a4763102000000f9119c40b468b66331bca67a034f43340d01683c00000000000000000000ffff" + "2617369ee16d4ca500000000000000000000ffff47d7b351a36b178aabc78a4a0d80684ee5dbf45b" + "17a4763102000000f9119c40b468b66331bca67a034f43340d2c683c00000000000000000000ffff" "b24a5be9b10123e6e4f15ecd1dff045298cfee069c88b02a0300000004b0595daf37deb499996bfb" - "667f072dec1e5d9cdc8a11f4409bcfb3c167090e99d90a8543961b2ccd47724a3c460ba85183f4c5" - "a7afe28302000000d05ff720b16c73761a3fcb675219061ab4c5c79489b2cc6a883c146972decb8b" + "667f072dec755d9cdc8a11f4409bcfb3c167090e99d90a8543961b2ccd47724a3c460ba85183f4c5" + "a7afe28302000000d05ff720b16c73761a3fcb675219061ab466c79489b2cc6a883c146972decb8b" "5adac8f5e6d66df46ea139107754dee62c36d2fcc7a4c65601000000cd625009a40aeb62a42b6a62" - "548e3c38b96c20022753c5fbfaf1809becd2506315a6da29b2e94d3ab51fe0e9af98b180a36035fb" - "e36f90d401000000fd41fe47684b370b6ec6584d64496089570968ead4d1ae91c819bb068196d599" + "548e3c38b96620022753c5fbfaf1809becd2506315a6da29b2e94d3ab51fe0e9af98b180a36035fb" + "e36f90d401000000fd41fe47684b370b6ec6584d64496089574568ead4d1ae91c819bb068196d599" "efde3246e43f5e7945aaf95e2ffa3a11b9ef1b21cdb3455c00000000c66af76e80f062ea1a7e0b7b" - "e218d067530e1b0487b8c3b99f2b8a1a8982c42f53700a3437c5156e072b2f2fb337c9cfe07c34dd" - "c896c9a7000000000a5f6abd850b06e3d9662795ce2cc1cee06302aba042aaa218dc091e9aa1477f" + "e218d067532d1b0487b8c3b99f2b8a1a8982c42f53700a3437c5156e072b2f2fb337c9cfe07c34dd" + "c896c9a7000000000a5f6abd850b06e3d9662795ce2cc1cee03202aba042aaa218dc091e9aa1477f" "6fdc9830d7fbc95829a8838314dff34d24c332214d4647f5000000000f88e3759a08e7a56663861e" - "ea1bf42ccd3c72f2ad063857ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec787bc45db" - "7625ca4a00000000e682f80f94315c45ef817264c89a736ed55ed637b077ddad3108be69f9b97d05" + "ea1bf42ccd1172f2ad063857ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec787bc45db" + "7625ca4a00000000e682f80f94315c45ef817264c89a736ed580d637b077ddad3108be69f9b97d05" "c917ad6b10e693bb6e26f2ba90c8e909e85e20e5b0916e5d030000004a503a712350acbd48411f0d" - "c15d2f0f49dad345599706954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8" - "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa701d120523bd98f29b098b43cd68be6e" + "c15d2f0f4952d345599706954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8" + "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa700b120523bd98f29b098b43cd68be6e" "3f81268193fd637e585287dc6ca972a2cfb18b2b3cae864b0200000091921681c2162b9bfa40546c" - "db544e44df2d7c2790d3119a051bb2ee8192ac0cfa3abc1ea0ab3e7d75a2f42b50c6a36340132378" - "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c1fa56dfdcfc1155f236c8b9c79a620" + "db544e44df587c2790d3119a051bb2ee8192ac0cfa3abc1ea0ab3e7d75a2f42b50c6a36340132378" + "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c03a56dfdcfc1155f236c8b9c79a620" "5f660bbf024b03ff0a8e27c405e6424457393c6fdac12ebd01000000667224dae775e5ccd141aca2" - "ff0f576767d3648102b61886b62f22ca33478e1f891715df7fc6a902edae579e2e10c7f7a22ba034" - "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c93ef054e93f8120abb42316c533d9c9" + "ff0f57676754648102b61886b62f22ca33478e1f891715df7fc6a902edae579e2e10c7f7a22ba034" + "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c951f054e93f8120abb42316c533d9c9" "4d88189c471a1ff2f0ce3ff66e782110125ae2eddaae7d6300000000c0982b283aa9986700b2a2b3" - "ed257b8b0489f48f053ec8cac72105b327335a25a364d1015ffac03089f45539e1b0178462156cd6" - "6855676c00000000017c9148da5892bb4951c3a7ed55689d3391ce7d3fd48469845f0c233dbdd2a9" + "ed257b8b042ff48f053ec8cac72105b327335a25a364d1015ffac03089f45539e1b0178462156cd6" + "6855676c00000000017c9148da5892bb4951c3a7ed55689d336bce7d3fd48469845f0c233dbdd2a9" "7835df1f1782dee88487fd0db5971b46b7a350f998c39e7c00000000efc57ff447036b9551adcb22" - "fd50793f797a3c7dadd1c86e759d0c90bc512e137dcef5e4a27985bd8d1e69c83dc21056b973972c" - "bc51e540030000006c3d048a92d3adc69a84eb47622400207799416afef0a086fbd7e2f7dea0077a" + "fd50793f791c3c7dadd1c86e759d0c90bc512e137dcef5e4a27985bd8d1e69c83dc21056b973972c" + "bc51e540030000006c3d048a92d3adc69a84eb47622400207708416afef0a086fbd7e2f7dea0077a" "e9c0c0d9c386e9c6e6a2cbfa10ee58bdc75183600422ae3f03000000c43bd794c697895e5bfe47c6" - "f53a54e01b0c1d89fecd94d0e02d2ec587fc330003781b0f294a2b73c4421398f4de67e9cee64b38" - "69121aec010000000fcc00ac2daa755e5292cd32dc096cd5d8a4c5cffe91ee1cfcc2056cc1ff8e51" + "f53a54e01b211d89fecd94d0e02d2ec587fc330003781b0f294a2b73c4421398f4de67e9cee64b38" + "69121aec010000000fcc00ac2daa755e5292cd32dc096cd5d830c5cffe91ee1cfcc2056cc1ff8e51" "bbce426cfa4b861cc78592be7b14e7ba9c15acb836674cde0200000054cfe8f42bf0e9381ba2c439" - "6de60f032bc7f34dfe959871c0d4c0d4eb720c4ae3c9472e5f2da5d9c5cfd9f2343b21362c19a092" - "b2cd3dbd01000000e958e48d2ab8b0313e7f8933e315130fa9a84c82feb83f6e70b1646d8026e9f1" + "6de60f032b5af34dfe959871c0d4c0d4eb720c4ae3c9472e5f2da5d9c5cfd9f2343b21362c19a092" + "b2cd3dbd01000000e958e48d2ab8b0313e7f8933e315130fa9274c82feb83f6e70b1646d8026e9f1" "704f1b16286ba2dabc7ef0820c91ed33ef60a8b5bcfe97e1000000007ad742d3d7320a4f880cf47f" - "5dd0cf69cb22840ffeb3fe7749509cb6752b2cee30b7e7736a0afc9879ea40e69710fc9f6e8e99cd" - "1dce2f83020000003a3efc240b9977e166bee737fe73670c439a644c000000000000000000000000" + "5dd0cf69cb7f840ffeb3fe7749509cb6752b2cee30b7e7736a0afc9879ea40e69710fc9f6e8e99cd" + "1dce2f83020000003a3efc240b9977e166bee737fe73670c436b644c000000000000000000000000" "000000015e0dfe491d1ba96742c7b5e02b227122cf844ef501000000cd743f59bd14c89b5cfd5ce6" - "e82e7854152f2b700000000000000000000000000000000127e19f254ac9588319558041fc62ed56" - "b83579b80300000072437f9b6245de39ec9e71ee4b951507beb42b34000000000000000000000000" + "e82e7854157b2b700000000000000000000000000000000127e19f254ac9588319558041fc62ed56" + "b83579b80300000072437f9b6245de39ec9e71ee4b951507be0a2b34000000000000000000000000" "00000001d639272e4a46eadfbd2146bf8cfdb205b3980deb020000006c1de913df1424ce3ee1d76f" - "c1ea76e98c9dfc7800000000000000000000000000000001e44ea74b4f2b83f9b3bee14d861a4c9e" - "0436242c03000000ee3fbc164632d4f28aa28c08f80dd50c6712dfb4000000000000000000000000" + "c1ea76e98c16fc7800000000000000000000000000000001e44ea74b4f2b83f9b3bee14d861a4c9e" + "0436242c03000000ee3fbc164632d4f28aa28c08f80dd50c6705dfb4000000000000000000000000" "00000000078a806d329c9b008bbfc9723107f3f52af145480300000004265f6c72923e22594f3f79" - "e208ed2e2ebb07710000000000000000000000000000000039dd8f88152a58454d9ca9d20f45dfa7" - "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f608df061000000000000000000000000" + "e208ed2e2e7107710000000000000000000000000000000039dd8f88152a58454d9ca9d20f45dfa7" + "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f6042f061000000000000000000000000" "000000008ae21e371051852c972ea7954079884a9a1b163a030000000e2f4f911a0dd8c052ed6b13" - "e2815a1a37a52bdd00000000000000000000000000000000bd99fcf72909ac9e7a55299e9bb4fd53" - "9f3c2c8803000000f51f44e644d7cf1d06b2b115d882549e62c12fba000000000000000000000000" + "e2815a1a37322bdd00000000000000000000000000000000bd99fcf72909ac9e7a55299e9bb4fd53" + "9f3c2c8803000000f51f44e644d7cf1d06b2b115d882549e62042fba000000000000000000000000" "000000004fc812bd2f8a59d96d064126666befbbf257b044030000006a4c619f9037d7a754655354" - "c47cf0f551e967700000000000000000000000000000000050b8d10ab700cc3a7cf51be0403b654b" - "74e7860402000000cd7db68b9ae586dacdb4070c50320427c4dd3d03000000000000000000000000" + "c47cf0f5512167700000000000000000000000000000000050b8d10ab700cc3a7cf51be0403b654b" + "74e7860402000000cd7db68b9ae586dacdb4070c50320427c4803d03000000000000000000000000" "00000000d538d34574c038404923e75d0e09e2fcfc1519de010000002545666937c22a3b6e9686dc" - "c405cbee5016c252000000000000000000000000000000006e4c699341b95f41d9c1e67743efb54e" - "6aae660b01000000fc1fb23d24438387def0f4c6544e4b275d9b7146000000000000000000000000" + "c405cbee504dc252000000000000000000000000000000006e4c699341b95f41d9c1e67743efb54e" + "6aae660b01000000fc1fb23d24438387def0f4c6544e4b275d4a7146000000000000000000000000" "00000000b883efebc09dd9d61966b7ae7412041dd7ac5f9901000000822a272bec4501a1e27acfee" - "7a8588ffd5a06cae0000000000000000000000000000000077a47579dda0b6ead1e1b34901eaedd5" - "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e2b00b6000000000000000000000000" + "7a8588ffd5286cae0000000000000000000000000000000077a47579dda0b6ead1e1b34901eaedd5" + "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e0e00b6000000000000000000000000" "000000005e7fa0f02f2f3313d8469d09a92409c0b4d54acb00000000a417f3da01d99dae5f190096" - "cc582cabd5ddd15a00000000000000000000000000000000fdb3d117d76b0de5416f95e7bb75d72f" - "fc2b445700000000496ad01bf007eacb0a28aec868282510b60291ea000000000000000000000000" + "cc582cabd534d15a00000000000000000000000000000000fdb3d117d76b0de5416f95e7bb75d72f" + "fc2b445700000000496ad01bf007eacb0a28aec868282510b61b91ea000000000000000000000000" "000000005b29a197b7b3cceaf5f1bb12c535256a74993ade03000000e62112898dab2dade2ab2fc9" - "c56a7c86be5f962200000000000000000000000000000000bdd06352cb2c354434819f4b248eb2b8" - "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a631480000000000000000000000000" + "c56a7c86be73962200000000000000000000000000000000bdd06352cb2c354434819f4b248eb2b8" + "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a0c1480000000000000000000000000" "000000005588456e4ead7cbda620a3abae816e34e3ff1a5f03000000b4425486c619147eb0216050" - "ed7afd741024e83600000000000000000000000000000000f493472201e48d163106cc397446a33f" - "bc74179403000000438a72e12ae0436a2495a609675344f7e2e3a5eb000000000000000000000000" + "ed7afd74105fe83600000000000000000000000000000000f493472201e48d163106cc397446a33f" + "bc74179403000000438a72e12ae0436a2495a609675344f7e226a5eb000000000000000000000000" "0000000061eda239d9d083c3bf085387046ef8a34153e174020000004d89c0bb5408c1b7d17fb084" - "d9e1825fa638e1af000000000000000000000000000000006e08cb887464b9344ec3127c750fabdd" - "6c0fc09c02000000809829af4a9df58dbfef186d416f3a1ef170d10f000000000000000000000000" + "d9e1825fa613e1af000000000000000000000000000000006e08cb887464b9344ec3127c750fabdd" + "6c0fc09c02000000809829af4a9df58dbfef186d416f3a1ef154d10f000000000000000000000000" "0000000087780281d47a32f2235376239daeaacba9a89f8701000000fcdf6d9be94030b34774d1d7" - "dddedd9899f0f62700000000000000000000000000000000848518c4e3f6cbf25e5e1b990fc46767" - "fd338f35010000006e8cc34573654ea6624f138ef9531cd9367a02e4000000000000000000000000" + "dddedd989972f62700000000000000000000000000000000848518c4e3f6cbf25e5e1b990fc46767" + "fd338f35010000006e8cc34573654ea6624f138ef9531cd9361802e4000000000000000000000000" "00000000da40d88d40a6d75bc404156225b7eede2cb749e2010000000eb4f73e75039fcf251b32fc" - "79685b05ddd3aa9b000000000000000000000000000000000d58fbcc58fc4c2fd0d44ff1e51c3515" - "b7f0318a01000000061db3dd7fde25654a4059d565dbc8a91e3b4457000000000000000000000000" + "79685b05dd3caa9b000000000000000000000000000000000d58fbcc58fc4c2fd0d44ff1e51c3515" + "b7f0318a01000000061db3dd7fde25654a4059d565dbc8a91e004457000000000000000000000000" "00000000c1b5aad487c192f08bd7a09bdd21444359747cf200000000ab2dadc5a39411fd4ff1116d" - "478987316a553fc2000000000000000000000000000000000819e3b10db116431ec6f7691539ce41" - "b95c593100000000dbe9962069e66142e22753344fda8a3e0b841cf7000000000000000000000000" + "478987316a293fc2000000000000000000000000000000000819e3b10db116431ec6f7691539ce41" + "b95c593100000000dbe9962069e66142e22753344fda8a3e0b001cf7000000000000000000000000" "000000008c54dda9ac3bb1b1d43e6505621b9a7f672d2fa501000000e276c5661a64192db44ce7af" - "5eb888e9eb37bb0400000000000000000000000000000002641e447514ca565fb9b68c08a5e4351e" - "9f3c39f201000000de745cb25851429b3bbfb50168dfd04edf6b3542000000000000000000000000" + "5eb888e9eb73bb0400000000000000000000000000000002641e447514ca565fb9b68c08a5e4351e" + "9f3c39f201000000de745cb25851429b3bbfb50168dfd04edf2a3542000000000000000000000000" "0000000218f93e742d08f0550b55726f2292362e70ccc56700000000a4c73cf5ffa4ffc8a4bab148" - "74afdf54e6aae8160000000000000000000000000000000268baf76ab08940f6fc3bbdc7f6090f75" - "4f1b134803000000449a774929e86716ec852d59f3d17232edd86ad6000000000000000000000000" + "74afdf54e64be8160000000000000000000000000000000268baf76ab08940f6fc3bbdc7f6090f75" + "4f1b134803000000449a774929e86716ec852d59f3d17232ed3a6ad6000000000000000000000000" "000000027dba1ee7e143fd52599364e13468f80f40b26f6603000000badfad0fe8fa8f3c1aad2a82" - "530b7447d9c2f401000000000000000000000000000000028147507e1cd59ea1ae6da48b1eba6d16" - "dedea2030100000002a1f8e6f5657f3f9e3eb807cbad76451055edb8000000000000000000000000" + "530b7447d93bf401000000000000000000000000000000028147507e1cd59ea1ae6da48b1eba6d16" + "dedea2030100000002a1f8e6f5657f3f9e3eb807cbad76451013edb8000000000000000000000000" "00000002d9279fb2b8fcbac6abea74f7a6df7979f6989d2d000000006ff7add5df2c93e05459507b" - "5d8c6a0f466554d200000000000000000000ffff1785d3ffdbd9a617857ae8126b028c0e3e295354"), - [2] = ("4cc43c5103000000c2aac1d67a909b297c703915312e9c3c5a06c40b20ea99d5ab90135fa636af44" + "5d8c6a0f465354d200000000000000000000ffff1785d3ffdbd9a617857ae8126b028c0e3e295354"), + [2] = ("4cc43c5103000000c2aac1d67a909b297c703915312e9c3c5a64c40b20ea99d5ab90135fa636af44" "64d210a256be75e0509c4477d19ac64b2e69220d54270e46020000006a894e387625da376feecb80" - "ac245409c0becc271d9c2f67179bff0644399ae79c64ed0432d599eaa660ac824b7fae389861419c" - "12ea3b9501000000fb95e8332fdfff658483a2d039a7bf148ed3481e0bccc460e89f7abdb682380a" + "ac245409c058cc271d9c2f67179bff0644399ae79c64ed0432d599eaa660ac824b7fae389861419c" + "12ea3b9501000000fb95e8332fdfff658483a2d039a7bf148e4c481e0bccc460e89f7abdb682380a" "62eae374835b4a49a2b980b6aba92da6409969aa7a899f2d010000009bf5a5d06776e66a9d80ab13" - "2e1ac921eb76adbb229df32e561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b" - "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ecc8fb112087b97cfcebbaeb0c565359" + "2e1ac921eb04adbb229df32e561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b" + "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ec5ffb112087b97cfcebbaeb0c565359" "23f14874042a8aff1d1f8e5ec3cc13cc36bbe3c9c4e1966800000000b3121cd5ef51e696c816290d" - "baee0e7726d082e1530ff5397c9125f59577d71c4b258a005116d11354edff62ceaa458fc75a91c4" - "25a5927300000000c4458d70911714f1819aa46fbe143934c933ae6dec0fb124f264af67eed7c9a8" + "baee0e77264c82e1530ff5397c9125f59577d71c4b258a005116d11354edff62ceaa458fc75a91c4" + "25a5927300000000c4458d70911714f1819aa46fbe143934c915ae6dec0fb124f264af67eed7c9a8" "c38b6b8ee2614344bea73223e59bf0193432e9fa2fea9bc10300000062fb81e733cff620ca7feefe" - "1933631e8e69f6d9fed62d2c6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196" - "9985d62700000000e491cbbbf9443fd6a77eb3c5bf64b671d6f18dbffeff9b8306c83bc026977911" + "1933631e8e18f6d9fed62d2c6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196" + "9985d62700000000e491cbbbf9443fd6a77eb3c5bf64b671d6098dbffeff9b8306c83bc026977911" "4bdade862c224f6f36506ebd455e679cef369bb8bb0ec36f0000000064a2827b7cdd38d0314c178e" - "925b568ae56bc4e9fefb24e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13" - "02e9b69000000000eacce745ea213cb1a84035b632ef3be6070dafa2fed87470e9da55709427c226" + "925b568ae566c4e9fefb24e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13" + "02e9b69000000000eacce745ea213cb1a84035b632ef3be60755afa2fed87470e9da55709427c226" "d324d9a086ed27184b443181a5edc511c1507a14d514573f03000000f6932f6932d1d5f5d0ce2cf6" - "bf7a70ba5193a162fe92ec1b11d9172cf84a03478b82cdd8883b0f3fd6ed84d6c959e553b887edcd" - "6297dfa700000000fd0d53bb6215de4bb6f6f3d031a790287e45e9c1feb836c0a25ea71f5daaed4d" + "bf7a70ba5111a162fe92ec1b11d9172cf84a03478b82cdd8883b0f3fd6ed84d6c959e553b887edcd" + "6297dfa700000000fd0d53bb6215de4bb6f6f3d031a790287e7be9c1feb836c0a25ea71f5daaed4d" "99eb2ebebe3c432fec7d3abc4ccf30d3aaaf02a4ae8cb60b0300000038d856a40c67c3d4afa9c864" - "9da90bd2fff2f25600000000000000000000000000000001955e1a13d1111b067acbce3e3bcdf382" - "00154d12010000009edfa7a74c1c83c18e47935e8354846e34f0386a000000000000000000000000" + "9da90bd2ff4cf25600000000000000000000000000000001955e1a13d1111b067acbce3e3bcdf382" + "00154d12010000009edfa7a74c1c83c18e47935e8354846e346e386a000000000000000000000000" "00000001ac26c7d41fd050bf8d85055bcfe3756c1e0f515601000000f57609453df7803357ae7998" - "9870ccec3def0ad9000000000000000000000000000000019f05a16768cb3bc873b04d937a6ed8f7" - "7c52a52d030000004e8e8bc17c01ac63eca0c1cfbb204132fb7213e4000000000000000000000000" + "9870ccec3d4f0ad9000000000000000000000000000000019f05a16768cb3bc873b04d937a6ed8f7" + "7c52a52d030000004e8e8bc17c01ac63eca0c1cfbb204132fb0813e4000000000000000000000000" "00000000accae11f8045a80f855ebf3b772eb2b1d03bc90503000000eb760e3f004a2efc8ffc96b8" - "9b1bc8f415bd4e7700000000000000000000000000000000d149f074a378b881153ece68ade15cec" - "6f0e9e6903000000ec2e2924348352d715cf9d411ea012e5307294b6000000000000000000000000" + "9b1bc8f415174e7700000000000000000000000000000000d149f074a378b881153ece68ade15cec" + "6f0e9e6903000000ec2e2924348352d715cf9d411ea012e5305a94b6000000000000000000000000" "00000000ccd2f50c30706cae3fdcc2f0ee3d5e267666c05303000000ffa8e89871fb9510b900b206" - "1d0c334b819f2dd100000000000000000000000000000000095a3230fde69eec3af2e6d0bd68ab72" - "81ae55f0030000000aa20624fbab244735d67c61283031667b2d74a1000000000000000000000000" + "1d0c334b816a2dd100000000000000000000000000000000095a3230fde69eec3af2e6d0bd68ab72" + "81ae55f0030000000aa20624fbab244735d67c61283031667b2274a1000000000000000000000000" "0000000000459582f4ef43c780908872746e39ef10613fc90200000016a31dc2e46a234558817151" - "b38fc9c909cf8d7100000000000000000000000000000000ae17bb4515856092b4d9e89b676761b2" - "57d6249b020000007dc7f17f666969e45c3baf7a119a65ee0b06fa9e000000000000000000000000" + "b38fc9c9093c8d7100000000000000000000000000000000ae17bb4515856092b4d9e89b676761b2" + "57d6249b020000007dc7f17f666969e45c3baf7a119a65ee0b08fa9e000000000000000000000000" "00000000b314f695bd82e9ea8891ed0a3d1b91ca6e91b91501000000be5b534e0a6527f67485ab35" - "aca0c7ee4197338500000000000000000000000000000000aefd4503189f7630248e501c9052c22b" - "5d68265d010000009b7a559be3acf4fc36fa6b513cf09e8b99289462000000000000000000000000" + "aca0c7ee4167338500000000000000000000000000000000aefd4503189f7630248e501c9052c22b" + "5d68265d010000009b7a559be3acf4fc36fa6b513cf09e8b99729462000000000000000000000000" "00000000b49185ad9b7d0070a36dd51323b2c542b56e03e0010000002f70249eefc67bce716c856e" "3973dc42a1003be9000000000000000000000000000000006176f78a19bb8df1804c122fce5078c1" - "e5fe7d360100000066a3ae3939762df33a2d55060c78d551cbb110fd000000000000000000000000" + "e5fe7d360100000066a3ae3939762df33a2d55060c78d551cb7410fd000000000000000000000000" "000000002c171ed15abbbe1cd911db7b56e8f3feeecc89f601000000d635edea8ffe423d01883918" - "039249f398f9b37e000000000000000000000000000000001fb7c8307f06b4b29088f20d9ac676d5" - "f90e54b501000000a779f37c30d2c5053c40ecae210aeb0934cb2a24000000000000000000000000" + "039249f3984ab37e000000000000000000000000000000001fb7c8307f06b4b29088f20d9ac676d5" + "f90e54b501000000a779f37c30d2c5053c40ecae210aeb0934392a24000000000000000000000000" "00000000df48f333ef467092818aa77d6732b678bcd7be0700000000e45aaf61814f703b40125e6b" - "af4648cbaaa37b86000000000000000000000000000000005ddb05f2a84e27c18a06e2c9cb29efdf" - "50da49bf00000000d94beb259f9f0251becc987907879cca68fec7bb000000000000000000000000" + "af4648cbaa557b86000000000000000000000000000000005ddb05f2a84e27c18a06e2c9cb29efdf" + "50da49bf00000000d94beb259f9f0251becc987907879cca6857c7bb000000000000000000000000" "000000009402a4c2167721673f2b02b3256ead1fa349e1cc00000000b879a45176740744fcb6a035" - "339d504c9726d80d000000000000000000000000000000009518862bc9a89192c1c8be55281a59d1" - "bae87d60000000005d31e5b2de252167c1c91b3a36ba0c700ee78477000000000000000000000000" + "339d504c972bd80d000000000000000000000000000000009518862bc9a89192c1c8be55281a59d1" + "bae87d60000000005d31e5b2de252167c1c91b3a36ba0c700e538477000000000000000000000000" "000000006ad0143a0554efb46b382ab0404cdf02ebfdcec30000000067a982be8a934f852cc3d4d8" - "2bc0ec7303f99f8f00000000000000000000000000000000901f55f960c55c22f32099a8020bea2a" - "a2f56808092069affaa100fdfdd68d34be184524a6bdc8789cf85178000000000000000000000000" + "2bc0ec7303579f8f00000000000000000000000000000000901f55f960c55c22f32099a8020bea2a" + "a2f56808092069affaa100fdfdd68d34be184524a6bdc8789c575178000000000000000000000000" "00000000020ffdb0e612098610e52bb2a16a4008aefd545b020000007ced4f8b362956cd46646a0e" - "222160e5f769bb2900000000000000000000000000000000a19da86c64fefab2548759d313a5b92d" - "f05d7308020000009d82c856ba942455050f63f8282f3464443cacf5000000000000000000000000" + "222160e5f749bb2900000000000000000000000000000000a19da86c64fefab2548759d313a5b92d" + "f05d7308020000009d82c856ba942455050f63f8282f34644466acf5000000000000000000000000" "0000000069d9942f460bcea75481f25e307d0de99a6ce5d900000000a6a5ee19d6f63fe9cbdd01f7" - "246f13b2050424a200000000000000000000000000000000ffe9d6812231bb02f922259b173c333c" - "5b1aea3401000000c7c3523f76cf1ce34ca17a9a32f3f0f218424e48000000000000000000000000" + "246f13b2055624a200000000000000000000000000000000ffe9d6812231bb02f922259b173c333c" + "5b1aea3401000000c7c3523f76cf1ce34ca17a9a32f3f0f2187c4e48000000000000000000000000" "00000002e6fbf952902383028e3b47f2111d891d17a4763102000000f9119c40b468b66331bca67a" - "034f43340d01683c00000000000000000000ffffb24a5be9b10123e6e4f15ecd1dff045298cfee06" - "536c75bb01000000720da9e07ac998b5690807d52617369ee1af4ca500000000000000000000ffff" + "034f43340d2c683c00000000000000000000ffffb24a5be9b10123e6e4f15ecd1dff045298cfee06" + "536c75bb01000000720da9e07ac998b5690807d52617369ee16d4ca500000000000000000000ffff" "47d7b351a36b178aabc78a4a0d80684ee5dbf45bb0916e5d030000004a503a712350acbd48411f0d" - "c15d2f0f49dad345599706954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8" - "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa701d120523bd98f29b098b43cd68be6e" + "c15d2f0f4952d345599706954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8" + "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa700b120523bd98f29b098b43cd68be6e" "3f81268193fd637e585287dc6ca972a2cfb18b2b3cae864b0200000091921681c2162b9bfa40546c" - "db544e44df2d7c2790d3119a051bb2ee8192ac0cfa3abc1ea0ab3e7d75a2f42b50c6a36340132378" - "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c1fa56dfdcfc1155f236c8b9c79a620" + "db544e44df587c2790d3119a051bb2ee8192ac0cfa3abc1ea0ab3e7d75a2f42b50c6a36340132378" + "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c03a56dfdcfc1155f236c8b9c79a620" "5f660bbf024b03ff0a8e27c405e6424457393c6fdac12ebd01000000667224dae775e5ccd141aca2" - "ff0f576767d3648102b61886b62f22ca33478e1f891715df7fc6a902edae579e2e10c7f7a22ba034" - "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c93ef054e93f8120abb42316c533d9c9" + "ff0f57676754648102b61886b62f22ca33478e1f891715df7fc6a902edae579e2e10c7f7a22ba034" + "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c951f054e93f8120abb42316c533d9c9" "4d88189c471a1ff2f0ce3ff66e782110125ae2edc7a4c65601000000cd625009a40aeb62a42b6a62" - "548e3c38b96c20022753c5fbfaf1809becd2506315a6da29b2e94d3ab51fe0e9af98b180a36035fb" - "daae7d6300000000c0982b283aa9986700b2a2b3ed257b8b0489f48f053ec8cac72105b327335a25" + "548e3c38b96620022753c5fbfaf1809becd2506315a6da29b2e94d3ab51fe0e9af98b180a36035fb" + "daae7d6300000000c0982b283aa9986700b2a2b3ed257b8b042ff48f053ec8cac72105b327335a25" "a364d1015ffac03089f45539e1b0178462156cd66855676c00000000017c9148da5892bb4951c3a7" - "ed55689d3391ce7d3fd48469845f0c233dbdd2a97835df1f1782dee88487fd0db5971b46b7a350f9" - "cdb3455c00000000c66af76e80f062ea1a7e0b7be218d067530e1b0487b8c3b99f2b8a1a8982c42f" + "ed55689d336bce7d3fd48469845f0c233dbdd2a97835df1f1782dee88487fd0db5971b46b7a350f9" + "cdb3455c00000000c66af76e80f062ea1a7e0b7be218d067532d1b0487b8c3b99f2b8a1a8982c42f" "53700a3437c5156e072b2f2fb337c9cfe07c34ddc896c9a7000000000a5f6abd850b06e3d9662795" - "ce2cc1cee06302aba042aaa218dc091e9aa1477f6fdc9830d7fbc95829a8838314dff34d24c33221" - "98c39e7c00000000efc57ff447036b9551adcb22fd50793f797a3c7dadd1c86e759d0c90bc512e13" + "ce2cc1cee03202aba042aaa218dc091e9aa1477f6fdc9830d7fbc95829a8838314dff34d24c33221" + "98c39e7c00000000efc57ff447036b9551adcb22fd50793f791c3c7dadd1c86e759d0c90bc512e13" "7dcef5e4a27985bd8d1e69c83dc21056b973972c7625ca4a00000000e682f80f94315c45ef817264" - "c89a736ed55ed637b077ddad3108be69f9b97d05c917ad6b10e693bb6e26f2ba90c8e909e85e20e5" - "9c88b02a0300000004b0595daf37deb499996bfb667f072dec1e5d9cdc8a11f4409bcfb3c167090e" + "c89a736ed580d637b077ddad3108be69f9b97d05c917ad6b10e693bb6e26f2ba90c8e909e85e20e5" + "9c88b02a0300000004b0595daf37deb499996bfb667f072dec755d9cdc8a11f4409bcfb3c167090e" "99d90a8543961b2ccd47724a3c460ba85183f4c5a7afe28302000000d05ff720b16c73761a3fcb67" - "5219061ab4c5c79489b2cc6a883c146972decb8b5adac8f5e6d66df46ea139107754dee62c36d2fc" - "e36f90d401000000fd41fe47684b370b6ec6584d64496089570968ead4d1ae91c819bb068196d599" + "5219061ab466c79489b2cc6a883c146972decb8b5adac8f5e6d66df46ea139107754dee62c36d2fc" + "e36f90d401000000fd41fe47684b370b6ec6584d64496089574568ead4d1ae91c819bb068196d599" "efde3246e43f5e7945aaf95e2ffa3a11b9ef1b214d4647f5000000000f88e3759a08e7a56663861e" - "ea1bf42ccd3c72f2ad063857ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec787bc45db" - "0422ae3f03000000c43bd794c697895e5bfe47c6f53a54e01b0c1d89fecd94d0e02d2ec587fc3300" + "ea1bf42ccd1172f2ad063857ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec787bc45db" + "0422ae3f03000000c43bd794c697895e5bfe47c6f53a54e01b211d89fecd94d0e02d2ec587fc3300" "03781b0f294a2b73c4421398f4de67e9cee64b38bc51e540030000006c3d048a92d3adc69a84eb47" - "622400207799416afef0a086fbd7e2f7dea0077ae9c0c0d9c386e9c6e6a2cbfa10ee58bdc7518360" - "36674cde0200000054cfe8f42bf0e9381ba2c4396de60f032bc7f34dfe959871c0d4c0d4eb720c4a" + "622400207708416afef0a086fbd7e2f7dea0077ae9c0c0d9c386e9c6e6a2cbfa10ee58bdc7518360" + "36674cde0200000054cfe8f42bf0e9381ba2c4396de60f032b5af34dfe959871c0d4c0d4eb720c4a" "e3c9472e5f2da5d9c5cfd9f2343b21362c19a092b2cd3dbd01000000e958e48d2ab8b0313e7f8933" - "e315130fa9a84c82feb83f6e70b1646d8026e9f1704f1b16286ba2dabc7ef0820c91ed33ef60a8b5" - "bcfe97e1000000007ad742d3d7320a4f880cf47f5dd0cf69cb22840ffeb3fe7749509cb6752b2cee" + "e315130fa9274c82feb83f6e70b1646d8026e9f1704f1b16286ba2dabc7ef0820c91ed33ef60a8b5" + "bcfe97e1000000007ad742d3d7320a4f880cf47f5dd0cf69cb7f840ffeb3fe7749509cb6752b2cee" "30b7e7736a0afc9879ea40e69710fc9f6e8e99cd69121aec010000000fcc00ac2daa755e5292cd32" - "dc096cd5d8a4c5cffe91ee1cfcc2056cc1ff8e51bbce426cfa4b861cc78592be7b14e7ba9c15acb8" - "b83579b80300000072437f9b6245de39ec9e71ee4b951507beb42b34000000000000000000000000" + "dc096cd5d830c5cffe91ee1cfcc2056cc1ff8e51bbce426cfa4b861cc78592be7b14e7ba9c15acb8" + "b83579b80300000072437f9b6245de39ec9e71ee4b951507be0a2b34000000000000000000000000" "00000001d639272e4a46eadfbd2146bf8cfdb205b3980deb020000006c1de913df1424ce3ee1d76f" - "c1ea76e98c9dfc7800000000000000000000000000000001e44ea74b4f2b83f9b3bee14d861a4c9e" - "cf844ef501000000cd743f59bd14c89b5cfd5ce6e82e7854152f2b70000000000000000000000000" + "c1ea76e98c16fc7800000000000000000000000000000001e44ea74b4f2b83f9b3bee14d861a4c9e" + "cf844ef501000000cd743f59bd14c89b5cfd5ce6e82e7854157b2b70000000000000000000000000" "0000000127e19f254ac9588319558041fc62ed561dce2f83020000003a3efc240b9977e166bee737" - "fe73670c439a644c000000000000000000000000000000015e0dfe491d1ba96742c7b5e02b227122" - "2af145480300000004265f6c72923e22594f3f79e208ed2e2ebb0771000000000000000000000000" + "fe73670c436b644c000000000000000000000000000000015e0dfe491d1ba96742c7b5e02b227122" + "2af145480300000004265f6c72923e22594f3f79e208ed2e2e710771000000000000000000000000" "0000000039dd8f88152a58454d9ca9d20f45dfa774993ade03000000e62112898dab2dade2ab2fc9" - "c56a7c86be5f962200000000000000000000000000000000bdd06352cb2c354434819f4b248eb2b8" - "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f608df061000000000000000000000000" + "c56a7c86be73962200000000000000000000000000000000bdd06352cb2c354434819f4b248eb2b8" + "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f6042f061000000000000000000000000" "000000008ae21e371051852c972ea7954079884a9f3c2c8803000000f51f44e644d7cf1d06b2b115" - "d882549e62c12fba000000000000000000000000000000004fc812bd2f8a59d96d064126666befbb" - "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a631480000000000000000000000000" + "d882549e62042fba000000000000000000000000000000004fc812bd2f8a59d96d064126666befbb" + "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a0c1480000000000000000000000000" "000000005588456e4ead7cbda620a3abae816e34e3ff1a5f03000000b4425486c619147eb0216050" - "ed7afd741024e83600000000000000000000000000000000f493472201e48d163106cc397446a33f" - "f257b044030000006a4c619f9037d7a754655354c47cf0f551e96770000000000000000000000000" + "ed7afd74105fe83600000000000000000000000000000000f493472201e48d163106cc397446a33f" + "f257b044030000006a4c619f9037d7a754655354c47cf0f551216770000000000000000000000000" "0000000050b8d10ab700cc3a7cf51be0403b654bbc74179403000000438a72e12ae0436a2495a609" - "675344f7e2e3a5eb0000000000000000000000000000000061eda239d9d083c3bf085387046ef8a3" - "4153e174020000004d89c0bb5408c1b7d17fb084d9e1825fa638e1af000000000000000000000000" + "675344f7e226a5eb0000000000000000000000000000000061eda239d9d083c3bf085387046ef8a3" + "4153e174020000004d89c0bb5408c1b7d17fb084d9e1825fa613e1af000000000000000000000000" "000000006e08cb887464b9344ec3127c750fabdd6c0fc09c02000000809829af4a9df58dbfef186d" - "416f3a1ef170d10f0000000000000000000000000000000087780281d47a32f2235376239daeaacb" - "74e7860402000000cd7db68b9ae586dacdb4070c50320427c4dd3d03000000000000000000000000" + "416f3a1ef154d10f0000000000000000000000000000000087780281d47a32f2235376239daeaacb" + "74e7860402000000cd7db68b9ae586dacdb4070c50320427c4803d03000000000000000000000000" "00000000d538d34574c038404923e75d0e09e2fca9a89f8701000000fcdf6d9be94030b34774d1d7" - "dddedd9899f0f62700000000000000000000000000000000848518c4e3f6cbf25e5e1b990fc46767" - "fd338f35010000006e8cc34573654ea6624f138ef9531cd9367a02e4000000000000000000000000" + "dddedd989972f62700000000000000000000000000000000848518c4e3f6cbf25e5e1b990fc46767" + "fd338f35010000006e8cc34573654ea6624f138ef9531cd9361802e4000000000000000000000000" "00000000da40d88d40a6d75bc404156225b7eede2cb749e2010000000eb4f73e75039fcf251b32fc" - "79685b05ddd3aa9b000000000000000000000000000000000d58fbcc58fc4c2fd0d44ff1e51c3515" - "6aae660b01000000fc1fb23d24438387def0f4c6544e4b275d9b7146000000000000000000000000" + "79685b05dd3caa9b000000000000000000000000000000000d58fbcc58fc4c2fd0d44ff1e51c3515" + "6aae660b01000000fc1fb23d24438387def0f4c6544e4b275d4a7146000000000000000000000000" "00000000b883efebc09dd9d61966b7ae7412041db7f0318a01000000061db3dd7fde25654a4059d5" - "65dbc8a91e3b445700000000000000000000000000000000c1b5aad487c192f08bd7a09bdd214443" - "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e2b00b6000000000000000000000000" + "65dbc8a91e00445700000000000000000000000000000000c1b5aad487c192f08bd7a09bdd214443" + "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e0e00b6000000000000000000000000" "000000005e7fa0f02f2f3313d8469d09a92409c0b4d54acb00000000a417f3da01d99dae5f190096" - "cc582cabd5ddd15a00000000000000000000000000000000fdb3d117d76b0de5416f95e7bb75d72f" - "59747cf200000000ab2dadc5a39411fd4ff1116d478987316a553fc2000000000000000000000000" + "cc582cabd534d15a00000000000000000000000000000000fdb3d117d76b0de5416f95e7bb75d72f" + "59747cf200000000ab2dadc5a39411fd4ff1116d478987316a293fc2000000000000000000000000" "000000000819e3b10db116431ec6f7691539ce41b95c593100000000dbe9962069e66142e2275334" - "4fda8a3e0b841cf7000000000000000000000000000000008c54dda9ac3bb1b1d43e6505621b9a7f" - "fc2b445700000000496ad01bf007eacb0a28aec868282510b60291ea000000000000000000000000" + "4fda8a3e0b001cf7000000000000000000000000000000008c54dda9ac3bb1b1d43e6505621b9a7f" + "fc2b445700000000496ad01bf007eacb0a28aec868282510b61b91ea000000000000000000000000" "000000005b29a197b7b3cceaf5f1bb12c535256a0436242c03000000ee3fbc164632d4f28aa28c08" - "f80dd50c6712dfb400000000000000000000000000000000078a806d329c9b008bbfc9723107f3f5" - "9a1b163a030000000e2f4f911a0dd8c052ed6b13e2815a1a37a52bdd000000000000000000000000" + "f80dd50c6705dfb400000000000000000000000000000000078a806d329c9b008bbfc9723107f3f5" + "9a1b163a030000000e2f4f911a0dd8c052ed6b13e2815a1a37322bdd000000000000000000000000" "00000000bd99fcf72909ac9e7a55299e9bb4fd53fc1519de010000002545666937c22a3b6e9686dc" - "c405cbee5016c252000000000000000000000000000000006e4c699341b95f41d9c1e67743efb54e" - "d7ac5f9901000000822a272bec4501a1e27acfee7a8588ffd5a06cae000000000000000000000000" + "c405cbee504dc252000000000000000000000000000000006e4c699341b95f41d9c1e67743efb54e" + "d7ac5f9901000000822a272bec4501a1e27acfee7a8588ffd5286cae000000000000000000000000" "0000000077a47579dda0b6ead1e1b34901eaedd54f1b134803000000449a774929e86716ec852d59" - "f3d17232edd86ad6000000000000000000000000000000027dba1ee7e143fd52599364e13468f80f" - "40b26f6603000000badfad0fe8fa8f3c1aad2a82530b7447d9c2f401000000000000000000000000" + "f3d17232ed3a6ad6000000000000000000000000000000027dba1ee7e143fd52599364e13468f80f" + "40b26f6603000000badfad0fe8fa8f3c1aad2a82530b7447d93bf401000000000000000000000000" "000000028147507e1cd59ea1ae6da48b1eba6d16dedea2030100000002a1f8e6f5657f3f9e3eb807" - "cbad76451055edb800000000000000000000000000000002d9279fb2b8fcbac6abea74f7a6df7979" - "672d2fa501000000e276c5661a64192db44ce7af5eb888e9eb37bb04000000000000000000000000" + "cbad76451013edb800000000000000000000000000000002d9279fb2b8fcbac6abea74f7a6df7979" + "672d2fa501000000e276c5661a64192db44ce7af5eb888e9eb73bb04000000000000000000000000" "00000002641e447514ca565fb9b68c08a5e4351e9f3c39f201000000de745cb25851429b3bbfb501" - "68dfd04edf6b35420000000000000000000000000000000218f93e742d08f0550b55726f2292362e" - "70ccc56700000000a4c73cf5ffa4ffc8a4bab14874afdf54e6aae816000000000000000000000000" + "68dfd04edf2a35420000000000000000000000000000000218f93e742d08f0550b55726f2292362e" + "70ccc56700000000a4c73cf5ffa4ffc8a4bab14874afdf54e64be816000000000000000000000000" "0000000268baf76ab08940f6fc3bbdc7f6090f75f6989d2d000000006ff7add5df2c93e05459507b" - "5d8c6a0f466554d200000000000000000000ffff1785d3ffdbd9a617857ae8126b028c0e3e295354"), + "5d8c6a0f465354d200000000000000000000ffff1785d3ffdbd9a617857ae8126b028c0e3e295354"), }; const int TEST_DATA_I = GPOINTER_TO_INT(test_data); const int addr_family = (TEST_DATA_I == 0 ? AF_INET : AF_INET6); @@ -584,8 +584,9 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data) nmtst_stable_rand(710086081, rand_map, sizeof(rand_map[0]) * N_ADDRESSES); for (i = 0; i < N_ADDRESSES; i++) { - NMPlatformIPXAddress *a = (gpointer) (&addresses[i * ELM_SIZE]); - guint64 r = rand_map[i]; + NMPlatformIPXAddress *a = (gpointer) (&addresses[i * ELM_SIZE]); + guint64 r = rand_map[i]; + guint64 r0 = r; struct in6_addr *a6; #define CONSUME_BITS(r, nbits) \ @@ -664,9 +665,10 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data) a->a4.label[0] = '\0'; } } + if (a->ax.plen > 0) + a->ax.plen = (r0 + 209284393u) % ((NM_IS_IPv4(addr_family) ? 32 : 128) + 1); if (addr_family == AF_INET) { if (CONSUME_BITS(r, 2) != 0) { - /* randomly make the label empty or not. */ a->a4.plen = CONSUME_BITS(r, 2); } } @@ -731,15 +733,17 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data) &bin_len); if (bin_len != ELM_SIZE * N_ADDRESSES || memcmp(addresses, bin_arr, bin_len) != 0) { - char *addresses_str = nm_utils_bin2hexstr(addresses, ELM_SIZE * N_ADDRESSES, -1); + gs_free char *addresses_str = NULL; + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error(">>> test_platform_ip_address_pretty_sort_cmp() will fail:\n" ">>> addresses[%zu]: %s\n" ">>> expected [%zu]: %s\n", ELM_SIZE * N_ADDRESSES, - addresses_str, + (addresses_str = nm_utils_bin2hexstr(addresses, ELM_SIZE * N_ADDRESSES, -1)), bin_len, EXPECTED_BUFFER[TEST_DATA_I]); + NM_PRAGMA_WARNING_REENABLE } g_assert_cmpmem(addresses, ELM_SIZE * N_ADDRESSES, bin_arr, bin_len); @@ -760,6 +764,7 @@ test_route_type_is_nodev(void) case RTN_BLACKHOLE: case RTN_UNREACHABLE: case RTN_PROHIBIT: + case RTN_THROW: is_nodev = TRUE; break; default: diff --git a/src/core/platform/tests/test-route.c b/src/core/platform/tests/test-route.c index cf0236e0..2d39cb37 100644 --- a/src/core/platform/tests/test-route.c +++ b/src/core/platform/tests/test-route.c @@ -1535,6 +1535,7 @@ _rule_fuzzy_equal(const NMPObject *obj, const NMPObject *obj_comp, int op_type) static void test_rule(gconstpointer test_data) { + char sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; const int TEST_IDX = GPOINTER_TO_INT(test_data); const gboolean TEST_SYNC = (TEST_IDX == 4); gs_unref_ptrarray GPtrArray *objs = NULL; @@ -1763,7 +1764,7 @@ again: g_print(">>> failing... errno=%d, rule=%s\n", r, - nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, sbuf1, sizeof(sbuf1))); nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); head_entry = nm_platform_lookup(platform, &lookup); @@ -1775,9 +1776,10 @@ again: && 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)); + g_print( + ">>> existing rule: %c %s\n", + ch, + nmp_object_to_string(o, NMP_OBJECT_TO_STRING_ALL, sbuf1, sizeof(sbuf1))); } nmtstp_run_command_check("ip rule"); @@ -1836,11 +1838,14 @@ again: 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)); + ">>> no fuzzy match between: %s\n", + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, sbuf1, sizeof(sbuf1))); + g_print(">>> and: %s\n", + nmp_object_to_string(objs->pdata[k], + NMP_OBJECT_TO_STRING_ALL, + sbuf1, + sizeof(sbuf1))); g_assert_not_reached(); } @@ -1899,14 +1904,17 @@ test_blackhole(gconstpointer test_data) NMPlatformIPXRoute rr = {}; int r = -1; int i; + guint8 rtn_type; + + rtn_type = nmtst_rand_select(RTN_BLACKHOLE, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_THROW); if (IS_IPv4) { rr.r4 = (const NMPlatformIP4Route){ - .type_coerced = nmtst_rand_select(RTN_BLACKHOLE, RTN_UNREACHABLE, RTN_PROHIBIT), + .type_coerced = nm_platform_route_type_coerce(rtn_type), }; } else { rr.r6 = (const NMPlatformIP6Route){ - .type_coerced = nmtst_rand_select(RTN_BLACKHOLE, RTN_UNREACHABLE, RTN_PROHIBIT), + .type_coerced = nm_platform_route_type_coerce(rtn_type), .metric = 1000, }; } diff --git a/src/core/ppp/nm-ppp-manager.c b/src/core/ppp/nm-ppp-manager.c index 5761d59d..896c233d 100644 --- a/src/core/ppp/nm-ppp-manager.c +++ b/src/core/ppp/nm-ppp-manager.c @@ -1247,7 +1247,7 @@ _ppp_manager_stop(NMPPPManager *self, SIGTERM, LOGD_PPP, "pppd", - 5000, + NM_SHUTDOWN_TIMEOUT_5000_MSEC, _stop_child_cb, handle); diff --git a/src/core/settings/nm-secret-agent.c b/src/core/settings/nm-secret-agent.c index b222bd5c..a3df4497 100644 --- a/src/core/settings/nm-secret-agent.c +++ b/src/core/settings/nm-secret-agent.c @@ -511,7 +511,7 @@ nm_secret_agent_cancel_call(NMSecretAgent *self, NMSecretAgentCallId *call_id) g_variant_new("(os)", call_id->path, call_id->setting_name), G_VARIANT_TYPE("()"), G_DBUS_CALL_FLAGS_NO_AUTO_START, - NM_SHUTDOWN_TIMEOUT_MS, + NM_SHUTDOWN_TIMEOUT_1500_MSEC, NULL, /* this operation is not cancellable. We rely on the timeout. */ _call_cancel_cb, call_id); diff --git a/src/core/settings/nm-settings-connection.c b/src/core/settings/nm-settings-connection.c index 55efaebe..1638efcd 100644 --- a/src/core/settings/nm-settings-connection.c +++ b/src/core/settings/nm-settings-connection.c @@ -606,6 +606,7 @@ _secrets_update(NMConnection *connection, gboolean nm_settings_connection_update(NMSettingsConnection *self, + const char *plugin_name, NMConnection *new_connection, NMSettingsConnectionPersistMode persist_mode, NMSettingsConnectionIntFlags sett_flags, @@ -618,6 +619,7 @@ nm_settings_connection_update(NMSettingsConnection *self, return nm_settings_update_connection(NM_SETTINGS_CONNECTION_GET_PRIVATE(self)->settings, self, + plugin_name, new_connection, persist_mode, sett_flags, @@ -835,6 +837,7 @@ nm_settings_connection_new_secrets(NMSettingsConnection *self, if (!nm_settings_connection_update( self, + NULL, new_connection ?: connection, NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP, NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, @@ -980,6 +983,7 @@ get_secrets_done_cb(NMAgentManager *manager, } if (!nm_settings_connection_update( self, + NULL, new_connection, agent_had_system ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP : NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST, @@ -1409,6 +1413,7 @@ typedef struct { NMConnection *new_settings; NMSettingsUpdate2Flags flags; char *audit_args; + char *plugin_name; bool is_update2 : 1; } UpdateInfo; @@ -1436,6 +1441,7 @@ update_complete(NMSettingsConnection *self, UpdateInfo *info, GError *error) g_clear_object(&info->agent_mgr); g_clear_object(&info->new_settings); g_free(info->audit_args); + g_free(info->plugin_name); g_slice_free(UpdateInfo, info); } @@ -1572,6 +1578,7 @@ update_auth_cb(NMSettingsConnection *self, nm_settings_connection_update( self, + info->plugin_name, info->new_settings, persist_mode, (NM_FLAGS_HAS(info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE) @@ -1642,6 +1649,7 @@ settings_connection_update(NMSettingsConnection *self, gboolean is_update2, GDBusMethodInvocation *context, GVariant *new_settings, + const char *plugin_name, NMSettingsUpdate2Flags flags) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self); @@ -1696,6 +1704,7 @@ settings_connection_update(NMSettingsConnection *self, info->subject = subject; info->flags = flags; info->new_settings = tmp; + info->plugin_name = g_strdup(plugin_name); permission = get_update_modify_permission(nm_settings_connection_get_connection(self), tmp ?: nm_settings_connection_get_connection(self)); @@ -1724,7 +1733,12 @@ impl_settings_connection_update(NMDBusObject *obj, gs_unref_variant GVariant *settings = NULL; g_variant_get(parameters, "(@a{sa{sv}})", &settings); - settings_connection_update(self, FALSE, invocation, settings, NM_SETTINGS_UPDATE2_FLAG_TO_DISK); + settings_connection_update(self, + FALSE, + invocation, + settings, + NULL, + NM_SETTINGS_UPDATE2_FLAG_TO_DISK); } static void @@ -1744,6 +1758,7 @@ impl_settings_connection_update_unsaved(NMDBusObject *obj, FALSE, invocation, settings, + NULL, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY); } @@ -1758,7 +1773,12 @@ impl_settings_connection_save(NMDBusObject *obj, { NMSettingsConnection *self = NM_SETTINGS_CONNECTION(obj); - settings_connection_update(self, FALSE, invocation, NULL, NM_SETTINGS_UPDATE2_FLAG_TO_DISK); + settings_connection_update(self, + FALSE, + invocation, + NULL, + NULL, + NM_SETTINGS_UPDATE2_FLAG_TO_DISK); } static void @@ -1770,13 +1790,15 @@ impl_settings_connection_update2(NMDBusObject *obj, GDBusMethodInvocation *invocation, GVariant *parameters) { - NMSettingsConnection *self = NM_SETTINGS_CONNECTION(obj); - gs_unref_variant GVariant *settings = NULL; - gs_unref_variant GVariant *args = NULL; + NMSettingsConnection *self = NM_SETTINGS_CONNECTION(obj); + gs_unref_variant GVariant *settings = NULL; + gs_unref_variant GVariant *args = NULL; + gs_free char *plugin_name = NULL; guint32 flags_u; GError *error = NULL; GVariantIter iter; const char *args_name; + GVariant *args_value; NMSettingsUpdate2Flags flags; g_variant_get(parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args); @@ -1812,7 +1834,13 @@ impl_settings_connection_update2(NMDBusObject *obj, nm_assert(g_variant_is_of_type(args, G_VARIANT_TYPE("a{sv}"))); g_variant_iter_init(&iter, args); - while (g_variant_iter_next(&iter, "{&sv}", &args_name, NULL)) { + while (g_variant_iter_next(&iter, "{&sv}", &args_name, &args_value)) { + if (plugin_name == NULL && nm_streq(args_name, "plugin") + && g_variant_is_of_type(args_value, G_VARIANT_TYPE_STRING)) { + plugin_name = g_variant_dup_string(args_value, NULL); + continue; + } + error = g_error_new(NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_ARGUMENTS, "Unsupported argument '%s'", @@ -1821,7 +1849,7 @@ impl_settings_connection_update2(NMDBusObject *obj, return; } - settings_connection_update(self, TRUE, invocation, settings, flags); + settings_connection_update(self, TRUE, invocation, settings, plugin_name, flags); } static void @@ -2010,6 +2038,7 @@ dbus_clear_secrets_auth_cb(NMSettingsConnection *self, if (!nm_settings_connection_update( self, + NULL, connection_cloned, NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP, NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, diff --git a/src/core/settings/nm-settings-connection.h b/src/core/settings/nm-settings-connection.h index 5f8a22e8..893b0d7b 100644 --- a/src/core/settings/nm-settings-connection.h +++ b/src/core/settings/nm-settings-connection.h @@ -240,6 +240,7 @@ nm_settings_connection_has_unmodified_applied_connection(NMSettingsConnection *s NMSettingCompareFlags compare_flage); gboolean nm_settings_connection_update(NMSettingsConnection *self, + const char *plugin_name, NMConnection *new_connection, NMSettingsConnectionPersistMode persist_mode, NMSettingsConnectionIntFlags sett_flags, diff --git a/src/core/settings/nm-settings.c b/src/core/settings/nm-settings.c index 6619d3e0..1ff66e25 100644 --- a/src/core/settings/nm-settings.c +++ b/src/core/settings/nm-settings.c @@ -236,12 +236,16 @@ _sett_conn_entry_get_conn(SettConnEntry *sett_conn_entry) * update-connection. If this parameter is omitted, then it's about what happens * when adding a new profile (add-connection). * + * @storage_check_ignore is optional, and if given then it skips this particular + * storage. + * * Returns: the conflicting storage or %NULL if there is none. */ static NMSettingsStorage * _sett_conn_entry_storage_find_conflicting_storage(SettConnEntry *sett_conn_entry, NMSettingsPlugin *target_plugin, NMSettingsStorage *storage_check_including, + NMSettingsStorage *storage_check_ignore, const GSList *plugins) { StorageData *sd; @@ -269,6 +273,12 @@ _sett_conn_entry_storage_find_conflicting_storage(SettConnEntry *sett_conn_e continue; } + if (sd->storage == storage_check_ignore) { + /* We ignore this one, because we're in the process of + * replacing it. */ + continue; + } + if (sd->storage == storage_check_including) { /* ok, the storage is the one we are about to check. All other * storages are lower priority, so there is no storage that hides @@ -557,6 +567,8 @@ _startup_complete_check(NMSettings *self, gint64 now_msec) return; } + nm_clear_g_source(&priv->startup_complete_timeout_id); + if (c_list_is_empty(&priv->startup_complete_scd_lst_head)) goto ready; @@ -592,8 +604,6 @@ next_with_ready: } c_list_splice(&priv->startup_complete_scd_lst_head, &ready_lst); - nm_clear_g_source(&priv->startup_complete_timeout_id); - if (scd_not_ready) { gint64 timeout_msec; @@ -1463,6 +1473,7 @@ _plugin_connections_reload(NMSettings *self) static gboolean _add_connection_to_first_plugin(NMSettings *self, + const char *plugin_name, SettConnEntry *sett_conn_entry, NMConnection *new_connection, gboolean in_memory, @@ -1471,12 +1482,14 @@ _add_connection_to_first_plugin(NMSettings *self, gboolean shadowed_owned, NMSettingsStorage **out_new_storage, NMConnection **out_new_connection, + NMSettingsStorage *drop_storage, GError **error) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self); gs_free_error GError *first_error = NULL; GSList *iter; const char *uuid; + gboolean no_plugin = TRUE; uuid = nm_connection_get_uuid(new_connection); @@ -1493,12 +1506,18 @@ _add_connection_to_first_plugin(NMSettings *self, gboolean success; const char *filename; + if (plugin_name && strcmp(plugin_name, nm_settings_plugin_get_plugin_name(plugin))) { + /* Not the plugin we're confined to. Ignore. */ + continue; + } + if (!in_memory) { NMSettingsStorage *conflicting_storage; conflicting_storage = _sett_conn_entry_storage_find_conflicting_storage(sett_conn_entry, plugin, NULL, + drop_storage, priv->plugins); if (conflicting_storage) { /* we have a connection provided by a plugin with higher priority than the one @@ -1545,6 +1564,8 @@ _add_connection_to_first_plugin(NMSettings *self, &add_error); } + no_plugin = FALSE; + if (!success) { _LOGT("add-connection: failed to add %s/'%s': %s", nm_connection_get_uuid(new_connection), @@ -1588,8 +1609,18 @@ _add_connection_to_first_plugin(NMSettings *self, return TRUE; } - nm_assert(first_error); - g_propagate_error(error, g_steal_pointer(&first_error)); + if (no_plugin) { + nm_assert(plugin_name); + nm_assert(!first_error); + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_ARGUMENTS, + "a plugin by the name of '%s' is not available", + plugin_name); + } else { + nm_assert(first_error); + g_propagate_error(error, g_steal_pointer(&first_error)); + } return FALSE; } @@ -1704,6 +1735,7 @@ _set_nmmeta_tombstone(NMSettings *self, */ gboolean nm_settings_add_connection(NMSettings *self, + const char *plugin, NMConnection *connection, NMSettingsConnectionPersistMode persist_mode, NMSettingsConnectionAddReason add_reason, @@ -1800,6 +1832,7 @@ nm_settings_add_connection(NMSettings *self, sett_conn_entry, nm_settings_storage_get_plugin(shadowed_storage), shadowed_storage, + NULL, priv->plugins); if (conflicting_storage) { /* We cannot add the profile as @shadowed_storage, because there is another, existing storage @@ -1824,6 +1857,7 @@ again_add_connection: if (!update_storage) { success = _add_connection_to_first_plugin(self, + plugin, sett_conn_entry, connection, new_in_memory, @@ -1832,6 +1866,7 @@ again_add_connection: FALSE, &new_storage, &new_connection, + NULL, &local); } else { success = _update_connection_to_plugin(self, @@ -1948,6 +1983,7 @@ again_delete_tombstone: gboolean nm_settings_update_connection(NMSettings *self, NMSettingsConnection *sett_conn, + const char *plugin_name, NMConnection *connection, NMSettingsConnectionPersistMode persist_mode, NMSettingsConnectionIntFlags sett_flags, @@ -2154,8 +2190,9 @@ nm_settings_update_connection(NMSettings *self, } else if (nm_settings_storage_is_keyfile_lib(cur_storage)) { /* the profile is a keyfile in /usr/lib. It cannot be overwritten, we must migrate it * from /usr/lib to /etc. */ - } else + } else { update_storage = cur_storage; + } if (new_in_memory) { if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY) { @@ -2174,8 +2211,22 @@ nm_settings_update_connection(NMSettings *self, } } + if (update_storage && plugin_name) { + NMSettingsPlugin *plugin = nm_settings_storage_get_plugin(update_storage); + + if (strcmp(plugin_name, nm_settings_plugin_get_plugin_name(plugin))) { + /* We're updating a connection, we're confined to a particular + * plugin, but the connection is currently using a different one. + * We need to migrate. Drop the existing storage and look out for + * a new one. */ + drop_storage = update_storage; + update_storage = NULL; + } + } + if (!update_storage) { success = _add_connection_to_first_plugin(self, + plugin_name, sett_conn_entry, connection, new_in_memory, @@ -2184,6 +2235,7 @@ nm_settings_update_connection(NMSettings *self, new_shadowed_owned, &new_storage, &new_connection, + drop_storage, &local); } else { success = _update_connection_to_plugin(self, @@ -2462,6 +2514,7 @@ pk_add_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data nm_assert(NM_IS_CONNECTION(connection)); nm_settings_add_connection(self, + nm_auth_chain_get_data(chain, "plugin"), connection, GPOINTER_TO_UINT(nm_auth_chain_get_data(chain, "persist-mode")), GPOINTER_TO_UINT(nm_auth_chain_get_data(chain, "add-reason")), @@ -2489,6 +2542,7 @@ pk_add_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data void nm_settings_add_connection_dbus(NMSettings *self, + const char *plugin, NMConnection *connection, NMSettingsConnectionPersistMode persist_mode, NMSettingsConnectionAddReason add_reason, @@ -2549,6 +2603,7 @@ nm_settings_add_connection_dbus(NMSettings *self, nm_auth_chain_set_data(chain, "persist-mode", GUINT_TO_POINTER(persist_mode), NULL); nm_auth_chain_set_data(chain, "add-reason", GUINT_TO_POINTER(add_reason), NULL); nm_auth_chain_set_data(chain, "sett-flags", GUINT_TO_POINTER(sett_flags), NULL); + nm_auth_chain_set_data(chain, "plugin", g_strdup(plugin), g_free); nm_auth_chain_add_call_unsafe(chain, perm, TRUE); return; @@ -2601,6 +2656,7 @@ settings_add_connection_helper(NMSettings *self, GDBusMethodInvocation *context, gboolean is_add_connection_2, GVariant *settings, + const char *plugin, NMSettingsAddConnection2Flags flags) { gs_unref_object NMConnection *connection = NULL; @@ -2636,6 +2692,7 @@ settings_add_connection_helper(NMSettings *self, nm_settings_add_connection_dbus( self, + plugin, connection, persist_mode, NM_FLAGS_HAS(flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT) @@ -2665,6 +2722,7 @@ impl_settings_add_connection(NMDBusObject *obj, invocation, FALSE, settings, + NULL, NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK); } @@ -2685,6 +2743,7 @@ impl_settings_add_connection_unsaved(NMDBusObject *obj, invocation, FALSE, settings, + NULL, NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY); } @@ -2700,8 +2759,10 @@ impl_settings_add_connection2(NMDBusObject *obj, NMSettings *self = NM_SETTINGS(obj); gs_unref_variant GVariant *settings = NULL; gs_unref_variant GVariant *args = NULL; + gs_free char *plugin = NULL; NMSettingsAddConnection2Flags flags; const char *args_name; + GVariant *args_value; GVariantIter iter; guint32 flags_u; @@ -2745,7 +2806,13 @@ impl_settings_add_connection2(NMDBusObject *obj, nm_assert(g_variant_is_of_type(args, G_VARIANT_TYPE("a{sv}"))); g_variant_iter_init(&iter, args); - while (g_variant_iter_next(&iter, "{&sv}", &args_name, NULL)) { + while (g_variant_iter_next(&iter, "{&sv}", &args_name, &args_value)) { + if (plugin == NULL && nm_streq(args_name, "plugin") + && g_variant_is_of_type(args_value, G_VARIANT_TYPE_STRING)) { + plugin = g_variant_dup_string(args_value, NULL); + continue; + } + g_dbus_method_invocation_take_error(invocation, g_error_new(NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_ARGUMENTS, @@ -2754,7 +2821,7 @@ impl_settings_add_connection2(NMDBusObject *obj, return; } - settings_add_connection_helper(self, invocation, TRUE, settings, flags); + settings_add_connection_helper(self, invocation, TRUE, settings, plugin, flags); } /*****************************************************************************/ @@ -3201,8 +3268,10 @@ nm_settings_get_connection_by_path(NMSettings *self, const char *path) priv = NM_SETTINGS_GET_PRIVATE(self); connection = - nm_dbus_manager_lookup_object(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)), path); - if (!connection || !NM_IS_SETTINGS_CONNECTION(connection)) + nm_dbus_manager_lookup_object_with_type(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)), + NM_TYPE_SETTINGS_CONNECTION, + path); + if (!connection) return NULL; nm_assert(c_list_contains(&priv->connections_lst_head, &connection->_connections_lst)); @@ -3604,6 +3673,7 @@ device_realized(NMDevice *device, GParamSpec *pspec, NMSettings *self) nm_device_get_iface(device)); nm_settings_add_connection(self, + NULL, connection, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, NM_SETTINGS_CONNECTION_ADD_REASON_NONE, diff --git a/src/core/settings/nm-settings.h b/src/core/settings/nm-settings.h index 56cebed0..8747d1dc 100644 --- a/src/core/settings/nm-settings.h +++ b/src/core/settings/nm-settings.h @@ -68,6 +68,7 @@ typedef void (*NMSettingsAddCallback)(NMSettings *settings, gpointer user_data); void nm_settings_add_connection_dbus(NMSettings *self, + const char *plugin, NMConnection *connection, NMSettingsConnectionPersistMode persist_mode, NMSettingsConnectionAddReason add_reason, @@ -90,6 +91,7 @@ NMSettingsConnection **nm_settings_get_connections_clone(NMSettings gpointer sort_data); gboolean nm_settings_add_connection(NMSettings *settings, + const char *plugin, NMConnection *connection, NMSettingsConnectionPersistMode persist_mode, NMSettingsConnectionAddReason add_reason, @@ -99,6 +101,7 @@ gboolean nm_settings_add_connection(NMSettings *settings, gboolean nm_settings_update_connection(NMSettings *self, NMSettingsConnection *sett_conn, + const char *plugin_name, NMConnection *new_connection, NMSettingsConnectionPersistMode persist_mode, NMSettingsConnectionIntFlags sett_flags, diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index 48c4cae9..551f2b91 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -397,7 +397,9 @@ write_8021x_setting(NMConnection *connection, if (wired) svSetValueStr(ifcfg, "KEY_MGMT", "IEEE8021X"); - /* EAP method */ + /* EAP method + * + * FIXME(ifcfg-full-cycle): persist all values of eap-method. */ if (nm_setting_802_1x_get_num_eap_methods(s_8021x)) { value = nm_setting_802_1x_get_eap_method(s_8021x, 0); if (value) @@ -455,11 +457,21 @@ write_8021x_setting(NMConnection *connection, value = "allow-auth"; else if (strcmp(value, "3") == 0) value = "allow-unauth allow-auth"; - else + else { + /* FIXME(ifcfg-full-cycle): does not handle the value "0". */ value = NULL; + } } svSetValueStr(ifcfg, "IEEE_8021X_FAST_PROVISIONING", value); + auth_flags = nm_setting_802_1x_get_phase1_auth_flags(s_8021x); + if (auth_flags != NM_SETTING_802_1X_AUTH_FLAGS_NONE) { + svSetValueEnum(ifcfg, + "IEEE_8021X_PHASE1_AUTH_FLAGS", + nm_setting_802_1x_auth_flags_get_type(), + auth_flags); + } + /* Phase2 auth methods */ phase2_auth = g_string_new(NULL); @@ -480,14 +492,6 @@ write_8021x_setting(NMConnection *connection, g_free(tmp); } - auth_flags = nm_setting_802_1x_get_phase1_auth_flags(s_8021x); - if (auth_flags != NM_SETTING_802_1X_AUTH_FLAGS_NONE) { - svSetValueEnum(ifcfg, - "IEEE_8021X_PHASE1_AUTH_FLAGS", - nm_setting_802_1x_auth_flags_get_type(), - auth_flags); - } - svSetValueStr(ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", phase2_auth->len ? phase2_auth->str : NULL); @@ -503,6 +507,8 @@ write_8021x_setting(NMConnection *connection, str = g_string_new(NULL); num = nm_setting_802_1x_get_num_altsubject_matches(s_8021x); for (i = 0; i < num; i++) { + /* FIXME(ifcfg-full-cycle): this cannot handle values with spaces, which + * are not rejected by nm_connection_verify(). */ if (i > 0) g_string_append_c(str, ' '); match = nm_setting_802_1x_get_altsubject_match(s_8021x, i); @@ -515,6 +521,8 @@ write_8021x_setting(NMConnection *connection, str = g_string_new(NULL); num = nm_setting_802_1x_get_num_phase2_altsubject_matches(s_8021x); for (i = 0; i < num; i++) { + /* FIXME(ifcfg-full-cycle): this cannot handle values with spaces, which + * are not rejected by nm_connection_verify(). */ if (i > 0) g_string_append_c(str, ' '); match = nm_setting_802_1x_get_phase2_altsubject_match(s_8021x, i); diff --git a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 7960ea2a..83e48b81 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -221,6 +221,7 @@ _assert_expected_content(NMConnection *connection, const char *filename, const c success = g_file_set_contents(expected, content_written, len_written, &error); nmtst_assert_success(success, error); } else { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error( "The content of \"%s\" (%zu) differs from \"%s\" (%zu). Set " "NMTST_IFCFG_RH_UPDATE_EXPECTED=yes (or NM_TEST_REGENERATE=1) to update the files " @@ -231,6 +232,7 @@ _assert_expected_content(NMConnection *connection, const char *filename, const c len_expectd, content_written, content_expectd); + NM_PRAGMA_WARNING_REENABLE } } } diff --git a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c index b4c30df7..11a8e416 100644 --- a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c +++ b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c @@ -2635,8 +2635,11 @@ _escape_filename(gboolean with_extension, const char *filename, gboolean would_b g_assert(esc && esc[0]); g_assert(!strchr(esc, '/')); - if (nm_keyfile_utils_ignore_filename(esc, with_extension)) + if (nm_keyfile_utils_ignore_filename(esc, with_extension)) { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("Escaping filename \"%s\" yielded \"%s\", but this is ignored", filename, esc); + NM_PRAGMA_WARNING_REENABLE + } } static void diff --git a/src/core/supplicant/nm-supplicant-config.c b/src/core/supplicant/nm-supplicant-config.c index 96c23579..a4e5cc4d 100644 --- a/src/core/supplicant/nm-supplicant-config.c +++ b/src/core/supplicant/nm-supplicant-config.c @@ -805,6 +805,7 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig NMSettingWirelessSecurity *setting, NMSetting8021x *setting_8021x, const char *con_uuid, + const char *mode, guint32 mtu, NMSettingWirelessSecurityPmf pmf, NMSettingWirelessSecurityFils fils, @@ -815,12 +816,20 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig const char *key_mgmt, *auth_alg; const char *psk; gboolean set_pmf, wps_disabled; + gboolean is_ap; g_return_val_if_fail(NM_IS_SUPPLICANT_CONFIG(self), FALSE); g_return_val_if_fail(setting != NULL, FALSE); g_return_val_if_fail(con_uuid != NULL, FALSE); g_return_val_if_fail(!error || !*error, FALSE); + /* Currently wpa_supplicant doesn't support FT in AP mode. Even + * if it did, it would require additional parameters as the nas + * identifier and the mobility domain. Therefore we disable all + * FT key-mgmts in AP mode. + */ + is_ap = nm_streq0(mode, NM_SETTING_WIRELESS_MODE_AP); + /* Check if we actually support FILS */ if (!_get_capability(priv, NM_SUPPL_CAP_TYPE_FILS)) { if (fils == NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED) { @@ -852,11 +861,39 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig g_string_append(key_mgmt_conf, "WPA-PSK"); if (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)) g_string_append(key_mgmt_conf, " WPA-PSK-SHA256"); - if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) + if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) g_string_append(key_mgmt_conf, " FT-PSK"); - if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SAE)) { + + /* For NM "key-mgmt=wpa-psk" doesn't strictly mean WPA1/wPA2 only, + * but also allows WPA3 (SAE), so that existing connections can + * benefit from the improved security when the AP gets upgraded. + * + * According to WPA3_Specification_v3.0 section 2.3, when operating + * in WPA3-Personal transition mode a STA: + * + * - should allow AKM suite selector: 00-0F-AC:6 (WPA-PSK-SHA256) to + * be selected for an association; + * - shall negotiate PMF when associating to an AP using SAE. + * + * Those conditions are met when the interface has capabilities + * SAE, PMF, BIP. + * + * According to WPA3_Specification_v3.0 section 2.3, when operating + * in WPA3-Personal transition mode an AP: + * + * - shall set MFPC to 1, MFPR to 0. + * + * Therefore, do not operate in WPA3-Personal transition mode when PMF + * is set to disabled. This also provides a way to be compatible with + * some devices that are not fully compatible with WPA3-Personal + * transition mode. + */ + if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SAE) + && _get_capability(priv, NM_SUPPL_CAP_TYPE_PMF) + && _get_capability(priv, NM_SUPPL_CAP_TYPE_BIP) + && (!is_ap || pmf != NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE)) { g_string_append(key_mgmt_conf, " SAE"); - if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) + if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) g_string_append(key_mgmt_conf, " FT-SAE"); } @@ -864,13 +901,13 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig pmf = NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED; g_string_append(key_mgmt_conf, "SAE"); - if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) + if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) g_string_append(key_mgmt_conf, " FT-SAE"); } else if (nm_streq(key_mgmt, "wpa-eap")) { if (pmf != NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) g_string_append(key_mgmt_conf, "WPA-EAP"); - if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) { + if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) { g_string_append(key_mgmt_conf, " FT-EAP"); if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SHA384)) g_string_append(key_mgmt_conf, " FT-EAP-SHA384"); @@ -891,7 +928,7 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig case NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL: if (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)) { g_string_append(key_mgmt_conf, " FILS-SHA256 FILS-SHA384"); - if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) { + if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) { g_string_append(key_mgmt_conf, " FT-FILS-SHA256"); if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SHA384)) g_string_append(key_mgmt_conf, " FT-FILS-SHA384"); @@ -907,7 +944,7 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig pmf = NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED; g_string_append(key_mgmt_conf, "WPA-EAP-SUITE-B-192"); - if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT) + if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT) && _get_capability(priv, NM_SUPPL_CAP_TYPE_SHA384)) g_string_append(key_mgmt_conf, " FT-EAP-SHA384"); } diff --git a/src/core/supplicant/nm-supplicant-config.h b/src/core/supplicant/nm-supplicant-config.h index 349c310f..ee7f4dc8 100644 --- a/src/core/supplicant/nm-supplicant-config.h +++ b/src/core/supplicant/nm-supplicant-config.h @@ -51,6 +51,7 @@ gboolean nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig NMSettingWirelessSecurity *setting, NMSetting8021x *setting_8021x, const char *con_uuid, + const char *mode, guint32 mtu, NMSettingWirelessSecurityPmf pmf, NMSettingWirelessSecurityFils fils, diff --git a/src/core/supplicant/nm-supplicant-interface.c b/src/core/supplicant/nm-supplicant-interface.c index d8276144..e502ae85 100644 --- a/src/core/supplicant/nm-supplicant-interface.c +++ b/src/core/supplicant/nm-supplicant-interface.c @@ -1225,8 +1225,10 @@ parse_capabilities(NMSupplicantInterface *self, GVariant *capabilities) const guint32 old_max_scan_ssids = priv->max_scan_ssids; gboolean have_ft = FALSE; gboolean have_sae = FALSE; + gboolean have_bip = FALSE; gint32 max_scan_ssids; const char **array; + guint i; nm_assert(capabilities && g_variant_is_of_type(capabilities, G_VARIANT_TYPE_VARDICT)); @@ -1236,12 +1238,28 @@ parse_capabilities(NMSupplicantInterface *self, GVariant *capabilities) g_free(array); } + if (g_variant_lookup(capabilities, "GroupMgmt", "^a&s", &array)) { + for (i = 0; array[i]; i++) { + if (NM_IN_STRSET(array[i], + "aes-128-cmac", + "bip-gmac-128", + "bip-gmac-256", + "bip-cmac-256")) { + have_bip = TRUE; + break; + } + } + } + priv->iface_capabilities = NM_SUPPL_CAP_MASK_SET(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_FT, have_ft ? NM_TERNARY_TRUE : NM_TERNARY_FALSE); priv->iface_capabilities = NM_SUPPL_CAP_MASK_SET(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_SAE, have_sae ? NM_TERNARY_TRUE : NM_TERNARY_FALSE); + priv->iface_capabilities = NM_SUPPL_CAP_MASK_SET(priv->iface_capabilities, + NM_SUPPL_CAP_TYPE_BIP, + have_bip ? NM_TERNARY_TRUE : NM_TERNARY_FALSE); if (g_variant_lookup(capabilities, "Modes", "^a&s", &array)) { /* Setting p2p_capable might toggle _prop_p2p_available_get(). However, @@ -1317,10 +1335,12 @@ _starting_check_ready(NMSupplicantInterface *self) " AP%c" " FT%c" " SAE%c" + " BIP%c" "", NM_SUPPL_CAP_TO_CHAR(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_AP), NM_SUPPL_CAP_TO_CHAR(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_FT), - NM_SUPPL_CAP_TO_CHAR(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_SAE)); + NM_SUPPL_CAP_TO_CHAR(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_SAE), + NM_SUPPL_CAP_TO_CHAR(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_BIP)); /* Other global properties are set in constructed() because they don't * depend on interface capabilities. */ @@ -1362,6 +1382,7 @@ _get_capability(NMSupplicantInterfacePrivate *priv, NMSupplCapType type) } break; case NM_SUPPL_CAP_TYPE_SAE: + case NM_SUPPL_CAP_TYPE_BIP: nm_assert(NM_SUPPL_CAP_MASK_GET(priv->global_capabilities, type) == NM_TERNARY_DEFAULT); value = NM_SUPPL_CAP_MASK_GET(priv->iface_capabilities, type); break; @@ -1395,10 +1416,13 @@ nm_supplicant_interface_get_capabilities(NMSupplicantInterface *self) caps = NM_SUPPL_CAP_MASK_SET(caps, NM_SUPPL_CAP_TYPE_SAE, _get_capability(priv, NM_SUPPL_CAP_TYPE_SAE)); + caps = NM_SUPPL_CAP_MASK_SET(caps, + NM_SUPPL_CAP_TYPE_BIP, + _get_capability(priv, NM_SUPPL_CAP_TYPE_BIP)); nm_assert(!NM_FLAGS_ANY(priv->iface_capabilities, ~(NM_SUPPL_CAP_MASK_T_AP_MASK | NM_SUPPL_CAP_MASK_T_FT_MASK - | NM_SUPPL_CAP_MASK_T_SAE_MASK))); + | NM_SUPPL_CAP_MASK_T_SAE_MASK | NM_SUPPL_CAP_MASK_T_BIP_MASK))); #if NM_MORE_ASSERTS > 10 { diff --git a/src/core/supplicant/nm-supplicant-manager.c b/src/core/supplicant/nm-supplicant-manager.c index 9de845f0..2ec7db23 100644 --- a/src/core/supplicant/nm-supplicant-manager.c +++ b/src/core/supplicant/nm-supplicant-manager.c @@ -920,6 +920,7 @@ _dbus_get_capabilities_cb(GVariant *res, GError *error, gpointer user_data) _caps_set(priv, NM_SUPPL_CAP_TYPE_PMF, NM_TERNARY_FALSE); _caps_set(priv, NM_SUPPL_CAP_TYPE_FILS, NM_TERNARY_FALSE); _caps_set(priv, NM_SUPPL_CAP_TYPE_SUITEB192, NM_TERNARY_FALSE); + _caps_set(priv, NM_SUPPL_CAP_TYPE_WEP, NM_TERNARY_TRUE); if (array) { for (a = array; *a; a++) { if (nm_streq(*a, "ap")) { @@ -954,6 +955,10 @@ _dbus_get_capabilities_cb(GVariant *res, GError *error, gpointer user_data) _caps_set(priv, NM_SUPPL_CAP_TYPE_SUITEB192, NM_TERNARY_TRUE); continue; } + if (nm_streq(*a, "wep_disabled")) { + _caps_set(priv, NM_SUPPL_CAP_TYPE_WEP, NM_TERNARY_FALSE); + continue; + } } } } @@ -994,6 +999,7 @@ _dbus_get_capabilities_cb(GVariant *res, GError *error, gpointer user_data) " FAST%c" " WFD%c" " SUITEB192%c" + " WEP%c" "", NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_AP), NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_PMF), @@ -1004,7 +1010,8 @@ _dbus_get_capabilities_cb(GVariant *res, GError *error, gpointer user_data) NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_MESH), NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_FAST), NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_WFD), - NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_SUITEB192)); + NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_SUITEB192), + NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_WEP)); nm_assert(g_hash_table_size(priv->supp_ifaces) == 0); nm_assert(c_list_is_empty(&priv->supp_lst_head)); diff --git a/src/core/supplicant/nm-supplicant-types.h b/src/core/supplicant/nm-supplicant-types.h index d5cf1bf7..1ae67e13 100644 --- a/src/core/supplicant/nm-supplicant-types.h +++ b/src/core/supplicant/nm-supplicant-types.h @@ -48,6 +48,10 @@ typedef enum { NM_SUPPL_CAP_TYPE_FAST, NM_SUPPL_CAP_TYPE_WFD, NM_SUPPL_CAP_TYPE_SUITEB192, + NM_SUPPL_CAP_TYPE_WEP, + NM_SUPPL_CAP_TYPE_BIP, + /* Note: if you're adding a capability here, log its presence at the + * bottom of _dbus_get_capabilities_cb(). */ _NM_SUPPL_CAP_TYPE_NUM, } NMSupplCapType; @@ -75,6 +79,8 @@ typedef enum { _NM_SUPPL_CAP_MASK_DEFINE(FT), _NM_SUPPL_CAP_MASK_DEFINE(SAE), _NM_SUPPL_CAP_MASK_DEFINE(SHA384), + _NM_SUPPL_CAP_MASK_DEFINE(WEP), + _NM_SUPPL_CAP_MASK_DEFINE(BIP), #undef _NM_SUPPL_CAP_MASK_DEFINE } NMSupplCapMask; diff --git a/src/core/supplicant/tests/test-supplicant-config.c b/src/core/supplicant/tests/test-supplicant-config.c index 53c5f70f..237b1a96 100644 --- a/src/core/supplicant/tests/test-supplicant-config.c +++ b/src/core/supplicant/tests/test-supplicant-config.c @@ -116,6 +116,7 @@ build_supplicant_config(NMConnection *connection, s_wsec, s_8021x, nm_connection_get_uuid(connection), + nm_setting_wireless_get_mode(s_wifi), mtu, pmf, fils, diff --git a/src/core/tests/test-core.c b/src/core/tests/test-core.c index f61d66d9..b4e1c4d5 100644 --- a/src/core/tests/test-core.c +++ b/src/core/tests/test-core.c @@ -2319,10 +2319,25 @@ test_machine_id_read(void) char machine_id_str[33]; gpointer logstate; + /* This unit test checks our functions to read /etc/machine-id. As + * the path name is not configurable (and the test does not setup + * a chroot/mountns), we read the actual file from the system. That + * is ugly, as the test depends on the system where it's running. + * + * Still, better a bad test, than no test. Patch welcome to fix this + * shortcoming. + * + * Also, if you have a sufficiently broken system, the unit test fails. + * In particular, if the machine-id file exists but does not contain + * a valid ID. Just don't have that. Fix your system. */ + logstate = nmtst_logging_disable(FALSE); /* If you run this test as root, without a valid /etc/machine-id, * the code will try to get the secret-key. That is a bit ugly, - * but no real problem. */ + * but no real problem. + * + * The real answer is: don't run our unit tests as root. That's + * not the way to do it. */ machine_id = nm_utils_machine_id_bin(); nmtst_logging_reenable(logstate); @@ -2338,9 +2353,15 @@ test_machine_id_read(void) * is invalid. Our machine-id is fake, and we have nothing to * compare against. */ - /* NOTE: this test will fail, if you don't have /etc/machine-id, - * but a valid "LOCALSTATEDIR/lib/dbus/machine-id" file. - * Just don't do that. */ + if (g_file_test(LOCALSTATEDIR "/lib/dbus/machine-id", G_FILE_TEST_EXISTS)) { + /* Hm. So systemd failed to read /etc/machine-id, but we may have the one from D-Bus. + * With LOCALSTATEDIR"/lib/dbus/machine-id", we don't really know whether we + * parsed that file. Assume we don't know and skip the test on this system. */ + g_assert(!nm_utils_machine_id_is_fake()); + return; + } + + /* OK, in this case, our function should have generated a random machine ID. */ g_assert(nm_utils_machine_id_is_fake()); } else { g_assert(!nm_utils_machine_id_is_fake()); diff --git a/src/core/tests/test-l3cfg.c b/src/core/tests/test-l3cfg.c index 5501079e..924d98f1 100644 --- a/src/core/tests/test-l3cfg.c +++ b/src/core/tests/test-l3cfg.c @@ -382,13 +382,11 @@ test_l3cfg(gconstpointer test_data) nm_l3cfg_commit_type_register(l3cfg0, NM_L3_CFG_COMMIT_TYPE_UPDATE, NULL, "test1"); if (!nmtst_get_rand_one_case_in(4)) { - commit_type_2 = - nm_l3cfg_commit_type_register(l3cfg0, - nmtst_rand_select(NM_L3_CFG_COMMIT_TYPE_NONE, - NM_L3_CFG_COMMIT_TYPE_ASSUME, - NM_L3_CFG_COMMIT_TYPE_UPDATE), - NULL, - "test2"); + commit_type_2 = nm_l3cfg_commit_type_register( + l3cfg0, + nmtst_rand_select(NM_L3_CFG_COMMIT_TYPE_NONE, NM_L3_CFG_COMMIT_TYPE_UPDATE), + NULL, + "test2"); } else commit_type_2 = NULL; @@ -956,7 +954,7 @@ test_l3_ipv6ll(gconstpointer test_data) .steps_done = FALSE, }; TestL3IPv6LLData *const tdata = &tdata_stack; - char sbuf1[sizeof(_nm_utils_to_string_buffer)]; + char sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; int r; _LOGD("test start (/l3-ipv6ll/%d)", TEST_IDX); diff --git a/src/core/tests/test-utils.c b/src/core/tests/test-utils.c index 2d2b7340..ad9950dd 100644 --- a/src/core/tests/test-utils.c +++ b/src/core/tests/test-utils.c @@ -212,6 +212,52 @@ test_hw_addr_gen_stable_eth(void) "04:0D:CD:0C:9E:2C"); } +static void +test_shorten_hostname(void) +{ + gs_free char *maxhost = NULL; + char *hostname; + +#define do_test_shorten_hostname(_host, _exp_res, _exp_short) \ + G_STMT_START \ + { \ + gboolean _res; \ + gs_free char *_short = NULL; \ + \ + _res = nm_utils_shorten_hostname((_host), &_short); \ + g_assert_cmpint((_res), ==, (_exp_res)); \ + g_assert_cmpstr(_short, ==, (_exp_short)); \ + } \ + G_STMT_END + + /* 'maxhost' is the longest allowed hostname according to + * system configuration (`getconf HOST_NAME_MAX`). On Linux + * it's typically 64 characters, but POSIX allows up to + * 255 characters. + */ + maxhost = g_strnfill(HOST_NAME_MAX, 'a'); + + do_test_shorten_hostname("name1", TRUE, NULL); + + do_test_shorten_hostname("name1.example.com", TRUE, NULL); + + do_test_shorten_hostname(maxhost, TRUE, NULL); + + hostname = g_strdup_printf("%sbbb", maxhost); + do_test_shorten_hostname(hostname, TRUE, maxhost); + nm_clear_g_free(&hostname); + + hostname = g_strdup_printf("%s.com", maxhost); + do_test_shorten_hostname(hostname, TRUE, maxhost); + nm_clear_g_free(&hostname); + + hostname = g_strdup_printf("name1.%s.com", maxhost); + do_test_shorten_hostname(hostname, TRUE, "name1"); + nm_clear_g_free(&hostname); + + do_test_shorten_hostname(".name1", FALSE, NULL); +} + /*****************************************************************************/ NMTST_DEFINE(); @@ -223,6 +269,7 @@ main(int argc, char **argv) g_test_add_func("/utils/stable_privacy", test_stable_privacy); g_test_add_func("/utils/hw_addr_gen_stable_eth", test_hw_addr_gen_stable_eth); + g_test_add_func("/utils/shorten-hostname", test_shorten_hostname); return g_test_run(); } diff --git a/src/libnm-base/meson.build b/src/libnm-base/meson.build index 1bb61020..f4d977e9 100644 --- a/src/libnm-base/meson.build +++ b/src/libnm-base/meson.build @@ -3,6 +3,7 @@ libnm_base = static_library( 'nm-base', sources: files( + 'nm-base.c', 'nm-ethtool-base.c', 'nm-net-aux.c', 'nm-priv-helper-utils.c', diff --git a/src/libnm-base/nm-base.c b/src/libnm-base/nm-base.c new file mode 100644 index 00000000..f81b285c --- /dev/null +++ b/src/libnm-base/nm-base.c @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nm-base.h" + +/*****************************************************************************/ + +NM_CACHED_QUARK_FCN("nm-crypto-error-quark", _nm_crypto_error_quark); + +/*****************************************************************************/ diff --git a/src/libnm-base/nm-base.h b/src/libnm-base/nm-base.h index 0363e3b6..ff062a18 100644 --- a/src/libnm-base/nm-base.h +++ b/src/libnm-base/nm-base.h @@ -393,4 +393,20 @@ typedef struct { #define NM_BOND_PORT_QUEUE_ID_DEF 0 +/*****************************************************************************/ + +/* NM_CRYPTO_ERROR is part of public API in libnm (implemented in libnm-core). + * We also want to use it without libnm-core. So this "_" variant is the internal + * version, with numerically same values -- to be used without libnm-base. */ + +#define _NM_CRYPTO_ERROR_FAILED 0 +#define _NM_CRYPTO_ERROR_INVALID_DATA 1 +#define _NM_CRYPTO_ERROR_INVALID_PASSWORD 2 +#define _NM_CRYPTO_ERROR_UNKNOWN_CIPHER 3 +#define _NM_CRYPTO_ERROR_DECRYPTION_FAILED 4 +#define _NM_CRYPTO_ERROR_ENCRYPTION_FAILED 5 + +#define _NM_CRYPTO_ERROR _nm_crypto_error_quark() +GQuark _nm_crypto_error_quark(void); + #endif /* __NM_LIBNM_BASE_H__ */ diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver index bb691ac2..b6d566fc 100644 --- a/src/libnm-client-impl/libnm.ver +++ b/src/libnm-client-impl/libnm.ver @@ -1820,4 +1820,10 @@ global: libnm_1_36_0 { global: nm_setting_ovs_dpdk_get_n_rxq; -} libnm_1_34_0; \ No newline at end of file +} libnm_1_34_0; + +libnm_1_38_0 { +global: + nm_client_get_radio_flags; + nm_radio_flags_get_type; +} libnm_1_36_0; \ No newline at end of file diff --git a/src/libnm-client-impl/meson.build b/src/libnm-client-impl/meson.build index 21a01e0b..2026f98d 100644 --- a/src/libnm-client-impl/meson.build +++ b/src/libnm-client-impl/meson.build @@ -140,6 +140,14 @@ if enable_introspection src_inc, top_inc, ], + dependencies: [ + declare_dependency( + compile_args: [ + '-UGLIB_VERSION_MIN_REQUIRED', + '-UGLIB_VERSION_MAX_ALLOWED', + ], + ), + ], nsversion: nm_gir_version, namespace: 'NM', identifier_prefix: nm_id_prefix, diff --git a/src/libnm-client-impl/nm-client.c b/src/libnm-client-impl/nm-client.c index cd2aa730..b18f47be 100644 --- a/src/libnm-client-impl/nm-client.c +++ b/src/libnm-client-impl/nm-client.c @@ -198,6 +198,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMClient, PROP_WWAN_HARDWARE_ENABLED, PROP_WIMAX_ENABLED, PROP_WIMAX_HARDWARE_ENABLED, + PROP_RADIO_FLAGS, PROP_ACTIVE_CONNECTIONS, PROP_CONNECTIVITY, PROP_CONNECTIVITY_CHECK_URI, @@ -307,6 +308,7 @@ typedef struct { guint32 connectivity; guint32 state; guint32 metered; + guint32 radio_flags; bool connectivity_check_available; bool connectivity_check_enabled; bool networking_enabled; @@ -4236,6 +4238,24 @@ nm_client_wireless_hardware_get_enabled(NMClient *client) } /** + * nm_client_get_radio_flags: + * @client: a #NMClient + * + * Get radio flags. + * + * Returns: the #NMRadioFlags. + * + * Since: 1.38 + **/ +NMRadioFlags +nm_client_get_radio_flags(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NM_RADIO_FLAG_NONE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.radio_flags; +} + +/** * nm_client_wwan_get_enabled: * @client: a #NMClient * @@ -7349,7 +7369,10 @@ _init_start_with_bus(NMClient *self) NULL); if (id == 0) { priv->init_data->cancel_on_idle_source = - nm_g_idle_source_new(G_PRIORITY_DEFAULT, _init_start_cancel_on_idle_cb, self, NULL); + nm_g_idle_source_new(G_PRIORITY_DEFAULT_IDLE, + _init_start_cancel_on_idle_cb, + self, + NULL); g_source_attach(priv->init_data->cancel_on_idle_source, priv->main_context); return; } @@ -7451,6 +7474,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) case PROP_WIRELESS_HARDWARE_ENABLED: g_value_set_boolean(value, nm_client_wireless_hardware_get_enabled(self)); break; + case PROP_RADIO_FLAGS: + g_value_set_uint(value, priv->nm.radio_flags); + break; case PROP_WWAN_ENABLED: g_value_set_boolean(value, nm_client_wwan_get_enabled(self)); break; @@ -8043,6 +8069,10 @@ const NMLDBusMetaIface _nml_dbus_meta_iface_nm = NML_DBUS_META_IFACE_INIT_PROP( _priv.nm.property_o[PROPERTY_O_IDX_NM_PRIMAY_CONNECTION], nm_active_connection_get_type), NML_DBUS_META_PROPERTY_INIT_IGNORE("PrimaryConnectionType", "s"), + NML_DBUS_META_PROPERTY_INIT_U("RadioFlags", + PROP_RADIO_FLAGS, + NMClient, + _priv.nm.radio_flags), NML_DBUS_META_PROPERTY_INIT_B("Startup", PROP_STARTUP, NMClient, _priv.nm.startup), NML_DBUS_META_PROPERTY_INIT_U("State", PROP_STATE, NMClient, _priv.nm.state), NML_DBUS_META_PROPERTY_INIT_S("Version", PROP_VERSION, NMClient, _priv.nm.version), @@ -8311,6 +8341,21 @@ nm_client_class_init(NMClientClass *client_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** + * NMClient:radio-flags: + * + * Flags for radio interfaces. See #NMRadioFlags. + * + * Since: 1.38 + **/ + obj_properties[PROP_RADIO_FLAGS] = g_param_spec_uint(NM_CLIENT_RADIO_FLAGS, + "", + "", + 0, + G_MAXUINT32, + NM_RADIO_FLAG_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** * NMClient:active-connections: (type GPtrArray(NMActiveConnection)) * * The active connections. diff --git a/src/libnm-client-impl/nm-libnm-utils.c b/src/libnm-client-impl/nm-libnm-utils.c index d3d429eb..951db1bc 100644 --- a/src/libnm-client-impl/nm-libnm-utils.c +++ b/src/libnm-client-impl/nm-libnm-utils.c @@ -10,7 +10,9 @@ #include "libnm-glib-aux/nm-time-utils.h" #include "libnm-core-aux-intern/nm-common-macros.h" +#include "libnm-crypto/nm-crypto.h" #include "nm-object.h" +#include "nm-utils.h" /*****************************************************************************/ @@ -914,3 +916,58 @@ nm_utils_print(int output_mode, const char *msg) else g_return_if_reached(); } + +/*****************************************************************************/ + +/** + * nm_utils_file_is_certificate: + * @filename: name of the file to test + * + * Tests if @filename has a valid extension for an X.509 certificate file + * (".cer", ".crt", ".der", or ".pem"), and contains a certificate in a format + * recognized by NetworkManager. + * + * Returns: %TRUE if the file is a certificate, %FALSE if it is not + **/ +gboolean +nm_utils_file_is_certificate(const char *filename) +{ + g_return_val_if_fail(filename != NULL, FALSE); + + return nm_crypto_utils_file_is_certificate(filename); +} + +/** + * nm_utils_file_is_private_key: + * @filename: name of the file to test + * @out_encrypted: (out): on return, whether the file is encrypted + * + * Tests if @filename has a valid extension for an X.509 private key file + * (".der", ".key", ".pem", or ".p12"), and contains a private key in a format + * recognized by NetworkManager. + * + * Returns: %TRUE if the file is a private key, %FALSE if it is not + **/ +gboolean +nm_utils_file_is_private_key(const char *filename, gboolean *out_encrypted) +{ + g_return_val_if_fail(filename != NULL, FALSE); + + return nm_crypto_utils_file_is_private_key(filename, out_encrypted); +} + +/** + * nm_utils_file_is_pkcs12: + * @filename: name of the file to test + * + * Tests if @filename is a PKCS#<!-- -->12 file. + * + * Returns: %TRUE if the file is PKCS#<!-- -->12, %FALSE if it is not + **/ +gboolean +nm_utils_file_is_pkcs12(const char *filename) +{ + g_return_val_if_fail(filename != NULL, FALSE); + + return nm_crypto_is_pkcs12_file(filename, NULL); +} diff --git a/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml b/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml index 91d04e13..d051725d 100644 --- a/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml +++ b/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml @@ -31,7 +31,7 @@ </setting><setting name="ppp"> </setting><setting name="pppoe"> </setting><setting name="proxy"> -<property name="method" variable="PROXY_METHOD(+)" format="" values="none, auto" default="none" example="" description="Method for proxy configuration. For "auto", WPAD is used for proxy configuration, or set the PAC file via PAC_URL or PAC_SCRIPT." /><property name="browser-only" variable="BROWSER_ONLY(+)" format="" values="" default="no" example="" description="Whether the proxy configuration is for browser only." /><property name="pac-url" variable="PAC_URL(+)" format="" values="" default="" example="PAC_URL=http://wpad.mycompany.com/wpad.dat" description="URL for PAC file." /><property name="pac-script" variable="PAC_SCRIPT(+)" format="" values="" default="" example="PAC_SCRIPT=/home/joe/proxy.pac" description="Path of the PAC script." /></setting><setting name="serial"> +<property name="method" variable="PROXY_METHOD(+)" format="" values="none, auto" default="none" example="" description="Method for proxy configuration. For "auto", WPAD is used for proxy configuration, or set the PAC file via PAC_URL or PAC_SCRIPT." /><property name="browser-only" variable="BROWSER_ONLY(+)" format="" values="" default="no" example="" description="Whether the proxy configuration is for browser only." /><property name="pac-url" variable="PAC_URL(+)" format="" values="" default="" example="PAC_URL=http://wpad.mycompany.com/wpad.dat" description="URL for PAC file." /><property name="pac-script" variable="PAC_SCRIPT(+)" format="" values="" default="" example="PAC_SCRIPT="function FindProxyForURL (url, host) { return 'PROXY proxy.example.com:8080; DIRECT'; }"" description="The PAC script. This is an UTF-8 encoded javascript code that defines a FindProxyForURL() function." /></setting><setting name="serial"> </setting><setting name="sriov"> <property name="total-vfs" variable="SRIOV_TOTAL_VFS(+)" format="" values="" default="" example="SRIOV_TOTAL_VFS=16" description="The total number of virtual functions to create" /><property name="vfs" variable="SRIOV_VF1(+), SRIOV_VF2(+), ..." format="" values="" default="" example="SRIOV_VF10="mac=00:11:22:33:44:55", ..." description="SR-IOV virtual function descriptors" /><property name="autoprobe-drivers" variable="SRIOV_AUTOPROBE_DRIVERS(+)" format="" values="" default="missing variable means global default" example="SRIOV_AUTOPROBE_DRIVERS=0,1" description="Whether to autoprobe virtual functions by a compatible driver" /></setting><setting name="tc"> <property name="qdiscs" variable="QDISC1(+), QDISC2(+), ..., TC_COMMIT(+)" format="" values="" default="" example="QDISC1=ingress, QDISC2="root handle 1234: fq_codel"" description="Queueing disciplines to set on the interface. When no QDISC1, QDISC2, ..., FILTER1, FILTER2, ... keys are present, NetworkManager doesn't touch qdiscs and filters present on the interface, unless TC_COMMIT is set to 'yes'." /><property name="qdiscs" variable="FILTER1(+), FILTER2(+), ..., TC_COMMIT(+)" format="" values="" default="" example="FILTER1="parent ffff: matchall action simple sdata Input", ..." description="Traffic filters to set on the interface. When no QDISC1, QDISC2, ..., FILTER1, FILTER2, ... keys are present, NetworkManager doesn't touch qdiscs and filters present on the interface, unless TC_COMMIT is set to 'yes'." /></setting><setting name="team-port"> diff --git a/src/libnm-client-impl/nm-property-infos-nmcli.xml b/src/libnm-client-impl/nm-property-infos-nmcli.xml index 3d067745..8e39a7a2 100644 --- a/src/libnm-client-impl/nm-property-infos-nmcli.xml +++ b/src/libnm-client-impl/nm-property-infos-nmcli.xml @@ -71,7 +71,8 @@ Various attributes are supported: </listitem> <listitem> <para><literal>"type"</literal> - one of <literal>unicast</literal>, <literal>local</literal>, <literal>blackhole</literal>, -<literal>unavailable</literal>, <literal>prohibit</literal>. The default is <literal>unicast</literal>.</para> +<literal>unavailable</literal>, <literal>prohibit</literal>, <literal>throw</literal>. +The default is <literal>unicast</literal>.</para> </listitem> <listitem> <para><literal>"window"</literal> - an unsigned 32 bit integer.</para> @@ -90,7 +91,7 @@ fixed priority. <para> Example: <literal>priority 5 from 192.167.4.0/24 table 45</literal> </para></description-docbook></property></setting><setting name="ipv6"> -<property name="addresses" variable="addresses" format="a comma separated list of addresses" values="" default="" example="" description="A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example "2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64". The addresses are listed in increasing priority, meaning the last address will be the primary address." /><property name="routes" variable="routes" format="a comma separated list of routes" values="" default="" example="" description=""><description-docbook> +<property name="addresses" variable="addresses" format="a comma separated list of addresses" values="" default="" example="" description="A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example "2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64". The addresses are listed in decreasing priority, meaning the first address will be the primary address. This can make a difference with IPv6 source address selection (RFC 6724, section 5)." /><property name="routes" variable="routes" format="a comma separated list of routes" values="" default="" example="" description=""><description-docbook> <para> A list of IPv6 destination addresses, prefix length, optional IPv6 next hop addresses, optional route metric, optional attribute. The valid syntax is: @@ -140,7 +141,8 @@ Various attributes are supported: </listitem> <listitem> <para><literal>"type"</literal> - one of <literal>unicast</literal>, <literal>local</literal>, <literal>blackhole</literal>, -<literal>unavailable</literal>, <literal>prohibit</literal>. The default is <literal>unicast</literal>.</para> +<literal>unavailable</literal>, <literal>prohibit</literal>, <literal>throw</literal>. +The default is <literal>unicast</literal>.</para> </listitem> <listitem> <para><literal>"window"</literal> - an unsigned 32 bit integer.</para> @@ -171,7 +173,7 @@ Example: <literal>priority 5 from 1:2:3::5/128 table 45</literal> </setting><setting name="ppp"> </setting><setting name="pppoe"> </setting><setting name="proxy"> -</setting><setting name="serial"> +<property name="pac-script" variable="pac-script" format="" values="" default="" example="" description="The PAC script. In the profile this must be an UTF-8 encoded javascript code that defines a FindProxyForURL() function. When setting the property in nmcli, a filename is accepted too. In that case, nmcli will read the content of the file and set the script. The prefixes "file://" and "js://" are supported to explicitly differentiate between the two." /></setting><setting name="serial"> </setting><setting name="sriov"> </setting><setting name="tc"> <property name="qdiscs" variable="qdiscs" format="GPtrArray(NMTCQdisc)" values="" default="" example="" description=""><description-docbook> diff --git a/src/libnm-client-impl/nm-settings-docs-gir.xml b/src/libnm-client-impl/nm-settings-docs-gir.xml index d8022132..77a95b51 100644 --- a/src/libnm-client-impl/nm-settings-docs-gir.xml +++ b/src/libnm-client-impl/nm-settings-docs-gir.xml @@ -5,7 +5,7 @@ <nm-setting-docs> <setting name="connection" description="General Connection Profile Settings" name_upper="CONNECTION" > <property name="auth-retries" name_upper="AUTH_RETRIES" type="int32" default="-1" description="The number of retries for the authentication. Zero means to try indefinitely; -1 means to use a global default. If the global default is not set, the authentication retries for 3 times before failing the connection. Currently, this only applies to 802-1x authentication." /> - <property name="autoconnect" name_upper="AUTOCONNECT" type="boolean" default="TRUE" description='Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See "secondaries" as an alternative to automatically connect VPN profiles.' /> + <property name="autoconnect" name_upper="AUTOCONNECT" type="boolean" default="TRUE" description='Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See "secondaries" as an alternative to automatically connect VPN profiles. If multiple profiles are ready to autoconnect on the same device, the one with the better "connection.autoconnect-priority" is chosen. If the priorities are equal, then the most recently connected profile is activated. If the profiles were not connected earlier or their "connection.timestamp" is identical, the choice is undefined. Depending on "connection.multi-connect", a profile can (auto)connect only once at a time or multiple times.' /> <property name="autoconnect-priority" name_upper="AUTOCONNECT_PRIORITY" type="int32" default="0" description="The autoconnect priority in range -999 to 999. If the connection is set to autoconnect, connections with higher priority will be preferred. The higher number means higher priority. Defaults to 0. Note that this property only matters if there are more than one candidate profile to select for autoconnect. In case of equal priority, the profile used most recently is chosen." /> <property name="autoconnect-retries" name_upper="AUTOCONNECT_RETRIES" type="int32" default="-1" description="The number of times a connection should be tried when autoactivating before giving up. Zero means forever, -1 means the global default (4 times if not overridden). Setting this to 1 means to try activation only once before blocking autoconnect. Note that after a timeout, NetworkManager will try to autoconnect again." /> <property name="autoconnect-slaves" name_upper="AUTOCONNECT_SLAVES" type="NMSettingConnectionAutoconnectSlaves (int32)" description='Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties "autoconnect", "autoconnect-priority" and "autoconnect-retries" are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0.' /> @@ -24,7 +24,7 @@ <property name="read-only" name_upper="READ_ONLY" type="boolean" default="FALSE" description="FALSE if the connection can be modified using the provided settings service's D-Bus interface with the right privileges, or TRUE if the connection is read-only and cannot be modified." /> <property name="secondaries" name_upper="SECONDARIES" type="array of string" description="List of connection UUIDs that should be activated when the base connection itself is activated. Currently, only VPN connections are supported." /> <property name="slave-type" name_upper="SLAVE_TYPE" type="string" description="Setting name of the device type of this slave's master connection (eg, "bond"), or NULL if this connection is not a slave." /> - <property name="stable-id" name_upper="STABLE_ID" type="string" description="This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret-key. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are "${CONNECTION}", "${DEVICE}", "${MAC}", "${BOOT}", "${RANDOM}". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that "${DEVICE}" corresponds to the interface name of the device and "${MAC}" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as "$$". For example, set it to "${CONNECTION}-${BOOT}-${DEVICE}" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to "${CONNECTION}" and uses a unique, fixed ID for the connection." /> + <property name="stable-id" name_upper="STABLE_ID" type="string" description="This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual about the secret-key and the host identity. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are "${CONNECTION}", "${DEVICE}", "${MAC}", "${BOOT}", "${RANDOM}". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that "${DEVICE}" corresponds to the interface name of the device and "${MAC}" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as "$$". For example, set it to "${CONNECTION}-${BOOT}-${DEVICE}" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to "${CONNECTION}" and uses a unique, fixed ID for the connection." /> <property name="timestamp" name_upper="TIMESTAMP" type="uint64" default="0" description="The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved)." /> <property name="type" name_upper="TYPE" type="string" description='Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, "802-3-ethernet" or "802-11-wireless" or "bluetooth", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, "vpn" or "bridge", etc).' /> <property name="uuid" name_upper="UUID" type="string" description='A universally unique identifier for the connection, for example generated with libuuid. It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network. For example, it should not be changed when the "id" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or "type" property changes. The UUID must be in the format "2815492f-7e56-435e-b2e9-246bd7cdc664" (ie, contains only hexadecimal characters and "-").' /> @@ -353,7 +353,7 @@ <setting name="proxy" description="WWW Proxy Settings" name_upper="PROXY" > <property name="browser-only" name_upper="BROWSER_ONLY" type="boolean" default="FALSE" description="Whether the proxy configuration is for browser only." /> <property name="method" name_upper="METHOD" type="int32" default="0" description="Method for proxy configuration, Default is NM_SETTING_PROXY_METHOD_NONE (0)" /> - <property name="pac-script" name_upper="PAC_SCRIPT" type="string" description="PAC script for the connection." /> + <property name="pac-script" name_upper="PAC_SCRIPT" type="string" description="PAC script for the connection. This is an UTF-8 encoded javascript code that defines a FindProxyForURL() function." /> <property name="pac-url" name_upper="PAC_URL" type="string" description="PAC URL for obtaining PAC file." /> </setting> <setting name="serial" description="Serial Link Settings" name_upper="SERIAL" > diff --git a/src/libnm-client-impl/nm-vpn-service-plugin.c b/src/libnm-client-impl/nm-vpn-service-plugin.c index 2a217502..3493d1db 100644 --- a/src/libnm-client-impl/nm-vpn-service-plugin.c +++ b/src/libnm-client-impl/nm-vpn-service-plugin.c @@ -729,54 +729,6 @@ nm_vpn_service_plugin_secrets_required(NMVpnServicePlugin *plugin, /*****************************************************************************/ -typedef struct { - char *buf; - gsize n_buf; - int fd; - bool eof : 1; - char buf_full[1024]; -} ReadFdBuf; - -static inline gboolean -_read_fd_buf_c(ReadFdBuf *read_buf, char *ch) -{ - gssize n_read; - - if (read_buf->n_buf > 0) - goto out_data; - if (read_buf->eof) - return FALSE; - -again: - n_read = read(read_buf->fd, read_buf->buf_full, sizeof(read_buf->buf_full)); - if (n_read <= 0) { - if (n_read < 0 && errno == EAGAIN) { - struct pollfd pfd; - int r; - - memset(&pfd, 0, sizeof(pfd)); - pfd.fd = read_buf->fd; - pfd.events = POLLIN; - - r = poll(&pfd, 1, -1); - if (r > 0) - goto again; - /* error or timeout. Fall through and set EOF. */ - } - read_buf->eof = TRUE; - return FALSE; - } - - read_buf->buf = read_buf->buf_full; - read_buf->n_buf = n_read; - -out_data: - read_buf->n_buf--; - *ch = read_buf->buf[0]; - read_buf->buf++; - return TRUE; -} - #define DATA_KEY_TAG "DATA_KEY=" #define DATA_VAL_TAG "DATA_VAL=" #define SECRET_KEY_TAG "SECRET_KEY=" @@ -808,7 +760,7 @@ nm_vpn_service_plugin_read_vpn_details(int fd, GHashTable **out_data, GHashTable nm_auto_free_gstring GString *val = NULL; nm_auto_free_gstring GString *line = NULL; GString *str = NULL; - ReadFdBuf read_buf; + char c; if (out_data) g_return_val_if_fail(*out_data == NULL, FALSE); @@ -819,27 +771,37 @@ nm_vpn_service_plugin_read_vpn_details(int fd, GHashTable **out_data, GHashTable secrets = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, (GDestroyNotify) nm_free_secret); - read_buf.buf = NULL; - read_buf.n_buf = 0; - read_buf.fd = fd; - read_buf.eof = FALSE; - line = g_string_new(NULL); /* Read stdin for data and secret items until we get a DONE */ while (1) { - gboolean eof; - char c = '\0'; + ssize_t nr; + + nr = read(fd, &c, 1); + if (nr < 0) { + if (errno == EAGAIN) { + struct pollfd pfd; + int r; - eof = !_read_fd_buf_c(&read_buf, &c); + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = POLLIN; + + r = poll(&pfd, 1, -1); + if (r > 0) + continue; + + /* error or timeout. Fall through and break. */ + } + break; + } - if (!eof && c == '\0') { - /* On the first '\0' char, we also assume the data is finished. Abort. */ - read_buf.eof = TRUE; - eof = TRUE; + if (nr > 0 && c == '\0') { + /* '\0' are not supported. Replace with newline. */ + c = '\n'; } - if (!eof && c != '\n') { + if (nr > 0 && c != '\n') { g_string_append_c(line, c); if (line->len > 512 * 1024) { /* we are about to read a huge line. That is not right, abort. */ @@ -893,7 +855,7 @@ nm_vpn_service_plugin_read_vpn_details(int fd, GHashTable **out_data, GHashTable next: g_string_truncate(line, 0); - if (eof) + if (nr == 0) break; } diff --git a/src/libnm-client-impl/tests/test-libnm.c b/src/libnm-client-impl/tests/test-libnm.c index 0f600a4a..1810e139 100644 --- a/src/libnm-client-impl/tests/test-libnm.c +++ b/src/libnm-client-impl/tests/test-libnm.c @@ -2272,6 +2272,12 @@ _memfd_create(const char *name) return -1; } +typedef enum { + READ_VPN_DETAIL_TYPE_GOOD, + READ_VPN_DETAIL_TYPE_NO_DONE, + READ_VPN_DETAIL_TYPE_BROKEN, +} ReadVpnDetailType; + typedef struct { const char *key; const char *val; @@ -2285,6 +2291,7 @@ _do_read_vpn_details_impl1(const char *file, int memfd, char *mem, gsize len, + ReadVpnDetailType detail_type, const ReadVpnDetailData *expected_data, guint expected_data_len, const ReadVpnDetailData *expected_secrets, @@ -2294,10 +2301,27 @@ _do_read_vpn_details_impl1(const char *file, off_t lseeked; gs_unref_hashtable GHashTable *data = NULL; gs_unref_hashtable GHashTable *secrets = NULL; + char ch; + gboolean append_quit; + char read_buf[1024]; + gssize n_read; + gssize i; written = write(memfd, mem, len); g_assert_cmpint(written, ==, (gssize) len); + append_quit = nmtst_get_rand_bool(); + + if (append_quit) { + if (len > 0 && mem[len - 1] != '\n') { + ch = '\n'; + written = write(memfd, &ch, 1); + g_assert_cmpint(written, ==, 1); + } + written = write(memfd, "QUIT", 4); + g_assert_cmpint(written, ==, 4); + } + lseeked = lseek(memfd, 0, SEEK_SET); g_assert_cmpint(lseeked, ==, 0); @@ -2327,13 +2351,14 @@ _do_read_vpn_details_impl1(const char *file, if (nm_streq(_expected[_i].key, _k)) \ break; \ } \ - if (_i >= _expected_len) \ + if (_i >= _expected_len) { \ g_error("%s:%d: hash '%s' contains unexpected data key '%s' with value '%s'", \ file, \ line, \ G_STRINGIFY(hash), \ _k, \ _v); \ + } \ } \ \ for (_i = 0; _i < _expected_len; _i++) { \ @@ -2342,7 +2367,7 @@ _do_read_vpn_details_impl1(const char *file, g_assert(_d->key); \ g_assert(_d->val); \ _v = g_hash_table_lookup(_hash, _d->key); \ - if (!nm_streq0(_v, _d->val)) \ + if (!nm_streq0(_v, _d->val)) { \ g_error("%s:%d: hash '%s' contains data key '%s' with value %s%s%s but we " \ "expected '%s'", \ file, \ @@ -2351,20 +2376,71 @@ _do_read_vpn_details_impl1(const char *file, _d->key, \ NM_PRINT_FMT_QUOTE_STRING(_v), \ _d->val); \ + } \ } \ \ g_assert_cmpint(g_hash_table_size(_hash), ==, _expected_len); \ } \ G_STMT_END + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER _assert_hash(data, expected_data, expected_data_len); _assert_hash(secrets, expected_secrets, expected_secrets_len); + NM_PRAGMA_WARNING_REENABLE #undef _assert_hash + + n_read = read(memfd, read_buf, sizeof(read_buf)); + + if (0) { + gs_free char *ss = NULL; + + g_print(">>>> n_read=%zd; \"%s\"", + n_read, + n_read > 0 ? ( + ss = nm_utils_buf_utf8safe_escape_cp(read_buf, + n_read, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)) + : ""); + } + + g_assert_cmpint(n_read, >=, 0); + g_assert_cmpint(n_read, <, sizeof(read_buf)); + + i = read(memfd, &ch, 1); + g_assert_cmpint(i, ==, 0); + + switch (detail_type) { + case READ_VPN_DETAIL_TYPE_GOOD: + g_assert_cmpint(n_read, >=, 0); + i = 0; + while (i < n_read && NM_IN_SET(read_buf[i], '\n')) + i++; + if (append_quit) + g_assert_cmpmem("QUIT", 4, &read_buf[i], n_read - i); + else + g_assert_cmpint(n_read - i, ==, 0); + break; + case READ_VPN_DETAIL_TYPE_NO_DONE: + g_assert_cmpint(n_read, ==, 0); + break; + case READ_VPN_DETAIL_TYPE_BROKEN: + g_assert_cmpint(n_read, >, 0); + if (append_quit) { + g_assert_cmpint(n_read, >, 4); + g_assert(memmem(&read_buf[i], n_read + i, "QUIT", 4)); + } + break; + default: + g_assert_not_reached(); + break; + } + return TRUE; } #define _do_read_vpn_details_impl0(str, \ + detail_type, \ expected_data, \ expected_data_len, \ expected_secrets, \ @@ -2385,6 +2461,7 @@ _do_read_vpn_details_impl1(const char *file, _memfd, \ "" str "", \ NM_STRLEN(str), \ + (detail_type), \ expected_data, \ expected_data_len, \ expected_secrets, \ @@ -2393,14 +2470,16 @@ _do_read_vpn_details_impl1(const char *file, } \ G_STMT_END -#define _do_read_vpn_details_empty(str) _do_read_vpn_details_impl0(str, NULL, 0, NULL, 0, {}) +#define _do_read_vpn_details_empty(str) \ + _do_read_vpn_details_impl0(str, READ_VPN_DETAIL_TYPE_GOOD, NULL, 0, NULL, 0, {}) -#define _do_read_vpn_details(str, expected_data, expected_secrets, pre_setup_cmd) \ - _do_read_vpn_details_impl0(str, \ - expected_data, \ - G_N_ELEMENTS(expected_data), \ - expected_secrets, \ - G_N_ELEMENTS(expected_secrets), \ +#define _do_read_vpn_details(str, detail_type, expected_data, expected_secrets, pre_setup_cmd) \ + _do_read_vpn_details_impl0(str, \ + detail_type, \ + expected_data, \ + G_N_ELEMENTS(expected_data), \ + expected_secrets, \ + G_N_ELEMENTS(expected_secrets), \ pre_setup_cmd) static void @@ -2426,6 +2505,7 @@ test_nm_vpn_service_plugin_read_vpn_details(void) "DONE\n" "\n" "", + READ_VPN_DETAIL_TYPE_GOOD, READ_VPN_DETAIL_DATA({"some-key", "string"}, {"some-other-key", "val2"}, ), READ_VPN_DETAIL_DATA({"some-secret", "val3"}, ), ); @@ -2433,6 +2513,7 @@ test_nm_vpn_service_plugin_read_vpn_details(void) "DATA_KEY=some-key\n" "DATA_VAL=string\n" "DONE\n", + READ_VPN_DETAIL_TYPE_GOOD, READ_VPN_DETAIL_DATA({"some-key", "string"}, ), READ_VPN_DETAIL_DATA(), ); @@ -2461,6 +2542,7 @@ test_nm_vpn_service_plugin_read_vpn_details(void) "\n" "DONE\n" "", + READ_VPN_DETAIL_TYPE_BROKEN, READ_VPN_DETAIL_DATA({"some-key", "string\ncontinued after a line break"}, ), READ_VPN_DETAIL_DATA({"key names\ncan have\ncontinuations too", "value"}, ), ); @@ -2474,6 +2556,7 @@ test_nm_vpn_service_plugin_read_vpn_details(void) "=continuations too\n" "SECRET_VAL=value\n" "", + READ_VPN_DETAIL_TYPE_NO_DONE, READ_VPN_DETAIL_DATA({"some-key", "string\ncontinued after a line break"}, ), READ_VPN_DETAIL_DATA({"key names\ncan have\ncontinuations too", "value"}, ), ); @@ -2507,6 +2590,7 @@ test_nm_vpn_service_plugin_read_vpn_details(void) "DONE\n" "\n" "", + READ_VPN_DETAIL_TYPE_GOOD, READ_VPN_DETAIL_DATA({"some\nkey-2", "val2"}, {"some-key", "string"}, {"key3\nkey-2", "val3"}, ), @@ -2546,8 +2630,15 @@ test_nm_vpn_service_plugin_read_vpn_details(void) "DONE\n" "\n" "", - READ_VPN_DETAIL_DATA({"some\nkey-2", "val2"}, {"some-key", "string"}, ), - READ_VPN_DETAIL_DATA(), ); + READ_VPN_DETAIL_TYPE_GOOD, + READ_VPN_DETAIL_DATA({"some\nkey-2", "val2"}, + {"some-key", "string"}, + {"key3\nkey-2", "val3"}, ), + READ_VPN_DETAIL_DATA({"some-secret", "val3"}, + {"key-inval", "in\xc1val"}, + {"ke\xc0yx", "inval"}, + {"keyx", ""}, + {"", "val3"}), ); } /*****************************************************************************/ @@ -3049,10 +3140,12 @@ check_dbus_properties: break; p_expected_type++; if (p_expected_type >= &expected_types[G_N_ELEMENTS(expected_types)]) { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("D-Bus type \"%s\" is not implemented (in property %s.%s)", (const char *) mpr->dbus_type, mif->dbus_iface_name, mpr->dbus_property_name); + NM_PRAGMA_WARNING_REENABLE } } @@ -3151,6 +3244,7 @@ check_dbus_properties: break; } if (p_expected_type_2 >= &expected_types[G_N_ELEMENTS(expected_types)]) { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("D-Bus property \"%s.%s\" (type \"%s\") maps to property \"%s\", " "but that has an unexpected property type %s (expected %s)", mif->dbus_iface_name, @@ -3159,6 +3253,7 @@ check_dbus_properties: pspec->name, g_type_name(pspec->value_type), g_type_name(p_expected_type->default_gtype)); + NM_PRAGMA_WARNING_REENABLE } } diff --git a/src/libnm-client-public/NetworkManager.h b/src/libnm-client-public/NetworkManager.h index 3134562e..66f676a1 100644 --- a/src/libnm-client-public/NetworkManager.h +++ b/src/libnm-client-public/NetworkManager.h @@ -120,8 +120,8 @@ #if !defined(NETWORKMANAGER_COMPILATION) \ && (!defined(NM_NO_INCLUDE_EXTRA_HEADERS) || !NM_NO_INCLUDE_EXTRA_HEADERS) /* historically, NetworkManager.h drags in the following system headers. - * These are not strictly necessary and the user may wish to opt out from - * including them. */ + * These are not strictly necessary and the user may wish to opt out from + * including them. */ #include <linux/if_ether.h> #include <linux/if_infiniband.h> #include <linux/if_vlan.h> diff --git a/src/libnm-client-public/nm-client.h b/src/libnm-client-public/nm-client.h index ac615b57..2e3e77c4 100644 --- a/src/libnm-client-public/nm-client.h +++ b/src/libnm-client-public/nm-client.h @@ -27,9 +27,9 @@ G_BEGIN_DECLS * * Since: 1.24 */ -typedef enum { /*< flags >*/ - NM_CLIENT_INSTANCE_FLAGS_NONE = 0, - NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS = 1, +typedef enum /*< flags >*/ { + NM_CLIENT_INSTANCE_FLAGS_NONE = 0, + NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS = 1, } NMClientInstanceFlags; #define NM_TYPE_CLIENT (nm_client_get_type()) @@ -61,6 +61,8 @@ _NM_DEPRECATED_SYNC_WRITABLE_PROPERTY #define NM_CLIENT_WWAN_HARDWARE_ENABLED "wwan-hardware-enabled" #define NM_CLIENT_WIMAX_HARDWARE_ENABLED "wimax-hardware-enabled" +#define NM_CLIENT_RADIO_FLAGS "radio-flags" + #define NM_CLIENT_ACTIVE_CONNECTIONS "active-connections" #define NM_CLIENT_CONNECTIVITY "connectivity" #define NM_CLIENT_CONNECTIVITY_CHECK_URI "connectivity-check-uri" @@ -213,6 +215,9 @@ void nm_client_wimax_set_enabled(NMClient *client, gboolean enabled); NM_DEPRECATED_IN_1_22 gboolean nm_client_wimax_hardware_get_enabled(NMClient *client); +NM_AVAILABLE_IN_1_38 +NMRadioFlags nm_client_get_radio_flags(NMClient *client); + NM_AVAILABLE_IN_1_10 gboolean nm_client_connectivity_check_get_available(NMClient *client); @@ -491,6 +496,10 @@ gboolean nm_client_dbus_set_property_finish(NMClient *client, GAsyncResult *resu NM_AVAILABLE_IN_1_30 void nm_utils_print(int output_mode, const char *msg); +gboolean nm_utils_file_is_certificate(const char *filename); +gboolean nm_utils_file_is_private_key(const char *filename, gboolean *out_encrypted); +gboolean nm_utils_file_is_pkcs12(const char *filename); + G_END_DECLS #endif /* __NM_CLIENT_H__ */ diff --git a/src/libnm-client-test/meson.build b/src/libnm-client-test/meson.build index 8e2fba11..bcac4377 100644 --- a/src/libnm-client-test/meson.build +++ b/src/libnm-client-test/meson.build @@ -13,6 +13,7 @@ libnm_client_test = static_library( ], dependencies: [ libnm_core_public_dep, + libnm_client_public_dep, glib_dep, ], ) diff --git a/src/libnm-client-test/nm-test-utils-impl.c b/src/libnm-client-test/nm-test-utils-impl.c index ef98d9c2..ad642d25 100644 --- a/src/libnm-client-test/nm-test-utils-impl.c +++ b/src/libnm-client-test/nm-test-utils-impl.c @@ -159,7 +159,9 @@ nmtstc_service_init(void) * via pygobject. */ return NULL; } + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("test service %s exited with error code %d", NMTSTC_NM_SERVICE, data.exit_code); + NM_PRAGMA_WARNING_REENABLE } } diff --git a/src/libnm-core-impl/meson.build b/src/libnm-core-impl/meson.build index 2e1f1751..83c29085 100644 --- a/src/libnm-core-impl/meson.build +++ b/src/libnm-core-impl/meson.build @@ -2,35 +2,6 @@ libnm_core_impl_inc = include_directories('.') -if crypto_nss_dep.found() - libnm_crypto_nss = static_library( - 'nm-crypto-nss', - sources: 'nm-crypto-nss.c', - dependencies: [ - libnm_core_public_dep, - crypto_nss_dep, - ], - ) -endif - -if crypto_gnutls_dep.found() - libnm_crypto_gnutls = static_library( - 'nm-crypto-gnutls', - sources: 'nm-crypto-gnutls.c', - dependencies: [ - libnm_core_public_dep, - crypto_gnutls_dep, - ], - ) -endif - -if crypto == 'nss' - libnm_crypto = libnm_crypto_nss -else - assert(crypto == 'gnutls', 'Unexpected setting "crypto=' + crypto + '"') - libnm_crypto = libnm_crypto_gnutls -endif - libnm_core_settings_sources = files( 'nm-setting-6lowpan.c', 'nm-setting-8021x.c', @@ -89,7 +60,6 @@ libnm_core_settings_sources = files( libnm_core_impl_sources = files( 'nm-connection.c', - 'nm-crypto.c', 'nm-dbus-utils.c', 'nm-errors.c', 'nm-keyfile-utils.c', diff --git a/src/libnm-core-impl/nm-connection.c b/src/libnm-core-impl/nm-connection.c index a9b130a7..27cea2cb 100644 --- a/src/libnm-core-impl/nm-connection.c +++ b/src/libnm-core-impl/nm-connection.c @@ -1642,6 +1642,66 @@ _normalize_gsm_auto_config(NMConnection *self) } static gboolean +_normalize_802_1x_empty_strings(NMConnection *self) +{ + NMSetting8021x *s_8021x; + gboolean changed = FALSE; + + s_8021x = _connection_get_setting_by_meta_type(NM_CONNECTION_GET_PRIVATE(self), + NM_META_SETTING_TYPE_802_1X); + if (!s_8021x) + return FALSE; + +#define _norm_8021x(s_8021x, getter, prop_name, p_changed) \ + G_STMT_START \ + { \ + NMSetting8021x *_s_8021x = (s_8021x); \ + gboolean *_p_changed = (p_changed); \ + const char *_v; \ + \ + _v = getter(_s_8021x); \ + if (_v && _v[0] == '\0') { \ + g_object_set(_s_8021x, "" prop_name "", NULL, NULL); \ + *(_p_changed) = TRUE; \ + } \ + } \ + G_STMT_END + + _norm_8021x(s_8021x, nm_setting_802_1x_get_identity, NM_SETTING_802_1X_IDENTITY, &changed); + _norm_8021x(s_8021x, + nm_setting_802_1x_get_anonymous_identity, + NM_SETTING_802_1X_ANONYMOUS_IDENTITY, + &changed); + _norm_8021x(s_8021x, nm_setting_802_1x_get_pac_file, NM_SETTING_802_1X_PAC_FILE, &changed); + _norm_8021x(s_8021x, + nm_setting_802_1x_get_subject_match, + NM_SETTING_802_1X_SUBJECT_MATCH, + &changed); + _norm_8021x(s_8021x, + nm_setting_802_1x_get_phase2_subject_match, + NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, + &changed); + _norm_8021x(s_8021x, + nm_setting_802_1x_get_domain_suffix_match, + NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, + &changed); + _norm_8021x(s_8021x, + nm_setting_802_1x_get_phase2_domain_suffix_match, + NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, + &changed); + _norm_8021x(s_8021x, + nm_setting_802_1x_get_domain_match, + NM_SETTING_802_1X_DOMAIN_MATCH, + &changed); + _norm_8021x(s_8021x, + nm_setting_802_1x_get_phase2_domain_match, + NM_SETTING_802_1X_PHASE2_DOMAIN_MATCH, + &changed); + + return changed; +} + +static gboolean _normalize_required_settings(NMConnection *self) { NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth(self); @@ -1952,6 +2012,7 @@ _connection_normalize(NMConnection *connection, was_modified |= _normalize_bridge_vlan_order(connection); was_modified |= _normalize_bridge_port_vlan_order(connection); was_modified |= _normalize_gsm_auto_config(connection); + was_modified |= _normalize_802_1x_empty_strings(connection); was_modified = !!was_modified; diff --git a/src/libnm-core-impl/nm-errors.c b/src/libnm-core-impl/nm-errors.c index ea4ca5e9..fad854bc 100644 --- a/src/libnm-core-impl/nm-errors.c +++ b/src/libnm-core-impl/nm-errors.c @@ -12,12 +12,24 @@ NM_CACHED_QUARK_FCN("nm-agent-manager-error-quark", nm_agent_manager_error_quark); NM_CACHED_QUARK_FCN("nm-connection-error-quark", nm_connection_error_quark); -NM_CACHED_QUARK_FCN("nm-crypto-error-quark", nm_crypto_error_quark); NM_CACHED_QUARK_FCN("nm-device-error-quark", nm_device_error_quark); NM_CACHED_QUARK_FCN("nm-secret-agent-error-quark", nm_secret_agent_error_quark); NM_CACHED_QUARK_FCN("nm-settings-error-quark", nm_settings_error_quark); NM_CACHED_QUARK_FCN("nm-vpn-plugin-error-quark", nm_vpn_plugin_error_quark); +GQuark +nm_crypto_error_quark(void) +{ + G_STATIC_ASSERT(NM_CRYPTO_ERROR_FAILED == _NM_CRYPTO_ERROR_FAILED); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_INVALID_DATA == _NM_CRYPTO_ERROR_INVALID_DATA); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_INVALID_PASSWORD == _NM_CRYPTO_ERROR_INVALID_PASSWORD); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_UNKNOWN_CIPHER == _NM_CRYPTO_ERROR_UNKNOWN_CIPHER); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_DECRYPTION_FAILED == _NM_CRYPTO_ERROR_DECRYPTION_FAILED); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_ENCRYPTION_FAILED == _NM_CRYPTO_ERROR_ENCRYPTION_FAILED); + + return _nm_crypto_error_quark(); +} + static void register_error_domain(GQuark domain, const char *interface, GType enum_type) { diff --git a/src/libnm-core-impl/nm-keyfile.c b/src/libnm-core-impl/nm-keyfile.c index dce53ae2..00fb8a33 100644 --- a/src/libnm-core-impl/nm-keyfile.c +++ b/src/libnm-core-impl/nm-keyfile.c @@ -115,12 +115,14 @@ _key_file_handler_data_init_write(NMKeyfileHandlerData *handler_data, &info->error); } -_nm_printf(5, 6) static void _handle_warn(KeyfileReaderInfo *info, - const char *kf_key, - const char *cur_property, - NMKeyfileWarnSeverity severity, - const char *fmt, - ...) +/*****************************************************************************/ + +_nm_printf(5, 6) static void _read_handle_warn(KeyfileReaderInfo *info, + const char *kf_key, + const char *cur_property, + NMKeyfileWarnSeverity severity, + const char *fmt, + ...) { NMKeyfileHandlerData handler_data; @@ -148,16 +150,75 @@ _nm_printf(5, 6) static void _handle_warn(KeyfileReaderInfo *info, g_free(handler_data.warn.message); } -#define handle_warn(arg_info, arg_kf_key, arg_property_name, arg_severity, ...) \ - ({ \ - KeyfileReaderInfo *_info = (arg_info); \ - \ - nm_assert(!_info->error); \ - \ - if (_info->read_handler) { \ - _handle_warn(_info, (arg_kf_key), (arg_property_name), (arg_severity), __VA_ARGS__); \ - } \ - _info->error == NULL; \ +#define read_handle_warn(arg_info, arg_kf_key, arg_property_name, arg_severity, ...) \ + ({ \ + KeyfileReaderInfo *_info = (arg_info); \ + \ + nm_assert(!_info->error); \ + \ + if (_info->read_handler) { \ + _read_handle_warn(_info, \ + (arg_kf_key), \ + (arg_property_name), \ + (arg_severity), \ + __VA_ARGS__); \ + } \ + _info->error == NULL; \ + }) + +/*****************************************************************************/ + +_nm_printf(6, 7) static void _write_handle_warn(KeyfileWriterInfo *info, + NMSetting *setting, + const char *kf_key, + const char *cur_property, + NMKeyfileWarnSeverity severity, + const char *fmt, + ...) +{ + NMKeyfileHandlerData handler_data; + + _key_file_handler_data_init_write(&handler_data, + NM_KEYFILE_HANDLER_TYPE_WARN, + info, + nm_setting_get_name(setting), + cur_property, + setting, + kf_key); + handler_data.warn = (NMKeyfileHandlerDataWarn){ + .severity = severity, + .message = NULL, + .fmt = fmt, + }; + + va_start(handler_data.warn.ap, fmt); + + info->write_handler(info->connection, + info->keyfile, + NM_KEYFILE_HANDLER_TYPE_WARN, + &handler_data, + info->user_data); + + va_end(handler_data.warn.ap); + + g_free(handler_data.warn.message); +} + +#define write_handle_warn(arg_info, arg_setting, arg_kf_key, arg_property_name, arg_severity, ...) \ + ({ \ + KeyfileWriterInfo *_info = (arg_info); \ + \ + nm_assert(!_info->error); \ + \ + if (_info->write_handler) { \ + _write_handle_warn(_info, \ + (arg_setting), \ + (arg_kf_key), \ + (arg_property_name), \ + (arg_severity), \ + __VA_ARGS__); \ + } \ + _info->error == NULL; \ }) /*****************************************************************************/ @@ -264,11 +325,11 @@ get_one_int(KeyfileReaderInfo *info, if (!str || !str[0]) { if (info) { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring missing number")); + read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring missing number")); } return FALSE; } @@ -276,12 +337,12 @@ get_one_int(KeyfileReaderInfo *info, tmp = _nm_utils_ascii_str_to_int64(str, 10, 0, max_val, -1); if (tmp == -1) { if (info) { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid number '%s'"), - str); + read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid number '%s'"), + str); } return FALSE; } @@ -305,13 +366,13 @@ build_address(KeyfileReaderInfo *info, addr = nm_ip_address_new(family, address_str, plen, &error); if (!addr) { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid %s address: %s"), - family == AF_INET ? "IPv4" : "IPv6", - error->message); + read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid %s address: %s"), + family == AF_INET ? "IPv4" : "IPv6", + error->message); g_error_free(error); } @@ -351,13 +412,13 @@ build_route(KeyfileReaderInfo *info, metric = u32; gateway_str = NULL; } else { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid gateway '%s' for %s route"), - gateway_str, - family == AF_INET ? "IPv4" : "IPv6"); + read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid gateway '%s' for %s route"), + gateway_str, + family == AF_INET ? "IPv4" : "IPv6"); return NULL; } } @@ -373,13 +434,13 @@ build_route(KeyfileReaderInfo *info, route = nm_ip_route_new(family, dest_str, plen, gateway_str, metric, &error); if (!route) { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid %s route: %s"), - family == AF_INET ? "IPv4" : "IPv6", - error->message); + read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid %s route: %s"), + family == AF_INET ? "IPv4" : "IPv6", + error->message); g_error_free(error); } @@ -551,15 +612,15 @@ read_one_ip_address_or_route(KeyfileReaderInfo *info, /* get address field */ address_str = read_field(¤t, &err_str, IP_ADDRESS_CHARS, DELIMITERS); if (err_str) { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("unexpected character '%c' for address %s: '%s' (position %td)"), - *err_str, - kf_key, - VALUE_ORIG(), - err_str - current); + read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("unexpected character '%c' for address %s: '%s' (position %td)"), + *err_str, + kf_key, + VALUE_ORIG(), + err_str - current); return NULL; } /* get prefix length field (skippable) */ @@ -567,30 +628,31 @@ read_one_ip_address_or_route(KeyfileReaderInfo *info, /* get gateway field */ gateway_str = read_field(¤t, &err_str, IP_ADDRESS_CHARS, DELIMITERS); if (err_str) { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("unexpected character '%c' for %s: '%s' (position %td)"), - *err_str, - kf_key, - VALUE_ORIG(), - err_str - current); + read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("unexpected character '%c' for %s: '%s' (position %td)"), + *err_str, + kf_key, + VALUE_ORIG(), + err_str - current); return NULL; } /* for routes, get metric */ if (route) { metric_str = read_field(¤t, &err_str, DIGITS, DELIMITERS); if (err_str) { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("unexpected character '%c' in prefix length for %s: '%s' (position %td)"), - *err_str, - kf_key, - VALUE_ORIG(), - err_str - current); + read_handle_warn( + info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("unexpected character '%c' in prefix length for %s: '%s' (position %td)"), + *err_str, + kf_key, + VALUE_ORIG(), + err_str - current); return NULL; } } else @@ -599,23 +661,23 @@ read_one_ip_address_or_route(KeyfileReaderInfo *info, /* there is still some data */ if (*current) { /* another field follows */ - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("garbage at the end of value %s: '%s'"), - kf_key, - VALUE_ORIG()); - return NULL; - } else { - /* semicolon at the end of input */ - if (!handle_warn(info, + read_handle_warn(info, kf_key, property_name, - NM_KEYFILE_WARN_SEVERITY_INFO, - _("deprecated semicolon at the end of value %s: '%s'"), + NM_KEYFILE_WARN_SEVERITY_WARN, + _("garbage at the end of value %s: '%s'"), kf_key, - VALUE_ORIG())) + VALUE_ORIG()); + return NULL; + } else { + /* semicolon at the end of input */ + if (!read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_INFO, + _("deprecated semicolon at the end of value %s: '%s'"), + kf_key, + VALUE_ORIG())) return NULL; } } @@ -628,26 +690,26 @@ read_one_ip_address_or_route(KeyfileReaderInfo *info, if (!get_one_int(info, kf_key, property_name, plen_str, ipv6 ? 128 : 32, &plen)) { plen = DEFAULT_PREFIX(route, ipv6); if (info->error - || !handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid prefix length for %s '%s', defaulting to %d"), - kf_key, - VALUE_ORIG(), - plen)) + || !read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid prefix length for %s '%s', defaulting to %d"), + kf_key, + VALUE_ORIG(), + plen)) return NULL; } } else { plen = DEFAULT_PREFIX(route, ipv6); - if (!handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("missing prefix length for %s '%s', defaulting to %d"), - kf_key, - VALUE_ORIG(), - plen)) + if (!read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("missing prefix length for %s '%s', defaulting to %d"), + kf_key, + VALUE_ORIG(), + plen)) return NULL; } @@ -983,13 +1045,13 @@ ip_routing_rule_parser_full(KeyfileReaderInfo *info, NULL, &local); if (!rule) { - if (!handle_warn(info, - build_list[i_build_list].s_key, - property_info->name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid value for \"%s\": %s"), - build_list[i_build_list].s_key, - local->message)) + if (!read_handle_warn(info, + build_list[i_build_list].s_key, + property_info->name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid value for \"%s\": %s"), + build_list[i_build_list].s_key, + local->message)) return; continue; } @@ -1061,13 +1123,13 @@ ip_dns_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) NMIPAddr addr; if (inet_pton(addr_family, list[i], &addr) <= 0) { - if (!handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid DNS server IPv%c address '%s'"), - nm_utils_addr_family_to_char(addr_family), - list[i])) { + if (!read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid DNS server IPv%c address '%s'"), + nm_utils_addr_family_to_char(addr_family), + list[i])) { do { nm_clear_g_free(&list[i]); } while (++i < length); @@ -1098,13 +1160,13 @@ ip6_addr_gen_mode_parser(KeyfileReaderInfo *info, NMSetting *setting, const char s, (int *) &addr_gen_mode, NULL)) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid option '%s', use one of [%s]"), - s, - "eui64,stable-privacy"); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid option '%s', use one of [%s]"), + s, + "eui64,stable-privacy"); return; } } else @@ -1160,7 +1222,11 @@ mac_address_parser(KeyfileReaderInfo *info, goto good_addr_bin; } - handle_warn(info, key, key, NM_KEYFILE_WARN_SEVERITY_WARN, _("ignoring invalid MAC address")); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid MAC address")); return; good_addr_bin: @@ -1232,14 +1298,14 @@ read_hash_of_string(KeyfileReaderInfo *info, gs_free_error GError *error = NULL; if (!_nm_setting_bond_validate_option(name, value, &error)) { - if (!handle_warn(info, - kf_key, - name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid bond option %s%s%s = %s%s%s: %s"), - NM_PRINT_FMT_QUOTE_STRING(name), - NM_PRINT_FMT_QUOTE_STRING(value), - error->message)) + if (!read_handle_warn(info, + kf_key, + name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid bond option %s%s%s = %s%s%s: %s"), + NM_PRINT_FMT_QUOTE_STRING(name), + NM_PRINT_FMT_QUOTE_STRING(value), + error->message)) return; } else nm_setting_bond_add_option(NM_SETTING_BOND(setting), name, value); @@ -1426,7 +1492,7 @@ ssid_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) bytes = get_bytes(info, setting_name, key, FALSE, TRUE); if (!bytes) { - handle_warn(info, key, key, NM_KEYFILE_WARN_SEVERITY_WARN, _("ignoring invalid SSID")); + read_handle_warn(info, key, key, NM_KEYFILE_WARN_SEVERITY_WARN, _("ignoring invalid SSID")); return; } g_object_set(setting, key, bytes, NULL); @@ -1440,11 +1506,11 @@ password_raw_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key bytes = get_bytes(info, setting_name, key, FALSE, TRUE); if (!bytes) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid raw password")); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid raw password")); return; } g_object_set(setting, key, bytes, NULL); @@ -1585,7 +1651,11 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) bin = g_bytes_get_data(bytes, &bin_len); if (bin_len == 0) { if (!info->error) { - handle_warn(info, key, key, NM_KEYFILE_WARN_SEVERITY_WARN, _("invalid key/cert value")); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid key/cert value")); } return; } @@ -1596,12 +1666,12 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) if (nm_setting_802_1x_check_cert_scheme(bin, bin_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PATH) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key/cert value path \"%s\""), - bin); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid key/cert value path \"%s\""), + bin); return; } @@ -1621,12 +1691,12 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) * then by invoking a callback (and possibly keyfile settings plugin would * collect the file names to be checked and check them later). */ if (!g_file_test(path2, G_FILE_TEST_EXISTS)) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE, - _("certificate or key file '%s' does not exist"), - path2); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE, + _("certificate or key file '%s' does not exist"), + path2); } return; } @@ -1634,12 +1704,12 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) if (HAS_SCHEME_PREFIX(bin, bin_len, NM_KEYFILE_CERT_SCHEME_PREFIX_PKCS11)) { if (nm_setting_802_1x_check_cert_scheme(bin, bin_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid PKCS#11 URI \"%s\""), - bin); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid PKCS#11 URI \"%s\""), + bin); return; } @@ -1680,11 +1750,11 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) bin_decoded = g_base64_decode(cdata, &bin_decoded_len); if (bin_decoded_len == 0) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key/cert value data:;base64, is not base64")); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid key/cert value data:;base64, is not base64")); return; } @@ -1693,11 +1763,11 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) /* The blob probably starts with "file://". Setting the cert data will confuse NMSetting8021x. * In fact this is a limitation of NMSetting8021x which does not support setting blobs that start * with file://. Just warn and return TRUE to signal that we ~handled~ the setting. */ - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key/cert value data:;base64,file://")); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid key/cert value data:;base64,file://")); return; } @@ -1718,12 +1788,12 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) /* Warn if the certificate didn't exist */ if (!path_exists) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE, - _("certificate or key file '%s' does not exist"), - path); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE, + _("certificate or key file '%s' does not exist"), + path); } return; } @@ -1734,11 +1804,11 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) * Setting the cert data will confuse NMSetting8021x. * In fact, NMSetting8021x does not support setting such binary data, so just warn and * continue. */ - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key/cert value is not a valid blob")); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid key/cert value is not a valid blob")); return; } @@ -1836,12 +1906,12 @@ parity_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) goto parity_good; } - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid parity value '%s'"), - tmp_str ?: ""); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid parity value '%s'"), + tmp_str ?: ""); return; parity_good: @@ -1858,12 +1928,12 @@ out_err: /* ignore such errors. The key is not present. */ return; } - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid setting: %s"), - err->message); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid setting: %s"), + err->message); } static void @@ -1878,12 +1948,12 @@ team_config_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) g_object_set(G_OBJECT(setting), key, conf, NULL); if (conf && !nm_setting_verify(setting, NULL, &error)) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid team configuration: %s"), - error->message); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid team configuration: %s"), + error->message); g_object_set(G_OBJECT(setting), key, NULL, NULL); } } @@ -1909,12 +1979,12 @@ bridge_vlan_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) for (iter = strv; *iter; iter++) { vlan = nm_bridge_vlan_from_str(*iter, &local); if (!vlan) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - "invalid bridge VLAN: %s", - local->message); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + "invalid bridge VLAN: %s", + local->message); g_clear_error(&local); continue; } @@ -1961,12 +2031,12 @@ qdisc_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) qdisc = nm_utils_tc_qdisc_from_str(qdisc_str, &err); if (!qdisc) { - handle_warn(info, - keys[i], - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid qdisc: %s"), - err->message); + read_handle_warn(info, + keys[i], + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid qdisc: %s"), + err->message); } else { g_ptr_array_add(qdiscs, qdisc); } @@ -2011,12 +2081,12 @@ tfilter_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) tfilter = nm_utils_tc_tfilter_from_str(tfilter_str, &err); if (!tfilter) { - handle_warn(info, - keys[i], - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid tfilter: %s"), - err->message); + read_handle_warn(info, + keys[i], + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid tfilter: %s"), + err->message); } else { g_ptr_array_add(tfilters, tfilter); } @@ -2689,16 +2759,10 @@ cert_writer_default(NMConnection *connection, static void cert_writer(KeyfileWriterInfo *info, NMSetting *setting, const char *key, const GValue *value) { - const NMSetting8021xSchemeVtable *vtable = NULL; + const NMSetting8021xSchemeVtable *vtable; const char *setting_name; - guint i; - for (i = 0; nm_setting_8021x_scheme_vtable[i].setting_key; i++) { - if (nm_streq0(nm_setting_8021x_scheme_vtable[i].setting_key, key)) { - vtable = &nm_setting_8021x_scheme_vtable[i]; - break; - } - } + vtable = nm_setting_8021x_scheme_vtable_by_setting_key(key); if (!vtable) g_return_if_reached(); @@ -3181,12 +3245,12 @@ read_one_setting_value(KeyfileReaderInfo *info, && !nm_keyfile_plugin_kf_has_key(keyfile, setting_info->setting_name, key, &err)) { /* Key doesn't exist or an error occurred, thus nothing to do. */ if (err) { - if (!handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("error loading setting value: %s"), - err->message)) + if (!read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("error loading setting value: %s"), + err->message)) return; } return; @@ -3296,7 +3360,7 @@ read_one_setting_value(KeyfileReaderInfo *info, if (val > 255u) { if (!already_warned - && !handle_warn( + && !read_handle_warn( info, key, key, @@ -3356,12 +3420,12 @@ read_one_setting_value(KeyfileReaderInfo *info, if (nm_keyfile_error_is_not_found(err)) { /* ignore such errors. The key is not present. */ } else { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid setting: %s"), - err->message); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid setting: %s"), + err->message); } } } @@ -3381,12 +3445,12 @@ _read_setting(KeyfileReaderInfo *info) type = nm_setting_lookup_type(alias); if (!type) { - handle_warn(info, - NULL, - NULL, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid setting name '%s'"), - info->group); + read_handle_warn(info, + NULL, + NULL, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid setting name '%s'"), + info->group); return; } @@ -3428,13 +3492,13 @@ _read_setting(KeyfileReaderInfo *info) variant_type = sett_info->detail.gendata_info->get_variant_type(sett_info, key, &local); if (!variant_type) { - if (!handle_warn(info, - key, - NULL, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key '%s.%s'"), - info->group, - key)) + if (!read_handle_warn(info, + key, + NULL, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid key '%s.%s'"), + info->group, + key)) break; continue; } @@ -3444,13 +3508,13 @@ _read_setting(KeyfileReaderInfo *info) v = g_key_file_get_boolean(info->keyfile, info->group, key, &local); if (local) { - if (!handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not boolean"), - info->group, - key)) + if (!read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' is not boolean"), + info->group, + key)) break; continue; } @@ -3461,13 +3525,13 @@ _read_setting(KeyfileReaderInfo *info) v = g_key_file_get_uint64(info->keyfile, info->group, key, &local); if (local) { - if (!handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not a uint32"), - info->group, - key)) + if (!read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' is not a uint32"), + info->group, + key)) break; continue; } @@ -3518,12 +3582,12 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) || !nm_streq0(str, cstr)) { /* the group name must be identical to the normalized(!) key, so that it * is uniquely identified. */ - handle_warn(info, - NULL, - NM_SETTING_WIREGUARD_PEERS, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid peer public key in section '%s'"), - info->group); + read_handle_warn(info, + NULL, + NM_SETTING_WIREGUARD_PEERS, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid peer public key in section '%s'"), + info->group); return; } nm_wireguard_peer_set_public_key(peer, cstr, TRUE); @@ -3533,13 +3597,13 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) str = nm_keyfile_plugin_kf_get_string(info->keyfile, info->group, key, NULL); if (str) { if (!nm_wireguard_peer_set_preshared_key(peer, str, FALSE)) { - if (!handle_warn(info, - key, - NM_SETTING_WIREGUARD_PEERS, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not a valid 256 bit key in base64 encoding"), - info->group, - key)) + if (!read_handle_warn(info, + key, + NM_SETTING_WIREGUARD_PEERS, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' is not a valid 256 bit key in base64 encoding"), + info->group, + key)) return; } nm_clear_g_free(&str); @@ -3556,13 +3620,13 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) NULL); if (errno != ENODATA) { if (i64 == -1 || !_nm_setting_secret_flags_valid(i64)) { - if (!handle_warn(info, - key, - NM_SETTING_WIREGUARD_PEERS, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not a valid secret flag"), - info->group, - key)) + if (!read_handle_warn(info, + key, + NM_SETTING_WIREGUARD_PEERS, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' is not a valid secret flag"), + info->group, + key)) return; } else nm_wireguard_peer_set_preshared_key_flags(peer, i64); @@ -3579,13 +3643,13 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) NULL); if (errno != ENODATA) { if (i64 == -1) { - if (!handle_warn(info, - key, - NM_SETTING_WIREGUARD_PEERS, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not a integer in range 0 to 2^32"), - info->group, - key)) + if (!read_handle_warn(info, + key, + NM_SETTING_WIREGUARD_PEERS, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' is not a integer in range 0 to 2^32"), + info->group, + key)) return; } else nm_wireguard_peer_set_persistent_keepalive(peer, i64); @@ -3595,13 +3659,13 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) str = nm_keyfile_plugin_kf_get_string(info->keyfile, info->group, key, NULL); if (str && str[0]) { if (!nm_wireguard_peer_set_endpoint(peer, str, FALSE)) { - if (!handle_warn(info, - key, - NM_SETTING_WIREGUARD_PEERS, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not a valid endpoint"), - info->group, - key)) + if (!read_handle_warn(info, + key, + NM_SETTING_WIREGUARD_PEERS, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' is not a valid endpoint"), + info->group, + key)) return; } } @@ -3621,13 +3685,13 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) nm_wireguard_peer_append_allowed_ip(peer, sa[i], TRUE); } if (has_error) { - if (!handle_warn(info, - key, - NM_SETTING_WIREGUARD_PEERS, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' has invalid allowed-ips"), - info->group, - key)) + if (!read_handle_warn(info, + key, + NM_SETTING_WIREGUARD_PEERS, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' has invalid allowed-ips"), + info->group, + key)) return; } } @@ -3636,13 +3700,13 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) return; if (!nm_wireguard_peer_is_valid(peer, TRUE, TRUE, &error)) { - handle_warn(info, - NULL, - NM_SETTING_WIREGUARD_PEERS, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("peer '%s' is invalid: %s"), - info->group, - error->message); + read_handle_warn(info, + NULL, + NM_SETTING_WIREGUARD_PEERS, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("peer '%s' is invalid: %s"), + info->group, + error->message); return; } @@ -3855,29 +3919,24 @@ write_setting_value(KeyfileWriterInfo *info, _parse_info_find(setting, key, &setting_info, NULL, &pip); + if (pip && pip->has_writer_full) { + pip->writer_full(info, setting_info, property_info, pip, setting); + return; + } + + if (pip && pip->writer_skip) + return; + if (!pip) { if (!setting_info) { - /* the setting type is unknown. That is highly unexpected - * (and as this is currently only called from NetworkManager - * daemon, not possible). - * - * Still, handle it gracefully, because later keyfile writer will become - * public API of libnm, where @setting is (untrusted) user input. - * - * Gracefully here just means: ignore the setting. */ + /* the setting type is unknown. Handle this gracefully by + * ignoring the setting. */ return; } if (!property_info->param_spec) return; if (nm_streq(key, NM_SETTING_NAME)) return; - } else { - if (pip->has_writer_full) { - pip->writer_full(info, setting_info, property_info, pip, setting); - return; - } - if (pip->writer_skip) - return; } nm_assert(property_info->param_spec); @@ -4089,8 +4148,10 @@ _write_setting_wireguard(NMSetting *setting, KeyfileWriterInfo *info) * @user_data: argument for @handler. * @error: the #GError in case writing fails. * - * @connection must verify as a valid profile according to - * nm_connection_verify(). + * @connection should verify as a valid profile according to + * nm_connection_verify(). If it does not verify, the keyfile may + * be incomplete and the parser may not be able to fully recreate + * the original profile. * * Returns: (transfer full): a new #GKeyFile or %NULL on error. * @@ -4104,7 +4165,6 @@ nm_keyfile_write(NMConnection *connection, GError **error) { nm_auto_unref_keyfile GKeyFile *keyfile = NULL; - GError *local = NULL; KeyfileWriterInfo info; NMSetting **settings; int i; @@ -4114,28 +4174,6 @@ nm_keyfile_write(NMConnection *connection, g_return_val_if_fail(!error || !*error, NULL); g_return_val_if_fail(handler_flags == NM_KEYFILE_HANDLER_FLAGS_NONE, NULL); - /* Technically, we might not require that a profile is valid in - * order to serialize it. Like also nm_keyfile_read() does not - * ensure that the read profile validates. - * - * However, if the profile does not validate, then there might be - * unexpected edge cases when we try to serialize it. Edge cases - * that might result in dangerous crash. - * - * So, for now we require valid profiles. */ - if (!nm_connection_verify(connection, error ? &local : NULL)) { - if (error) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - _("the profile is not valid: %s"), - local->message); - g_error_free(local); - } else - nm_assert(!local); - return NULL; - } - keyfile = g_key_file_new(); info = (KeyfileWriterInfo){ @@ -4189,11 +4227,16 @@ nm_keyfile_write(NMConnection *connection, key, (guint64) g_variant_get_uint32(v)); } else { - /* BUG: The variant type is not implemented. Since the connection - * verifies, this can only mean we either wrongly didn't reject - * the connection as invalid, or we didn't properly implement the - * variant type. */ - nm_assert_not_reached(); + if (!write_handle_warn(&info, + setting, + NULL, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("unsupported option \"%s.%s\" of variant type %s"), + setting_name, + key, + g_variant_get_type_string(v))) + goto out_with_info_error; continue; } } diff --git a/src/libnm-core-impl/nm-meta-setting-base-impl.c b/src/libnm-core-impl/nm-meta-setting-base-impl.c index cd55779f..69daa76c 100644 --- a/src/libnm-core-impl/nm-meta-setting-base-impl.c +++ b/src/libnm-core-impl/nm-meta-setting-base-impl.c @@ -153,6 +153,68 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = { #undef _D }; +const NMSetting8021xSchemeVtable * +nm_setting_8021x_scheme_vtable_by_setting_key(const char *key) +{ + static const NMSetting8021xSchemeType sorted_index[] = { + NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY, + NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY, + }; + int imin, imax; + + nm_assert(key); + + if (NM_MORE_ASSERT_ONCE(5)) { + const NMSetting8021xSchemeVtable *vtable_prev = NULL; + int i, j; + + for (i = 0; i < (int) G_N_ELEMENTS(sorted_index); i++) { + const NMSetting8021xSchemeType t = sorted_index[i]; + const NMSetting8021xSchemeVtable *vtable; + + nm_assert(_NM_INT_NOT_NEGATIVE(t)); + nm_assert(t < G_N_ELEMENTS(nm_setting_8021x_scheme_vtable) - 1); + + for (j = 0; j < i; j++) + nm_assert(t != sorted_index[j]); + + vtable = &nm_setting_8021x_scheme_vtable[t]; + + nm_assert(vtable->scheme_type == t); + nm_assert(vtable->setting_key); + + if (vtable_prev) + nm_assert(strcmp(vtable_prev->setting_key, vtable->setting_key) < 0); + vtable_prev = vtable; + } + } + + imin = 0; + imax = G_N_ELEMENTS(sorted_index) - 1; + while (imin <= imax) { + const NMSetting8021xSchemeVtable *vtable; + const int imid = imin + (imax - imin) / 2; + int cmp; + + vtable = &nm_setting_8021x_scheme_vtable[sorted_index[imid]]; + + cmp = strcmp(vtable->setting_key, key); + if (cmp == 0) + return vtable; + + if (cmp < 0) + imin = imid + 1; + else + imax = imid - 1; + } + + return NULL; +} + /*****************************************************************************/ const NMMetaSettingInfo nm_meta_setting_infos[] = { diff --git a/src/libnm-core-impl/nm-setting-8021x.c b/src/libnm-core-impl/nm-setting-8021x.c index 884f8830..41feae57 100644 --- a/src/libnm-core-impl/nm-setting-8021x.c +++ b/src/libnm-core-impl/nm-setting-8021x.c @@ -9,8 +9,8 @@ #include "nm-setting-8021x.h" #include "libnm-glib-aux/nm-secret-utils.h" +#include "libnm-crypto/nm-crypto.h" #include "nm-utils.h" -#include "nm-crypto.h" #include "nm-utils-private.h" #include "nm-setting-private.h" #include "nm-core-enum-types.h" @@ -516,7 +516,7 @@ _cert_impl_set(NMSetting8021x *setting, gs_unref_bytes GBytes *file = NULL; if (NM_IN_SET(property, PROP_PRIVATE_KEY, PROP_PHASE2_PRIVATE_KEY)) { - file = nm_crypto_read_file(value, error); + file = nm_utils_read_crypto_file_to_bytes(value, error); if (!file) goto err; format = nm_crypto_verify_private_key_data(g_bytes_get_data(file, NULL), @@ -2520,230 +2520,172 @@ need_secrets_sim(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2) g_ptr_array_add(secrets, NM_SETTING_802_1X_PIN); } -static gboolean -need_private_key_password(GBytes *blob, - NMSetting8021xCKScheme scheme, - const char *path, - const char *password, - NMSettingSecretFlags flags) -{ - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - - if (flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) - return FALSE; - - /* Private key password is required */ - if (password) { - if (path) - format = nm_crypto_verify_private_key(path, password, NULL, NULL); - else if (blob) - format = nm_crypto_verify_private_key_data(g_bytes_get_data(blob, NULL), - g_bytes_get_size(blob), - password, - NULL, - NULL); - else - return FALSE; - } - - return (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN); -} - static void need_secrets_tls(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2) { NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); NMSetting8021xCKScheme scheme; - GBytes *blob = NULL; - const char *path = NULL; - - if (phase2) { - scheme = nm_setting_802_1x_get_phase2_private_key_scheme(self); - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) - path = nm_setting_802_1x_get_phase2_private_key_path(self); - else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) - blob = nm_setting_802_1x_get_phase2_private_key_blob(self); - else if (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11) - g_warning("%s: unknown phase2 private key scheme %d", __func__, scheme); - - if (need_private_key_password(blob, - scheme, - path, - priv->phase2_private_key_password, - priv->phase2_private_key_password_flags)) - g_ptr_array_add(secrets, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD); - - scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme(self); - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11 - && !(priv->phase2_ca_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) - && !priv->phase2_ca_cert_password) - g_ptr_array_add(secrets, NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD); - scheme = nm_setting_802_1x_get_phase2_client_cert_scheme(self); - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11 - && !(priv->phase2_client_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) - && !priv->phase2_client_cert_password) - g_ptr_array_add(secrets, NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD); - } else { - scheme = nm_setting_802_1x_get_private_key_scheme(self); - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) - path = nm_setting_802_1x_get_private_key_path(self); - else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) - blob = nm_setting_802_1x_get_private_key_blob(self); - else if (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11) - g_warning("%s: unknown private key scheme %d", __func__, scheme); - - if (need_private_key_password(blob, - scheme, - path, - priv->private_key_password, - priv->private_key_password_flags)) - g_ptr_array_add(secrets, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); - - scheme = nm_setting_802_1x_get_ca_cert_scheme(self); + if (!NM_FLAGS_HAS(phase2 ? priv->phase2_private_key_password_flags + : priv->private_key_password_flags, + NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + gboolean has_password = FALSE; + const char *password; + + password = phase2 ? priv->phase2_private_key_password : priv->private_key_password; + + /* Check whether the password works. */ + if (password) { + scheme = phase2 ? nm_setting_802_1x_get_phase2_private_key_scheme(self) + : nm_setting_802_1x_get_private_key_scheme(self); + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + const char *path = phase2 ? nm_setting_802_1x_get_phase2_private_key_path(self) + : nm_setting_802_1x_get_private_key_path(self); + + if (path) + format = nm_crypto_verify_private_key(path, password, NULL, NULL); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + GBytes *blob = phase2 ? nm_setting_802_1x_get_phase2_private_key_blob(self) + : nm_setting_802_1x_get_private_key_blob(self); + + if (blob) + format = nm_crypto_verify_private_key_data(g_bytes_get_data(blob, NULL), + g_bytes_get_size(blob), + password, + NULL, + NULL); + } else { + /* For PKCS#11 URLS, we assume the password is correct. */ + has_password = TRUE; + } + } + if (!has_password && format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) { + g_ptr_array_add(secrets, + phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD + : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); + } + } + + if (!NM_FLAGS_HAS(phase2 ? priv->phase2_ca_cert_password_flags : priv->ca_cert_password_flags, + NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { + scheme = phase2 ? nm_setting_802_1x_get_phase2_ca_cert_scheme(self) + : nm_setting_802_1x_get_ca_cert_scheme(self); if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11 - && !(priv->ca_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) - && !priv->ca_cert_password) - g_ptr_array_add(secrets, NM_SETTING_802_1X_CA_CERT_PASSWORD); + && !(phase2 ? priv->phase2_ca_cert_password : priv->ca_cert_password)) { + g_ptr_array_add(secrets, + phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD + : NM_SETTING_802_1X_CA_CERT_PASSWORD); + } + } - scheme = nm_setting_802_1x_get_client_cert_scheme(self); + if (!NM_FLAGS_HAS(phase2 ? priv->phase2_client_cert_password_flags + : priv->client_cert_password_flags, + NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { + scheme = phase2 ? nm_setting_802_1x_get_phase2_client_cert_scheme(self) + : nm_setting_802_1x_get_client_cert_scheme(self); if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11 - && !(priv->client_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) - && !priv->client_cert_password) - g_ptr_array_add(secrets, NM_SETTING_802_1X_CLIENT_CERT_PASSWORD); + && !(phase2 ? priv->phase2_client_cert_password : priv->client_cert_password)) { + g_ptr_array_add(secrets, + phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD + : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD); + } } } static gboolean -verify_tls(NMSetting8021x *self, gboolean phase2, GError **error) +verify_identity(NMSetting8021x *self, gboolean phase2, GError **error) { NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); - if (phase2) { - if (!priv->phase2_client_cert) { + if (nm_str_is_empty(priv->identity)) { + if (!priv->identity) { g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY, _("property is missing")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_CLIENT_CERT); - return FALSE; - } else if (!g_bytes_get_size(priv->phase2_client_cert)) { + } else { g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, _("property is empty")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_CLIENT_CERT); - return FALSE; } + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_IDENTITY); + return FALSE; + } - /* Private key is required for TLS */ - if (!priv->phase2_private_key) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("property is missing")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); - return FALSE; - } else if (!g_bytes_get_size(priv->phase2_private_key)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("property is empty")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); - return FALSE; - } + return TRUE; +} - /* If the private key is PKCS#12, check that it matches the client cert */ - if (nm_crypto_is_pkcs12_data(g_bytes_get_data(priv->phase2_private_key, NULL), - g_bytes_get_size(priv->phase2_private_key), - NULL)) { - if (!g_bytes_equal(priv->phase2_private_key, priv->phase2_client_cert)) { - g_set_error(error, +static gboolean +verify_tls(NMSetting8021x *self, gboolean phase2, GError **error) +{ + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); + GBytes *client_cert; + GBytes *private_key; + const char *prop_client_cert; + const char *prop_private_key; + + client_cert = phase2 ? priv->phase2_client_cert : priv->client_cert; + private_key = phase2 ? priv->phase2_private_key : priv->private_key; + prop_client_cert = + phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT : NM_SETTING_802_1X_CLIENT_CERT; + prop_private_key = + phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY : NM_SETTING_802_1X_PRIVATE_KEY; + + if (!client_cert) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_client_cert); + return FALSE; + } + if (g_bytes_get_size(client_cert) == 0) { + g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("has to match '%s' property for PKCS#12"), - NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_CLIENT_CERT); - return FALSE; - } - } - } else { - if (!priv->client_cert) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("property is missing")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_CLIENT_CERT); - return FALSE; - } else if (!g_bytes_get_size(priv->client_cert)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("property is empty")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_CLIENT_CERT); - return FALSE; - } + _("property is empty")); + g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_client_cert); + return FALSE; + } - /* Private key is required for TLS */ - if (!priv->private_key) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("property is missing")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PRIVATE_KEY); - return FALSE; - } else if (!g_bytes_get_size(priv->private_key)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("property is empty")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PRIVATE_KEY); - return FALSE; - } + /* Private key is required for TLS */ + if (!private_key) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_private_key); + return FALSE; + } - /* If the private key is PKCS#12, check that it matches the client cert */ - if (nm_crypto_is_pkcs12_data(g_bytes_get_data(priv->private_key, NULL), - g_bytes_get_size(priv->private_key), - NULL)) { - if (!g_bytes_equal(priv->private_key, priv->client_cert)) { - g_set_error(error, + if (g_bytes_get_size(private_key) == 0) { + g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("has to match '%s' property for PKCS#12"), - NM_SETTING_802_1X_PRIVATE_KEY); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_CLIENT_CERT); - return FALSE; - } + _("property is empty")); + g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_private_key); + return FALSE; + } + + if (_nm_setting_802_1x_cert_get_scheme(private_key, NULL) == NM_SETTING_802_1X_CK_SCHEME_BLOB + && nm_crypto_is_pkcs12_data(g_bytes_get_data(private_key, NULL), + g_bytes_get_size(private_key), + NULL)) { + /* If the private key is PKCS#12, check that it matches the client cert */ + if (!g_bytes_equal(private_key, client_cert)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("has to match '%s' property for PKCS#12"), + prop_private_key); + g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_client_cert); + return FALSE; } } @@ -2755,24 +2697,8 @@ verify_ttls(NMSetting8021x *self, gboolean phase2, GError **error) { NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); - if (nm_str_is_empty(priv->identity)) { - if (!priv->identity) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("property is missing")); - } else { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("property is empty")); - } - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_IDENTITY); + if (!verify_identity(self, phase2, error)) return FALSE; - } if ((!priv->phase2_auth && !priv->phase2_autheap) || (priv->phase2_auth && priv->phase2_autheap)) { @@ -2792,33 +2718,6 @@ verify_ttls(NMSetting8021x *self, gboolean phase2, GError **error) return TRUE; } -static gboolean -verify_identity(NMSetting8021x *self, gboolean phase2, GError **error) -{ - NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); - - if (nm_str_is_empty(priv->identity)) { - if (!priv->identity) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("property is missing")); - } else { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("property is empty")); - } - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_IDENTITY); - return FALSE; - } - - return TRUE; -} - static void need_secrets_phase2(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2) { @@ -2833,17 +2732,14 @@ need_secrets_phase2(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2) if (!method) method = priv->phase2_autheap; - if (!method) { - g_warning("Couldn't find EAP method."); - g_assert_not_reached(); - return; - } + if (!method) + g_return_if_reached(); /* Ask the configured phase2 method if it needs secrets */ for (i = 0; eap_methods_table[i].method; i++) { - if (eap_methods_table[i].ns_func == NULL) + if (!eap_methods_table[i].ns_func) continue; - if (!strcmp(eap_methods_table[i].method, method)) { + if (nm_streq(eap_methods_table[i].method, method)) { (*eap_methods_table[i].ns_func)(self, secrets, TRUE); break; } @@ -2929,11 +2825,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } if (!NM_IN_STRSET(priv->phase1_peapver, NULL, "0", "1")) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("'%s' is not a valid value for the property"), - priv->phase1_peapver); + if (priv->phase1_peapver) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase1_peapver); + } else { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + } g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, @@ -2942,11 +2845,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } if (!NM_IN_STRSET(priv->phase1_peaplabel, NULL, "0", "1")) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("'%s' is not a valid value for the property"), - priv->phase1_peaplabel); + if (priv->phase1_peaplabel) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase1_peaplabel); + } else { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + } g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, @@ -2955,11 +2865,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } if (!NM_IN_STRSET(priv->phase1_fast_provisioning, NULL, "0", "1", "2", "3")) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("'%s' is not a valid value for the property"), - priv->phase1_fast_provisioning); + if (priv->phase1_fast_provisioning) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase1_fast_provisioning); + } else { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + } g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, @@ -2989,11 +2906,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) "otp", "md5", "tls")) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("'%s' is not a valid value for the property"), - priv->phase2_auth); + if (priv->phase2_auth) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase2_auth); + } else { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + } g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, @@ -3002,11 +2926,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } if (!NM_IN_STRSET(priv->phase2_autheap, NULL, "md5", "mschapv2", "otp", "gtc", "tls")) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("'%s' is not a valid value for the property"), - priv->phase2_autheap); + if (priv->phase2_autheap) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase2_autheap); + } else { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + } g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, @@ -3049,6 +2980,50 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) error)) return FALSE; + /* normalizable warnings from here on. */ + +#define _check_strempty_and_return(priv, prop_name, field, error) \ + G_STMT_START \ + { \ + NMSetting8021xPrivate *_priv = (priv); \ + GError **_error = (error); \ + \ + if (_priv->field && _priv->field[0] == '\0') { \ + g_set_error(_error, \ + NM_CONNECTION_ERROR, \ + NM_CONNECTION_ERROR_INVALID_PROPERTY, \ + _("property is empty")); \ + g_prefix_error(_error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, "" prop_name ""); \ + return NM_SETTING_VERIFY_NORMALIZABLE; \ + } \ + } \ + G_STMT_END + + _check_strempty_and_return(priv, NM_SETTING_802_1X_IDENTITY, identity, error); + _check_strempty_and_return(priv, + NM_SETTING_802_1X_ANONYMOUS_IDENTITY, + anonymous_identity, + error); + _check_strempty_and_return(priv, NM_SETTING_802_1X_PAC_FILE, pac_file, error); + _check_strempty_and_return(priv, NM_SETTING_802_1X_SUBJECT_MATCH, subject_match, error); + _check_strempty_and_return(priv, + NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, + phase2_subject_match, + error); + _check_strempty_and_return(priv, + NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, + domain_suffix_match, + error); + _check_strempty_and_return(priv, + NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, + phase2_domain_suffix_match, + error); + _check_strempty_and_return(priv, NM_SETTING_802_1X_DOMAIN_MATCH, domain_match, error); + _check_strempty_and_return(priv, + NM_SETTING_802_1X_PHASE2_DOMAIN_MATCH, + phase2_domain_match, + error); + return TRUE; } diff --git a/src/libnm-core-impl/nm-setting-connection.c b/src/libnm-core-impl/nm-setting-connection.c index 73611004..0d7d7cd5 100644 --- a/src/libnm-core-impl/nm-setting-connection.c +++ b/src/libnm-core-impl/nm-setting-connection.c @@ -71,32 +71,32 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingConnection, PROP_MUD_URL, ); typedef struct { - GArray *permissions; - GArray *secondaries; - char *id; - char *uuid; - char *stable_id; - char *interface_name; - char *type; - char *master; - char *slave_type; - char *zone; - char *mud_url; - guint64 timestamp; - int autoconnect_slaves; - int metered; - gint32 autoconnect_priority; - gint32 autoconnect_retries; - gint32 multi_connect; - gint32 auth_retries; - gint32 mdns; - gint32 llmnr; - gint32 dns_over_tls; - gint32 wait_device_timeout; - gint32 lldp; - guint32 gateway_ping_timeout; - bool autoconnect; - bool read_only; + GArray *permissions; + NMValueStrv secondaries; + char *id; + char *uuid; + char *stable_id; + char *interface_name; + char *type; + char *master; + char *slave_type; + char *zone; + char *mud_url; + guint64 timestamp; + int autoconnect_slaves; + int metered; + gint32 autoconnect_priority; + gint32 autoconnect_retries; + gint32 multi_connect; + gint32 auth_retries; + gint32 mdns; + gint32 llmnr; + gint32 dns_over_tls; + gint32 wait_device_timeout; + gint32 lldp; + guint32 gateway_ping_timeout; + bool autoconnect; + bool read_only; } NMSettingConnectionPrivate; /** @@ -757,7 +757,7 @@ nm_setting_connection_get_autoconnect_slaves(NMSettingConnection *setting) GArray * _nm_setting_connection_get_secondaries(NMSettingConnection *setting) { - return NM_SETTING_CONNECTION_GET_PRIVATE(setting)->secondaries; + return NM_SETTING_CONNECTION_GET_PRIVATE(setting)->secondaries.arr; } /** @@ -771,7 +771,7 @@ nm_setting_connection_get_num_secondaries(NMSettingConnection *setting) { g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting), 0); - return nm_g_array_len(NM_SETTING_CONNECTION_GET_PRIVATE(setting)->secondaries); + return nm_g_array_len(NM_SETTING_CONNECTION_GET_PRIVATE(setting)->secondaries.arr); } /** @@ -794,14 +794,14 @@ nm_setting_connection_get_secondary(NMSettingConnection *setting, guint32 idx) priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting); - secondaries_len = nm_g_array_len(priv->secondaries); + secondaries_len = nm_g_array_len(priv->secondaries.arr); if (idx >= secondaries_len) { /* access one past the length is OK. */ g_return_val_if_fail(idx == secondaries_len, NULL); return NULL; } - return nm_strvarray_get_idx(priv->secondaries, idx); + return nm_strvarray_get_idx(priv->secondaries.arr, idx); } /** @@ -841,10 +841,10 @@ nm_setting_connection_add_secondary(NMSettingConnection *setting, const char *se priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting); - if (nm_strvarray_find_first(priv->secondaries, sec_uuid) >= 0) + if (nm_strvarray_find_first(priv->secondaries.arr, sec_uuid) >= 0) return FALSE; - nm_strvarray_add(nm_strvarray_ensure(&priv->secondaries), sec_uuid); + nm_strvarray_add(nm_strvarray_ensure(&priv->secondaries.arr), sec_uuid); _notify(setting, PROP_SECONDARIES); return TRUE; } @@ -865,9 +865,9 @@ nm_setting_connection_remove_secondary(NMSettingConnection *setting, guint32 idx priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting); - g_return_if_fail(idx < nm_g_array_len(priv->secondaries)); + g_return_if_fail(idx < nm_g_array_len(priv->secondaries.arr)); - g_array_remove_index(priv->secondaries, idx); + g_array_remove_index(priv->secondaries.arr, idx); _notify(setting, PROP_SECONDARIES); } @@ -890,7 +890,7 @@ nm_setting_connection_remove_secondary_by_value(NMSettingConnection *setting, co priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting); - if (nm_strvarray_remove_first(priv->secondaries, sec_uuid)) { + if (nm_strvarray_remove_first(priv->secondaries.arr, sec_uuid)) { _notify(setting, PROP_SECONDARIES); return TRUE; } @@ -1524,7 +1524,7 @@ after_interface_name: return NM_SETTING_VERIFY_NORMALIZABLE; } - if (!_nm_setting_connection_verify_secondaries(priv->secondaries, error)) + if (!_nm_setting_connection_verify_secondaries(priv->secondaries.arr, error)) return NM_SETTING_VERIFY_NORMALIZABLE; return TRUE; @@ -1637,9 +1637,6 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) case PROP_TIMESTAMP: g_value_set_uint64(value, nm_setting_connection_get_timestamp(setting)); break; - case PROP_SECONDARIES: - g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(priv->secondaries, NULL)); - break; default: _nm_setting_property_get_property_direct(object, prop_id, value, pspec); break; @@ -1675,9 +1672,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps case PROP_TIMESTAMP: priv->timestamp = g_value_get_uint64(value); break; - case PROP_SECONDARIES: - nm_strvarray_set_strv(&priv->secondaries, g_value_get_boxed(value)); - break; default: _nm_setting_property_set_property_direct(object, prop_id, value, pspec); break; @@ -1709,7 +1703,7 @@ finalize(GObject *object) NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE(object); nm_clear_pointer(&priv->permissions, g_array_unref); - nm_clear_pointer(&priv->secondaries, g_array_unref); + nm_clear_pointer(&priv->secondaries.arr, g_array_unref); G_OBJECT_CLASS(nm_setting_connection_parent_class)->finalize(object); } @@ -1808,7 +1802,8 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass) * is commonly also included, so that different systems end up generating * different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's * name is included, so that different interfaces yield different addresses. - * The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret-key. + * The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. + * See NetworkManager(8) manual about the secret-key and the host identity. * * The '$' character is treated special to perform dynamic substitutions * at runtime. Currently, supported are "${CONNECTION}", "${DEVICE}", "${MAC}", @@ -1958,6 +1953,15 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass) * Note that autoconnect is not implemented for VPN profiles. See * #NMSettingConnection:secondaries as an alternative to automatically * connect VPN profiles. + * + * If multiple profiles are ready to autoconnect on the same device, + * the one with the better "connection.autoconnect-priority" is chosen. If + * the priorities are equal, then the most recently connected profile is activated. + * If the profiles were not connected earlier or their + * "connection.timestamp" is identical, the choice is undefined. + * + * Depending on "connection.multi-connect", a profile can (auto)connect only + * once at a time or multiple times. **/ /* ---ifcfg-rh--- * property: autoconnect @@ -2230,12 +2234,13 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass) * together with this connection. * ---end--- */ - obj_properties[PROP_SECONDARIES] = g_param_spec_boxed( - NM_SETTING_CONNECTION_SECONDARIES, - "", - "", - G_TYPE_STRV, - G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS); + _nm_setting_property_define_direct_strv(properties_override, + obj_properties, + NM_SETTING_CONNECTION_SECONDARIES, + PROP_SECONDARIES, + NM_SETTING_PARAM_FUZZY_IGNORE, + NMSettingConnectionPrivate, + secondaries); /** * NMSettingConnection:gateway-ping-timeout: diff --git a/src/libnm-core-impl/nm-setting-ip-config.c b/src/libnm-core-impl/nm-setting-ip-config.c index 52aa6515..cffd5b19 100644 --- a/src/libnm-core-impl/nm-setting-ip-config.c +++ b/src/libnm-core-impl/nm-setting-ip-config.c @@ -1390,7 +1390,8 @@ _ip_route_attribute_validate(const char *name, RTN_LOCAL, RTN_BLACKHOLE, RTN_UNREACHABLE, - RTN_PROHIBIT)) { + RTN_PROHIBIT, + RTN_THROW)) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -1494,6 +1495,7 @@ _nm_ip_route_attribute_validate_all(const NMIPRoute *route, GError **error) case RTN_BLACKHOLE: case RTN_UNREACHABLE: case RTN_PROHIBIT: + case RTN_THROW: if (route->next_hop) { g_set_error(error, NM_CONNECTION_ERROR, @@ -2684,7 +2686,7 @@ nm_ip_routing_rule_validate(const NMIPRoutingRule *self, GError **error) g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("invalid priority")); + _("missing priority")); return FALSE; } diff --git a/src/libnm-core-impl/nm-setting-ip4-config.c b/src/libnm-core-impl/nm-setting-ip4-config.c index 5b06739c..bf555168 100644 --- a/src/libnm-core-impl/nm-setting-ip4-config.c +++ b/src/libnm-core-impl/nm-setting-ip4-config.c @@ -1006,7 +1006,8 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) * </listitem> * <listitem> * <para><literal>"type"</literal> - one of <literal>unicast</literal>, <literal>local</literal>, <literal>blackhole</literal>, - * <literal>unavailable</literal>, <literal>prohibit</literal>. The default is <literal>unicast</literal>.</para> + * <literal>unavailable</literal>, <literal>prohibit</literal>, <literal>throw</literal>. + * The default is <literal>unicast</literal>.</para> * </listitem> * <listitem> * <para><literal>"window"</literal> - an unsigned 32 bit integer.</para> diff --git a/src/libnm-core-impl/nm-setting-ip6-config.c b/src/libnm-core-impl/nm-setting-ip6-config.c index f4623b28..f75c14ce 100644 --- a/src/libnm-core-impl/nm-setting-ip6-config.c +++ b/src/libnm-core-impl/nm-setting-ip6-config.c @@ -946,8 +946,9 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) * format: a comma separated list of addresses * description: A list of IPv6 addresses and their prefix length. Multiple addresses * can be separated by comma. For example "2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64". - * The addresses are listed in increasing priority, meaning the last address will - * be the primary address. + * The addresses are listed in decreasing priority, meaning the first address will + * be the primary address. This can make a difference with IPv6 source address selection + * (RFC 6724, section 5). * ---end--- */ _nm_properties_override_gobj( @@ -1042,7 +1043,8 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) * </listitem> * <listitem> * <para><literal>"type"</literal> - one of <literal>unicast</literal>, <literal>local</literal>, <literal>blackhole</literal>, - * <literal>unavailable</literal>, <literal>prohibit</literal>. The default is <literal>unicast</literal>.</para> + * <literal>unavailable</literal>, <literal>prohibit</literal>, <literal>throw</literal>. + * The default is <literal>unicast</literal>.</para> * </listitem> * <listitem> * <para><literal>"window"</literal> - an unsigned 32 bit integer.</para> diff --git a/src/libnm-core-impl/nm-setting-match.c b/src/libnm-core-impl/nm-setting-match.c index 0ddffee5..f0e9fdf8 100644 --- a/src/libnm-core-impl/nm-setting-match.c +++ b/src/libnm-core-impl/nm-setting-match.c @@ -32,11 +32,11 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingMatch, * Since: 1.14 */ struct _NMSettingMatch { - NMSetting parent; - GArray *interface_name; - GArray *kernel_command_line; - GArray *driver; - GArray *path; + NMSetting parent; + NMValueStrv interface_name; + NMValueStrv kernel_command_line; + NMValueStrv driver; + NMValueStrv path; }; struct _NMSettingMatchClass { @@ -60,7 +60,7 @@ nm_setting_match_get_num_interface_names(NMSettingMatch *setting) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), 0); - return nm_g_array_len(setting->interface_name); + return nm_g_array_len(setting->interface_name.arr); } /** @@ -77,10 +77,11 @@ nm_setting_match_get_interface_name(NMSettingMatch *setting, int idx) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - g_return_val_if_fail(setting->interface_name && idx >= 0 && idx < setting->interface_name->len, + g_return_val_if_fail(setting->interface_name.arr && idx >= 0 + && idx < setting->interface_name.arr->len, NULL); - return nm_strvarray_get_idx(setting->interface_name, idx); + return nm_strvarray_get_idx(setting->interface_name.arr, idx); } /** @@ -98,7 +99,7 @@ nm_setting_match_add_interface_name(NMSettingMatch *setting, const char *interfa g_return_if_fail(NM_IS_SETTING_MATCH(setting)); g_return_if_fail(interface_name); - nm_strvarray_add(nm_strvarray_ensure(&setting->interface_name), interface_name); + nm_strvarray_add(nm_strvarray_ensure(&setting->interface_name.arr), interface_name); _notify(setting, PROP_INTERFACE_NAME); } @@ -116,9 +117,10 @@ nm_setting_match_remove_interface_name(NMSettingMatch *setting, int idx) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - g_return_if_fail(setting->interface_name && idx >= 0 && idx < setting->interface_name->len); + g_return_if_fail(setting->interface_name.arr && idx >= 0 + && idx < setting->interface_name.arr->len); - g_array_remove_index(setting->interface_name, idx); + g_array_remove_index(setting->interface_name.arr, idx); _notify(setting, PROP_INTERFACE_NAME); } @@ -139,7 +141,7 @@ nm_setting_match_remove_interface_name_by_value(NMSettingMatch *setting, const c g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), FALSE); g_return_val_if_fail(interface_name, FALSE); - if (nm_strvarray_remove_first(setting->interface_name, interface_name)) { + if (nm_strvarray_remove_first(setting->interface_name.arr, interface_name)) { _notify(setting, PROP_INTERFACE_NAME); return TRUE; } @@ -160,8 +162,8 @@ nm_setting_match_clear_interface_names(NMSettingMatch *setting) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - if (nm_g_array_len(setting->interface_name) != 0) { - nm_clear_pointer(&setting->interface_name, g_array_unref); + if (nm_g_array_len(setting->interface_name.arr) != 0) { + nm_clear_pointer(&setting->interface_name.arr, g_array_unref); _notify(setting, PROP_INTERFACE_NAME); } } @@ -185,7 +187,7 @@ nm_setting_match_get_interface_names(NMSettingMatch *setting, guint *length) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - return nm_strvarray_get_strv(&setting->interface_name, length); + return nm_strvarray_get_strv(&setting->interface_name.arr, length); } /*****************************************************************************/ @@ -203,7 +205,7 @@ nm_setting_match_get_num_kernel_command_lines(NMSettingMatch *setting) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), 0); - return nm_g_array_len(setting->kernel_command_line); + return nm_g_array_len(setting->kernel_command_line.arr); } /** @@ -220,10 +222,11 @@ nm_setting_match_get_kernel_command_line(NMSettingMatch *setting, guint idx) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - g_return_val_if_fail(setting->kernel_command_line && idx < setting->kernel_command_line->len, + g_return_val_if_fail(setting->kernel_command_line.arr + && idx < setting->kernel_command_line.arr->len, NULL); - return nm_strvarray_get_idx(setting->kernel_command_line, idx); + return nm_strvarray_get_idx(setting->kernel_command_line.arr, idx); } /** @@ -241,7 +244,7 @@ nm_setting_match_add_kernel_command_line(NMSettingMatch *setting, const char *ke g_return_if_fail(NM_IS_SETTING_MATCH(setting)); g_return_if_fail(kernel_command_line); - nm_strvarray_add(nm_strvarray_ensure(&setting->kernel_command_line), kernel_command_line); + nm_strvarray_add(nm_strvarray_ensure(&setting->kernel_command_line.arr), kernel_command_line); _notify(setting, PROP_KERNEL_COMMAND_LINE); } @@ -259,9 +262,10 @@ nm_setting_match_remove_kernel_command_line(NMSettingMatch *setting, guint idx) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - g_return_if_fail(setting->kernel_command_line && idx < setting->kernel_command_line->len); + g_return_if_fail(setting->kernel_command_line.arr + && idx < setting->kernel_command_line.arr->len); - g_array_remove_index(setting->kernel_command_line, idx); + g_array_remove_index(setting->kernel_command_line.arr, idx); _notify(setting, PROP_KERNEL_COMMAND_LINE); } @@ -283,7 +287,7 @@ nm_setting_match_remove_kernel_command_line_by_value(NMSettingMatch *setting, g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), FALSE); g_return_val_if_fail(kernel_command_line, FALSE); - if (nm_strvarray_remove_first(setting->kernel_command_line, kernel_command_line)) { + if (nm_strvarray_remove_first(setting->kernel_command_line.arr, kernel_command_line)) { _notify(setting, PROP_KERNEL_COMMAND_LINE); return TRUE; } @@ -304,8 +308,8 @@ nm_setting_match_clear_kernel_command_lines(NMSettingMatch *setting) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - if (nm_g_array_len(setting->kernel_command_line) != 0) { - nm_clear_pointer(&setting->kernel_command_line, g_array_unref); + if (nm_g_array_len(setting->kernel_command_line.arr) != 0) { + nm_clear_pointer(&setting->kernel_command_line.arr, g_array_unref); _notify(setting, PROP_KERNEL_COMMAND_LINE); } } @@ -326,7 +330,7 @@ nm_setting_match_get_kernel_command_lines(NMSettingMatch *setting, guint *length { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - return nm_strvarray_get_strv(&setting->kernel_command_line, length); + return nm_strvarray_get_strv(&setting->kernel_command_line.arr, length); } /*****************************************************************************/ @@ -344,7 +348,7 @@ nm_setting_match_get_num_drivers(NMSettingMatch *setting) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), 0); - return nm_g_array_len(setting->driver); + return nm_g_array_len(setting->driver.arr); } /** @@ -361,9 +365,9 @@ nm_setting_match_get_driver(NMSettingMatch *setting, guint idx) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - g_return_val_if_fail(setting->driver && idx < setting->driver->len, NULL); + g_return_val_if_fail(setting->driver.arr && idx < setting->driver.arr->len, NULL); - return nm_strvarray_get_idx(setting->driver, idx); + return nm_strvarray_get_idx(setting->driver.arr, idx); } /** @@ -381,7 +385,7 @@ nm_setting_match_add_driver(NMSettingMatch *setting, const char *driver) g_return_if_fail(NM_IS_SETTING_MATCH(setting)); g_return_if_fail(driver); - nm_strvarray_add(nm_strvarray_ensure(&setting->driver), driver); + nm_strvarray_add(nm_strvarray_ensure(&setting->driver.arr), driver); _notify(setting, PROP_DRIVER); } @@ -399,9 +403,9 @@ nm_setting_match_remove_driver(NMSettingMatch *setting, guint idx) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - g_return_if_fail(setting->driver && idx < setting->driver->len); + g_return_if_fail(setting->driver.arr && idx < setting->driver.arr->len); - g_array_remove_index(setting->driver, idx); + g_array_remove_index(setting->driver.arr, idx); _notify(setting, PROP_DRIVER); } @@ -422,7 +426,7 @@ nm_setting_match_remove_driver_by_value(NMSettingMatch *setting, const char *dri g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), FALSE); g_return_val_if_fail(driver, FALSE); - if (nm_strvarray_remove_first(setting->driver, driver)) { + if (nm_strvarray_remove_first(setting->driver.arr, driver)) { _notify(setting, PROP_DRIVER); return TRUE; } @@ -443,8 +447,8 @@ nm_setting_match_clear_drivers(NMSettingMatch *setting) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - if (nm_g_array_len(setting->driver) != 0) { - nm_clear_pointer(&setting->driver, g_array_unref); + if (nm_g_array_len(setting->driver.arr) != 0) { + nm_clear_pointer(&setting->driver.arr, g_array_unref); _notify(setting, PROP_DRIVER); } } @@ -465,7 +469,7 @@ nm_setting_match_get_drivers(NMSettingMatch *setting, guint *length) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - return nm_strvarray_get_strv(&setting->driver, length); + return nm_strvarray_get_strv(&setting->driver.arr, length); } /*****************************************************************************/ @@ -483,7 +487,7 @@ nm_setting_match_get_num_paths(NMSettingMatch *setting) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), 0); - return nm_g_array_len(setting->path); + return nm_g_array_len(setting->path.arr); } /** @@ -500,9 +504,9 @@ nm_setting_match_get_path(NMSettingMatch *setting, guint idx) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - g_return_val_if_fail(setting->path && idx < setting->path->len, NULL); + g_return_val_if_fail(setting->path.arr && idx < setting->path.arr->len, NULL); - return nm_strvarray_get_idx(setting->path, idx); + return nm_strvarray_get_idx(setting->path.arr, idx); } /** @@ -520,7 +524,7 @@ nm_setting_match_add_path(NMSettingMatch *setting, const char *path) g_return_if_fail(NM_IS_SETTING_MATCH(setting)); g_return_if_fail(path); - nm_strvarray_add(nm_strvarray_ensure(&setting->path), path); + nm_strvarray_add(nm_strvarray_ensure(&setting->path.arr), path); _notify(setting, PROP_PATH); } @@ -538,9 +542,9 @@ nm_setting_match_remove_path(NMSettingMatch *setting, guint idx) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - g_return_if_fail(setting->path && idx < setting->path->len); + g_return_if_fail(setting->path.arr && idx < setting->path.arr->len); - g_array_remove_index(setting->path, idx); + g_array_remove_index(setting->path.arr, idx); _notify(setting, PROP_PATH); } @@ -561,7 +565,7 @@ nm_setting_match_remove_path_by_value(NMSettingMatch *setting, const char *path) g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), FALSE); g_return_val_if_fail(path, FALSE); - if (nm_strvarray_remove_first(setting->path, path)) { + if (nm_strvarray_remove_first(setting->path.arr, path)) { _notify(setting, PROP_PATH); return TRUE; } @@ -582,8 +586,8 @@ nm_setting_match_clear_paths(NMSettingMatch *setting) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - if (nm_g_array_len(setting->path) != 0) { - nm_clear_pointer(&setting->path, g_array_unref); + if (nm_g_array_len(setting->path.arr) != 0) { + nm_clear_pointer(&setting->path.arr, g_array_unref); _notify(setting, PROP_PATH); } } @@ -604,58 +608,7 @@ nm_setting_match_get_paths(NMSettingMatch *setting, guint *length) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - return nm_strvarray_get_strv(&setting->path, length); -} - -/*****************************************************************************/ - -static void -get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - NMSettingMatch *self = NM_SETTING_MATCH(object); - - switch (prop_id) { - case PROP_INTERFACE_NAME: - g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(self->interface_name, NULL)); - break; - case PROP_KERNEL_COMMAND_LINE: - g_value_take_boxed(value, - nm_strvarray_get_strv_non_empty_dup(self->kernel_command_line, NULL)); - break; - case PROP_DRIVER: - g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(self->driver, NULL)); - break; - case PROP_PATH: - g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(self->path, NULL)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void -set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - NMSettingMatch *self = NM_SETTING_MATCH(object); - - switch (prop_id) { - case PROP_INTERFACE_NAME: - nm_strvarray_set_strv(&self->interface_name, g_value_get_boxed(value)); - break; - case PROP_KERNEL_COMMAND_LINE: - nm_strvarray_set_strv(&self->kernel_command_line, g_value_get_boxed(value)); - break; - case PROP_DRIVER: - nm_strvarray_set_strv(&self->driver, g_value_get_boxed(value)); - break; - case PROP_PATH: - nm_strvarray_set_strv(&self->path, g_value_get_boxed(value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } + return nm_strvarray_get_strv(&setting->path.arr, length); } /*****************************************************************************/ @@ -698,9 +651,9 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) NMSettingMatch *self = NM_SETTING_MATCH(setting); guint i; - if (self->interface_name) { - for (i = 0; i < self->interface_name->len; i++) { - if (nm_str_is_empty(nm_strvarray_get_idx(self->interface_name, i))) { + if (self->interface_name.arr) { + for (i = 0; i < self->interface_name.arr->len; i++) { + if (nm_str_is_empty(nm_strvarray_get_idx(self->interface_name.arr, i))) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -714,9 +667,9 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } } - if (self->kernel_command_line) { - for (i = 0; i < self->kernel_command_line->len; i++) { - if (nm_str_is_empty(nm_strvarray_get_idx(self->kernel_command_line, i))) { + if (self->kernel_command_line.arr) { + for (i = 0; i < self->kernel_command_line.arr->len; i++) { + if (nm_str_is_empty(nm_strvarray_get_idx(self->kernel_command_line.arr, i))) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -730,9 +683,9 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } } - if (self->driver) { - for (i = 0; i < self->driver->len; i++) { - if (nm_str_is_empty(nm_strvarray_get_idx(self->driver, i))) { + if (self->driver.arr) { + for (i = 0; i < self->driver.arr->len; i++) { + if (nm_str_is_empty(nm_strvarray_get_idx(self->driver.arr, i))) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -746,9 +699,9 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } } - if (self->path) { - for (i = 0; i < self->path->len; i++) { - if (nm_str_is_empty(nm_strvarray_get_idx(self->path, i))) { + if (self->path.arr) { + for (i = 0; i < self->path.arr->len; i++) { + if (nm_str_is_empty(nm_strvarray_get_idx(self->path.arr, i))) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -770,10 +723,10 @@ finalize(GObject *object) { NMSettingMatch *self = NM_SETTING_MATCH(object); - nm_clear_pointer(&self->interface_name, g_array_unref); - nm_clear_pointer(&self->kernel_command_line, g_array_unref); - nm_clear_pointer(&self->driver, g_array_unref); - nm_clear_pointer(&self->path, g_array_unref); + nm_clear_pointer(&self->interface_name.arr, g_array_unref); + nm_clear_pointer(&self->kernel_command_line.arr, g_array_unref); + nm_clear_pointer(&self->driver.arr, g_array_unref); + nm_clear_pointer(&self->path.arr, g_array_unref); G_OBJECT_CLASS(nm_setting_match_parent_class)->finalize(object); } @@ -781,11 +734,12 @@ finalize(GObject *object) static void nm_setting_match_class_init(NMSettingMatchClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS(klass); - NMSettingClass *setting_class = NM_SETTING_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + NMSettingClass *setting_class = NM_SETTING_CLASS(klass); + GArray *properties_override = _nm_sett_info_property_override_create_array(); - object_class->get_property = get_property; - object_class->set_property = set_property; + object_class->get_property = _nm_setting_property_get_property_direct; + object_class->set_property = _nm_setting_property_set_property_direct; object_class->finalize = finalize; setting_class->verify = verify; @@ -810,12 +764,13 @@ nm_setting_match_class_init(NMSettingMatchClass *klass) * * Since: 1.14 **/ - obj_properties[PROP_INTERFACE_NAME] = g_param_spec_boxed( - NM_SETTING_MATCH_INTERFACE_NAME, - "", - "", - G_TYPE_STRV, - NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + _nm_setting_property_define_direct_strv(properties_override, + obj_properties, + NM_SETTING_MATCH_INTERFACE_NAME, + PROP_INTERFACE_NAME, + NM_SETTING_PARAM_FUZZY_IGNORE, + NMSettingMatch, + interface_name); /** * NMSettingMatch:kernel-command-line @@ -834,12 +789,13 @@ nm_setting_match_class_init(NMSettingMatchClass *klass) * * Since: 1.26 **/ - obj_properties[PROP_KERNEL_COMMAND_LINE] = g_param_spec_boxed( - NM_SETTING_MATCH_KERNEL_COMMAND_LINE, - "", - "", - G_TYPE_STRV, - NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + _nm_setting_property_define_direct_strv(properties_override, + obj_properties, + NM_SETTING_MATCH_KERNEL_COMMAND_LINE, + PROP_KERNEL_COMMAND_LINE, + NM_SETTING_PARAM_FUZZY_IGNORE, + NMSettingMatch, + kernel_command_line); /** * NMSettingMatch:driver @@ -852,12 +808,13 @@ nm_setting_match_class_init(NMSettingMatchClass *klass) * * Since: 1.26 **/ - obj_properties[PROP_DRIVER] = g_param_spec_boxed( - NM_SETTING_MATCH_DRIVER, - "", - "", - G_TYPE_STRV, - NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + _nm_setting_property_define_direct_strv(properties_override, + obj_properties, + NM_SETTING_MATCH_DRIVER, + PROP_DRIVER, + NM_SETTING_PARAM_FUZZY_IGNORE, + NMSettingMatch, + driver); /** * NMSettingMatch:path @@ -892,14 +849,19 @@ nm_setting_match_class_init(NMSettingMatchClass *klass) * example: MATCH_PATH="pci-0000:01:00.0 pci-0000:0c:00.0" * ---end--- */ - obj_properties[PROP_PATH] = g_param_spec_boxed(NM_SETTING_MATCH_PATH, - "", - "", - G_TYPE_STRV, - NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_READWRITE - | G_PARAM_STATIC_STRINGS); + _nm_setting_property_define_direct_strv(properties_override, + obj_properties, + NM_SETTING_MATCH_PATH, + PROP_PATH, + NM_SETTING_PARAM_FUZZY_IGNORE, + NMSettingMatch, + path); g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); - _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_MATCH, NULL, NULL, 0); + _nm_setting_class_commit(setting_class, + NM_META_SETTING_TYPE_MATCH, + NULL, + properties_override, + 0); } diff --git a/src/libnm-core-impl/nm-setting-private.h b/src/libnm-core-impl/nm-setting-private.h index 7483eaed..38b233e9 100644 --- a/src/libnm-core-impl/nm-setting-private.h +++ b/src/libnm-core-impl/nm-setting-private.h @@ -232,6 +232,14 @@ gboolean _nm_setting_clear_secrets(NMSetting *setting, /*****************************************************************************/ +/* This holds a property of type NM_VALUE_TYPE_STRV. You probably want + * to use nm_strvarray_*() API with this. */ +typedef struct { + GArray *arr; +} NMValueStrv; + +/*****************************************************************************/ + #define NM_SETTING_PARAM_NONE 0 /* The property of the #NMSetting should be considered during comparisons that @@ -277,6 +285,7 @@ extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_int64; extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_uint64; extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_string; extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_bytes; +extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_strv; extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_enum; extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_flags; extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_mac_address; @@ -774,6 +783,42 @@ _nm_properties_override(GArray *properties_override, const NMSettInfoProperty *p /*****************************************************************************/ +#define _nm_setting_property_define_direct_strv(properties_override, \ + obj_properties, \ + prop_name, \ + prop_id, \ + param_flags, \ + private_struct_type, \ + private_struct_field, \ + ... /* extra NMSettInfoProperty fields */) \ + G_STMT_START \ + { \ + GParamSpec *_param_spec; \ + \ + G_STATIC_ASSERT(!NM_FLAGS_ANY((param_flags), ~(NM_SETTING_PARAM_FUZZY_IGNORE))); \ + \ + _param_spec = \ + g_param_spec_boxed("" prop_name "", \ + "", \ + "", \ + G_TYPE_STRV, \ + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (param_flags)); \ + \ + (obj_properties)[(prop_id)] = _param_spec; \ + \ + _nm_properties_override_gobj((properties_override), \ + _param_spec, \ + &nm_sett_info_propert_type_direct_strv, \ + .direct_offset = \ + NM_STRUCT_OFFSET_ENSURE_TYPE(NMValueStrv, \ + private_struct_type, \ + private_struct_field), \ + __VA_ARGS__); \ + } \ + G_STMT_END + +/*****************************************************************************/ + #define _nm_setting_property_define_direct_enum(properties_override, \ obj_properties, \ prop_name, \ diff --git a/src/libnm-core-impl/nm-setting-proxy.c b/src/libnm-core-impl/nm-setting-proxy.c index 4b9a2970..8b91210b 100644 --- a/src/libnm-core-impl/nm-setting-proxy.c +++ b/src/libnm-core-impl/nm-setting-proxy.c @@ -114,7 +114,7 @@ nm_setting_proxy_get_pac_url(NMSettingProxy *setting) * nm_setting_proxy_get_pac_script: * @setting: the #NMSettingProxy * - * Returns: the PAC script + * Returns: the PAC script. * * Since: 1.6 **/ @@ -315,15 +315,25 @@ nm_setting_proxy_class_init(NMSettingProxyClass *klass) /** * NMSettingProxy:pac-script: * - * PAC script for the connection. + * PAC script for the connection. This is an UTF-8 encoded javascript code + * that defines a FindProxyForURL() function. * * Since: 1.6 **/ + /* ---nmcli--- + * property: pac-script + * description: The PAC script. In the profile this must be an UTF-8 encoded javascript code that defines + * a FindProxyForURL() function. + * When setting the property in nmcli, a filename is accepted too. In that case, + * nmcli will read the content of the file and set the script. The prefixes "file://" and "js://" are + * supported to explicitly differentiate between the two. + * ---end--- + */ /* ---ifcfg-rh--- * property: pac-script * variable: PAC_SCRIPT(+) - * description: Path of the PAC script. - * example: PAC_SCRIPT=/home/joe/proxy.pac + * description: The PAC script. This is an UTF-8 encoded javascript code that defines a FindProxyForURL() function. + * example: PAC_SCRIPT="function FindProxyForURL (url, host) { return 'PROXY proxy.example.com:8080; DIRECT'; }" * ---end--- */ _nm_setting_property_define_direct_string(properties_override, diff --git a/src/libnm-core-impl/nm-setting.c b/src/libnm-core-impl/nm-setting.c index be88effb..35070bae 100644 --- a/src/libnm-core-impl/nm-setting.c +++ b/src/libnm-core-impl/nm-setting.c @@ -774,6 +774,13 @@ _nm_setting_property_get_property_direct(GObject *object, g_value_set_boxed(value, *p_val); return; } + case NM_VALUE_TYPE_STRV: + { + const NMValueStrv *p_val = _nm_setting_get_private_field(setting, sett_info, property_info); + + g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(p_val->arr, NULL)); + return; + } default: goto out_fail; } @@ -909,6 +916,18 @@ _nm_setting_property_set_property_direct(GObject *object, *p_val = v ? g_bytes_ref(v) : NULL; goto out_notify; } + case NM_VALUE_TYPE_STRV: + { + NMValueStrv *p_val = _nm_setting_get_private_field(setting, sett_info, property_info); + const char *const *v; + + v = g_value_get_boxed(value); + if (nm_strvarray_equal_strv(p_val->arr, v, -1)) + return; + + nm_strvarray_set_strv(&p_val->arr, v); + goto out_notify; + } default: goto out_fail; } @@ -1026,6 +1045,11 @@ _init_direct(NMSetting *setting) nm_assert(!(*((const GBytes *const *) _nm_setting_get_private_field(setting, sett_info, property_info)))); break; + case NM_VALUE_TYPE_STRV: + nm_assert(!((const NMValueStrv *) + _nm_setting_get_private_field(setting, sett_info, property_info)) + ->arr); + break; default: nm_assert_not_reached(); break; @@ -1081,6 +1105,13 @@ _finalize_direct(NMSetting *setting) nm_clear_pointer(p_val, g_bytes_unref); break; } + case NM_VALUE_TYPE_STRV: + { + NMValueStrv *p_val = _nm_setting_get_private_field(setting, sett_info, property_info); + + nm_clear_pointer(&p_val->arr, g_array_unref); + break; + } default: nm_assert_not_reached(); break; @@ -1199,6 +1230,20 @@ _nm_setting_property_to_dbus_fcn_direct(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_ return NULL; return nm_g_bytes_to_variant_ay(val); } + case NM_VALUE_TYPE_STRV: + { + const NMValueStrv *val; + + /* Strv properties have always NULL as default. Setting "including_default" has no defined meaning + * (but it could have). */ + nm_assert(!property_info->to_dbus_including_default); + + val = + (const NMValueStrv *) _nm_setting_get_private_field(setting, sett_info, property_info); + if (!val->arr) + return NULL; + return g_variant_new_strv((const char *const *) val->arr->data, val->arr->len); + } default: return nm_assert_unreachable_val(NULL); } @@ -1545,6 +1590,8 @@ _nm_setting_property_from_dbus_fcn_direct(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS gs_unref_bytes GBytes *v = NULL; GBytes **p_val; + nm_assert(!property_info->property_type->from_dbus_direct_allow_transform); + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BYTESTRING)) goto out_error_wrong_dbus_type; @@ -1557,6 +1604,28 @@ _nm_setting_property_from_dbus_fcn_direct(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS NM_SWAP(p_val, &v); goto out_notify; } + case NM_VALUE_TYPE_STRV: + { + NMValueStrv *p_val; + gs_free const char **ss = NULL; + gsize ss_len; + + nm_assert(!property_info->property_type->from_dbus_direct_allow_transform); + + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING_ARRAY)) + goto out_error_wrong_dbus_type; + + ss = g_variant_get_strv(value, &ss_len); + nm_assert(ss_len <= G_MAXUINT); + + p_val = _nm_setting_get_private_field(setting, sett_info, property_info); + + if (nm_strvarray_equal_strv(p_val->arr, ss, ss_len)) + goto out_unchanged; + + nm_strvarray_set_strv(&p_val->arr, ss); + goto out_notify; + } default: break; } @@ -2465,6 +2534,9 @@ _nm_setting_property_compare_fcn_direct(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_ return nm_streq0(*((const char *const *) p_a), *((const char *const *) p_b)); case NM_VALUE_TYPE_BYTES: return nm_g_bytes_equal0(*((const GBytes *const *) p_a), *((const GBytes *const *) p_b)); + case NM_VALUE_TYPE_STRV: + return nm_strvarray_equal(((const NMValueStrv *) p_a)->arr, + ((const NMValueStrv *) p_b)->arr); default: return nm_assert_unreachable_val(TRUE); } @@ -3529,8 +3601,15 @@ const NMSettInfoPropertType nm_sett_info_propert_type_direct_bytes = .compare_fcn = _nm_setting_property_compare_fcn_direct, .to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct, .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct, - .from_dbus_is_full = TRUE, - .from_dbus_direct_allow_transform = TRUE); + .from_dbus_is_full = TRUE); + +const NMSettInfoPropertType nm_sett_info_propert_type_direct_strv = + NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING_ARRAY, + .direct_type = NM_VALUE_TYPE_STRV, + .compare_fcn = _nm_setting_property_compare_fcn_direct, + .to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct, + .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct, + .from_dbus_is_full = TRUE); const NMSettInfoPropertType nm_sett_info_propert_type_direct_enum = NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_INT32, @@ -3896,7 +3975,7 @@ nm_setting_option_set(NMSetting *setting, const char *opt_name, GVariant *varian g_hash_table_insert(hash, g_strdup(opt_name), g_variant_ref_sink(variant)); if (changed_value) - _nm_setting_option_notify(setting, !changed_name); + _nm_setting_option_notify(setting, changed_name); } /** @@ -3933,7 +4012,7 @@ nm_setting_option_set_boolean(NMSetting *setting, const char *opt_name, gboolean g_hash_table_insert(hash, g_strdup(opt_name), g_variant_ref_sink(g_variant_new_boolean(value))); if (changed_value) - _nm_setting_option_notify(setting, !changed_name); + _nm_setting_option_notify(setting, changed_name); } /** @@ -3968,7 +4047,7 @@ nm_setting_option_set_uint32(NMSetting *setting, const char *opt_name, guint32 v g_hash_table_insert(hash, g_strdup(opt_name), g_variant_ref_sink(g_variant_new_uint32(value))); if (changed_value) - _nm_setting_option_notify(setting, !changed_name); + _nm_setting_option_notify(setting, changed_name); } /*****************************************************************************/ diff --git a/src/libnm-core-impl/nm-utils.c b/src/libnm-core-impl/nm-utils.c index 55b01c15..d5d884f2 100644 --- a/src/libnm-core-impl/nm-utils.c +++ b/src/libnm-core-impl/nm-utils.c @@ -27,7 +27,6 @@ #include "libnm-core-aux-intern/nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" -#include "nm-crypto.h" #include "nm-setting-bond.h" #include "nm-setting-bond-port.h" #include "nm-setting-bridge.h" @@ -3083,94 +3082,6 @@ nm_utils_uuid_generate(void) /*****************************************************************************/ -static gboolean -file_has_extension(const char *filename, const char *extensions[]) -{ - const char *ext; - gsize i; - - ext = strrchr(filename, '.'); - if (!ext) - return FALSE; - - for (i = 0; extensions[i]; i++) { - if (!g_ascii_strcasecmp(ext, extensions[i])) - return TRUE; - } - - return FALSE; -} - -/** - * nm_utils_file_is_certificate: - * @filename: name of the file to test - * - * Tests if @filename has a valid extension for an X.509 certificate file - * (".cer", ".crt", ".der", or ".pem"), and contains a certificate in a format - * recognized by NetworkManager. - * - * Returns: %TRUE if the file is a certificate, %FALSE if it is not - **/ -gboolean -nm_utils_file_is_certificate(const char *filename) -{ - const char *extensions[] = {".der", ".pem", ".crt", ".cer", NULL}; - NMCryptoFileFormat file_format; - - g_return_val_if_fail(filename != NULL, FALSE); - - if (!file_has_extension(filename, extensions)) - return FALSE; - - if (!nm_crypto_load_and_verify_certificate(filename, &file_format, NULL, NULL)) - return FALSE; - return file_format = NM_CRYPTO_FILE_FORMAT_X509; -} - -/** - * nm_utils_file_is_private_key: - * @filename: name of the file to test - * @out_encrypted: (out): on return, whether the file is encrypted - * - * Tests if @filename has a valid extension for an X.509 private key file - * (".der", ".key", ".pem", or ".p12"), and contains a private key in a format - * recognized by NetworkManager. - * - * Returns: %TRUE if the file is a private key, %FALSE if it is not - **/ -gboolean -nm_utils_file_is_private_key(const char *filename, gboolean *out_encrypted) -{ - const char *extensions[] = {".der", ".pem", ".p12", ".key", NULL}; - - g_return_val_if_fail(filename != NULL, FALSE); - - NM_SET_OUT(out_encrypted, FALSE); - if (!file_has_extension(filename, extensions)) - return FALSE; - - return nm_crypto_verify_private_key(filename, NULL, out_encrypted, NULL) - != NM_CRYPTO_FILE_FORMAT_UNKNOWN; -} - -/** - * nm_utils_file_is_pkcs12: - * @filename: name of the file to test - * - * Tests if @filename is a PKCS#<!-- -->12 file. - * - * Returns: %TRUE if the file is PKCS#<!-- -->12, %FALSE if it is not - **/ -gboolean -nm_utils_file_is_pkcs12(const char *filename) -{ - g_return_val_if_fail(filename != NULL, FALSE); - - return nm_crypto_is_pkcs12_file(filename, NULL); -} - -/*****************************************************************************/ - gboolean _nm_utils_check_file(const char *filename, gint64 check_owner, @@ -3795,22 +3706,13 @@ nm_utils_hwaddr_aton(const char *asc, gpointer buffer, gsize length) char * nm_utils_bin2hexstr(gconstpointer src, gsize len, int final_len) { - char *result; gsize buflen = (len * 2) + 1; g_return_val_if_fail(src != NULL, NULL); g_return_val_if_fail(len > 0 && (buflen - 1) / 2 == len, NULL); g_return_val_if_fail(final_len < 0 || (gsize) final_len < buflen, NULL); - result = g_malloc(buflen); - - nm_utils_bin2hexstr_full(src, len, '\0', FALSE, result); - - /* Cut converted key off at the correct length for this cipher type */ - if (final_len >= 0 && (gsize) final_len < buflen) - result[final_len] = '\0'; - - return result; + return _nm_utils_bin2hexstr(src, len, final_len); } /** diff --git a/src/libnm-core-impl/tests/test-crypto.c b/src/libnm-core-impl/tests/test-crypto.c index 6a6e7fbc..896c3c2e 100644 --- a/src/libnm-core-impl/tests/test-crypto.c +++ b/src/libnm-core-impl/tests/test-crypto.c @@ -10,7 +10,7 @@ #include <stdlib.h> #include <stdio.h> -#include "nm-crypto-impl.h" +#include "libnm-crypto/nm-crypto-impl.h" #include "nm-utils.h" #include "nm-errors.h" #include "libnm-core-intern/nm-core-internal.h" @@ -92,7 +92,7 @@ test_cert(gconstpointer test_data) nmtst_assert_success(success, error); g_assert_cmpint(format, ==, NM_CRYPTO_FILE_FORMAT_X509); - g_assert(nm_utils_file_is_certificate(path)); + g_assert(nm_crypto_utils_file_is_certificate(path)); } static void @@ -106,7 +106,7 @@ test_load_private_key(const char *path, gs_unref_bytes GBytes *array = NULL; GError *error = NULL; - g_assert(nm_utils_file_is_private_key(path, &is_encrypted)); + g_assert(nm_crypto_utils_file_is_private_key(path, &is_encrypted)); g_assert(is_encrypted); array = nmtst_crypto_decrypt_openssl_private_key(path, password, &key_type, &error); @@ -146,7 +146,7 @@ test_load_pkcs12(const char *path, const char *password, int expected_error) gboolean is_encrypted = FALSE; GError *error = NULL; - g_assert(nm_utils_file_is_private_key(path, NULL)); + g_assert(nm_crypto_utils_file_is_private_key(path, NULL)); format = nm_crypto_verify_private_key(path, password, &is_encrypted, &error); if (expected_error != -1) { @@ -167,7 +167,7 @@ test_load_pkcs12_no_password(const char *path) gboolean is_encrypted = FALSE; GError *error = NULL; - g_assert(nm_utils_file_is_private_key(path, NULL)); + g_assert(nm_crypto_utils_file_is_private_key(path, NULL)); /* We should still get a valid returned crypto file format */ format = nm_crypto_verify_private_key(path, NULL, &is_encrypted, &error); @@ -201,7 +201,7 @@ test_load_pkcs8(const char *path, const char *password, int expected_error) gboolean is_encrypted = FALSE; GError *error = NULL; - g_assert(nm_utils_file_is_private_key(path, NULL)); + g_assert(nm_crypto_utils_file_is_private_key(path, NULL)); format = nm_crypto_verify_private_key(path, password, &is_encrypted, &error); if (expected_error != -1) { @@ -285,7 +285,7 @@ test_key_decrypted(gconstpointer test_data) path = g_build_filename(TEST_CERT_DIR, file, NULL); - g_assert(nm_utils_file_is_private_key(path, &is_encrypted)); + g_assert(nm_crypto_utils_file_is_private_key(path, &is_encrypted)); g_assert(!is_encrypted); g_free(path); @@ -399,6 +399,23 @@ test_md5(void) } } +/*****************************************************************************/ + +static void +test_crypto_error(void) +{ + G_STATIC_ASSERT(NM_CRYPTO_ERROR_FAILED == _NM_CRYPTO_ERROR_FAILED); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_INVALID_DATA == _NM_CRYPTO_ERROR_INVALID_DATA); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_INVALID_PASSWORD == _NM_CRYPTO_ERROR_INVALID_PASSWORD); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_UNKNOWN_CIPHER == _NM_CRYPTO_ERROR_UNKNOWN_CIPHER); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_DECRYPTION_FAILED == _NM_CRYPTO_ERROR_DECRYPTION_FAILED); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_ENCRYPTION_FAILED == _NM_CRYPTO_ERROR_ENCRYPTION_FAILED); + + g_assert_cmpint(NM_CRYPTO_ERROR, ==, _NM_CRYPTO_ERROR); +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -448,6 +465,7 @@ main(int argc, char **argv) g_test_add_data_func("/libnm/crypto/PKCS#8", "pkcs8-enc-key.pem, 1234567890", test_pkcs8); g_test_add_func("/libnm/crypto/md5", test_md5); + g_test_add_func("/libnm/crypto/error", test_crypto_error); ret = g_test_run(); diff --git a/src/libnm-core-impl/tests/test-general-enums.h b/src/libnm-core-impl/tests/test-general-enums.h index b3163e23..2ac8cf2b 100644 --- a/src/libnm-core-impl/tests/test-general-enums.h +++ b/src/libnm-core-impl/tests/test-general-enums.h @@ -25,11 +25,11 @@ typedef enum { NM_TEST_GENERAL_META_FLAGS_0x4 = (1 << 4), } NMTestGeneralMetaFlags; -typedef enum { /*< flags >*/ - NM_TEST_GENERAL_COLOR_FLAGS_WHITE = 1, /*< skip >*/ - NM_TEST_GENERAL_COLOR_FLAGS_BLUE = 2, - NM_TEST_GENERAL_COLOR_FLAGS_RED = 4, - NM_TEST_GENERAL_COLOR_FLAGS_GREEN = 8, +typedef enum /*< flags >*/ { + NM_TEST_GENERAL_COLOR_FLAGS_WHITE = 1, /*< skip >*/ + NM_TEST_GENERAL_COLOR_FLAGS_BLUE = 2, + NM_TEST_GENERAL_COLOR_FLAGS_RED = 4, + NM_TEST_GENERAL_COLOR_FLAGS_GREEN = 8, } NMTestGeneralColorFlags; #endif /* _NM_TEST_GENERAL_ENUMS_H_ */ diff --git a/src/libnm-core-impl/tests/test-general.c b/src/libnm-core-impl/tests/test-general.c index d0f85a9f..1856f6ad 100644 --- a/src/libnm-core-impl/tests/test-general.c +++ b/src/libnm-core-impl/tests/test-general.c @@ -5060,7 +5060,7 @@ test_setting_connection_changed_signal(void) ASSERT_CHANGED(nm_setting_connection_add_secondary(s_con, uuid)); ASSERT_CHANGED(nm_setting_connection_remove_secondary(s_con, 0)); - NMTST_EXPECT_LIBNM_CRITICAL(NMTST_G_RETURN_MSG(idx < nm_g_array_len(priv->secondaries))); + NMTST_EXPECT_LIBNM_CRITICAL(NMTST_G_RETURN_MSG(idx < nm_g_array_len(priv->secondaries.arr))); ASSERT_UNCHANGED(nm_setting_connection_remove_secondary(s_con, 1)); g_test_assert_expected_messages(); diff --git a/src/libnm-core-impl/tests/test-keyfile.c b/src/libnm-core-impl/tests/test-keyfile.c index 9bd13ffc..c163c429 100644 --- a/src/libnm-core-impl/tests/test-keyfile.c +++ b/src/libnm-core-impl/tests/test-keyfile.c @@ -5,16 +5,18 @@ #include "libnm-core-impl/nm-default-libnm-core.h" -#include "libnm-glib-aux/nm-json-aux.h" -#include "libnm-core-intern/nm-keyfile-utils.h" +#include "libnm-base/nm-ethtool-utils-base.h" #include "libnm-core-intern/nm-keyfile-internal.h" -#include "nm-simple-connection.h" -#include "nm-setting-connection.h" -#include "nm-setting-wired.h" +#include "libnm-core-intern/nm-keyfile-utils.h" +#include "libnm-glib-aux/nm-json-aux.h" #include "nm-setting-8021x.h" +#include "nm-setting-connection.h" +#include "nm-setting-ethtool.h" +#include "nm-setting-proxy.h" #include "nm-setting-team.h" #include "nm-setting-user.h" -#include "nm-setting-proxy.h" +#include "nm-setting-wired.h" +#include "nm-simple-connection.h" #include "libnm-glib-aux/nm-test-utils.h" @@ -887,6 +889,100 @@ test_bridge_port_vlans(void) /*****************************************************************************/ +typedef struct { + bool expect; + guint n_calls; +} InvalidOptionWriteData; + +static gboolean +_invalid_option_write_handler(NMConnection *connection, + GKeyFile *keyfile, + NMKeyfileHandlerType handler_type, + NMKeyfileHandlerData *handler_data, + void *user_data) +{ + InvalidOptionWriteData *data = user_data; + const char *message; + NMKeyfileWarnSeverity severity; + + g_assert(data); + g_assert(data->expect); + + g_assert(data->n_calls == 0); + data->n_calls++; + + switch (handler_type) { + case NM_KEYFILE_HANDLER_TYPE_WARN: + nm_keyfile_handler_data_warn_get(handler_data, &message, &severity); + g_assert(message && strstr(message, "ethtool.bogus")); + break; + default: + g_assert_not_reached(); + } + + return TRUE; +} + +static void +test_invalid_option(void) +{ + gs_unref_object NMConnection *con = NULL; + NMSetting *s_ethtool; + nm_auto_unref_keyfile GKeyFile *kf = NULL; + gs_free_error GError *error = NULL; + InvalidOptionWriteData data; + + con = nmtst_create_minimal_connection("test invalid option", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + s_ethtool = nm_setting_ethtool_new(); + + nm_connection_add_setting(con, s_ethtool); + + nm_setting_option_set_boolean(s_ethtool, NM_ETHTOOL_OPTNAME_PAUSE_RX, TRUE); + + data = (InvalidOptionWriteData){}; + kf = nm_keyfile_write(con, + NM_KEYFILE_HANDLER_FLAGS_NONE, + _invalid_option_write_handler, + &data, + nmtst_get_rand_bool() ? &error : NULL); + nmtst_assert_success(kf, error); + nm_clear_pointer(&kf, g_key_file_unref); + + nmtst_connection_normalize(con); + + nmtst_assert_connection_verifies_without_normalization(con); + + data = (InvalidOptionWriteData){}; + kf = nm_keyfile_write(con, + NM_KEYFILE_HANDLER_FLAGS_NONE, + _invalid_option_write_handler, + &data, + nmtst_get_rand_bool() ? &error : NULL); + nmtst_assert_success(kf, error); + nm_clear_pointer(&kf, g_key_file_unref); + + nm_setting_option_set(s_ethtool, "bogus", g_variant_new_int64(0)); + + data = (InvalidOptionWriteData){ + .expect = TRUE, + }; + kf = nm_keyfile_write(con, + NM_KEYFILE_HANDLER_FLAGS_NONE, + _invalid_option_write_handler, + &data, + nmtst_get_rand_bool() ? &error : NULL); + nmtst_assert_success(kf, error); + nm_clear_pointer(&kf, g_key_file_unref); + + g_assert_cmpint(data.n_calls, ==, 1); +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -904,6 +1000,7 @@ main(int argc, char **argv) g_test_add_func("/core/keyfile/test_vpn/1", test_vpn_1); g_test_add_func("/core/keyfile/bridge/vlans", test_bridge_vlans); g_test_add_func("/core/keyfile/bridge-port/vlans", test_bridge_port_vlans); + g_test_add_func("/core/keyfile/invalid-option", test_invalid_option); return g_test_run(); } diff --git a/src/libnm-core-impl/tests/test-setting.c b/src/libnm-core-impl/tests/test-setting.c index b260ac37..788f218d 100644 --- a/src/libnm-core-impl/tests/test-setting.c +++ b/src/libnm-core-impl/tests/test-setting.c @@ -4565,6 +4565,12 @@ test_setting_metadata(void) == _nm_setting_property_to_dbus_fcn_direct); g_assert(sip->param_spec); g_assert(sip->param_spec->value_type == G_TYPE_BYTES); + } else if (sip->property_type->direct_type == NM_VALUE_TYPE_STRV) { + g_assert(g_variant_type_equal(sip->property_type->dbus_type, "as")); + g_assert(sip->property_type->to_dbus_fcn + == _nm_setting_property_to_dbus_fcn_direct); + g_assert(sip->param_spec); + g_assert(sip->param_spec->value_type == G_TYPE_STRV); } else g_assert_not_reached(); @@ -4653,7 +4659,12 @@ check_done:; } if (sip->property_type->from_dbus_fcn == _nm_setting_property_from_dbus_fcn_direct) { /* for the moment, all direct properties allow transformation. */ - g_assert(sip->property_type->from_dbus_direct_allow_transform); + if (NM_IN_SET(sip->property_type->direct_type, + NM_VALUE_TYPE_BYTES, + NM_VALUE_TYPE_STRV)) + g_assert(!sip->property_type->from_dbus_direct_allow_transform); + else + g_assert(sip->property_type->from_dbus_direct_allow_transform); } if (sip->property_type->from_dbus_fcn == _nm_setting_property_from_dbus_fcn_gprop) @@ -4763,10 +4774,12 @@ check_done:; g_assert(NM_IS_SETTING_VPN(setting)); g_assert_cmpstr(sip->name, ==, NM_SETTING_VPN_SECRETS); } else { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("secret %s.%s is of unexpected property type %s", nm_setting_get_name(setting), sip->name, g_type_name(sip->param_spec->value_type)); + NM_PRAGMA_WARNING_REENABLE } } } @@ -4889,12 +4902,14 @@ check_done:; /* the property-types with same content should all be shared. Here we have two that * are the same content, but different instances. Bug. */ + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("The identical property type for D-Bus type \"%s\" is used by: %s and %s. " "If a NMSettInfoPropertType is identical, it should be shared by creating " "a common instance of the property type", (const char *) pt->dbus_type, _PROP_IDX_OWNER(h_property_types, pt), _PROP_IDX_OWNER(h_property_types, pt_2)); + NM_PRAGMA_WARNING_REENABLE } } } diff --git a/src/libnm-core-intern/nm-core-internal.h b/src/libnm-core-intern/nm-core-internal.h index d9634b04..032f6a9a 100644 --- a/src/libnm-core-intern/nm-core-internal.h +++ b/src/libnm-core-intern/nm-core-internal.h @@ -189,14 +189,14 @@ NM_TERNARY_TO_OPTION_BOOL(NMTernary v) NMSetting **_nm_connection_get_settings_arr(NMConnection *connection); -typedef enum { /*< skip >*/ - NM_SETTING_PARSE_FLAGS_NONE = 0, - NM_SETTING_PARSE_FLAGS_STRICT = 1LL << 0, - NM_SETTING_PARSE_FLAGS_BEST_EFFORT = 1LL << 1, - NM_SETTING_PARSE_FLAGS_NORMALIZE = 1LL << 2, - - _NM_SETTING_PARSE_FLAGS_LAST, - NM_SETTING_PARSE_FLAGS_ALL = ((_NM_SETTING_PARSE_FLAGS_LAST - 1) << 1) - 1, +typedef enum /*< skip >*/ { + NM_SETTING_PARSE_FLAGS_NONE = 0, + NM_SETTING_PARSE_FLAGS_STRICT = 1LL << 0, + NM_SETTING_PARSE_FLAGS_BEST_EFFORT = 1LL << 1, + NM_SETTING_PARSE_FLAGS_NORMALIZE = 1LL << 2, + + _NM_SETTING_PARSE_FLAGS_LAST, + NM_SETTING_PARSE_FLAGS_ALL = ((_NM_SETTING_PARSE_FLAGS_LAST - 1) << 1) - 1, } NMSettingParseFlags; gboolean _nm_connection_replace_settings(NMConnection *connection, diff --git a/src/libnm-core-intern/nm-meta-setting-base-impl.h b/src/libnm-core-intern/nm-meta-setting-base-impl.h index ea4f85ff..12688659 100644 --- a/src/libnm-core-intern/nm-meta-setting-base-impl.h +++ b/src/libnm-core-intern/nm-meta-setting-base-impl.h @@ -38,20 +38,20 @@ * * 10: NMSettingUser */ -typedef enum { /*< skip >*/ - NM_SETTING_PRIORITY_INVALID = 0, - NM_SETTING_PRIORITY_CONNECTION = 1, - NM_SETTING_PRIORITY_HW_BASE = 2, - NM_SETTING_PRIORITY_HW_NON_BASE = 3, - NM_SETTING_PRIORITY_HW_AUX = 4, - NM_SETTING_PRIORITY_AUX = 5, - NM_SETTING_PRIORITY_IP = 6, - NM_SETTING_PRIORITY_USER = 10, +typedef enum /*< skip >*/ { + NM_SETTING_PRIORITY_INVALID = 0, + NM_SETTING_PRIORITY_CONNECTION = 1, + NM_SETTING_PRIORITY_HW_BASE = 2, + NM_SETTING_PRIORITY_HW_NON_BASE = 3, + NM_SETTING_PRIORITY_HW_AUX = 4, + NM_SETTING_PRIORITY_AUX = 5, + NM_SETTING_PRIORITY_IP = 6, + NM_SETTING_PRIORITY_USER = 10, } NMSettingPriority; /*****************************************************************************/ -typedef enum { +typedef enum _nm_packed { NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT, NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT, NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT, @@ -92,6 +92,8 @@ typedef struct { extern const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[_NM_SETTING_802_1X_SCHEME_TYPE_NUM + 1]; +const NMSetting8021xSchemeVtable *nm_setting_8021x_scheme_vtable_by_setting_key(const char *key); + /*****************************************************************************/ typedef enum _nm_packed { diff --git a/src/libnm-core-public/nm-connection.h b/src/libnm-core-public/nm-connection.h index 081f115e..93061f25 100644 --- a/src/libnm-core-public/nm-connection.h +++ b/src/libnm-core-public/nm-connection.h @@ -122,15 +122,15 @@ NMSetting *nm_connection_get_setting_by_name(NMConnection *connection, const cha * These flags determine which properties are serialized when calling * nm_connection_to_dbus(). **/ -typedef enum { /*< flags >*/ - NM_CONNECTION_SERIALIZE_ALL = 0x00000000, - NM_CONNECTION_SERIALIZE_WITH_NON_SECRET = 0x00000001, - NM_CONNECTION_SERIALIZE_NO_SECRETS = 0x00000001, - NM_CONNECTION_SERIALIZE_WITH_SECRETS = 0x00000002, - NM_CONNECTION_SERIALIZE_ONLY_SECRETS = 0x00000002, - NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED = 0x00000004, - NM_CONNECTION_SERIALIZE_WITH_SECRETS_SYSTEM_OWNED = 0x00000008, - NM_CONNECTION_SERIALIZE_WITH_SECRETS_NOT_SAVED = 0x00000010, +typedef enum /*< flags >*/ { + NM_CONNECTION_SERIALIZE_ALL = 0x00000000, + NM_CONNECTION_SERIALIZE_WITH_NON_SECRET = 0x00000001, + NM_CONNECTION_SERIALIZE_NO_SECRETS = 0x00000001, + NM_CONNECTION_SERIALIZE_WITH_SECRETS = 0x00000002, + NM_CONNECTION_SERIALIZE_ONLY_SECRETS = 0x00000002, + NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED = 0x00000004, + NM_CONNECTION_SERIALIZE_WITH_SECRETS_SYSTEM_OWNED = 0x00000008, + NM_CONNECTION_SERIALIZE_WITH_SECRETS_NOT_SAVED = 0x00000010, } NMConnectionSerializationFlags; GVariant *nm_connection_to_dbus(NMConnection *connection, NMConnectionSerializationFlags flags); diff --git a/src/libnm-core-public/nm-dbus-interface.h b/src/libnm-core-public/nm-dbus-interface.h index 0d23c7d7..94622793 100644 --- a/src/libnm-core-public/nm-dbus-interface.h +++ b/src/libnm-core-public/nm-dbus-interface.h @@ -265,12 +265,12 @@ typedef enum { * * General device capability flags. **/ -typedef enum { /*< flags >*/ - NM_DEVICE_CAP_NONE = 0x00000000, - NM_DEVICE_CAP_NM_SUPPORTED = 0x00000001, - NM_DEVICE_CAP_CARRIER_DETECT = 0x00000002, - NM_DEVICE_CAP_IS_SOFTWARE = 0x00000004, - NM_DEVICE_CAP_SRIOV = 0x00000008, +typedef enum /*< flags >*/ { + NM_DEVICE_CAP_NONE = 0x00000000, + NM_DEVICE_CAP_NM_SUPPORTED = 0x00000001, + NM_DEVICE_CAP_CARRIER_DETECT = 0x00000002, + NM_DEVICE_CAP_IS_SOFTWARE = 0x00000004, + NM_DEVICE_CAP_SRIOV = 0x00000008, } NMDeviceCapabilities; /** @@ -292,21 +292,21 @@ typedef enum { /*< flags >*/ * * 802.11 specific device encryption and authentication capabilities. **/ -typedef enum { /*< flags >*/ - NM_WIFI_DEVICE_CAP_NONE = 0x00000000, - NM_WIFI_DEVICE_CAP_CIPHER_WEP40 = 0x00000001, - NM_WIFI_DEVICE_CAP_CIPHER_WEP104 = 0x00000002, - NM_WIFI_DEVICE_CAP_CIPHER_TKIP = 0x00000004, - NM_WIFI_DEVICE_CAP_CIPHER_CCMP = 0x00000008, - NM_WIFI_DEVICE_CAP_WPA = 0x00000010, - NM_WIFI_DEVICE_CAP_RSN = 0x00000020, - NM_WIFI_DEVICE_CAP_AP = 0x00000040, - NM_WIFI_DEVICE_CAP_ADHOC = 0x00000080, - NM_WIFI_DEVICE_CAP_FREQ_VALID = 0x00000100, - NM_WIFI_DEVICE_CAP_FREQ_2GHZ = 0x00000200, - NM_WIFI_DEVICE_CAP_FREQ_5GHZ = 0x00000400, - NM_WIFI_DEVICE_CAP_MESH = 0x00001000, - NM_WIFI_DEVICE_CAP_IBSS_RSN = 0x00002000, +typedef enum /*< flags >*/ { + NM_WIFI_DEVICE_CAP_NONE = 0x00000000, + NM_WIFI_DEVICE_CAP_CIPHER_WEP40 = 0x00000001, + NM_WIFI_DEVICE_CAP_CIPHER_WEP104 = 0x00000002, + NM_WIFI_DEVICE_CAP_CIPHER_TKIP = 0x00000004, + NM_WIFI_DEVICE_CAP_CIPHER_CCMP = 0x00000008, + NM_WIFI_DEVICE_CAP_WPA = 0x00000010, + NM_WIFI_DEVICE_CAP_RSN = 0x00000020, + NM_WIFI_DEVICE_CAP_AP = 0x00000040, + NM_WIFI_DEVICE_CAP_ADHOC = 0x00000080, + NM_WIFI_DEVICE_CAP_FREQ_VALID = 0x00000100, + NM_WIFI_DEVICE_CAP_FREQ_2GHZ = 0x00000200, + NM_WIFI_DEVICE_CAP_FREQ_5GHZ = 0x00000400, + NM_WIFI_DEVICE_CAP_MESH = 0x00001000, + NM_WIFI_DEVICE_CAP_IBSS_RSN = 0x00002000, } NMDeviceWifiCapabilities; /** @@ -320,12 +320,12 @@ typedef enum { /*< flags >*/ * * 802.11 access point flags. **/ -typedef enum { /*< underscore_name=nm_802_11_ap_flags, flags >*/ - NM_802_11_AP_FLAGS_NONE = 0x00000000, - NM_802_11_AP_FLAGS_PRIVACY = 0x00000001, - NM_802_11_AP_FLAGS_WPS = 0x00000002, - NM_802_11_AP_FLAGS_WPS_PBC = 0x00000004, - NM_802_11_AP_FLAGS_WPS_PIN = 0x00000008, +typedef enum /*< underscore_name=nm_802_11_ap_flags, flags >*/ { + NM_802_11_AP_FLAGS_NONE = 0x00000000, + NM_802_11_AP_FLAGS_PRIVACY = 0x00000001, + NM_802_11_AP_FLAGS_WPS = 0x00000002, + NM_802_11_AP_FLAGS_WPS_PBC = 0x00000004, + NM_802_11_AP_FLAGS_WPS_PIN = 0x00000008, } NM80211ApFlags; /** @@ -361,22 +361,22 @@ typedef enum { /*< underscore_name=nm_802_11_ap_flags, flags >*/ * the current security requirements of an access point as determined from the * access point's beacon. **/ -typedef enum { /*< underscore_name=nm_802_11_ap_security_flags, flags >*/ - NM_802_11_AP_SEC_NONE = 0x00000000, - NM_802_11_AP_SEC_PAIR_WEP40 = 0x00000001, - NM_802_11_AP_SEC_PAIR_WEP104 = 0x00000002, - NM_802_11_AP_SEC_PAIR_TKIP = 0x00000004, - NM_802_11_AP_SEC_PAIR_CCMP = 0x00000008, - NM_802_11_AP_SEC_GROUP_WEP40 = 0x00000010, - NM_802_11_AP_SEC_GROUP_WEP104 = 0x00000020, - NM_802_11_AP_SEC_GROUP_TKIP = 0x00000040, - NM_802_11_AP_SEC_GROUP_CCMP = 0x00000080, - NM_802_11_AP_SEC_KEY_MGMT_PSK = 0x00000100, - NM_802_11_AP_SEC_KEY_MGMT_802_1X = 0x00000200, - NM_802_11_AP_SEC_KEY_MGMT_SAE = 0x00000400, - NM_802_11_AP_SEC_KEY_MGMT_OWE = 0x00000800, - NM_802_11_AP_SEC_KEY_MGMT_OWE_TM = 0x00001000, - NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192 = 0x00002000, +typedef enum /*< underscore_name=nm_802_11_ap_security_flags, flags >*/ { + NM_802_11_AP_SEC_NONE = 0x00000000, + NM_802_11_AP_SEC_PAIR_WEP40 = 0x00000001, + NM_802_11_AP_SEC_PAIR_WEP104 = 0x00000002, + NM_802_11_AP_SEC_PAIR_TKIP = 0x00000004, + NM_802_11_AP_SEC_PAIR_CCMP = 0x00000008, + NM_802_11_AP_SEC_GROUP_WEP40 = 0x00000010, + NM_802_11_AP_SEC_GROUP_WEP104 = 0x00000020, + NM_802_11_AP_SEC_GROUP_TKIP = 0x00000040, + NM_802_11_AP_SEC_GROUP_CCMP = 0x00000080, + NM_802_11_AP_SEC_KEY_MGMT_PSK = 0x00000100, + NM_802_11_AP_SEC_KEY_MGMT_802_1X = 0x00000200, + NM_802_11_AP_SEC_KEY_MGMT_SAE = 0x00000400, + NM_802_11_AP_SEC_KEY_MGMT_OWE = 0x00000800, + NM_802_11_AP_SEC_KEY_MGMT_OWE_TM = 0x00001000, + NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192 = 0x00002000, } NM80211ApSecurityFlags; /** @@ -412,10 +412,10 @@ typedef enum { /*< underscore_name=nm_802_11_mode >*/ * #NMBluetoothCapabilities values indicate the usable capabilities of a * Bluetooth device. **/ -typedef enum { /*< flags >*/ - NM_BT_CAPABILITY_NONE = 0x00000000, - NM_BT_CAPABILITY_DUN = 0x00000001, - NM_BT_CAPABILITY_NAP = 0x00000002, +typedef enum /*< flags >*/ { + NM_BT_CAPABILITY_NONE = 0x00000000, + NM_BT_CAPABILITY_DUN = 0x00000001, + NM_BT_CAPABILITY_NAP = 0x00000002, } NMBluetoothCapabilities; /** @@ -435,13 +435,13 @@ typedef enum { /*< flags >*/ * specific access technologies the device supports use the ModemManager D-Bus * API. **/ -typedef enum { /*< flags >*/ - NM_DEVICE_MODEM_CAPABILITY_NONE = 0x00000000, - NM_DEVICE_MODEM_CAPABILITY_POTS = 0x00000001, - NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO = 0x00000002, - NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS = 0x00000004, - NM_DEVICE_MODEM_CAPABILITY_LTE = 0x00000008, - NM_DEVICE_MODEM_CAPABILITY_5GNR = 0x00000040, +typedef enum /*< flags >*/ { + NM_DEVICE_MODEM_CAPABILITY_NONE = 0x00000000, + NM_DEVICE_MODEM_CAPABILITY_POTS = 0x00000001, + NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO = 0x00000002, + NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS = 0x00000004, + NM_DEVICE_MODEM_CAPABILITY_LTE = 0x00000008, + NM_DEVICE_MODEM_CAPABILITY_5GNR = 0x00000040, } NMDeviceModemCapabilities; /* Note: the numeric values of NMDeviceModemCapabilities must be identical to the values * in MMModemCapability. See the G_STATIC_ASSERT() in nm-modem-broadband.c's get_capabilities(). */ @@ -834,16 +834,16 @@ typedef enum { * * #NMSecretAgentGetSecretsFlags values modify the behavior of a GetSecrets request. */ -typedef enum { /*< flags >*/ - NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE = 0x0, - NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION = 0x1, - NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW = 0x2, - NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED = 0x4, - NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE = 0x8, +typedef enum /*< flags >*/ { + NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE = 0x0, + NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION = 0x1, + NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW = 0x2, + NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED = 0x4, + NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE = 0x8, - /* Internal to NM; not part of the D-Bus API */ - NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM = 0x80000000, - NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS = 0x40000000, + /* Internal to NM; not part of the D-Bus API */ + NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM = 0x80000000, + NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS = 0x40000000, } NMSecretAgentGetSecretsFlags; /** @@ -962,20 +962,20 @@ typedef enum { * @NM_CHECKPOINT_CREATE_FLAG_NO_PRESERVE_EXTERNAL_PORTS: during rollback, * by default externally added ports attached to bridge devices are preserved. * With this flag, the rollback detaches all external ports. - * This only has an effect for bridge ports. Before 1.38, 1.36.2, this was the default - * behavior. Since: 1.38, 1.36.2. + * This only has an effect for bridge ports. Before 1.38, this was the default + * behavior. Since: 1.38. * * The flags for CheckpointCreate call * * Since: 1.4 (gi flags generated since 1.12) */ -typedef enum { /*< flags >*/ - NM_CHECKPOINT_CREATE_FLAG_NONE = 0, - NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL = 0x01, - NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS = 0x02, - NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES = 0x04, - NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING = 0x08, - NM_CHECKPOINT_CREATE_FLAG_NO_PRESERVE_EXTERNAL_PORTS = 0x10, +typedef enum /*< flags >*/ { + NM_CHECKPOINT_CREATE_FLAG_NONE = 0, + NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL = 0x01, + NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS = 0x02, + NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES = 0x04, + NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING = 0x08, + NM_CHECKPOINT_CREATE_FLAG_NO_PRESERVE_EXTERNAL_PORTS = 0x10, } NMCheckpointCreateFlags; /** @@ -989,11 +989,11 @@ typedef enum { /*< flags >*/ * * Since: 1.4 **/ -typedef enum { /*< skip >*/ - NM_ROLLBACK_RESULT_OK = 0, - NM_ROLLBACK_RESULT_ERR_NO_DEVICE = 1, - NM_ROLLBACK_RESULT_ERR_DEVICE_UNMANAGED = 2, - NM_ROLLBACK_RESULT_ERR_FAILED = 3, +typedef enum /*< skip >*/ { + NM_ROLLBACK_RESULT_OK = 0, + NM_ROLLBACK_RESULT_ERR_NO_DEVICE = 1, + NM_ROLLBACK_RESULT_ERR_DEVICE_UNMANAGED = 2, + NM_ROLLBACK_RESULT_ERR_FAILED = 3, } NMRollbackResult; /** @@ -1019,12 +1019,12 @@ typedef enum { /*< skip >*/ * * Since: 1.12 **/ -typedef enum { /*< flags >*/ - NM_SETTINGS_CONNECTION_FLAG_NONE = 0, - NM_SETTINGS_CONNECTION_FLAG_UNSAVED = 0x01, - NM_SETTINGS_CONNECTION_FLAG_NM_GENERATED = 0x02, - NM_SETTINGS_CONNECTION_FLAG_VOLATILE = 0x04, - NM_SETTINGS_CONNECTION_FLAG_EXTERNAL = 0x08, +typedef enum /*< flags >*/ { + NM_SETTINGS_CONNECTION_FLAG_NONE = 0, + NM_SETTINGS_CONNECTION_FLAG_UNSAVED = 0x01, + NM_SETTINGS_CONNECTION_FLAG_NM_GENERATED = 0x02, + NM_SETTINGS_CONNECTION_FLAG_VOLATILE = 0x04, + NM_SETTINGS_CONNECTION_FLAG_EXTERNAL = 0x08, } NMSettingsConnectionFlags; /** @@ -1048,17 +1048,17 @@ typedef enum { /*< flags >*/ * * Since: 1.10 **/ -typedef enum { /*< flags >*/ - NM_ACTIVATION_STATE_FLAG_NONE = 0, +typedef enum /*< flags >*/ { + NM_ACTIVATION_STATE_FLAG_NONE = 0, - NM_ACTIVATION_STATE_FLAG_IS_MASTER = 0x1, - NM_ACTIVATION_STATE_FLAG_IS_SLAVE = 0x2, - NM_ACTIVATION_STATE_FLAG_LAYER2_READY = 0x4, - NM_ACTIVATION_STATE_FLAG_IP4_READY = 0x8, - NM_ACTIVATION_STATE_FLAG_IP6_READY = 0x10, - NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES = 0x20, - NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY = 0x40, - NM_ACTIVATION_STATE_FLAG_EXTERNAL = 0x80, + NM_ACTIVATION_STATE_FLAG_IS_MASTER = 0x1, + NM_ACTIVATION_STATE_FLAG_IS_SLAVE = 0x2, + NM_ACTIVATION_STATE_FLAG_LAYER2_READY = 0x4, + NM_ACTIVATION_STATE_FLAG_IP4_READY = 0x8, + NM_ACTIVATION_STATE_FLAG_IP6_READY = 0x10, + NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES = 0x20, + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY = 0x40, + NM_ACTIVATION_STATE_FLAG_EXTERNAL = 0x80, } NMActivationStateFlags; /** @@ -1075,11 +1075,11 @@ typedef enum { /*< flags >*/ * * Since: 1.20 */ -typedef enum { /*< flags >*/ - NM_SETTINGS_ADD_CONNECTION2_FLAG_NONE = 0, - NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK = 0x1, - NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY = 0x2, - NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT = 0x20, +typedef enum /*< flags >*/ { + NM_SETTINGS_ADD_CONNECTION2_FLAG_NONE = 0, + NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK = 0x1, + NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY = 0x2, + NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT = 0x20, } NMSettingsAddConnection2Flags; /** @@ -1131,15 +1131,15 @@ typedef enum { /*< flags >*/ * * Since: 1.12 */ -typedef enum { /*< flags >*/ - NM_SETTINGS_UPDATE2_FLAG_NONE = 0, - NM_SETTINGS_UPDATE2_FLAG_TO_DISK = 0x1, - NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY = 0x2, - NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED = 0x4, - NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY = 0x8, - NM_SETTINGS_UPDATE2_FLAG_VOLATILE = 0x10, - NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT = 0x20, - NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY = 0x40, +typedef enum /*< flags >*/ { + NM_SETTINGS_UPDATE2_FLAG_NONE = 0, + NM_SETTINGS_UPDATE2_FLAG_TO_DISK = 0x1, + NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY = 0x2, + NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED = 0x4, + NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY = 0x8, + NM_SETTINGS_UPDATE2_FLAG_VOLATILE = 0x10, + NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT = 0x20, + NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY = 0x40, } NMSettingsUpdate2Flags; /** @@ -1178,12 +1178,12 @@ typedef enum { * * Since: 1.22 */ -typedef enum { /*< flags >*/ - NM_MANAGER_RELOAD_FLAG_NONE = 0, /*< skip >*/ - NM_MANAGER_RELOAD_FLAG_CONF = 0x1, - NM_MANAGER_RELOAD_FLAG_DNS_RC = 0x2, - NM_MANAGER_RELOAD_FLAG_DNS_FULL = 0x4, - NM_MANAGER_RELOAD_FLAG_ALL = 0x7, /*< skip >*/ +typedef enum /*< flags >*/ { + NM_MANAGER_RELOAD_FLAG_NONE = 0, /*< skip >*/ + NM_MANAGER_RELOAD_FLAG_CONF = 0x1, + NM_MANAGER_RELOAD_FLAG_DNS_RC = 0x2, + NM_MANAGER_RELOAD_FLAG_DNS_FULL = 0x4, + NM_MANAGER_RELOAD_FLAG_ALL = 0x7, /*< skip >*/ } NMManagerReloadFlags; /** @@ -1205,15 +1205,15 @@ typedef enum { /*< flags >*/ * * Since: 1.22 */ -typedef enum { /*< flags >*/ - /* kernel flags */ - NM_DEVICE_INTERFACE_FLAG_NONE = 0, /*< skip >*/ - NM_DEVICE_INTERFACE_FLAG_UP = 0x1, - NM_DEVICE_INTERFACE_FLAG_LOWER_UP = 0x2, - NM_DEVICE_INTERFACE_FLAG_PROMISC = 0x4, - /* NM-specific flags */ - NM_DEVICE_INTERFACE_FLAG_CARRIER = 0x10000, - NM_DEVICE_INTERFACE_FLAG_LLDP_CLIENT_ENABLED = 0x20000, +typedef enum /*< flags >*/ { + /* kernel flags */ + NM_DEVICE_INTERFACE_FLAG_NONE = 0, /*< skip >*/ + NM_DEVICE_INTERFACE_FLAG_UP = 0x1, + NM_DEVICE_INTERFACE_FLAG_LOWER_UP = 0x2, + NM_DEVICE_INTERFACE_FLAG_PROMISC = 0x4, + /* NM-specific flags */ + NM_DEVICE_INTERFACE_FLAG_CARRIER = 0x10000, + NM_DEVICE_INTERFACE_FLAG_LLDP_CLIENT_ENABLED = 0x20000, } NMDeviceInterfaceFlags; /** @@ -1297,4 +1297,22 @@ typedef enum { NM_CLIENT_PERMISSION_RESULT_NO } NMClientPermissionResult; +/** + * NMRadioFlags: + * @NM_RADIO_FLAG_NONE: an alias for numeric zero, no flags set. + * @NM_RADIO_FLAG_WLAN_AVAILABLE: A Wireless LAN device or rfkill switch + * is detected in the system. + * @NM_RADIO_FLAG_WWAN_AVAILABLE: A Wireless WAN device or rfkill switch + * is detected in the system. + * + * Flags related to radio interfaces. + * + * Since: 1.38 + */ +typedef enum /*< flags >*/ { + NM_RADIO_FLAG_NONE = 0, + NM_RADIO_FLAG_WLAN_AVAILABLE = 0x1, + NM_RADIO_FLAG_WWAN_AVAILABLE = 0x2, +} NMRadioFlags; + #endif /* __NM_DBUS_INTERFACE_H__ */ diff --git a/src/libnm-core-public/nm-dbus-types.xml b/src/libnm-core-public/nm-dbus-types.xml index 653859e6..f28ac4cc 100644 --- a/src/libnm-core-public/nm-dbus-types.xml +++ b/src/libnm-core-public/nm-dbus-types.xml @@ -1650,7 +1650,7 @@ <row role="constant"> <entry role="enum_member_name"><para>NM_CHECKPOINT_CREATE_FLAG_NO_PRESERVE_EXTERNAL_PORTS</para><para></para></entry> <entry role="enum_member_value"><para>= <literal>0x10</literal></para><para></para></entry> - <entry role="enum_member_description"><para>during rollback, by default externally added ports attached to bridge devices are preserved. With this flag, the rollback detaches all external ports. This only has an effect for bridge ports. Before 1.38, 1.36.2, this was the default behavior. Since: 1.38, 1.36.2.</para><para></para></entry> + <entry role="enum_member_description"><para>during rollback, by default externally added ports attached to bridge devices are preserved. With this flag, the rollback detaches all external ports. This only has an effect for bridge ports. Before 1.38, this was the default behavior. Since: 1.38.</para><para></para></entry> </row> </tbody> </tgroup> @@ -2188,4 +2188,39 @@ </refsect3> </refsect2> + <refsect2 id="NMRadioFlags" role="enum"> + <title>enum NMRadioFlags</title> + <indexterm zone="NMRadioFlags"> + <primary>NMRadioFlags</primary> + </indexterm> + <para><para>Flags related to radio interfaces.</para><para>Since: 1.38</para><para></para></para> + <refsect3 role="enum_members"> + <title>Values</title> + <informaltable role="enum_members_table" pgwide="1" frame="none"> + <tgroup cols="4"> + <colspec colname="enum_members_name" colwidth="300px" /> + <colspec colname="enum_members_value" colwidth="100px"/> + <colspec colname="enum_members_description" /> + <tbody> + <row role="constant"> + <entry role="enum_member_name"><para>NM_RADIO_FLAG_NONE</para><para></para></entry> + <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry> + <entry role="enum_member_description"><para>an alias for numeric zero, no flags set.</para><para></para></entry> + </row> + <row role="constant"> + <entry role="enum_member_name"><para>NM_RADIO_FLAG_WLAN_AVAILABLE</para><para></para></entry> + <entry role="enum_member_value"><para>= <literal>0x1</literal></para><para></para></entry> + <entry role="enum_member_description"><para>A Wireless LAN device or rfkill switch is detected in the system.</para><para></para></entry> + </row> + <row role="constant"> + <entry role="enum_member_name"><para>NM_RADIO_FLAG_WWAN_AVAILABLE</para><para></para></entry> + <entry role="enum_member_value"><para>= <literal>0x2</literal></para><para></para></entry> + <entry role="enum_member_description"><para>A Wireless WAN device or rfkill switch is detected in the system.</para><para></para></entry> + </row> + </tbody> + </tgroup> + </informaltable> + </refsect3> + </refsect2> + </refentry> diff --git a/src/libnm-core-public/nm-keyfile.h b/src/libnm-core-public/nm-keyfile.h index 8f8b1f1b..80fd0043 100644 --- a/src/libnm-core-public/nm-keyfile.h +++ b/src/libnm-core-public/nm-keyfile.h @@ -21,8 +21,8 @@ G_BEGIN_DECLS * * Since: 1.30 */ -typedef enum { /*< flags >*/ - NM_KEYFILE_HANDLER_FLAGS_NONE = 0, +typedef enum /*< flags >*/ { + NM_KEYFILE_HANDLER_FLAGS_NONE = 0, } NMKeyfileHandlerFlags; /** diff --git a/src/libnm-core-public/nm-setting-8021x.h b/src/libnm-core-public/nm-setting-8021x.h index 2bd089da..93c78fa3 100644 --- a/src/libnm-core-public/nm-setting-8021x.h +++ b/src/libnm-core-public/nm-setting-8021x.h @@ -75,13 +75,13 @@ typedef enum { /*< underscore_name=nm_setting_802_1x_ck_scheme >*/ * * Since: 1.8 */ -typedef enum { /*< flags, underscore_name=nm_setting_802_1x_auth_flags >*/ - NM_SETTING_802_1X_AUTH_FLAGS_NONE = 0, - NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE = 0x1, - NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE = 0x2, - NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_DISABLE = 0x4, +typedef enum /*< underscore_name=nm_setting_802_1x_auth_flags, flags >*/ { + NM_SETTING_802_1X_AUTH_FLAGS_NONE = 0, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE = 0x1, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE = 0x2, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_DISABLE = 0x4, - NM_SETTING_802_1X_AUTH_FLAGS_ALL = 0x7, + NM_SETTING_802_1X_AUTH_FLAGS_ALL = 0x7, } NMSetting8021xAuthFlags; #define NM_TYPE_SETTING_802_1X (nm_setting_802_1x_get_type()) diff --git a/src/libnm-core-public/nm-setting-dcb.h b/src/libnm-core-public/nm-setting-dcb.h index ba702bd7..25676090 100644 --- a/src/libnm-core-public/nm-setting-dcb.h +++ b/src/libnm-core-public/nm-setting-dcb.h @@ -35,11 +35,11 @@ G_BEGIN_DECLS * * DCB feature flags. **/ -typedef enum { /*< flags >*/ - NM_SETTING_DCB_FLAG_NONE = 0x00000000, - NM_SETTING_DCB_FLAG_ENABLE = 0x00000001, - NM_SETTING_DCB_FLAG_ADVERTISE = 0x00000002, - NM_SETTING_DCB_FLAG_WILLING = 0x00000004 +typedef enum /*< flags >*/ { + NM_SETTING_DCB_FLAG_NONE = 0x00000000, + NM_SETTING_DCB_FLAG_ENABLE = 0x00000001, + NM_SETTING_DCB_FLAG_ADVERTISE = 0x00000002, + NM_SETTING_DCB_FLAG_WILLING = 0x00000004 } NMSettingDcbFlags; /** diff --git a/src/libnm-core-public/nm-setting-ip-config.h b/src/libnm-core-public/nm-setting-ip-config.h index 6a19bdb3..257a687a 100644 --- a/src/libnm-core-public/nm-setting-ip-config.h +++ b/src/libnm-core-public/nm-setting-ip-config.h @@ -31,9 +31,9 @@ G_BEGIN_DECLS * * Since: 1.22 */ -typedef enum { /*< flags >*/ - NM_IP_ADDRESS_CMP_FLAGS_NONE = 0, - NM_IP_ADDRESS_CMP_FLAGS_WITH_ATTRS = 0x1, +typedef enum /*< flags >*/ { + NM_IP_ADDRESS_CMP_FLAGS_NONE = 0, + NM_IP_ADDRESS_CMP_FLAGS_WITH_ATTRS = 0x1, } NMIPAddressCmpFlags; typedef struct NMIPAddress NMIPAddress; @@ -86,9 +86,9 @@ void nm_ip_route_ref(NMIPRoute *route); void nm_ip_route_unref(NMIPRoute *route); gboolean nm_ip_route_equal(NMIPRoute *route, NMIPRoute *other); -enum { /*< flags >*/ - NM_IP_ROUTE_EQUAL_CMP_FLAGS_NONE = 0, - NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS = 0x1, +enum /*< flags >*/ { + NM_IP_ROUTE_EQUAL_CMP_FLAGS_NONE = 0, + NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS = 0x1, }; NM_AVAILABLE_IN_1_10 @@ -277,12 +277,12 @@ gboolean nm_ip_routing_rule_validate(const NMIPRoutingRule *self, GError **error * * Since: 1.18 */ -typedef enum { /*< flags >*/ - NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE = 0, +typedef enum /*< flags >*/ { + NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE = 0, - NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET = 0x1, - NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6 = 0x2, - NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE = 0x4, + NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET = 0x1, + NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6 = 0x2, + NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE = 0x4, } NMIPRoutingRuleAsStringFlags; NM_AVAILABLE_IN_1_18 @@ -386,14 +386,14 @@ typedef struct _NMSettingIPConfigClass NMSettingIPConfigClass; * * Since: 1.22 */ -typedef enum { /*< flags >*/ - NM_DHCP_HOSTNAME_FLAG_NONE = 0x0, +typedef enum /*< flags >*/ { + NM_DHCP_HOSTNAME_FLAG_NONE = 0x0, - NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE = 0x1, - NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED = 0x2, - NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE = 0x4, + NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE = 0x1, + NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED = 0x2, + NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE = 0x4, - NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS = 0x8, + NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS = 0x8, } NMDhcpHostnameFlags; diff --git a/src/libnm-core-public/nm-setting-ip-tunnel.h b/src/libnm-core-public/nm-setting-ip-tunnel.h index 4710411c..34920099 100644 --- a/src/libnm-core-public/nm-setting-ip-tunnel.h +++ b/src/libnm-core-public/nm-setting-ip-tunnel.h @@ -60,14 +60,14 @@ typedef struct _NMSettingIPTunnelClass NMSettingIPTunnelClass; * * Since: 1.12 */ -typedef enum { /*< flags, prefix=NM_IP_TUNNEL_FLAG >*/ - NM_IP_TUNNEL_FLAG_NONE = 0x0, - NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT = 0x1, - NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS = 0x2, - NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL = 0x4, - NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV = 0x8, - NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY = 0x10, - NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK = 0x20, +typedef enum /*< prefix=NM_IP_TUNNEL_FLAG, flags >*/ { + NM_IP_TUNNEL_FLAG_NONE = 0x0, + NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT = 0x1, + NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS = 0x2, + NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL = 0x4, + NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV = 0x8, + NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY = 0x10, + NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK = 0x20, } NMIPTunnelFlags; NM_AVAILABLE_IN_1_2 diff --git a/src/libnm-core-public/nm-setting-team.h b/src/libnm-core-public/nm-setting-team.h index 339830d3..63df4080 100644 --- a/src/libnm-core-public/nm-setting-team.h +++ b/src/libnm-core-public/nm-setting-team.h @@ -26,11 +26,11 @@ G_BEGIN_DECLS * @NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS: the arp_ping link watcher option * 'send_always' is enabled (set to true). */ -typedef enum { /*< flags >*/ - NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE = 0, /*< skip >*/ - NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE = 0x2, - NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE = 0x4, - NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS = 0x8, +typedef enum /*< flags >*/ { + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE = 0, /*< skip >*/ + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE = 0x2, + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE = 0x4, + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS = 0x8, } NMTeamLinkWatcherArpPingFlags; #define NM_TEAM_LINK_WATCHER_ETHTOOL "ethtool" diff --git a/src/libnm-core-public/nm-setting-vlan.h b/src/libnm-core-public/nm-setting-vlan.h index 10a95da0..a99d7256 100644 --- a/src/libnm-core-public/nm-setting-vlan.h +++ b/src/libnm-core-public/nm-setting-vlan.h @@ -42,12 +42,10 @@ typedef struct _NMSettingVlanClass NMSettingVlanClass; * A selector for traffic priority maps; these map Linux SKB priorities * to 802.1p priorities used in VLANs. **/ -/* clang-format off */ typedef enum { NM_VLAN_INGRESS_MAP, - NM_VLAN_EGRESS_MAP + NM_VLAN_EGRESS_MAP, } NMVlanPriorityMap; -/* clang-format on */ /** * NMVlanFlags: @@ -63,17 +61,17 @@ typedef enum { * * #NMVlanFlags values control the behavior of the VLAN interface. **/ -typedef enum { /*< flags >*/ +typedef enum /*< flags >*/ { - NM_VLAN_FLAG_REORDER_HEADERS = 0x1, - NM_VLAN_FLAG_GVRP = 0x2, - NM_VLAN_FLAG_LOOSE_BINDING = 0x4, - NM_VLAN_FLAG_MVRP = 0x8, + NM_VLAN_FLAG_REORDER_HEADERS = 0x1, + NM_VLAN_FLAG_GVRP = 0x2, + NM_VLAN_FLAG_LOOSE_BINDING = 0x4, + NM_VLAN_FLAG_MVRP = 0x8, - /* NOTE: if adding flags update nm-setting-vlan.c::verify() */ + /* NOTE: if adding flags update nm-setting-vlan.c::verify() */ - /* NOTE: these flags must correspond to the value from the kernel - * header files. */ + /* NOTE: these flags must correspond to the value from the kernel + * header files. */ } NMVlanFlags; #define NM_VLAN_FLAGS_ALL \ diff --git a/src/libnm-core-public/nm-setting-wired.h b/src/libnm-core-public/nm-setting-wired.h index ebe0d00a..5ad3b7f2 100644 --- a/src/libnm-core-public/nm-setting-wired.h +++ b/src/libnm-core-public/nm-setting-wired.h @@ -49,20 +49,20 @@ G_BEGIN_DECLS * * Since: 1.2 */ -typedef enum { /*< flags >*/ - NM_SETTING_WIRED_WAKE_ON_LAN_NONE = 0, /*< skip >*/ - NM_SETTING_WIRED_WAKE_ON_LAN_PHY = 0x2, - NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST = 0x4, - NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST = 0x8, - NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST = 0x10, - NM_SETTING_WIRED_WAKE_ON_LAN_ARP = 0x20, - NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC = 0x40, - - NM_SETTING_WIRED_WAKE_ON_LAN_ALL = 0x7E, /*< skip >*/ - - NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT = 0x1, - NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE = 0x8000, - NM_SETTING_WIRED_WAKE_ON_LAN_EXCLUSIVE_FLAGS = 0x8001, /*< skip >*/ +typedef enum /*< flags >*/ { + NM_SETTING_WIRED_WAKE_ON_LAN_NONE = 0, /*< skip >*/ + NM_SETTING_WIRED_WAKE_ON_LAN_PHY = 0x2, + NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST = 0x4, + NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST = 0x8, + NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST = 0x10, + NM_SETTING_WIRED_WAKE_ON_LAN_ARP = 0x20, + NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC = 0x40, + + NM_SETTING_WIRED_WAKE_ON_LAN_ALL = 0x7E, /*< skip >*/ + + NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT = 0x1, + NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE = 0x8000, + NM_SETTING_WIRED_WAKE_ON_LAN_EXCLUSIVE_FLAGS = 0x8001, /*< skip >*/ } NMSettingWiredWakeOnLan; #define NM_SETTING_WIRED_PORT "port" diff --git a/src/libnm-core-public/nm-setting-wireless-security.h b/src/libnm-core-public/nm-setting-wireless-security.h index c9aec27d..e564204b 100644 --- a/src/libnm-core-public/nm-setting-wireless-security.h +++ b/src/libnm-core-public/nm-setting-wireless-security.h @@ -99,12 +99,12 @@ typedef enum { * * Since: 1.10 **/ -typedef enum { /*< flags >*/ - NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT = 0x00000000, - NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED = 0x00000001, - NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_AUTO = 0x00000002, - NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PBC = 0x00000004, - NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN = 0x00000008, +typedef enum /*< flags >*/ { + NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT = 0x00000000, + NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED = 0x00000001, + NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_AUTO = 0x00000002, + NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PBC = 0x00000004, + NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN = 0x00000008, } NMSettingWirelessSecurityWpsMethod; /** diff --git a/src/libnm-core-public/nm-setting-wireless.h b/src/libnm-core-public/nm-setting-wireless.h index 0d4872fb..1f94060c 100644 --- a/src/libnm-core-public/nm-setting-wireless.h +++ b/src/libnm-core-public/nm-setting-wireless.h @@ -53,7 +53,7 @@ G_BEGIN_DECLS * Since: 1.12 */ /* clang-format off */ -typedef enum { /*< flags >*/ +typedef enum /*< flags >*/ { NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE = 0, /*< skip >*/ NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY = 0x2, NM_SETTING_WIRELESS_WAKE_ON_WLAN_DISCONNECT = 0x4, diff --git a/src/libnm-core-public/nm-setting.h b/src/libnm-core-public/nm-setting.h index ddf648bc..ee2076c3 100644 --- a/src/libnm-core-public/nm-setting.h +++ b/src/libnm-core-public/nm-setting.h @@ -57,13 +57,13 @@ G_BEGIN_DECLS * is to be stored and/or requested when it is needed. * **/ -typedef enum { /*< flags >*/ - NM_SETTING_SECRET_FLAG_NONE = 0x00000000, - NM_SETTING_SECRET_FLAG_AGENT_OWNED = 0x00000001, - NM_SETTING_SECRET_FLAG_NOT_SAVED = 0x00000002, - NM_SETTING_SECRET_FLAG_NOT_REQUIRED = 0x00000004 +typedef enum /*< flags >*/ { + NM_SETTING_SECRET_FLAG_NONE = 0x00000000, + NM_SETTING_SECRET_FLAG_AGENT_OWNED = 0x00000001, + NM_SETTING_SECRET_FLAG_NOT_SAVED = 0x00000002, + NM_SETTING_SECRET_FLAG_NOT_REQUIRED = 0x00000004 - /* NOTE: if adding flags, update nm-core-internal.h as well */ + /* NOTE: if adding flags, update nm-core-internal.h as well */ } NMSettingSecretFlags; /** diff --git a/src/libnm-core-public/nm-utils.h b/src/libnm-core-public/nm-utils.h index 0a7c7a81..5faed75a 100644 --- a/src/libnm-core-public/nm-utils.h +++ b/src/libnm-core-public/nm-utils.h @@ -111,10 +111,6 @@ GPtrArray *nm_utils_ip_routes_from_variant(GVariant *value, int family); char *nm_utils_uuid_generate(void); -gboolean nm_utils_file_is_certificate(const char *filename); -gboolean nm_utils_file_is_private_key(const char *filename, gboolean *out_encrypted); -gboolean nm_utils_file_is_pkcs12(const char *filename); - typedef gboolean (*NMUtilsFileSearchInPathsPredicate)(const char *filename, gpointer user_data); struct stat; diff --git a/src/libnm-core-public/nm-version-macros.h b/src/libnm-core-public/nm-version-macros.h index 0aff11b9..b10be844 100644 --- a/src/libnm-core-public/nm-version-macros.h +++ b/src/libnm-core-public/nm-version-macros.h @@ -22,7 +22,7 @@ * Evaluates to the minor version number of NetworkManager which this source * is compiled against. */ -#define NM_MINOR_VERSION (36) +#define NM_MINOR_VERSION (37) /** * NM_MICRO_VERSION: @@ -30,7 +30,7 @@ * Evaluates to the micro version number of NetworkManager which this source * compiled against. */ -#define NM_MICRO_VERSION (4) +#define NM_MICRO_VERSION (92) /** * NM_CHECK_VERSION: @@ -70,6 +70,7 @@ #define NM_VERSION_1_32 (NM_ENCODE_VERSION (1, 32, 0)) #define NM_VERSION_1_34 (NM_ENCODE_VERSION (1, 34, 0)) #define NM_VERSION_1_36 (NM_ENCODE_VERSION (1, 36, 0)) +#define NM_VERSION_1_38 (NM_ENCODE_VERSION (1, 38, 0)) /* For releases, NM_API_VERSION is equal to NM_VERSION. * diff --git a/src/libnm-core-public/nm-version-macros.h.in b/src/libnm-core-public/nm-version-macros.h.in index 76dd4240..516aded5 100644 --- a/src/libnm-core-public/nm-version-macros.h.in +++ b/src/libnm-core-public/nm-version-macros.h.in @@ -70,6 +70,7 @@ #define NM_VERSION_1_32 (NM_ENCODE_VERSION (1, 32, 0)) #define NM_VERSION_1_34 (NM_ENCODE_VERSION (1, 34, 0)) #define NM_VERSION_1_36 (NM_ENCODE_VERSION (1, 36, 0)) +#define NM_VERSION_1_38 (NM_ENCODE_VERSION (1, 38, 0)) /* For releases, NM_API_VERSION is equal to NM_VERSION. * diff --git a/src/libnm-core-public/nm-version.h b/src/libnm-core-public/nm-version.h index d6f58924..ac98f868 100644 --- a/src/libnm-core-public/nm-version.h +++ b/src/libnm-core-public/nm-version.h @@ -313,6 +313,20 @@ #define NM_AVAILABLE_IN_1_36 #endif +#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_1_38 +#define NM_DEPRECATED_IN_1_38 G_DEPRECATED +#define NM_DEPRECATED_IN_1_38_FOR(f) G_DEPRECATED_FOR(f) +#else +#define NM_DEPRECATED_IN_1_38 +#define NM_DEPRECATED_IN_1_38_FOR(f) +#endif + +#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_38 +#define NM_AVAILABLE_IN_1_38 G_UNAVAILABLE(1, 38) +#else +#define NM_AVAILABLE_IN_1_38 +#endif + /* * Synchronous API for calling D-Bus in libnm is deprecated. See * https://developer.gnome.org/libnm/stable/usage.html#sync-api diff --git a/src/libnm-crypto/README.md b/src/libnm-crypto/README.md new file mode 100644 index 00000000..5e83eb01 --- /dev/null +++ b/src/libnm-crypto/README.md @@ -0,0 +1,7 @@ +libnm-crypto +============ + +libnm-core has a dependency on crypto code (either backed by +"gnutls", "nss" or the "null" dummy implementation). + +libnm-core gets then statically linked into the daemon and into libnm.so. diff --git a/src/libnm-crypto/meson.build b/src/libnm-crypto/meson.build new file mode 100644 index 00000000..9134c929 --- /dev/null +++ b/src/libnm-crypto/meson.build @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +if crypto_nss_dep.found() + libnm_crypto_nss = static_library( + 'nm-crypto-nss', + sources: 'nm-crypto-nss.c', + include_directories: [ + top_inc, + src_inc, + ], + dependencies: [ + glib_dep, + crypto_nss_dep, + ], + ) +endif + +if crypto_gnutls_dep.found() + libnm_crypto_gnutls = static_library( + 'nm-crypto-gnutls', + sources: 'nm-crypto-gnutls.c', + include_directories: [ + top_inc, + src_inc, + ], + dependencies: [ + glib_dep, + crypto_gnutls_dep, + ], + ) +endif + +libnm_crypto_null = static_library( + 'nm-crypto-null', + sources: 'nm-crypto-null.c', + include_directories: [ + top_inc, + src_inc, + ], + dependencies: [ + glib_dep, + ], +) + +if crypto == 'nss' + libnm_crypto_impl = libnm_crypto_nss +elif crypto == 'gnutls' + libnm_crypto_impl = libnm_crypto_gnutls +else + assert(crypto == 'null', 'Unexpected setting "crypto=' + crypto + '"') + libnm_crypto_impl = libnm_crypto_null +endif + +libnm_crypto = static_library( + 'nm-crypto', + sources: [ + 'nm-crypto.c', + ], + include_directories: [ + top_inc, + src_inc, + ], + link_with: [ + libnm_crypto_impl, + ], + dependencies: [ + glib_dep, + ], +) diff --git a/src/libnm-core-impl/nm-crypto-gnutls.c b/src/libnm-crypto/nm-crypto-gnutls.c index d9e59136..60adf4d3 100644 --- a/src/libnm-core-impl/nm-crypto-gnutls.c +++ b/src/libnm-crypto/nm-crypto-gnutls.c @@ -14,7 +14,6 @@ #include <gnutls/pkcs12.h> #include "libnm-glib-aux/nm-secret-utils.h" -#include "nm-errors.h" /*****************************************************************************/ @@ -54,8 +53,8 @@ _nm_crypto_init(GError **error) if (gnutls_global_init() != 0) { gnutls_global_deinit(); g_set_error_literal(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, _("Failed to initialize the crypto engine.")); return FALSE; } @@ -87,8 +86,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, if (!_get_cipher_info(cipher, &cipher_mech, &real_iv_len)) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_UNKNOWN_CIPHER, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_UNKNOWN_CIPHER, _("Unsupported key cipher for decryption")); return NULL; } @@ -98,8 +97,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, if (iv_len < real_iv_len) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Invalid IV length (must be at least %u)."), (guint) real_iv_len); return NULL; @@ -116,8 +115,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, err = gnutls_cipher_init(&ctx, cipher_mech, &key_dt, &iv_dt); if (err < 0) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to initialize the decryption cipher context: %s (%s)"), gnutls_strerror_name(err), gnutls_strerror(err)); @@ -130,8 +129,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, if (err < 0) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to decrypt the private key: %s (%s)"), gnutls_strerror_name(err), gnutls_strerror(err)); @@ -143,8 +142,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, /* Check if the padding at the end of the decrypted data is valid */ if (pad_len == 0 || pad_len > real_iv_len) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to decrypt the private key: unexpected padding length.")); return NULL; } @@ -155,8 +154,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, for (pad_i = 1; pad_i <= pad_len; ++pad_i) { if (output.bin[data_len - pad_i] != pad_len) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to decrypt the private key.")); return NULL; } @@ -189,8 +188,8 @@ _nmtst_crypto_encrypt(NMCryptoCipherType cipher, if (cipher == NM_CRYPTO_CIPHER_DES_CBC || !_get_cipher_info(cipher, &cipher_mech, NULL)) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_UNKNOWN_CIPHER, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_UNKNOWN_CIPHER, _("Unsupported key cipher for encryption")); return NULL; } @@ -206,8 +205,8 @@ _nmtst_crypto_encrypt(NMCryptoCipherType cipher, err = gnutls_cipher_init(&ctx, cipher_mech, &key_dt, &iv_dt); if (err < 0) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_ENCRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Failed to initialize the encryption cipher context: %s (%s)"), gnutls_strerror_name(err), gnutls_strerror(err)); @@ -234,8 +233,8 @@ _nmtst_crypto_encrypt(NMCryptoCipherType cipher, if (err < 0) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_ENCRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Failed to encrypt the data: %s (%s)"), gnutls_strerror_name(err), gnutls_strerror(err)); @@ -259,8 +258,8 @@ _nm_crypto_verify_x509(const guint8 *data, gsize len, GError **error) err = gnutls_x509_crt_init(&der); if (err < 0) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Error initializing certificate data: %s"), gnutls_strerror(err)); return FALSE; @@ -282,8 +281,8 @@ _nm_crypto_verify_x509(const guint8 *data, gsize len, GError **error) return TRUE; g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Couldn't decode certificate: %s"), gnutls_strerror(err)); return FALSE; @@ -307,8 +306,8 @@ _nm_crypto_verify_pkcs12(const guint8 *data, gsize data_len, const char *passwor err = gnutls_pkcs12_init(&p12); if (err < 0) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, _("Couldn't initialize PKCS#12 decoder: %s"), gnutls_strerror(err)); return FALSE; @@ -321,8 +320,8 @@ _nm_crypto_verify_pkcs12(const guint8 *data, gsize data_len, const char *passwor err = gnutls_pkcs12_import(p12, &dt, GNUTLS_X509_FMT_PEM, 0); if (err < 0) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Couldn't decode PKCS#12 file: %s"), gnutls_strerror(err)); gnutls_pkcs12_deinit(p12); @@ -331,18 +330,18 @@ _nm_crypto_verify_pkcs12(const guint8 *data, gsize data_len, const char *passwor } err = gnutls_pkcs12_verify_mac(p12, password); - - gnutls_pkcs12_deinit(p12); - if (err != GNUTLS_E_SUCCESS) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Couldn't verify PKCS#12 file: %s"), gnutls_strerror(err)); + gnutls_pkcs12_deinit(p12); return FALSE; } + gnutls_pkcs12_deinit(p12); + return TRUE; } @@ -365,8 +364,8 @@ _nm_crypto_verify_pkcs8(const guint8 *data, err = gnutls_x509_privkey_init(&p8); if (err < 0) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, _("Couldn't initialize PKCS#8 decoder: %s"), gnutls_strerror(err)); return FALSE; @@ -393,8 +392,8 @@ _nm_crypto_verify_pkcs8(const guint8 *data, */ } else { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Couldn't decode PKCS#8 file: %s"), gnutls_strerror(err)); return FALSE; diff --git a/src/libnm-core-impl/nm-crypto-impl.h b/src/libnm-crypto/nm-crypto-impl.h index 61c3f7f8..0b7c14dc 100644 --- a/src/libnm-core-impl/nm-crypto-impl.h +++ b/src/libnm-crypto/nm-crypto-impl.h @@ -8,6 +8,7 @@ #define __NM_CRYPTO_IMPL_H__ #include "nm-crypto.h" +#include "libnm-base/nm-base.h" gboolean _nm_crypto_init(GError **error); diff --git a/src/libnm-core-impl/nm-crypto-nss.c b/src/libnm-crypto/nm-crypto-nss.c index c27bf09e..b31ca55e 100644 --- a/src/libnm-core-impl/nm-crypto-nss.c +++ b/src/libnm-crypto/nm-crypto-nss.c @@ -21,7 +21,7 @@ NM_PRAGMA_WARNING_DISABLE("-Wstrict-prototypes") NM_PRAGMA_WARNING_REENABLE #include "libnm-glib-aux/nm-secret-utils.h" -#include "nm-errors.h" +#include "libnm-base/nm-base.h" /*****************************************************************************/ @@ -65,8 +65,8 @@ _nm_crypto_init(GError **error) ret = NSS_NoDB_Init(NULL); if (ret != SECSuccess) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, _("Failed to initialize the crypto engine: %d."), PR_GetError()); PR_Cleanup(); @@ -113,16 +113,16 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, if (!_get_cipher_info(cipher, &cipher_mech, &real_iv_len)) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_UNKNOWN_CIPHER, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_UNKNOWN_CIPHER, _("Unsupported key cipher for decryption")); return NULL; } if (iv_len < real_iv_len) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Invalid IV length (must be at least %u)."), (guint) real_iv_len); return NULL; @@ -134,8 +134,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, slot = PK11_GetBestSlot(cipher_mech, NULL); if (!slot) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, _("Failed to initialize the decryption cipher slot.")); goto out; } @@ -145,8 +145,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, sym_key = PK11_ImportSymKey(slot, cipher_mech, PK11_OriginUnwrap, CKA_DECRYPT, &key_item, NULL); if (!sym_key) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to set symmetric key for decryption.")); goto out; } @@ -156,8 +156,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, sec_param = PK11_ParamFromIV(cipher_mech, &key_item); if (!sec_param) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to set IV for decryption.")); goto out; } @@ -165,8 +165,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, ctx = PK11_CreateContextBySymKey(cipher_mech, CKA_DECRYPT, sym_key, sec_param); if (!ctx) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to initialize the decryption context.")); goto out; } @@ -182,8 +182,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, data_len); if (s != SECSuccess) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to decrypt the private key: %d."), PORT_GetError()); goto out; @@ -191,8 +191,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, if (decrypted_len > data_len) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to decrypt the private key: decrypted data too large.")); goto out; } @@ -203,8 +203,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, data_len - decrypted_len); if (s != SECSuccess) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to finalize decryption of the private key: %d."), PORT_GetError()); goto out; @@ -216,8 +216,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, /* Check if the padding at the end of the decrypted data is valid */ if (pad_len == 0 || pad_len > real_iv_len) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to decrypt the private key: unexpected padding length.")); goto out; } @@ -228,8 +228,8 @@ _nmtst_crypto_decrypt(NMCryptoCipherType cipher, for (i = pad_len; i > 0; i--) { if (output.bin[data_len - i] != pad_len) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to decrypt the private key.")); goto out; } @@ -283,8 +283,8 @@ _nmtst_crypto_encrypt(NMCryptoCipherType cipher, if (cipher == NM_CRYPTO_CIPHER_DES_CBC || !_get_cipher_info(cipher, &cipher_mech, NULL)) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_UNKNOWN_CIPHER, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_UNKNOWN_CIPHER, _("Unsupported key cipher for encryption")); return NULL; } @@ -295,8 +295,8 @@ _nmtst_crypto_encrypt(NMCryptoCipherType cipher, slot = PK11_GetBestSlot(cipher_mech, NULL); if (!slot) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, _("Failed to initialize the encryption cipher slot.")); return NULL; } @@ -304,8 +304,8 @@ _nmtst_crypto_encrypt(NMCryptoCipherType cipher, sym_key = PK11_ImportSymKey(slot, cipher_mech, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL); if (!sym_key) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_ENCRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Failed to set symmetric key for encryption.")); goto out; } @@ -313,8 +313,8 @@ _nmtst_crypto_encrypt(NMCryptoCipherType cipher, sec_param = PK11_ParamFromIV(cipher_mech, &iv_item); if (!sec_param) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_ENCRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Failed to set IV for encryption.")); goto out; } @@ -322,8 +322,8 @@ _nmtst_crypto_encrypt(NMCryptoCipherType cipher, ctx = PK11_CreateContextBySymKey(cipher_mech, CKA_ENCRYPT, sym_key, sec_param); if (!ctx) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_ENCRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Failed to initialize the encryption context.")); goto out; } @@ -347,8 +347,8 @@ _nmtst_crypto_encrypt(NMCryptoCipherType cipher, PK11_CipherOp(ctx, output.bin, &encrypted_len, output.len, padded_buf.bin, padded_buf.len); if (ret != SECSuccess) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_ENCRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Failed to encrypt: %d."), PORT_GetError()); goto out; @@ -356,8 +356,8 @@ _nmtst_crypto_encrypt(NMCryptoCipherType cipher, if (encrypted_len != output.len) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_ENCRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Unexpected amount of data after encrypting.")); goto out; } @@ -393,8 +393,8 @@ _nm_crypto_verify_x509(const guint8 *data, gsize len, GError **error) cert = CERT_DecodeCertFromPackage((char *) data, len); if (!cert) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Couldn't decode certificate: %d"), PORT_GetError()); return FALSE; @@ -438,8 +438,8 @@ _nm_crypto_verify_pkcs12(const guint8 *data, gsize data_len, const char *passwor if (!ucs2_password.bin || ucs2_password.len == 0) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_PASSWORD, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_PASSWORD, _("Password must be UTF-8")); return FALSE; } @@ -461,15 +461,18 @@ _nm_crypto_verify_pkcs12(const guint8 *data, gsize data_len, const char *passwor slot = PK11_GetInternalKeySlot(); if (!slot) { - g_set_error(error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_FAILED, _("Couldn't initialize slot")); + g_set_error(error, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, + _("Couldn't initialize slot")); goto out; } p12ctx = SEC_PKCS12DecoderStart(&pw, slot, NULL, NULL, NULL, NULL, NULL, NULL); if (!p12ctx) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, _("Couldn't initialize PKCS#12 decoder: %d"), PORT_GetError()); goto out; @@ -478,8 +481,8 @@ _nm_crypto_verify_pkcs12(const guint8 *data, gsize data_len, const char *passwor s = SEC_PKCS12DecoderUpdate(p12ctx, (guint8 *) data, data_len); if (s != SECSuccess) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Couldn't decode PKCS#12 file: %d"), PORT_GetError()); goto out; @@ -488,8 +491,8 @@ _nm_crypto_verify_pkcs12(const guint8 *data, gsize data_len, const char *passwor s = SEC_PKCS12DecoderVerify(p12ctx); if (s != SECSuccess) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Couldn't verify PKCS#12 file: %d"), PORT_GetError()); goto out; @@ -506,6 +509,7 @@ out: if (pw.data) SECITEM_ZfreeItem(&pw, PR_FALSE); + nm_assert(!error || (success == (!*error))); return success; } @@ -539,8 +543,8 @@ _nm_crypto_randomize(void *buffer, gsize buffer_len, GError **error) s = PK11_GenerateRandom(buffer, buffer_len); if (s != SECSuccess) { g_set_error_literal(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_FAILED, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, _("Could not generate random data.")); return FALSE; } diff --git a/src/libnm-crypto/nm-crypto-null.c b/src/libnm-crypto/nm-crypto-null.c new file mode 100644 index 00000000..6f6c7f28 --- /dev/null +++ b/src/libnm-crypto/nm-crypto-null.c @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Christian Eggers <ceggers@arri.de> + * Copyright (C) 2020 - 2022 ARRI Lighting + */ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nm-crypto-impl.h" + +#include "libnm-glib-aux/nm-secret-utils.h" + +/*****************************************************************************/ + +gboolean +_nm_crypto_init(GError **error) +{ + g_set_error(error, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, + _("Compiled without crypto support.")); + return FALSE; +} + +guint8 * +_nmtst_crypto_decrypt(NMCryptoCipherType cipher, + const guint8 *data, + gsize data_len, + const guint8 *iv, + gsize iv_len, + const guint8 *key, + gsize key_len, + gsize *out_len, + GError **error) +{ + g_set_error(error, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, + _("Compiled without crypto support.")); + return NULL; +} + +guint8 * +_nmtst_crypto_encrypt(NMCryptoCipherType cipher, + const guint8 *data, + gsize data_len, + const guint8 *iv, + gsize iv_len, + const guint8 *key, + gsize key_len, + gsize *out_len, + GError **error) +{ + g_set_error(error, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, + _("Compiled without crypto support.")); + return NULL; +} + +gboolean +_nm_crypto_verify_x509(const guint8 *data, gsize len, GError **error) +{ + g_set_error(error, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, + _("Compiled without crypto support.")); + return FALSE; +} + +gboolean +_nm_crypto_verify_pkcs12(const guint8 *data, gsize data_len, const char *password, GError **error) +{ + g_set_error(error, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, + _("Compiled without crypto support.")); + return FALSE; +} + +gboolean +_nm_crypto_verify_pkcs8(const guint8 *data, + gsize data_len, + gboolean is_encrypted, + const char *password, + GError **error) +{ + g_set_error(error, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, + _("Compiled without crypto support.")); + return FALSE; +} + +gboolean +_nm_crypto_randomize(void *buffer, gsize buffer_len, GError **error) +{ + g_set_error(error, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_FAILED, + _("Compiled without crypto support.")); + return FALSE; +} diff --git a/src/libnm-core-impl/nm-crypto.c b/src/libnm-crypto/nm-crypto.c index 78f793bd..69d2b53f 100644 --- a/src/libnm-core-impl/nm-crypto.c +++ b/src/libnm-crypto/nm-crypto.c @@ -4,7 +4,7 @@ * Copyright (C) 2007 - 2018 Red Hat, Inc. */ -#include "libnm-core-impl/nm-default-libnm-core.h" +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-crypto.h" @@ -16,8 +16,8 @@ #include "libnm-glib-aux/nm-io-utils.h" #include "nm-crypto-impl.h" -#include "nm-utils.h" -#include "nm-errors.h" + +/*****************************************************************************/ #define PEM_RSA_KEY_BEGIN "-----BEGIN RSA PRIVATE KEY-----" #define PEM_RSA_KEY_END "-----END RSA PRIVATE KEY-----" @@ -203,8 +203,8 @@ parse_old_openssl_key_file(const guint8 *data, end_tag = PEM_DSA_KEY_END; } else { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("PEM key file had no start tag")); return FALSE; } @@ -212,8 +212,8 @@ parse_old_openssl_key_file(const guint8 *data, start += strlen(start_tag); if (!find_tag(end_tag, data, data_len, start, &end)) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("PEM key file had no end tag '%s'."), end_tag); return FALSE; @@ -240,8 +240,8 @@ parse_old_openssl_key_file(const guint8 *data, if (!strncmp(p, PROC_TYPE_TAG, strlen(PROC_TYPE_TAG))) { if (enc_tags++ != 0 || str_p != str) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: Proc-Type was not first tag.")); return FALSE; } @@ -249,8 +249,8 @@ parse_old_openssl_key_file(const guint8 *data, p += strlen(PROC_TYPE_TAG); if (strcmp(p, "4,ENCRYPTED")) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: unknown Proc-Type tag '%s'."), p); return FALSE; @@ -262,8 +262,8 @@ parse_old_openssl_key_file(const guint8 *data, if (enc_tags++ != 1 || str_p != str) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: DEK-Info was not the second tag.")); return FALSE; } @@ -274,8 +274,8 @@ parse_old_openssl_key_file(const guint8 *data, comma = strchr(p, ','); if (!comma || (*(comma + 1) == '\0')) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: no IV found in DEK-Info tag.")); return FALSE; } @@ -283,8 +283,8 @@ parse_old_openssl_key_file(const guint8 *data, comma++; if (!g_ascii_isxdigit(*comma)) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: invalid format of IV in DEK-Info tag.")); return FALSE; } @@ -295,8 +295,8 @@ parse_old_openssl_key_file(const guint8 *data, cipher_info = nm_crypto_cipher_get_info_by_name(p, p_len); if (!cipher_info) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: unknown private key cipher '%s'."), p); return FALSE; @@ -305,8 +305,8 @@ parse_old_openssl_key_file(const guint8 *data, } else { if (enc_tags == 1) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, "Malformed PEM file: both Proc-Type and DEK-Info tags are required."); return FALSE; } @@ -318,8 +318,8 @@ parse_old_openssl_key_file(const guint8 *data, parsed.bin = (guint8 *) g_base64_decode(str, &parsed.len); if (!parsed.bin || parsed.len == 0) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Could not decode private key.")); nm_secret_ptr_clear(&parsed); return FALSE; @@ -360,8 +360,8 @@ parse_pkcs8_key_file(const guint8 *data, encrypted = FALSE; } else { g_set_error_literal(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Failed to find expected PKCS#8 start tag.")); return FALSE; } @@ -369,8 +369,8 @@ parse_pkcs8_key_file(const guint8 *data, start += strlen(start_tag); if (!find_tag(end_tag, data, data_len, start, &end)) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Failed to find expected PKCS#8 end tag '%s'."), end_tag); return FALSE; @@ -382,8 +382,8 @@ parse_pkcs8_key_file(const guint8 *data, parsed->bin = (guint8 *) g_base64_decode(der_base64, &parsed->len); if (!parsed->bin || parsed->len == 0) { g_set_error_literal(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Failed to decode PKCS#8 private key.")); nm_secret_ptr_clear(parsed); return FALSE; @@ -412,8 +412,8 @@ parse_tpm2_wrapped_key_file(const guint8 *data, end_tag = PEM_TPM2_OLD_WRAPPED_KEY_END; } else { g_set_error_literal(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Failed to find expected TSS start tag.")); return FALSE; } @@ -421,8 +421,8 @@ parse_tpm2_wrapped_key_file(const guint8 *data, start += strlen(start_tag); if (!find_tag(end_tag, data, data_len, start, &end)) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Failed to find expected TSS end tag '%s'."), end_tag); return FALSE; @@ -432,35 +432,6 @@ parse_tpm2_wrapped_key_file(const guint8 *data, return TRUE; } -static gboolean -file_read_contents(const char *filename, NMSecretPtr *out_contents, GError **error) -{ - nm_assert(out_contents); - nm_assert(out_contents->len == 0); - nm_assert(!out_contents->str); - - return nm_utils_file_get_contents(-1, - filename, - 100 * 1024 * 1024, - NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET, - &out_contents->str, - &out_contents->len, - NULL, - error); -} - -GBytes * -nm_crypto_read_file(const char *filename, GError **error) -{ - nm_auto_clear_secret_ptr NMSecretPtr contents = {0}; - - g_return_val_if_fail(filename, NULL); - - if (!file_read_contents(filename, &contents, error)) - return NULL; - return nm_secret_copy_to_gbytes(contents.bin, contents.len); -} - /* * Convert a hex string into bytes. */ @@ -476,8 +447,8 @@ _nmtst_convert_iv(const char *src, gsize *out_len, GError **error) num = strlen(src); if (num == 0 || (num % 2) != 0) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("IV must be an even number of bytes in length.")); return NULL; } @@ -493,8 +464,8 @@ _nmtst_convert_iv(const char *src, gsize *out_len, GError **error) if (((c0 = nm_utils_hexchar_to_int(*(src++))) < 0) || ((c1 = nm_utils_hexchar_to_int(*(src++))) < 0)) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("IV contains non-hexadecimal digits.")); nm_explicit_bzero(c, i); return FALSE; @@ -569,8 +540,8 @@ _nmtst_decrypt_key(NMCryptoCipherType cipher, if (bin_iv.len < 8) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("IV must contain at least 8 characters")); return FALSE; } @@ -619,8 +590,8 @@ nmtst_crypto_decrypt_openssl_private_key_data(const guint8 *data, if (!parse_old_openssl_key_file(data, data_len, &parsed, &key_type, &cipher, &iv, NULL)) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Unable to determine private key type.")); return NULL; } @@ -632,8 +603,8 @@ nmtst_crypto_decrypt_openssl_private_key_data(const guint8 *data, if (cipher == NM_CRYPTO_CIPHER_UNKNOWN || !iv) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_PASSWORD, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_PASSWORD, _("Password provided, but key was not encrypted.")); return NULL; } @@ -661,7 +632,7 @@ nmtst_crypto_decrypt_openssl_private_key(const char *file, if (!_nm_crypto_init(error)) return NULL; - if (!file_read_contents(file, &contents, error)) + if (!nm_utils_read_crypto_file(file, &contents, error)) return NULL; return nmtst_crypto_decrypt_openssl_private_key_data(contents.bin, @@ -688,8 +659,8 @@ extract_pem_cert_data(const guint8 *contents, if (!find_tag(PEM_CERT_BEGIN, contents, contents_len, 0, &start)) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("PEM certificate had no start tag '%s'."), PEM_CERT_BEGIN); return FALSE; @@ -698,8 +669,8 @@ extract_pem_cert_data(const guint8 *contents, start += strlen(PEM_CERT_BEGIN); if (!find_tag(PEM_CERT_END, contents, contents_len, start, &end)) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("PEM certificate had no end tag '%s'."), PEM_CERT_END); return FALSE; @@ -711,8 +682,8 @@ extract_pem_cert_data(const guint8 *contents, out_cert->bin = (guint8 *) g_base64_decode(der_base64, &out_cert->len); if (!out_cert->bin || !out_cert->len) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Failed to decode certificate.")); nm_secret_ptr_clear(out_cert); return FALSE; @@ -735,13 +706,13 @@ nm_crypto_load_and_verify_certificate(const char *file, if (!_nm_crypto_init(error)) goto out; - if (!file_read_contents(file, &contents, error)) + if (!nm_utils_read_crypto_file(file, &contents, error)) goto out; if (contents.len == 0) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Certificate file is empty")); goto out; } @@ -773,8 +744,8 @@ nm_crypto_load_and_verify_certificate(const char *file, } g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Failed to recognize certificate")); out: @@ -786,13 +757,13 @@ out: gboolean nm_crypto_is_pkcs12_data(const guint8 *data, gsize data_len, GError **error) { - GError *local = NULL; - gboolean success; + gs_free_error GError *local = NULL; + gboolean success; if (!data_len) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("Certificate file is empty")); return FALSE; } @@ -803,17 +774,14 @@ nm_crypto_is_pkcs12_data(const guint8 *data, gsize data_len, GError **error) return FALSE; success = _nm_crypto_verify_pkcs12(data, data_len, NULL, &local); - if (success == FALSE) { - /* If the error was just a decryption error, then it's pkcs#12 */ - if (local) { - if (g_error_matches(local, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_DECRYPTION_FAILED)) { - success = TRUE; - g_error_free(local); - } else - g_propagate_error(error, local); - } + + /* If the error was just a decryption error, then it's pkcs#12 */ + if (!success && !g_error_matches(local, _NM_CRYPTO_ERROR, _NM_CRYPTO_ERROR_DECRYPTION_FAILED)) { + g_propagate_error(error, g_steal_pointer(&local)); + return FALSE; } - return success; + + return TRUE; } gboolean @@ -826,7 +794,7 @@ nm_crypto_is_pkcs12_file(const char *file, GError **error) if (!_nm_crypto_init(error)) return FALSE; - if (!file_read_contents(file, &contents, error)) + if (!nm_utils_read_crypto_file(file, &contents, error)) return FALSE; return nm_crypto_is_pkcs12_data(contents.bin, contents.len, error); @@ -881,8 +849,8 @@ nm_crypto_verify_private_key_data(const guint8 *data, if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN && error && !*error) { g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, + _NM_CRYPTO_ERROR, + _NM_CRYPTO_ERROR_INVALID_DATA, _("not a valid private key")); } @@ -904,7 +872,7 @@ nm_crypto_verify_private_key(const char *filename, if (!_nm_crypto_init(error)) return NM_CRYPTO_FILE_FORMAT_UNKNOWN; - if (!file_read_contents(filename, &contents, error)) + if (!nm_utils_read_crypto_file(filename, &contents, error)) return NM_CRYPTO_FILE_FORMAT_UNKNOWN; return nm_crypto_verify_private_key_data(contents.bin, @@ -965,7 +933,7 @@ nmtst_crypto_rsa_key_encrypt(const guint8 *data, if (!nm_crypto_randomize(pw_buf.bin, pw_buf.len, error)) return NULL; - tmp_password = nm_utils_bin2hexstr(pw_buf.bin, pw_buf.len, -1); + tmp_password = _nm_utils_bin2hexstr(pw_buf.bin, pw_buf.len, -1); in_password = tmp_password; } @@ -1009,7 +977,7 @@ nmtst_crypto_rsa_key_encrypt(const guint8 *data, pem, g_strdup_printf("DEK-Info: %s,", nm_crypto_cipher_get_info(NM_CRYPTO_CIPHER_DES_EDE3_CBC)->name)); - g_ptr_array_add(pem, nm_utils_bin2hexstr(salt, sizeof(salt), sizeof(salt) * 2)); + g_ptr_array_add(pem, _nm_utils_bin2hexstr(salt, sizeof(salt), sizeof(salt) * 2)); g_ptr_array_add(pem, g_strdup("\n\n")); /* Convert the encrypted key to a base64 string */ @@ -1042,3 +1010,54 @@ nmtst_crypto_rsa_key_encrypt(const guint8 *data, NM_SET_OUT(out_password, g_strdup(tmp_password)); return nm_secret_buf_to_gbytes_take(ret, ret_len); } + +/*****************************************************************************/ + +static gboolean +file_has_extension(const char *filename, const char *extensions[]) +{ + const char *ext; + gsize i; + + ext = strrchr(filename, '.'); + if (!ext) + return FALSE; + + for (i = 0; extensions[i]; i++) { + if (!g_ascii_strcasecmp(ext, extensions[i])) + return TRUE; + } + + return FALSE; +} + +gboolean +nm_crypto_utils_file_is_certificate(const char *filename) +{ + const char *extensions[] = {".der", ".pem", ".crt", ".cer", NULL}; + NMCryptoFileFormat file_format; + + nm_assert(filename); + + if (!file_has_extension(filename, extensions)) + return FALSE; + + if (!nm_crypto_load_and_verify_certificate(filename, &file_format, NULL, NULL)) + return FALSE; + return file_format = NM_CRYPTO_FILE_FORMAT_X509; +} + +gboolean +nm_crypto_utils_file_is_private_key(const char *filename, gboolean *out_encrypted) +{ + const char *extensions[] = {".der", ".pem", ".p12", ".key", NULL}; + + nm_assert(filename); + + NM_SET_OUT(out_encrypted, FALSE); + if (!file_has_extension(filename, extensions)) + return FALSE; + + return nm_crypto_verify_private_key(filename, NULL, out_encrypted, NULL) + != NM_CRYPTO_FILE_FORMAT_UNKNOWN; +} diff --git a/src/libnm-core-impl/nm-crypto.h b/src/libnm-crypto/nm-crypto.h index a740c43c..48c7c6b7 100644 --- a/src/libnm-core-impl/nm-crypto.h +++ b/src/libnm-crypto/nm-crypto.h @@ -93,4 +93,7 @@ guint8 *nmtst_crypto_make_des_aes_key(NMCryptoCipherType cipher, /*****************************************************************************/ +gboolean nm_crypto_utils_file_is_certificate(const char *filename); +gboolean nm_crypto_utils_file_is_private_key(const char *filename, gboolean *out_encrypted); + #endif /* __NM_CRYPTO_H__ */ diff --git a/src/libnm-glib-aux/nm-glib.h b/src/libnm-glib-aux/nm-glib.h index 4ab9cbbd..f3be3b32 100644 --- a/src/libnm-glib-aux/nm-glib.h +++ b/src/libnm-glib-aux/nm-glib.h @@ -717,4 +717,14 @@ _nm_deprecated("Don't use this API") void _nm_forbidden_glib_api_n(gconstpointer /*****************************************************************************/ +/* g_alloca0() evaluates the "size" argument multiple times. That seems an error + * prone API (as it's not function-like). + * + * We could fix it by using an expression statement. But it doesn't seem + * worth it, so hide it to prevent its use. */ +#undef g_alloca0 +#undef g_newa0 + +/*****************************************************************************/ + #endif /* __NM_GLIB_H__ */ diff --git a/src/libnm-glib-aux/nm-json-aux.h b/src/libnm-glib-aux/nm-json-aux.h index 275ed770..3054b1f9 100644 --- a/src/libnm-glib-aux/nm-json-aux.h +++ b/src/libnm-glib-aux/nm-json-aux.h @@ -391,6 +391,7 @@ nm_value_type_to_json(NMValueType value_type, GString *gstr, gconstpointer p_fie nm_json_gstr_append_string(gstr, *((const char *const *) p_field)); return; case NM_VALUE_TYPE_BYTES: + case NM_VALUE_TYPE_STRV: case NM_VALUE_TYPE_NONE: case NM_VALUE_TYPE_UNSPEC: break; @@ -428,12 +429,12 @@ nm_value_type_from_json(const NMJsonVt *vt, return (nm_jansson_json_as_string(vt, elem, out_val) > 0); case NM_VALUE_TYPE_BYTES: + case NM_VALUE_TYPE_STRV: case NM_VALUE_TYPE_NONE: case NM_VALUE_TYPE_UNSPEC: break; } - nm_assert_not_reached(); - return FALSE; + return nm_assert_unreachable_val(FALSE); } #endif /* NM_VALUE_TYPE_DEFINE_FUNCTIONS */ diff --git a/src/libnm-glib-aux/nm-logging-fwd.h b/src/libnm-glib-aux/nm-logging-fwd.h index 3e999c4a..0e715c50 100644 --- a/src/libnm-glib-aux/nm-logging-fwd.h +++ b/src/libnm-glib-aux/nm-logging-fwd.h @@ -9,54 +9,54 @@ /* Log domains */ -typedef enum { /*< skip >*/ - LOGD_NONE = 0LL, - LOGD_PLATFORM = (1LL << 0), /* Platform services */ - LOGD_RFKILL = (1LL << 1), - LOGD_ETHER = (1LL << 2), - LOGD_WIFI = (1LL << 3), - LOGD_BT = (1LL << 4), - LOGD_MB = (1LL << 5), /* mobile broadband */ - LOGD_DHCP4 = (1LL << 6), - LOGD_DHCP6 = (1LL << 7), - LOGD_PPP = (1LL << 8), - LOGD_WIFI_SCAN = (1LL << 9), - LOGD_IP4 = (1LL << 10), - LOGD_IP6 = (1LL << 11), - LOGD_AUTOIP4 = (1LL << 12), - LOGD_DNS = (1LL << 13), - LOGD_VPN = (1LL << 14), - LOGD_SHARING = (1LL << 15), /* Connection sharing/dnsmasq */ - LOGD_SUPPLICANT = (1LL << 16), /* Wi-Fi and 802.1x */ - LOGD_AGENTS = (1LL << 17), /* Secret agents */ - LOGD_SETTINGS = (1LL << 18), /* Settings */ - LOGD_SUSPEND = (1LL << 19), /* Suspend/Resume */ - LOGD_CORE = (1LL << 20), /* Core daemon and policy stuff */ - LOGD_DEVICE = (1LL << 21), /* Device state and activation */ - LOGD_OLPC = (1LL << 22), - LOGD_INFINIBAND = (1LL << 23), - LOGD_FIREWALL = (1LL << 24), - LOGD_ADSL = (1LL << 25), - LOGD_BOND = (1LL << 26), - LOGD_VLAN = (1LL << 27), - LOGD_BRIDGE = (1LL << 28), - LOGD_DBUS_PROPS = (1LL << 29), - LOGD_TEAM = (1LL << 30), - LOGD_CONCHECK = (1LL << 31), - LOGD_DCB = (1LL << 32), /* Data Center Bridging */ - LOGD_DISPATCH = (1LL << 33), - LOGD_AUDIT = (1LL << 34), - LOGD_SYSTEMD = (1LL << 35), - LOGD_VPN_PLUGIN = (1LL << 36), - LOGD_PROXY = (1LL << 37), - - __LOGD_MAX, - LOGD_ALL = (((__LOGD_MAX - 1LL) << 1) - 1LL), - LOGD_DEFAULT = LOGD_ALL & ~(LOGD_DBUS_PROPS | LOGD_WIFI_SCAN | LOGD_VPN_PLUGIN | 0), - - /* aliases: */ - LOGD_DHCP = LOGD_DHCP4 | LOGD_DHCP6, - LOGD_IP = LOGD_IP4 | LOGD_IP6, +typedef enum { + LOGD_NONE = 0LL, + LOGD_PLATFORM = (1LL << 0), /* Platform services */ + LOGD_RFKILL = (1LL << 1), + LOGD_ETHER = (1LL << 2), + LOGD_WIFI = (1LL << 3), + LOGD_BT = (1LL << 4), + LOGD_MB = (1LL << 5), /* mobile broadband */ + LOGD_DHCP4 = (1LL << 6), + LOGD_DHCP6 = (1LL << 7), + LOGD_PPP = (1LL << 8), + LOGD_WIFI_SCAN = (1LL << 9), + LOGD_IP4 = (1LL << 10), + LOGD_IP6 = (1LL << 11), + LOGD_AUTOIP4 = (1LL << 12), + LOGD_DNS = (1LL << 13), + LOGD_VPN = (1LL << 14), + LOGD_SHARING = (1LL << 15), /* Connection sharing/dnsmasq */ + LOGD_SUPPLICANT = (1LL << 16), /* Wi-Fi and 802.1x */ + LOGD_AGENTS = (1LL << 17), /* Secret agents */ + LOGD_SETTINGS = (1LL << 18), /* Settings */ + LOGD_SUSPEND = (1LL << 19), /* Suspend/Resume */ + LOGD_CORE = (1LL << 20), /* Core daemon and policy stuff */ + LOGD_DEVICE = (1LL << 21), /* Device state and activation */ + LOGD_OLPC = (1LL << 22), + LOGD_INFINIBAND = (1LL << 23), + LOGD_FIREWALL = (1LL << 24), + LOGD_ADSL = (1LL << 25), + LOGD_BOND = (1LL << 26), + LOGD_VLAN = (1LL << 27), + LOGD_BRIDGE = (1LL << 28), + LOGD_DBUS_PROPS = (1LL << 29), + LOGD_TEAM = (1LL << 30), + LOGD_CONCHECK = (1LL << 31), + LOGD_DCB = (1LL << 32), /* Data Center Bridging */ + LOGD_DISPATCH = (1LL << 33), + LOGD_AUDIT = (1LL << 34), + LOGD_SYSTEMD = (1LL << 35), + LOGD_VPN_PLUGIN = (1LL << 36), + LOGD_PROXY = (1LL << 37), + + __LOGD_MAX, + LOGD_ALL = (((__LOGD_MAX - 1LL) << 1) - 1LL), + LOGD_DEFAULT = LOGD_ALL & ~(LOGD_DBUS_PROPS | LOGD_WIFI_SCAN | LOGD_VPN_PLUGIN | 0), + + /* aliases: */ + LOGD_DHCP = LOGD_DHCP4 | LOGD_DHCP6, + LOGD_IP = LOGD_IP4 | LOGD_IP6, #define LOGD_DHCPX(is_ipv4) ((is_ipv4) ? LOGD_DHCP4 : LOGD_DHCP6) #define LOGD_IPX(is_ipv4) ((is_ipv4) ? LOGD_IP4 : LOGD_IP6) @@ -64,19 +64,20 @@ typedef enum { /*< skip >*/ } NMLogDomain; /* Log levels */ -typedef enum { /*< skip >*/ - LOGL_TRACE, - LOGL_DEBUG, - LOGL_INFO, - LOGL_WARN, - LOGL_ERR, +typedef enum { - _LOGL_N_REAL, /* the number of actual logging levels */ + LOGL_TRACE, + LOGL_DEBUG, + LOGL_INFO, + LOGL_WARN, + LOGL_ERR, - _LOGL_OFF = _LOGL_N_REAL, /* special logging level that is always disabled. */ - _LOGL_KEEP, /* special logging level to indicate that the logging level should not be changed. */ + _LOGL_N_REAL, /* the number of actual logging levels */ - _LOGL_N, /* the number of logging levels including "OFF" */ + _LOGL_OFF = _LOGL_N_REAL, /* special logging level that is always disabled. */ + _LOGL_KEEP, /* special logging level to indicate that the logging level should not be changed. */ + + _LOGL_N, /* the number of logging levels including "OFF" */ } NMLogLevel; gboolean _nm_log_enabled_impl(gboolean mt_require_locking, NMLogLevel level, NMLogDomain domain); diff --git a/src/libnm-glib-aux/nm-macros-internal.h b/src/libnm-glib-aux/nm-macros-internal.h index 14c16020..7cc8ac97 100644 --- a/src/libnm-glib-aux/nm-macros-internal.h +++ b/src/libnm-glib-aux/nm-macros-internal.h @@ -184,22 +184,31 @@ _nm_auto_freev(gpointer ptr) * same name for the same warning. */ #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#define NM_PRAGMA_DIAGNOSTICS_PUSH _Pragma("GCC diagnostic push") #define NM_PRAGMA_WARNING_DISABLE(warning) \ - _Pragma("GCC diagnostic push") _Pragma(_NM_PRAGMA_WARNING_DO(warning)) + NM_PRAGMA_DIAGNOSTICS_PUSH _Pragma(_NM_PRAGMA_WARNING_DO(warning)) +#define NM_PRAGMA_WARNING_REENABLE _Pragma("GCC diagnostic pop") #elif defined(__clang__) -#define NM_PRAGMA_WARNING_DISABLE(warning) \ - _Pragma("clang diagnostic push") _Pragma(_NM_PRAGMA_WARNING_DO("-Wunknown-warning-option")) \ +#define NM_PRAGMA_DIAGNOSTICS_PUSH _Pragma("clang diagnostic push") +#define NM_PRAGMA_WARNING_DISABLE(warning) \ + NM_PRAGMA_DIAGNOSTICS_PUSH _Pragma(_NM_PRAGMA_WARNING_DO("-Wunknown-warning-option")) \ _Pragma(_NM_PRAGMA_WARNING_DO(warning)) +#define NM_PRAGMA_WARNING_REENABLE _Pragma("clang diagnostic pop") #else +#define NM_PRAGMA_DIAGNOSTICS_PUSH #define NM_PRAGMA_WARNING_DISABLE(warning) +#define NM_PRAGMA_WARNING_REENABLE #endif -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -#define NM_PRAGMA_WARNING_REENABLE _Pragma("GCC diagnostic pop") -#elif defined(__clang__) -#define NM_PRAGMA_WARNING_REENABLE _Pragma("clang diagnostic pop") +/*****************************************************************************/ + +/* Seems gcc-12 has a tendency for false-positive -Wdangling-pointer warnings with + * g_error()'s `for(;;);`. See https://bugzilla.redhat.com/show_bug.cgi?id=2056613 . + * Work around, but only for the affected gcc 12.0.1. */ +#if defined(__GNUC__) && __GNUC__ == 12 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ <= 1 +#define NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER NM_PRAGMA_WARNING_DISABLE("-Wdangling-pointer") #else -#define NM_PRAGMA_WARNING_REENABLE +#define NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER NM_PRAGMA_DIAGNOSTICS_PUSH #endif /*****************************************************************************/ @@ -611,10 +620,16 @@ nm_str_realloc(char *str) /* invokes _notify() for all arguments (of type _PropertyEnums). Note, that if * there are more than one prop arguments, this will involve a freeze/thaw * of GObject property notifications. */ -#define nm_gobject_notify_together_full(suffix, obj, ...) \ - _nm_gobject_notify_together_impl##suffix(obj, \ - NM_NARG(__VA_ARGS__), \ - (const _PropertyEnums##suffix[]){__VA_ARGS__}) +#define nm_gobject_notify_together_full(suffix, obj, ...) \ + G_STMT_START \ + { \ + const _PropertyEnums##suffix _props[] = {__VA_ARGS__}; \ + \ + G_STATIC_ASSERT(G_N_ELEMENTS(_props) == NM_NARG(__VA_ARGS__)); \ + \ + _nm_gobject_notify_together_impl##suffix(obj, G_N_ELEMENTS(_props), _props); \ + } \ + G_STMT_END #define nm_gobject_notify_together(obj, ...) nm_gobject_notify_together_full(, obj, __VA_ARGS__) diff --git a/src/libnm-glib-aux/nm-secret-utils.c b/src/libnm-glib-aux/nm-secret-utils.c index c764b6e5..983b04ca 100644 --- a/src/libnm-glib-aux/nm-secret-utils.c +++ b/src/libnm-glib-aux/nm-secret-utils.c @@ -10,6 +10,8 @@ #include <malloc.h> +#include "nm-io-utils.h" + /*****************************************************************************/ void @@ -176,3 +178,34 @@ nm_utils_memeqzero_secret(gconstpointer data, gsize length) } return 1 & ((acc - 1) >> 8); } + +/*****************************************************************************/ + +gboolean +nm_utils_read_crypto_file(const char *filename, NMSecretPtr *out_contents, GError **error) +{ + nm_assert(out_contents); + nm_assert(out_contents->len == 0); + nm_assert(!out_contents->str); + + return nm_utils_file_get_contents(-1, + filename, + 100 * 1024 * 1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET, + &out_contents->str, + &out_contents->len, + NULL, + error); +} + +GBytes * +nm_utils_read_crypto_file_to_bytes(const char *filename, GError **error) +{ + nm_auto_clear_secret_ptr NMSecretPtr contents = {0}; + + g_return_val_if_fail(filename, NULL); + + if (!nm_utils_read_crypto_file(filename, &contents, error)) + return NULL; + return nm_secret_copy_to_gbytes(contents.bin, contents.len); +} diff --git a/src/libnm-glib-aux/nm-secret-utils.h b/src/libnm-glib-aux/nm-secret-utils.h index 513dbca5..c175bc8f 100644 --- a/src/libnm-glib-aux/nm-secret-utils.h +++ b/src/libnm-glib-aux/nm-secret-utils.h @@ -286,4 +286,8 @@ nm_secret_mem_try_realloc_take(gpointer m_old, gboolean do_bzero_mem, gsize cur_ /*****************************************************************************/ +gboolean nm_utils_read_crypto_file(const char *filename, NMSecretPtr *out_contents, GError **error); + +GBytes *nm_utils_read_crypto_file_to_bytes(const char *filename, GError **error); + #endif /* __NM_SECRET_UTILS_H__ */ diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c index 563b7cc2..ad99a6b9 100644 --- a/src/libnm-glib-aux/nm-shared-utils.c +++ b/src/libnm-glib-aux/nm-shared-utils.c @@ -870,7 +870,12 @@ nm_utils_to_string_buffer_init(char **buf, gsize *len) { if (!*buf) { *buf = _nm_utils_to_string_buffer; - *len = sizeof(_nm_utils_to_string_buffer); + *len = NM_UTILS_TO_STRING_BUFFER_SIZE; + + /* We no longer want to support callers to omit the buffer + * and fallback to the global buffer. Callers should be fixed + * to always provide a valid buffer. */ + g_return_if_reached(); } } @@ -945,20 +950,6 @@ nm_utils_flags2str(const NMUtilsFlags2StrDesc *descs, /*****************************************************************************/ -/** - * _nm_utils_ip4_prefix_to_netmask: - * @prefix: a CIDR prefix - * - * Returns: the netmask represented by the prefix, in network byte order - **/ -guint32 -_nm_utils_ip4_prefix_to_netmask(guint32 prefix) -{ - return prefix < 32 ? ~htonl(0xFFFFFFFFu >> prefix) : 0xFFFFFFFFu; -} - -/*****************************************************************************/ - guint32 _nm_utils_ip4_get_default_prefix0(in_addr_t ip) { @@ -1130,6 +1121,7 @@ nm_utils_parse_inaddr_bin_full(int addr_family, #if NM_MORE_ASSERTS > 10 if (addr_family == AF_INET) { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER gs_free_error GError *error = NULL; in_addr_t a; @@ -1145,6 +1137,7 @@ nm_utils_parse_inaddr_bin_full(int addr_family, error->message); } nm_assert(addrbin.addr4 == a); + NM_PRAGMA_WARNING_REENABLE } #endif @@ -2958,8 +2951,8 @@ _str_buf_append_c_escape_octal(NMStrBuf *strbuf, char ch) * * Returns: the unescaped buffer of length @out_len. If @str is %NULL, this returns %NULL * and sets @out_len to 0. Otherwise, a non-%NULL binary buffer is returned with - * @out_len bytes. Note that the binary buffer is guaranteed to be NUL terminated. That - * is @result[@out_len] is NUL. + * @out_len bytes. Note that the binary buffer is guaranteed to be NUL terminated + * (@result[@out_len] is NUL). * Note that the result is binary, and may have embedded NUL characters and non-UTF-8. * If the function can avoid cloning the input string, it will return a pointer inside * the input @str. For example, if there is no backslash, no cloning is necessary. In that @@ -3121,11 +3114,14 @@ nm_utils_buf_utf8safe_unescape(const char *str, * invalid UTF-8 sequences, and other (depending on @flags). * * Returns: the escaped input buffer, as valid UTF-8. If no escaping - * is necessary, it returns the input @buf. Otherwise, an allocated - * string @to_free is returned which must be freed by the caller - * with g_free. The escaping can be reverted by g_strcompress(). - * There are cases where this function can return NULL: - * - if @buflen is 0 + * is necessary and @buflen is negative, it returns the input @buf + * that can be interpreted as NUL terminated UTF-8 string. + * Otherwise, an allocated string @to_free is returned which must be freed + * by the caller with g_free(). + * The escaping can be reverted by nm_utils_buf_utf8safe_unescape() + * (or, if in the absence of NUL characters, with g_strcompress()). + * There are cases where this function returns %NULL: + * - if @buflen is 0. * - if @buflen is negative and @buf is NULL. **/ const char * @@ -4689,14 +4685,20 @@ _nm_utils_invoke_on_idle_start(gboolean use_timeout, } if (use_timeout) { + /* We use G_PRIORITY_DEFAULT_IDLE both for the with/without timeout + * case. The reason is not strong, but it seems right that the caller + * requests a lower priority than G_PRIORITY_DEFAULT. That is unlike + * what g_timeout_add() would do. */ source = nm_g_timeout_source_new(timeout_msec, - G_PRIORITY_DEFAULT, + G_PRIORITY_DEFAULT_IDLE, _nm_utils_invoke_on_idle_cb_idle, data, NULL); } else { - source = - nm_g_idle_source_new(G_PRIORITY_DEFAULT, _nm_utils_invoke_on_idle_cb_idle, data, NULL); + source = nm_g_idle_source_new(G_PRIORITY_DEFAULT_IDLE, + _nm_utils_invoke_on_idle_cb_idle, + data, + NULL); } /* use the current thread default context. */ @@ -4840,6 +4842,27 @@ nm_utils_bin2hexstr_full(gconstpointer addr, return out0; } +char * +_nm_utils_bin2hexstr(gconstpointer src, gsize len, int final_len) +{ + char *result; + gsize buflen = (len * 2) + 1; + + nm_assert(src); + nm_assert(len > 0 && (buflen - 1) / 2 == len); + nm_assert(final_len < 0 || (gsize) final_len < buflen); + + result = g_malloc(buflen); + + nm_utils_bin2hexstr_full(src, len, '\0', FALSE, result); + + /* Cut converted key off at the correct length for this cipher type */ + if (final_len >= 0 && (gsize) final_len < buflen) + result[final_len] = '\0'; + + return result; +} + guint8 * nm_utils_hexstr2bin_full(const char *hexstr, gboolean allow_0x_prefix, @@ -6259,7 +6282,7 @@ _nm_utils_ssid_to_string_gbytes(GBytes *ssid) /*****************************************************************************/ gconstpointer -nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint8 plen) +nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint32 plen) { g_return_val_if_fail(dst, NULL); @@ -6283,21 +6306,10 @@ nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer return dst; } -/* nm_utils_ip4_address_clear_host_address: - * @addr: source ip6 address - * @plen: prefix length of network - * - * returns: the input address, with the host address set to 0. - */ -in_addr_t -nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint8 plen) -{ - return addr & _nm_utils_ip4_prefix_to_netmask(plen); -} - /* nm_utils_ip6_address_clear_host_address: * @dst: destination output buffer, will contain the network part of the @src address - * @src: source ip6 address + * @src: source ip6 address. If NULL, this does an in-place update of @dst. + * Also, @src and @dst are allowed to be the same pointers. * @plen: prefix length of network * * Note: this function is self assignment safe, to update @src inplace, set both @@ -6306,7 +6318,7 @@ nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint8 plen) const struct in6_addr * nm_utils_ip6_address_clear_host_address(struct in6_addr *dst, const struct in6_addr *src, - guint8 plen) + guint32 plen) { g_return_val_if_fail(plen <= 128, NULL); g_return_val_if_fail(dst, NULL); @@ -6335,14 +6347,15 @@ nm_utils_ip6_address_clear_host_address(struct in6_addr *dst, int nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a, const struct in6_addr *addr_b, - guint8 plen) + guint32 plen) { int nbytes; guint8 va, vb, m; - if (plen >= 128) + if (plen >= 128) { + nm_assert(plen == 128); NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, sizeof(struct in6_addr)); - else { + } else { nbytes = plen / 8; if (nbytes) NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, nbytes); diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index a953140b..941312bd 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -389,15 +389,43 @@ gboolean nm_utils_get_ipv6_interface_identifier(NMLinkType link_type, /*****************************************************************************/ +/** + * _nm_utils_ip4_prefix_to_netmask: + * @prefix: a CIDR prefix + * + * Returns: the netmask represented by the prefix, in network byte order + **/ +static inline in_addr_t +_nm_utils_ip4_prefix_to_netmask(guint32 prefix) +{ + nm_assert(prefix <= 32); + return prefix < 32 ? ~htonl(0xFFFFFFFFu >> prefix) : 0xFFFFFFFFu; +} + +guint32 _nm_utils_ip4_get_default_prefix0(in_addr_t ip); +guint32 _nm_utils_ip4_get_default_prefix(in_addr_t ip); + gconstpointer -nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint8 plen); -in_addr_t nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint8 plen); +nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint32 plen); + +/* nm_utils_ip4_address_clear_host_address: + * @addr: source ip6 address + * @plen: prefix length of network + * + * returns: the input address, with the host address set to 0. + */ +static inline in_addr_t +nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint32 plen) +{ + return addr & _nm_utils_ip4_prefix_to_netmask(plen); +} + const struct in6_addr *nm_utils_ip6_address_clear_host_address(struct in6_addr *dst, const struct in6_addr *src, - guint8 plen); + guint32 plen); static inline int -nm_utils_ip4_address_same_prefix_cmp(in_addr_t addr_a, in_addr_t addr_b, guint8 plen) +nm_utils_ip4_address_same_prefix_cmp(in_addr_t addr_a, in_addr_t addr_b, guint32 plen) { NM_CMP_DIRECT(htonl(nm_utils_ip4_address_clear_host_address(addr_a, plen)), htonl(nm_utils_ip4_address_clear_host_address(addr_b, plen))); @@ -406,7 +434,21 @@ nm_utils_ip4_address_same_prefix_cmp(in_addr_t addr_a, in_addr_t addr_b, guint8 int nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a, const struct in6_addr *addr_b, - guint8 plen); + guint32 plen); + +static inline gboolean +nm_utils_ip4_address_same_prefix(in_addr_t addr_a, in_addr_t addr_b, guint32 plen) +{ + return nm_utils_ip4_address_same_prefix_cmp(addr_a, addr_b, plen) == 0; +} + +static inline gboolean +nm_utils_ip6_address_same_prefix(const struct in6_addr *addr_a, + const struct in6_addr *addr_b, + guint8 plen) +{ + return nm_utils_ip6_address_same_prefix_cmp(addr_a, addr_b, plen) == 0; +} static inline int nm_utils_ip_address_same_prefix_cmp(int addr_family, @@ -414,8 +456,6 @@ nm_utils_ip_address_same_prefix_cmp(int addr_family, gconstpointer addr_b, guint8 plen) { - nm_assert_addr_family(addr_family); - NM_CMP_SELF(addr_a, addr_b); if (NM_IS_IPv4(addr_family)) { @@ -428,20 +468,6 @@ nm_utils_ip_address_same_prefix_cmp(int addr_family, } static inline gboolean -nm_utils_ip4_address_same_prefix(in_addr_t addr_a, in_addr_t addr_b, guint8 plen) -{ - return nm_utils_ip4_address_same_prefix_cmp(addr_a, addr_b, plen) == 0; -} - -static inline gboolean -nm_utils_ip6_address_same_prefix(const struct in6_addr *addr_a, - const struct in6_addr *addr_b, - guint8 plen) -{ - return nm_utils_ip6_address_same_prefix_cmp(addr_a, addr_b, plen) == 0; -} - -static inline gboolean nm_utils_ip_address_same_prefix(int addr_family, gconstpointer addr_a, gconstpointer addr_b, @@ -458,6 +484,10 @@ nm_utils_ip_address_same_prefix(int addr_family, /*****************************************************************************/ +gboolean nm_utils_ip_is_site_local(int addr_family, const void *address); + +/*****************************************************************************/ + #define NM_IPV4LL_NETWORK ((in_addr_t) (htonl(0xA9FE0000lu))) #define NM_IPV4LL_NETMASK ((in_addr_t) (htonl(0xFFFF0000lu))) @@ -974,24 +1004,6 @@ nm_utils_escaped_tokens_options_escape_val(const char *val, char **out_to_free) /*****************************************************************************/ -guint32 _nm_utils_ip4_prefix_to_netmask(guint32 prefix); -guint32 _nm_utils_ip4_get_default_prefix0(in_addr_t ip); -guint32 _nm_utils_ip4_get_default_prefix(in_addr_t ip); - -gconstpointer -nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint8 plen); -in_addr_t nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint8 plen); -const struct in6_addr *nm_utils_ip6_address_clear_host_address(struct in6_addr *dst, - const struct in6_addr *src, - guint8 plen); -int nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a, - const struct in6_addr *addr_b, - guint8 plen); - -gboolean nm_utils_ip_is_site_local(int addr_family, const void *address); - -/*****************************************************************************/ - gboolean nm_utils_parse_inaddr_bin_full(int addr_family, gboolean accept_legacy, const char *text, @@ -1047,7 +1059,9 @@ int _nm_utils_ascii_str_to_bool(const char *str, int default_value); /*****************************************************************************/ -extern _nm_thread_local char _nm_utils_to_string_buffer[2096]; +#define NM_UTILS_TO_STRING_BUFFER_SIZE 2096 + +extern _nm_thread_local char _nm_utils_to_string_buffer[NM_UTILS_TO_STRING_BUFFER_SIZE]; void nm_utils_to_string_buffer_init(char **buf, gsize *len); gboolean nm_utils_to_string_buffer_init_null(gconstpointer obj, char **buf, gsize *len); @@ -2710,6 +2724,8 @@ char *nm_utils_bin2hexstr_full(gconstpointer addr, gboolean upper_case, char *out); +char *_nm_utils_bin2hexstr(gconstpointer src, gsize len, int final_len); + #define nm_utils_bin2hexstr_a(addr, length, delimiter, upper_case, str_to_free) \ ({ \ gconstpointer _addr = (addr); \ @@ -3141,6 +3157,33 @@ nm_strvarray_remove_first(GArray *strv, const char *needle) return TRUE; } +static inline int +nm_strvarray_cmp(const GArray *a, const GArray *b) +{ + NM_CMP_SELF(a, b); + + return nm_strv_cmp_n((const char *const *) a->data, + a->len, + (const char *const *) b->data, + b->len); +} + +#define nm_strvarray_equal(a, b) (nm_strvarray_cmp((a), (b)) == 0) + +static inline int +_nm_strvarray_cmp_strv(const GArray *strv, const char *const *ss, gsize ss_len) +{ + return nm_strv_cmp_n(strv ? (const char *const *) strv->data : NULL, + strv ? ((gssize) strv->len) : -1, + ss, + ss_len); +} +#define nm_strvarray_cmp_strv(strv, ss, ss_len) \ + _nm_strvarray_cmp_strv((strv), NM_CAST_STRV_CC(ss), (ss_len)) + +#define nm_strvarray_equal_strv(strv, ss, ss_len) \ + (nm_strvarray_cmp_strv((strv), (ss), (ss_len)) == 0) + /*****************************************************************************/ struct _NMVariantAttributeSpec { diff --git a/src/libnm-glib-aux/nm-test-utils.h b/src/libnm-glib-aux/nm-test-utils.h index caf43d5f..2dfe9e32 100644 --- a/src/libnm-glib-aux/nm-test-utils.h +++ b/src/libnm-glib-aux/nm-test-utils.h @@ -1714,8 +1714,11 @@ __nmtst_spawn_sync(const char *working_directory, standard_err, &exit_status, &error); - if (!success) + if (!success) { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("nmtst_spawn_sync(%s): %s", ((char **) argv->pdata)[0], error->message); + NM_PRAGMA_WARNING_REENABLE + } g_assert(!error); g_assert(!standard_out || *standard_out); @@ -1844,7 +1847,8 @@ _nmtst_assert_resolve_relative_path_equals(const char *f1, /* Fixme: later we might need to coalesce repeated '/', "./", and "../". * For now, it's good enough. */ - if (g_strcmp0(p1, p2) != 0) + if (g_strcmp0(p1, p2) != 0) { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("%s:%d : filenames don't match \"%s\" vs. \"%s\" // \"%s\" - \"%s\"", file, line, @@ -1852,6 +1856,8 @@ _nmtst_assert_resolve_relative_path_equals(const char *f1, f2, p1, p2); + NM_PRAGMA_WARNING_REENABLE + } } #define nmtst_assert_resolve_relative_path_equals(f1, f2) \ _nmtst_assert_resolve_relative_path_equals(f1, f2, __FILE__, __LINE__); @@ -2404,9 +2410,11 @@ _nmtst_assert_connection_has_settings(NMConnection *connection, settings = nm_connection_get_settings(connection, &len); for (i = 0; i < len; i++) { if (!g_hash_table_remove(names, nm_setting_get_name(settings[i])) && has_at_most) { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error( "nmtst_assert_connection_has_settings(): has setting \"%s\" which is not expected", nm_setting_get_name(settings[i])); + NM_PRAGMA_WARNING_REENABLE } } if (g_hash_table_size(names) > 0 && has_at_least) { @@ -2419,11 +2427,13 @@ _nmtst_assert_connection_has_settings(NMConnection *connection, settings_names[i] = nm_setting_get_name(settings[i]); has_str = g_strjoinv(" ", (char **) settings_names); + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("nmtst_assert_connection_has_settings(): the setting lacks %u expected settings " "(expected: [%s] vs. has: [%s])", g_hash_table_size(names), expected_str, has_str); + NM_PRAGMA_WARNING_REENABLE } } #define nmtst_assert_connection_has_settings(connection, ...) \ diff --git a/src/libnm-glib-aux/nm-time-utils.h b/src/libnm-glib-aux/nm-time-utils.h index 3c3e935f..461d6845 100644 --- a/src/libnm-glib-aux/nm-time-utils.h +++ b/src/libnm-glib-aux/nm-time-utils.h @@ -41,6 +41,12 @@ nm_utils_get_monotonic_timestamp_msec_cached(gint64 *cache_now) return (*cache_now) ?: (*cache_now = nm_utils_get_monotonic_timestamp_msec()); } +static inline gint32 +nm_utils_get_monotonic_timestamp_sec_cached(gint32 *cache_now) +{ + return (*cache_now) ?: (*cache_now = nm_utils_get_monotonic_timestamp_sec()); +} + gint64 nm_utils_clock_gettime_nsec(clockid_t clockid); gint64 nm_utils_clock_gettime_msec(clockid_t clockid); diff --git a/src/libnm-glib-aux/nm-uuid.c b/src/libnm-glib-aux/nm-uuid.c index 19b09667..ff0649d0 100644 --- a/src/libnm-glib-aux/nm-uuid.c +++ b/src/libnm-glib-aux/nm-uuid.c @@ -316,8 +316,20 @@ nm_uuid_generate_from_string(NMUuid *uuid, NMUuidType uuid_type, const NMUuid *type_args) { - g_return_val_if_fail(uuid, FALSE); - g_return_val_if_fail(slen == 0 || s, FALSE); + nm_auto_free_checksum GChecksum *sum = NULL; + union { + guint8 sha1[NM_UTILS_CHECKSUM_LENGTH_SHA1]; + guint8 md5[NM_UTILS_CHECKSUM_LENGTH_MD5]; + NMUuid uuid; + } digest; + gsize digest_len; + GChecksumType checksum_type; + + G_STATIC_ASSERT_EXPR(sizeof(digest.md5) >= sizeof(digest.uuid)); + G_STATIC_ASSERT_EXPR(sizeof(digest.sha1) >= sizeof(digest.uuid)); + + g_return_val_if_fail(uuid, NULL); + g_return_val_if_fail(slen <= 0 || s, NULL); if (slen < 0) slen = s ? strlen(s) : 0; @@ -325,43 +337,41 @@ nm_uuid_generate_from_string(NMUuid *uuid, switch (uuid_type) { case NM_UUID_TYPE_LEGACY: nm_assert(!type_args); - nm_crypto_md5_hash(NULL, 0, (guint8 *) s, slen, (guint8 *) uuid, sizeof(*uuid)); + type_args = NULL; + checksum_type = G_CHECKSUM_MD5; break; case NM_UUID_TYPE_VERSION3: + if (!type_args) + type_args = &nm_uuid_ns_zero; + checksum_type = G_CHECKSUM_MD5; + break; case NM_UUID_TYPE_VERSION5: - { if (!type_args) type_args = &nm_uuid_ns_zero; + checksum_type = G_CHECKSUM_SHA1; + break; + default: + g_return_val_if_reached(NULL); + } - if (uuid_type == NM_UUID_TYPE_VERSION3) { - nm_crypto_md5_hash((guint8 *) s, - slen, - (guint8 *) type_args, - sizeof(*type_args), - (guint8 *) uuid, - sizeof(*uuid)); - } else { - nm_auto_free_checksum GChecksum *sum = NULL; - union { - guint8 sha1[NM_UTILS_CHECKSUM_LENGTH_SHA1]; - NMUuid uuid; - } digest; - - sum = g_checksum_new(G_CHECKSUM_SHA1); - g_checksum_update(sum, (guchar *) type_args, sizeof(*type_args)); - g_checksum_update(sum, (guchar *) s, slen); - nm_utils_checksum_get_digest(sum, digest.sha1); - - G_STATIC_ASSERT_EXPR(sizeof(digest.sha1) > sizeof(digest.uuid)); - *uuid = digest.uuid; - } + sum = g_checksum_new(checksum_type); + if (type_args) + g_checksum_update(sum, (guchar *) type_args, sizeof(*type_args)); + g_checksum_update(sum, (guchar *) s, slen); + + digest_len = sizeof(digest); + g_checksum_get_digest(sum, (guint8 *) &digest, &digest_len); + + nm_assert(digest_len >= sizeof(digest.uuid)); + nm_assert(digest_len + == ((checksum_type == G_CHECKSUM_MD5 ? NM_UTILS_CHECKSUM_LENGTH_MD5 + : NM_UTILS_CHECKSUM_LENGTH_SHA1))); + + *uuid = digest.uuid; + if (uuid_type != NM_UUID_TYPE_LEGACY) { uuid->uuid[6] = (uuid->uuid[6] & 0x0F) | (uuid_type << 4); uuid->uuid[8] = (uuid->uuid[8] & 0x3F) | 0x80; - break; - } - default: - g_return_val_if_reached(NULL); } return uuid; diff --git a/src/libnm-glib-aux/nm-value-type.h b/src/libnm-glib-aux/nm-value-type.h index d774e120..771021da 100644 --- a/src/libnm-glib-aux/nm-value-type.h +++ b/src/libnm-glib-aux/nm-value-type.h @@ -9,25 +9,26 @@ typedef enum _nm_packed { NM_VALUE_TYPE_NONE = 0, NM_VALUE_TYPE_UNSPEC = 1, - NM_VALUE_TYPE_BOOL = 2, - NM_VALUE_TYPE_INT32 = 3, - NM_VALUE_TYPE_INT = 4, - NM_VALUE_TYPE_INT64 = 5, - NM_VALUE_TYPE_UINT32 = 6, - NM_VALUE_TYPE_UINT = 7, - NM_VALUE_TYPE_UINT64 = 8, + + NM_VALUE_TYPE_BOOL, + NM_VALUE_TYPE_INT32, + NM_VALUE_TYPE_INT, + NM_VALUE_TYPE_INT64, + NM_VALUE_TYPE_UINT32, + NM_VALUE_TYPE_UINT, + NM_VALUE_TYPE_UINT64, /* Flags are for G_TYPE_FLAGS. That is, internally they are tracked * as a guint, they have a g_param_spec_flags() property and they are * serialized on D-Bus as "u". */ - NM_VALUE_TYPE_FLAGS = 9, + NM_VALUE_TYPE_FLAGS, /* G_TYPE_ENUM */ - NM_VALUE_TYPE_ENUM = 10, - - NM_VALUE_TYPE_STRING = 11, + NM_VALUE_TYPE_ENUM, - NM_VALUE_TYPE_BYTES = 12, + NM_VALUE_TYPE_STRING, + NM_VALUE_TYPE_BYTES, + NM_VALUE_TYPE_STRV, } NMValueType; /*****************************************************************************/ @@ -112,14 +113,17 @@ nm_value_type_cmp(NMValueType value_type, gconstpointer p_a, gconstpointer p_b) return 0; case NM_VALUE_TYPE_STRING: return nm_strcmp0(*((const char *const *) p_a), *((const char *const *) p_b)); + case NM_VALUE_TYPE_BYTES: - return nm_g_bytes_equal0(*((const GBytes *const *) p_a), *((const GBytes *const *) p_b)); + case NM_VALUE_TYPE_STRV: + /* These types have implementation define memory representations. */ + break; + case NM_VALUE_TYPE_NONE: case NM_VALUE_TYPE_UNSPEC: break; } - nm_assert_not_reached(); - return 0; + return nm_assert_unreachable_val(0); } static inline gboolean @@ -163,14 +167,12 @@ nm_value_type_copy(NMValueType value_type, gpointer dst, gconstpointer src) *((char **) dst) = g_strdup(*((const char *const *) src)); } return; + case NM_VALUE_TYPE_BYTES: - /* self assignment safe! */ - if (*((GBytes **) dst) != *((const GBytes *const *) src)) { - _nm_unused gs_unref_bytes GBytes *old = *((GBytes **) dst); + case NM_VALUE_TYPE_STRV: + /* These types have implementation define memory representations. */ + break; - *((GBytes **) dst) = g_bytes_ref(*((GBytes *const *) src)); - } - return; case NM_VALUE_TYPE_NONE: case NM_VALUE_TYPE_UNSPEC: break; @@ -212,14 +214,18 @@ nm_value_type_get_from_variant(NMValueType value_type, return; case NM_VALUE_TYPE_BYTES: + case NM_VALUE_TYPE_STRV: + /* These types have implementation define memory representations. */ + break; + case NM_VALUE_TYPE_INT: case NM_VALUE_TYPE_UINT: case NM_VALUE_TYPE_ENUM: case NM_VALUE_TYPE_FLAGS: /* These types don't have a defined variant type, because it's not * clear how many bits we would need or how to handle the type. */ + break; - /* fall-through */ case NM_VALUE_TYPE_NONE: case NM_VALUE_TYPE_UNSPEC: break; @@ -230,8 +236,7 @@ nm_value_type_get_from_variant(NMValueType value_type, static inline GVariant * nm_value_type_to_variant(NMValueType value_type, gconstpointer src) { - const char *v_string; - const GBytes *v_bytes; + const char *v_string; switch (value_type) { case NM_VALUE_TYPE_BOOL: @@ -247,9 +252,11 @@ nm_value_type_to_variant(NMValueType value_type, gconstpointer src) case NM_VALUE_TYPE_STRING: v_string = *((const char *const *) src); return v_string ? g_variant_new_string(v_string) : NULL; + case NM_VALUE_TYPE_BYTES: - v_bytes = *((const GBytes *const *) src); - return v_bytes ? nm_g_bytes_to_variant_ay(v_bytes) : NULL; + case NM_VALUE_TYPE_STRV: + /* These types have implementation define memory representations. */ + break; case NM_VALUE_TYPE_INT: case NM_VALUE_TYPE_UINT: @@ -257,14 +264,13 @@ nm_value_type_to_variant(NMValueType value_type, gconstpointer src) case NM_VALUE_TYPE_FLAGS: /* These types don't have a defined variant type, because it's not * clear how many bits we would need or how to handle the type. */ + break; - /* fall-through */ case NM_VALUE_TYPE_NONE: case NM_VALUE_TYPE_UNSPEC: break; } - nm_assert_not_reached(); - return NULL; + return nm_assert_unreachable_val(NULL); } static inline const GVariantType * @@ -285,6 +291,8 @@ nm_value_type_get_variant_type(NMValueType value_type) return G_VARIANT_TYPE_STRING; case NM_VALUE_TYPE_BYTES: return G_VARIANT_TYPE_BYTESTRING; + case NM_VALUE_TYPE_STRV: + return G_VARIANT_TYPE_STRING_ARRAY; case NM_VALUE_TYPE_INT: case NM_VALUE_TYPE_UINT: diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index ecba02e0..46689947 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -283,9 +283,6 @@ test_nm_utils_ip4_prefix_to_netmask(void) g_assert_cmpint(_nm_utils_ip4_prefix_to_netmask(32), ==, nmtst_inet4_from_string("255.255.255.255")); - g_assert_cmpint(_nm_utils_ip4_prefix_to_netmask(33), - ==, - nmtst_inet4_from_string("255.255.255.255")); } /*****************************************************************************/ diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index ce21ebbb..40cb4396 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -1114,20 +1114,22 @@ _linktype_get_type(NMPlatform *platform, return link_type; } - if (arptype == ARPHRD_LOOPBACK) + switch (arptype) { + case ARPHRD_LOOPBACK: return NM_LINK_TYPE_LOOPBACK; - else if (arptype == ARPHRD_INFINIBAND) + case ARPHRD_INFINIBAND: return NM_LINK_TYPE_INFINIBAND; - else if (arptype == ARPHRD_SIT) + case ARPHRD_SIT: return NM_LINK_TYPE_SIT; - else if (arptype == ARPHRD_TUNNEL6) + case ARPHRD_TUNNEL6: return NM_LINK_TYPE_IP6TNL; - else if (arptype == ARPHRD_PPP) + case ARPHRD_PPP: return NM_LINK_TYPE_PPP; - else if (arptype == ARPHRD_IEEE802154) + case ARPHRD_IEEE802154: return NM_LINK_TYPE_WPAN; - else if (arptype == ARPHRD_6LOWPAN) + case ARPHRD_6LOWPAN: return NM_LINK_TYPE_6LOWPAN; + } { NMPUtilsEthtoolDriverInfo driver_info; @@ -3453,7 +3455,8 @@ _new_from_nl_route(struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter *parse RTN_LOCAL, RTN_BLACKHOLE, RTN_UNREACHABLE, - RTN_PROHIBIT)) + RTN_PROHIBIT, + RTN_THROW)) return NULL; if (nlmsg_parse_arr(nlh, sizeof(struct rtmsg), tb, policy) < 0) @@ -6339,6 +6342,7 @@ cache_prune_one_type(NMPlatform *platform, const NMPLookup *lookup) nm_dedup_multi_iter_init(&iter, nmp_cache_lookup(cache, lookup)); while (nm_dedup_multi_iter_next(&iter)) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; const NMDedupMultiEntry *main_entry; /* we only track the dirty flag for the OBJECT-TYPE index. That means, @@ -6350,7 +6354,7 @@ cache_prune_one_type(NMPlatform *platform, const NMPLookup *lookup) obj = main_entry->obj; _LOGt("cache-prune: prune %s", - nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, sbuf, sizeof(sbuf))); { nm_auto_nmpobj const NMPObject *obj_old = NULL; @@ -6390,8 +6394,8 @@ cache_on_change(NMPlatform *platform, const NMPObject *obj_new) { const NMPClass *klass; - char str_buf[sizeof(_nm_utils_to_string_buffer)]; - char str_buf2[sizeof(_nm_utils_to_string_buffer)]; + char str_buf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + char str_buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPCache *cache = nm_platform_get_cache(platform); ASSERT_nmp_cache_ops(cache, cache_op, obj_old, obj_new); @@ -7023,6 +7027,7 @@ event_seq_check(NMPlatform *platform, static void event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events) { + char sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMLinuxPlatformPrivate *priv; nm_auto_nmpobj NMPObject *obj = NULL; NMPCacheOpsType cache_op; @@ -7079,8 +7084,8 @@ event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events is_dump ? ", in-dump" : "", nmp_object_to_string(obj, is_del ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, - NULL, - 0)); + sbuf1, + sizeof(sbuf1))); while (TRUE) { nm_auto_nmpobj const NMPObject *obj_old = NULL; @@ -7306,6 +7311,7 @@ do_add_addrroute(NMPlatform *platform, struct nl_msg *nlmsg, gboolean suppress_netlink_failure) { + char sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; gs_free char *errmsg = NULL; int nle; @@ -7328,7 +7334,7 @@ do_add_addrroute(NMPlatform *platform, if (nle < 0) { _LOGE("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name, - nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, sbuf1, sizeof(sbuf1)), nm_strerror(nle), -nle); return -NME_PL_NETLINK; @@ -7344,7 +7350,7 @@ do_add_addrroute(NMPlatform *platform, : LOGL_WARN, "do-add-%s[%s]: %s", NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name, - nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, sbuf1, sizeof(sbuf1)), wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf))); if (NMP_OBJECT_GET_TYPE(obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) { @@ -7365,6 +7371,7 @@ do_add_addrroute(NMPlatform *platform, static gboolean do_delete_object(NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) { + char sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; gs_free char *errmsg = NULL; int nle; @@ -7383,7 +7390,7 @@ do_delete_object(NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *n if (nle < 0) { _LOGE("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name, - nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, sbuf1, sizeof(sbuf1)), nm_strerror(nle), -nle); return FALSE; @@ -7402,6 +7409,8 @@ do_delete_object(NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *n && NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id), NMP_OBJECT_TYPE_IP6_ADDRESS)) { /* On RHEL7 kernel, deleting a non existing address fails with ENXIO */ log_detail = ", meaning the address was already removed"; + } else if (NM_IN_SET(-((int) seq_result), ENODEV)) { + log_detail = ", meaning the device was already removed"; } else if (NM_IN_SET(-((int) seq_result), EADDRNOTAVAIL) && NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id), NMP_OBJECT_TYPE_IP4_ADDRESS, @@ -7413,7 +7422,7 @@ do_delete_object(NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *n _NMLOG(success ? LOGL_DEBUG : LOGL_WARN, "do-delete-%s[%s]: %s%s", NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name, - nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, sbuf1, sizeof(sbuf1)), wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf)), log_detail); @@ -7534,6 +7543,21 @@ out: } static int +link_change(NMPlatform *platform, NMLinkType type, int ifindex, gconstpointer extra_data) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + + nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, 0); + if (!nlmsg) + return -NME_UNSPEC; + + if (!_nl_msg_new_link_set_linkinfo(nlmsg, type, extra_data)) + return -NME_UNSPEC; + + return do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); +} + +static int link_add(NMPlatform *platform, NMLinkType type, const char *name, @@ -8451,6 +8475,7 @@ static gboolean wifi_get_capabilities(NMPlatform *platform, int ifindex, _NMDeviceWifiCapabilities *caps) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + if (caps) *caps = nm_wifi_utils_get_caps(wifi_data); return TRUE; @@ -8460,6 +8485,7 @@ static guint32 wifi_get_frequency(NMPlatform *platform, int ifindex) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0); + return nm_wifi_utils_get_freq(wifi_data); } @@ -8471,6 +8497,7 @@ wifi_get_station(NMPlatform *platform, guint32 *out_rate) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_get_station(wifi_data, out_bssid, out_quality, out_rate); } @@ -8478,6 +8505,7 @@ static _NM80211Mode wifi_get_mode(NMPlatform *platform, int ifindex) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, _NM_802_11_MODE_UNKNOWN); + return nm_wifi_utils_get_mode(wifi_data); } @@ -8485,6 +8513,7 @@ static void wifi_set_mode(NMPlatform *platform, int ifindex, _NM80211Mode mode) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, ); + nm_wifi_utils_set_mode(wifi_data, mode); } @@ -8492,6 +8521,7 @@ static void wifi_set_powersave(NMPlatform *platform, int ifindex, guint32 powersave) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, ); + nm_wifi_utils_set_powersave(wifi_data, powersave); } @@ -8499,6 +8529,7 @@ static guint32 wifi_find_frequency(NMPlatform *platform, int ifindex, const guint32 *freqs) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0); + return nm_wifi_utils_find_freq(wifi_data, freqs); } @@ -8506,6 +8537,7 @@ static void wifi_indicate_addressing_running(NMPlatform *platform, int ifindex, gboolean running) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, ); + nm_wifi_utils_indicate_addressing_running(wifi_data, running); } @@ -8513,6 +8545,7 @@ static _NMSettingWirelessWakeOnWLan wifi_get_wake_on_wlan(NMPlatform *platform, int ifindex) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_get_wake_on_wlan(wifi_data); } @@ -8520,9 +8553,26 @@ static gboolean wifi_set_wake_on_wlan(NMPlatform *platform, int ifindex, _NMSettingWirelessWakeOnWLan wowl) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_set_wake_on_wlan(wifi_data, wowl); } +static gboolean +wifi_get_csme_conn_info(NMPlatform *platform, int ifindex, NMPlatformCsmeConnInfo *out_conn_info) +{ + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + + return nm_wifi_utils_get_csme_conn_info(wifi_data, out_conn_info); +} + +static gboolean +wifi_get_device_from_csme(NMPlatform *platform, int ifindex) +{ + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + + return nm_wifi_utils_get_device_from_csme(wifi_data); +} + /*****************************************************************************/ static gboolean @@ -8565,6 +8615,7 @@ static guint32 mesh_get_channel(NMPlatform *platform, int ifindex) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0); + return nm_wifi_utils_get_mesh_channel(wifi_data); } @@ -8572,6 +8623,7 @@ static gboolean mesh_set_channel(NMPlatform *platform, int ifindex, guint32 channel) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_set_mesh_channel(wifi_data, channel); } @@ -8579,6 +8631,7 @@ static gboolean mesh_set_ssid(NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_set_mesh_ssid(wifi_data, ssid, len); } @@ -9727,13 +9780,10 @@ constructed(GObject *_object) fd); priv->event_source = - nm_g_unix_fd_source_new(fd, + nm_g_unix_fd_add_source(fd, G_IO_IN | G_IO_NVAL | G_IO_PRI | G_IO_ERR | G_IO_HUP, - G_PRIORITY_DEFAULT, event_handler, - platform, - NULL); - g_source_attach(priv->event_source, NULL); + platform); /* complete construction of the GObject instance before populating the cache. */ G_OBJECT_CLASS(nm_linux_platform_parent_class)->constructed(_object); @@ -9889,6 +9939,7 @@ nm_linux_platform_class_init(NMLinuxPlatformClass *klass) platform_class->sysctl_get = sysctl_get; platform_class->link_add = link_add; + platform_class->link_change = link_change; platform_class->link_delete = link_delete; platform_class->link_refresh = link_refresh; @@ -9938,6 +9989,8 @@ nm_linux_platform_class_init(NMLinuxPlatformClass *klass) platform_class->wifi_indicate_addressing_running = wifi_indicate_addressing_running; platform_class->wifi_get_wake_on_wlan = wifi_get_wake_on_wlan; platform_class->wifi_set_wake_on_wlan = wifi_set_wake_on_wlan; + platform_class->wifi_get_csme_conn_info = wifi_get_csme_conn_info; + platform_class->wifi_get_device_from_csme = wifi_get_device_from_csme; platform_class->mesh_get_channel = mesh_get_channel; platform_class->mesh_set_channel = mesh_set_channel; diff --git a/src/libnm-platform/nm-platform-utils.c b/src/libnm-platform/nm-platform-utils.c index 9ad030df..bebc53a8 100644 --- a/src/libnm-platform/nm-platform-utils.c +++ b/src/libnm-platform/nm-platform-utils.c @@ -2132,12 +2132,15 @@ guint32 nmp_utils_lifetime_get(guint32 timestamp, guint32 lifetime, guint32 preferred, - gint32 now, + gint32 *cached_now, guint32 *out_preferred) { - guint32 t_lifetime, t_preferred; + guint32 t_lifetime; + guint32 t_preferred; + gint32 now; - nm_assert(now >= 0); + nm_assert(cached_now); + nm_assert(*cached_now >= 0); if (timestamp == 0 && lifetime == 0) { /* We treat lifetime==0 && timestamp==0 addresses as permanent addresses to allow easy @@ -2150,8 +2153,7 @@ nmp_utils_lifetime_get(guint32 timestamp, return NM_PLATFORM_LIFETIME_PERMANENT; } - if (now <= 0) - now = nm_utils_get_monotonic_timestamp_sec(); + now = nm_utils_get_monotonic_timestamp_sec_cached(cached_now); t_lifetime = nmp_utils_lifetime_rebase_relative_time_on_now(timestamp, lifetime, now); if (!t_lifetime) { diff --git a/src/libnm-platform/nm-platform-utils.h b/src/libnm-platform/nm-platform-utils.h index a9ccebb3..9f17da48 100644 --- a/src/libnm-platform/nm-platform-utils.h +++ b/src/libnm-platform/nm-platform-utils.h @@ -86,7 +86,7 @@ nmp_utils_lifetime_rebase_relative_time_on_now(guint32 timestamp, guint32 durati guint32 nmp_utils_lifetime_get(guint32 timestamp, guint32 lifetime, guint32 preferred, - gint32 now, + gint32 *cached_now, guint32 *out_preferred); int nmp_utils_modprobe(GError **error, gboolean suppress_error_logging, const char *arg1, ...) diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index 922f412d..8866a436 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -1304,6 +1304,45 @@ nm_platform_link_add(NMPlatform *self, ->link_add(self, type, name, parent, address, address_len, mtu, extra_data, out_link); } +int +nm_platform_link_change(NMPlatform *self, NMLinkType type, int ifindex, gconstpointer extra_data) +{ + char buf[512]; + const char *name = nm_platform_link_get_name(self, ifindex); + + _CHECK_SELF(self, klass, -NME_BUG); + + _LOG2D("link: changing link: " + "%s " /* type */ + "\"%s\"" /* name */ + "%s" /* extra_data */ + "", + nm_link_type_to_string(type), + name, + ({ + char *buf_p = buf; + gsize buf_len = sizeof(buf); + + buf[0] = '\0'; + + switch (type) { + case NM_LINK_TYPE_BRIDGE: + nm_strbuf_append_str(&buf_p, &buf_len, ", "); + nm_platform_lnk_bridge_to_string((const NMPlatformLnkBridge *) extra_data, + buf_p, + buf_len); + break; + default: + nm_assert(!extra_data); + break; + } + + buf; + })); + + return klass->link_change(self, type, ifindex, extra_data); +} + /** * nm_platform_link_delete: * @self: platform instance @@ -1834,11 +1873,14 @@ nm_platform_link_set_sriov_vfs(NMPlatform *self, int ifindex, const NMPlatformVF g_return_val_if_fail(ifindex > 0, FALSE); - _LOG3D("link: setting VFs"); - for (i = 0; vfs[i]; i++) { - const NMPlatformVF *vf = vfs[i]; + if (_LOGD_ENABLED()) { + _LOG3D("link: setting VFs"); + for (i = 0; vfs[i]; i++) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + const NMPlatformVF *vf = vfs[i]; - _LOG3D("link: VF %s", nm_platform_vf_to_string(vf, NULL, 0)); + _LOG3D("link: VF %s", nm_platform_vf_to_string(vf, sbuf, sizeof(sbuf))); + } } return klass->link_set_sriov_vfs(self, ifindex, vfs); @@ -1855,14 +1897,18 @@ nm_platform_link_set_bridge_vlans(NMPlatform *self, 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)); + if (_LOGD_ENABLED()) { + _LOG3D("link: %s bridge VLANs on %s", + vlans ? "setting" : "clearing", + on_master ? "master" : "self"); + if (vlans) { + for (i = 0; vlans[i]; i++) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + const NMPlatformBridgeVlan *vlan = vlans[i]; + + _LOG3D("link: bridge VLAN %s", + nm_platform_bridge_vlan_to_string(vlan, sbuf, sizeof(sbuf))); + } } } @@ -2092,7 +2138,10 @@ nm_platform_link_release(NMPlatform *self, int master, int ifindex) if (nm_platform_link_get_master(self, ifindex) != master) return FALSE; - _LOG3D("link: releasing from master '%s'", nm_platform_link_get_name(self, master)); + _LOG3D("link: releasing %d from master '%s' (%d)", + ifindex, + nm_platform_link_get_name(self, master), + master); return klass->link_release(self, master, ifindex); } @@ -3058,6 +3107,28 @@ nm_platform_wifi_set_wake_on_wlan(NMPlatform *self, int ifindex, _NMSettingWirel return klass->wifi_set_wake_on_wlan(self, ifindex, wowl); } +gboolean +nm_platform_wifi_get_csme_conn_info(NMPlatform *self, + int ifindex, + NMPlatformCsmeConnInfo *out_conn_info) +{ + _CHECK_SELF(self, klass, FALSE); + + g_return_val_if_fail(ifindex > 0, FALSE); + + return klass->wifi_get_csme_conn_info(self, ifindex, out_conn_info); +} + +gboolean +nm_platform_wifi_get_device_from_csme(NMPlatform *self, int ifindex) +{ + _CHECK_SELF(self, klass, FALSE); + + g_return_val_if_fail(ifindex > 0, FALSE); + + return klass->wifi_get_device_from_csme(self, ifindex); +} + guint32 nm_platform_mesh_get_channel(NMPlatform *self, int ifindex) { @@ -3479,6 +3550,7 @@ nm_platform_ip4_address_add(NMPlatform *self, FALSE); if (_LOGD_ENABLED()) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatformIP4Address addr; addr = (NMPlatformIP4Address){ @@ -3497,7 +3569,7 @@ nm_platform_ip4_address_add(NMPlatform *self, g_strlcpy(addr.label, label, sizeof(addr.label)); _LOG3D("address: adding or updating IPv4 address: %s", - nm_platform_ip4_address_to_string(&addr, NULL, 0)); + nm_platform_ip4_address_to_string(&addr, sbuf, sizeof(sbuf))); } return klass->ip4_address_add(self, ifindex, @@ -3529,6 +3601,7 @@ nm_platform_ip6_address_add(NMPlatform *self, g_return_val_if_fail(preferred <= lifetime, FALSE); if (_LOGD_ENABLED()) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatformIP6Address addr = {0}; addr.ifindex = ifindex; @@ -3541,7 +3614,7 @@ nm_platform_ip6_address_add(NMPlatform *self, addr.n_ifa_flags = flags; _LOG3D("address: adding or updating IPv6 address: %s", - nm_platform_ip6_address_to_string(&addr, NULL, 0)); + nm_platform_ip6_address_to_string(&addr, sbuf, sizeof(sbuf))); } return klass ->ip6_address_add(self, ifindex, address, plen, peer_address, lifetime, preferred, flags); @@ -3592,6 +3665,41 @@ nm_platform_ip6_address_delete(NMPlatform *self, int ifindex, struct in6_addr ad return klass->ip6_address_delete(self, ifindex, address, plen); } +const NMPObject * +nm_platform_ip_address_get(NMPlatform *self, + int addr_family, + int ifindex, + gconstpointer /* (NMPlatformIPAddress *) */ needle) +{ + const NMPlatformIPXAddress *addr; + NMPObject obj_id; + const NMPObject *obj; + + nm_assert(NM_IS_PLATFORM(self)); + nm_assert_addr_family(addr_family); + nm_assert(needle); + + addr = needle; + + if (ifindex <= 0) { + /* We allow the caller to override the ifindex. */ + ifindex = addr->ax.ifindex; + } + + if (NM_IS_IPv4(addr_family)) { + nmp_object_stackinit_id_ip4_address(&obj_id, + ifindex, + addr->a4.address, + addr->a4.plen, + addr->a4.peer_address); + } else + nmp_object_stackinit_id_ip6_address(&obj_id, ifindex, &addr->a6.address); + + obj = nmp_cache_lookup_obj(nm_platform_get_cache(self), &obj_id); + nm_assert(!obj || nmp_object_is_visible(obj)); + return obj; +} + const NMPlatformIP4Address * nm_platform_ip4_address_get(NMPlatform *self, int ifindex, @@ -3632,7 +3740,7 @@ static gboolean _addr_array_clean_expired(int addr_family, int ifindex, GPtrArray *array, - guint32 now, + gint32 *cached_now, GHashTable **idx) { guint i; @@ -3640,7 +3748,8 @@ _addr_array_clean_expired(int addr_family, nm_assert_addr_family(addr_family); nm_assert(ifindex > 0); - nm_assert(now > 0); + nm_assert(cached_now); + nm_assert(*cached_now >= 0); if (!array) return FALSE; @@ -3674,17 +3783,16 @@ _addr_array_clean_expired(int addr_family, goto clear_and_next; } - if (!nmp_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, now, NULL)) + if (!nmp_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, cached_now, NULL)) goto clear_and_next; - if (idx) { - if (G_UNLIKELY(!*idx)) { - *idx = g_hash_table_new((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal); - } - if (!g_hash_table_add(*idx, (gpointer) NMP_OBJECT_UP_CAST(a))) - nm_assert_not_reached(); + if (G_UNLIKELY(!*idx)) { + *idx = + g_hash_table_new((GHashFunc) nmp_object_id_hash, (GEqualFunc) nmp_object_id_equal); } + if (!g_hash_table_add(*idx, (gpointer) NMP_OBJECT_UP_CAST(a))) + nm_assert_not_reached(); + any_addrs = TRUE; continue; @@ -3698,8 +3806,8 @@ clear_and_next: static gboolean ip4_addr_subnets_is_plain_address(const GPtrArray *addresses, gconstpointer needle) { - return needle >= (gconstpointer) &addresses->pdata[0] - && needle < (gconstpointer) &addresses->pdata[addresses->len]; + return nm_ptr_to_uintptr(needle) >= nm_ptr_to_uintptr(&addresses->pdata[0]) + && nm_ptr_to_uintptr(needle) < nm_ptr_to_uintptr(&addresses->pdata[addresses->len]); } static const NMPObject ** @@ -3733,6 +3841,30 @@ ip4_addr_subnets_destroy_index(GHashTable *subnets, const GPtrArray *addresses) g_hash_table_unref(subnets); } +static guint +_ip4_addr_subnets_hash(gconstpointer ptr) +{ + const NMPlatformIP4Address *addr = NMP_OBJECT_CAST_IP4_ADDRESS(ptr); + NMHashState h; + + nm_hash_init(&h, 3282159733); + nm_hash_update_vals(&h, + addr->plen, + nm_utils_ip4_address_clear_host_address(addr->address, addr->plen)); + return nm_hash_complete(&h); +} + +static gboolean +_ip4_addr_subnets_equal(gconstpointer p_a, gconstpointer p_b) +{ + const NMPlatformIP4Address *a = NMP_OBJECT_CAST_IP4_ADDRESS(p_a); + const NMPlatformIP4Address *b = NMP_OBJECT_CAST_IP4_ADDRESS(p_b); + + return a->plen == b->plen + && (nm_utils_ip4_address_clear_host_address(a->address, a->plen) + == nm_utils_ip4_address_clear_host_address(b->address, b->plen)); +} + static GHashTable * ip4_addr_subnets_build_index(const GPtrArray *addresses, gboolean consider_flags, @@ -3743,34 +3875,35 @@ ip4_addr_subnets_build_index(const GPtrArray *addresses, nm_assert(addresses && addresses->len); - subnets = g_hash_table_new(nm_direct_hash, NULL); + subnets = g_hash_table_new(_ip4_addr_subnets_hash, _ip4_addr_subnets_equal); /* Build a hash table of all addresses per subnet */ for (i = 0; i < addresses->len; i++) { + const NMPObject **p_obj; + const NMPObject *obj; const NMPlatformIP4Address *address; - gpointer p_address; GPtrArray *addr_list; - guint32 net; int position; gpointer p; if (!addresses->pdata[i]) continue; - p_address = &addresses->pdata[i]; - address = NMP_OBJECT_CAST_IP4_ADDRESS(addresses->pdata[i]); + p_obj = (const NMPObject **) &addresses->pdata[i]; + obj = *p_obj; - net = address->address & _nm_utils_ip4_prefix_to_netmask(address->plen); - if (!g_hash_table_lookup_extended(subnets, GUINT_TO_POINTER(net), NULL, &p)) { - g_hash_table_insert(subnets, GUINT_TO_POINTER(net), p_address); + if (!g_hash_table_lookup_extended(subnets, obj, NULL, &p)) { + g_hash_table_insert(subnets, (gpointer) obj, p_obj); continue; } nm_assert(p); + address = NMP_OBJECT_CAST_IP4_ADDRESS(obj); + if (full_index) { if (ip4_addr_subnets_is_plain_address(addresses, p)) { addr_list = g_ptr_array_new(); - g_hash_table_insert(subnets, GUINT_TO_POINTER(net), addr_list); + g_hash_table_insert(subnets, (gpointer) obj, addr_list); g_ptr_array_add(addr_list, p); } else addr_list = p; @@ -3779,13 +3912,13 @@ ip4_addr_subnets_build_index(const GPtrArray *addresses, position = -1; /* append */ else position = 0; /* prepend */ - g_ptr_array_insert(addr_list, position, p_address); + g_ptr_array_insert(addr_list, position, p_obj); } else { /* we only care about the primary. No need to track the secondaries * as a GPtrArray. */ nm_assert(ip4_addr_subnets_is_plain_address(addresses, p)); if (consider_flags && !NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_SECONDARY)) { - g_hash_table_insert(subnets, GUINT_TO_POINTER(net), p_address); + g_hash_table_insert(subnets, (gpointer) obj, p_obj); } } } @@ -3811,16 +3944,11 @@ ip4_addr_subnets_is_secondary(const NMPObject *address, const GPtrArray *addresses, const GPtrArray **out_addr_list) { - const NMPlatformIP4Address *a; - const GPtrArray *addr_list; - gconstpointer p; - guint32 net; - const NMPObject **o; + const GPtrArray *addr_list; + gconstpointer p; + const NMPObject **o; - a = NMP_OBJECT_CAST_IP4_ADDRESS(address); - - net = a->address & _nm_utils_ip4_prefix_to_netmask(a->plen); - p = g_hash_table_lookup(subnets, GUINT_TO_POINTER(net)); + p = g_hash_table_lookup(subnets, address); nm_assert(p); if (!ip4_addr_subnets_is_plain_address(addresses, p)) { addr_list = p; @@ -3857,41 +3985,33 @@ ip6_address_scope(const NMPlatformIP6Address *a) } static int -ip6_address_scope_cmp(gconstpointer p_a, gconstpointer p_b, gpointer increasing) +ip6_address_scope_cmp_ascending(gconstpointer p_a, gconstpointer p_b, gpointer unused) { - const NMPlatformIP6Address *a; - const NMPlatformIP6Address *b; - - if (!increasing) - NM_SWAP(&p_a, &p_b); - - a = NMP_OBJECT_CAST_IP6_ADDRESS(*(const NMPObject *const *) p_a); - b = NMP_OBJECT_CAST_IP6_ADDRESS(*(const NMPObject *const *) p_b); - - NM_CMP_DIRECT(ip6_address_scope(a), ip6_address_scope(b)); + NM_CMP_DIRECT(ip6_address_scope(NMP_OBJECT_CAST_IP6_ADDRESS(*(const NMPObject *const *) p_a)), + ip6_address_scope(NMP_OBJECT_CAST_IP6_ADDRESS(*(const NMPObject *const *) p_b))); return 0; } +static int +ip6_address_scope_cmp_descending(gconstpointer p_a, gconstpointer p_b, gpointer unused) +{ + return ip6_address_scope_cmp_ascending(p_b, p_a, NULL); +} + /** * nm_platform_ip_address_sync: * @self: platform instance * @addr_family: the address family AF_INET or AF_INET6. * @ifindex: Interface index - * @known_addresses: List of addresses. The list will be modified and only - * addresses that were successfully added will be kept in the list. - * That means, expired addresses and addresses that could not be added - * will be dropped. - * Hence, the input argument @known_addresses is also an output argument - * telling which addresses were successfully added. - * Addresses are removed by unrefing the instance via nmp_object_unref() - * and leaving a NULL tombstone. + * @known_addresses: List of addresses. The list will be modified and + * expired addresses will be cleared (by calling nmp_object_unref() + * on the array element). * @addresses_prune: (allow-none): the list of addresses to delete. * If platform has such an address configured, it will be deleted * at the beginning of the sync. Note that the array will be modified * by the function. - * Note that the addresses must be properly sorted, by their priority. - * Create this list with nm_platform_ip_address_get_prune_list() which - * gets the sorting right. + * Addresses that are both contained in @known_addresses and @addresses_prune + * will be configured. * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -3906,11 +4026,14 @@ nm_platform_ip_address_sync(NMPlatform *self, GPtrArray *known_addresses, GPtrArray *addresses_prune) { - const gint32 now = nm_utils_get_monotonic_timestamp_sec(); - const int IS_IPv4 = NM_IS_IPv4(addr_family); - gs_unref_hashtable GHashTable *known_addresses_idx = NULL; - GPtrArray *plat_addresses; - GHashTable *known_subnets = NULL; + gint32 now = 0; + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMPLookup lookup; + const gboolean EXTRA_LOGGING = FALSE; + gs_unref_hashtable GHashTable *known_addresses_idx = NULL; + gs_unref_hashtable GHashTable *plat_addrs_to_delete = NULL; + gs_unref_ptrarray GPtrArray *plat_addresses = NULL; + gboolean success; guint i_plat; guint i_know; guint i; @@ -3918,74 +4041,181 @@ nm_platform_ip_address_sync(NMPlatform *self, _CHECK_SELF(self, klass, FALSE); +#define _plat_addrs_to_delete_ensure(ptr) \ + ({ \ + GHashTable **_ptr = (ptr); \ + \ + if (!*_ptr) { \ + *_ptr = g_hash_table_new_full((GHashFunc) nmp_object_id_hash, \ + (GEqualFunc) nmp_object_id_equal, \ + (GDestroyNotify) nmp_object_unref, \ + NULL); \ + } \ + *_ptr; \ + }) + + /* Disabled. Enable this for printf debugging. */ + if (EXTRA_LOGGING) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + char sbuf1[50]; + + _LOG3T("IPv%c address sync on %d (%u addresses, %u to prune)", + nm_utils_addr_family_to_char(addr_family), + ifindex, + nm_g_ptr_array_len(known_addresses), + nm_g_ptr_array_len(addresses_prune)); + for (i = 0; known_addresses && i < known_addresses->len; i++) { + _LOG3T(" address#%u: %s%s", + i, + nmp_object_to_string(known_addresses->pdata[i], + NMP_OBJECT_TO_STRING_ALL, + sbuf, + sizeof(sbuf)), + IS_IPv4 ? "" + : nm_sprintf_buf(sbuf1, + " (scope %d)", + (int) ip6_address_scope(NMP_OBJECT_CAST_IP6_ADDRESS( + known_addresses->pdata[i])))); + } + for (i = 0; addresses_prune && i < addresses_prune->len; i++) { + _LOG3T(" prune #%u: %s", + i, + nmp_object_to_string(addresses_prune->pdata[i], + NMP_OBJECT_TO_STRING_ALL, + sbuf, + sizeof(sbuf))); + } + } + + /* @known_addresses are in decreasing priority order (highest priority addresses first). */ + /* The order we want to enforce is only among addresses with the same * scope, as the kernel keeps addresses sorted by scope. Therefore, * apply the same sorting to known addresses, so that we don't try to * unnecessary change the order of addresses with different scopes. */ if (!IS_IPv4) { if (known_addresses) - g_ptr_array_sort_with_data(known_addresses, - ip6_address_scope_cmp, - GINT_TO_POINTER(TRUE)); + g_ptr_array_sort_with_data(known_addresses, ip6_address_scope_cmp_descending, NULL); } if (!_addr_array_clean_expired(addr_family, ifindex, known_addresses, - now, + &now, &known_addresses_idx)) known_addresses = NULL; - /* @plat_addresses must be sorted in decreasing priority order (highest priority addresses first), contrary to - * @known_addresses which is in increasing priority order (lowest priority addresses first). */ - plat_addresses = addresses_prune; + if (nm_g_ptr_array_len(addresses_prune) > 0) { + /* First delete addresses that we should prune (and which are no longer tracked + * as @known_addresses. */ + for (i = 0; i < addresses_prune->len; i++) { + const NMPObject *prune_obj = addresses_prune->pdata[i]; + + nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(prune_obj), + NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS)); + + if (nm_g_hash_table_contains(known_addresses_idx, prune_obj)) + continue; + + nm_platform_ip_address_delete(self, + addr_family, + ifindex, + NMP_OBJECT_CAST_IP_ADDRESS(prune_obj)); + } + } + + /* ensure we have the platform cache up to date. */ + nm_platform_process_events(self); + + /* @plat_addresses for IPv6 must be sorted in decreasing priority order (highest priority addresses first). + * IPv4 are probably unsorted or sorted with lowest priority first, but their order doesn't matter because + * we check the "secondary" flag. */ + plat_addresses = nm_platform_lookup_clone( + self, + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex), + NULL, + NULL); + + if (EXTRA_LOGGING && plat_addresses) { + for (i = 0; i < plat_addresses->len; i++) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + char sbuf1[50]; + + _LOG3T(" platform#%u: %s%s", + i, + nmp_object_to_string(plat_addresses->pdata[i], + NMP_OBJECT_TO_STRING_ALL, + sbuf, + sizeof(sbuf)), + IS_IPv4 ? "" + : nm_sprintf_buf(sbuf1, + " (scope %d)", + (int) ip6_address_scope(NMP_OBJECT_CAST_IP6_ADDRESS( + plat_addresses->pdata[i])))); + } + } if (nm_g_ptr_array_len(plat_addresses) > 0) { - /* Delete unknown addresses */ + /* Delete addresses that interfere with our intended order. */ if (IS_IPv4) { - GHashTable *plat_subnets; + GHashTable *known_subnets = NULL; + GHashTable *plat_subnets; + gs_free bool *plat_handled_to_free = NULL; + bool *plat_handled = NULL; + + /* For IPv4, we only consider it a conflict for addresses in the same + * subnet. That's where kernel will assign a primary/secondary flag. + * For different subnets, we don't define the order. */ plat_subnets = ip4_addr_subnets_build_index(plat_addresses, TRUE, TRUE); for (i = 0; i < plat_addresses->len; i++) { - const NMPObject *plat_obj; + const NMPObject *plat_obj = plat_addresses->pdata[i]; + const NMPObject *known_obj; const NMPlatformIP4Address *plat_address; const GPtrArray *addr_list; + gboolean secondary; - plat_obj = plat_addresses->pdata[i]; - if (!plat_obj) { - /* Already deleted */ + if (plat_handled && plat_handled[i]) continue; - } - plat_address = NMP_OBJECT_CAST_IP4_ADDRESS(plat_obj); + known_obj = nm_g_hash_table_lookup(known_addresses_idx, plat_obj); - if (known_addresses) { - const NMPObject *o; + if (!known_obj) { + /* this address is added externally. Even if it's presence would mess + * with our desired order, we cannot delete it. Skip it. */ + if (!plat_handled) { + plat_handled = nm_malloc0_maybe_a(300, + sizeof(bool) * plat_addresses->len, + &plat_handled_to_free); + } + plat_handled[i] = TRUE; + continue; + } - o = g_hash_table_lookup(known_addresses_idx, plat_obj); - if (o) { - gboolean secondary; + if (!known_subnets) + known_subnets = ip4_addr_subnets_build_index(known_addresses, FALSE, FALSE); - if (!known_subnets) - known_subnets = - ip4_addr_subnets_build_index(known_addresses, FALSE, FALSE); + plat_address = NMP_OBJECT_CAST_IP4_ADDRESS(plat_obj); - secondary = - ip4_addr_subnets_is_secondary(o, known_subnets, known_addresses, NULL); - if (secondary == NM_FLAGS_HAS(plat_address->n_ifa_flags, IFA_F_SECONDARY)) { - /* if we have an existing known-address, with matching secondary role, - * do not delete the platform-address. */ - continue; - } - } + secondary = + ip4_addr_subnets_is_secondary(known_obj, known_subnets, known_addresses, NULL); + if (secondary == NM_FLAGS_HAS(plat_address->n_ifa_flags, IFA_F_SECONDARY)) { + /* if we have an existing known-address, with matching secondary role, + * do not delete the platform-address. */ + continue; } - nm_platform_ip4_address_delete(self, - ifindex, - plat_address->address, - plat_address->plen, - plat_address->peer_address); + if (!plat_handled) { + plat_handled = nm_malloc0_maybe_a(300, + sizeof(bool) * plat_addresses->len, + &plat_handled_to_free); + } + plat_handled[i] = TRUE; + + g_hash_table_add(_plat_addrs_to_delete_ensure(&plat_addrs_to_delete), + (gpointer) nmp_object_ref(plat_obj)); if (!ip4_addr_subnets_is_secondary(plat_obj, plat_subnets, @@ -3999,74 +4229,73 @@ nm_platform_ip_address_sync(NMPlatform *self, * addresses are deleted, so that we can start with a clean * slate and add addresses in the right order. */ for (j = 1; j < addr_list->len; j++) { - const NMPObject **o; + const NMPObject **o = ip4_addr_subnets_addr_list_get(addr_list, j); + guint o_idx; - o = ip4_addr_subnets_addr_list_get(addr_list, j); - nm_assert(o); + o_idx = (o - ((const NMPObject **) &plat_addresses->pdata[0])); - if (*o) { - const NMPlatformIP4Address *a; + nm_assert(o_idx < plat_addresses->len); + nm_assert(o == ((const NMPObject **) &plat_addresses->pdata[o_idx])); - a = NMP_OBJECT_CAST_IP4_ADDRESS(*o); - nm_platform_ip4_address_delete(self, - ifindex, - a->address, - a->plen, - a->peer_address); - nmp_object_unref(*o); - *o = NULL; + if (plat_handled[o_idx]) + continue; + + plat_handled[o_idx] = TRUE; + + if (!nm_g_hash_table_contains(known_addresses_idx, *o)) { + /* Again, this is an external address. We cannot delete + * it to fix the address order. Pass. */ + continue; } + + g_hash_table_add(_plat_addrs_to_delete_ensure(&plat_addrs_to_delete), + (gpointer) nmp_object_ref(*o)); } } } ip4_addr_subnets_destroy_index(plat_subnets, plat_addresses); + ip4_addr_subnets_destroy_index(known_subnets, known_addresses); } else { - guint known_addresses_len; IP6AddrScope cur_scope; gboolean delete_remaining_addrs; - g_ptr_array_sort_with_data(plat_addresses, - ip6_address_scope_cmp, - GINT_TO_POINTER(FALSE)); + /* For IPv6, we only compare addresses per-scope. Addresses in different + * scopes don't have a defined order. */ - known_addresses_len = known_addresses ? known_addresses->len : 0; + g_ptr_array_sort_with_data(plat_addresses, ip6_address_scope_cmp_descending, NULL); - /* First, compare every address whether it is still a "known address", that is, whether - * to keep it or to delete it. - * - * If we don't find a matching valid address in @known_addresses, we will delete - * plat_addr. - * - * Certain addresses, like temporary addresses, are ignored by this function - * if not run with full_sync. These addresses are usually not managed by NetworkManager - * directly, or at least, they are not managed via nm_platform_ip6_address_sync(). - * Only in full_sync mode, we really want to get rid of them (usually, when we take - * the interface down). - * - * Note that we mark handled addresses by setting it to %NULL in @plat_addresses array. */ + /* First, check that existing addresses have a matching plen as the ones + * we are about to configure (@known_addresses). If not, delete them. */ for (i_plat = 0; i_plat < plat_addresses->len; i_plat++) { - const NMPObject *plat_obj = plat_addresses->pdata[i_plat]; - const NMPObject *know_obj; - const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj); - - if (known_addresses_idx) { - know_obj = g_hash_table_lookup(known_addresses_idx, plat_obj); - if (know_obj - && plat_addr->plen == NMP_OBJECT_CAST_IP6_ADDRESS(know_obj)->plen) { - /* technically, plen is not part of the ID for IPv6 addresses and thus - * @plat_addr is essentially the same address as @know_addr (regrading - * its identity, not its other attributes). - * However, we cannot modify an existing addresses' plen without - * removing and readding it. Thus, only keep plat_addr, if the plen - * matches. - * - * keep this one, and continue */ - continue; - } + const NMPObject *plat_obj = plat_addresses->pdata[i_plat]; + const NMPObject *known_obj; + + known_obj = nm_g_hash_table_lookup(known_addresses_idx, plat_obj); + if (!known_obj) { + /* We don't know this address. It was added externally. Keep it configured. + * We also don't want to delete the address below, so mark it as handled + * by clearing the pointer. */ + nm_clear_pointer(&plat_addresses->pdata[i_plat], nmp_object_unref); + continue; } - nm_platform_ip6_address_delete(self, ifindex, plat_addr->address, plat_addr->plen); - nmp_object_unref(g_steal_pointer(&plat_addresses->pdata[i_plat])); + if (NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj)->plen + != NMP_OBJECT_CAST_IP6_ADDRESS(known_obj)->plen) { + /* technically, plen is not part of the ID for IPv6 addresses and thus + * @plat_addr is essentially the same address as @know_addr (w.r.t. + * its identity, not its other attributes). + * However, we cannot modify an existing addresses' plen without + * removing and readding it. Thus, we need to delete plat_addr. + * + * We don't just add this address to @plat_addrs_to_delete, because + * it's too different. Instead, delete and re-add below. */ + nm_platform_ip_address_delete(self, + AF_INET6, + ifindex, + NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj)); + /* Mark address as handled. */ + nm_clear_pointer(&plat_addresses->pdata[i_plat], nmp_object_unref); + } } /* Next, we must preserve the priority of the routes. That is, source address @@ -4077,16 +4306,17 @@ nm_platform_ip_address_sync(NMPlatform *self, * @known_addresses (which has lowest priority first). * * If we find a first discrepancy, we need to delete all remaining addresses - * with same scope from that point on, because below we must re-add all the + * for same scope from that point on, because below we must re-add all the * addresses in the right order to get their priority right. */ cur_scope = IP6_ADDR_SCOPE_LOOPBACK; delete_remaining_addrs = FALSE; i_plat = plat_addresses->len; - i_know = 0; + i_know = nm_g_ptr_array_len(known_addresses); + while (i_plat > 0) { - const NMPlatformIP6Address *plat_addr = - NMP_OBJECT_CAST_IP6_ADDRESS(plat_addresses->pdata[--i_plat]); - IP6AddrScope plat_scope; + const NMPObject *plat_obj = plat_addresses->pdata[--i_plat]; + const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj); + IP6AddrScope plat_scope; if (!plat_addr) continue; @@ -4099,10 +4329,9 @@ nm_platform_ip_address_sync(NMPlatform *self, } if (!delete_remaining_addrs) { - delete_remaining_addrs = TRUE; - for (; i_know < known_addresses_len; i_know++) { + while (i_know > 0) { const NMPlatformIP6Address *know_addr = - NMP_OBJECT_CAST_IP6_ADDRESS(known_addresses->pdata[i_know]); + NMP_OBJECT_CAST_IP6_ADDRESS(known_addresses->pdata[--i_know]); IP6AddrScope know_scope; if (!know_addr) @@ -4114,18 +4343,18 @@ nm_platform_ip_address_sync(NMPlatform *self, if (IN6_ARE_ADDR_EQUAL(&plat_addr->address, &know_addr->address)) { /* we have a match. Mark address as handled. */ - i_know++; - delete_remaining_addrs = FALSE; goto next_plat; } - /* plat_address has no match. Now delete_remaining_addrs is TRUE and we will - * delete all the remaining addresses with cur_scope. */ + /* "plat_address" has no match. "delete_remaining_addrs" will be set to TRUE and we will + * delete all the remaining addresses with "cur_scope". */ break; } + delete_remaining_addrs = TRUE; } - nm_platform_ip6_address_delete(self, ifindex, plat_addr->address, plat_addr->plen); + g_hash_table_add(_plat_addrs_to_delete_ensure(&plat_addrs_to_delete), + (gpointer) nmp_object_ref(plat_obj)); next_plat:; } } @@ -4134,33 +4363,69 @@ next_plat:; if (!known_addresses) return TRUE; - if (IS_IPv4) - ip4_addr_subnets_destroy_index(known_subnets, known_addresses); + success = TRUE; /* Add missing addresses. New addresses are added by kernel with top * priority. */ - for (i_know = 0; i_know < known_addresses->len; i_know++) { + for (i = 0; i < known_addresses->len; i++) { + const NMPObject *plat_obj; + const NMPObject *known_obj; const NMPlatformIPXAddress *known_address; - const NMPObject *o; guint32 lifetime; guint32 preferred; - o = known_addresses->pdata[i_know]; - if (!o) + /* IPv4 addresses we need to add in the order most important first. + * IPv6 addresses we need to add in the reverse order with least + * important first. Kernel will interpret the last address as most + * important. + * + * @known_addresses is always in the order most-important-first. */ + i_know = IS_IPv4 ? i : (known_addresses->len - i - 1u); + + known_obj = known_addresses->pdata[i_know]; + if (!known_obj) continue; - nm_assert(NMP_OBJECT_GET_TYPE(o) == NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)); + nm_assert(NMP_OBJECT_GET_TYPE(known_obj) == NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)); - known_address = NMP_OBJECT_CAST_IPX_ADDRESS(o); + known_address = NMP_OBJECT_CAST_IPX_ADDRESS(known_obj); lifetime = nmp_utils_lifetime_get(known_address->ax.timestamp, known_address->ax.lifetime, known_address->ax.preferred, - now, + &now, &preferred); nm_assert(lifetime > 0); + plat_obj = nm_platform_ip_address_get(self, addr_family, ifindex, known_address); + + if (plat_obj && nm_g_hash_table_contains(plat_addrs_to_delete, plat_obj)) { + /* This address exists, but it had the wrong priority earlier. We + * cannot just update it, we need to remove it first. */ + nm_platform_ip_address_delete(self, + addr_family, + ifindex, + NMP_OBJECT_CAST_IP_ADDRESS(plat_obj)); + plat_obj = NULL; + } + + if (plat_obj + && nm_platform_vtable_address.vx[IS_IPv4].address_cmp( + known_address, + NMP_OBJECT_CAST_IPX_ADDRESS(plat_obj), + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) + == 0) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + + /* The object is already added. Skip update. */ + _LOG3T( + "address: skip updating IPv%c address: %s", + nm_utils_addr_family_to_char(addr_family), + nmp_object_to_string(known_obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); + continue; + } + if (IS_IPv4) { if (!nm_platform_ip4_address_add( self, @@ -4172,9 +4437,8 @@ next_plat:; lifetime, preferred, IFA_F_NOPREFIXROUTE, - known_address->a4.label)) { - /* ignore error, for unclear reasons. */ - } + known_address->a4.label)) + success = FALSE; } else { if (!nm_platform_ip6_address_add(self, ifindex, @@ -4184,11 +4448,11 @@ next_plat:; lifetime, preferred, IFA_F_NOPREFIXROUTE | known_address->a6.n_ifa_flags)) - return FALSE; + success = FALSE; } } - return TRUE; + return success; } gboolean @@ -4254,7 +4518,7 @@ nm_platform_ip_address_get_prune_list(NMPlatform *self, const int IS_IPv4 = NM_IS_IPv4(addr_family); const NMDedupMultiHeadEntry *head_entry; NMPLookup lookup; - GPtrArray *result; + GPtrArray *result = NULL; CList *iter; nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(NM_IS_IPv4(addr_family)), ifindex); @@ -4264,8 +4528,6 @@ nm_platform_ip_address_get_prune_list(NMPlatform *self, if (!head_entry) return NULL; - result = g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nmp_object_unref); - c_list_for_each (iter, &head_entry->lst_entries_head) { const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj; @@ -4275,13 +4537,12 @@ nm_platform_ip_address_get_prune_list(NMPlatform *self, continue; } + if (!result) + result = g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nmp_object_unref); + g_ptr_array_add(result, (gpointer) nmp_object_ref(obj)); } - if (result->len == 0) { - g_ptr_array_unref(result); - return NULL; - } return result; } @@ -4292,7 +4553,7 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, NMIPRouteTableSyncMode route_table_sync) { NMPLookup lookup; - GPtrArray *routes_prune; + GPtrArray *routes_prune = NULL; const NMDedupMultiHeadEntry *head_entry; CList *iter; NMPlatformIP4Route rt_local4; @@ -4324,8 +4585,6 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, rt_local6.plen = 0; rt_mcast6.plen = 0; - routes_prune = g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nm_dedup_multi_obj_unref); - c_list_for_each (iter, &head_entry->lst_entries_head) { const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj; const NMPlatformIPXRoute *rt = NMP_OBJECT_CAST_IPX_ROUTE(obj); @@ -4463,13 +4722,14 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, break; } + if (!routes_prune) { + routes_prune = + g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nm_dedup_multi_obj_unref); + } + g_ptr_array_add(routes_prune, (gpointer) nmp_object_ref(obj)); } - if (routes_prune->len == 0) { - g_ptr_array_unref(routes_prune); - return NULL; - } return routes_prune; } @@ -4506,8 +4766,8 @@ nm_platform_ip_route_sync(NMPlatform *self, guint i; int i_type; gboolean success = TRUE; - char sbuf1[sizeof(_nm_utils_to_string_buffer)]; - char sbuf2[sizeof(_nm_utils_to_string_buffer)]; + char sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; + char sbuf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; nm_assert(NM_IS_PLATFORM(self)); nm_assert(ifindex > 0); @@ -4854,7 +5114,7 @@ nm_platform_ip_route_normalize(int addr_family, NMPlatformIPRoute *route) static int _ip_route_add(NMPlatform *self, NMPNlmFlags flags, int addr_family, gconstpointer route) { - char sbuf[sizeof(_nm_utils_to_string_buffer)]; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; int ifindex; _CHECK_SELF(self, klass, FALSE); @@ -4906,7 +5166,8 @@ nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformI gboolean nm_platform_object_delete(NMPlatform *self, const NMPObject *obj) { - int ifindex; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + int ifindex; _CHECK_SELF(self, klass, FALSE); @@ -4914,7 +5175,7 @@ nm_platform_object_delete(NMPlatform *self, const NMPObject *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)); + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); break; case NMP_OBJECT_TYPE_IP4_ROUTE: case NMP_OBJECT_TYPE_IP6_ROUTE: @@ -4923,7 +5184,7 @@ nm_platform_object_delete(NMPlatform *self, const NMPObject *obj) 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)); + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); break; default: g_return_val_if_reached(FALSE); @@ -4941,6 +5202,7 @@ nm_platform_ip_route_get(NMPlatform *self, int oif_ifindex, NMPObject **out_route) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; nm_auto_nmpobj NMPObject *route = NULL; int result; char buf[NM_UTILS_INET_ADDRSTRLEN]; @@ -4977,7 +5239,7 @@ nm_platform_ip_route_get(NMPlatform *self, _LOGD("route: get IPv%c route for: %s succeeded: %s", nm_utils_addr_family_to_char(addr_family), inet_ntop(addr_family, address, buf, sizeof(buf)), - nmp_object_to_string(route, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(route, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); NM_SET_OUT(out_route, g_steal_pointer(&route)); } return result; @@ -5004,6 +5266,7 @@ _ip4_dev_route_blacklist_timeout_ms_marked(gint64 timeout_msec) static gboolean _ip4_dev_route_blacklist_check_cb(gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatform *self = user_data; NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); GHashTableIter iter; @@ -5034,7 +5297,7 @@ again: continue; _LOGT("ip4-dev-route: delete %s", - nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); nm_platform_object_delete(self, p_obj); goto again; } @@ -5097,6 +5360,7 @@ _ip4_dev_route_blacklist_notify_route(NMPlatform *self, const NMPObject *obj) static gboolean _ip4_dev_route_blacklist_gc_timeout_handle(gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatform *self = user_data; NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); GHashTableIter iter; @@ -5112,7 +5376,7 @@ _ip4_dev_route_blacklist_gc_timeout_handle(gpointer user_data) while (g_hash_table_iter_next(&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get(*p_timeout_ms)) { _LOGT("ip4-dev-route: cleanup %s", - nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); g_hash_table_iter_remove(&iter); } } @@ -5172,6 +5436,7 @@ nm_platform_ip4_dev_route_blacklist_set(NMPlatform *self, int ifindex, GPtrArray *ip4_dev_route_blacklist) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatformPrivate *priv; GHashTableIter iter; const NMPObject *p_obj; @@ -5227,14 +5492,17 @@ nm_platform_ip4_dev_route_blacklist_set(NMPlatform *self, if (nmp_object_equal(p_obj, o)) { /* un-expire and reuse the entry. */ _LOGT("ip4-dev-route: register %s (update)", - nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(p_obj, + NMP_OBJECT_TO_STRING_PUBLIC, + sbuf, + sizeof(sbuf))); *p_timeout_ms = timeout_msec_val; continue; } } _LOGT("ip4-dev-route: register %s", - nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); p_timeout_ms = g_slice_new(gint64); *p_timeout_ms = timeout_msec_val; g_hash_table_replace(priv->ip4_dev_route_blacklist_hash, @@ -5256,12 +5524,13 @@ nm_platform_routing_rule_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformRoutingRule *routing_rule) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; _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)); + nm_platform_routing_rule_to_string(routing_rule, sbuf, sizeof(sbuf))); return klass->routing_rule_add(self, flags, routing_rule); } @@ -5270,13 +5539,15 @@ nm_platform_routing_rule_add(NMPlatform *self, int nm_platform_qdisc_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformQdisc *qdisc) { - int ifindex = qdisc->ifindex; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + int ifindex = qdisc->ifindex; _CHECK_SELF(self, klass, -NME_BUG); /* Note: @qdisc must not be copied or kept alive because the lifetime of qdisc.kind * is undefined. */ - _LOG3D("adding or updating a qdisc: %s", nm_platform_qdisc_to_string(qdisc, NULL, 0)); + _LOG3D("adding or updating a qdisc: %s", + nm_platform_qdisc_to_string(qdisc, sbuf, sizeof(sbuf))); return klass->qdisc_add(self, flags, qdisc); } @@ -5294,13 +5565,15 @@ nm_platform_qdisc_delete(NMPlatform *self, int ifindex, guint32 parent, gboolean int nm_platform_tfilter_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformTfilter *tfilter) { - int ifindex = tfilter->ifindex; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + int ifindex = tfilter->ifindex; _CHECK_SELF(self, klass, -NME_BUG); /* Note: @tfilter must not be copied or kept alive because the lifetime of tfilter.kind * and tfilter.action.kind is undefined. */ - _LOG3D("adding or updating a tfilter: %s", nm_platform_tfilter_to_string(tfilter, NULL, 0)); + _LOG3D("adding or updating a tfilter: %s", + nm_platform_tfilter_to_string(tfilter, sbuf, sizeof(sbuf))); return klass->tfilter_add(self, flags, tfilter); } @@ -6251,7 +6524,6 @@ nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf "%s" /* label */ " src %s" "%s" /* a_acd_not_ready */ - "%s" /* a_assume_config_once */ "%s" /* a_force_commit */ "", s_address, @@ -6271,7 +6543,6 @@ nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf str_label, nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), address->a_acd_not_ready ? " ip4acd-not-ready" : "", - address->a_assume_config_once ? " assume-config-once" : "", address->a_force_commit ? " force-commit" : ""); g_free(str_peer); return buf; @@ -6392,7 +6663,6 @@ nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf buf, len, "%s/%d lft %s pref %s%s%s%s%s src %s" - "%s" /* a_assume_config_once */ "%s" /* a_force_commit */ "", s_address, @@ -6404,7 +6674,6 @@ nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf str_dev, _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)), nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), - address->a_assume_config_once ? " assume-config-once" : "", address->a_force_commit ? " force-commit" : ""); g_free(str_peer); return buf; @@ -6508,7 +6777,6 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz "%s" /* initcwnd */ "%s" /* initrwnd */ "%s" /* mtu */ - "%s" /* r_assume_config_once */ "%s" /* r_force_commit */ "", nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced), @@ -6567,7 +6835,6 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz route->lock_mtu ? "lock " : "", route->mtu) : "", - route->r_assume_config_once ? " assume-config-once" : "", route->r_force_commit ? " force-commit" : ""); return buf; } @@ -6643,7 +6910,6 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz "%s" /* initrwnd */ "%s" /* mtu */ "%s" /* pref */ - "%s" /* r_assume_config_once */ "%s" /* r_force_commit */ "", nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced), @@ -6706,7 +6972,6 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz " pref %s", nm_icmpv6_router_pref_to_string(route->rt_pref, str_pref2, sizeof(str_pref2))) : "", - route->r_assume_config_once ? " assume-config-once" : "", route->r_force_commit ? " force-commit" : ""); return buf; @@ -7746,8 +8011,8 @@ nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, * subnet (and thus also the primary/secondary role) is * preserved. */ - n1 = a1->address & _nm_utils_ip4_prefix_to_netmask(a1->plen); - n2 = a2->address & _nm_utils_ip4_prefix_to_netmask(a2->plen); + n1 = nm_utils_ip4_address_clear_host_address(a1->address, a1->plen); + n2 = nm_utils_ip4_address_clear_host_address(a2->address, a2->plen); NM_CMP_DIRECT_MEMCMP(&n1, &n2, sizeof(guint32)); return 0; } @@ -7770,6 +8035,25 @@ _address_pretty_sort_get_prio_6(const struct in6_addr *addr) return 6; } +static int +_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b) +{ + guint32 lifetime_a; + guint32 lifetime_b; + guint32 preferred_a; + guint32 preferred_b; + gint32 now = 0; + + lifetime_a = + nmp_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, &now, &preferred_a); + lifetime_b = + nmp_utils_lifetime_get(b->timestamp, b->lifetime, b->preferred, &now, &preferred_b); + + NM_CMP_DIRECT(lifetime_a, lifetime_b); + NM_CMP_DIRECT(preferred_a, preferred_b); + return 0; +} + int nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1, const NMPlatformIP6Address *a2, @@ -7843,71 +8127,122 @@ nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState NM_HASH_COMBINE_BOOLS(guint8, obj->use_ip4_broadcast_address, obj->a_acd_not_ready, - obj->a_assume_config_once, obj->a_force_commit)); nm_hash_update_strarr(h, obj->label); } int -nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) +nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, + const NMPlatformIP4Address *b, + NMPlatformIPAddressCmpType cmp_type) { NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD(a, b, address); NM_CMP_FIELD(a, b, plen); - NM_CMP_FIELD(a, b, peer_address); - NM_CMP_FIELD_UNSAFE(a, b, use_ip4_broadcast_address); - if (a->use_ip4_broadcast_address) - NM_CMP_FIELD(a, b, broadcast_address); - NM_CMP_FIELD(a, b, addr_source); - NM_CMP_FIELD(a, b, timestamp); - NM_CMP_FIELD(a, b, lifetime); - NM_CMP_FIELD(a, b, preferred); - NM_CMP_FIELD(a, b, n_ifa_flags); - NM_CMP_FIELD_STR(a, b, label); - NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready); - NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once); - NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); - return 0; + NM_CMP_FIELD(a, b, address); + + switch (cmp_type) { + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID: + /* for IPv4 addresses, you can add the same local address with differing peer-address + * (IFA_ADDRESS), provided that their net-part differs. */ + NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a->peer_address, b->peer_address, a->plen); + return 0; + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY: + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL: + NM_CMP_FIELD(a, b, peer_address); + NM_CMP_FIELD_STR(a, b, label); + if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) { + NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a, + (const NMPlatformIPAddress *) b)); + + /* Most flags are set by kernel. We only compare the ones that + * NetworkManager actively sets. + * + * NM actively only sets IFA_F_NOPREFIXROUTE (and IFA_F_MANAGETEMPADDR for IPv6), + * where nm_platform_ip_address_sync() always sets IFA_F_NOPREFIXROUTE. + * There are thus no flags to compare for IPv4. */ + + NM_CMP_DIRECT(nm_platform_ip4_broadcast_address_from_addr(a), + nm_platform_ip4_broadcast_address_from_addr(b)); + } else { + NM_CMP_FIELD(a, b, timestamp); + NM_CMP_FIELD(a, b, lifetime); + NM_CMP_FIELD(a, b, preferred); + NM_CMP_FIELD(a, b, n_ifa_flags); + NM_CMP_FIELD(a, b, addr_source); + NM_CMP_FIELD_UNSAFE(a, b, use_ip4_broadcast_address); + if (a->use_ip4_broadcast_address) + NM_CMP_FIELD(a, b, broadcast_address); + NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready); + NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); + } + return 0; + } + return nm_assert_unreachable_val(0); } void nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h) { - nm_hash_update_vals( - h, - obj->ifindex, - obj->addr_source, - obj->timestamp, - obj->lifetime, - obj->preferred, - obj->n_ifa_flags, - obj->plen, - obj->address, - obj->peer_address, - NM_HASH_COMBINE_BOOLS(guint8, obj->a_assume_config_once, obj->a_force_commit)); + nm_hash_update_vals(h, + obj->ifindex, + obj->addr_source, + obj->timestamp, + obj->lifetime, + obj->preferred, + obj->n_ifa_flags, + obj->plen, + obj->address, + obj->peer_address, + NM_HASH_COMBINE_BOOLS(guint8, obj->a_force_commit)); } int -nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) +nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, + const NMPlatformIP6Address *b, + NMPlatformIPAddressCmpType cmp_type) { const struct in6_addr *p_a, *p_b; NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD_MEMCMP(a, b, address); - NM_CMP_FIELD(a, b, plen); - p_a = nm_platform_ip6_address_get_peer(a); - p_b = nm_platform_ip6_address_get_peer(b); - NM_CMP_DIRECT_MEMCMP(p_a, p_b, sizeof(*p_a)); - NM_CMP_FIELD(a, b, addr_source); - NM_CMP_FIELD(a, b, timestamp); - NM_CMP_FIELD(a, b, lifetime); - NM_CMP_FIELD(a, b, preferred); - NM_CMP_FIELD(a, b, n_ifa_flags); - NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once); - NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); - return 0; + NM_CMP_FIELD_IN6ADDR(a, b, address); + + switch (cmp_type) { + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID: + /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ + return 0; + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY: + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL: + NM_CMP_FIELD(a, b, plen); + p_a = nm_platform_ip6_address_get_peer(a); + p_b = nm_platform_ip6_address_get_peer(b); + NM_CMP_DIRECT_MEMCMP(p_a, p_b, sizeof(*p_a)); + if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) { + NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a, + (const NMPlatformIPAddress *) b)); + + /* Most flags are set by kernel. We only compare the ones that + * NetworkManager actively sets. + * + * NM actively only sets IFA_F_NOPREFIXROUTE and IFA_F_MANAGETEMPADDR, + * where nm_platform_ip_address_sync() always sets IFA_F_NOPREFIXROUTE. + * We thus only care about IFA_F_MANAGETEMPADDR. */ + NM_CMP_DIRECT(a->n_ifa_flags & IFA_F_MANAGETEMPADDR, + b->n_ifa_flags & IFA_F_MANAGETEMPADDR); + } else { + NM_CMP_FIELD(a, b, timestamp); + NM_CMP_FIELD(a, b, lifetime); + NM_CMP_FIELD(a, b, preferred); + NM_CMP_FIELD(a, b, n_ifa_flags); + NM_CMP_FIELD(a, b, addr_source); + NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); + } + return 0; + } + return nm_assert_unreachable_val(0); } void @@ -8007,7 +8342,7 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->initrwnd, obj->mtu, obj->r_rtm_flags, - NM_HASH_COMBINE_BOOLS(guint16, + NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any, obj->lock_window, @@ -8015,7 +8350,6 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->lock_initcwnd, obj->lock_initrwnd, obj->lock_mtu, - obj->r_assume_config_once, obj->r_force_commit)); break; } @@ -8106,10 +8440,8 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, NM_CMP_FIELD(a, b, initcwnd); NM_CMP_FIELD(a, b, initrwnd); NM_CMP_FIELD(a, b, mtu); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) { - NM_CMP_FIELD_UNSAFE(a, b, r_assume_config_once); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) NM_CMP_FIELD_UNSAFE(a, b, r_force_commit); - } break; } return 0; @@ -8202,7 +8534,6 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, obj->lock_initcwnd, obj->lock_initrwnd, obj->lock_mtu, - obj->r_assume_config_once, obj->r_force_commit), obj->window, obj->cwnd, @@ -8286,10 +8617,8 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, NM_CMP_DIRECT(_route_pref_normalize(a->rt_pref), _route_pref_normalize(b->rt_pref)); else NM_CMP_FIELD(a, b, rt_pref); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) { - NM_CMP_FIELD_UNSAFE(a, b, r_assume_config_once); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) NM_CMP_FIELD_UNSAFE(a, b, r_force_commit); - } break; } return 0; @@ -8651,9 +8980,11 @@ log_link(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + _LOG3D("signal: link %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_link_to_string(device, NULL, 0)); + nm_platform_link_to_string(device, sbuf, sizeof(sbuf))); } static void @@ -8664,9 +8995,11 @@ log_ip4_address(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + _LOG3D("signal: address 4 %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip4_address_to_string(address, NULL, 0)); + nm_platform_ip4_address_to_string(address, sbuf, sizeof(sbuf))); } static void @@ -8677,9 +9010,11 @@ log_ip6_address(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + _LOG3D("signal: address 6 %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip6_address_to_string(address, NULL, 0)); + nm_platform_ip6_address_to_string(address, sbuf, sizeof(sbuf))); } static void @@ -8690,9 +9025,11 @@ log_ip4_route(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + _LOG3D("signal: route 4 %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip4_route_to_string(route, NULL, 0)); + nm_platform_ip4_route_to_string(route, sbuf, sizeof(sbuf))); } static void @@ -8703,9 +9040,11 @@ log_ip6_route(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + _LOG3D("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip6_route_to_string(route, NULL, 0)); + nm_platform_ip6_route_to_string(route, sbuf, sizeof(sbuf))); } static void @@ -8716,10 +9055,12 @@ log_routing_rule(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + /* 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)); + nm_platform_routing_rule_to_string(routing_rule, sbuf, sizeof(sbuf))); } static void @@ -8730,9 +9071,11 @@ log_qdisc(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + _LOG3D("signal: qdisc %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_qdisc_to_string(qdisc, NULL, 0)); + nm_platform_qdisc_to_string(qdisc, sbuf, sizeof(sbuf))); } static void @@ -8743,9 +9086,11 @@ log_tfilter(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + _LOG3D("signal: tfilter %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_tfilter_to_string(tfilter, NULL, 0)); + nm_platform_tfilter_to_string(tfilter, sbuf, sizeof(sbuf))); } /*****************************************************************************/ @@ -8756,6 +9101,7 @@ nm_platform_cache_update_emit_signal(NMPlatform *self, const NMPObject *obj_old, const NMPObject *obj_new) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; gboolean visible_new; gboolean visible_old; const NMPObject *o; @@ -8818,7 +9164,7 @@ nm_platform_cache_update_emit_signal(NMPlatform *self, _LOG3t("emit signal %s %s: %s", klass->signal_type, nm_platform_signal_change_type_to_string((NMPlatformSignalChangeType) cache_op), - nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); nmp_object_ref(o); g_signal_emit(self, @@ -8863,6 +9209,37 @@ nm_platform_netns_push(NMPlatform *self, NMPNetns **netns) /*****************************************************************************/ +const _NMPlatformVTableAddressUnion nm_platform_vtable_address = { + .v4 = + { + .is_ip4 = TRUE, + .obj_type = NMP_OBJECT_TYPE_IP4_ADDRESS, + .addr_family = AF_INET, + .sizeof_address = sizeof(NMPlatformIP4Address), + .address_cmp = + (int (*)(const NMPlatformIPXAddress *a, + const NMPlatformIPXAddress *b, + NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip4_address_cmp, + .address_to_string = (const char *(*) (const NMPlatformIPXAddress *address, + char *buf, + gsize len)) nm_platform_ip4_address_to_string, + }, + .v6 = + { + .is_ip4 = FALSE, + .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, + .addr_family = AF_INET6, + .sizeof_address = sizeof(NMPlatformIP6Address), + .address_cmp = + (int (*)(const NMPlatformIPXAddress *a, + const NMPlatformIPXAddress *b, + NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip6_address_cmp, + .address_to_string = (const char *(*) (const NMPlatformIPXAddress *address, + char *buf, + gsize len)) nm_platform_ip6_address_to_string, + }, +}; + const _NMPlatformVTableRouteUnion nm_platform_vtable_route = { .v4 = { diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index 6f5cd524..246d6ff6 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -95,6 +95,14 @@ typedef enum { } NMPNlmFlags; typedef enum { + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID, + + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY, + + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL, +} NMPlatformIPAddressCmpType; + +typedef enum { /* compare fields which kernel considers as similar routes. * It is a looser comparisong then NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID * and means that `ip route add` would fail to add two routes @@ -259,17 +267,17 @@ struct _NMPlatformLink { bool initialized : 1; }; -typedef enum { /*< skip >*/ - NM_PLATFORM_SIGNAL_ID_NONE, - NM_PLATFORM_SIGNAL_ID_LINK, - NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, - 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, +typedef enum { + NM_PLATFORM_SIGNAL_ID_NONE, + NM_PLATFORM_SIGNAL_ID_LINK, + NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, + 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, } NMPlatformSignalIdType; guint _nm_platform_signal_id_get(NMPlatformSignalIdType signal_type); @@ -323,8 +331,6 @@ typedef enum { /* Meta flags not honored by NMPlatform (netlink code). Instead, they can be * used by the upper layers which use NMPlatformIPRoute to track addresses that * should be configured. */ \ - /* Whether the address is should be configured once during assume. */ \ - bool a_assume_config_once : 1; \ bool a_force_commit : 1; \ \ guint8 plen; \ @@ -468,12 +474,9 @@ typedef union { * This field overrides "table_coerced" field. If "table_any" is true, then * the "table_coerced" field is ignored (unlike for the metric). */ \ bool table_any : 1; \ - \ /* Meta flags not honored by NMPlatform (netlink code). Instead, they can be * used by the upper layers which use NMPlatformIPRoute to track routes that * should be configured. */ \ - /* Whether the route is should be configured once during assume. */ \ - bool r_assume_config_once : 1; \ /* Whether the route should be committed even if it was removed externally. */ \ bool r_force_commit : 1; \ \ @@ -776,10 +779,31 @@ typedef struct { #undef __NMPlatformObjWithIfindex_COMMON typedef struct { - gboolean is_ip4; + bool is_ip4; + NMPObjectType obj_type; + gint8 addr_family; + guint8 sizeof_address; + int (*address_cmp)(const NMPlatformIPXAddress *a, + const NMPlatformIPXAddress *b, + NMPlatformIPAddressCmpType cmp_type); + const char *(*address_to_string)(const NMPlatformIPXAddress *address, char *buf, gsize len); +} NMPlatformVTableAddress; + +typedef union { + struct { + NMPlatformVTableAddress v6; + NMPlatformVTableAddress v4; + }; + NMPlatformVTableAddress vx[2]; +} _NMPlatformVTableAddressUnion; + +extern const _NMPlatformVTableAddressUnion nm_platform_vtable_address; + +typedef struct { + bool is_ip4; + gint8 addr_family; + guint8 sizeof_route; NMPObjectType obj_type; - int addr_family; - gsize sizeof_route; int (*route_cmp)(const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type); @@ -1017,6 +1041,14 @@ typedef void (*NMPlatformAsyncCallback)(GError *error, gpointer user_data); /*****************************************************************************/ +typedef struct _NMPlatformCsmeConnInfo { + guint8 ssid[32]; + guint32 channel; + NMEtherAddr addr; + guint8 sta_cipher; + guint8 auth_mode; +} NMPlatformCsmeConnInfo; + typedef enum { NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE, @@ -1105,6 +1137,9 @@ typedef struct { guint32 mtu, gconstpointer extra_data, const NMPlatformLink **out_link); + + int (*link_change)(NMPlatform *self, NMLinkType type, int ifindex, gconstpointer extra_data); + gboolean (*link_delete)(NMPlatform *self, int ifindex); gboolean (*link_refresh)(NMPlatform *self, int ifindex); gboolean (*link_set_netns)(NMPlatform *self, int ifindex, int netns_fd); @@ -1204,6 +1239,10 @@ typedef struct { gboolean (*wifi_set_wake_on_wlan)(NMPlatform *self, int ifindex, _NMSettingWirelessWakeOnWLan wowl); + gboolean (*wifi_get_csme_conn_info)(NMPlatform *self, + int ifindex, + NMPlatformCsmeConnInfo *out_conn_info); + gboolean (*wifi_get_device_from_csme)(NMPlatform *self, int ifindex); guint32 (*mesh_get_channel)(NMPlatform *self, int ifindex); gboolean (*mesh_set_channel)(NMPlatform *self, int ifindex, guint32 channel); @@ -1411,7 +1450,11 @@ _nm_platform_link_get_inet6_addr_gen_mode(const NMPlatformLink *pllink) static inline gboolean nm_platform_route_type_is_nodev(guint8 type) { - return NM_IN_SET(type, 6 /* RTN_BLACKHOLE */, 7 /* RTN_UNREACHABLE */, 8 /* RTN_PROHIBIT */); + return NM_IN_SET(type, + 6 /* RTN_BLACKHOLE */, + 7 /* RTN_UNREACHABLE */, + 8 /* RTN_PROHIBIT */, + 9 /* RTN_THROW */); } /** @@ -1570,6 +1613,9 @@ int nm_platform_link_add(NMPlatform *self, gconstpointer extra_data, const NMPlatformLink **out_link); +int +nm_platform_link_change(NMPlatform *self, NMLinkType type, int ifindex, gconstpointer extra_data); + static inline int nm_platform_link_veth_add(NMPlatform *self, const char *name, @@ -1606,6 +1652,12 @@ nm_platform_link_bridge_add(NMPlatform *self, } static inline int +nm_platform_link_bridge_change(NMPlatform *self, int ifindex, const NMPlatformLnkBridge *props) +{ + return nm_platform_link_change(self, NM_LINK_TYPE_BRIDGE, ifindex, props); +} + +static inline int nm_platform_link_bond_add(NMPlatform *self, const char *name, const NMPlatformLink **out_link) { return nm_platform_link_add(self, NM_LINK_TYPE_BOND, name, 0, NULL, 0, 0, NULL, out_link); @@ -2024,6 +2076,10 @@ void nm_platform_wifi_indicate_addressing_running(NMPlatform *self, int ifindex, _NMSettingWirelessWakeOnWLan nm_platform_wifi_get_wake_on_wlan(NMPlatform *self, int ifindex); gboolean nm_platform_wifi_set_wake_on_wlan(NMPlatform *self, int ifindex, _NMSettingWirelessWakeOnWLan wowl); +gboolean nm_platform_wifi_get_csme_conn_info(NMPlatform *self, + int ifindex, + NMPlatformCsmeConnInfo *out_conn_info); +gboolean nm_platform_wifi_get_device_from_csme(NMPlatform *self, int ifindex); guint32 nm_platform_mesh_get_channel(NMPlatform *self, int ifindex); gboolean nm_platform_mesh_set_channel(NMPlatform *self, int ifindex, guint32 channel); @@ -2038,6 +2094,11 @@ gboolean nm_platform_wpan_set_channel(NMPlatform *self, int ifindex, guint8 page void nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen); const struct in6_addr *nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr); +const NMPObject *nm_platform_ip_address_get(NMPlatform *self, + int addr_family, + int ifindex, + gconstpointer /* (NMPlatformIPAddress *) */ needle); + const NMPlatformIP4Address *nm_platform_ip4_address_get(NMPlatform *self, int ifindex, in_addr_t address, @@ -2097,6 +2158,29 @@ gboolean nm_platform_ip4_address_delete(NMPlatform *self, gboolean nm_platform_ip6_address_delete(NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen); +static inline gboolean +nm_platform_ip_address_delete(NMPlatform *self, + int addr_family, + int ifindex, + gconstpointer /* (const NMPlatformIPAddress *) */ addr) +{ + if (NM_IS_IPv4(addr_family)) { + const NMPlatformIP4Address *a = addr; + + if (ifindex <= 0) + ifindex = a->ifindex; + + return nm_platform_ip4_address_delete(self, ifindex, a->address, a->plen, a->peer_address); + } else { + const NMPlatformIP6Address *a = addr; + + if (ifindex <= 0) + ifindex = a->ifindex; + + return nm_platform_ip6_address_delete(self, ifindex, a->address, a->plen); + } +} + gboolean nm_platform_ip_address_sync(NMPlatform *self, int addr_family, int ifindex, @@ -2288,8 +2372,24 @@ int nm_platform_lnk_vlan_cmp(const NMPlatformLnkVlan *a, const NMPlatformLnkVlan int nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b); int nm_platform_lnk_vxlan_cmp(const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b); int nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformLnkWireGuard *b); -int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b); -int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b); +int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, + const NMPlatformIP4Address *b, + NMPlatformIPAddressCmpType cmp_type); +int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, + const NMPlatformIP6Address *b, + NMPlatformIPAddressCmpType cmp_type); + +static inline int +nm_platform_ip4_address_cmp_full(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) +{ + return nm_platform_ip4_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL); +} + +static inline int +nm_platform_ip6_address_cmp_full(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) +{ + return nm_platform_ip6_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL); +} int nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, const NMPlatformIP4Address *a2); diff --git a/src/libnm-platform/nmp-base.h b/src/libnm-platform/nmp-base.h index a80fd4d3..48631688 100644 --- a/src/libnm-platform/nmp-base.h +++ b/src/libnm-platform/nmp-base.h @@ -110,7 +110,7 @@ typedef struct _NMPlatformIP6Route NMPlatformIP6Route; typedef struct _NMPlatformLink NMPlatformLink; typedef struct _NMPObject NMPObject; -typedef enum { +typedef enum _nm_packed { NMP_OBJECT_TYPE_UNKNOWN, NMP_OBJECT_TYPE_LINK, diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c index d518e6e5..6c177b4c 100644 --- a/src/libnm-platform/nmp-object.c +++ b/src/libnm-platform/nmp-object.c @@ -799,7 +799,7 @@ nmp_object_new_link(int ifindex) /*****************************************************************************/ -static void +static NMPObject * _nmp_object_stackinit_from_class(NMPObject *obj, const NMPClass *klass) { nm_assert(obj); @@ -812,25 +812,13 @@ _nmp_object_stackinit_from_class(NMPObject *obj, const NMPClass *klass) ._ref_count = NM_OBJ_REF_COUNT_STACKINIT, }, }; + return obj; } static NMPObject * _nmp_object_stackinit_from_type(NMPObject *obj, NMPObjectType obj_type) { - const NMPClass *klass; - - nm_assert(obj); - klass = nmp_class_from_type(obj_type); - nm_assert(klass); - - *obj = (NMPObject){ - .parent = - { - .klass = (const NMDedupMultiObjClass *) klass, - ._ref_count = NM_OBJ_REF_COUNT_STACKINIT, - }, - }; - return obj; + return _nmp_object_stackinit_from_class(obj, nmp_class_from_type(obj_type)); } const NMPObject * @@ -901,7 +889,7 @@ nmp_object_to_string(const NMPObject *obj, gsize buf_size) { const NMPClass *klass; - char buf2[sizeof(_nm_utils_to_string_buffer)]; + char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; if (!nm_utils_to_string_buffer_init_null(obj, &buf, &buf_size)) return buf; @@ -992,7 +980,7 @@ _vt_cmd_obj_to_string_lnk_vlan(const NMPObject *obj, gsize buf_size) { const NMPClass *klass; - char buf2[sizeof(_nm_utils_to_string_buffer)]; + char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; char *b; gsize l; @@ -1056,7 +1044,7 @@ _vt_cmd_obj_to_string_lnk_wireguard(const NMPObject *obj, gsize buf_size) { const NMPClass *klass; - char buf2[sizeof(_nm_utils_to_string_buffer)]; + char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; char *b; guint i; @@ -1523,20 +1511,21 @@ nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2) _vt_cmd_plobj_id_cmp(link, NMPlatformLink, { NM_CMP_FIELD(obj1, obj2, ifindex); }); -_vt_cmd_plobj_id_cmp(ip4_address, NMPlatformIP4Address, { - NM_CMP_FIELD(obj1, obj2, ifindex); - NM_CMP_FIELD(obj1, obj2, plen); - NM_CMP_FIELD(obj1, obj2, address); - /* for IPv4 addresses, you can add the same local address with differing peer-address - * (IFA_ADDRESS), provided that their net-part differs. */ - NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(obj1->peer_address, obj2->peer_address, obj1->plen); -}); +static int +_vt_cmd_plobj_id_cmp_ip4_address(const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_ip4_address_cmp((const NMPlatformIP4Address *) obj1, + (const NMPlatformIP4Address *) obj2, + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID); +} -_vt_cmd_plobj_id_cmp(ip6_address, NMPlatformIP6Address, { - NM_CMP_FIELD(obj1, obj2, ifindex); - /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ - NM_CMP_FIELD_IN6ADDR(obj1, obj2, address); -}); +static int +_vt_cmd_plobj_id_cmp_ip6_address(const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_ip6_address_cmp((const NMPlatformIP6Address *) obj1, + (const NMPlatformIP6Address *) obj2, + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID); +} _vt_cmd_plobj_id_cmp(qdisc, NMPlatformQdisc, { NM_CMP_FIELD(obj1, obj2, ifindex); @@ -1551,24 +1540,24 @@ _vt_cmd_plobj_id_cmp(tfilter, NMPlatformTfilter, { static int _vt_cmd_plobj_id_cmp_ip4_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) { - return nm_platform_ip4_route_cmp((NMPlatformIP4Route *) obj1, - (NMPlatformIP4Route *) obj2, + return nm_platform_ip4_route_cmp((const NMPlatformIP4Route *) obj1, + (const NMPlatformIP4Route *) obj2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); } static int _vt_cmd_plobj_id_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) { - return nm_platform_ip6_route_cmp((NMPlatformIP6Route *) obj1, - (NMPlatformIP6Route *) obj2, + return nm_platform_ip6_route_cmp((const NMPlatformIP6Route *) obj1, + (const 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, + return nm_platform_routing_rule_cmp((const NMPlatformRoutingRule *) obj1, + (const NMPlatformRoutingRule *) obj2, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID); } @@ -3158,28 +3147,29 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_address, .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip4_address_to_string, .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_ip4_address_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip4_address_cmp, + .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip4_address_cmp_full, + }, + [NMP_OBJECT_TYPE_IP6_ADDRESS - 1] = + { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, + .sizeof_data = sizeof(NMPObjectIP6Address), + .sizeof_public = sizeof(NMPlatformIP6Address), + .obj_type_name = "ip6-address", + .addr_family = AF_INET6, + .rtm_gettype = RTM_GETADDR, + .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, + .signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, + .supported_cache_ids = _supported_cache_ids_ipx_address, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, + .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_address, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_address, + .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address, + .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_address_to_string, + .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_ip6_address_hash_update, + .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip6_address_cmp_full, }, - [NMP_OBJECT_TYPE_IP6_ADDRESS - - 1] = {.parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, - .sizeof_data = sizeof(NMPObjectIP6Address), - .sizeof_public = sizeof(NMPlatformIP6Address), - .obj_type_name = "ip6-address", - .addr_family = AF_INET6, - .rtm_gettype = RTM_GETADDR, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, - .signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - .supported_cache_ids = _supported_cache_ids_ipx_address, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, - .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_address, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_address, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_address_to_string, - .cmd_plobj_hash_update = - (CmdPlobjHashUpdateFunc) nm_platform_ip6_address_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip6_address_cmp}, [NMP_OBJECT_TYPE_IP4_ROUTE - 1] = { .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), diff --git a/src/libnm-platform/nmp-object.h b/src/libnm-platform/nmp-object.h index 784dcc2d..d6b88ba2 100644 --- a/src/libnm-platform/nmp-object.h +++ b/src/libnm-platform/nmp-object.h @@ -91,17 +91,17 @@ typedef struct _NMPWireGuardPeer { /*****************************************************************************/ -typedef enum { /*< skip >*/ - NMP_OBJECT_TO_STRING_ID, - NMP_OBJECT_TO_STRING_PUBLIC, - NMP_OBJECT_TO_STRING_ALL, +typedef enum { + NMP_OBJECT_TO_STRING_ID, + NMP_OBJECT_TO_STRING_PUBLIC, + NMP_OBJECT_TO_STRING_ALL, } NMPObjectToStringMode; -typedef enum { /*< skip >*/ - NMP_CACHE_OPS_UNCHANGED = NM_PLATFORM_SIGNAL_NONE, - NMP_CACHE_OPS_ADDED = NM_PLATFORM_SIGNAL_ADDED, - NMP_CACHE_OPS_UPDATED = NM_PLATFORM_SIGNAL_CHANGED, - NMP_CACHE_OPS_REMOVED = NM_PLATFORM_SIGNAL_REMOVED, +typedef enum { + NMP_CACHE_OPS_UNCHANGED = NM_PLATFORM_SIGNAL_NONE, + NMP_CACHE_OPS_ADDED = NM_PLATFORM_SIGNAL_ADDED, + NMP_CACHE_OPS_UPDATED = NM_PLATFORM_SIGNAL_CHANGED, + NMP_CACHE_OPS_REMOVED = NM_PLATFORM_SIGNAL_REMOVED, } NMPCacheOpsType; /* The NMPCacheIdType are the different index types. @@ -1098,21 +1098,6 @@ nm_platform_lookup_object_by_addr_family(NMPlatform *platform, /*****************************************************************************/ static inline gboolean -nmp_object_get_assume_config_once(const NMPObject *obj) -{ - switch (NMP_OBJECT_GET_TYPE(obj)) { - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP6_ADDRESS: - return NMP_OBJECT_CAST_IP_ADDRESS(obj)->a_assume_config_once; - case NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - return NMP_OBJECT_CAST_IP_ROUTE(obj)->r_assume_config_once; - default: - return nm_assert_unreachable_val(FALSE); - } -} - -static inline gboolean nmp_object_get_force_commit(const NMPObject *obj) { switch (NMP_OBJECT_GET_TYPE(obj)) { diff --git a/src/libnm-platform/nmp-route-manager.c b/src/libnm-platform/nmp-route-manager.c index c31c9806..842301b1 100644 --- a/src/libnm-platform/nmp-route-manager.c +++ b/src/libnm-platform/nmp-route-manager.c @@ -397,14 +397,17 @@ nmp_route_manager_track(NMPRouteManager *self, _track_data_assert(track_data, TRUE); if (changed) { - _LOGD("track [" NM_HASH_OBFUSCATE_PTR_FMT ",%s%u] %s \"%s\"", - NM_HASH_OBFUSCATE_PTR(track_data->user_tag), - (track_data->track_priority_val == 0 - ? "" - : (track_data->track_priority_present ? "+" : "-")), - (guint) track_data->track_priority_val, - NMP_OBJECT_GET_CLASS(track_data->obj)->obj_type_name, - nmp_object_to_string(track_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + + _LOGD( + "track [" NM_HASH_OBFUSCATE_PTR_FMT ",%s%u] %s \"%s\"", + NM_HASH_OBFUSCATE_PTR(track_data->user_tag), + (track_data->track_priority_val == 0 + ? "" + : (track_data->track_priority_present ? "+" : "-")), + (guint) track_data->track_priority_val, + NMP_OBJECT_GET_CLASS(track_data->obj)->obj_type_name, + nmp_object_to_string(track_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); } return changed || changed_untrack; @@ -416,6 +419,7 @@ _track_data_untrack(NMPRouteManager *self, gboolean remove_user_tag_data, gboolean make_owned_by_us) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; TrackObjData *obj_data; nm_assert(NMP_IS_ROUTE_MANAGER(self)); @@ -426,7 +430,7 @@ _track_data_untrack(NMPRouteManager *self, _LOGD("untrack [" NM_HASH_OBFUSCATE_PTR_FMT "] %s \"%s\"", NM_HASH_OBFUSCATE_PTR(track_data->user_tag), NMP_OBJECT_GET_CLASS(track_data->obj)->obj_type_name, - nmp_object_to_string(track_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(track_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); #if NM_MORE_ASSERTS { @@ -552,6 +556,7 @@ nmp_route_manager_untrack_all(NMPRouteManager *self, void nmp_route_manager_sync(NMPRouteManager *self, NMPObjectType obj_type, gboolean keep_deleted) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; const NMDedupMultiHeadEntry *pl_head_entry; NMDedupMultiIter pl_iter; const NMPObject *plobj; @@ -608,7 +613,7 @@ nmp_route_manager_sync(NMPRouteManager *self, NMPObjectType obj_type, gboolean k if (keep_deleted) { _LOGD("forget/leak object added by us: %s \"%s\"", NMP_OBJECT_GET_CLASS(plobj)->obj_type_name, - nmp_object_to_string(plobj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(plobj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); continue; } diff --git a/src/libnm-platform/tests/test-nm-platform.c b/src/libnm-platform/tests/test-nm-platform.c index d4de0dd5..9ac69bde 100644 --- a/src/libnm-platform/tests/test-nm-platform.c +++ b/src/libnm-platform/tests/test-nm-platform.c @@ -131,11 +131,13 @@ test_nmp_link_mode_all_advertised_modes_bits(void) for (i = 0; i < (int) G_N_ELEMENTS(_nmp_link_mode_all_advertised_modes); i++) { if (flags[i] != _nmp_link_mode_all_advertised_modes[i]) { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("_nmp_link_mode_all_advertised_modes[%d] should be 0x%0x but is 0x%0x " "(according to the bits in _nmp_link_mode_all_advertised_modes_bits)", i, flags[i], _nmp_link_mode_all_advertised_modes[i]); + NM_PRAGMA_WARNING_REENABLE } } } diff --git a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c index 3906384b..475b8a39 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c +++ b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c @@ -15,6 +15,8 @@ #include <linux/nl80211.h> #include <linux/if.h> +#include "linux-headers/nl80211-vnd-intel.h" + #include "libnm-log-core/nm-logging.h" #include "libnm-platform/nm-netlink.h" #include "nm-wifi-utils-private.h" @@ -384,8 +386,10 @@ wifi_nl80211_find_freq(NMWifiUtils *data, const guint32 *freqs) int i; int j; - for (i = 0; i < self->num_freqs; i++) { - for (j = 0; freqs[j] != 0; j++) { + /* It's important to check the values in the order of @freqs, because + * that array might be sorted to contain preferred frequencies first. */ + for (j = 0; freqs[j] != 0; j++) { + for (i = 0; i < self->num_freqs; i++) { if (self->freqs[i] == freqs[j]) return freqs[j]; } @@ -597,7 +601,7 @@ nl80211_wiphy_info_handler(struct nl_msg *msg, void *arg) struct nlattr *nl_freq; int rem_freq; int rem_band; - int freq_idx; + guint num_alloc; #ifdef NL80211_FREQUENCY_ATTR_NO_IR G_STATIC_ASSERT_EXPR(NL80211_FREQUENCY_ATTR_PASSIVE_SCAN == NL80211_FREQUENCY_ATTR_NO_IR @@ -651,8 +655,10 @@ nl80211_wiphy_info_handler(struct nl_msg *msg, void *arg) } } - /* Find number of supported frequencies */ + /* Read supported frequencies */ + num_alloc = 32; info->num_freqs = 0; + info->freqs = g_new(guint32, num_alloc); nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) { if (nla_parse_nested_arr(tb_band, nl_band, NULL) < 0) @@ -665,38 +671,28 @@ nl80211_wiphy_info_handler(struct nl_msg *msg, void *arg) if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) continue; - info->num_freqs++; - } - } - - /* Read supported frequencies */ - info->freqs = g_malloc0(sizeof(guint32) * info->num_freqs); - - freq_idx = 0; - nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) { - if (nla_parse_nested_arr(tb_band, nl_band, NULL) < 0) - return NL_SKIP; - - nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { - if (nla_parse_nested_arr(tb_freq, nl_freq, freq_policy) < 0) - continue; - - if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) continue; - info->freqs[freq_idx] = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + if (info->num_freqs >= num_alloc) { + num_alloc *= 2; + info->freqs = g_renew(guint32, info->freqs, num_alloc); + } + info->freqs[info->num_freqs] = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_VALID; - if (info->freqs[freq_idx] > 2400 && info->freqs[freq_idx] < 2500) + if (info->freqs[info->num_freqs] > 2400 && info->freqs[info->num_freqs] < 2500) info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_2GHZ; - if (info->freqs[freq_idx] > 4900 && info->freqs[freq_idx] < 6000) + if (info->freqs[info->num_freqs] > 4900 && info->freqs[info->num_freqs] < 6000) info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_5GHZ; - freq_idx++; + info->num_freqs++; } } + info->freqs = g_renew(guint32, info->freqs, info->num_freqs); + /* Read security/encryption support */ if (tb[NL80211_ATTR_CIPHER_SUITES]) { guint32 *ciphers = nla_data(tb[NL80211_ATTR_CIPHER_SUITES]); @@ -815,6 +811,111 @@ nla_put_failure: g_return_val_if_reached(FALSE); } +struct nl80211_csme_conn_info { + NMWifiUtilsNl80211 *self; + NMPlatformCsmeConnInfo *conn_info; +}; + +static int +nl80211_csme_conn_event_handler(struct nl_msg *msg, void *arg) +{ + struct nl80211_csme_conn_info *info = arg; + NMPlatformCsmeConnInfo *out_conn_info = info->conn_info; + NMWifiUtilsNl80211 *self = info->self; + struct genlmsghdr *gnlh = (void *) nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct nlattr *data; + struct nlattr *attrs[NUM_IWL_MVM_VENDOR_ATTR]; + int err; + + static const struct nla_policy iwl_vendor_policy[NUM_IWL_MVM_VENDOR_ATTR] = { + [IWL_MVM_VENDOR_ATTR_AUTH_MODE] = {.type = NLA_U32}, + [IWL_MVM_VENDOR_ATTR_SSID] = {.type = NLA_UNSPEC, .maxlen = NM_IW_ESSID_MAX_SIZE}, + [IWL_MVM_VENDOR_ATTR_STA_CIPHER] = {.type = NLA_U32}, + [IWL_MVM_VENDOR_ATTR_CHANNEL_NUM] = {.type = NLA_U8}, + [IWL_MVM_VENDOR_ATTR_ADDR] = {.type = NLA_UNSPEC, .minlen = ETH_ALEN, .maxlen = ETH_ALEN}, + }; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); + data = tb[NL80211_ATTR_VENDOR_DATA]; + + *out_conn_info = (NMPlatformCsmeConnInfo){}; + + err = nla_parse_nested(attrs, MAX_IWL_MVM_VENDOR_ATTR, data, iwl_vendor_policy); + if (err) { + _LOGD("IWL_MVM_VENDOR_CMD_GET_CSME_CONN_INFO Failed to parse CSME connection info: %s", + nm_strerror(err)); + return -EINVAL; + } + + if (attrs[IWL_MVM_VENDOR_ATTR_AUTH_MODE]) + out_conn_info->auth_mode = nla_get_u8(attrs[IWL_MVM_VENDOR_ATTR_AUTH_MODE]); + + if (attrs[IWL_MVM_VENDOR_ATTR_SSID]) + memcpy(out_conn_info->ssid, + nla_data(attrs[IWL_MVM_VENDOR_ATTR_SSID]), + nla_len(attrs[IWL_MVM_VENDOR_ATTR_SSID])); + + if (attrs[IWL_MVM_VENDOR_ATTR_STA_CIPHER]) + out_conn_info->sta_cipher = nla_get_u8(attrs[IWL_MVM_VENDOR_ATTR_STA_CIPHER]); + + if (attrs[IWL_MVM_VENDOR_ATTR_CHANNEL_NUM]) + out_conn_info->channel = nla_get_u8(attrs[IWL_MVM_VENDOR_ATTR_CHANNEL_NUM]); + + if (attrs[IWL_MVM_VENDOR_ATTR_ADDR]) + memcpy(&out_conn_info->addr, + nla_data(attrs[IWL_MVM_VENDOR_ATTR_ADDR]), + sizeof(out_conn_info->addr)); + + return NL_SKIP; +} + +static gboolean +wifi_nl80211_intel_vnd_get_csme_conn_info(NMWifiUtils *data, NMPlatformCsmeConnInfo *out_conn_info) +{ + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; + nm_auto_nlmsg struct nl_msg *msg = NULL; + int err; + struct nl80211_csme_conn_info conn_info = { + .self = self, + .conn_info = out_conn_info, + }; + + msg = nl80211_alloc_msg(self, NL80211_CMD_VENDOR, 0); + NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, INTEL_OUI); + NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD, IWL_MVM_VENDOR_CMD_GET_CSME_CONN_INFO); + + err = nl80211_send_and_recv(self, msg, nl80211_csme_conn_event_handler, &conn_info); + if (err < 0) + _LOGD("IWL_MVM_VENDOR_CMD_GET_CSME_CONN_INFO request failed: %s", nm_strerror(err)); + + return err >= 0; + +nla_put_failure: + g_return_val_if_reached(FALSE); +} + +static gboolean +wifi_nl80211_intel_vnd_get_device_from_csme(NMWifiUtils *data) +{ + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; + nm_auto_nlmsg struct nl_msg *msg = NULL; + int err; + + msg = nl80211_alloc_msg(self, NL80211_CMD_VENDOR, 0); + NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, INTEL_OUI); + NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD, IWL_MVM_VENDOR_CMD_HOST_GET_OWNERSHIP); + + err = nl80211_send_and_recv(self, msg, NULL, NULL); + if (err < 0) + _LOGD("IWL_MVM_VENDOR_CMD_HOST_GET_OWNERSHIP request failed: %s", nm_strerror(err)); + + return err >= 0; + +nla_put_failure: + g_return_val_if_reached(FALSE); +} + static void nm_wifi_utils_nl80211_init(NMWifiUtilsNl80211 *self) {} @@ -839,6 +940,8 @@ nm_wifi_utils_nl80211_class_init(NMWifiUtilsNl80211Class *klass) wifi_utils_class->get_mesh_channel = wifi_nl80211_get_mesh_channel; wifi_utils_class->set_mesh_channel = wifi_nl80211_set_mesh_channel; wifi_utils_class->set_mesh_ssid = wifi_nl80211_set_mesh_ssid; + wifi_utils_class->get_csme_conn_info = wifi_nl80211_intel_vnd_get_csme_conn_info; + wifi_utils_class->get_device_from_csme = wifi_nl80211_intel_vnd_get_device_from_csme; } NMWifiUtils * diff --git a/src/libnm-platform/wifi/nm-wifi-utils-private.h b/src/libnm-platform/wifi/nm-wifi-utils-private.h index 7461b65e..7fe157f2 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-private.h +++ b/src/libnm-platform/wifi/nm-wifi-utils-private.h @@ -7,6 +7,7 @@ #define __WIFI_UTILS_PRIVATE_H__ #include "nm-wifi-utils.h" +#include "libnm-platform/nm-platform.h" typedef struct { GObjectClass parent; @@ -53,6 +54,10 @@ typedef struct { gboolean (*set_mesh_ssid)(NMWifiUtils *data, const guint8 *ssid, gsize len); gboolean (*indicate_addressing_running)(NMWifiUtils *data, gboolean running); + + gboolean (*get_csme_conn_info)(NMWifiUtils *data, NMPlatformCsmeConnInfo *out_conn_info); + + gboolean (*get_device_from_csme)(NMWifiUtils *data); } NMWifiUtilsClass; struct NMWifiUtils { diff --git a/src/libnm-platform/wifi/nm-wifi-utils-wext.c b/src/libnm-platform/wifi/nm-wifi-utils-wext.c index 8d0e6ed0..678d71fe 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-wext.c +++ b/src/libnm-platform/wifi/nm-wifi-utils-wext.c @@ -255,8 +255,10 @@ wifi_wext_find_freq(NMWifiUtils *data, const guint32 *freqs) guint i; guint j; - for (i = 0; i < wext->num_freqs; i++) { - for (j = 0; freqs[j] != 0; j++) { + /* It's important to check the values in the order of @freqs, because + * that array might be sorted to contain preferred frequencies first. */ + for (j = 0; freqs[j] != 0; j++) { + for (i = 0; i < wext->num_freqs; i++) { if (wext->freqs[i] == freqs[j]) return freqs[j]; } diff --git a/src/libnm-platform/wifi/nm-wifi-utils.c b/src/libnm-platform/wifi/nm-wifi-utils.c index 8bdd65c2..08a8ec4f 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils.c +++ b/src/libnm-platform/wifi/nm-wifi-utils.c @@ -60,6 +60,7 @@ _NM80211Mode nm_wifi_utils_get_mode(NMWifiUtils *data) { g_return_val_if_fail(data != NULL, _NM_802_11_MODE_UNKNOWN); + return NM_WIFI_UTILS_GET_CLASS(data)->get_mode(data); } @@ -118,6 +119,7 @@ guint32 nm_wifi_utils_get_freq(NMWifiUtils *data) { g_return_val_if_fail(data != NULL, 0); + return NM_WIFI_UTILS_GET_CLASS(data)->get_freq(data); } @@ -126,6 +128,7 @@ nm_wifi_utils_find_freq(NMWifiUtils *data, const guint32 *freqs) { g_return_val_if_fail(data != NULL, 0); g_return_val_if_fail(freqs != NULL, 0); + return NM_WIFI_UTILS_GET_CLASS(data)->find_freq(data, freqs); } @@ -154,6 +157,28 @@ nm_wifi_utils_is_wifi(int dirfd, const char *ifname) return FALSE; } +gboolean +nm_wifi_utils_get_csme_conn_info(NMWifiUtils *data, NMPlatformCsmeConnInfo *out_conn_info) +{ + NMWifiUtilsClass *klass; + + g_return_val_if_fail(data != NULL, FALSE); + + klass = NM_WIFI_UTILS_GET_CLASS(data); + return klass->get_csme_conn_info ? klass->get_csme_conn_info(data, out_conn_info) : FALSE; +} + +gboolean +nm_wifi_utils_get_device_from_csme(NMWifiUtils *data) +{ + NMWifiUtilsClass *klass; + + g_return_val_if_fail(data != NULL, FALSE); + + klass = NM_WIFI_UTILS_GET_CLASS(data); + return klass->get_device_from_csme ? klass->get_device_from_csme(data) : FALSE; +} + /* OLPC Mesh-only functions */ guint32 diff --git a/src/libnm-platform/wifi/nm-wifi-utils.h b/src/libnm-platform/wifi/nm-wifi-utils.h index be33b90c..0d30c1a1 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils.h +++ b/src/libnm-platform/wifi/nm-wifi-utils.h @@ -63,6 +63,12 @@ _NMSettingWirelessWakeOnWLan nm_wifi_utils_get_wake_on_wlan(NMWifiUtils *data); gboolean nm_wifi_utils_set_wake_on_wlan(NMWifiUtils *data, _NMSettingWirelessWakeOnWLan wowl); +struct _NMPlatformCsmeConnInfo; +gboolean nm_wifi_utils_get_csme_conn_info(NMWifiUtils *data, + struct _NMPlatformCsmeConnInfo *out_conn_info); + +gboolean nm_wifi_utils_get_device_from_csme(NMWifiUtils *data); + /* OLPC Mesh-only functions */ guint32 nm_wifi_utils_get_mesh_channel(NMWifiUtils *data); diff --git a/src/libnm-std-aux/nm-std-aux.h b/src/libnm-std-aux/nm-std-aux.h index b79b1c65..c44bcc72 100644 --- a/src/libnm-std-aux/nm-std-aux.h +++ b/src/libnm-std-aux/nm-std-aux.h @@ -487,6 +487,22 @@ nm_streq0(const char *s1, const char *s2) return (s1 == s2) || (s1 && s2 && strcmp(s1, s2) == 0); } +/* + * Very similar to g_str_has_prefix() with the obvious meaning. + * Differences: + * 1) suffix is enforced to be a C string literal + * (it is thus more restricted, but you'll know it at compile time). + * 2) it accepts str==NULL + * (it is thus more forgiving than g_str_has_prefix()) + * 3) it can get the job done with one strncmp() (with + * the length argument being a compile time constant, and compiler optimizing + * strncmp() call). + * Compare to g_str_has_prefix() which requires one call into glib, then + * one strlen() and one strncmp() call. + * + * If it compiles (re:1), NM_STR_HAS_PREFIX() can fully replace g_str_has_prefix(). + * The other way is not necessarily possible due to 2). + */ #define NM_STR_HAS_PREFIX(str, prefix) \ ({ \ const char *const _str_has_prefix = (str); \ @@ -496,6 +512,22 @@ nm_streq0(const char *s1, const char *s2) _str_has_prefix && (strncmp(_str_has_prefix, "" prefix "", NM_STRLEN(prefix)) == 0); \ }) +/* + * Very similar to g_str_has_suffix() with the obvious meaning. + * Differences: + * 1) suffix is enforced to be a C string literal + * (it is thus more restricted, but you'll know it at compile time). + * 2) it accepts str==NULL + * (it is thus more forgiving than g_str_has_suffix()) + * 3) it can get the job done with one strlen() and one memcpy() call (with + * the length argument being a compile time constant, and compiler optimizing + * memcpy() call). + * Compare to g_str_has_suffix() which requires one call into glib, then + * two strlen() and one strcmp() call. + * + * If it compiles (re:1), NM_STR_HAS_SUFFIX() can fully replace g_str_has_suffix(). + * The other way is not necessarily possible due to 2). + */ #define NM_STR_HAS_SUFFIX(str, suffix) \ ({ \ const char *const _str_has_suffix = (str); \ @@ -746,7 +778,7 @@ nm_streq0(const char *s1, const char *s2) /*****************************************************************************/ -#define _NM_IN_SET_OP(x, idx) (_x == (x)) +#define _NM_IN_SET_OP(x, idx) ((int) (_x == (x))) #define _NM_IN_SET(op, type, x, ...) \ ({ \ type _x = (x); \ @@ -782,7 +814,7 @@ _NM_IN_STRSET_EVAL_op_streq(const char *x1, const char *x) return x && nm_streq(x1, x); } -#define _NM_IN_STRSET_EVAL_OP_NULL(x, idx) (((const char *) NULL) == (x)) +#define _NM_IN_STRSET_EVAL_OP_NULL(x, idx) ((int) (((const char *) NULL) == (x))) #define _NM_IN_STRSET_EVAL_OP_STREQ(x, idx) _NM_IN_STRSET_EVAL_op_streq(_x1, x) #define _NM_IN_STRSET_EVAL(op, eval_op, x1, ...) \ ({ \ diff --git a/src/libnm-systemd-core/meson.build b/src/libnm-systemd-core/meson.build index 1fa94486..80b95c42 100644 --- a/src/libnm-systemd-core/meson.build +++ b/src/libnm-systemd-core/meson.build @@ -10,6 +10,7 @@ libnm_systemd_core = static_library( 'src/libsystemd-network/dhcp-packet.c', 'src/libsystemd-network/dhcp6-network.c', 'src/libsystemd-network/dhcp6-option.c', + 'src/libsystemd-network/dhcp6-protocol.c', 'src/libsystemd-network/lldp-neighbor.c', 'src/libsystemd-network/lldp-network.c', 'src/libsystemd-network/network-common.c', diff --git a/src/libnm-systemd-core/nm-sd-utils-dhcp.c b/src/libnm-systemd-core/nm-sd-utils-dhcp.c index 7fe889fd..13c75055 100644 --- a/src/libnm-systemd-core/nm-sd-utils-dhcp.c +++ b/src/libnm-systemd-core/nm-sd-utils-dhcp.c @@ -13,8 +13,7 @@ int nm_sd_dhcp_lease_get_private_options(sd_dhcp_lease *lease, nm_sd_dhcp_option **out_options) { - struct sd_dhcp_raw_option *raw_option; - int cnt = 0; + int cnt = 0; g_return_val_if_fail(lease, -EINVAL); g_return_val_if_fail(out_options, -EINVAL); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c index 74aa19de..f2d94214 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c @@ -10,20 +10,27 @@ #include "sd-id128.h" #include "dhcp-identifier.h" -#include "dhcp6-protocol.h" #include "netif-util.h" #include "siphash24.h" #include "sparse-endian.h" #include "stat-util.h" -#include "stdio-util.h" +#include "string-table.h" #include "udev-util.h" -#include "virt.h" #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03) #define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */ -int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) { +static const char * const duid_type_table[_DUID_TYPE_MAX] = { + [DUID_TYPE_LLT] = "DUID-LLT", + [DUID_TYPE_EN] = "DUID-EN/Vendor", + [DUID_TYPE_LL] = "DUID-LL", + [DUID_TYPE_UUID] = "UUID", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType); + +int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict) { struct duid d; assert_cc(sizeof(d.raw) >= MAX_DUID_LEN); @@ -60,107 +67,141 @@ int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) { } #if 0 /* NM_IGNORED */ -int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) { +static int dhcp_identifier_set_duid_llt(const uint8_t *addr, size_t addr_len, uint16_t arp_type, usec_t t, struct duid *ret_duid, size_t *ret_len) { uint16_t time_from_2000y; - assert(duid); - assert(len); assert(addr); + assert(ret_duid); + assert(ret_len); + + if (addr_len == 0) + return -EOPNOTSUPP; if (arp_type == ARPHRD_ETHER) assert_return(addr_len == ETH_ALEN, -EINVAL); else if (arp_type == ARPHRD_INFINIBAND) assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); else - return -EINVAL; + return -EOPNOTSUPP; if (t < USEC_2000) time_from_2000y = 0; else time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff); - unaligned_write_be16(&duid->type, DUID_TYPE_LLT); - unaligned_write_be16(&duid->llt.htype, arp_type); - unaligned_write_be32(&duid->llt.time, time_from_2000y); - memcpy(duid->llt.haddr, addr, addr_len); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT); + unaligned_write_be16(&ret_duid->llt.htype, arp_type); + unaligned_write_be32(&ret_duid->llt.time, time_from_2000y); + memcpy(ret_duid->llt.haddr, addr, addr_len); - *len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len; + *ret_len = offsetof(struct duid, llt.haddr) + addr_len; return 0; } -int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) { - assert(duid); - assert(len); +static int dhcp_identifier_set_duid_ll(const uint8_t *addr, size_t addr_len, uint16_t arp_type, struct duid *ret_duid, size_t *ret_len) { assert(addr); + assert(ret_duid); + assert(ret_len); + + if (addr_len == 0) + return -EOPNOTSUPP; if (arp_type == ARPHRD_ETHER) assert_return(addr_len == ETH_ALEN, -EINVAL); else if (arp_type == ARPHRD_INFINIBAND) assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); else - return -EINVAL; + return -EOPNOTSUPP; - unaligned_write_be16(&duid->type, DUID_TYPE_LL); - unaligned_write_be16(&duid->ll.htype, arp_type); - memcpy(duid->ll.haddr, addr, addr_len); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL); + unaligned_write_be16(&ret_duid->ll.htype, arp_type); + memcpy(ret_duid->ll.haddr, addr, addr_len); - *len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len; + *ret_len = offsetof(struct duid, ll.haddr) + addr_len; return 0; } #endif /* NM_IGNORED */ -int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { +int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len) { sd_id128_t machine_id; uint64_t hash; int r; - assert(duid); - assert(len); + assert(ret_duid); + assert(ret_len); - r = sd_id128_get_machine(&machine_id); - if (r < 0) { -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (!test_mode) { + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + } else + /* For tests, especially for fuzzers, reproducibility is important. + * Hence, use a static and constant machine ID. + * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */ machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10); -#else - return r; -#endif - } - unaligned_write_be16(&duid->type, DUID_TYPE_EN); - unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN); - - *len = sizeof(duid->type) + sizeof(duid->en); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN); + unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN); /* a bit of snake-oil perhaps, but no need to expose the machine-id * directly; duid->en.id might not be aligned, so we need to copy */ hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes)); - memcpy(duid->en.id, &hash, sizeof(duid->en.id)); + memcpy(ret_duid->en.id, &hash, sizeof(ret_duid->en.id)); + + *ret_len = offsetof(struct duid, en.id) + sizeof(ret_duid->en.id); + + if (test_mode) + assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0); return 0; } #if 0 /* NM_IGNORED */ -int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) { +static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) { sd_id128_t machine_id; int r; - assert(duid); - assert(len); + assert(ret_duid); + assert(ret_len); r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id); if (r < 0) return r; - unaligned_write_be16(&duid->type, DUID_TYPE_UUID); - memcpy(&duid->raw.data, &machine_id, sizeof(machine_id)); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID); + memcpy(&ret_duid->uuid.uuid, &machine_id, sizeof(machine_id)); - *len = sizeof(duid->type) + sizeof(machine_id); + *ret_len = offsetof(struct duid, uuid.uuid) + sizeof(machine_id); return 0; } -#endif + +int dhcp_identifier_set_duid( + DUIDType duid_type, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type, + usec_t llt_time, + bool test_mode, + struct duid *ret_duid, + size_t *ret_len) { + + switch (duid_type) { + case DUID_TYPE_LLT: + return dhcp_identifier_set_duid_llt(addr, addr_len, arp_type, llt_time, ret_duid, ret_len); + case DUID_TYPE_EN: + return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len); + case DUID_TYPE_LL: + return dhcp_identifier_set_duid_ll(addr, addr_len, arp_type, ret_duid, ret_len); + case DUID_TYPE_UUID: + return dhcp_identifier_set_duid_uuid(ret_duid, ret_len); + default: + return -EINVAL; + } +} +#endif /* NM_IGNORED */ int dhcp_identifier_set_iaid( int ifindex, @@ -168,7 +209,7 @@ int dhcp_identifier_set_iaid( size_t mac_len, bool legacy_unstable_byteorder, bool use_mac, - void *_id) { + void *ret) { #if 0 /* NM_IGNORED */ /* name is a pointer to memory in the sd_device struct, so must @@ -221,7 +262,7 @@ int dhcp_identifier_set_iaid( * behavior. */ id32 = be32toh(id32); - unaligned_write_ne32(_id, id32); + unaligned_write_ne32(ret, id32); return 0; #else /* NM_IGNORED */ /* for NetworkManager, we don't use this function and we should never call here. diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h index 6c24af03..697ba3bf 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h @@ -54,9 +54,23 @@ struct duid { }; } _packed_; -int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict); -int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); -int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); -int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len); -int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len); -int dhcp_identifier_set_iaid(int ifindex, const uint8_t *mac, size_t mac_len, bool legacy_unstable_byteorder, bool use_mac, void *_id); +int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict); +int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len); +int dhcp_identifier_set_duid( + DUIDType duid_type, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type, + usec_t llt_time, + bool test_mode, + struct duid *ret_duid, + size_t *ret_len); +int dhcp_identifier_set_iaid( + int ifindex, + const uint8_t *mac, + size_t mac_len, + bool legacy_unstable_byteorder, + bool use_mac, + void *ret); + +const char *duid_type_to_string(DUIDType t) _const_; diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h index 992ac9f3..c67e9511 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h @@ -16,8 +16,6 @@ struct sd_dhcp_route { struct in_addr dst_addr; struct in_addr gw_addr; unsigned char dst_prefixlen; - - uint8_t option; }; struct sd_dhcp_raw_option { @@ -52,8 +50,10 @@ struct sd_dhcp_lease { DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX]; - struct sd_dhcp_route *static_route; - size_t static_route_size; + struct sd_dhcp_route *static_routes; + size_t n_static_routes; + struct sd_dhcp_route *classless_routes; + size_t n_classless_routes; uint16_t mtu; /* 0 if unset */ diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-option.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-option.c index 1710f696..712f6097 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-option.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-option.c @@ -60,7 +60,6 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, case SD_DHCP_OPTION_USER_CLASS: { size_t total = 0; - char **s; if (strv_isempty((char **) optval)) return -EINVAL; diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-protocol.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-protocol.h index 11f4201a..dd54bcf6 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-protocol.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-protocol.h @@ -73,15 +73,24 @@ enum { }; enum { - DHCP_DISCOVER = 1, - DHCP_OFFER = 2, - DHCP_REQUEST = 3, - DHCP_DECLINE = 4, - DHCP_ACK = 5, - DHCP_NAK = 6, - DHCP_RELEASE = 7, - DHCP_INFORM = 8, - DHCP_FORCERENEW = 9, + DHCP_DISCOVER = 1, /* [RFC2132] */ + DHCP_OFFER = 2, /* [RFC2132] */ + DHCP_REQUEST = 3, /* [RFC2132] */ + DHCP_DECLINE = 4, /* [RFC2132] */ + DHCP_ACK = 5, /* [RFC2132] */ + DHCP_NAK = 6, /* [RFC2132] */ + DHCP_RELEASE = 7, /* [RFC2132] */ + DHCP_INFORM = 8, /* [RFC2132] */ + DHCP_FORCERENEW = 9, /* [RFC3203] */ + DHCPLEASEQUERY = 10, /* [RFC4388] */ + DHCPLEASEUNASSIGNED = 11, /* [RFC4388] */ + DHCPLEASEUNKNOWN = 12, /* [RFC4388] */ + DHCPLEASEACTIVE = 13, /* [RFC4388] */ + DHCPBULKLEASEQUERY = 14, /* [RFC6926] */ + DHCPLEASEQUERYDONE = 15, /* [RFC6926] */ + DHCPACTIVELEASEQUERY = 16, /* [RFC7724] */ + DHCPLEASEQUERYSTATUS = 17, /* [RFC7724] */ + DHCPTLS = 18, /* [RFC7724] */ }; enum { diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h index f9434098..0d7813f6 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h @@ -11,138 +11,83 @@ #include "sd-event.h" #include "sd-dhcp6-client.h" +#include "dhcp-identifier.h" +#include "dhcp6-option.h" #include "dhcp6-protocol.h" +#include "ether-addr-util.h" #include "hashmap.h" -#include "list.h" #include "macro.h" #include "network-common.h" +#include "ordered-set.h" #include "sparse-endian.h" +#include "time-util.h" -typedef struct sd_dhcp6_option { +/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */ +typedef enum DHCP6RequestIA { + DHCP6_REQUEST_IA_NA = 1 << 0, + DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */ + DHCP6_REQUEST_IA_PD = 1 << 2, +} DHCP6RequestIA; + +struct sd_dhcp6_client { unsigned n_ref; - uint32_t enterprise_identifier; - uint16_t option; - void *data; - size_t length; -} sd_dhcp6_option; - -extern const struct hash_ops dhcp6_option_hash_ops; - -/* Common option header */ -typedef struct DHCP6Option { - be16_t code; - be16_t len; - uint8_t data[]; -} _packed_ DHCP6Option; - -/* Address option */ -struct iaaddr { - struct in6_addr address; - be32_t lifetime_preferred; - be32_t lifetime_valid; -} _packed_; - -/* Prefix Delegation Prefix option */ -struct iapdprefix { - be32_t lifetime_preferred; - be32_t lifetime_valid; - uint8_t prefixlen; - struct in6_addr address; -} _packed_; - -typedef struct DHCP6Address DHCP6Address; - -struct DHCP6Address { - LIST_FIELDS(DHCP6Address, addresses); - - union { - struct iaaddr iaaddr; - struct iapdprefix iapdprefix; - }; + int ifindex; + char *ifname; + + struct in6_addr local_address; + struct hw_addr_data hw_addr; + uint16_t arp_type; + + sd_event *event; + sd_event_source *receive_message; + sd_event_source *timeout_resend; + sd_event_source *timeout_expire; + sd_event_source *timeout_t1; + sd_event_source *timeout_t2; + int event_priority; + int fd; + + DHCP6State state; + bool information_request; + usec_t information_request_time_usec; + usec_t information_refresh_time_usec; + be32_t transaction_id; + usec_t transaction_start; + usec_t retransmit_time; + uint8_t retransmit_count; + + bool iaid_set; + DHCP6IA ia_na; + DHCP6IA ia_pd; + DHCP6RequestIA request_ia; + struct duid duid; + size_t duid_len; + be16_t *req_opts; + size_t req_opts_len; + char *fqdn; + char *mudurl; + char **user_class; + char **vendor_class; + OrderedHashmap *extra_options; + OrderedSet *vendor_options; + + struct sd_dhcp6_lease *lease; + + sd_dhcp6_client_callback_t callback; + void *userdata; + + /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */ + bool test_mode; }; -/* Non-temporary Address option */ -struct ia_na { - be32_t id; - be32_t lifetime_t1; - be32_t lifetime_t2; -} _packed_; - -/* Prefix Delegation option */ -struct ia_pd { - be32_t id; - be32_t lifetime_t1; - be32_t lifetime_t2; -} _packed_; - -/* Temporary Address option */ -struct ia_ta { - be32_t id; -} _packed_; - -typedef struct DHCP6IA { - uint16_t type; - union { - struct ia_na ia_na; - struct ia_pd ia_pd; - struct ia_ta ia_ta; - }; - - LIST_HEAD(DHCP6Address, addresses); -} DHCP6IA; - -typedef struct sd_dhcp6_client sd_dhcp6_client; - -bool dhcp6_option_can_request(uint16_t option); -int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, - size_t optlen, const void *optval); -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); -int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix); -int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); -int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class); -int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class); -int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options); - -int dhcp6_option_parse( - const uint8_t *buf, - size_t buflen, - size_t *offset, - uint16_t *ret_option_code, - size_t *ret_option_data_len, - const uint8_t **ret_option_data); -int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message); -int dhcp6_option_parse_ia( - sd_dhcp6_client *client, - be32_t iaid, - uint16_t option_code, - size_t option_data_len, - const uint8_t *option_data, - DHCP6IA *ret); -int dhcp6_option_parse_addresses( - const uint8_t *optval, - size_t optlen, - struct in6_addr **addrs, - size_t *count); -int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret); -int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret); - int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address); int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, const void *packet, size_t len); -int client_parse_message( - sd_dhcp6_client *client, - DHCP6Message *message, - size_t len, - sd_dhcp6_lease *lease); - -const char *dhcp6_message_type_to_string(int s) _const_; -int dhcp6_message_type_from_string(const char *s) _pure_; -const char *dhcp6_message_status_to_string(int s) _const_; -int dhcp6_message_status_from_string(const char *s) _pure_; - +int dhcp6_client_send_message(sd_dhcp6_client *client); void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode); +int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id); #define log_dhcp6_client_errno(client, error, fmt, ...) \ log_interface_prefix_full_errno( \ diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h index 8ae2ecd6..1f10dccb 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h @@ -5,11 +5,13 @@ Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ -#include <stdint.h> +#include <inttypes.h> #include "sd-dhcp6-lease.h" -#include "dhcp6-internal.h" +#include "dhcp6-option.h" +#include "macro.h" +#include "time-util.h" struct sd_dhcp6_lease { unsigned n_ref; @@ -21,10 +23,13 @@ struct sd_dhcp6_lease { uint8_t preference; bool rapid_commit; triple_timestamp timestamp; + usec_t lifetime_t1; + usec_t lifetime_t2; + usec_t lifetime_valid; struct in6_addr server_address; - DHCP6IA ia; - DHCP6IA pd; + DHCP6IA *ia_na; /* Identity association non-temporary addresses */ + DHCP6IA *ia_pd; /* Identity association prefix delegation */ DHCP6Address *addr_iter; DHCP6Address *prefix_iter; @@ -40,17 +45,15 @@ struct sd_dhcp6_lease { char *fqdn; }; -int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire); -DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia); - +int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid); int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len); int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len); int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference); -int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference); +int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret); int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease); -int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); +int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret); int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); @@ -59,3 +62,10 @@ int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t op int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_new(sd_dhcp6_lease **ret); +int dhcp6_lease_new_from_message( + sd_dhcp6_client *client, + const DHCP6Message *message, + size_t len, + const triple_timestamp *timestamp, + const struct in6_addr *server_address, + sd_dhcp6_lease **ret); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c index 237771e6..03d56371 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c @@ -11,14 +11,12 @@ #include "sd-dhcp6-client.h" #include "alloc-util.h" -#include "dhcp-identifier.h" #include "dhcp6-internal.h" -#include "dhcp6-lease-internal.h" +#include "dhcp6-option.h" #include "dhcp6-protocol.h" #include "dns-domain.h" #include "escape.h" #include "memory-util.h" -#include "sparse-endian.h" #include "strv.h" #include "unaligned.h" @@ -76,8 +74,8 @@ bool dhcp6_option_can_request(uint16_t option) { return false; case SD_DHCP6_OPTION_CLIENT_FQDN: case SD_DHCP6_OPTION_PANA_AGENT: - case SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE: - case SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE: + case SD_DHCP6_OPTION_POSIX_TIMEZONE: + case SD_DHCP6_OPTION_TZDB_TIMEZONE: return true; case SD_DHCP6_OPTION_ERO: case SD_DHCP6_OPTION_LQ_QUERY: @@ -214,19 +212,15 @@ bool dhcp6_option_can_request(uint16_t option) { } static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { - DHCP6Option *option; - assert_return(buf, -EINVAL); assert_return(*buf, -EINVAL); assert_return(buflen, -EINVAL); - option = (DHCP6Option*) *buf; - if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data)) return -ENOBUFS; - option->code = htobe16(optcode); - option->len = htobe16(optlen); + unaligned_write_be16(*buf + offsetof(DHCP6Option, code), optcode); + unaligned_write_be16(*buf + offsetof(DHCP6Option, len), optlen); *buf += offsetof(DHCP6Option, data); *buflen -= offsetof(DHCP6Option, data); @@ -244,15 +238,13 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, if (r < 0) return r; - memcpy_safe(*buf, optval, optlen); - - *buf += optlen; + *buf = mempcpy_safe(*buf, optval, optlen); *buflen -= optlen; return 0; } -int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) { +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options) { sd_dhcp6_option *options; int r; @@ -261,7 +253,7 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash assert(buflen); assert(vendor_options); - ORDERED_HASHMAP_FOREACH(options, vendor_options) { + ORDERED_SET_FOREACH(options, vendor_options) { _cleanup_free_ uint8_t *p = NULL; size_t total; @@ -284,79 +276,31 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash return 0; } -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { - size_t ia_buflen, ia_addrlen = 0; - struct ia_na ia_na; - struct ia_ta ia_ta; - DHCP6Address *addr; - uint8_t *ia_hdr; - uint16_t len; - void *p; +static int option_append_ia_address(uint8_t **buf, size_t *buflen, const struct iaaddr *address) { + struct iaaddr a; int r; - assert_return(buf, -EINVAL); - assert_return(*buf, -EINVAL); - assert_return(buflen, -EINVAL); - assert_return(ia, -EINVAL); - - /* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */ - - switch (ia->type) { - case SD_DHCP6_OPTION_IA_NA: - len = DHCP6_OPTION_IA_NA_LEN; - ia_na = (struct ia_na) { - .id = ia->ia_na.id, - }; - p = &ia_na; - break; - - case SD_DHCP6_OPTION_IA_TA: - len = DHCP6_OPTION_IA_TA_LEN; - ia_ta = (struct ia_ta) { - .id = ia->ia_ta.id, - }; - p = &ia_ta; - break; - - default: - return -EINVAL; - } - - if (*buflen < offsetof(DHCP6Option, data) + len) - return -ENOBUFS; - - ia_hdr = *buf; - ia_buflen = *buflen; - - *buf += offsetof(DHCP6Option, data); - *buflen -= offsetof(DHCP6Option, data); - - memcpy(*buf, p, len); - - *buf += len; - *buflen -= len; - - LIST_FOREACH(addresses, addr, ia->addresses) { - struct iaaddr a = { - .address = addr->iaaddr.address, - }; - - r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr)); - if (r < 0) - return r; + assert(buf); + assert(*buf); + assert(buflen); + assert(address); - memcpy(*buf, &a, sizeof(struct iaaddr)); + /* Do not append T1 and T2. */ + a = (struct iaaddr) { + .address = address->address, + }; - *buf += sizeof(struct iaaddr); - *buflen -= sizeof(struct iaaddr); + r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr)); + if (r < 0) + return r; - ia_addrlen += offsetof(DHCP6Option, data) + sizeof(struct iaaddr); - } + *buf = mempcpy(*buf, &a, sizeof(struct iaaddr)); + *buflen -= sizeof(struct iaaddr); - return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen); + return offsetof(DHCP6Option, data) + sizeof(struct iaaddr); } -static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Address *prefix) { +static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct iapdprefix *prefix) { struct iapdprefix p; int r; @@ -365,79 +309,84 @@ static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Add assert(buflen); assert(prefix); - if (prefix->iapdprefix.prefixlen == 0) + if (prefix->prefixlen == 0) return -EINVAL; /* Do not append T1 and T2. */ - p = (struct iapdprefix) { - .prefixlen = prefix->iapdprefix.prefixlen, - .address = prefix->iapdprefix.address, + .prefixlen = prefix->prefixlen, + .address = prefix->address, }; r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix)); if (r < 0) return r; - memcpy(*buf, &p, sizeof(struct iapdprefix)); - - *buf += sizeof(struct iapdprefix); + *buf = mempcpy(*buf, &p, sizeof(struct iapdprefix)); *buflen -= sizeof(struct iapdprefix); return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix); } -int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix) { - struct ia_pd ia_pd; - size_t len, pd_buflen; - uint8_t *pd_hdr; +int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { + struct ia_header header; + size_t ia_buflen; + uint8_t *ia_hdr; + uint16_t len; int r; assert_return(buf, -EINVAL); assert_return(*buf, -EINVAL); assert_return(buflen, -EINVAL); - assert_return(pd, -EINVAL); - assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL); + assert_return(ia, -EINVAL); - /* Do not set T1 and T2. */ - ia_pd = (struct ia_pd) { - .id = pd->ia_pd.id, - }; - len = sizeof(struct ia_pd); + /* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */ + + switch (ia->type) { + case SD_DHCP6_OPTION_IA_NA: + case SD_DHCP6_OPTION_IA_PD: + len = sizeof(struct ia_header); + header = (struct ia_header) { + .id = ia->header.id, + }; + break; + + case SD_DHCP6_OPTION_IA_TA: + len = sizeof(header.id); /* IA_TA does not have lifetime. */ + header = (struct ia_header) { + .id = ia->header.id, + }; + break; + + default: + assert_not_reached(); + } if (*buflen < offsetof(DHCP6Option, data) + len) return -ENOBUFS; - pd_hdr = *buf; - pd_buflen = *buflen; + ia_hdr = *buf; + ia_buflen = *buflen; /* The header will be written at the end of this function. */ *buf += offsetof(DHCP6Option, data); *buflen -= offsetof(DHCP6Option, data); - memcpy(*buf, &ia_pd, len); - - *buf += sizeof(struct ia_pd); - *buflen -= sizeof(struct ia_pd); - - DHCP6Address *prefix; - LIST_FOREACH(addresses, prefix, pd->addresses) { - r = option_append_pd_prefix(buf, buflen, prefix); - if (r < 0) - return r; - - len += r; - } + *buf = mempcpy(*buf, &header, len); + *buflen -= len; - if (hint_pd_prefix && hint_pd_prefix->iapdprefix.prefixlen > 0) { - r = option_append_pd_prefix(buf, buflen, hint_pd_prefix); + LIST_FOREACH(addresses, addr, ia->addresses) { + if (ia->type == SD_DHCP6_OPTION_IA_PD) + r = option_append_pd_prefix(buf, buflen, &addr->iapdprefix); + else + r = option_append_ia_address(buf, buflen, &addr->iaaddr); if (r < 0) return r; len += r; } - return option_append_hdr(&pd_hdr, &pd_buflen, pd->type, len); + return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len); } int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { @@ -470,7 +419,6 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class) { _cleanup_free_ uint8_t *p = NULL; size_t total = 0, offset = 0; - char * const *s; assert(buf); assert(*buf); @@ -503,7 +451,6 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const _cleanup_free_ uint8_t *p = NULL; uint32_t enterprise_identifier; size_t total, offset; - char * const *s; assert(buf); assert(*buf); @@ -550,7 +497,6 @@ int dhcp6_option_parse( size_t *ret_option_data_len, const uint8_t **ret_option_data) { - const DHCP6Option *option; size_t len; assert(buf); @@ -565,16 +511,15 @@ int dhcp6_option_parse( if (*offset >= buflen - offsetof(DHCP6Option, data)) return -EBADMSG; - option = (const DHCP6Option*) (buf + *offset); - len = be16toh(option->len); + len = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, len)); if (len > buflen - offsetof(DHCP6Option, data) - *offset) return -EBADMSG; - *offset += offsetof(DHCP6Option, data) + len; - *ret_option_code = be16toh(option->code); + *ret_option_code = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, code)); *ret_option_data_len = len; - *ret_option_data = option->data; + *ret_option_data = buf + *offset + offsetof(DHCP6Option, data); + *offset += offsetof(DHCP6Option, data) + len; return 0; } @@ -603,9 +548,9 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) { int r; - assert(buf); + assert(buf || buflen == 0); - for(size_t offset = 0; offset < buflen;) { + for (size_t offset = 0; offset < buflen;) { const uint8_t *data; size_t data_len; uint16_t code; @@ -614,22 +559,22 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t if (r < 0) return r; - switch(code) { + switch (code) { case SD_DHCP6_OPTION_STATUS_CODE: { _cleanup_free_ char *msg = NULL; r = dhcp6_option_parse_status(data, data_len, &msg); if (r == -ENOMEM) return r; - if (r < 0) - /* Let's log but ignore the invalid status option. */ - log_dhcp6_client_errno(client, r, - "Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m"); - else if (r > 0) + if (r > 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received an IA address or PD prefix option with non-zero status: %s%s%s", strempty(msg), isempty(msg) ? "" : ": ", dhcp6_message_status_to_string(r)); + if (r < 0) + /* Let's log but ignore the invalid status option. */ + log_dhcp6_client_errno(client, r, + "Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m"); break; } default: @@ -640,19 +585,29 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t return 0; } -static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) { +static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) { + _cleanup_free_ DHCP6Address *a = NULL; uint32_t lt_valid, lt_pref; - DHCP6Address *a; int r; - assert(data); - assert(ret); + assert(ia); + assert(data || len == 0); + + if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA address sub-option in an invalid option, ignoring."); if (len < sizeof(struct iaaddr)) return -EBADMSG; - lt_valid = be32toh(((const struct iaaddr*) data)->lifetime_valid); - lt_pref = be32toh(((const struct iaaddr*) data)->lifetime_preferred); + a = new(DHCP6Address, 1); + if (!a) + return -ENOMEM; + + memcpy(&a->iaaddr, data, sizeof(struct iaaddr)); + + lt_valid = be32toh(a->iaaddr.lifetime_valid); + lt_pref = be32toh(a->iaaddr.lifetime_preferred); if (lt_valid == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), @@ -669,27 +624,33 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t return r; } - a = new(DHCP6Address, 1); - if (!a) - return -ENOMEM; - - LIST_INIT(addresses, a); - memcpy(&a->iaaddr, data, sizeof(struct iaaddr)); - - *ret = a; + LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a)); return 0; } -static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) { +static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) { + _cleanup_free_ DHCP6Address *a = NULL; uint32_t lt_valid, lt_pref; - DHCP6Address *a; int r; + assert(ia); + assert(data || len == 0); + + if (ia->type != SD_DHCP6_OPTION_IA_PD) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an PD prefix sub-option in an invalid option, ignoring"); + if (len < sizeof(struct iapdprefix)) - return -ENOMSG; + return -EBADMSG; + + a = new(DHCP6Address, 1); + if (!a) + return -ENOMEM; + + memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix)); - lt_valid = be32toh(((const struct iapdprefix*) data)->lifetime_valid); - lt_pref = be32toh(((const struct iapdprefix*) data)->lifetime_preferred); + lt_valid = be32toh(a->iapdprefix.lifetime_valid); + lt_pref = be32toh(a->iapdprefix.lifetime_preferred); if (lt_valid == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), @@ -706,14 +667,7 @@ static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t return r; } - a = new(DHCP6Address, 1); - if (!a) - return -ENOMEM; - - LIST_INIT(addresses, a); - memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix)); - - *ret = a; + LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a)); return 0; } @@ -723,16 +677,15 @@ int dhcp6_option_parse_ia( uint16_t option_code, size_t option_data_len, const uint8_t *option_data, - DHCP6IA *ret) { + DHCP6IA **ret) { - _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; - uint32_t lt_t1, lt_t2, lt_min = UINT32_MAX; - be32_t received_iaid; - size_t offset; + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; + uint32_t lt_t1, lt_t2; + size_t header_len; int r; assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD)); - assert(option_data); + assert(option_data || option_data_len == 0); assert(ret); /* This will return the following: @@ -745,56 +698,51 @@ int dhcp6_option_parse_ia( switch (option_code) { case SD_DHCP6_OPTION_IA_NA: - - if (option_data_len < DHCP6_OPTION_IA_NA_LEN) - return -EBADMSG; - - offset = DHCP6_OPTION_IA_NA_LEN; - - received_iaid = ((const struct ia_na*) option_data)->id; - lt_t1 = be32toh(((const struct ia_na*) option_data)->lifetime_t1); - lt_t2 = be32toh(((const struct ia_na*) option_data)->lifetime_t2); - break; - case SD_DHCP6_OPTION_IA_PD: - - if (option_data_len < DHCP6_OPTION_IA_PD_LEN) - return -EBADMSG; - - offset = DHCP6_OPTION_IA_PD_LEN; - - received_iaid = ((const struct ia_pd*) option_data)->id; - lt_t1 = be32toh(((const struct ia_pd*) option_data)->lifetime_t1); - lt_t2 = be32toh(((const struct ia_pd*) option_data)->lifetime_t2); + header_len = sizeof(struct ia_header); break; case SD_DHCP6_OPTION_IA_TA: - if (option_data_len < DHCP6_OPTION_IA_TA_LEN) - return -ENOMSG; - - offset = DHCP6_OPTION_IA_TA_LEN; - - received_iaid = ((const struct ia_ta*) option_data)->id; - lt_t1 = lt_t2 = 0; /* No lifetime for IA_TA. */ + header_len = sizeof(be32_t); /* IA_TA does not have lifetime. */ break; default: assert_not_reached(); } + if (option_data_len < header_len) + return -EBADMSG; + + ia = new(DHCP6IA, 1); + if (!ia) + return -ENOMEM; + + *ia = (DHCP6IA) { + .type = option_code, + }; + memcpy(&ia->header, option_data, header_len); + /* According to RFC8415, IAs which do not match the client's IAID should be ignored, * but not necessary to ignore or refuse the whole message. */ - if (received_iaid != iaid) + if (ia->header.id != iaid) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO), "Received an IA option with a different IAID " "from the one chosen by the client, ignoring."); + /* It is not necessary to check if the lifetime_t2 is zero here, as in that case it will be updated later. */ + lt_t1 = be32toh(ia->header.lifetime_t1); + lt_t2 = be32toh(ia->header.lifetime_t2); + if (lt_t1 > lt_t2) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.", lt_t1, lt_t2); + if (lt_t1 == 0 && lt_t2 > 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA option with zero T1 and non-zero T2 (%"PRIu32"sec), ignoring.", + lt_t2); - for (; offset < option_data_len;) { + for (size_t offset = header_len; offset < option_data_len;) { const uint8_t *subdata; size_t subdata_len; uint16_t subopt; @@ -805,41 +753,19 @@ int dhcp6_option_parse_ia( switch (subopt) { case SD_DHCP6_OPTION_IAADDR: { - DHCP6Address *a; - - if (!IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) { - log_dhcp6_client(client, "Received an IA_PD option with an IA address, ignoring."); - continue; - } - - r = dhcp6_option_parse_ia_address(client, subdata, subdata_len, &a); + r = dhcp6_option_parse_ia_address(client, ia, subdata, subdata_len); if (r == -ENOMEM) return r; - if (r < 0) - /* Ignore the sub-option on non-critical errors. */ - continue; - lt_min = MIN(lt_min, be32toh(a->iaaddr.lifetime_valid)); - LIST_PREPEND(addresses, ia.addresses, a); + /* Ignore non-critical errors in the sub-option. */ break; } case SD_DHCP6_OPTION_IA_PD_PREFIX: { - DHCP6Address *a; - - if (option_code != SD_DHCP6_OPTION_IA_PD) { - log_dhcp6_client(client, "Received an IA_NA or IA_TA option with an PD prefix, ignoring"); - continue; - } - - r = dhcp6_option_parse_ia_pdprefix(client, subdata, subdata_len, &a); + r = dhcp6_option_parse_ia_pdprefix(client, ia, subdata, subdata_len); if (r == -ENOMEM) return r; - if (r < 0) - /* Ignore the sub-option on non-critical errors. */ - continue; - lt_min = MIN(lt_min, be32toh(a->iapdprefix.lifetime_valid)); - LIST_PREPEND(addresses, ia.addresses, a); + /* Ignore non-critical errors in the sub-option. */ break; } case SD_DHCP6_OPTION_STATUS_CODE: { @@ -848,14 +774,14 @@ int dhcp6_option_parse_ia( r = dhcp6_option_parse_status(subdata, subdata_len, &msg); if (r == -ENOMEM) return r; - if (r < 0) - log_dhcp6_client_errno(client, r, - "Received an IA option with an invalid status sub option, ignoring: %m"); - else if (r > 0) + if (r > 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received an IA option with non-zero status: %s%s%s", strempty(msg), isempty(msg) ? "" : ": ", dhcp6_message_status_to_string(r)); + if (r < 0) + log_dhcp6_client_errno(client, r, + "Received an IA option with an invalid status sub option, ignoring: %m"); break; } default: @@ -863,50 +789,11 @@ int dhcp6_option_parse_ia( } } - if (!ia.addresses) + if (!ia->addresses) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA), "Received an IA option without valid IA addresses or PD prefixes, ignoring."); - if (IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_PD) && - lt_t1 == 0 && lt_t2 == 0 && lt_min != UINT32_MAX) { - lt_t1 = lt_min / 2; - lt_t2 = lt_min / 10 * 8; - - log_dhcp6_client(client, "Received an IA option with both T1 and T2 equal to zero. " - "Adjusting them based on the minimum valid lifetime of IA addresses or PD prefixes: " - "T1=%"PRIu32"sec, T2=%"PRIu32"sec", lt_t1, lt_t2); - } - - switch(option_code) { - case SD_DHCP6_OPTION_IA_NA: - *ret = (DHCP6IA) { - .type = option_code, - .ia_na.id = iaid, - .ia_na.lifetime_t1 = htobe32(lt_t1), - .ia_na.lifetime_t2 = htobe32(lt_t2), - .addresses = TAKE_PTR(ia.addresses), - }; - break; - case SD_DHCP6_OPTION_IA_TA: - *ret = (DHCP6IA) { - .type = option_code, - .ia_ta.id = iaid, - .addresses = TAKE_PTR(ia.addresses), - }; - break; - case SD_DHCP6_OPTION_IA_PD: - *ret = (DHCP6IA) { - .type = option_code, - .ia_pd.id = iaid, - .ia_pd.lifetime_t1 = htobe32(lt_t1), - .ia_pd.lifetime_t2 = htobe32(lt_t2), - .addresses = TAKE_PTR(ia.addresses), - }; - break; - default: - assert_not_reached(); - } - + *ret = TAKE_PTR(ia); return 0; } diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h new file mode 100644 index 00000000..80aba7f3 --- /dev/null +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-dhcp6-client.h" + +#include "hash-funcs.h" +#include "list.h" +#include "macro.h" +#include "ordered-set.h" +#include "sparse-endian.h" + +typedef struct sd_dhcp6_option { + unsigned n_ref; + + uint32_t enterprise_identifier; + uint16_t option; + void *data; + size_t length; +} sd_dhcp6_option; + +extern const struct hash_ops dhcp6_option_hash_ops; + +/* Common option header */ +typedef struct DHCP6Option { + be16_t code; + be16_t len; + uint8_t data[]; +} _packed_ DHCP6Option; + +/* Address option */ +struct iaaddr { + struct in6_addr address; + be32_t lifetime_preferred; + be32_t lifetime_valid; +} _packed_; + +/* Prefix Delegation Prefix option */ +struct iapdprefix { + be32_t lifetime_preferred; + be32_t lifetime_valid; + uint8_t prefixlen; + struct in6_addr address; +} _packed_; + +typedef struct DHCP6Address DHCP6Address; + +struct DHCP6Address { + LIST_FIELDS(DHCP6Address, addresses); + + union { + struct iaaddr iaaddr; + struct iapdprefix iapdprefix; + }; +}; + +struct ia_header { + be32_t id; + be32_t lifetime_t1; + be32_t lifetime_t2; +} _packed_; + +typedef struct DHCP6IA { + uint16_t type; + struct ia_header header; + + LIST_HEAD(DHCP6Address, addresses); +} DHCP6IA; + +void dhcp6_ia_clear_addresses(DHCP6IA *ia); +DHCP6IA *dhcp6_ia_free(DHCP6IA *ia); +DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6IA*, dhcp6_ia_free); + +bool dhcp6_option_can_request(uint16_t option); + +int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, + size_t optlen, const void *optval); +int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); +int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); +int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class); +int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class); +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options); + +int dhcp6_option_parse( + const uint8_t *buf, + size_t buflen, + size_t *offset, + uint16_t *ret_option_code, + size_t *ret_option_data_len, + const uint8_t **ret_option_data); +int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message); +int dhcp6_option_parse_ia( + sd_dhcp6_client *client, + be32_t iaid, + uint16_t option_code, + size_t option_data_len, + const uint8_t *option_data, + DHCP6IA **ret); +int dhcp6_option_parse_addresses( + const uint8_t *optval, + size_t optlen, + struct in6_addr **addrs, + size_t *count); +int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret); +int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c new file mode 100644 index 00000000..e19faa51 --- /dev/null +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "nm-sd-adapt-core.h" + +#include "dhcp6-protocol.h" +#include "string-table.h" + +static const char * const dhcp6_state_table[_DHCP6_STATE_MAX] = { + [DHCP6_STATE_STOPPED] = "stopped", + [DHCP6_STATE_INFORMATION_REQUEST] = "information-request", + [DHCP6_STATE_SOLICITATION] = "solicitation", + [DHCP6_STATE_REQUEST] = "request", + [DHCP6_STATE_BOUND] = "bound", + [DHCP6_STATE_RENEW] = "renew", + [DHCP6_STATE_REBIND] = "rebind", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_state, DHCP6State); + +static const char * const dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = { + [DHCP6_MESSAGE_SOLICIT] = "Solicit", + [DHCP6_MESSAGE_ADVERTISE] = "Advertise", + [DHCP6_MESSAGE_REQUEST] = "Request", + [DHCP6_MESSAGE_CONFIRM] = "Confirm", + [DHCP6_MESSAGE_RENEW] = "Renew", + [DHCP6_MESSAGE_REBIND] = "Rebind", + [DHCP6_MESSAGE_REPLY] = "Reply", + [DHCP6_MESSAGE_RELEASE] = "Release", + [DHCP6_MESSAGE_DECLINE] = "Decline", + [DHCP6_MESSAGE_RECONFIGURE] = "Reconfigure", + [DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request", + [DHCP6_MESSAGE_RELAY_FORWARD] = "Relay Forward", + [DHCP6_MESSAGE_RELAY_REPLY] = "Relay Reply", + [DHCP6_MESSAGE_LEASE_QUERY] = "Lease Query", + [DHCP6_MESSAGE_LEASE_QUERY_REPLY] = "Lease Query Reply", + [DHCP6_MESSAGE_LEASE_QUERY_DONE] = "Lease Query Done", + [DHCP6_MESSAGE_LEASE_QUERY_DATA] = "Lease Query Data", + [DHCP6_MESSAGE_RECONFIGURE_REQUEST] = "Reconfigure Request", + [DHCP6_MESSAGE_RECONFIGURE_REPLY] = "Reconfigure Reply", + [DHCP6_MESSAGE_DHCPV4_QUERY] = "DHCPv4 Query", + [DHCP6_MESSAGE_DHCPV4_RESPONSE] = "DHCPv4 Response", + [DHCP6_MESSAGE_ACTIVE_LEASE_QUERY] = "Active Lease Query", + [DHCP6_MESSAGE_START_TLS] = "Start TLS", + [DHCP6_MESSAGE_BINDING_UPDATE] = "Binding Update", + [DHCP6_MESSAGE_BINDING_REPLY] = "Binding Reply", + [DHCP6_MESSAGE_POOL_REQUEST] = "Pool Request", + [DHCP6_MESSAGE_POOL_RESPONSE] = "Pool Response", + [DHCP6_MESSAGE_UPDATE_REQUEST] = "Update Request", + [DHCP6_MESSAGE_UPDATE_REQUEST_ALL] = "Update Request All", + [DHCP6_MESSAGE_UPDATE_DONE] = "Update Done", + [DHCP6_MESSAGE_CONNECT] = "Connect", + [DHCP6_MESSAGE_CONNECT_REPLY] = "Connect Reply", + [DHCP6_MESSAGE_DISCONNECT] = "Disconnect", + [DHCP6_MESSAGE_STATE] = "State", + [DHCP6_MESSAGE_CONTACT] = "Contact", +}; + +DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, DHCP6MessageType); + +static const char * const dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { + [DHCP6_STATUS_SUCCESS] = "Success", + [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure", + [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available", + [DHCP6_STATUS_NO_BINDING] = "Binding unavailable", + [DHCP6_STATUS_NOT_ON_LINK] = "Not on link", + [DHCP6_STATUS_USE_MULTICAST] = "Use multicast", + [DHCP6_STATUS_NO_PREFIX_AVAIL] = "No prefix available", + [DHCP6_STATUS_UNKNOWN_QUERY_TYPE] = "Unknown query type", + [DHCP6_STATUS_MALFORMED_QUERY] = "Malformed query", + [DHCP6_STATUS_NOT_CONFIGURED] = "Not configured", + [DHCP6_STATUS_NOT_ALLOWED] = "Not allowed", + [DHCP6_STATUS_QUERY_TERMINATED] = "Query terminated", + [DHCP6_STATUS_DATA_MISSING] = "Data missing", + [DHCP6_STATUS_CATCHUP_COMPLETE] = "Catch up complete", + [DHCP6_STATUS_NOT_SUPPORTED] = "Not supported", + [DHCP6_STATUS_TLS_CONNECTION_REFUSED] = "TLS connection refused", + [DHCP6_STATUS_ADDRESS_IN_USE] = "Address in use", + [DHCP6_STATUS_CONFIGURATION_CONFLICT] = "Configuration conflict", + [DHCP6_STATUS_MISSING_BINDING_INFORMATION] = "Missing binding information", + [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information", + [DHCP6_STATUS_SERVER_SHUTTING_DOWN] = "Server shutting down", + [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED] = "DNS update not supported", + [DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew", +}; + +DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, DHCP6Status); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h index 5d2af439..f4e47857 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h @@ -28,8 +28,8 @@ typedef struct DHCP6Message DHCP6Message; #define DHCP6_MIN_OPTIONS_SIZE \ 1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr) -#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \ - { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ +#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \ + { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } } enum { @@ -100,13 +100,15 @@ typedef enum DHCP6MessageType { DHCP6_MESSAGE_STATE = 34, /* RFC 8156 */ DHCP6_MESSAGE_CONTACT = 35, /* RFC 8156 */ _DHCP6_MESSAGE_TYPE_MAX, - _DHCP6_MESSAGE_TYPE_INVALID = -EINVAL, + _DHCP6_MESSAGE_TYPE_INVALID = -EINVAL, } DHCP6MessageType; typedef enum DHCP6NTPSubOption { DHCP6_NTP_SUBOPTION_SRV_ADDR = 1, DHCP6_NTP_SUBOPTION_MC_ADDR = 2, DHCP6_NTP_SUBOPTION_SRV_FQDN = 3, + _DHCP6_NTP_SUBOPTION_MAX, + _DHCP6_NTP_SUBOPTION_INVALID = -EINVAL, } DHCP6NTPSubOption; /* @@ -138,7 +140,7 @@ typedef enum DHCP6Status { DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED = 21, DHCP6_STATUS_EXCESSIVE_TIME_SKEW = 22, _DHCP6_STATUS_MAX, - _DHCP6_STATUS_INVALID = -EINVAL, + _DHCP6_STATUS_INVALID = -EINVAL, } DHCP6Status; typedef enum DHCP6FQDNFlag { @@ -146,3 +148,9 @@ typedef enum DHCP6FQDNFlag { DHCP6_FQDN_FLAG_O = 1 << 1, DHCP6_FQDN_FLAG_N = 1 << 2, } DHCP6FQDNFlag; + +const char *dhcp6_state_to_string(DHCP6State s) _const_; +const char *dhcp6_message_type_to_string(DHCP6MessageType s) _const_; +DHCP6MessageType dhcp6_message_type_from_string(const char *s) _pure_; +const char *dhcp6_message_status_to_string(DHCP6Status s) _const_; +DHCP6Status dhcp6_message_status_from_string(const char *s) _pure_; diff --git a/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c b/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c index b4d7c327..f56929ad 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c +++ b/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c @@ -335,9 +335,9 @@ void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) { usec_t base; /* Use the packet's timestamp if there is one known */ - base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic()); - if (base <= 0 || base == USEC_INFINITY) - base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */ + base = triple_timestamp_by_clock(&n->timestamp, CLOCK_BOOTTIME); + if (!timestamp_is_set(base)) + base = now(CLOCK_BOOTTIME); /* Otherwise, take the current time */ n->until = usec_add(base, n->ttl * USEC_PER_SEC); } else diff --git a/src/libnm-systemd-core/src/libsystemd-network/network-internal.h b/src/libnm-systemd-core/src/libsystemd-network/network-internal.h index 895a00d0..5aa225e9 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/network-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/network-internal.h @@ -21,7 +21,7 @@ int deserialize_in6_addrs(struct in6_addr **addresses, const char *string); struct sd_dhcp_route; struct sd_dhcp_lease; -void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size); +void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route **routes, size_t size); int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, const char *string); /* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */ diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c index 1600f806..d56978ad 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c @@ -147,11 +147,11 @@ static const uint8_t default_req_opts_anonymize[] = { SD_DHCP_OPTION_ROUTER, /* 3 */ SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */ SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */ - SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */ + SD_DHCP_OPTION_ROUTER_DISCOVERY, /* 31 */ SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */ SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */ - SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */ - SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */ + SD_DHCP_OPTION_NETBIOS_NAME_SERVER, /* 44 */ + SD_DHCP_OPTION_NETBIOS_NODE_TYPE, /* 46 */ SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */ SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */ SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */ @@ -249,7 +249,7 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) { assert_return(client, -EINVAL); assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY); - switch(option) { + switch (option) { case SD_DHCP_OPTION_PAD: case SD_DHCP_OPTION_OVERLOAD: @@ -454,7 +454,7 @@ static int dhcp_client_set_iaid_duid_internal( bool iaid_append, bool iaid_set, uint32_t iaid, - uint16_t duid_type, + DUIDType duid_type, const void *duid, size_t duid_len, usec_t llt_time) { @@ -493,37 +493,20 @@ static int dhcp_client_set_iaid_duid_internal( client->client_id.ns.duid.type = htobe16(duid_type); memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len); len = sizeof(client->client_id.ns.duid.type) + duid_len; - } else - switch (duid_type) { - case DUID_TYPE_LLT: - if (client->mac_addr_len == 0) - return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set."); - r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m"); - break; - case DUID_TYPE_EN: - r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m"); - break; - case DUID_TYPE_LL: - if (client->mac_addr_len == 0) - return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set."); - - r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m"); - break; - case DUID_TYPE_UUID: - r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m"); - break; - default: - return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type"); - } + } else { + r = dhcp_identifier_set_duid(duid_type, client->mac_addr, client->mac_addr_len, + client->arp_type, llt_time, client->test_mode, + &client->client_id.ns.duid, &len); + if (r == -EOPNOTSUPP) + return log_dhcp_client_errno(client, r, + "Failed to set %s. MAC address is not set or " + "interface type is not supported.", + duid_type_to_string(duid_type)); + if (r < 0) + return log_dhcp_client_errno(client, r, "Failed to set %s: %m", + duid_type_to_string(duid_type)); + } client->client_id_len = sizeof(client->client_id.type) + len + (iaid_append ? sizeof(client->client_id.ns.iaid) : 0); @@ -617,7 +600,6 @@ int sd_dhcp_client_set_user_class( sd_dhcp_client *client, char * const *user_class) { - char * const *p; char **s = NULL; assert_return(client, -EINVAL); @@ -844,7 +826,7 @@ static int client_message_init( /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers refuse to issue an DHCP lease if 'secs' is set to zero */ - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now); if (r < 0) return r; assert(time_now >= client->start_time); @@ -881,7 +863,7 @@ static int client_message_init( if (r < 0) return r; - r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len); + r = dhcp_identifier_set_duid_en(client->test_mode, &client->client_id.ns.duid, &duid_len); if (r < 0) return r; @@ -1269,7 +1251,7 @@ static int client_timeout_resend( assert(client); assert(client->event); - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now); if (r < 0) goto error; @@ -1317,7 +1299,7 @@ static int client_timeout_resend( } r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, next_timeout, 10 * USEC_PER_MSEC, client_timeout_resend, client, client->event_priority, "dhcp4-resend-timer", true); @@ -1417,12 +1399,12 @@ static int client_initialize_time_events(sd_dhcp_client *client) { assert(client->event); if (client->start_delay > 0) { - assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0); + assert_se(sd_event_now(client->event, CLOCK_BOOTTIME, &usec) >= 0); usec += client->start_delay; } r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, usec, 0, client_timeout_resend, client, client->event_priority, "dhcp4-resend-timer", true); @@ -1463,7 +1445,7 @@ static int client_start_delayed(sd_dhcp_client *client) { client->fd = r; if (IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT)) - client->start_time = now(clock_boottime_or_monotonic()); + client->start_time = now(CLOCK_BOOTTIME); return client_initialize_events(client, client_receive_message_raw); } @@ -1707,7 +1689,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { return 0; } - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now); if (r < 0) return r; assert(client->request_sent <= time_now); @@ -1740,7 +1722,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { /* arm lifetime timeout */ r = event_reset_time(client->event, &client->timeout_expire, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, client->expire_time, 10 * USEC_PER_MSEC, client_timeout_expire, client, client->event_priority, "dhcp4-lifetime", true); @@ -1756,7 +1738,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { /* arm T2 timeout */ r = event_reset_time(client->event, &client->timeout_t2, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, client->t2_time, 10 * USEC_PER_MSEC, client_timeout_t2, client, client->event_priority, "dhcp4-t2-timeout", true); @@ -1772,7 +1754,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { /* arm T1 timeout */ r = event_reset_time(client->event, &client->timeout_t1, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, client->t1_time, 10 * USEC_PER_MSEC, client_timeout_t1, client, client->event_priority, "dhcp4-t1-timer", true); @@ -1807,7 +1789,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i client->attempt = 0; r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, 0, 0, client_timeout_resend, client, client->event_priority, "dhcp4-resend-timer", true); diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c index 7a250534..5f83fa66 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c @@ -217,25 +217,38 @@ int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) { * The returned routes array must be freed by the caller. * Route objects have the same lifetime of the lease and must not be freed. */ -int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) { - sd_dhcp_route **ret; - unsigned i; +static int dhcp_lease_get_routes(sd_dhcp_route *routes, size_t n_routes, sd_dhcp_route ***ret) { + assert(routes || n_routes == 0); - assert_return(lease, -EINVAL); - assert_return(routes, -EINVAL); - - if (lease->static_route_size <= 0) + if (n_routes <= 0) return -ENODATA; - ret = new(sd_dhcp_route *, lease->static_route_size); - if (!ret) - return -ENOMEM; + if (ret) { + sd_dhcp_route **buf; + + buf = new(sd_dhcp_route*, n_routes); + if (!buf) + return -ENOMEM; + + for (size_t i = 0; i < n_routes; i++) + buf[i] = &routes[i]; + + *ret = buf; + } - for (i = 0; i < lease->static_route_size; i++) - ret[i] = &lease->static_route[i]; + return (int) n_routes; +} + +int sd_dhcp_lease_get_static_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret) { + assert_return(lease, -EINVAL); + + return dhcp_lease_get_routes(lease->static_routes, lease->n_static_routes, ret); +} + +int sd_dhcp_lease_get_classless_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret) { + assert_return(lease, -EINVAL); - *routes = ret; - return (int) lease->static_route_size; + return dhcp_lease_get_routes(lease->classless_routes, lease->n_classless_routes, ret); } int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) { @@ -314,7 +327,8 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) { for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++) free(lease->servers[i].addr); - free(lease->static_route); + free(lease->static_routes); + free(lease->classless_routes); free(lease->client_id); free(lease->vendor_specific); strv_free(lease->search_domains); @@ -469,96 +483,88 @@ static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_a return lease_parse_in_addrs(option + 1, len - 1, ret, n_ret); } -static int lease_parse_routes( - const uint8_t *option, size_t len, - struct sd_dhcp_route **routes, size_t *routes_size) { - - struct in_addr addr; +static int lease_parse_static_routes(sd_dhcp_lease *lease, const uint8_t *option, size_t len) { + int r; + assert(lease); assert(option || len <= 0); - assert(routes); - assert(routes_size); - - if (len <= 0) - return 0; if (len % 8 != 0) return -EINVAL; - if (!GREEDY_REALLOC(*routes, *routes_size + (len / 8))) - return -ENOMEM; - while (len >= 8) { - struct sd_dhcp_route *route = *routes + *routes_size; - int r; + struct in_addr dst, gw; + uint8_t prefixlen; - route->option = SD_DHCP_OPTION_STATIC_ROUTE; - r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen); + assert_se(lease_parse_be32(option, 4, &dst.s_addr) >= 0); + option += 4; + + assert_se(lease_parse_be32(option, 4, &gw.s_addr) >= 0); + option += 4; + + len -= 8; + + r = in4_addr_default_prefixlen(&dst, &prefixlen); if (r < 0) { - log_debug("Failed to determine destination prefix length from class based IP, ignoring"); + log_debug("sd-dhcp-lease: cannot determine class of received static route, ignoring."); continue; } - assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0); - route->dst_addr = inet_makeaddr(inet_netof(addr), 0); - option += 4; + (void) in4_addr_mask(&dst, prefixlen); - assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0); - option += 4; + if (!GREEDY_REALLOC(lease->static_routes, lease->n_static_routes + 1)) + return -ENOMEM; - len -= 8; - (*routes_size)++; + lease->static_routes[lease->n_static_routes++] = (struct sd_dhcp_route) { + .dst_addr = dst, + .gw_addr = gw, + .dst_prefixlen = prefixlen, + }; } return 0; } /* parses RFC3442 Classless Static Route Option */ -static int lease_parse_classless_routes( - const uint8_t *option, size_t len, - struct sd_dhcp_route **routes, size_t *routes_size) { - +static int lease_parse_classless_routes(sd_dhcp_lease *lease, const uint8_t *option, size_t len) { + assert(lease); assert(option || len <= 0); - assert(routes); - assert(routes_size); - if (len <= 0) - return 0; - - /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */ + /* option format: (subnet-mask-width significant-subnet-octets gateway-ip) */ while (len > 0) { - uint8_t dst_octets; - struct sd_dhcp_route *route; - - if (!GREEDY_REALLOC(*routes, *routes_size + 1)) - return -ENOMEM; - - route = *routes + *routes_size; - route->option = SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE; + uint8_t prefixlen, dst_octets; + struct in_addr dst = {}, gw; - dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1); - route->dst_prefixlen = *option; + prefixlen = *option; option++; len--; + dst_octets = DIV_ROUND_UP(prefixlen, 8); + /* can't have more than 4 octets in IPv4 */ if (dst_octets > 4 || len < dst_octets) return -EINVAL; - route->dst_addr.s_addr = 0; - memcpy(&route->dst_addr.s_addr, option, dst_octets); + memcpy(&dst, option, dst_octets); option += dst_octets; len -= dst_octets; if (len < 4) return -EINVAL; - assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0); + assert_se(lease_parse_be32(option, 4, &gw.s_addr) >= 0); option += 4; len -= 4; - (*routes_size)++; + if (!GREEDY_REALLOC(lease->classless_routes, lease->n_classless_routes + 1)) + return -ENOMEM; + + lease->classless_routes[lease->n_classless_routes++] = (struct sd_dhcp_route) { + .dst_addr = dst, + .gw_addr = gw, + .dst_prefixlen = prefixlen, + }; } return 0; @@ -625,7 +631,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void assert(lease); - switch(code) { + switch (code) { case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME: r = lease_parse_u32(option, len, &lease->lifetime, 1); @@ -700,12 +706,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; case SD_DHCP_OPTION_STATIC_ROUTE: - r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size); + r = lease_parse_static_routes(lease, option, len); if (r < 0) log_debug_errno(r, "Failed to parse static routes, ignoring: %m"); break; - case SD_DHCP_OPTION_INTERFACE_MTU: + case SD_DHCP_OPTION_MTU_INTERFACE: r = lease_parse_u16(option, len, &lease->mtu, 68); if (r < 0) log_debug_errno(r, "Failed to parse MTU, ignoring: %m"); @@ -725,7 +731,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; - case SD_DHCP_OPTION_DOMAIN_SEARCH_LIST: + case SD_DHCP_OPTION_DOMAIN_SEARCH: r = dhcp_lease_parse_search_domains(option, len, &lease->search_domains); if (r < 0) log_debug_errno(r, "Failed to parse Domain Search List, ignoring: %m"); @@ -746,28 +752,25 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void log_debug_errno(r, "Failed to parse root path, ignoring: %m"); break; - case SD_DHCP_OPTION_RENEWAL_T1_TIME: + case SD_DHCP_OPTION_RENEWAL_TIME: r = lease_parse_u32(option, len, &lease->t1, 1); if (r < 0) log_debug_errno(r, "Failed to parse T1 time, ignoring: %m"); break; - case SD_DHCP_OPTION_REBINDING_T2_TIME: + case SD_DHCP_OPTION_REBINDING_TIME: r = lease_parse_u32(option, len, &lease->t2, 1); if (r < 0) log_debug_errno(r, "Failed to parse T2 time, ignoring: %m"); break; case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE: - r = lease_parse_classless_routes( - option, len, - &lease->static_route, - &lease->static_route_size); + r = lease_parse_classless_routes(lease, option, len); if (r < 0) log_debug_errno(r, "Failed to parse classless routes, ignoring: %m"); break; - case SD_DHCP_OPTION_NEW_TZDB_TIMEZONE: { + case SD_DHCP_OPTION_TZDB_TIMEZONE: { _cleanup_free_ char *tz = NULL; r = lease_parse_string(option, len, &tz); @@ -909,19 +912,21 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d pos = next_chunk; } - *domains = TAKE_PTR(names); + strv_free_and_replace(*domains, names); return cnt; } int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) { - struct sd_dhcp_raw_option *cur, *option; + struct sd_dhcp_raw_option *option, *before = NULL; assert(lease); LIST_FOREACH(options, cur, lease->private_options) { - if (tag < cur->tag) + if (tag < cur->tag) { + before = cur; break; + } if (tag == cur->tag) { log_debug("Ignoring duplicate option, tagged %i.", tag); return 0; @@ -940,7 +945,7 @@ int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const vo return -ENOMEM; } - LIST_INSERT_BEFORE(options, lease->private_options, cur, option); + LIST_INSERT_BEFORE(options, lease->private_options, before, option); return 0; } @@ -960,7 +965,6 @@ int dhcp_lease_new(sd_dhcp_lease **ret) { int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; - struct sd_dhcp_raw_option *option; struct in_addr address; const struct in_addr *addresses; const void *client_id, *data; @@ -969,7 +973,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { const char *string; uint16_t mtu; _cleanup_free_ sd_dhcp_route **routes = NULL; - char **search_domains = NULL; + char **search_domains; uint32_t t1, t2, lifetime; int r; @@ -1068,9 +1072,14 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { if (r >= 0) fprintf(f, "ROOT_PATH=%s\n", string); - r = sd_dhcp_lease_get_routes(lease, &routes); + r = sd_dhcp_lease_get_static_routes(lease, &routes); + if (r > 0) + serialize_dhcp_routes(f, "STATIC_ROUTES", routes, r); + + routes = mfree(routes); + r = sd_dhcp_lease_get_classless_routes(lease, &routes); if (r > 0) - serialize_dhcp_routes(f, "ROUTES", routes, r); + serialize_dhcp_routes(f, "CLASSLESS_ROUTES", routes, r); r = sd_dhcp_lease_get_timezone(lease, &string); if (r >= 0) @@ -1118,6 +1127,18 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { return 0; } +static char **private_options_free(char **options) { + if (!options) + return NULL; + + for (unsigned i = 0; i < SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1; i++) + free(options[i]); + + return mfree(options); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(char**, private_options_free); + int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; _cleanup_free_ char @@ -1134,14 +1155,15 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { *smtp = NULL, *lpr = NULL, *mtu = NULL, - *routes = NULL, + *static_routes = NULL, + *classless_routes = NULL, *domains = NULL, *client_id_hex = NULL, *vendor_specific_hex = NULL, *lifetime = NULL, *t1 = NULL, - *t2 = NULL, - *options[SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1] = {}; + *t2 = NULL; + _cleanup_(private_options_freep) char **options = NULL; int r, i; @@ -1152,6 +1174,10 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { if (r < 0) return r; + options = new0(char*, SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1); + if (!options) + return -ENOMEM; + r = parse_env_file(NULL, lease_file, "ADDRESS", &address, "ROUTER", &router, @@ -1170,7 +1196,8 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { "HOSTNAME", &lease->hostname, "DOMAIN_SEARCH_LIST", &domains, "ROOT_PATH", &lease->root_path, - "ROUTES", &routes, + "STATIC_ROUTES", &static_routes, + "CLASSLESS_ROUTES", &classless_routes, "CLIENTID", &client_id_hex, "TIMEZONE", &lease->timezone, "VENDOR_SPECIFIC", &vendor_specific_hex, @@ -1317,13 +1344,22 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { lease->search_domains = TAKE_PTR(a); } - if (routes) { + if (static_routes) { r = deserialize_dhcp_routes( - &lease->static_route, - &lease->static_route_size, - routes); + &lease->static_routes, + &lease->n_static_routes, + static_routes); if (r < 0) - log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes); + log_debug_errno(r, "Failed to parse DHCP static routes %s, ignoring: %m", static_routes); + } + + if (classless_routes) { + r = deserialize_dhcp_routes( + &lease->classless_routes, + &lease->n_classless_routes, + classless_routes); + if (r < 0) + log_debug_errno(r, "Failed to parse DHCP classless routes %s, ignoring: %m", classless_routes); } if (lifetime) { @@ -1470,9 +1506,3 @@ int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) { *gateway = route->gw_addr; return 0; } - -int sd_dhcp_route_get_option(sd_dhcp_route *route) { - assert_return(route, -EINVAL); - - return route->option; -} diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c index 0c2206ff..4394ef49 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c @@ -20,7 +20,6 @@ #include "dhcp-identifier.h" #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" -#include "dhcp6-protocol.h" #include "dns-domain.h" #include "event-util.h" #include "fd-util.h" @@ -28,74 +27,11 @@ #include "hostname-util.h" #include "in-addr-util.h" #include "io-util.h" -#include "network-common.h" #include "random-util.h" #include "socket-util.h" -#include "string-table.h" #include "strv.h" -#include "util.h" #include "web-util.h" -#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN - -#define IRT_DEFAULT (1 * USEC_PER_DAY) -#define IRT_MINIMUM (600 * USEC_PER_SEC) - -/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */ -typedef enum DHCP6RequestIA { - DHCP6_REQUEST_IA_NA = 1 << 0, - DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */ - DHCP6_REQUEST_IA_PD = 1 << 2, -} DHCP6RequestIA; - -struct sd_dhcp6_client { - unsigned n_ref; - - DHCP6State state; - sd_event *event; - int event_priority; - int ifindex; - char *ifname; - DHCP6Address hint_pd_prefix; - struct in6_addr local_address; - uint8_t mac_addr[MAX_MAC_ADDR_LEN]; - size_t mac_addr_len; - uint16_t arp_type; - DHCP6IA ia_na; - DHCP6IA ia_pd; - sd_event_source *timeout_t1; - sd_event_source *timeout_t2; - DHCP6RequestIA request_ia; - be32_t transaction_id; - usec_t transaction_start; - struct sd_dhcp6_lease *lease; - int fd; - bool information_request; - bool iaid_set; - be16_t *req_opts; - size_t req_opts_len; - char *fqdn; - char *mudurl; - char **user_class; - char **vendor_class; - sd_event_source *receive_message; - usec_t retransmit_time; - uint8_t retransmit_count; - sd_event_source *timeout_resend; - sd_event_source *timeout_resend_expire; - sd_dhcp6_client_callback_t callback; - void *userdata; - struct duid duid; - size_t duid_len; - usec_t information_request_time_usec; - usec_t information_refresh_time_usec; - OrderedHashmap *extra_options; - OrderedHashmap *vendor_options; - - /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */ - bool test_mode; -}; - static const uint16_t default_req_opts[] = { SD_DHCP6_OPTION_DNS_SERVERS, SD_DHCP6_OPTION_DOMAIN_LIST, @@ -103,78 +39,10 @@ static const uint16_t default_req_opts[] = { SD_DHCP6_OPTION_SNTP_SERVERS, }; -const char * dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = { - [DHCP6_MESSAGE_SOLICIT] = "Solicit", - [DHCP6_MESSAGE_ADVERTISE] = "Advertise", - [DHCP6_MESSAGE_REQUEST] = "Request", - [DHCP6_MESSAGE_CONFIRM] = "Confirm", - [DHCP6_MESSAGE_RENEW] = "Renew", - [DHCP6_MESSAGE_REBIND] = "Rebind", - [DHCP6_MESSAGE_REPLY] = "Reply", - [DHCP6_MESSAGE_RELEASE] = "Release", - [DHCP6_MESSAGE_DECLINE] = "Decline", - [DHCP6_MESSAGE_RECONFIGURE] = "Reconfigure", - [DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request", - [DHCP6_MESSAGE_RELAY_FORWARD] = "Relay Forward", - [DHCP6_MESSAGE_RELAY_REPLY] = "Relay Reply", - [DHCP6_MESSAGE_LEASE_QUERY] = "Lease Query", - [DHCP6_MESSAGE_LEASE_QUERY_REPLY] = "Lease Query Reply", - [DHCP6_MESSAGE_LEASE_QUERY_DONE] = "Lease Query Done", - [DHCP6_MESSAGE_LEASE_QUERY_DATA] = "Lease Query Data", - [DHCP6_MESSAGE_RECONFIGURE_REQUEST] = "Reconfigure Request", - [DHCP6_MESSAGE_RECONFIGURE_REPLY] = "Reconfigure Reply", - [DHCP6_MESSAGE_DHCPV4_QUERY] = "DHCPv4 Query", - [DHCP6_MESSAGE_DHCPV4_RESPONSE] = "DHCPv4 Response", - [DHCP6_MESSAGE_ACTIVE_LEASE_QUERY] = "Active Lease Query", - [DHCP6_MESSAGE_START_TLS] = "Start TLS", - [DHCP6_MESSAGE_BINDING_UPDATE] = "Binding Update", - [DHCP6_MESSAGE_BINDING_REPLY] = "Binding Reply", - [DHCP6_MESSAGE_POOL_REQUEST] = "Pool Request", - [DHCP6_MESSAGE_POOL_RESPONSE] = "Pool Response", - [DHCP6_MESSAGE_UPDATE_REQUEST] = "Update Request", - [DHCP6_MESSAGE_UPDATE_REQUEST_ALL] = "Update Request All", - [DHCP6_MESSAGE_UPDATE_DONE] = "Update Done", - [DHCP6_MESSAGE_CONNECT] = "Connect", - [DHCP6_MESSAGE_CONNECT_REPLY] = "Connect Reply", - [DHCP6_MESSAGE_DISCONNECT] = "Disconnect", - [DHCP6_MESSAGE_STATE] = "State", - [DHCP6_MESSAGE_CONTACT] = "Contact", -}; - -DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int); - -const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { - [DHCP6_STATUS_SUCCESS] = "Success", - [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure", - [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available", - [DHCP6_STATUS_NO_BINDING] = "Binding unavailable", - [DHCP6_STATUS_NOT_ON_LINK] = "Not on link", - [DHCP6_STATUS_USE_MULTICAST] = "Use multicast", - [DHCP6_STATUS_NO_PREFIX_AVAIL] = "No prefix available", - [DHCP6_STATUS_UNKNOWN_QUERY_TYPE] = "Unknown query type", - [DHCP6_STATUS_MALFORMED_QUERY] = "Malformed query", - [DHCP6_STATUS_NOT_CONFIGURED] = "Not configured", - [DHCP6_STATUS_NOT_ALLOWED] = "Not allowed", - [DHCP6_STATUS_QUERY_TERMINATED] = "Query terminated", - [DHCP6_STATUS_DATA_MISSING] = "Data missing", - [DHCP6_STATUS_CATCHUP_COMPLETE] = "Catch up complete", - [DHCP6_STATUS_NOT_SUPPORTED] = "Not supported", - [DHCP6_STATUS_TLS_CONNECTION_REFUSED] = "TLS connection refused", - [DHCP6_STATUS_ADDRESS_IN_USE] = "Address in use", - [DHCP6_STATUS_CONFIGURATION_CONFLICT] = "Configuration conflict", - [DHCP6_STATUS_MISSING_BINDING_INFORMATION] = "Missing binding information", - [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information", - [DHCP6_STATUS_SERVER_SHUTTING_DOWN] = "Server shutting down", - [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED] = "DNS update not supported", - [DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew", -}; - -DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int); - #define DHCP6_CLIENT_DONT_DESTROY(client) \ _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client) -static int client_start(sd_dhcp6_client *client, DHCP6State state); +static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state); int sd_dhcp6_client_set_callback( sd_dhcp6_client *client, @@ -191,8 +59,8 @@ int sd_dhcp6_client_set_callback( int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(ifindex > 0, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); client->ifindex = ifindex; return 0; @@ -228,9 +96,9 @@ int sd_dhcp6_client_set_local_address( const struct in6_addr *local_address) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(local_address, -EINVAL); assert_return(in6_addr_is_link_local(local_address) > 0, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); client->local_address = *local_address; @@ -239,13 +107,16 @@ int sd_dhcp6_client_set_local_address( int sd_dhcp6_client_set_mac( sd_dhcp6_client *client, - const uint8_t *addr, size_t addr_len, + const uint8_t *addr, + size_t addr_len, uint16_t arp_type) { assert_return(client, -EINVAL); assert_return(addr, -EINVAL); - assert_return(addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(addr_len <= sizeof(client->hw_addr.bytes), -EINVAL); + + /* Unlike the other setters, it is OK to set a new MAC address while the client is running, + * as the MAC address is used only when setting DUID or IAID. */ if (arp_type == ARPHRD_ETHER) assert_return(addr_len == ETH_ALEN, -EINVAL); @@ -253,12 +124,12 @@ int sd_dhcp6_client_set_mac( assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); else { client->arp_type = ARPHRD_NONE; - client->mac_addr_len = 0; + client->hw_addr.length = 0; return 0; } - memcpy(&client->mac_addr, addr, addr_len); - client->mac_addr_len = addr_len; + memcpy(client->hw_addr.bytes, addr, addr_len); + client->hw_addr.length = addr_len; client->arp_type = arp_type; return 0; @@ -267,25 +138,47 @@ int sd_dhcp6_client_set_mac( int sd_dhcp6_client_set_prefix_delegation_hint( sd_dhcp6_client *client, uint8_t prefixlen, - const struct in6_addr *pd_address) { + const struct in6_addr *pd_prefix) { + + _cleanup_free_ DHCP6Address *prefix = NULL; assert_return(client, -EINVAL); - assert_return(pd_address, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + + if (!pd_prefix) { + /* clear previous assignments. */ + dhcp6_ia_clear_addresses(&client->ia_pd); + return 0; + } - client->hint_pd_prefix.iapdprefix.address = *pd_address; - client->hint_pd_prefix.iapdprefix.prefixlen = prefixlen; + assert_return(prefixlen > 0 && prefixlen <= 128, -EINVAL); - return 0; + prefix = new(DHCP6Address, 1); + if (!prefix) + return -ENOMEM; + + *prefix = (DHCP6Address) { + .iapdprefix.address = *pd_prefix, + .iapdprefix.prefixlen = prefixlen, + }; + + LIST_PREPEND(addresses, client->ia_pd.addresses, TAKE_PTR(prefix)); + return 1; } int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) { int r; assert_return(client, -EINVAL); - assert_return(v, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + + if (!v) { + /* Clear the previous assignments. */ + ordered_set_clear(client->vendor_options); + return 0; + } - r = ordered_hashmap_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v, v); + r = ordered_set_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v); if (r < 0) return r; @@ -295,10 +188,12 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option * } static int client_ensure_duid(sd_dhcp6_client *client) { + assert(client); + if (client->duid_len != 0) return 0; - return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); + return dhcp_identifier_set_duid_en(client->test_mode, &client->duid, &client->duid_len); } /** @@ -308,15 +203,15 @@ static int client_ensure_duid(sd_dhcp6_client *client) { */ static int dhcp6_client_set_duid_internal( sd_dhcp6_client *client, - uint16_t duid_type, + DUIDType duid_type, const void *duid, size_t duid_len, usec_t llt_time) { int r; assert_return(client, -EINVAL); - assert_return(duid_len == 0 || duid != NULL, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + assert_return(duid_len == 0 || duid, -EINVAL); if (duid) { r = dhcp_validate_duid_len(duid_type, duid_len, true); @@ -331,41 +226,23 @@ static int dhcp6_client_set_duid_internal( client->duid.type = htobe16(duid_type); memcpy(&client->duid.raw.data, duid, duid_len); client->duid_len = sizeof(client->duid.type) + duid_len; - } else -#if 0 /* NM_IGNORED */ - switch (duid_type) { - case DUID_TYPE_LLT: - if (client->mac_addr_len == 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set."); - r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m"); - break; - case DUID_TYPE_EN: - r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m"); - break; - case DUID_TYPE_LL: - if (client->mac_addr_len == 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set."); - - r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m"); - break; - case DUID_TYPE_UUID: - r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m"); - break; - default: - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type"); - } + } else { +#if 0 /* NM_IGNORED */ + r = dhcp_identifier_set_duid(duid_type, client->hw_addr.bytes, client->hw_addr.length, + client->arp_type, llt_time, client->test_mode, &client->duid, &client->duid_len); + if (r == -EOPNOTSUPP) + return log_dhcp6_client_errno(client, r, + "Failed to set %s. MAC address is not set or " + "interface type is not supported.", + duid_type_to_string(duid_type)); + if (r < 0) + return log_dhcp6_client_errno(client, r, "Failed to set %s: %m", + duid_type_to_string(duid_type)); #else /* NM_IGNORED */ g_return_val_if_reached (-EINVAL); #endif /* NM_IGNORED */ + } return 0; } @@ -384,14 +261,6 @@ int sd_dhcp6_client_set_duid_llt( return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time); } -static const char* const dhcp6_duid_type_table[_DUID_TYPE_MAX] = { - [DUID_TYPE_LLT] = "DUID-LLT", - [DUID_TYPE_EN] = "DUID-EN/Vendor", - [DUID_TYPE_LL] = "DUID-LL", - [DUID_TYPE_UUID] = "UUID", -}; -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_duid_type, DUIDType); - int sd_dhcp6_client_duid_as_string( sd_dhcp6_client *client, char **duid) { @@ -403,7 +272,7 @@ int sd_dhcp6_client_duid_as_string( assert_return(client->duid_len > 0, -ENODATA); assert_return(duid, -EINVAL); - v = dhcp6_duid_type_to_string(be16toh(client->duid.type)); + v = duid_type_to_string(be16toh(client->duid.type)); if (v) { s = strdup(v); if (!s) @@ -429,19 +298,36 @@ int sd_dhcp6_client_duid_as_string( int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); - client->ia_na.ia_na.id = htobe32(iaid); - client->ia_pd.ia_pd.id = htobe32(iaid); + client->ia_na.header.id = htobe32(iaid); + client->ia_pd.header.id = htobe32(iaid); client->iaid_set = true; return 0; } -void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode) { +static int client_ensure_iaid(sd_dhcp6_client *client) { + int r; + uint32_t iaid; + assert(client); - client->test_mode = test_mode; + if (client->iaid_set) + return 0; + + r = dhcp_identifier_set_iaid(client->ifindex, client->hw_addr.bytes, client->hw_addr.length, + /* legacy_unstable_byteorder = */ true, + /* use_mac = */ client->test_mode, + &iaid); + if (r < 0) + return r; + + client->ia_na.header.id = iaid; + client->ia_pd.header.id = iaid; + client->iaid_set = true; + + return 0; } int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) { @@ -451,16 +337,23 @@ int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) { if (!client->iaid_set) return -ENODATA; - *iaid = be32toh(client->ia_na.ia_na.id); + *iaid = be32toh(client->ia_na.header.id); return 0; } +void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode) { + assert(client); + + client->test_mode = test_mode; +} + int sd_dhcp6_client_set_fqdn( sd_dhcp6_client *client, const char *fqdn) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); /* Make sure FQDN qualifies as DNS and as Linux hostname */ if (fqdn && @@ -472,7 +365,7 @@ int sd_dhcp6_client_set_fqdn( int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) { assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); client->information_request = enabled; @@ -492,7 +385,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) size_t t; assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); if (!dhcp6_option_can_request(option)) return -EINVAL; @@ -511,7 +404,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mudurl) { assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(mudurl, -EINVAL); assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL); assert_return(http_url_is_valid(mudurl), -EINVAL); @@ -520,11 +413,10 @@ int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mud } int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const *user_class) { - char * const *p; char **s; assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(!strv_isempty(user_class), -EINVAL); STRV_FOREACH(p, user_class) { @@ -542,11 +434,10 @@ int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const } int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char * const *vendor_class) { - char * const *p; char **s; assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(!strv_isempty(vendor_class), -EINVAL); STRV_FOREACH(p, vendor_class) { @@ -574,6 +465,7 @@ int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegati int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_PD, delegation); @@ -591,16 +483,20 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) { int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_NA, request); return 0; } -int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) { - assert_return(client, -EINVAL); +int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) { + assert(client); + assert(client->test_mode); - client->transaction_id = transaction_id; + /* This is for tests or fuzzers. */ + + client->transaction_id = transaction_id & htobe32(0x00ffffff); return 0; } @@ -631,6 +527,18 @@ int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) { return 0; } +static void client_set_state(sd_dhcp6_client *client, DHCP6State state) { + assert(client); + + if (client->state == state) + return; + + log_dhcp6_client(client, "State changed: %s -> %s", + dhcp6_state_to_string(client->state), dhcp6_state_to_string(state)); + + client->state = state; +} + static void client_notify(sd_dhcp6_client *client, int event) { assert(client); @@ -638,52 +546,122 @@ static void client_notify(sd_dhcp6_client *client, int event) { client->callback(client, event, client->userdata); } -static int client_reset(sd_dhcp6_client *client) { - assert(client); +static void client_stop(sd_dhcp6_client *client, int error) { + DHCP6_CLIENT_DONT_DESTROY(client); - client->lease = sd_dhcp6_lease_unref(client->lease); + assert(client); - client->receive_message = - sd_event_source_unref(client->receive_message); + client_notify(client, error); - client->transaction_id = 0; - client->transaction_start = 0; + client->lease = sd_dhcp6_lease_unref(client->lease); - client->retransmit_time = 0; - client->retransmit_count = 0; + /* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode, + * even though the lease is freed below. */ + client->information_request_time_usec = 0; + client->information_refresh_time_usec = 0; + (void) event_source_disable(client->receive_message); (void) event_source_disable(client->timeout_resend); - (void) event_source_disable(client->timeout_resend_expire); + (void) event_source_disable(client->timeout_expire); (void) event_source_disable(client->timeout_t1); (void) event_source_disable(client->timeout_t2); - client->state = DHCP6_STATE_STOPPED; - - return 0; + client_set_state(client, DHCP6_STATE_STOPPED); } -static void client_stop(sd_dhcp6_client *client, int error) { - DHCP6_CLIENT_DONT_DESTROY(client); +static int client_append_common_options_in_managed_mode( + sd_dhcp6_client *client, + uint8_t **opt, + size_t *optlen, + const DHCP6IA *ia_na, + const DHCP6IA *ia_pd) { + + int r; assert(client); + assert(IN_SET(client->state, + DHCP6_STATE_SOLICITATION, + DHCP6_STATE_REQUEST, + DHCP6_STATE_RENEW, + DHCP6_STATE_REBIND)); + assert(opt); + assert(optlen); + + if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && ia_na) { + r = dhcp6_option_append_ia(opt, optlen, ia_na); + if (r < 0) + return r; + } - client_notify(client, error); + if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && ia_pd) { + r = dhcp6_option_append_ia(opt, optlen, ia_pd); + if (r < 0) + return r; + } - client_reset(client); + if (client->fqdn) { + r = dhcp6_option_append_fqdn(opt, optlen, client->fqdn); + if (r < 0) + return r; + } + + if (client->user_class) { + r = dhcp6_option_append_user_class(opt, optlen, client->user_class); + if (r < 0) + return r; + } + + if (client->vendor_class) { + r = dhcp6_option_append_vendor_class(opt, optlen, client->vendor_class); + if (r < 0) + return r; + } + + if (!ordered_set_isempty(client->vendor_options)) { + r = dhcp6_option_append_vendor_option(opt, optlen, client->vendor_options); + if (r < 0) + return r; + } + + return 0; } -static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { +static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) { + assert(client); + + switch (client->state) { + case DHCP6_STATE_INFORMATION_REQUEST: + return DHCP6_MESSAGE_INFORMATION_REQUEST; + case DHCP6_STATE_SOLICITATION: + return DHCP6_MESSAGE_SOLICIT; + case DHCP6_STATE_REQUEST: + return DHCP6_MESSAGE_REQUEST; + case DHCP6_STATE_RENEW: + return DHCP6_MESSAGE_RENEW; + case DHCP6_STATE_REBIND: + return DHCP6_MESSAGE_REBIND; + default: + assert_not_reached(); + } +} + +int dhcp6_client_send_message(sd_dhcp6_client *client) { _cleanup_free_ DHCP6Message *message = NULL; struct in6_addr all_servers = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; struct sd_dhcp6_option *j; size_t len, optlen = 512; uint8_t *opt; - int r; - usec_t elapsed_usec; + usec_t elapsed_usec, time_now; be16_t elapsed_time; + int r; assert(client); + assert(client->event); + + r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now); + if (r < 0) + return r; len = sizeof(DHCP6Message) + optlen; @@ -694,194 +672,56 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { opt = (uint8_t *)(message + 1); message->transaction_id = client->transaction_id; + message->type = client_message_type_from_state(client); - switch(client->state) { + switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: - message->type = DHCP6_MESSAGE_INFORMATION_REQUEST; - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - break; case DHCP6_STATE_SOLICITATION: - message->type = DHCP6_MESSAGE_SOLICIT; - - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); if (r < 0) return r; - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) { - r = dhcp6_option_append_ia(&opt, &optlen, - &client->ia_na); - if (r < 0) - return r; - } - - if (client->fqdn) { - r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); - if (r < 0) - return r; - } - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - - if (client->user_class) { - r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); - if (r < 0) - return r; - } - - if (client->vendor_class) { - r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); - if (r < 0) - return r; - } - - if (!ordered_hashmap_isempty(client->vendor_options)) { - r = dhcp6_option_append_vendor_option(&opt, &optlen, - client->vendor_options); - if (r < 0) - return r; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD)) { - r = dhcp6_option_append_pd(&opt, &optlen, &client->ia_pd, &client->hint_pd_prefix); - if (r < 0) - return r; - } - + r = client_append_common_options_in_managed_mode(client, &opt, &optlen, + &client->ia_na, &client->ia_pd); + if (r < 0) + return r; break; case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: - if (client->state == DHCP6_STATE_REQUEST) - message->type = DHCP6_MESSAGE_REQUEST; - else - message->type = DHCP6_MESSAGE_RENEW; - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID, client->lease->serverid_len, client->lease->serverid); if (r < 0) return r; - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) { - r = dhcp6_option_append_ia(&opt, &optlen, - &client->lease->ia); - if (r < 0) - return r; - } - - if (client->fqdn) { - r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); - if (r < 0) - return r; - } - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - - if (client->user_class) { - r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); - if (r < 0) - return r; - } - - if (client->vendor_class) { - r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); - if (r < 0) - return r; - } - - if (!ordered_hashmap_isempty(client->vendor_options)) { - r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options); - if (r < 0) - return r; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { - r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL); - if (r < 0) - return r; - } - - break; - + _fallthrough_; case DHCP6_STATE_REBIND: - message->type = DHCP6_MESSAGE_REBIND; - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) { - r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); - if (r < 0) - return r; - } - - if (client->fqdn) { - r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); - if (r < 0) - return r; - } - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - if (client->user_class) { - r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); - if (r < 0) - return r; - } - - if (client->vendor_class) { - r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); - if (r < 0) - return r; - } - - if (!ordered_hashmap_isempty(client->vendor_options)) { - r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options); - if (r < 0) - return r; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { - r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL); - if (r < 0) - return r; - } + assert(client->lease); + r = client_append_common_options_in_managed_mode(client, &opt, &optlen, + client->lease->ia_na, client->lease->ia_pd); + if (r < 0) + return r; break; case DHCP6_STATE_STOPPED: case DHCP6_STATE_BOUND: - return -EINVAL; default: assert_not_reached(); } + if (client->mudurl) { + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_MUD_URL_V6, + strlen(client->mudurl), client->mudurl); + if (r < 0) + return r; + } + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO, client->req_opts_len * sizeof(be16_t), client->req_opts); @@ -894,6 +734,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; + ORDERED_HASHMAP_FOREACH(j, client->extra_options) { + r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data); + if (r < 0) + return r; + } + /* RFC 8415 Section 21.9. * A client MUST include an Elapsed Time option in messages to indicate how long the client has * been trying to complete a DHCP message exchange. */ @@ -903,12 +749,6 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; - ORDERED_HASHMAP_FOREACH(j, client->extra_options) { - r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data); - if (r < 0) - return r; - } - r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message, len - optlen); if (r < 0) @@ -920,100 +760,43 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return 0; } -static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - - assert(s); - assert(client); - assert(client->lease); - - (void) event_source_disable(client->timeout_t2); - - log_dhcp6_client(client, "Timeout T2"); - - client_start(client, DHCP6_STATE_REBIND); - - return 0; -} - -static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - - assert(s); - assert(client); - assert(client->lease); - - (void) event_source_disable(client->timeout_t1); - - log_dhcp6_client(client, "Timeout T1"); - - client_start(client, DHCP6_STATE_RENEW); - - return 0; -} - -static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - DHCP6_CLIENT_DONT_DESTROY(client); - DHCP6State state; - - assert(s); - assert(client); - assert(client->event); - - state = client->state; - - client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE); - - /* RFC 3315, section 18.1.4., says that "...the client may choose to - use a Solicit message to locate a new DHCP server..." */ - if (state == DHCP6_STATE_REBIND) - client_start(client, DHCP6_STATE_SOLICITATION); - - return 0; -} - static usec_t client_timeout_compute_random(usec_t val) { - return val - (random_u32() % USEC_PER_SEC) * val / 10 / USEC_PER_SEC; + return usec_sub_unsigned(val, random_u64_range(val / 10)); } static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) { - int r = 0; - sd_dhcp6_client *client = userdata; - usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0; - usec_t max_retransmit_duration = 0; - uint8_t max_retransmit_count = 0; + sd_dhcp6_client *client = ASSERT_PTR(userdata); + usec_t init_retransmit_time, max_retransmit_time; + int r; - assert(s); - assert(client); assert(client->event); - (void) event_source_disable(client->timeout_resend); - switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: init_retransmit_time = DHCP6_INF_TIMEOUT; max_retransmit_time = DHCP6_INF_MAX_RT; - break; case DHCP6_STATE_SOLICITATION: if (client->retransmit_count > 0 && client->lease) { - client_start(client, DHCP6_STATE_REQUEST); + (void) client_start_transaction(client, DHCP6_STATE_REQUEST); return 0; } init_retransmit_time = DHCP6_SOL_TIMEOUT; max_retransmit_time = DHCP6_SOL_MAX_RT; - break; case DHCP6_STATE_REQUEST: + + if (client->retransmit_count >= DHCP6_REQ_MAX_RC) { + client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX); + return 0; + } + init_retransmit_time = DHCP6_REQ_TIMEOUT; max_retransmit_time = DHCP6_REQ_MAX_RT; - max_retransmit_count = DHCP6_REQ_MAX_RC; - break; case DHCP6_STATE_RENEW: @@ -1023,422 +806,365 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda /* RFC 3315, section 18.1.3. says max retransmit duration will be the remaining time until T2. Instead of setting MRD, wait for T2 to trigger with the same end result */ - break; case DHCP6_STATE_REBIND: init_retransmit_time = DHCP6_REB_TIMEOUT; max_retransmit_time = DHCP6_REB_MAX_RT; - if (event_source_is_enabled(client->timeout_resend_expire) <= 0) { - uint32_t expire = 0; - - r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, &expire); - if (r < 0) { - client_stop(client, r); - return 0; - } - max_retransmit_duration = expire * USEC_PER_SEC; - } - + /* Also, instead of setting MRD, the expire timer is already set in client_enter_bound_state(). */ break; case DHCP6_STATE_STOPPED: case DHCP6_STATE_BOUND: - return 0; default: assert_not_reached(); } - if (max_retransmit_count > 0 && - client->retransmit_count >= max_retransmit_count) { - client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX); - return 0; - } - - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - goto error; - - r = client_send_message(client, time_now); + r = dhcp6_client_send_message(client); if (r >= 0) client->retransmit_count++; if (client->retransmit_time == 0) { - client->retransmit_time = - client_timeout_compute_random(init_retransmit_time); + client->retransmit_time = client_timeout_compute_random(init_retransmit_time); if (client->state == DHCP6_STATE_SOLICITATION) client->retransmit_time += init_retransmit_time / 10; - } else { - assert(max_retransmit_time > 0); - if (client->retransmit_time > max_retransmit_time / 2) - client->retransmit_time = client_timeout_compute_random(max_retransmit_time); - else - client->retransmit_time += client_timeout_compute_random(client->retransmit_time); - } + } else if (client->retransmit_time > max_retransmit_time / 2) + client->retransmit_time = client_timeout_compute_random(max_retransmit_time); + else + client->retransmit_time += client_timeout_compute_random(client->retransmit_time); log_dhcp6_client(client, "Next retransmission in %s", FORMAT_TIMESPAN(client->retransmit_time, USEC_PER_SEC)); - r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), - time_now + client->retransmit_time, 10 * USEC_PER_MSEC, - client_timeout_resend, client, - client->event_priority, "dhcp6-resend-timer", true); - if (r < 0) - goto error; - - if (max_retransmit_duration > 0 && event_source_is_enabled(client->timeout_resend_expire) <= 0) { - - log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs", - max_retransmit_duration / USEC_PER_SEC); - - r = event_reset_time(client->event, &client->timeout_resend_expire, - clock_boottime_or_monotonic(), - time_now + max_retransmit_duration, USEC_PER_SEC, - client_timeout_resend_expire, client, - client->event_priority, "dhcp6-resend-expire-timer", true); - if (r < 0) - goto error; - } - -error: + r = event_reset_time_relative(client->event, &client->timeout_resend, + CLOCK_BOOTTIME, + client->retransmit_time, 10 * USEC_PER_MSEC, + client_timeout_resend, client, + client->event_priority, "dhcp6-resend-timer", true); if (r < 0) client_stop(client, r); return 0; } -static int client_ensure_iaid(sd_dhcp6_client *client) { +static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state) { int r; - uint32_t iaid; assert(client); + assert(client->event); - if (client->iaid_set) - return 0; - - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, - /* legacy_unstable_byteorder = */ true, - /* use_mac = */ client->test_mode, - &iaid); - if (r < 0) - return r; - - client->ia_na.ia_na.id = iaid; - client->ia_pd.ia_pd.id = iaid; - client->iaid_set = true; - - return 0; -} - -int client_parse_message( - sd_dhcp6_client *client, - DHCP6Message *message, - size_t len, - sd_dhcp6_lease *lease) { - - uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX; - usec_t irt = IRT_DEFAULT; - int r; - - assert(client); - assert(message); - assert(len >= sizeof(DHCP6Message)); - assert(lease); + switch (state) { + case DHCP6_STATE_INFORMATION_REQUEST: + case DHCP6_STATE_SOLICITATION: + assert(client->state == DHCP6_STATE_STOPPED); + break; + case DHCP6_STATE_REQUEST: + assert(client->state == DHCP6_STATE_SOLICITATION); + break; + case DHCP6_STATE_RENEW: + assert(client->state == DHCP6_STATE_BOUND); + break; + case DHCP6_STATE_REBIND: + assert(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_RENEW)); + break; + case DHCP6_STATE_STOPPED: + case DHCP6_STATE_BOUND: + default: + assert_not_reached(); + } - len -= sizeof(DHCP6Message); - for (size_t offset = 0; offset < len;) { - uint16_t optcode; - size_t optlen; - const uint8_t *optval; + client_set_state(client, state); - r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval); - if (r < 0) - return r; + client->retransmit_time = 0; + client->retransmit_count = 0; + client->transaction_id = random_u32() & htobe32(0x00ffffff); - switch (optcode) { - case SD_DHCP6_OPTION_CLIENTID: - if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs", - dhcp6_message_type_to_string(message->type)); + r = sd_event_now(client->event, CLOCK_BOOTTIME, &client->transaction_start); + if (r < 0) + goto error; - r = dhcp6_lease_set_clientid(lease, optval, optlen); - if (r < 0) - return r; + r = event_reset_time(client->event, &client->timeout_resend, + CLOCK_BOOTTIME, + 0, 0, + client_timeout_resend, client, + client->event_priority, "dhcp6-resend-timeout", true); + if (r < 0) + goto error; - break; + r = sd_event_source_set_enabled(client->receive_message, SD_EVENT_ON); + if (r < 0) + goto error; - case SD_DHCP6_OPTION_SERVERID: - if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs", - dhcp6_message_type_to_string(message->type)); + return 0; - r = dhcp6_lease_set_serverid(lease, optval, optlen); - if (r < 0) - return r; +error: + client_stop(client, r); + return r; +} - break; +static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp6_client *client = ASSERT_PTR(userdata); + DHCP6_CLIENT_DONT_DESTROY(client); + DHCP6State state; - case SD_DHCP6_OPTION_PREFERENCE: - if (optlen != 1) - return -EINVAL; + (void) event_source_disable(client->timeout_expire); + (void) event_source_disable(client->timeout_t2); + (void) event_source_disable(client->timeout_t1); - r = dhcp6_lease_set_preference(lease, optval[0]); - if (r < 0) - return r; + state = client->state; - break; + client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE); - case SD_DHCP6_OPTION_STATUS_CODE: { - _cleanup_free_ char *msg = NULL; + /* RFC 3315, section 18.1.4., says that "...the client may choose to + use a Solicit message to locate a new DHCP server..." */ + if (state == DHCP6_STATE_REBIND) + (void) client_start_transaction(client, DHCP6_STATE_SOLICITATION); - r = dhcp6_option_parse_status(optval, optlen, &msg); - if (r < 0) - return r; - - if (r > 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Received %s message with non-zero status: %s%s%s", - dhcp6_message_type_to_string(message->type), - strempty(msg), isempty(msg) ? "" : ": ", - dhcp6_message_status_to_string(r)); - break; - } - case SD_DHCP6_OPTION_IA_NA: { - _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; + return 0; +} - if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { - log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode."); - break; - } +static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp6_client *client = ASSERT_PTR(userdata); - r = dhcp6_option_parse_ia(client, client->ia_pd.ia_na.id, optcode, optlen, optval, &ia); - if (r == -ENOMEM) - return r; - if (r < 0) - continue; + (void) event_source_disable(client->timeout_t2); + (void) event_source_disable(client->timeout_t1); - if (lease->ia.addresses) { - log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring."); - continue; - } + log_dhcp6_client(client, "Timeout T2"); - lease->ia = ia; - ia = (DHCP6IA) {}; + (void) client_start_transaction(client, DHCP6_STATE_REBIND); - lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2)); + return 0; +} - break; - } - case SD_DHCP6_OPTION_IA_PD: { - _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; +static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp6_client *client = ASSERT_PTR(userdata); - if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { - log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode."); - break; - } + (void) event_source_disable(client->timeout_t1); - r = dhcp6_option_parse_ia(client, client->ia_pd.ia_pd.id, optcode, optlen, optval, &ia); - if (r == -ENOMEM) - return r; - if (r < 0) - continue; + log_dhcp6_client(client, "Timeout T1"); - if (lease->pd.addresses) { - log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring."); - continue; - } + (void) client_start_transaction(client, DHCP6_STATE_RENEW); - lease->pd = ia; - ia = (DHCP6IA) {}; + return 0; +} - lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2)); +static int client_enter_bound_state(sd_dhcp6_client *client) { + usec_t lifetime_t1, lifetime_t2, lifetime_valid; + int r; - break; - } - case SD_DHCP6_OPTION_RAPID_COMMIT: - r = dhcp6_lease_set_rapid_commit(lease); - if (r < 0) - return r; + assert(client); + assert(client->lease); + assert(IN_SET(client->state, + DHCP6_STATE_SOLICITATION, + DHCP6_STATE_REQUEST, + DHCP6_STATE_RENEW, + DHCP6_STATE_REBIND)); - break; + (void) event_source_disable(client->receive_message); + (void) event_source_disable(client->timeout_resend); - case SD_DHCP6_OPTION_DNS_SERVERS: - r = dhcp6_lease_add_dns(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m"); + r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2, &lifetime_valid); + if (r < 0) + goto error; - break; + lifetime_t2 = client_timeout_compute_random(lifetime_t2); + lifetime_t1 = client_timeout_compute_random(MIN(lifetime_t1, lifetime_t2)); - case SD_DHCP6_OPTION_DOMAIN_LIST: - r = dhcp6_lease_add_domains(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m"); + if (lifetime_t1 == USEC_INFINITY) { + log_dhcp6_client(client, "Infinite T1"); + event_source_disable(client->timeout_t1); + } else { + log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(lifetime_t1, USEC_PER_SEC)); + r = event_reset_time_relative(client->event, &client->timeout_t1, + CLOCK_BOOTTIME, + lifetime_t1, 10 * USEC_PER_SEC, + client_timeout_t1, client, + client->event_priority, "dhcp6-t1-timeout", true); + if (r < 0) + goto error; + } - break; + if (lifetime_t2 == USEC_INFINITY) { + log_dhcp6_client(client, "Infinite T2"); + event_source_disable(client->timeout_t2); + } else { + log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(lifetime_t2, USEC_PER_SEC)); + r = event_reset_time_relative(client->event, &client->timeout_t2, + CLOCK_BOOTTIME, + lifetime_t2, 10 * USEC_PER_SEC, + client_timeout_t2, client, + client->event_priority, "dhcp6-t2-timeout", true); + if (r < 0) + goto error; + } - case SD_DHCP6_OPTION_NTP_SERVER: - r = dhcp6_lease_add_ntp(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m"); + if (lifetime_valid == USEC_INFINITY) { + log_dhcp6_client(client, "Infinite valid lifetime"); + event_source_disable(client->timeout_expire); + } else { + log_dhcp6_client(client, "Valid lifetime expires in %s", FORMAT_TIMESPAN(lifetime_valid, USEC_PER_SEC)); - break; + r = event_reset_time_relative(client->event, &client->timeout_expire, + CLOCK_BOOTTIME, + lifetime_valid, USEC_PER_SEC, + client_timeout_expire, client, + client->event_priority, "dhcp6-lease-expire", true); + if (r < 0) + goto error; + } - case SD_DHCP6_OPTION_SNTP_SERVERS: - r = dhcp6_lease_add_sntp(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m"); + client_set_state(client, DHCP6_STATE_BOUND); + client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); + return 0; - break; +error: + client_stop(client, r); + return r; +} - case SD_DHCP6_OPTION_CLIENT_FQDN: - r = dhcp6_lease_set_fqdn(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m"); +static int log_invalid_message_type(sd_dhcp6_client *client, const DHCP6Message *message) { + const char *type_str; - break; + assert(client); + assert(message); - case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME: - if (optlen != 4) - return -EINVAL; + type_str = dhcp6_message_type_to_string(message->type); + if (type_str) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received unexpected %s message, ignoring.", type_str); + else + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received unsupported message type %u, ignoring.", message->type); +} - irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC; - break; - } - } +static int client_process_information( + sd_dhcp6_client *client, + DHCP6Message *message, + size_t len, + const triple_timestamp *timestamp, + const struct in6_addr *server_address) { - uint8_t *clientid; - size_t clientid_len; - if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s message does not contain client ID. Ignoring.", - dhcp6_message_type_to_string(message->type)); + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; + int r; - if (clientid_len != client->duid_len || - memcmp(clientid, &client->duid, clientid_len) != 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "The client ID in %s message does not match. Ignoring.", - dhcp6_message_type_to_string(message->type)); + assert(client); + assert(message); - if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { - r = dhcp6_lease_get_serverid(lease, NULL, NULL); - if (r < 0) - return log_dhcp6_client_errno(client, r, "%s has no server id", - dhcp6_message_type_to_string(message->type)); + if (message->type != DHCP6_MESSAGE_REPLY) + return log_invalid_message_type(client, message); - if (!lease->ia.addresses && !lease->pd.addresses) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring."); + r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); + if (r < 0) + return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); - if (lease->ia.addresses) { - lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1); - lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2); - } + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); - if (lease->pd.addresses) { - lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1); - lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2); - } - } + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); - client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM); + /* Do not call client_stop() here, as it frees the acquired lease. */ + (void) event_source_disable(client->receive_message); + (void) event_source_disable(client->timeout_resend); + client_set_state(client, DHCP6_STATE_STOPPED); + client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); return 0; } -static int client_receive_reply( +static int client_process_reply( sd_dhcp6_client *client, - DHCP6Message *reply, + DHCP6Message *message, size_t len, - const triple_timestamp *t, + const triple_timestamp *timestamp, const struct in6_addr *server_address) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - bool rapid_commit; int r; assert(client); - assert(reply); - assert(t); + assert(message); - if (reply->type != DHCP6_MESSAGE_REPLY) - return 0; + if (message->type != DHCP6_MESSAGE_REPLY) + return log_invalid_message_type(client, message); - r = dhcp6_lease_new(&lease); + r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); if (r < 0) - return -ENOMEM; + return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); - lease->timestamp = *t; - if (server_address) - lease->server_address = *server_address; - - r = client_parse_message(client, reply, len, lease); - if (r < 0) - return r; - - if (client->state == DHCP6_STATE_SOLICITATION) { - r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit); - if (r < 0) - return r; - - if (!rapid_commit) - return 0; - } + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); - return DHCP6_STATE_BOUND; + return client_enter_bound_state(client); } -static int client_receive_advertise( +static int client_process_advertise_or_rapid_commit_reply( sd_dhcp6_client *client, - DHCP6Message *advertise, + DHCP6Message *message, size_t len, - const triple_timestamp *t, + const triple_timestamp *timestamp, const struct in6_addr *server_address) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - uint8_t pref_advertise = 0, pref_lease = 0; + uint8_t pref_advertise, pref_lease = 0; int r; assert(client); - assert(advertise); - assert(t); + assert(message); - if (advertise->type != DHCP6_MESSAGE_ADVERTISE) - return 0; + if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY)) + return log_invalid_message_type(client, message); - r = dhcp6_lease_new(&lease); + r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); if (r < 0) - return r; + return log_dhcp6_client_errno(client, r, "Failed to process received %s message, ignoring: %m", + dhcp6_message_type_to_string(message->type)); - lease->timestamp = *t; - if (server_address) - lease->server_address = *server_address; + if (message->type == DHCP6_MESSAGE_REPLY) { + bool rapid_commit; - r = client_parse_message(client, advertise, len, lease); - if (r < 0) - return r; + r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit); + if (r < 0) + return r; + + if (!rapid_commit) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received reply message without rapid commit flag, ignoring."); + + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); + + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); + + return client_enter_bound_state(client); + } r = dhcp6_lease_get_preference(lease, &pref_advertise); if (r < 0) return r; - r = dhcp6_lease_get_preference(client->lease, &pref_lease); + if (client->lease) { + r = dhcp6_lease_get_preference(client->lease, &pref_lease); + if (r < 0) + return r; + } - if (r < 0 || pref_advertise > pref_lease) { + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); + + if (!client->lease || pref_advertise > pref_lease) { + /* If this is the first advertise message or has higher preference, then save the lease. */ sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); - r = 0; } if (pref_advertise == 255 || client->retransmit_count > 1) - r = DHCP6_STATE_REQUEST; + (void) client_start_transaction(client, DHCP6_STATE_REQUEST); - return r; + return 0; } static int client_receive_message( @@ -1447,7 +1173,7 @@ static int client_receive_message( revents, void *userdata) { - sd_dhcp6_client *client = userdata; + sd_dhcp6_client *client = ASSERT_PTR(userdata); DHCP6_CLIENT_DONT_DESTROY(client); /* This needs to be initialized with zero. See #20741. */ CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {}; @@ -1466,11 +1192,6 @@ static int client_receive_message( _cleanup_free_ DHCP6Message *message = NULL; struct in6_addr *server_address = NULL; ssize_t buflen, len; - int r = 0; - - assert(s); - assert(client); - assert(client->event); buflen = next_datagram_size_fd(fd); if (buflen < 0) { @@ -1517,236 +1238,43 @@ static int client_receive_message( triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg))); } - if (!triple_timestamp_is_set(&t)) - triple_timestamp_get(&t); - - if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY, DHCP6_MESSAGE_RECONFIGURE)) { - const char *type_str = dhcp6_message_type_to_string(message->type); - if (type_str) - log_dhcp6_client(client, "Received unexpected %s message, ignoring.", type_str); - else - log_dhcp6_client(client, "Received unsupported message type %u, ignoring.", message->type); - return 0; - } - if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff))) return 0; switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: - r = client_receive_reply(client, message, len, &t, server_address); - if (r < 0) { - log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); + if (client_process_information(client, message, len, &t, server_address) < 0) return 0; - } - - client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); - - client_start(client, DHCP6_STATE_STOPPED); - break; case DHCP6_STATE_SOLICITATION: - r = client_receive_advertise(client, message, len, &t, server_address); - if (r < 0) { - log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m"); + if (client_process_advertise_or_rapid_commit_reply(client, message, len, &t, server_address) < 0) return 0; - } - - if (r == DHCP6_STATE_REQUEST) { - client_start(client, r); - break; - } + break; - _fallthrough_; /* for Solicitation Rapid Commit option check */ case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: case DHCP6_STATE_REBIND: - - r = client_receive_reply(client, message, len, &t, server_address); - if (r < 0) { - log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); + if (client_process_reply(client, message, len, &t, server_address) < 0) return 0; - } - - if (r == DHCP6_STATE_BOUND) { - r = client_start(client, DHCP6_STATE_BOUND); - if (r < 0) { - client_stop(client, r); - return 0; - } - - client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); - } - break; case DHCP6_STATE_BOUND: - - break; - case DHCP6_STATE_STOPPED: - return 0; default: assert_not_reached(); } - log_dhcp6_client(client, "Recv %s", - dhcp6_message_type_to_string(message->type)); - return 0; } -static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1, - uint32_t *lifetime_t2) { - assert_return(client, -EINVAL); - assert_return(client->lease, -EINVAL); - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) { - *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1); - *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2); - - return 0; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { - *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1); - *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2); - - return 0; - } - - return -ENOMSG; -} - -static int client_start(sd_dhcp6_client *client, DHCP6State state) { - int r; - usec_t timeout, time_now; - uint32_t lifetime_t1, lifetime_t2; - - assert_return(client, -EINVAL); - assert_return(client->event, -EINVAL); - assert_return(client->ifindex > 0, -EINVAL); - assert_return(client->state != state, -EINVAL); - - (void) event_source_disable(client->timeout_resend_expire); - (void) event_source_disable(client->timeout_resend); - client->retransmit_time = 0; - client->retransmit_count = 0; - - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return r; - - if (!client->receive_message) { - r = sd_event_add_io(client->event, &client->receive_message, - client->fd, EPOLLIN, client_receive_message, - client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->receive_message, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->receive_message, - "dhcp6-receive-message"); - if (r < 0) - goto error; - } - - switch (state) { - case DHCP6_STATE_STOPPED: - if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { - client->state = DHCP6_STATE_STOPPED; - - return 0; - } - - _fallthrough_; - case DHCP6_STATE_SOLICITATION: - client->state = DHCP6_STATE_SOLICITATION; - - break; - - case DHCP6_STATE_INFORMATION_REQUEST: - case DHCP6_STATE_REQUEST: - case DHCP6_STATE_RENEW: - case DHCP6_STATE_REBIND: - - client->state = state; - - break; - - case DHCP6_STATE_BOUND: - - r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2); - if (r < 0) - goto error; - - if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) { - log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x", - lifetime_t1, lifetime_t2); - - return 0; - } - - timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC); - - log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); - - r = event_reset_time(client->event, &client->timeout_t1, - clock_boottime_or_monotonic(), - time_now + timeout, 10 * USEC_PER_SEC, - client_timeout_t1, client, - client->event_priority, "dhcp6-t1-timeout", true); - if (r < 0) - goto error; - - timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC); - - log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); - - r = event_reset_time(client->event, &client->timeout_t2, - clock_boottime_or_monotonic(), - time_now + timeout, 10 * USEC_PER_SEC, - client_timeout_t2, client, - client->event_priority, "dhcp6-t2-timeout", true); - if (r < 0) - goto error; - - client->state = state; - - return 0; - default: - assert_not_reached(); - } - - client->transaction_id = random_u32() & htobe32(0x00ffffff); - client->transaction_start = time_now; - - r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), - 0, 0, - client_timeout_resend, client, - client->event_priority, "dhcp6-resend-timeout", true); - if (r < 0) - goto error; - - return 0; - - error: - client_reset(client); - return r; -} - int sd_dhcp6_client_stop(sd_dhcp6_client *client) { if (!client) return 0; client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP); + client->receive_message = sd_event_source_unref(client->receive_message); client->fd = safe_close(client->fd); return 0; @@ -1766,16 +1294,12 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { assert_return(client->event, -EINVAL); assert_return(client->ifindex > 0, -EINVAL); assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + assert_return(client->information_request || client->request_ia != 0, -EINVAL); - if (client->state != DHCP6_STATE_STOPPED) - return -EBUSY; - - if (!client->information_request && client->request_ia == 0) - return -EINVAL; - - r = client_reset(client); - if (r < 0) - return r; + /* Even if the client is in the STOPPED state, the lease acquired in the previous information + * request may be stored. */ + client->lease = sd_dhcp6_lease_unref(client->lease); r = client_ensure_iaid(client); if (r < 0) @@ -1790,7 +1314,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { if (r < 0) { _cleanup_free_ char *p = NULL; - (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p); + (void) in6_addr_to_string(&client->local_address, &p); return log_dhcp6_client_errno(client, r, "Failed to bind to UDP socket at address %s: %m", strna(p)); } @@ -1798,6 +1322,24 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { client->fd = r; } + if (!client->receive_message) { + _cleanup_(sd_event_source_disable_unrefp) sd_event_source *s = NULL; + + r = sd_event_add_io(client->event, &s, client->fd, EPOLLIN, client_receive_message, client); + if (r < 0) + return r; + + r = sd_event_source_set_priority(s, client->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(s, "dhcp6-receive-message"); + if (r < 0) + return r; + + client->receive_message = TAKE_PTR(s); + } + if (client->information_request) { usec_t t = now(CLOCK_MONOTONIC); @@ -1808,10 +1350,10 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { state = DHCP6_STATE_INFORMATION_REQUEST; } - log_dhcp6_client(client, "Started in %s mode", + log_dhcp6_client(client, "Starting in %s mode", client->information_request ? "Information request" : "Managed"); - return client_start(client, state); + return client_start_transaction(client, state); } int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) { @@ -1819,6 +1361,7 @@ int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64 assert_return(client, -EINVAL); assert_return(!client->event, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); if (event) client->event = sd_event_ref(event); @@ -1835,6 +1378,7 @@ int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); client->event = sd_event_unref(client->event); @@ -1848,24 +1392,26 @@ sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) { } static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { - assert(client); + if (!client) + return NULL; - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire); - client->timeout_t1 = sd_event_source_unref(client->timeout_t1); - client->timeout_t2 = sd_event_source_unref(client->timeout_t2); + sd_dhcp6_lease_unref(client->lease); - client_reset(client); + sd_event_source_disable_unref(client->receive_message); + sd_event_source_disable_unref(client->timeout_resend); + sd_event_source_disable_unref(client->timeout_expire); + sd_event_source_disable_unref(client->timeout_t1); + sd_event_source_disable_unref(client->timeout_t2); + sd_event_unref(client->event); client->fd = safe_close(client->fd); - sd_dhcp6_client_detach_event(client); - free(client->req_opts); free(client->fqdn); free(client->mudurl); - + dhcp6_ia_clear_addresses(&client->ia_pd); ordered_hashmap_free(client->extra_options); + ordered_set_free(client->vendor_options); strv_free(client->user_class); strv_free(client->vendor_class); free(client->ifname); @@ -1901,8 +1447,6 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) { .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD, .fd = -1, .req_opts_len = ELEMENTSOF(default_req_opts), - .hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1, - .hint_pd_prefix.iapdprefix.lifetime_valid = (be32_t) -1, .req_opts = TAKE_PTR(req_opts), }; diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c index b46a2e33..fd47cb73 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c @@ -8,10 +8,21 @@ #include <errno.h> #include "alloc-util.h" +#include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" -#include "dhcp6-protocol.h" #include "strv.h" -#include "util.h" + +#define IRT_DEFAULT (1 * USEC_PER_DAY) +#define IRT_MINIMUM (600 * USEC_PER_SEC) + +static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timestamp *timestamp) { + assert(lease); + + if (timestamp && triple_timestamp_is_set(timestamp)) + lease->timestamp = *timestamp; + else + triple_timestamp_get(&lease->timestamp); +} int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) { assert_return(lease, -EINVAL); @@ -26,63 +37,105 @@ int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_ return 0; } -int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) { - assert_return(lease, -EINVAL); - assert_return(ret, -EINVAL); - - *ret = lease->server_address; - return 0; +static usec_t sec2usec(uint32_t sec) { + return sec == UINT32_MAX ? USEC_INFINITY : sec * USEC_PER_SEC; } -int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { - DHCP6Address *addr; - uint32_t valid = 0, t; +static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) { + uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX; + + assert(lease); + assert(lease->ia_na || lease->ia_pd); - assert_return(ia, -EINVAL); - assert_return(expire, -EINVAL); + if (lease->ia_na) { + t1 = MIN(t1, be32toh(lease->ia_na->header.lifetime_t1)); + t2 = MIN(t2, be32toh(lease->ia_na->header.lifetime_t2)); - LIST_FOREACH(addresses, addr, ia->addresses) { - t = be32toh(addr->iaaddr.lifetime_valid); - if (valid < t) - valid = t; + LIST_FOREACH(addresses, a, lease->ia_na->addresses) + min_valid_lt = MIN(min_valid_lt, be32toh(a->iaaddr.lifetime_valid)); } - t = be32toh(ia->ia_na.lifetime_t2); - if (t > valid) - return -EINVAL; + if (lease->ia_pd) { + t1 = MIN(t1, be32toh(lease->ia_pd->header.lifetime_t1)); + t2 = MIN(t2, be32toh(lease->ia_pd->header.lifetime_t2)); - *expire = valid - t; + LIST_FOREACH(addresses, a, lease->ia_pd->addresses) + min_valid_lt = MIN(min_valid_lt, be32toh(a->iapdprefix.lifetime_valid)); + } + if (t2 == 0 || t2 > min_valid_lt) { + /* If T2 is zero or longer than the minimum valid lifetime of the addresses or prefixes, + * then adjust lifetime with it. */ + t1 = min_valid_lt / 2; + t2 = min_valid_lt / 10 * 8; + } + + lease->lifetime_valid = sec2usec(min_valid_lt); + lease->lifetime_t1 = sec2usec(t1); + lease->lifetime_t2 = sec2usec(t2); +} + +int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid) { + assert(lease); + + if (!lease->ia_na && !lease->ia_pd) + return -ENODATA; + + if (ret_t1) + *ret_t1 = lease->lifetime_t1; + if (ret_t2) + *ret_t2 = lease->lifetime_t2; + if (ret_valid) + *ret_valid = lease->lifetime_valid; return 0; } -DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) { - DHCP6Address *address; +static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) { + assert(lease); - if (!ia) - return NULL; + if (server_address) + lease->server_address = *server_address; + else + lease->server_address = (struct in6_addr) {}; +} - while (ia->addresses) { - address = ia->addresses; +int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) { + assert_return(lease, -EINVAL); + assert_return(ret, -EINVAL); - LIST_REMOVE(addresses, ia->addresses, address); + *ret = lease->server_address; + return 0; +} - free(address); - } +void dhcp6_ia_clear_addresses(DHCP6IA *ia) { + assert(ia); + + LIST_FOREACH(addresses, a, ia->addresses) + free(a); + + ia->addresses = NULL; +} + +DHCP6IA *dhcp6_ia_free(DHCP6IA *ia) { + if (!ia) + return NULL; + + dhcp6_ia_clear_addresses(ia); - return NULL; + return mfree(ia); } int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) { - uint8_t *clientid; + uint8_t *clientid = NULL; - assert_return(lease, -EINVAL); - assert_return(id, -EINVAL); - assert_return(len > 0, -EINVAL); + assert(lease); + assert(id || len == 0); - clientid = memdup(id, len); - if (!clientid) - return -ENOMEM; + if (len > 0) { + clientid = memdup(id, len); + if (!clientid) + return -ENOMEM; + } free_and_replace(lease->clientid, clientid); lease->clientid_len = len; @@ -91,7 +144,7 @@ int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t le } int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) { - assert_return(lease, -EINVAL); + assert(lease); if (!lease->clientid) return -ENODATA; @@ -105,15 +158,16 @@ int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re } int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) { - uint8_t *serverid; + uint8_t *serverid = NULL; - assert_return(lease, -EINVAL); - assert_return(id, -EINVAL); - assert_return(len > 0, -EINVAL); + assert(lease); + assert(id || len == 0); - serverid = memdup(id, len); - if (!serverid) - return -ENOMEM; + if (len > 0) { + serverid = memdup(id, len); + if (!serverid) + return -ENOMEM; + } free_and_replace(lease->serverid, serverid); lease->serverid_len = len; @@ -122,7 +176,7 @@ int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t le } int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) { - assert_return(lease, -EINVAL); + assert(lease); if (!lease->serverid) return -ENODATA; @@ -131,107 +185,99 @@ int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re *ret_id = lease->serverid; if (ret_len) *ret_len = lease->serverid_len; - return 0; } int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) { - assert_return(lease, -EINVAL); + assert(lease); lease->preference = preference; - return 0; } -int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) { - assert_return(preference, -EINVAL); - - if (!lease) - return -EINVAL; - - *preference = lease->preference; +int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret) { + assert(lease); + assert(ret); + *ret = lease->preference; return 0; } int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) { - assert_return(lease, -EINVAL); + assert(lease); lease->rapid_commit = true; - return 0; } -int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit) { - assert_return(lease, -EINVAL); - assert_return(rapid_commit, -EINVAL); - - *rapid_commit = lease->rapid_commit; +int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) { + assert(lease); + assert(ret); + *ret = lease->rapid_commit; return 0; } -int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid) { +int sd_dhcp6_lease_get_address( + sd_dhcp6_lease *lease, + struct in6_addr *ret_addr, + uint32_t *ret_lifetime_preferred, + uint32_t *ret_lifetime_valid) { + assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(lifetime_preferred, -EINVAL); - assert_return(lifetime_valid, -EINVAL); if (!lease->addr_iter) - return -ENOMSG; + return -ENODATA; - memcpy(addr, &lease->addr_iter->iaaddr.address, - sizeof(struct in6_addr)); - *lifetime_preferred = - be32toh(lease->addr_iter->iaaddr.lifetime_preferred); - *lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid); + if (ret_addr) + *ret_addr = lease->addr_iter->iaaddr.address; + if (ret_lifetime_preferred) + *ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred); + if (ret_lifetime_valid) + *ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid); lease->addr_iter = lease->addr_iter->addresses_next; - return 0; } void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { if (lease) - lease->addr_iter = lease->ia.addresses; + lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL; } -int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, - uint8_t *prefix_len, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid) { +int sd_dhcp6_lease_get_pd( + sd_dhcp6_lease *lease, + struct in6_addr *ret_prefix, + uint8_t *ret_prefix_len, + uint32_t *ret_lifetime_preferred, + uint32_t *ret_lifetime_valid) { + assert_return(lease, -EINVAL); - assert_return(prefix, -EINVAL); - assert_return(prefix_len, -EINVAL); - assert_return(lifetime_preferred, -EINVAL); - assert_return(lifetime_valid, -EINVAL); if (!lease->prefix_iter) - return -ENOMSG; + return -ENODATA; - memcpy(prefix, &lease->prefix_iter->iapdprefix.address, - sizeof(struct in6_addr)); - *prefix_len = lease->prefix_iter->iapdprefix.prefixlen; - *lifetime_preferred = - be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred); - *lifetime_valid = - be32toh(lease->prefix_iter->iapdprefix.lifetime_valid); + if (ret_prefix) + *ret_prefix = lease->prefix_iter->iapdprefix.address; + if (ret_prefix_len) + *ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen; + if (ret_lifetime_preferred) + *ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred); + if (ret_lifetime_valid) + *ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid); lease->prefix_iter = lease->prefix_iter->addresses_next; - return 0; } void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) { if (lease) - lease->prefix_iter = lease->pd.addresses; + lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL; } int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); if (optlen == 0) return 0; @@ -243,7 +289,7 @@ int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) { assert_return(lease, -EINVAL); if (!lease->dns) - return -ENOENT; + return -ENODATA; if (ret) *ret = lease->dns; @@ -255,8 +301,8 @@ int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t _cleanup_strv_free_ char **domains = NULL; int r; - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); if (optlen == 0) return 0; @@ -273,7 +319,7 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) { assert_return(ret, -EINVAL); if (!lease->domains) - return -ENOENT; + return -ENODATA; *ret = lease->domains; return strv_length(lease->domains); @@ -282,8 +328,8 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) { int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { int r; - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); for (size_t offset = 0; offset < optlen;) { const uint8_t *subval; @@ -294,11 +340,11 @@ int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt if (r < 0) return r; - switch(subopt) { + switch (subopt) { case DHCP6_NTP_SUBOPTION_SRV_ADDR: case DHCP6_NTP_SUBOPTION_MC_ADDR: if (sublen != 16) - return 0; + return -EINVAL; r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count); if (r < 0) @@ -328,8 +374,8 @@ int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt } int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); if (optlen == 0) return 0; @@ -354,14 +400,14 @@ int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr ** return lease->sntp_count; } - return -ENOENT; + return -ENODATA; } int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) { assert_return(lease, -EINVAL); if (!lease->ntp_fqdn) - return -ENOENT; + return -ENODATA; if (ret) *ret = lease->ntp_fqdn; @@ -369,11 +415,14 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) { } int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { - int r; char *fqdn; + int r; - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); + + if (optlen == 0) + return 0; if (optlen < 2) return -ENODATA; @@ -392,20 +441,222 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) { assert_return(ret, -EINVAL); if (!lease->fqdn) - return -ENOENT; + return -ENODATA; *ret = lease->fqdn; return 0; } +static int dhcp6_lease_parse_message( + sd_dhcp6_client *client, + sd_dhcp6_lease *lease, + const DHCP6Message *message, + size_t len) { + + usec_t irt = IRT_DEFAULT; + int r; + + assert(client); + assert(lease); + assert(message); + assert(len >= sizeof(DHCP6Message)); + + len -= sizeof(DHCP6Message); + for (size_t offset = 0; offset < len;) { + uint16_t optcode; + size_t optlen; + const uint8_t *optval; + + r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval); + if (r < 0) + return r; + + switch (optcode) { + case SD_DHCP6_OPTION_CLIENTID: + if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs", + dhcp6_message_type_to_string(message->type)); + + r = dhcp6_lease_set_clientid(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_SERVERID: + if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs", + dhcp6_message_type_to_string(message->type)); + + r = dhcp6_lease_set_serverid(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_PREFERENCE: + if (optlen != 1) + return -EINVAL; + + r = dhcp6_lease_set_preference(lease, optval[0]); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_STATUS_CODE: { + _cleanup_free_ char *msg = NULL; + + r = dhcp6_option_parse_status(optval, optlen, &msg); + if (r < 0) + return r; + + if (r > 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received %s message with non-zero status: %s%s%s", + dhcp6_message_type_to_string(message->type), + strempty(msg), isempty(msg) ? "" : ": ", + dhcp6_message_status_to_string(r)); + break; + } + case SD_DHCP6_OPTION_IA_NA: { + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; + + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode."); + break; + } + + r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia); + if (r == -ENOMEM) + return r; + if (r < 0) + continue; + + if (lease->ia_na) { + log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring."); + continue; + } + + dhcp6_ia_free(lease->ia_na); + lease->ia_na = TAKE_PTR(ia); + break; + } + case SD_DHCP6_OPTION_IA_PD: { + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; + + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode."); + break; + } + + r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia); + if (r == -ENOMEM) + return r; + if (r < 0) + continue; + + if (lease->ia_pd) { + log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring."); + continue; + } + + dhcp6_ia_free(lease->ia_pd); + lease->ia_pd = TAKE_PTR(ia); + break; + } + case SD_DHCP6_OPTION_RAPID_COMMIT: + r = dhcp6_lease_set_rapid_commit(lease); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_DNS_SERVERS: + r = dhcp6_lease_add_dns(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_DOMAIN_LIST: + r = dhcp6_lease_add_domains(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_NTP_SERVER: + r = dhcp6_lease_add_ntp(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_SNTP_SERVERS: + r = dhcp6_lease_add_sntp(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_CLIENT_FQDN: + r = dhcp6_lease_set_fqdn(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME: + if (optlen != 4) + return -EINVAL; + + irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC; + break; + } + } + + uint8_t *clientid; + size_t clientid_len; + if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "%s message does not contain client ID. Ignoring.", + dhcp6_message_type_to_string(message->type)); + + if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "The client ID in %s message does not match. Ignoring.", + dhcp6_message_type_to_string(message->type)); + + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM); + log_dhcp6_client(client, "New information request will be refused in %s.", + FORMAT_TIMESPAN(client->information_refresh_time_usec, USEC_PER_SEC)); + + } else { + r = dhcp6_lease_get_serverid(lease, NULL, NULL); + if (r < 0) + return log_dhcp6_client_errno(client, r, "%s has no server id", + dhcp6_message_type_to_string(message->type)); + + if (!lease->ia_na && !lease->ia_pd) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "No IA_PD prefix or IA_NA address received. Ignoring."); + + dhcp6_lease_set_lifetime(lease); + } + + return 0; +} + static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { if (!lease) return NULL; free(lease->clientid); free(lease->serverid); - dhcp6_lease_free_ia(&lease->ia); - dhcp6_lease_free_ia(&lease->pd); + dhcp6_ia_free(lease->ia_na); + dhcp6_ia_free(lease->ia_pd); free(lease->dns); free(lease->fqdn); strv_free(lease->domains); @@ -421,14 +672,47 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free); int dhcp6_lease_new(sd_dhcp6_lease **ret) { sd_dhcp6_lease *lease; - lease = new0(sd_dhcp6_lease, 1); + assert(ret); + + lease = new(sd_dhcp6_lease, 1); if (!lease) return -ENOMEM; - lease->n_ref = 1; - - LIST_HEAD_INIT(lease->ia.addresses); + *lease = (sd_dhcp6_lease) { + .n_ref = 1, + }; *ret = lease; return 0; } + +int dhcp6_lease_new_from_message( + sd_dhcp6_client *client, + const DHCP6Message *message, + size_t len, + const triple_timestamp *timestamp, + const struct in6_addr *server_address, + sd_dhcp6_lease **ret) { + + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; + int r; + + assert(client); + assert(message); + assert(len >= sizeof(DHCP6Message)); + assert(ret); + + r = dhcp6_lease_new(&lease); + if (r < 0) + return r; + + dhcp6_lease_set_timestamp(lease, timestamp); + dhcp6_lease_set_server_address(lease, server_address); + + r = dhcp6_lease_parse_message(client, lease, message, len); + if (r < 0) + return r; + + *ret = TAKE_PTR(lease); + return 0; +} diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c b/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c index f50e9c8d..496eb8f6 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c @@ -71,7 +71,7 @@ static int lldp_rx_make_space(sd_lldp_rx *lldp_rx, size_t extra) { goto remove_one; if (t == USEC_INFINITY) - t = now(clock_boottime_or_monotonic()); + t = now(CLOCK_BOOTTIME); if (n->until > t) break; @@ -450,7 +450,7 @@ static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor) return event_source_disable(lldp_rx->timer_event_source); return event_reset_time(lldp_rx->event, &lldp_rx->timer_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, n->until, 0, on_timer_event, lldp_rx, lldp_rx->event_priority, "lldp-rx-timer", true); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c index 13e16f9b..6e3c2ce9 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c @@ -6,6 +6,7 @@ #include "event-source.h" #include "event-util.h" +#include "fd-util.h" #include "log.h" #include "string-util.h" @@ -123,3 +124,43 @@ int event_source_is_enabled(sd_event_source *s) { return sd_event_source_get_enabled(s, NULL); } + +#if 0 /* NM_IGNORED */ +int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata) { + _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; + _cleanup_close_ int fd = -1; + int r; + + assert(e); + + /* Allocates an IO event source that gets woken up whenever the clock changes. Needs to be recreated on each event */ + + fd = time_change_fd(); + if (fd < 0) + return fd; + + r = sd_event_add_io(e, &s, fd, EPOLLIN, callback, userdata); + if (r < 0) + return r; + + r = sd_event_source_set_io_fd_own(s, true); + if (r < 0) + return r; + + TAKE_FD(fd); + + r = sd_event_source_set_description(s, "time-change"); + if (r < 0) + return r; + + if (ret) + *ret = TAKE_PTR(s); + else { + r = sd_event_source_set_floating(s, true); + if (r < 0) + return r; + } + + return 0; +} +#endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h index 64a41992..abd043bd 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h @@ -29,3 +29,5 @@ int event_reset_time_relative( bool force_reset); int event_source_disable(sd_event_source *s); int event_source_is_enabled(sd_event_source *s); + +int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c index e87d900c..240476e6 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c @@ -1924,7 +1924,6 @@ static int event_make_inode_data( static uint32_t inode_data_determine_mask(struct inode_data *d) { bool excl_unlink = true; uint32_t combined = 0; - sd_event_source *s; assert(d); @@ -3462,9 +3461,7 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) { /* The queue overran, let's pass this event to all event sources connected to this inotify * object */ - HASHMAP_FOREACH(inode_data, d->inodes) { - sd_event_source *s; - + HASHMAP_FOREACH(inode_data, d->inodes) LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) { if (event_source_is_offline(s)) @@ -3474,10 +3471,8 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) { if (r < 0) return r; } - } } else { struct inode_data *inode_data; - sd_event_source *s; /* Find the inode object for this watch descriptor. If IN_IGNORED is set we also remove it from * our watch descriptor table. */ @@ -3525,7 +3520,6 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) { } static int process_inotify(sd_event *e) { - struct inotify_data *d; int r, done = 0; assert(e); @@ -3910,6 +3904,7 @@ static int epoll_wait_usec( int msec; #if 0 static bool epoll_pwait2_absent = false; + int r; /* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not. * @@ -3918,12 +3913,10 @@ static int epoll_wait_usec( * https://github.com/systemd/systemd/issues/19052. */ if (!epoll_pwait2_absent && timeout != USEC_INFINITY) { - struct timespec ts; - r = epoll_pwait2(fd, events, maxevents, - timespec_store(&ts, timeout), + TIMESPEC_STORE(timeout), NULL); if (r >= 0) return r; @@ -4324,12 +4317,6 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) { if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock)) return -EOPNOTSUPP; - /* Generate a clean error in case CLOCK_BOOTTIME is not available. Note that don't use clock_supported() here, - * for a reason: there are systems where CLOCK_BOOTTIME is supported, but CLOCK_BOOTTIME_ALARM is not, but for - * the purpose of getting the time this doesn't matter. */ - if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported()) - return -EOPNOTSUPP; - if (!triple_timestamp_is_set(&e->timestamp)) { /* Implicitly fall back to now() if we never ran before and thus have no cached time. */ *usec = now(clock); @@ -4469,6 +4456,7 @@ _public_ int sd_event_source_get_floating(sd_event_source *s) { return s->floating; } +#endif /* NM_IGNORED */ _public_ int sd_event_source_set_floating(sd_event_source *s, int b) { assert_return(s, -EINVAL); @@ -4492,6 +4480,7 @@ _public_ int sd_event_source_set_floating(sd_event_source *s, int b) { return 1; } +#if 0 /* NM_IGNORED */ _public_ int sd_event_source_get_exit_on_failure(sd_event_source *s) { assert_return(s, -EINVAL); assert_return(s->type != SOURCE_EXIT, -EDOM); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c index 9ec2abc8..f1a3a42e 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c @@ -15,29 +15,6 @@ #include "sync-util.h" #if 0 /* NM_IGNORED */ -char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]) { - unsigned n, k = 0; - - assert(s); - - /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */ - - for (n = 0; n < 16; n++) { - - if (IN_SET(n, 4, 6, 8, 10)) - s[k++] = '-'; - - s[k++] = hexchar(id.bytes[n] >> 4); - s[k++] = hexchar(id.bytes[n] & 0xF); - } - - assert(k == 36); - - s[k] = 0; - - return s; -} - bool id128_is_valid(const char *s) { size_t i, l; @@ -158,13 +135,13 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) { assert(f < _ID128_FORMAT_MAX); if (f != ID128_UUID) { - sd_id128_to_string(id, buffer); - buffer[32] = '\n'; - sz = 33; + assert_se(sd_id128_to_string(id, buffer)); + buffer[SD_ID128_STRING_MAX - 1] = '\n'; + sz = SD_ID128_STRING_MAX; } else { - id128_to_uuid_string(id, buffer); - buffer[36] = '\n'; - sz = 37; + assert_se(sd_id128_to_uuid_string(id, buffer)); + buffer[SD_ID128_UUID_STRING_MAX - 1] = '\n'; + sz = SD_ID128_UUID_STRING_MAX; } r = loop_write(fd, buffer, sz, false); @@ -234,4 +211,20 @@ int id128_get_product(sd_id128_t *ret) { *ret = uuid; return 0; } + +int id128_equal_string(const char *s, sd_id128_t id) { + sd_id128_t parsed; + int r; + + if (!s) + return false; + + /* Checks if the specified string matches a valid string representation of the specified 128 bit ID/uuid */ + + r = sd_id128_from_string(s, &parsed); + if (r < 0) + return r; + + return sd_id128_equal(parsed, id); +} #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h index b7327a1f..65a278c8 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h @@ -8,12 +8,6 @@ #include "hash-funcs.h" #include "macro.h" -#define ID128_UUID_STRING_MAX 37 - -char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]); - -#define ID128_TO_UUID_STRING(id) id128_to_uuid_string((id), (char[ID128_UUID_STRING_MAX]) {}) - bool id128_is_valid(const char *s) _pure_; typedef enum Id128Format { @@ -40,3 +34,5 @@ extern const struct hash_ops id128_hash_ops; sd_id128_t id128_make_v4_uuid(sd_id128_t id); int id128_get_product(sd_id128_t *ret); + +int id128_equal_string(const char *s, sd_id128_t id); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c index c3ed3bbb..8e47e26e 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c @@ -22,16 +22,36 @@ #if 0 /* NM_IGNORED */ _public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) { - unsigned n; - assert_return(s, NULL); - for (n = 0; n < 16; n++) { + for (size_t n = 0; n < 16; n++) { s[n*2] = hexchar(id.bytes[n] >> 4); s[n*2+1] = hexchar(id.bytes[n] & 0xF); } - s[32] = 0; + s[SD_ID128_STRING_MAX-1] = 0; + + return s; +} + +_public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]) { + size_t k = 0; + + assert_return(s, NULL); + + /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */ + + for (size_t n = 0; n < 16; n++) { + + if (IN_SET(n, 4, 6, 8, 10)) + s[k++] = '-'; + + s[k++] = hexchar(id.bytes[n] >> 4); + s[k++] = hexchar(id.bytes[n] & 0xF); + } + + assert(k == SD_ID128_UUID_STRING_MAX - 1); + s[k] = 0; return s; } @@ -261,9 +281,7 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) { assert_return(ret, -EINVAL); - /* We allow usage if x86-64 RDRAND here. It might not be trusted enough for keeping secrets, but it should be - * fine for UUIDS. */ - r = genuine_random_bytes(&t, sizeof t, RANDOM_ALLOW_RDRAND); + r = genuine_random_bytes(&t, sizeof(t), 0); if (r < 0) return r; diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h b/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h index 4af4b45f..834f80b4 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h @@ -44,67 +44,175 @@ enum { * The client may want to start acquiring link-local addresses. */ }; +/* https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#options */ enum { - SD_DHCP_OPTION_PAD = 0, - SD_DHCP_OPTION_SUBNET_MASK = 1, - SD_DHCP_OPTION_TIME_OFFSET = 2, - SD_DHCP_OPTION_ROUTER = 3, - SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6, - SD_DHCP_OPTION_LPR_SERVER = 9, - SD_DHCP_OPTION_HOST_NAME = 12, - SD_DHCP_OPTION_BOOT_FILE_SIZE = 13, - SD_DHCP_OPTION_DOMAIN_NAME = 15, - SD_DHCP_OPTION_ROOT_PATH = 17, - SD_DHCP_OPTION_ENABLE_IP_FORWARDING = 19, - SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20, - SD_DHCP_OPTION_POLICY_FILTER = 21, - SD_DHCP_OPTION_INTERFACE_MDR = 22, - SD_DHCP_OPTION_INTERFACE_TTL = 23, - SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24, - SD_DHCP_OPTION_INTERFACE_MTU = 26, - SD_DHCP_OPTION_BROADCAST = 28, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_ROUTER_DISCOVER = 31, - SD_DHCP_OPTION_STATIC_ROUTE = 33, - SD_DHCP_OPTION_NTP_SERVER = 42, - SD_DHCP_OPTION_VENDOR_SPECIFIC = 43, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_NETBIOS_NAMESERVER = 44, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_NETBIOS_NODETYPE = 46, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_NETBIOS_SCOPE = 47, - SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50, - SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51, - SD_DHCP_OPTION_OVERLOAD = 52, - SD_DHCP_OPTION_MESSAGE_TYPE = 53, - SD_DHCP_OPTION_SERVER_IDENTIFIER = 54, - SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55, - SD_DHCP_OPTION_ERROR_MESSAGE = 56, - SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57, - SD_DHCP_OPTION_RENEWAL_T1_TIME = 58, - SD_DHCP_OPTION_REBINDING_T2_TIME = 59, - SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, - SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61, - SD_DHCP_OPTION_SMTP_SERVER = 69, - SD_DHCP_OPTION_POP3_SERVER = 70, - SD_DHCP_OPTION_USER_CLASS = 77, - SD_DHCP_OPTION_FQDN = 81, - SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82, - SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, - SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, - SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119, - SD_DHCP_OPTION_SIP_SERVER = 120, - SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, - SD_DHCP_OPTION_MUD_URL = 161, - SD_DHCP_OPTION_6RD = 212, + SD_DHCP_OPTION_PAD = 0, /* [RFC2132] */ + SD_DHCP_OPTION_SUBNET_MASK = 1, /* [RFC2132] */ + SD_DHCP_OPTION_TIME_OFFSET = 2, /* [RFC2132], deprecated by 100 and 101 */ + SD_DHCP_OPTION_ROUTER = 3, /* [RFC2132] */ + SD_DHCP_OPTION_TIME_SERVER = 4, /* [RFC2132] */ + SD_DHCP_OPTION_NAME_SERVER = 5, /* [RFC2132] */ + SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6, /* [RFC2132] */ + SD_DHCP_OPTION_LOG_SERVER = 7, /* [RFC2132] */ + SD_DHCP_OPTION_QUOTES_SERVER = 8, /* [RFC2132] */ + SD_DHCP_OPTION_LPR_SERVER = 9, /* [RFC2132] */ + SD_DHCP_OPTION_IMPRESS_SERVER = 10, /* [RFC2132] */ + SD_DHCP_OPTION_RLP_SERVER = 11, /* [RFC2132] */ + SD_DHCP_OPTION_HOST_NAME = 12, /* [RFC2132] */ + SD_DHCP_OPTION_BOOT_FILE_SIZE = 13, /* [RFC2132] */ + SD_DHCP_OPTION_MERIT_DUMP_FILE = 14, /* [RFC2132] */ + SD_DHCP_OPTION_DOMAIN_NAME = 15, /* [RFC2132] */ + SD_DHCP_OPTION_SWAP_SERVER = 16, /* [RFC2132] */ + SD_DHCP_OPTION_ROOT_PATH = 17, /* [RFC2132] */ + SD_DHCP_OPTION_EXTENSION_FILE = 18, /* [RFC2132] */ + SD_DHCP_OPTION_FORWARD = 19, /* [RFC2132] */ + SD_DHCP_OPTION_SOURCE_ROUTE = 20, /* [RFC2132] */ + SD_DHCP_OPTION_POLICY_FILTER = 21, /* [RFC2132] */ + SD_DHCP_OPTION_MAX_DATAGRAM_ASSEMBLY = 22, /* [RFC2132] */ + SD_DHCP_OPTION_DEFAULT_IP_TTL = 23, /* [RFC2132] */ + SD_DHCP_OPTION_MTU_TIMEOUT = 24, /* [RFC2132] */ + SD_DHCP_OPTION_MTU_PLATEAU = 25, /* [RFC2132] */ + SD_DHCP_OPTION_MTU_INTERFACE = 26, /* [RFC2132] */ + SD_DHCP_OPTION_MTU_SUBNET = 27, /* [RFC2132] */ + SD_DHCP_OPTION_BROADCAST = 28, /* [RFC2132] */ + SD_DHCP_OPTION_MASK_DISCOVERY = 29, /* [RFC2132] */ + SD_DHCP_OPTION_MASK_SUPPLIER = 30, /* [RFC2132] */ + SD_DHCP_OPTION_ROUTER_DISCOVERY = 31, /* [RFC2132] */ + SD_DHCP_OPTION_ROUTER_REQUEST = 32, /* [RFC2132] */ + SD_DHCP_OPTION_STATIC_ROUTE = 33, /* [RFC2132] */ + SD_DHCP_OPTION_TRAILERS = 34, /* [RFC2132] */ + SD_DHCP_OPTION_ARP_TIMEOUT = 35, /* [RFC2132] */ + SD_DHCP_OPTION_ETHERNET = 36, /* [RFC2132] */ + SD_DHCP_OPTION_DEFAULT_TCP_TTL = 37, /* [RFC2132] */ + SD_DHCP_OPTION_KEEPALIVE_TIME = 38, /* [RFC2132] */ + SD_DHCP_OPTION_KEEPALIVE_DATA = 39, /* [RFC2132] */ + SD_DHCP_OPTION_NIS_DOMAIN = 40, /* [RFC2132] */ + SD_DHCP_OPTION_NIS_SERVER = 41, /* [RFC2132] */ + SD_DHCP_OPTION_NTP_SERVER = 42, /* [RFC2132] */ + SD_DHCP_OPTION_VENDOR_SPECIFIC = 43, /* [RFC2132] */ + SD_DHCP_OPTION_NETBIOS_NAME_SERVER = 44, /* [RFC2132] */ + SD_DHCP_OPTION_NETBIOS_DIST_SERVER = 45, /* [RFC2132] */ + SD_DHCP_OPTION_NETBIOS_NODE_TYPE = 46, /* [RFC2132] */ + SD_DHCP_OPTION_NETBIOS_SCOPE = 47, /* [RFC2132] */ + SD_DHCP_OPTION_X_WINDOW_FONT = 48, /* [RFC2132] */ + SD_DHCP_OPTION_X_WINDOW_MANAGER = 49, /* [RFC2132] */ + SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50, /* [RFC2132] */ + SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51, /* [RFC2132] */ + SD_DHCP_OPTION_OVERLOAD = 52, /* [RFC2132] */ + SD_DHCP_OPTION_MESSAGE_TYPE = 53, /* [RFC2132] */ + SD_DHCP_OPTION_SERVER_IDENTIFIER = 54, /* [RFC2132] */ + SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55, /* [RFC2132] */ + SD_DHCP_OPTION_ERROR_MESSAGE = 56, /* [RFC2132] */ + SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57, /* [RFC2132] */ + SD_DHCP_OPTION_RENEWAL_TIME = 58, /* [RFC2132] */ + SD_DHCP_OPTION_REBINDING_TIME = 59, /* [RFC2132] */ + SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, /* [RFC2132] */ + SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61, /* [RFC2132] */ + SD_DHCP_OPTION_NETWARE_IP_DOMAIN = 62, /* [RFC2242] */ + SD_DHCP_OPTION_NETWARE_IP_OPTION = 63, /* [RFC2242] */ + SD_DHCP_OPTION_NIS_DOMAIN_NAME = 64, /* [RFC2132] */ + SD_DHCP_OPTION_NIS_SERVER_ADDR = 65, /* [RFC2132] */ + SD_DHCP_OPTION_BOOT_SERVER_NAME = 66, /* [RFC2132] */ + SD_DHCP_OPTION_BOOT_FILENAME = 67, /* [RFC2132] */ + SD_DHCP_OPTION_HOME_AGENT_ADDRESSES = 68, /* [RFC2132] */ + SD_DHCP_OPTION_SMTP_SERVER = 69, /* [RFC2132] */ + SD_DHCP_OPTION_POP3_SERVER = 70, /* [RFC2132] */ + SD_DHCP_OPTION_NNTP_SERVER = 71, /* [RFC2132] */ + SD_DHCP_OPTION_WWW_SERVER = 72, /* [RFC2132] */ + SD_DHCP_OPTION_FINGER_SERVER = 73, /* [RFC2132] */ + SD_DHCP_OPTION_IRC_SERVER = 74, /* [RFC2132] */ + SD_DHCP_OPTION_STREETTALK_SERVER = 75, /* [RFC2132] */ + SD_DHCP_OPTION_STDA_SERVER = 76, /* [RFC2132] */ + SD_DHCP_OPTION_USER_CLASS = 77, /* [RFC3004] */ + SD_DHCP_OPTION_DIRECTORY_AGENT = 78, /* [RFC2610] */ + SD_DHCP_OPTION_SERVICE_SCOPE = 79, /* [RFC2610] */ + SD_DHCP_OPTION_RAPID_COMMIT = 80, /* [RFC4039] */ + SD_DHCP_OPTION_FQDN = 81, /* [RFC4702] */ + SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82, /* [RFC3046] */ + SD_DHCP_OPTION_ISNS = 83, /* [RFC4174] */ + /* option code 84 is unassigned [RFC3679] */ + SD_DHCP_OPTION_NDS_SERVER = 85, /* [RFC2241] */ + SD_DHCP_OPTION_NDS_TREE_NAME = 86, /* [RFC2241] */ + SD_DHCP_OPTION_NDS_CONTEXT = 87, /* [RFC2241] */ + SD_DHCP_OPTION_BCMCS_CONTROLLER_DOMAIN_NAM = 88, /* [RFC4280] */ + SD_DHCP_OPTION_BCMCS_CONTROLLER_ADDRESS = 89, /* [RFC4280] */ + SD_DHCP_OPTION_AUTHENTICATION = 90, /* [RFC3118] */ + SD_DHCP_OPTION_CLIENT_LAST_TRANSACTION_TIME = 91, /* [RFC4388] */ + SD_DHCP_OPTION_ASSOCIATED_IP = 92, /* [RFC4388] */ + SD_DHCP_OPTION_CLIENT_SYSTEM = 93, /* [RFC4578] */ + SD_DHCP_OPTION_CLIENT_NDI = 94, /* [RFC4578] */ + SD_DHCP_OPTION_LDAP = 95, /* [RFC3679] */ + /* option code 96 is unassigned [RFC3679] */ + SD_DHCP_OPTION_UUID = 97, /* [RFC4578] */ + SD_DHCP_OPTION_USER_AUTHENTICATION = 98, /* [RFC2485] */ + SD_DHCP_OPTION_GEOCONF_CIVIC = 99, /* [RFC4776] */ + SD_DHCP_OPTION_POSIX_TIMEZONE = 100, /* [RFC4833] */ + SD_DHCP_OPTION_TZDB_TIMEZONE = 101, /* [RFC4833] */ + /* option codes 102-107 are unassigned [RFC3679] */ + SD_DHCP_OPTION_IPV6_ONLY_PREFERRED = 108, /* [RFC8925] */ + SD_DHCP_OPTION_DHCP4O6_SOURCE_ADDRESS = 109, /* [RFC8539] */ + /* option codes 110-111 are unassigned [RFC3679] */ + SD_DHCP_OPTION_NETINFO_ADDRESS = 112, /* [RFC3679] */ + SD_DHCP_OPTION_NETINFO_TAG = 113, /* [RFC3679] */ + SD_DHCP_OPTION_DHCP_CAPTIVE_PORTAL = 114, /* [RFC8910] */ + /* option code 115 is unassigned [RFC3679] */ + SD_DHCP_OPTION_AUTO_CONFIG = 116, /* [RFC2563] */ + SD_DHCP_OPTION_NAME_SERVICE_SEARCH = 117, /* [RFC2937] */ + SD_DHCP_OPTION_SUBNET_SELECTION = 118, /* [RFC3011] */ + SD_DHCP_OPTION_DOMAIN_SEARCH = 119, /* [RFC3397] */ + SD_DHCP_OPTION_SIP_SERVER = 120, /* [RFC3361] */ + SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, /* [RFC3442] */ + SD_DHCP_OPTION_CABLELABS_CLIENT_CONFIGURATION = 122, /* [RFC3495] */ + SD_DHCP_OPTION_GEOCONF = 123, /* [RFC6225] */ + SD_DHCP_OPTION_VENDOR_CLASS = 124, /* [RFC3925] */ + SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION = 125, /* [RFC3925] */ + /* option codes 126-127 are unassigned [RFC3679] */ + /* option codes 128-135 are assigned to use by PXE, but they are vendor specific [RFC4578] */ + SD_DHCP_OPTION_PANA_AGENT = 136, /* [RFC5192] */ + SD_DHCP_OPTION_LOST_SERVER_FQDN = 137, /* [RFC5223] */ + SD_DHCP_OPTION_CAPWAP_AC_ADDRESS = 138, /* [RFC5417] */ + SD_DHCP_OPTION_MOS_ADDRESS = 139, /* [RFC5678] */ + SD_DHCP_OPTION_MOS_FQDN = 140, /* [RFC5678] */ + SD_DHCP_OPTION_SIP_SERVICE_DOMAINS = 141, /* [RFC6011] */ + SD_DHCP_OPTION_ANDSF_ADDRESS = 142, /* [RFC6153] */ + SD_DHCP_OPTION_SZTP_REDIRECT = 143, /* [RFC8572] */ + SD_DHCP_OPTION_GEOLOC = 144, /* [RFC6225] */ + SD_DHCP_OPTION_FORCERENEW_NONCE_CAPABLE = 145, /* [RFC6704] */ + SD_DHCP_OPTION_RDNSS_SELECTION = 146, /* [RFC6731] */ + SD_DHCP_OPTION_DOTS_RI = 147, /* [RFC8973] */ + SD_DHCP_OPTION_DOTS_ADDRESS = 148, /* [RFC8973] */ + /* option code 149 is unassigned [RFC3942] */ + SD_DHCP_OPTION_TFTP_SERVER_ADDRESS = 150, /* [RFC5859] */ + SD_DHCP_OPTION_STATUS_CODE = 151, /* [RFC6926] */ + SD_DHCP_OPTION_BASE_TIME = 152, /* [RFC6926] */ + SD_DHCP_OPTION_START_TIME_OF_STATE = 153, /* [RFC6926] */ + SD_DHCP_OPTION_QUERY_START_TIME = 154, /* [RFC6926] */ + SD_DHCP_OPTION_QUERY_END_TIME = 155, /* [RFC6926] */ + SD_DHCP_OPTION_DHCP_STATE = 156, /* [RFC6926] */ + SD_DHCP_OPTION_DATA_SOURCE = 157, /* [RFC6926] */ + SD_DHCP_OPTION_PCP_SERVER = 158, /* [RFC7291] */ + SD_DHCP_OPTION_PORT_PARAMS = 159, /* [RFC7618] */ + /* option code 160 is unassigned [RFC7710][RFC8910] */ + SD_DHCP_OPTION_MUD_URL = 161, /* [RFC8520] */ + /* option codes 162-174 are unassigned [RFC3942] */ + /* option codes 175-177 are temporary assigned. */ + /* option codes 178-207 are unassigned [RFC3942] */ + SD_DHCP_OPTION_PXELINUX_MAGIC = 208, /* [RFC5071] Deprecated */ + SD_DHCP_OPTION_CONFIGURATION_FILE = 209, /* [RFC5071] */ + SD_DHCP_OPTION_PATH_PREFIX = 210, /* [RFC5071] */ + SD_DHCP_OPTION_REBOOT_TIME = 211, /* [RFC5071] */ + SD_DHCP_OPTION_6RD = 212, /* [RFC5969] */ + SD_DHCP_OPTION_ACCESS_DOMAIN = 213, /* [RFC5986] */ + /* option codes 214-219 are unassigned */ + SD_DHCP_OPTION_SUBNET_ALLOCATION = 220, /* [RFC6656] */ + SD_DHCP_OPTION_VIRTUAL_SUBNET_SELECTION = 221, /* [RFC6607] */ + /* option codes 222-223 are unassigned [RFC3942] */ + /* option codes 224-254 are reserved for private use */ SD_DHCP_OPTION_PRIVATE_BASE = 224, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252, + SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249, /* [RFC7844] */ + SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252, /* [RFC7844] */ SD_DHCP_OPTION_PRIVATE_LAST = 254, - SD_DHCP_OPTION_END = 255, + SD_DHCP_OPTION_END = 255, /* [RFC2132] */ }; /* Suboptions for SD_DHCP_OPTION_RELAY_AGENT_INFORMATION option */ diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp-lease.h b/src/libnm-systemd-core/src/systemd/sd-dhcp-lease.h index 478bbfd7..578ac6d4 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp-lease.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp-lease.h @@ -67,7 +67,8 @@ int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname); int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains); int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname); int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path); -int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes); +int sd_dhcp_lease_get_static_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret); +int sd_dhcp_lease_get_classless_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret); int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len); int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len); int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone); @@ -82,7 +83,6 @@ int sd_dhcp_lease_get_6rd( int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination); int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length); int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway); -int sd_dhcp_route_get_option(sd_dhcp_route *route); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref); diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h index 0e23c84e..d89b7d1c 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h @@ -81,8 +81,8 @@ enum { SD_DHCP6_OPTION_SUBSCRIBER_ID = 38, /* RFC 4580 */ SD_DHCP6_OPTION_CLIENT_FQDN = 39, /* RFC 4704 */ SD_DHCP6_OPTION_PANA_AGENT = 40, /* RFC 5192 */ - SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE = 41, /* RFC 4833 */ - SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE = 42, /* RFC 4833 */ + SD_DHCP6_OPTION_POSIX_TIMEZONE = 41, /* RFC 4833 */ + SD_DHCP6_OPTION_TZDB_TIMEZONE = 42, /* RFC 4833 */ SD_DHCP6_OPTION_ERO = 43, /* RFC 4994 */ SD_DHCP6_OPTION_LQ_QUERY = 44, /* RFC 5007 */ SD_DHCP6_OPTION_CLIENT_DATA = 45, /* RFC 5007 */ @@ -251,7 +251,7 @@ int sd_dhcp6_client_set_request_vendor_class( int sd_dhcp6_client_set_prefix_delegation_hint( sd_dhcp6_client *client, uint8_t prefixlen, - const struct in6_addr *pd_address); + const struct in6_addr *pd_prefix); int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation); int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, @@ -260,8 +260,6 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request); int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request); -int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, - uint32_t transaction_id); int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v); diff --git a/src/libnm-systemd-core/src/systemd/sd-id128.h b/src/libnm-systemd-core/src/systemd/sd-id128.h index e64f3200..f7d3244b 100644 --- a/src/libnm-systemd-core/src/systemd/sd-id128.h +++ b/src/libnm-systemd-core/src/systemd/sd-id128.h @@ -35,11 +35,14 @@ union sd_id128 { }; #define SD_ID128_STRING_MAX 33U +#define SD_ID128_UUID_STRING_MAX 37U char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]); +char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]); int sd_id128_from_string(const char *s, sd_id128_t *ret); #define SD_ID128_TO_STRING(id) sd_id128_to_string((id), (char[SD_ID128_STRING_MAX]) {}) +#define SD_ID128_TO_UUID_STRING(id) sd_id128_to_uuid_string((id), (char[SD_ID128_UUID_STRING_MAX]) {}) int sd_id128_randomize(sd_id128_t *ret); diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h index 65d51756..9dde770d 100644 --- a/src/libnm-systemd-shared/src/basic/alloc-util.h +++ b/src/libnm-systemd-shared/src/basic/alloc-util.h @@ -13,6 +13,7 @@ #endif typedef void (*free_func_t)(void *p); +typedef void* (*mfree_func_t)(void *p); /* If for some reason more than 4M are allocated on the stack, let's abort immediately. It's better than * proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */ @@ -173,13 +174,23 @@ void* greedy_realloc0(void **p, size_t need, size_t size); * is compatible with _FORTIFY_SOURCES. If _FORTIFY_SOURCES is used many memory operations will take the * object size as returned by __builtin_object_size() into account. Hence, let's return the smaller size of * malloc_usable_size() and __builtin_object_size() here, so that we definitely operate in safe territory by - * both the compiler's and libc's standards. Note that __builtin_object_size() evaluates to SIZE_MAX if the - * size cannot be determined, hence the MIN() expression should be safe with dynamically sized memory, - * too. Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and + * both the compiler's and libc's standards. Note that _FORTIFY_SOURCES=3 handles also dynamically allocated + * objects and thus it's safer using __builtin_dynamic_object_size if _FORTIFY_SOURCES=3 is used (#22801). + * Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and * __builtin_object_size() returns SIZE_MAX too, hence we also return a sensible value of 0 in this corner * case. */ + +#if defined __has_builtin +# if __has_builtin(__builtin_dynamic_object_size) +# define MALLOC_SIZEOF_SAFE(x) \ + MIN(malloc_usable_size(x), __builtin_dynamic_object_size(x, 0)) +# endif +#endif + +#ifndef MALLOC_SIZEOF_SAFE #define MALLOC_SIZEOF_SAFE(x) \ MIN(malloc_usable_size(x), __builtin_object_size(x, 0)) +#endif /* Inspired by ELEMENTSOF() but operates on malloc()'ed memory areas: typesafely returns the number of items * that fit into the specified memory block */ diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h index 461c01b3..4c413a8d 100644 --- a/src/libnm-systemd-shared/src/basic/cgroup-util.h +++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h @@ -205,6 +205,8 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path); int cg_rmdir(const char *controller, const char *path); +int cg_is_threaded(const char *controller, const char *path); + typedef enum { CG_KEY_MODE_GRACEFUL = 1 << 0, } CGroupKeyMode; diff --git a/src/libnm-systemd-shared/src/basic/env-file.c b/src/libnm-systemd-shared/src/basic/env-file.c index baab9bd2..61a9cd60 100644 --- a/src/libnm-systemd-shared/src/basic/env-file.c +++ b/src/libnm-systemd-shared/src/basic/env-file.c @@ -18,14 +18,12 @@ static int parse_env_file_internal( FILE *f, const char *fname, int (*push) (const char *filename, unsigned line, - const char *key, char *value, void *userdata, int *n_pushed), - void *userdata, - int *n_pushed) { + const char *key, char *value, void *userdata), + void *userdata) { size_t n_key = 0, n_value = 0, last_value_whitespace = SIZE_MAX, last_key_whitespace = SIZE_MAX; _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL; unsigned line = 1; - char *p; int r; enum { @@ -48,7 +46,7 @@ static int parse_env_file_internal( if (r < 0) return r; - for (p = contents; *p; p++) { + for (char *p = contents; *p; p++) { char c = *p; switch (state) { @@ -102,7 +100,7 @@ static int parse_env_file_internal( if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; - r = push(fname, line, key, value, userdata, n_pushed); + r = push(fname, line, key, value, userdata); if (r < 0) return r; @@ -145,7 +143,7 @@ static int parse_env_file_internal( if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; - r = push(fname, line, key, value, userdata, n_pushed); + r = push(fname, line, key, value, userdata); if (r < 0) return r; @@ -264,7 +262,7 @@ static int parse_env_file_internal( if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; - r = push(fname, line, key, value, userdata, n_pushed); + r = push(fname, line, key, value, userdata); if (r < 0) return r; @@ -302,8 +300,7 @@ static int check_utf8ness_and_warn( static int parse_env_file_push( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { + void *userdata) { const char *k; va_list aq, *ap = userdata; @@ -325,9 +322,6 @@ static int parse_env_file_push( free(*v); *v = value; - if (n_pushed) - (*n_pushed)++; - return 1; } } @@ -343,16 +337,13 @@ int parse_env_filev( const char *fname, va_list ap) { - int r, n_pushed = 0; + int r; va_list aq; va_copy(aq, ap); - r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed); + r = parse_env_file_internal(f, fname, parse_env_file_push, &aq); va_end(aq); - if (r < 0) - return r; - - return n_pushed; + return r; } int parse_env_file_sentinel( @@ -374,8 +365,7 @@ int parse_env_file_sentinel( static int load_env_file_push( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { + void *userdata) { char ***m = userdata; char *p; int r; @@ -392,78 +382,69 @@ static int load_env_file_push( if (r < 0) return r; - if (n_pushed) - (*n_pushed)++; - free(value); return 0; } int load_env_file(FILE *f, const char *fname, char ***rl) { - char **m = NULL; + _cleanup_strv_free_ char **m = NULL; int r; - r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL); - if (r < 0) { - strv_free(m); + r = parse_env_file_internal(f, fname, load_env_file_push, &m); + if (r < 0) return r; - } - *rl = m; + *rl = TAKE_PTR(m); return 0; } static int load_env_file_push_pairs( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; + void *userdata) { + + char ***m = ASSERT_PTR(userdata); int r; r = check_utf8ness_and_warn(filename, line, key, value); if (r < 0) return r; + /* Check if the key is present */ + for (char **t = *m; t && *t; t += 2) + if (streq(t[0], key)) { + if (value) + return free_and_replace(t[1], value); + else + return free_and_strdup(t+1, ""); + } + r = strv_extend(m, key); if (r < 0) - return -ENOMEM; - - if (!value) { - r = strv_extend(m, ""); - if (r < 0) - return -ENOMEM; - } else { - r = strv_push(m, value); - if (r < 0) - return r; - } - - if (n_pushed) - (*n_pushed)++; + return r; - return 0; + if (value) + return strv_push(m, value); + else + return strv_extend(m, ""); } int load_env_file_pairs(FILE *f, const char *fname, char ***rl) { - char **m = NULL; + _cleanup_strv_free_ char **m = NULL; int r; - r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL); - if (r < 0) { - strv_free(m); + r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m); + if (r < 0) return r; - } - *rl = m; + *rl = TAKE_PTR(m); return 0; } static int merge_env_file_push( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { + void *userdata) { char ***env = userdata; char *expanded_value; @@ -492,7 +473,7 @@ static int merge_env_file_push( log_debug("%s:%u: setting %s=%s", filename, line, key, value); - return load_env_file_push(filename, line, key, value, env, n_pushed); + return load_env_file_push(filename, line, key, value, env); } int merge_env_file( @@ -504,7 +485,7 @@ int merge_env_file( * plus "extended" substitutions, unlike other exported parsing functions. */ - return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL); + return parse_env_file_internal(f, fname, merge_env_file_push, env); } static void write_env_var(FILE *f, const char *v) { @@ -541,7 +522,6 @@ static void write_env_var(FILE *f, const char *v) { int write_env_file(const char *fname, char **l) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; - char **i; int r; assert(fname); diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c index f47ddefe..b4ac6811 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.c +++ b/src/libnm-systemd-shared/src/basic/env-util.c @@ -99,8 +99,6 @@ bool env_assignment_is_valid(const char *e) { } bool strv_env_is_valid(char **e) { - char **p, **q; - STRV_FOREACH(p, e) { size_t k; @@ -118,8 +116,6 @@ bool strv_env_is_valid(char **e) { } bool strv_env_name_is_valid(char **l) { - char **p; - STRV_FOREACH(p, l) { if (!env_name_is_valid(*p)) return false; @@ -132,8 +128,6 @@ bool strv_env_name_is_valid(char **l) { } bool strv_env_name_or_assignment_is_valid(char **l) { - char **p; - STRV_FOREACH(p, l) { if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p)) return false; @@ -275,7 +269,7 @@ static bool env_entry_has_name(const char *entry, const char *name) { char **strv_env_delete(char **x, size_t n_lists, ...) { size_t n, i = 0; - char **k, **r; + char **r; va_list ap; /* Deletes every entry from x that is mentioned in the other @@ -290,7 +284,7 @@ char **strv_env_delete(char **x, size_t n_lists, ...) { STRV_FOREACH(k, x) { va_start(ap, n_lists); for (size_t v = 0; v < n_lists; v++) { - char **l, **j; + char **l; l = va_arg(ap, char**); STRV_FOREACH(j, l) @@ -382,7 +376,6 @@ char **strv_env_unset_many(char **l, ...) { int strv_env_replace_consume(char ***l, char *p) { const char *t, *name; - char **f; int r; assert(p); @@ -470,8 +463,6 @@ int strv_env_assign(char ***l, const char *key, const char *value) { } char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) { - char **i; - assert(name); if (k <= 0) @@ -499,7 +490,7 @@ char *strv_env_get(char **l, const char *name) { } char *strv_env_pairs_get(char **l, const char *name) { - char **key, **value, *result = NULL; + char *result = NULL; assert(name); @@ -511,7 +502,6 @@ char *strv_env_pairs_get(char **l, const char *name) { } char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) { - char **p, **q; int k = 0; STRV_FOREACH(p, e) { @@ -705,7 +695,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) { } char **replace_env_argv(char **argv, char **env) { - char **ret, **i; + char **ret; size_t k = 0, l = 0; l = strv_length(argv); @@ -837,7 +827,6 @@ int setenv_systemd_exec_pid(bool update_only) { int getenv_path_list(const char *name, char ***ret_paths) { _cleanup_strv_free_ char **l = NULL; const char *e; - char **p; int r; assert(name); @@ -873,20 +862,37 @@ int getenv_path_list(const char *name, char ***ret_paths) { return 1; } -int unsetenv_erase(const char *name) { - char *p; +int getenv_steal_erase(const char *name, char **ret) { + _cleanup_(erase_and_freep) char *a = NULL; + char *e; assert(name); - p = getenv(name); - if (!p) + /* Reads an environment variable, makes a copy of it, erases its memory in the environment block and removes + * it from there. Usecase: reading passwords from the env block (which is a bad idea, but useful for + * testing, and given that people are likely going to misuse this, be thorough) */ + + e = getenv(name); + if (!e) { + if (ret) + *ret = NULL; return 0; + } + + if (ret) { + a = strdup(e); + if (!a) + return -ENOMEM; + } - string_erase(p); + string_erase(e); if (unsetenv(name) < 0) return -errno; + if (ret) + *ret = TAKE_PTR(a); + return 1; } #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/env-util.h b/src/libnm-systemd-shared/src/basic/env-util.h index 38bfc8a3..2bf0603f 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.h +++ b/src/libnm-systemd-shared/src/basic/env-util.h @@ -69,4 +69,4 @@ int setenv_systemd_exec_pid(bool update_only); * PATH-like colon-separated absolute paths */ int getenv_path_list(const char *name, char ***ret_paths); -int unsetenv_erase(const char *name); +int getenv_steal_erase(const char *name, char **ret); diff --git a/src/libnm-systemd-shared/src/basic/errno-util.h b/src/libnm-systemd-shared/src/basic/errno-util.h index 09abf0b7..648de50e 100644 --- a/src/libnm-systemd-shared/src/basic/errno-util.h +++ b/src/libnm-systemd-shared/src/basic/errno-util.h @@ -138,10 +138,18 @@ static inline bool ERRNO_IS_PRIVILEGE(int r) { EPERM); } -/* Three difference errors for "not enough disk space" */ +/* Three different errors for "not enough disk space" */ static inline bool ERRNO_IS_DISK_SPACE(int r) { return IN_SET(abs(r), ENOSPC, EDQUOT, EFBIG); } + +/* Three different errors for "this device does not quite exist" */ +static inline bool ERRNO_IS_DEVICE_ABSENT(int r) { + return IN_SET(abs(r), + ENODEV, + ENXIO, + ENOENT); +} diff --git a/src/libnm-systemd-shared/src/basic/escape.c b/src/libnm-systemd-shared/src/basic/escape.c index 4dfb9a16..cc317dfb 100644 --- a/src/libnm-systemd-shared/src/basic/escape.c +++ b/src/libnm-systemd-shared/src/basic/escape.c @@ -552,7 +552,6 @@ char* quote_command_line(char **argv, ShellEscapeFlags flags) { assert(argv); - char **a; STRV_FOREACH(a, argv) { _cleanup_free_ char *t = NULL; diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c index c3df68de..53efeb40 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.c +++ b/src/libnm-systemd-shared/src/basic/fd-util.c @@ -5,6 +5,7 @@ #include <errno.h> #include <fcntl.h> #include <linux/btrfs.h> +#include <linux/fs.h> #include <linux/magic.h> #include <sys/ioctl.h> #include <sys/resource.h> @@ -19,6 +20,7 @@ #include "io-util.h" #include "macro.h" #include "missing_fcntl.h" +#include "missing_fs.h" #include "missing_syscall.h" #include "parse-util.h" #include "path-util.h" @@ -420,14 +422,11 @@ int same_fd(int a, int b) { assert(a >= 0); assert(b >= 0); - /* Compares two file descriptors. Note that semantics are - * quite different depending on whether we have kcmp() or we - * don't. If we have kcmp() this will only return true for - * dup()ed file descriptors, but not otherwise. If we don't - * have kcmp() this will also return true for two fds of the same - * file, created by separate open() calls. Since we use this - * call mostly for filtering out duplicates in the fd store - * this difference hopefully doesn't matter too much. */ + /* Compares two file descriptors. Note that semantics are quite different depending on whether we + * have kcmp() or we don't. If we have kcmp() this will only return true for dup()ed file + * descriptors, but not otherwise. If we don't have kcmp() this will also return true for two fds of + * the same file, created by separate open() calls. Since we use this call mostly for filtering out + * duplicates in the fd store this difference hopefully doesn't matter too much. */ if (a == b) return true; @@ -439,7 +438,7 @@ int same_fd(int a, int b) { return true; if (r > 0) return false; - if (!IN_SET(errno, ENOSYS, EACCES, EPERM)) + if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) return -errno; /* We don't have kcmp(), use fstat() instead. */ @@ -449,23 +448,17 @@ int same_fd(int a, int b) { if (fstat(b, &stb) < 0) return -errno; - if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT)) + if (!stat_inode_same(&sta, &stb)) return false; - /* We consider all device fds different, since two device fds - * might refer to quite different device contexts even though - * they share the same inode and backing dev_t. */ + /* We consider all device fds different, since two device fds might refer to quite different device + * contexts even though they share the same inode and backing dev_t. */ if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode)) return false; - if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino) - return false; - - /* The fds refer to the same inode on disk, let's also check - * if they have the same fd flags. This is useful to - * distinguish the read and write side of a pipe created with - * pipe(). */ + /* The fds refer to the same inode on disk, let's also check if they have the same fd flags. This is + * useful to distinguish the read and write side of a pipe created with pipe(). */ fa = fcntl(a, F_GETFL); if (fa < 0) return -errno; @@ -804,4 +797,25 @@ int btrfs_defrag_fd(int fd) { return RET_NERRNO(ioctl(fd, BTRFS_IOC_DEFRAG, NULL)); } + +int fd_get_diskseq(int fd, uint64_t *ret) { + uint64_t diskseq; + + assert(fd >= 0); + assert(ret); + + if (ioctl(fd, BLKGETDISKSEQ, &diskseq) < 0) { + /* Note that the kernel is weird: non-existing ioctls currently return EINVAL + * rather than ENOTTY on loopback block devices. They should fix that in the kernel, + * but in the meantime we accept both here. */ + if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EINVAL) + return -errno; + + return -EOPNOTSUPP; + } + + *ret = diskseq; + + return 0; +} #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h index f5cfcb4e..808cac4d 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.h +++ b/src/libnm-systemd-shared/src/basic/fd-util.h @@ -109,6 +109,7 @@ static inline int make_null_stdio(void) { int fd_reopen(int fd, int flags); int read_nr_open(void); int btrfs_defrag_fd(int fd); +int fd_get_diskseq(int fd, uint64_t *ret); /* The maximum length a buffer for a /proc/self/fd/<fd> path needs */ #define PROC_FD_PATH_MAX \ diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c index c66bc67b..0ad8ae03 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.c +++ b/src/libnm-systemd-shared/src/basic/fileio.c @@ -1049,8 +1049,6 @@ static int search_and_fopen_internal( FILE **ret, char **ret_path) { - char **i; - assert(path); assert(mode); assert(ret); @@ -1204,7 +1202,7 @@ int write_timestamp_file_atomic(const char *fn, usec_t n) { /* Creates a "timestamp" file, that contains nothing but a * usec_t timestamp, formatted in ASCII. */ - if (n <= 0 || n >= USEC_INFINITY) + if (!timestamp_is_set(n)) return -ERANGE; xsprintf(ln, USEC_FMT "\n", n); @@ -1225,7 +1223,7 @@ int read_timestamp_file(const char *fn, usec_t *ret) { if (r < 0) return r; - if (t <= 0 || t >= (uint64_t) USEC_INFINITY) + if (!timestamp_is_set(t)) return -ERANGE; *ret = (usec_t) t; diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h index cea3dd89..9151d823 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.h +++ b/src/libnm-systemd-shared/src/basic/fileio.h @@ -110,6 +110,12 @@ typedef enum ReadLineFlags { int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret); +static inline bool file_offset_beyond_memory_size(off_t x) { + if (x < 0) /* off_t is signed, filter that out */ + return false; + return (uint64_t) x > (uint64_t) SIZE_MAX; +} + static inline int read_line(FILE *f, size_t limit, char **ret) { return read_line_full(f, limit, 0, ret); } diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c index b524a6fd..dccfb27b 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.c +++ b/src/libnm-systemd-shared/src/basic/fs-util.c @@ -123,7 +123,6 @@ int readlinkat_malloc(int fd, const char *p, char **ret) { size_t l = PATH_MAX; assert(p); - assert(ret); for (;;) { _cleanup_free_ char *c = NULL; @@ -139,7 +138,10 @@ int readlinkat_malloc(int fd, const char *p, char **ret) { if ((size_t) n < l) { c[n] = 0; - *ret = TAKE_PTR(c); + + if (ret) + *ret = TAKE_PTR(c); + return 0; } @@ -576,7 +578,6 @@ int get_files_in_directory(const char *path, char ***list) { #endif /* NM_IGNORED */ static int getenv_tmp_dir(const char **ret_path) { - const char *n; int r, ret = 0; assert(ret_path); @@ -866,8 +867,7 @@ int conservative_renameat( if (fstat(new_fd, &new_stat) < 0) goto do_rename; - if (new_stat.st_ino == old_stat.st_ino && - new_stat.st_dev == old_stat.st_dev) + if (stat_inode_same(&new_stat, &old_stat)) goto is_same; if (old_stat.st_mode != new_stat.st_mode || @@ -1094,3 +1094,50 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) { return TAKE_FD(fd); } + +int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created) { + unsigned attempts = 7; + int fd; + + /* Just like openat(), but adds one thing: optionally returns whether we created the file anew or if + * it already existed before. This is only relevant if O_CREAT is set without O_EXCL, and thus will + * shortcut to openat() otherwise */ + + if (!ret_newly_created) + return RET_NERRNO(openat(dirfd, pathname, flags, mode)); + + if (!FLAGS_SET(flags, O_CREAT) || FLAGS_SET(flags, O_EXCL)) { + fd = openat(dirfd, pathname, flags, mode); + if (fd < 0) + return -errno; + + *ret_newly_created = FLAGS_SET(flags, O_CREAT); + return fd; + } + + for (;;) { + /* First, attempt to open without O_CREAT/O_EXCL, i.e. open existing file */ + fd = openat(dirfd, pathname, flags & ~(O_CREAT | O_EXCL), mode); + if (fd >= 0) { + *ret_newly_created = false; + return fd; + } + if (errno != ENOENT) + return -errno; + + /* So the file didn't exist yet, hence create it with O_CREAT/O_EXCL. */ + fd = openat(dirfd, pathname, flags | O_CREAT | O_EXCL, mode); + if (fd >= 0) { + *ret_newly_created = true; + return fd; + } + if (errno != EEXIST) + return -errno; + + /* Hmm, so now we got EEXIST? So it apparently exists now? If so, let's try to open again + * without the two flags. But let's not spin forever, hence put a limit on things */ + + if (--attempts == 0) /* Give up eventually, somebody is playing with us */ + return -EEXIST; + } +} diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h index 0bbb3f62..e48cf680 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.h +++ b/src/libnm-systemd-shared/src/basic/fs-util.h @@ -110,3 +110,5 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size); int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char **ret_path); int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode); + +int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created); diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c index ee2a12e8..e7dedc6d 100644 --- a/src/libnm-systemd-shared/src/basic/hashmap.c +++ b/src/libnm-systemd-shared/src/basic/hashmap.c @@ -164,7 +164,7 @@ struct _packed_ indirect_storage { unsigned n_buckets; /* number of buckets */ unsigned idx_lowest_entry; /* Index below which all buckets are free. - Makes "while(hashmap_steal_first())" loops + Makes "while (hashmap_steal_first())" loops O(n) instead of O(n^2) for unordered hashmaps. */ uint8_t _pad[3]; /* padding for the whole HashmapBase */ /* The bitfields in HashmapBase complete the alignment of the whole thing. */ @@ -1868,7 +1868,6 @@ int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l HASHMAP_DEBUG_PARAMS) { int n = 0, r; - char **i; assert(s); diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.c b/src/libnm-systemd-shared/src/basic/hexdecoct.c index 381e79ba..570da688 100644 --- a/src/libnm-systemd-shared/src/basic/hexdecoct.c +++ b/src/libnm-systemd-shared/src/basic/hexdecoct.c @@ -688,8 +688,7 @@ static int base64_append_width( s += indent; } - memcpy(s, x + width * line, act); - s += act; + s = mempcpy(s, x + width * line, act); *(s++) = line < lines - 1 ? '\n' : '\0'; avail -= act; } diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.c b/src/libnm-systemd-shared/src/basic/hostname-util.c index 1d1c2f51..692733ac 100644 --- a/src/libnm-systemd-shared/src/basic/hostname-util.c +++ b/src/libnm-systemd-shared/src/basic/hostname-util.c @@ -10,6 +10,7 @@ #include <unistd.h> #include "alloc-util.h" +#include "env-file.h" #include "hostname-util.h" #include "os-util.h" #include "string-util.h" @@ -197,3 +198,20 @@ bool is_localhost(const char *hostname) { endswith_no_case(hostname, ".localhost.localdomain") || endswith_no_case(hostname, ".localhost.localdomain."); } + +int get_pretty_hostname(char **ret) { + _cleanup_free_ char *n = NULL; + int r; + + assert(ret); + + r = parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", &n); + if (r < 0) + return r; + + if (isempty(n)) + return -ENXIO; + + *ret = TAKE_PTR(n); + return 0; +} diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.h b/src/libnm-systemd-shared/src/basic/hostname-util.h index d435bed5..a00b8523 100644 --- a/src/libnm-systemd-shared/src/basic/hostname-util.h +++ b/src/libnm-systemd-shared/src/basic/hostname-util.h @@ -4,7 +4,6 @@ #include <stdbool.h> #include <stdio.h> -#include "env-file.h" #include "macro.h" #include "strv.h" @@ -61,6 +60,4 @@ static inline bool is_outbound_hostname(const char *hostname) { return STRCASE_IN_SET(hostname, "_outbound", "_outbound."); } -static inline int get_pretty_hostname(char **ret) { - return parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", ret); -} +int get_pretty_hostname(char **ret); diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.c b/src/libnm-systemd-shared/src/basic/in-addr-util.c index 1bf0764d..51a6b165 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.c +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c @@ -123,6 +123,19 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) { return -EAFNOSUPPORT; } +int in_addr_is_localhost_one(int family, const union in_addr_union *u) { + assert(u); + + if (family == AF_INET) + /* 127.0.0.1 */ + return be32toh(u->in.s_addr) == UINT32_C(0x7F000001); + + if (family == AF_INET6) + return IN6_IS_ADDR_LOOPBACK(&u->in6); /* lgtm [cpp/potentially-dangerous-function] */ + + return -EAFNOSUPPORT; +} + bool in6_addr_is_ipv4_mapped_address(const struct in6_addr *a) { return a->s6_addr32[0] == 0 && a->s6_addr32[1] == 0 && diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.h b/src/libnm-systemd-shared/src/basic/in-addr-util.h index 0178391e..5de87a95 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.h +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h @@ -49,6 +49,7 @@ bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a); bool in4_addr_is_localhost(const struct in_addr *a); int in_addr_is_localhost(int family, const union in_addr_union *u); +int in_addr_is_localhost_one(int family, const union in_addr_union *u); bool in4_addr_is_local_multicast(const struct in_addr *a); bool in4_addr_is_non_local(const struct in_addr *a); diff --git a/src/libnm-systemd-shared/src/basic/inotify-util.h b/src/libnm-systemd-shared/src/basic/inotify-util.h index 88af0868..61951ff3 100644 --- a/src/libnm-systemd-shared/src/basic/inotify-util.h +++ b/src/libnm-systemd-shared/src/basic/inotify-util.h @@ -6,12 +6,28 @@ #include <stddef.h> #include <sys/inotify.h> +#include "log.h" + #define INOTIFY_EVENT_MAX (offsetof(struct inotify_event, name) + NAME_MAX + 1) -#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ - for ((e) = &buffer.ev; \ - (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \ - (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len)) +#define _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, start, end) \ + for (struct inotify_event \ + *start = &((buffer).ev), \ + *end = (struct inotify_event*) ((uint8_t*) start + (sz)), \ + *e = start; \ + (size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) && \ + ((size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) + e->len || \ + (log_full(log_level, "Received invalid inotify event, ignoring."), false)); \ + e = (struct inotify_event*) ((uint8_t*) e + sizeof(struct inotify_event) + e->len)) + +#define _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, log_level) \ + _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, UNIQ_T(start, UNIQ), UNIQ_T(end, UNIQ)) + +#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ + _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_DEBUG) + +#define FOREACH_INOTIFY_EVENT_WARN(e, buffer, sz) \ + _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_WARNING) union inotify_event_buffer { struct inotify_event ev; diff --git a/src/libnm-systemd-shared/src/basic/io-util.c b/src/libnm-systemd-shared/src/basic/io-util.c index d5cbb899..8a2b9c2f 100644 --- a/src/libnm-systemd-shared/src/basic/io-util.c +++ b/src/libnm-systemd-shared/src/basic/io-util.c @@ -165,7 +165,6 @@ int pipe_eof(int fd) { #endif /* NM_IGNORED */ int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) { - struct timespec ts; int r; assert(fds || nfds == 0); @@ -173,7 +172,7 @@ int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) { if (nfds == 0) return 0; - r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : timespec_store(&ts, timeout), NULL); + r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : TIMESPEC_STORE(timeout), NULL); if (r < 0) return -errno; if (r == 0) diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h index f827e721..58e83a6c 100644 --- a/src/libnm-systemd-shared/src/basic/list.h +++ b/src/libnm-systemd-shared/src/basic/list.h @@ -136,33 +136,39 @@ #define LIST_JUST_US(name,item) \ (!(item)->name##_prev && !(item)->name##_next) +/* The type of the iterator 'i' is automatically determined by the type of 'head', and declared in the + * loop. Hence, do not declare the same variable in the outer scope. Sometimes, we set 'head' through + * hashmap_get(). In that case, you need to explicitly cast the result. */ +#define LIST_FOREACH_WITH_NEXT(name,i,n,head) \ + for (typeof(*(head)) *n, *i = (head); i && (n = i->name##_next, true); i = n) + #define LIST_FOREACH(name,i,head) \ - for ((i) = (head); (i); (i) = (i)->name##_next) + LIST_FOREACH_WITH_NEXT(name, i, UNIQ_T(n, UNIQ), head) -#define LIST_FOREACH_SAFE(name,i,n,head) \ - for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n)) +#define _LIST_FOREACH_WITH_PREV(name,i,p,start) \ + for (typeof(*(start)) *p, *i = (start); i && (p = i->name##_prev, true); i = p) -#define LIST_FOREACH_BACKWARDS(name,i,p) \ - for ((i) = (p); (i); (i) = (i)->name##_prev) +#define LIST_FOREACH_BACKWARDS(name,i,start) \ + _LIST_FOREACH_WITH_PREV(name, i, UNIQ_T(p, UNIQ), start) /* Iterate through all the members of the list p is included in, but skip over p */ #define LIST_FOREACH_OTHERS(name,i,p) \ - for (({ \ - (i) = (p); \ - while ((i) && (i)->name##_prev) \ - (i) = (i)->name##_prev; \ - if ((i) == (p)) \ - (i) = (p)->name##_next; \ - }); \ - (i); \ - (i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next) - -/* Loop starting from p->next until p->prev. - p can be adjusted meanwhile. */ + for (typeof(*(p)) *_p = (p), *i = ({ \ + typeof(*_p) *_j = _p; \ + while (_j && _j->name##_prev) \ + _j = _j->name##_prev; \ + if (_j == _p) \ + _j = _p->name##_next; \ + _j; \ + }); \ + i; \ + i = i->name##_next == _p ? _p->name##_next : i->name##_next) + +/* Loop starting from p->next until p->prev. p can be adjusted meanwhile. */ #define LIST_LOOP_BUT_ONE(name,i,head,p) \ - for ((i) = (p)->name##_next ? (p)->name##_next : (head); \ - (i) != (p); \ - (i) = (i)->name##_next ? (i)->name##_next : (head)) + for (typeof(*(p)) *i = (p)->name##_next ? (p)->name##_next : (head); \ + i != (p); \ + i = i->name##_next ? i->name##_next : (head)) #define LIST_IS_EMPTY(head) \ (!(head)) diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h index 88d278fd..6361eb0f 100644 --- a/src/libnm-systemd-shared/src/basic/log.h +++ b/src/libnm-systemd-shared/src/basic/log.h @@ -409,9 +409,11 @@ int log_emergency_level(void); }) #if LOG_TRACE -# define log_trace(...) log_debug(__VA_ARGS__) +# define log_trace(...) log_debug(__VA_ARGS__) +# define log_trace_errno(...) log_debug_errno(__VA_ARGS__) #else -# define log_trace(...) do {} while (0) +# define log_trace(...) do {} while (0) +# define log_trace_errno(e, ...) (-ERRNO_VALUE(e)) #endif /* Structured logging */ diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h index 79437775..41d83b3e 100644 --- a/src/libnm-systemd-shared/src/basic/macro.h +++ b/src/libnm-systemd-shared/src/basic/macro.h @@ -318,23 +318,32 @@ static inline int __coverity_check_and_return__(int condition) { #define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member) -/* Returns the number of chars needed to format variables of the - * specified type as a decimal string. Adds in extra space for a - * negative '-' prefix (hence works correctly on signed - * types). Includes space for the trailing NUL. */ +/* Returns the number of chars needed to format variables of the specified type as a decimal string. Adds in + * extra space for a negative '-' prefix for signed types. Includes space for the trailing NUL. */ #define DECIMAL_STR_MAX(type) \ - (2U+(sizeof(type) <= 1 ? 3U : \ + ((size_t) IS_SIGNED_INTEGER_TYPE(type) + 1U + \ + (sizeof(type) <= 1 ? 3U : \ sizeof(type) <= 2 ? 5U : \ sizeof(type) <= 4 ? 10U : \ - sizeof(type) <= 8 ? 20U : sizeof(int[-2*(sizeof(type) > 8)]))) - -#define DECIMAL_STR_WIDTH(x) \ - ({ \ - typeof(x) _x_ = (x); \ - size_t ans = 1; \ - while ((_x_ /= 10) != 0) \ - ans++; \ - ans; \ + sizeof(type) <= 8 ? (IS_SIGNED_INTEGER_TYPE(type) ? 19U : 20U) : sizeof(int[-2*(sizeof(type) > 8)]))) + +/* Returns the number of chars needed to format the specified integer value. It's hence more specific than + * DECIMAL_STR_MAX() which answers the same question for all possible values of the specified type. Does + * *not* include space for a trailing NUL. (If you wonder why we special case _x_ == 0 here: it's to trick + * out gcc's -Wtype-limits, which would complain on comparing an unsigned type with < 0, otherwise. By + * special-casing == 0 here first, we can use <= 0 instead of < 0 to trick out gcc.) */ +#define DECIMAL_STR_WIDTH(x) \ + ({ \ + typeof(x) _x_ = (x); \ + size_t ans; \ + if (_x_ == 0) \ + ans = 1; \ + else { \ + ans = _x_ <= 0 ? 2 : 1; \ + while ((_x_ /= 10) != 0) \ + ans++; \ + } \ + ans; \ }) #define SWAP_TWO(x, y) do { \ @@ -463,4 +472,20 @@ typedef struct { assert_cc(sizeof(dummy_t) == 0); +/* A little helper for subtracting 1 off a pointer in a safe UB-free way. This is intended to be used for for + * loops that count down from a high pointer until some base. A naive loop would implement this like this: + * + * for (p = end-1; p >= base; p--) … + * + * But this is not safe because p before the base is UB in C. With this macro the loop becomes this instead: + * + * for (p = PTR_SUB1(end, base); p; p = PTR_SUB1(p, base)) … + * + * And is free from UB! */ +#define PTR_SUB1(p, base) \ + ({ \ + typeof(p) _q = (p); \ + _q && _q > (base) ? &_q[-1] : NULL; \ + }) + #include "log.h" diff --git a/src/libnm-systemd-shared/src/basic/memory-util.h b/src/libnm-systemd-shared/src/basic/memory-util.h index 9f37431f..6e3280b9 100644 --- a/src/libnm-systemd-shared/src/basic/memory-util.h +++ b/src/libnm-systemd-shared/src/basic/memory-util.h @@ -15,7 +15,7 @@ size_t page_size(void) _pure_; #define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1)) #define PAGE_OFFSET(l) ((l) & (page_size() - 1)) -/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */ +/* Normal memcpy() requires src to be nonnull. We do nothing if n is 0. */ static inline void *memcpy_safe(void *dst, const void *src, size_t n) { if (n == 0) return dst; @@ -23,7 +23,15 @@ static inline void *memcpy_safe(void *dst, const void *src, size_t n) { return memcpy(dst, src, n); } -/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */ +/* Normal mempcpy() requires src to be nonnull. We do nothing if n is 0. */ +static inline void *mempcpy_safe(void *dst, const void *src, size_t n) { + if (n == 0) + return dst; + assert(src); + return mempcpy(dst, src, n); +} + +/* Normal memcmp() requires s1 and s2 to be nonnull. We do nothing if n is 0. */ static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { if (n == 0) return 0; diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h index 41c83ec5..e5bdc0e1 100644 --- a/src/libnm-systemd-shared/src/basic/missing_syscall.h +++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h @@ -574,6 +574,10 @@ static inline int missing_open_tree( #define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */ #endif +#ifndef MOVE_MOUNT_T_EMPTY_PATH +#define MOVE_MOUNT_T_EMPTY_PATH 0x00000040 /* Empty to path permitted */ +#endif + static inline int missing_move_mount( int from_dfd, const char *from_pathname, diff --git a/src/libnm-systemd-shared/src/basic/ordered-set.c b/src/libnm-systemd-shared/src/basic/ordered-set.c index 62a36b5d..f402bb5b 100644 --- a/src/libnm-systemd-shared/src/basic/ordered-set.c +++ b/src/libnm-systemd-shared/src/basic/ordered-set.c @@ -60,7 +60,6 @@ int _ordered_set_put_strdup(OrderedSet **s, const char *p HASHMAP_DEBUG_PARAMS) int _ordered_set_put_strdupv(OrderedSet **s, char **l HASHMAP_DEBUG_PARAMS) { int n = 0, r; - char **i; STRV_FOREACH(i, l) { r = _ordered_set_put_strdup(s, *i HASHMAP_DEBUG_PASS_ARGS); diff --git a/src/libnm-systemd-shared/src/basic/ordered-set.h b/src/libnm-systemd-shared/src/basic/ordered-set.h index 3ee47350..c0650e01 100644 --- a/src/libnm-systemd-shared/src/basic/ordered-set.h +++ b/src/libnm-systemd-shared/src/basic/ordered-set.h @@ -18,6 +18,14 @@ int _ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops HA int _ordered_set_ensure_put(OrderedSet **s, const struct hash_ops *ops, void *p HASHMAP_DEBUG_PARAMS); #define ordered_set_ensure_put(s, hash_ops, key) _ordered_set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS) +static inline void ordered_set_clear(OrderedSet *s) { + return ordered_hashmap_clear((OrderedHashmap*) s); +} + +static inline void ordered_set_clear_free(OrderedSet *s) { + return ordered_hashmap_clear_free((OrderedHashmap*) s); +} + static inline OrderedSet* ordered_set_free(OrderedSet *s) { return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s); } diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c index 4ba2657c..0a623c51 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.c +++ b/src/libnm-systemd-shared/src/basic/parse-util.c @@ -421,7 +421,7 @@ int safe_atoi(const char *s, int *ret_i) { return 0; } -int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) { +int safe_atollu_full(const char *s, unsigned base, unsigned long long *ret_llu) { char *x = NULL; unsigned long long l; diff --git a/src/libnm-systemd-shared/src/basic/parse-util.h b/src/libnm-systemd-shared/src/basic/parse-util.h index 3dc5e140..82731246 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.h +++ b/src/libnm-systemd-shared/src/basic/parse-util.h @@ -65,9 +65,9 @@ static inline int safe_atoi32(const char *s, int32_t *ret_i) { return safe_atoi(s, (int*) ret_i); } -int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu); +int safe_atollu_full(const char *s, unsigned base, unsigned long long *ret_llu); -static inline int safe_atollu(const char *s, long long unsigned *ret_llu) { +static inline int safe_atollu(const char *s, unsigned long long *ret_llu) { return safe_atollu_full(s, 0, ret_llu); } @@ -82,12 +82,12 @@ static inline int safe_atoi64(const char *s, int64_t *ret_i) { } static inline int safe_atoux64(const char *s, uint64_t *ret) { - assert_cc(sizeof(int64_t) == sizeof(long long unsigned)); - return safe_atollu_full(s, 16, (long long unsigned*) ret); + assert_cc(sizeof(int64_t) == sizeof(unsigned long long)); + return safe_atollu_full(s, 16, (unsigned long long*) ret); } #if LONG_MAX == INT_MAX -static inline int safe_atolu_full(const char *s, unsigned base, long unsigned *ret_u) { +static inline int safe_atolu_full(const char *s, unsigned base, unsigned long *ret_u) { assert_cc(sizeof(unsigned long) == sizeof(unsigned)); return safe_atou_full(s, base, (unsigned*) ret_u); } @@ -117,7 +117,7 @@ static inline int safe_atozu(const char *s, size_t *ret_u) { } #else static inline int safe_atozu(const char *s, size_t *ret_u) { - assert_cc(sizeof(size_t) == sizeof(long unsigned)); + assert_cc(sizeof(size_t) == sizeof(unsigned long)); return safe_atolu(s, ret_u); } #endif diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c index f391848f..f6088458 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.c +++ b/src/libnm-systemd-shared/src/basic/path-util.c @@ -66,7 +66,7 @@ char *path_make_absolute(const char *p, const char *prefix) { } int safe_getcwd(char **ret) { - char *cwd; + _cleanup_free_ char *cwd = NULL; cwd = get_current_dir_name(); if (!cwd) @@ -74,12 +74,12 @@ int safe_getcwd(char **ret) { /* Let's make sure the directory is really absolute, to protect us from the logic behind * CVE-2018-1000001 */ - if (cwd[0] != '/') { - free(cwd); + if (cwd[0] != '/') return -ENOMEDIUM; - } - *ret = cwd; + if (ret) + *ret = TAKE_PTR(cwd); + return 0; } @@ -205,9 +205,9 @@ int path_make_relative(const char *from, const char *to, char **ret) { } char* path_startswith_strv(const char *p, char **set) { - char **s, *t; - STRV_FOREACH(s, set) { + char *t; + t = path_startswith(p, *s); if (t) return t; @@ -217,7 +217,6 @@ char* path_startswith_strv(const char *p, char **set) { } int path_strv_make_absolute_cwd(char **l) { - char **s; int r; /* Goes through every item in the string list and makes it @@ -239,7 +238,6 @@ int path_strv_make_absolute_cwd(char **l) { } char **path_strv_resolve(char **l, const char *root) { - char **s; unsigned k = 0; bool enomem = false; int r; @@ -707,12 +705,12 @@ int find_executable_full(const char *name, const char *root, char **exec_search_ p = DEFAULT_PATH; if (exec_search_path) { - char **element; - STRV_FOREACH(element, exec_search_path) { _cleanup_free_ char *full_path = NULL; + if (!path_is_absolute(*element)) continue; + full_path = path_join(*element, name); if (!full_path) return -ENOMEM; @@ -761,7 +759,6 @@ int find_executable_full(const char *name, const char *root, char **exec_search_ bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) { bool changed = false, originally_unset; - const char* const* i; assert(timestamp); @@ -940,8 +937,9 @@ int path_find_first_component(const char **p, bool accept_dot_dot, const char ** static const char *skip_slash_or_dot_backward(const char *path, const char *q) { assert(path); + assert(!q || q >= path); - for (; q >= path; q--) { + for (; q; q = PTR_SUB1(q, path)) { if (*q == '/') continue; if (q > path && strneq(q - 1, "/.", 2)) @@ -1006,7 +1004,7 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char * q = path + strlen(path) - 1; q = skip_slash_or_dot_backward(path, q); - if ((q < path) || /* the root directory */ + if (!q || /* the root directory */ (q == path && *q == '.')) { /* path is "." or "./" */ if (next) *next = path; @@ -1017,10 +1015,10 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char * last_end = q + 1; - while (q >= path && *q != '/') - q--; + while (q && *q != '/') + q = PTR_SUB1(q, path); - last_begin = q + 1; + last_begin = q ? q + 1 : path; len = last_end - last_begin; if (len > NAME_MAX) @@ -1030,10 +1028,7 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char * if (next) { q = skip_slash_or_dot_backward(path, q); - if (q < path) - *next = path; - else - *next = q + 1; + *next = q ? q + 1 : path; } if (ret) @@ -1249,9 +1244,10 @@ bool hidden_or_backup_file(const char *filename) { assert(filename); if (filename[0] == '.' || - streq(filename, "lost+found") || - streq(filename, "aquota.user") || - streq(filename, "aquota.group") || + STR_IN_SET(filename, + "lost+found", + "aquota.user", + "aquota.group") || endswith(filename, "~")) return true; @@ -1348,7 +1344,7 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version) _cleanup_strv_free_ char **names = NULL; _cleanup_free_ char *path = NULL; - char *c, **name; + char *c; path = path_join(root, pattern); if (!path) @@ -1422,8 +1418,6 @@ bool empty_or_root(const char *path) { } bool path_strv_contains(char **l, const char *path) { - char **i; - STRV_FOREACH(i, l) if (path_equal(*i, path)) return true; @@ -1432,10 +1426,9 @@ bool path_strv_contains(char **l, const char *path) { } bool prefixed_path_strv_contains(char **l, const char *path) { - char **i, *j; - STRV_FOREACH(i, l) { - j = *i; + const char *j = *i; + if (*j == '-') j++; if (*j == '+') diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c index 70564bd3..83e3a71e 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.c +++ b/src/libnm-systemd-shared/src/basic/process-util.c @@ -6,6 +6,7 @@ #include <errno.h> #include <limits.h> #include <linux/oom.h> +#include <pthread.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -220,7 +221,6 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags assert(!(flags & PROCESS_CMDLINE_USE_LOCALE)); _cleanup_strv_free_ char **args = NULL; - char **p; args = strv_parse_nulstr(t, k); if (!args) @@ -478,37 +478,33 @@ int get_process_capeff(pid_t pid, char **ret) { return r; } -static int get_process_link_contents(const char *proc_file, char **ret) { +static int get_process_link_contents(pid_t pid, const char *proc_file, char **ret) { + const char *p; int r; assert(proc_file); - assert(ret); - r = readlink_malloc(proc_file, ret); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; + p = procfs_file_alloca(pid, proc_file); - return 0; + r = readlink_malloc(p, ret); + return r == -ENOENT ? -ESRCH : r; } int get_process_exe(pid_t pid, char **ret) { - const char *p; char *d; int r; assert(pid >= 0); - assert(ret); - p = procfs_file_alloca(pid, "exe"); - r = get_process_link_contents(p, ret); + r = get_process_link_contents(pid, "exe", ret); if (r < 0) return r; - d = endswith(*ret, " (deleted)"); - if (d) - *d = '\0'; + if (ret) { + d = endswith(*ret, " (deleted)"); + if (d) + *d = '\0'; + } return 0; } @@ -578,28 +574,17 @@ int get_process_gid(pid_t pid, gid_t *ret) { } int get_process_cwd(pid_t pid, char **ret) { - const char *p; - assert(pid >= 0); - assert(ret); if (pid == 0 || pid == getpid_cached()) return safe_getcwd(ret); - p = procfs_file_alloca(pid, "cwd"); - - return get_process_link_contents(p, ret); + return get_process_link_contents(pid, "cwd", ret); } int get_process_root(pid_t pid, char **ret) { - const char *p; - assert(pid >= 0); - assert(ret); - - p = procfs_file_alloca(pid, "root"); - - return get_process_link_contents(p, ret); + return get_process_link_contents(pid, "root", ret); } #define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U) @@ -651,7 +636,7 @@ int get_process_environ(pid_t pid, char **ret) { int get_process_ppid(pid_t pid, pid_t *ret) { _cleanup_free_ char *line = NULL; - long unsigned ppid; + unsigned long ppid; const char *p; int r; @@ -694,7 +679,7 @@ int get_process_ppid(pid_t pid, pid_t *ret) { if (ppid == 0) return -EADDRNOTAVAIL; - if ((pid_t) ppid < 0 || (long unsigned) (pid_t) ppid != ppid) + if ((pid_t) ppid < 0 || (unsigned long) (pid_t) ppid != ppid) return -ERANGE; if (ret) @@ -825,13 +810,12 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) { for (;;) { usec_t n; siginfo_t status = {}; - struct timespec ts; n = now(CLOCK_MONOTONIC); if (n >= until) break; - r = RET_NERRNO(sigtimedwait(&mask, NULL, timespec_store(&ts, until - n))); + r = RET_NERRNO(sigtimedwait(&mask, NULL, TIMESPEC_STORE(until - n))); /* Assuming we woke due to the child exiting. */ if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) { if (status.si_pid == pid) { @@ -1170,12 +1154,6 @@ void reset_cached_pid(void) { cached_pid = CACHED_PID_UNSET; } -/* We use glibc __register_atfork() + __dso_handle directly here, as they are not included in the glibc - * headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against - * libpthread, as it is part of glibc anyway. */ -extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void *dso_handle); -extern void* __dso_handle _weak_; - pid_t getpid_cached(void) { static bool installed = false; pid_t current_value; @@ -1203,7 +1181,7 @@ pid_t getpid_cached(void) { * only half-documented (glibc doesn't document it but LSB does — though only superficially) * we'll check for errors only in the most generic fashion possible. */ - if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) { + if (pthread_atfork(NULL, NULL, reset_cached_pid) != 0) { /* OOM? Let's try again later */ cached_pid = CACHED_PID_UNSET; return new_pid; @@ -1632,6 +1610,30 @@ _noreturn_ void freeze(void) { pause(); } +bool argv_looks_like_help(int argc, char **argv) { + char **l; + + /* Scans the command line for indications the user asks for help. This is supposed to be called by + * tools that do not implement getopt() style command line parsing because they are not primarily + * user-facing. Detects four ways of asking for help: + * + * 1. Passing zero arguments + * 2. Passing "help" as first argument + * 3. Passing --help as any argument + * 4. Passing -h as any argument + */ + + if (argc <= 1) + return true; + + if (streq_ptr(argv[1], "help")) + return true; + + l = strv_skip(argv, 1); + + return strv_contains(l, "--help") || + strv_contains(l, "-h"); +} static const char *const sigchld_code_table[] = { [CLD_EXITED] = "exited", diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h index a957cab2..c488c6a8 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.h +++ b/src/libnm-systemd-shared/src/basic/process-util.h @@ -193,3 +193,5 @@ int setpriority_closest(int priority); bool invoked_as(char *argv[], const char *token); _noreturn_ void freeze(void); + +bool argv_looks_like_help(int argc, char **argv); diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c index 9832f8ef..e3f4ee72 100644 --- a/src/libnm-systemd-shared/src/basic/random-util.c +++ b/src/libnm-systemd-shared/src/basic/random-util.c @@ -37,225 +37,41 @@ static bool srand_called = false; -int rdrand(unsigned long *ret) { - - /* So, you are a "security researcher", and you wonder why we bother with using raw RDRAND here, - * instead of sticking to /dev/urandom or getrandom()? - * - * Here's why: early boot. On Linux, during early boot the random pool that backs /dev/urandom and - * getrandom() is generally not initialized yet. It is very common that initialization of the random - * pool takes a longer time (up to many minutes), in particular on embedded devices that have no - * explicit hardware random generator, as well as in virtualized environments such as major cloud - * installations that do not provide virtio-rng or a similar mechanism. - * - * In such an environment using getrandom() synchronously means we'd block the entire system boot-up - * until the pool is initialized, i.e. *very* long. Using getrandom() asynchronously (GRND_NONBLOCK) - * would mean acquiring randomness during early boot would simply fail. Using /dev/urandom would mean - * generating many kmsg log messages about our use of it before the random pool is properly - * initialized. Neither of these outcomes is desirable. - * - * Thus, for very specific purposes we use RDRAND instead of either of these three options. RDRAND - * provides us quickly and relatively reliably with random values, without having to delay boot, - * without triggering warning messages in kmsg. - * - * Note that we use RDRAND only under very specific circumstances, when the requirements on the - * quality of the returned entropy permit it. Specifically, here are some cases where we *do* use - * RDRAND: - * - * • UUID generation: UUIDs are supposed to be universally unique but are not cryptographic - * key material. The quality and trust level of RDRAND should hence be OK: UUIDs should be - * generated in a way that is reliably unique, but they do not require ultimate trust into - * the entropy generator. systemd generates a number of UUIDs during early boot, including - * 'invocation IDs' for every unit spawned that identify the specific invocation of the - * service globally, and a number of others. Other alternatives for generating these UUIDs - * have been considered, but don't really work: for example, hashing uuids from a local - * system identifier combined with a counter falls flat because during early boot disk - * storage is not yet available (think: initrd) and thus a system-specific ID cannot be - * stored or retrieved yet. - * - * • Hash table seed generation: systemd uses many hash tables internally. Hash tables are - * generally assumed to have O(1) access complexity, but can deteriorate to prohibitive - * O(n) access complexity if an attacker manages to trigger a large number of hash - * collisions. Thus, systemd (as any software employing hash tables should) uses seeded - * hash functions for its hash tables, with a seed generated randomly. The hash tables - * systemd employs watch the fill level closely and reseed if necessary. This allows use of - * a low quality RNG initially, as long as it improves should a hash table be under attack: - * the attacker after all needs to trigger many collisions to exploit it for the purpose - * of DoS, but if doing so improves the seed the attack surface is reduced as the attack - * takes place. - * - * Some cases where we do NOT use RDRAND are: - * - * • Generation of cryptographic key material 🔑 - * - * • Generation of cryptographic salt values 🧂 - * - * This function returns: - * - * -EOPNOTSUPP → RDRAND is not available on this system 😔 - * -EAGAIN → The operation failed this time, but is likely to work if you try again a few - * times â™» - * -EUCLEAN → We got some random value, but it looked strange, so we refused using it. - * This failure might or might not be temporary. 😕 - */ - -#if defined(__i386__) || defined(__x86_64__) - static int have_rdrand = -1; - unsigned long v; - uint8_t success; - - if (have_rdrand < 0) { - uint32_t eax, ebx, ecx, edx; - - /* Check if RDRAND is supported by the CPU */ - if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0) { - have_rdrand = false; - return -EOPNOTSUPP; - } - -/* Compat with old gcc where bit_RDRND didn't exist yet */ -#ifndef bit_RDRND -#define bit_RDRND (1U << 30) -#endif - - have_rdrand = !!(ecx & bit_RDRND); - - if (have_rdrand > 0) { - /* Allow disabling use of RDRAND with SYSTEMD_RDRAND=0 - If it is unset getenv_bool_secure will return a negative value. */ - if (getenv_bool_secure("SYSTEMD_RDRAND") == 0) { - have_rdrand = false; - return -EOPNOTSUPP; - } - } - } - - if (have_rdrand == 0) - return -EOPNOTSUPP; - - asm volatile("rdrand %0;" - "setc %1" - : "=r" (v), - "=qm" (success)); - msan_unpoison(&success, sizeof(success)); - if (!success) - return -EAGAIN; - - /* Apparently on some AMD CPUs RDRAND will sometimes (after a suspend/resume cycle?) report success - * via the carry flag but nonetheless return the same fixed value -1 in all cases. This appears to be - * a bad bug in the CPU or firmware. Let's deal with that and work-around this by explicitly checking - * for this special value (and also 0, just to be sure) and filtering it out. This is a work-around - * only however and something AMD really should fix properly. The Linux kernel should probably work - * around this issue by turning off RDRAND altogether on those CPUs. See: - * https://github.com/systemd/systemd/issues/11810 */ - if (v == 0 || v == ULONG_MAX) - return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN), - "RDRAND returned suspicious value %lx, assuming bad hardware RNG, not using value.", v); - - *ret = v; - return 0; -#else - return -EOPNOTSUPP; -#endif -} - int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { static int have_syscall = -1; _cleanup_close_ int fd = -1; - bool got_some = false; - - /* Gathers some high-quality randomness from the kernel (or potentially mid-quality randomness from - * the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't block, unless the RANDOM_BLOCK - * flag is set. If RANDOM_MAY_FAIL is set, an error is returned if the random pool is not - * initialized. Otherwise it will always return some data from the kernel, regardless of whether the - * random pool is fully initialized or not. If RANDOM_EXTEND_WITH_PSEUDO is set, and some but not - * enough better quality randomness could be acquired, the rest is filled up with low quality - * randomness. - * - * Of course, when creating cryptographic key material you really shouldn't use RANDOM_ALLOW_DRDRAND - * or even RANDOM_EXTEND_WITH_PSEUDO. - * - * When generating UUIDs it's fine to use RANDOM_ALLOW_RDRAND but not OK to use - * RANDOM_EXTEND_WITH_PSEUDO. In fact RANDOM_EXTEND_WITH_PSEUDO is only really fine when invoked via - * an "all bets are off" wrapper, such as random_bytes(), see below. */ + + /* Gathers some high-quality randomness from the kernel. This call won't block, unless the RANDOM_BLOCK + * flag is set. If it doesn't block, it will still always return some data from the kernel, regardless + * of whether the random pool is fully initialized or not. When creating cryptographic key material you + * should always use RANDOM_BLOCK. */ if (n == 0) return 0; - if (FLAGS_SET(flags, RANDOM_ALLOW_RDRAND)) - /* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality randomness is - * not required, as we don't trust it (who does?). Note that we only do a single iteration of - * RDRAND here, even though the Intel docs suggest calling this in a tight loop of 10 - * invocations or so. That's because we don't really care about the quality here. We - * generally prefer using RDRAND if the caller allows us to, since this way we won't upset - * the kernel's random subsystem by accessing it before the pool is initialized (after all it - * will kmsg log about every attempt to do so). */ - for (;;) { - unsigned long u; - size_t m; - - if (rdrand(&u) < 0) { - if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { - /* Fill in the remaining bytes using pseudo-random values */ - pseudo_random_bytes(p, n); - return 0; - } - - /* OK, this didn't work, let's go to getrandom() + /dev/urandom instead */ - break; - } - - m = MIN(sizeof(u), n); - memcpy(p, &u, m); - - p = (uint8_t*) p + m; - n -= m; - - if (n == 0) - return 0; /* Yay, success! */ - - got_some = true; - } - /* Use the getrandom() syscall unless we know we don't have it. */ if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) { - for (;;) { - ssize_t l; -#if !HAVE_GETRANDOM +#if HAVE_GETRANDOM + ssize_t l = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_INSECURE); +#else /* NetworkManager Note: systemd calls the syscall directly in this case. Don't add that workaround. * If you don't compile against a libc that provides getrandom(), you don't get it. */ - l = -1; + ssize_t l = -1; errno = ENOSYS; -#else - l = getrandom(p, n, - (FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) | - (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0)); #endif + if (l > 0) { have_syscall = true; if ((size_t) l == n) return 0; /* Yay, success! */ + /* We didn't get enough data, so try again */ assert((size_t) l < n); p = (uint8_t*) p + l; n -= l; - - if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { - /* Fill in the remaining bytes using pseudo-random values */ - pseudo_random_bytes(p, n); - return 0; - } - - got_some = true; - - /* Hmm, we didn't get enough good data but the caller insists on good data? Then try again */ - if (FLAGS_SET(flags, RANDOM_BLOCK)) - continue; - - /* Fill in the rest with /dev/urandom */ - break; + continue; } else if (l == 0) { have_syscall = true; @@ -266,37 +82,12 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { have_syscall = false; break; - } else if (errno == EAGAIN) { - /* The kernel has no entropy whatsoever. Let's remember to use the syscall - * the next time again though. - * - * If RANDOM_MAY_FAIL is set, return an error so that random_bytes() can - * produce some pseudo-random bytes instead. Otherwise, fall back to - * /dev/urandom, which we know is empty, but the kernel will produce some - * bytes for us on a best-effort basis. */ - have_syscall = true; - - if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { - /* Fill in the remaining bytes using pseudorandom values */ - pseudo_random_bytes(p, n); - return 0; - } - - if (FLAGS_SET(flags, RANDOM_MAY_FAIL)) - return -ENODATA; - - /* Use /dev/urandom instead */ - break; - } else if (errno == EINVAL) { - - /* Most likely: unknown flag. We know that GRND_INSECURE might cause this, - * hence try without. */ - - if (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE)) { - flags = flags &~ RANDOM_ALLOW_INSECURE; - continue; - } + /* If we previously passed GRND_INSECURE, and this flag isn't known, then + * we're likely running an old kernel which has getrandom() but not + * GRND_INSECURE. In this case, fall back to /dev/urandom. */ + if (!FLAGS_SET(flags, RANDOM_BLOCK)) + break; return -errno; } else @@ -321,8 +112,6 @@ void initialize_srand(void) { #if HAVE_SYS_AUXV_H const void *auxv; #endif - unsigned long k; - if (srand_called) return; @@ -347,9 +136,6 @@ void initialize_srand(void) { x ^= (unsigned) now(CLOCK_REALTIME); x ^= (unsigned) gettid(); - if (rdrand(&k) >= 0) - x ^= (unsigned) k; - srand(x); srand_called = true; @@ -403,11 +189,8 @@ void random_bytes(void *p, size_t n) { * * What this function will do: * - * • This function will preferably use the CPU's RDRAND operation, if it is available, in - * order to return "mid-quality" random values cheaply. - * - * • Use getrandom() with GRND_NONBLOCK, to return high-quality random values if they are - * cheaply available. + * • Use getrandom(GRND_INSECURE) or /dev/urandom, to return high-quality random values if + * they are cheaply available, or less high-quality random values if they are not. * * • This function will return pseudo-random data, generated via libc rand() if nothing * better is available. @@ -430,7 +213,7 @@ void random_bytes(void *p, size_t n) { * This function is hence not useful for generating UUIDs or cryptographic key material. */ - if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND|RANDOM_ALLOW_INSECURE) >= 0) + if (genuine_random_bytes(p, n, 0) >= 0) return; /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */ @@ -504,6 +287,7 @@ int random_write_entropy(int fd, const void *seed, size_t size, bool credit) { return 1; } +#endif /* NM_IGNORED */ uint64_t random_u64_range(uint64_t m) { uint64_t x, remainder; @@ -523,4 +307,3 @@ uint64_t random_u64_range(uint64_t m) { return x % m; } -#endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/random-util.h b/src/libnm-systemd-shared/src/basic/random-util.h index e6528ddc..ccee3279 100644 --- a/src/libnm-systemd-shared/src/basic/random-util.h +++ b/src/libnm-systemd-shared/src/basic/random-util.h @@ -6,11 +6,7 @@ #include <stdint.h> typedef enum RandomFlags { - RANDOM_EXTEND_WITH_PSEUDO = 1 << 0, /* If we can't get enough genuine randomness, but some, fill up the rest with pseudo-randomness */ - RANDOM_BLOCK = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */ - RANDOM_MAY_FAIL = 1 << 2, /* If we can't get any randomness at all, return early with -ENODATA */ - RANDOM_ALLOW_RDRAND = 1 << 3, /* Allow usage of the CPU RNG */ - RANDOM_ALLOW_INSECURE = 1 << 4, /* Allow usage of GRND_INSECURE flag to kernel's getrandom() API */ + RANDOM_BLOCK = 1 << 0, /* Rather block than return crap randomness (only if the kernel supports that) */ } RandomFlags; int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled up with pseudo random, if not enough is available */ @@ -31,10 +27,8 @@ static inline uint32_t random_u32(void) { return u; } -int rdrand(unsigned long *ret); - /* Some limits on the pool sizes when we deal with the kernel random pool */ -#define RANDOM_POOL_SIZE_MIN 512U +#define RANDOM_POOL_SIZE_MIN 32U #define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U) size_t random_pool_size(void); diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h index ddea5350..709da206 100644 --- a/src/libnm-systemd-shared/src/basic/socket-util.h +++ b/src/libnm-systemd-shared/src/basic/socket-util.h @@ -230,7 +230,7 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng ({ \ const union sockaddr_union *__sa = &(sa); \ size_t _len; \ - switch(__sa->sa.sa_family) { \ + switch (__sa->sa.sa_family) { \ case AF_INET: \ _len = sizeof(struct sockaddr_in); \ break; \ diff --git a/src/libnm-systemd-shared/src/basic/sort-util.h b/src/libnm-systemd-shared/src/basic/sort-util.h index 28c06c40..52d611b8 100644 --- a/src/libnm-systemd-shared/src/basic/sort-util.h +++ b/src/libnm-systemd-shared/src/basic/sort-util.h @@ -16,8 +16,8 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, #define typesafe_bsearch_r(k, b, n, func, userdata) \ ({ \ - const typeof(b[0]) *_k = k; \ - int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \ + const typeof((b)[0]) *_k = k; \ + int (*_func_)(const typeof((b)[0])*, const typeof((b)[0])*, typeof(userdata)) = func; \ xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \ }) @@ -36,8 +36,8 @@ static inline void* bsearch_safe(const void *key, const void *base, #define typesafe_bsearch(k, b, n, func) \ ({ \ - const typeof(b[0]) *_k = k; \ - int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \ + const typeof((b)[0]) *_k = k; \ + int (*_func_)(const typeof((b)[0])*, const typeof((b)[0])*) = func; \ bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \ }) @@ -57,7 +57,7 @@ static inline void _qsort_safe(void *base, size_t nmemb, size_t size, comparison * is the prototype for the comparison function */ #define typesafe_qsort(p, n, func) \ ({ \ - int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ + int (*_func_)(const typeof((p)[0])*, const typeof((p)[0])*) = func; \ _qsort_safe((p), (n), sizeof((p)[0]), (comparison_fn_t) _func_); \ }) @@ -72,7 +72,7 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, compariso #define typesafe_qsort_r(p, n, func, userdata) \ ({ \ - int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \ + int (*_func_)(const typeof((p)[0])*, const typeof((p)[0])*, typeof(userdata)) = func; \ qsort_r_safe((p), (n), sizeof((p)[0]), (comparison_userdata_fn_t) _func_, userdata); \ }) #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c index 8126ce08..fd32f25d 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.c +++ b/src/libnm-systemd-shared/src/basic/stat-util.c @@ -132,17 +132,22 @@ bool null_or_empty(struct stat *st) { return false; } -int null_or_empty_path(const char *fn) { +int null_or_empty_path_with_root(const char *fn, const char *root) { struct stat st; + int r; assert(fn); - /* If we have the path, let's do an easy text comparison first. */ - if (path_equal(fn, "/dev/null")) + /* A symlink to /dev/null or an empty file? + * When looking under root_dir, we can't expect /dev/ to be mounted, + * so let's see if the path is a (possibly dangling) symlink to /dev/null. */ + + if (path_equal_ptr(path_startswith(fn, root ?: "/"), "dev/null")) return true; - if (stat(fn, &st) < 0) - return -errno; + r = chase_symlinks_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st, NULL); + if (r < 0) + return r; return null_or_empty(&st); } @@ -191,8 +196,7 @@ int files_same(const char *filea, const char *fileb, int flags) { if (fstatat(AT_FDCWD, fileb, &b, flags) < 0) return -errno; - return a.st_dev == b.st_dev && - a.st_ino == b.st_ino; + return stat_inode_same(&a, &b); } bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { @@ -211,7 +215,7 @@ int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { return is_fs_type(&s, magic_value); } -#endif /* NM_IGNORE */ +#endif /* NM_IGNORED */ int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { struct statfs s; @@ -222,7 +226,7 @@ int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { return is_fs_type(&s, magic_value); } -#if 0 /* NM_IGNORE */ +#if 0 /* NM_IGNORED */ bool is_temporary_fs(const struct statfs *s) { return fs_in_group(s, FILESYSTEM_SET_TEMPORARY); } @@ -257,6 +261,15 @@ int path_is_temporary_fs(const char *path) { return is_temporary_fs(&s); } + +int path_is_network_fs(const char *path) { + struct statfs s; + + if (statfs(path, &s) < 0) + return -errno; + + return is_network_fs(&s); +} #endif /* NM_IGNORED */ int stat_verify_regular(const struct stat *st) { @@ -420,6 +433,18 @@ int proc_mounted(void) { return r; } +bool stat_inode_same(const struct stat *a, const struct stat *b) { + + /* Returns if the specified stat structure references the same (though possibly modified) inode. Does + * a thorough check, comparing inode nr, backing device and if the inode is still of the same type. */ + + return a && b && + (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ + ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ + a->st_dev == b->st_dev && + a->st_ino == b->st_ino; +} + #if 0 /* NM_IGNORED */ bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { @@ -432,14 +457,10 @@ bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { * about contents of the file. The purpose here is to detect file contents changes, and nothing * else. */ - return a && b && - (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ - ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ + return stat_inode_same(a, b) && a->st_mtim.tv_sec == b->st_mtim.tv_sec && a->st_mtim.tv_nsec == b->st_mtim.tv_nsec && (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */ - a->st_dev == b->st_dev && - a->st_ino == b->st_ino && (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */ } diff --git a/src/libnm-systemd-shared/src/basic/stat-util.h b/src/libnm-systemd-shared/src/basic/stat-util.h index a566114f..37513a43 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.h +++ b/src/libnm-systemd-shared/src/basic/stat-util.h @@ -31,9 +31,13 @@ static inline int dir_is_populated(const char *path) { } bool null_or_empty(struct stat *st) _pure_; -int null_or_empty_path(const char *fn); +int null_or_empty_path_with_root(const char *fn, const char *root); int null_or_empty_fd(int fd); +static inline int null_or_empty_path(const char *fn) { + return null_or_empty_path_with_root(fn, NULL); +} + int path_is_read_only_fs(const char *path); int files_same(const char *filea, const char *fileb, int flags); @@ -53,6 +57,7 @@ int fd_is_temporary_fs(int fd); int fd_is_network_fs(int fd); int path_is_temporary_fs(const char *path); +int path_is_network_fs(const char *path); /* Because statfs.t_type can be int on some architectures, we have to cast * the const magic to the type, otherwise the compiler warns about @@ -91,6 +96,7 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret int proc_mounted(void); +bool stat_inode_same(const struct stat *a, const struct stat *b); bool stat_inode_unmodified(const struct stat *a, const struct stat *b); int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx); @@ -113,3 +119,9 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s struct new_statx nsx; \ } var #endif + +static inline bool devid_set_and_equal(dev_t a, dev_t b) { + /* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't + * know" and we'll return false */ + return a == b && a != 0; +} diff --git a/src/libnm-systemd-shared/src/basic/string-table.h b/src/libnm-systemd-shared/src/basic/string-table.h index 97c1adc0..e3a26a62 100644 --- a/src/libnm-systemd-shared/src/basic/string-table.h +++ b/src/libnm-systemd-shared/src/basic/string-table.h @@ -113,4 +113,4 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k fputc_unlocked('\n', stdout); \ } \ funlockfile(stdout); \ - } while(false) + } while (false) diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c index 3e0faf95..f2cb80fe 100644 --- a/src/libnm-systemd-shared/src/basic/strv.c +++ b/src/libnm-systemd-shared/src/basic/strv.c @@ -19,8 +19,6 @@ #include "strv.h" char* strv_find(char * const *l, const char *name) { - char * const *i; - assert(name); STRV_FOREACH(i, l) @@ -31,8 +29,6 @@ char* strv_find(char * const *l, const char *name) { } char* strv_find_case(char * const *l, const char *name) { - char * const *i; - assert(name); STRV_FOREACH(i, l) @@ -43,8 +39,6 @@ char* strv_find_case(char * const *l, const char *name) { } char* strv_find_prefix(char * const *l, const char *name) { - char * const *i; - assert(name); STRV_FOREACH(i, l) @@ -55,14 +49,14 @@ char* strv_find_prefix(char * const *l, const char *name) { } char* strv_find_startswith(char * const *l, const char *name) { - char * const *i, *e; - assert(name); /* Like strv_find_prefix, but actually returns only the * suffix, not the whole item */ STRV_FOREACH(i, l) { + char *e; + e = startswith(*i, name); if (e) return e; @@ -72,18 +66,13 @@ char* strv_find_startswith(char * const *l, const char *name) { } char** strv_free(char **l) { - if (!l) - return NULL; - - for (char **k = l; *k; k++) + STRV_FOREACH(k, l) free(*k); return mfree(l); } char** strv_free_erase(char **l) { - char **i; - STRV_FOREACH(i, l) erase_and_freep(i); @@ -91,32 +80,29 @@ char** strv_free_erase(char **l) { } char** strv_copy(char * const *l) { - char **r, **k; + _cleanup_strv_free_ char **result = NULL; + char **k; - k = r = new(char*, strv_length(l) + 1); - if (!r) + result = new(char*, strv_length(l) + 1); + if (!result) return NULL; - if (l) - for (; *l; k++, l++) { - *k = strdup(*l); - if (!*k) { - strv_free(r); - return NULL; - } - } + k = result; + STRV_FOREACH(i, l) { + *k = strdup(*i); + if (!*k) + return NULL; + k++; + } *k = NULL; - return r; + return TAKE_PTR(result); } size_t strv_length(char * const *l) { size_t n = 0; - if (!l) - return 0; - - for (; *l; l++) + STRV_FOREACH(i, l) n++; return n; @@ -173,8 +159,8 @@ char** strv_new_internal(const char *x, ...) { } int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) { - char * const *s, **t; size_t p, q, i = 0; + char **t; assert(a); @@ -220,7 +206,6 @@ rollback: #if 0 /* NM_IGNORED */ int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) { - char * const *s; int r; STRV_FOREACH(s, b) { @@ -373,7 +358,6 @@ int strv_split_colon_pairs(char ***t, const char *s) { #endif /* NM_IGNORED */ char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) { - char * const *s; char *r, *e; size_t n, k, m; @@ -601,8 +585,6 @@ int strv_extend_front(char ***l, const char *value) { } char** strv_uniq(char **l) { - char **i; - /* Drops duplicate entries. The first identical string will be * kept, the others dropped */ @@ -613,10 +595,8 @@ char** strv_uniq(char **l) { } bool strv_is_uniq(char * const *l) { - char * const *i; - STRV_FOREACH(i, l) - if (strv_find(i+1, *i)) + if (strv_contains(i+1, *i)) return false; return true; @@ -725,7 +705,6 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) { */ _cleanup_free_ char *m = NULL; - char * const *i; size_t n = 0; assert(ret); @@ -762,8 +741,6 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) { } bool strv_overlap(char * const *a, char * const *b) { - char * const *i; - STRV_FOREACH(i, a) if (strv_contains(b, *i)) return true; @@ -805,8 +782,6 @@ int strv_compare(char * const *a, char * const *b) { } void strv_print(char * const *l) { - char * const *s; - STRV_FOREACH(s, l) puts(*s); } @@ -841,8 +816,6 @@ char** strv_reverse(char **l) { #if 0 /* NM_IGNORED */ char** strv_shell_escape(char **l, const char *bad) { - char **s; - /* Escapes every character in every string in l that is in bad, * edits in-place, does not roll-back on error. */ @@ -926,7 +899,6 @@ rollback: int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) { bool b = false; - char * const *s; int r; /* Like fputs(), but for strv, and with a less stupid argument order */ diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h index 092d40c8..2a858326 100644 --- a/src/libnm-systemd-shared/src/basic/strv.h +++ b/src/libnm-systemd-shared/src/basic/strv.h @@ -122,19 +122,30 @@ static inline int strv_from_nulstr(char ***a, const char *nulstr) { bool strv_overlap(char * const *a, char * const *b) _pure_; +#define _STRV_FOREACH(s, l, i) \ + for (typeof(*(l)) *s, *i = (l); (s = i) && *i; i++) + #define STRV_FOREACH(s, l) \ - for ((s) = (l); (s) && *(s); (s)++) + _STRV_FOREACH(s, l, UNIQ_T(i, UNIQ)) + +#define _STRV_FOREACH_BACKWARDS(s, l, h, i) \ + for (typeof(*(l)) *s, *h = (l), *i = ({ \ + size_t _len = strv_length(h); \ + _len > 0 ? h + _len - 1 : NULL; \ + }); \ + (s = i); \ + i = PTR_SUB1(i, h)) + +#define STRV_FOREACH_BACKWARDS(s, l) \ + _STRV_FOREACH_BACKWARDS(s, l, UNIQ_T(h, UNIQ), UNIQ_T(i, UNIQ)) -#define STRV_FOREACH_BACKWARDS(s, l) \ - for (s = ({ \ - typeof(l) _l = l; \ - _l ? _l + strv_length(_l) - 1U : NULL; \ - }); \ - (l) && ((s) >= (l)); \ - (s)--) +#define _STRV_FOREACH_PAIR(x, y, l, i) \ + for (typeof(*l) *x, *y, *i = (l); \ + i && *(x = i) && *(y = i + 1); \ + i += 2) -#define STRV_FOREACH_PAIR(x, y, l) \ - for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) +#define STRV_FOREACH_PAIR(x, y, l) \ + _STRV_FOREACH_PAIR(x, y, l, UNIQ_T(i, UNIQ)) char** strv_sort(char **l); void strv_print(char * const *l); @@ -185,7 +196,7 @@ void strv_print(char * const *l); #define STARTSWITH_SET(p, ...) \ ({ \ const char *_p = (p); \ - char *_found = NULL, **_i; \ + char *_found = NULL; \ STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \ _found = startswith(_p, *_i); \ if (_found) \ @@ -197,7 +208,7 @@ void strv_print(char * const *l); #define ENDSWITH_SET(p, ...) \ ({ \ const char *_p = (p); \ - char *_found = NULL, **_i; \ + char *_found = NULL; \ STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \ _found = endswith(_p, *_i); \ if (_found) \ @@ -207,7 +218,7 @@ void strv_print(char * const *l); }) #define _FOREACH_STRING(uniq, x, y, ...) \ - for (char **UNIQ_T(l, uniq) = STRV_MAKE(({ x = y; }), ##__VA_ARGS__); \ + for (const char *x, * const*UNIQ_T(l, uniq) = STRV_MAKE_CONST(({ x = y; }), ##__VA_ARGS__); \ x; \ x = *(++UNIQ_T(l, uniq))) diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c index d7ea2b38..8476a650 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.c +++ b/src/libnm-systemd-shared/src/basic/time-util.c @@ -79,7 +79,7 @@ triple_timestamp* triple_timestamp_get(triple_timestamp *ts) { ts->realtime = now(CLOCK_REALTIME); ts->monotonic = now(CLOCK_MONOTONIC); - ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY; + ts->boottime = now(CLOCK_BOOTTIME); return ts; } @@ -128,7 +128,7 @@ usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) { dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { assert(ts); - if (u == USEC_INFINITY || u == 0) { + if (!timestamp_is_set(u)) { ts->realtime = ts->monotonic = u; return ts; } @@ -143,7 +143,7 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) assert(ts); - if (u == USEC_INFINITY || u == 0) { + if (!timestamp_is_set(u)) { ts->realtime = ts->monotonic = ts->boottime = u; return ts; } @@ -152,9 +152,7 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) ts->realtime = u; ts->monotonic = map_clock_usec_internal(u, nowr, now(CLOCK_MONOTONIC)); - ts->boottime = clock_boottime_supported() ? - map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME)) : - USEC_INFINITY; + ts->boottime = map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME)); return ts; } @@ -172,8 +170,7 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { return ts; } -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { - clockid_t cid; +dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u) { usec_t nowm; if (u == USEC_INFINITY) { @@ -181,14 +178,8 @@ dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, us return ts; } - cid = clock_boottime_or_monotonic(); - nowm = now(cid); - - if (cid == CLOCK_MONOTONIC) - ts->monotonic = u; - else - ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC)); - + nowm = now(CLOCK_BOOTTIME); + ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC)); ts->realtime = map_clock_usec_internal(u, nowm, now(CLOCK_REALTIME)); return ts; } @@ -323,11 +314,13 @@ char *format_timestamp_style( time_t sec; size_t n; bool utc = false, us = false; + int r; assert(buf); switch (style) { case TIMESTAMP_PRETTY: + case TIMESTAMP_UNIX: break; case TIMESTAMP_US: us = true; @@ -350,9 +343,17 @@ char *format_timestamp_style( 1 + 1 + /* space and shortest possible zone */ 1)) return NULL; /* Not enough space even for the shortest form. */ - if (t <= 0 || t == USEC_INFINITY) + if (!timestamp_is_set(t)) return NULL; /* Timestamp is unset */ + if (style == TIMESTAMP_UNIX) { + r = snprintf(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down µs → s */ + if (r < 0 || (size_t) r >= l) + return NULL; /* Doesn't fit */ + + return buf; + } + /* Let's not format times with years > 9999 */ if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) { assert(l >= STRLEN("--- XXXX-XX-XX XX:XX:XX") + 1); @@ -420,7 +421,7 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) { const char *s; usec_t n, d; - if (t <= 0 || t == USEC_INFINITY) + if (!timestamp_is_set(t)) return NULL; n = now(CLOCK_REALTIME); @@ -793,6 +794,16 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { goto from_tm; } + /* Support OUTPUT_SHORT and OUTPUT_SHORT_PRECISE formats */ + tm = copy; + k = strptime(t, "%b %d %H:%M:%S", &tm); + if (k) { + if (*k == '.') + goto parse_usec; + else if (*k == 0) + goto from_tm; + } + tm = copy; k = strptime(t, "%y-%m-%d %H:%M", &tm); if (k && *k == 0) { @@ -1447,33 +1458,6 @@ int verify_timezone(const char *name, int log_level) { return 0; } -bool clock_boottime_supported(void) { - static int supported = -1; - - /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */ - - if (supported < 0) { - int fd; - - fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - supported = false; - else { - safe_close(fd); - supported = true; - } - } - - return supported; -} - -clockid_t clock_boottime_or_monotonic(void) { - if (clock_boottime_supported()) - return CLOCK_BOOTTIME; - else - return CLOCK_MONOTONIC; -} - bool clock_supported(clockid_t clock) { struct timespec ts; @@ -1481,16 +1465,10 @@ bool clock_supported(clockid_t clock) { case CLOCK_MONOTONIC: case CLOCK_REALTIME: - return true; - case CLOCK_BOOTTIME: - return clock_boottime_supported(); - - case CLOCK_BOOTTIME_ALARM: - if (!clock_boottime_supported()) - return false; + /* These three are always available in our baseline, and work in timerfd, as of kernel 3.15 */ + return true; - _fallthrough_; default: /* For everything else, check properly */ return clock_gettime(clock, &ts) >= 0; @@ -1639,6 +1617,7 @@ static const char* const timestamp_style_table[_TIMESTAMP_STYLE_MAX] = { [TIMESTAMP_US] = "us", [TIMESTAMP_UTC] = "utc", [TIMESTAMP_US_UTC] = "us+utc", + [TIMESTAMP_UNIX] = "unix", }; /* Use the macro for enum → string to allow for aliases */ diff --git a/src/libnm-systemd-shared/src/basic/time-util.h b/src/libnm-systemd-shared/src/basic/time-util.h index 895af882..bf312442 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.h +++ b/src/libnm-systemd-shared/src/basic/time-util.h @@ -34,6 +34,7 @@ typedef enum TimestampStyle { TIMESTAMP_US, TIMESTAMP_UTC, TIMESTAMP_US_UTC, + TIMESTAMP_UNIX, _TIMESTAMP_STYLE_MAX, _TIMESTAMP_STYLE_INVALID = -EINVAL, } TimestampStyle; @@ -81,7 +82,7 @@ usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock); dual_timestamp* dual_timestamp_get(dual_timestamp *ts); dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u); -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u); +dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u); triple_timestamp* triple_timestamp_get(triple_timestamp *ts); triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u); @@ -114,9 +115,13 @@ nsec_t timespec_load_nsec(const struct timespec *ts) _pure_; struct timespec* timespec_store(struct timespec *ts, usec_t u); struct timespec* timespec_store_nsec(struct timespec *ts, nsec_t n); +#define TIMESPEC_STORE(u) timespec_store(&(struct timespec) {}, (u)) + usec_t timeval_load(const struct timeval *tv) _pure_; struct timeval* timeval_store(struct timeval *tv, usec_t u); +#define TIMEVAL_STORE(u) timeval_store(&(struct timeval) {}, (u)) + char* format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style) _warn_unused_result_; char* format_timestamp_relative(char *buf, size_t l, usec_t t) _warn_unused_result_; char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) _warn_unused_result_; @@ -150,9 +155,7 @@ static inline bool timezone_is_valid(const char *name, int log_level) { return verify_timezone(name, log_level) >= 0; } -bool clock_boottime_supported(void); bool clock_supported(clockid_t clock); -clockid_t clock_boottime_or_monotonic(void); usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to); diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.c b/src/libnm-systemd-shared/src/basic/tmpfile-util.c index dc5a49e2..57447a42 100644 --- a/src/libnm-systemd-shared/src/basic/tmpfile-util.c +++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.c @@ -280,6 +280,28 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { return fd; } +int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file) { + _cleanup_free_ char *path = NULL; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_close_ int fd = -1; + + assert(target); + assert(ret_file); + assert(ret_path); + + fd = open_tmpfile_linkable(target, flags, &path); + if (fd < 0) + return fd; + + f = take_fdopen(&fd, "w"); + if (!f) + return -ENOMEM; + + *ret_path = TAKE_PTR(path); + *ret_file = TAKE_PTR(f); + return 0; +} + int link_tmpfile(int fd, const char *path, const char *target) { assert(fd >= 0); assert(target); @@ -297,6 +319,23 @@ int link_tmpfile(int fd, const char *path, const char *target) { return RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW)); } +int flink_tmpfile(FILE *f, const char *path, const char *target) { + int fd, r; + + assert(f); + assert(target); + + fd = fileno(f); + if (fd < 0) /* Not all FILE* objects encapsulate fds */ + return -EBADF; + + r = fflush_sync_and_check(f); + if (r < 0) + return r; + + return link_tmpfile(fd, path, target); +} + int mkdtemp_malloc(const char *template, char **ret) { _cleanup_free_ char *p = NULL; int r; diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.h b/src/libnm-systemd-shared/src/basic/tmpfile-util.h index 45255fc0..610cbaf8 100644 --- a/src/libnm-systemd-shared/src/basic/tmpfile-util.h +++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.h @@ -13,7 +13,9 @@ int tempfn_random_child(const char *p, const char *extra, char **ret); int open_tmpfile_unlinkable(const char *directory, int flags); int open_tmpfile_linkable(const char *target, int flags, char **ret_path); +int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file); int link_tmpfile(int fd, const char *path, const char *target); +int flink_tmpfile(FILE *f, const char *path, const char *target); int mkdtemp_malloc(const char *template, char **ret); diff --git a/src/libnm-systemd-shared/src/basic/util.h b/src/libnm-systemd-shared/src/basic/util.h index dd1cb4f3..368e3e4f 100644 --- a/src/libnm-systemd-shared/src/basic/util.h +++ b/src/libnm-systemd-shared/src/basic/util.h @@ -9,6 +9,12 @@ extern int saved_argc; extern char **saved_argv; static inline void save_argc_argv(int argc, char **argv) { + + /* Protect against CVE-2021-4034 style attacks */ + assert_se(argc > 0); + assert_se(argv); + assert_se(argv[0]); + saved_argc = argc; saved_argv = argv; } diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h index d597c743..1c198f6a 100644 --- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h +++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h @@ -53,14 +53,16 @@ #define CONCATENATE(x, y) XCONCATENATE(x, y) #ifdef SD_BOOT + void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_; + #ifdef NDEBUG #define assert(expr) #define assert_not_reached() __builtin_unreachable() #else - void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_; #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); }) #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__) #endif + #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); }) #define memcpy(a, b, c) CopyMem((a), (b), (c)) #define free(a) FreePool(a) @@ -74,6 +76,13 @@ _expr_; \ }) +#define ASSERT_SE_PTR(expr) \ + ({ \ + typeof(expr) _expr_ = (expr); \ + assert_se(_expr_); \ + _expr_; \ + }) + #if defined(static_assert) #define assert_cc(expr) \ static_assert(expr, #expr) @@ -243,23 +252,23 @@ CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \ (CASE_F,__VA_ARGS__) -#define IN_SET(x, ...) \ - ({ \ - sd_bool _found = sd_false; \ +#define IN_SET(x, ...) \ + ({ \ + sd_bool _found = sd_false; \ /* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \ * type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \ * the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \ * doesn't work, as we want to use this on bitfields and gcc refuses typeof() on bitfields.) */ \ static const long double __assert_in_set[] _unused_ = { __VA_ARGS__ }; \ - assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \ - switch(x) { \ - FOR_EACH_MAKE_CASE(__VA_ARGS__) \ - _found = sd_true; \ - break; \ - default: \ - break; \ - } \ - _found; \ + assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \ + switch (x) { \ + FOR_EACH_MAKE_CASE(__VA_ARGS__) \ + _found = sd_true; \ + break; \ + default: \ + break; \ + } \ + _found; \ }) /* Takes inspiration from Rust's Option::take() method: reads and returns a pointer, but at the same time diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.c b/src/libnm-systemd-shared/src/shared/dns-domain.c index b82a9a1c..e932b693 100644 --- a/src/libnm-systemd-shared/src/shared/dns-domain.c +++ b/src/libnm-systemd-shared/src/shared/dns-domain.c @@ -171,24 +171,18 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha /* Skip current terminal character (and accept domain names ending it ".") */ if (*terminal == 0) - terminal--; + terminal = PTR_SUB1(terminal, name); if (terminal >= name && *terminal == '.') - terminal--; + terminal = PTR_SUB1(terminal, name); /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */ - for (;;) { - if (terminal < name) { - /* Reached the first label, so indicate that there are no more */ - terminal = NULL; - break; - } - + while (terminal) { /* Find the start of the last label */ if (*terminal == '.') { const char *y; unsigned slashes = 0; - for (y = terminal - 1; y >= name && *y == '\\'; y--) + for (y = PTR_SUB1(terminal, name); y && *y == '\\'; y = PTR_SUB1(y, name)) slashes++; if (slashes % 2 == 0) { @@ -201,7 +195,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha } } - terminal--; + terminal = PTR_SUB1(terminal, name); } r = dns_label_unescape(&name, dest, sz, 0); @@ -1424,4 +1418,19 @@ int dns_name_dot_suffixed(const char *name) { return false; } } + +bool dns_name_dont_resolve(const char *name) { + + /* Never respond to some of the domains listed in RFC6303 */ + if (dns_name_endswith(name, "0.in-addr.arpa") > 0 || + dns_name_equal(name, "255.255.255.255.in-addr.arpa") > 0 || + dns_name_equal(name, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) + return true; + + /* Never respond to some of the domains listed in RFC6761 */ + if (dns_name_endswith(name, "invalid") > 0) + return true; + + return false; +} #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.h b/src/libnm-systemd-shared/src/shared/dns-domain.h index c237f761..ec174c0e 100644 --- a/src/libnm-systemd-shared/src/shared/dns-domain.h +++ b/src/libnm-systemd-shared/src/shared/dns-domain.h @@ -105,3 +105,5 @@ int dns_name_apply_idna(const char *name, char **ret); int dns_name_is_valid_or_address(const char *name); int dns_name_dot_suffixed(const char *name); + +bool dns_name_dont_resolve(const char *name); diff --git a/src/libnmc-base/nm-client-utils.c b/src/libnmc-base/nm-client-utils.c index 01b3df32..e2ed7fc2 100644 --- a/src/libnmc-base/nm-client-utils.c +++ b/src/libnmc-base/nm-client-utils.c @@ -732,7 +732,7 @@ nmc_print_qrcode(const char *str) * Example of @passwd_file contents: * wifi.psk:tajne heslo * 802-1x.password:krakonos - * 802-11-wireless-security:leap-password:my leap password + * 802-11-wireless-security.leap-password:my leap password * * Returns: (transfer full): hash table with parsed passwords, or %NULL on an error */ diff --git a/src/libnmc-base/nm-secret-agent-simple.c b/src/libnmc-base/nm-secret-agent-simple.c index 5651a15b..de3de05a 100644 --- a/src/libnmc-base/nm-secret-agent-simple.c +++ b/src/libnmc-base/nm-secret-agent-simple.c @@ -795,7 +795,6 @@ try_spawn_vpn_auth_helper(RequestData *request, GPtrArray *secrets) &auth_dialog_out_fd, NULL, &error)) { - g_warning("Failed to spawn the auth dialog%s\n", error->message); return FALSE; } diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.c b/src/libnmc-setting/nm-meta-setting-base-impl.c index cd55779f..69daa76c 100644 --- a/src/libnmc-setting/nm-meta-setting-base-impl.c +++ b/src/libnmc-setting/nm-meta-setting-base-impl.c @@ -153,6 +153,68 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = { #undef _D }; +const NMSetting8021xSchemeVtable * +nm_setting_8021x_scheme_vtable_by_setting_key(const char *key) +{ + static const NMSetting8021xSchemeType sorted_index[] = { + NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY, + NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY, + }; + int imin, imax; + + nm_assert(key); + + if (NM_MORE_ASSERT_ONCE(5)) { + const NMSetting8021xSchemeVtable *vtable_prev = NULL; + int i, j; + + for (i = 0; i < (int) G_N_ELEMENTS(sorted_index); i++) { + const NMSetting8021xSchemeType t = sorted_index[i]; + const NMSetting8021xSchemeVtable *vtable; + + nm_assert(_NM_INT_NOT_NEGATIVE(t)); + nm_assert(t < G_N_ELEMENTS(nm_setting_8021x_scheme_vtable) - 1); + + for (j = 0; j < i; j++) + nm_assert(t != sorted_index[j]); + + vtable = &nm_setting_8021x_scheme_vtable[t]; + + nm_assert(vtable->scheme_type == t); + nm_assert(vtable->setting_key); + + if (vtable_prev) + nm_assert(strcmp(vtable_prev->setting_key, vtable->setting_key) < 0); + vtable_prev = vtable; + } + } + + imin = 0; + imax = G_N_ELEMENTS(sorted_index) - 1; + while (imin <= imax) { + const NMSetting8021xSchemeVtable *vtable; + const int imid = imin + (imax - imin) / 2; + int cmp; + + vtable = &nm_setting_8021x_scheme_vtable[sorted_index[imid]]; + + cmp = strcmp(vtable->setting_key, key); + if (cmp == 0) + return vtable; + + if (cmp < 0) + imin = imid + 1; + else + imax = imid - 1; + } + + return NULL; +} + /*****************************************************************************/ const NMMetaSettingInfo nm_meta_setting_infos[] = { diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.h b/src/libnmc-setting/nm-meta-setting-base-impl.h index ea4f85ff..12688659 100644 --- a/src/libnmc-setting/nm-meta-setting-base-impl.h +++ b/src/libnmc-setting/nm-meta-setting-base-impl.h @@ -38,20 +38,20 @@ * * 10: NMSettingUser */ -typedef enum { /*< skip >*/ - NM_SETTING_PRIORITY_INVALID = 0, - NM_SETTING_PRIORITY_CONNECTION = 1, - NM_SETTING_PRIORITY_HW_BASE = 2, - NM_SETTING_PRIORITY_HW_NON_BASE = 3, - NM_SETTING_PRIORITY_HW_AUX = 4, - NM_SETTING_PRIORITY_AUX = 5, - NM_SETTING_PRIORITY_IP = 6, - NM_SETTING_PRIORITY_USER = 10, +typedef enum /*< skip >*/ { + NM_SETTING_PRIORITY_INVALID = 0, + NM_SETTING_PRIORITY_CONNECTION = 1, + NM_SETTING_PRIORITY_HW_BASE = 2, + NM_SETTING_PRIORITY_HW_NON_BASE = 3, + NM_SETTING_PRIORITY_HW_AUX = 4, + NM_SETTING_PRIORITY_AUX = 5, + NM_SETTING_PRIORITY_IP = 6, + NM_SETTING_PRIORITY_USER = 10, } NMSettingPriority; /*****************************************************************************/ -typedef enum { +typedef enum _nm_packed { NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT, NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT, NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT, @@ -92,6 +92,8 @@ typedef struct { extern const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[_NM_SETTING_802_1X_SCHEME_TYPE_NUM + 1]; +const NMSetting8021xSchemeVtable *nm_setting_8021x_scheme_vtable_by_setting_key(const char *key); + /*****************************************************************************/ typedef enum _nm_packed { diff --git a/src/libnmc-setting/nm-meta-setting-desc.h b/src/libnmc-setting/nm-meta-setting-desc.h index 91905253..b08d4c08 100644 --- a/src/libnmc-setting/nm-meta-setting-desc.h +++ b/src/libnmc-setting/nm-meta-setting-desc.h @@ -91,6 +91,7 @@ typedef enum { NM_META_COLOR_CONNECTION_INVISIBLE, NM_META_COLOR_CONNECTION_EXTERNAL, NM_META_COLOR_CONNECTION_UNKNOWN, + NM_META_COLOR_CONNECTION_DEPRECATED, NM_META_COLOR_CONNECTIVITY_FULL, NM_META_COLOR_CONNECTIVITY_LIMITED, NM_META_COLOR_CONNECTIVITY_NONE, @@ -126,6 +127,7 @@ typedef enum { NM_META_COLOR_WIFI_SIGNAL_GOOD, NM_META_COLOR_WIFI_SIGNAL_POOR, NM_META_COLOR_WIFI_SIGNAL_UNKNOWN, + NM_META_COLOR_WIFI_DEPRECATED, NM_META_COLOR_DISABLED, NM_META_COLOR_ENABLED, _NM_META_COLOR_NUM diff --git a/src/libnmc-setting/settings-docs-input.xml b/src/libnmc-setting/settings-docs-input.xml index 37ad35ba..f7552644 100644 --- a/src/libnmc-setting/settings-docs-input.xml +++ b/src/libnmc-setting/settings-docs-input.xml @@ -1,4 +1,4 @@ -<nm-setting-docs><setting name="connection" description="General Connection Profile Settings" name_upper="CONNECTION"><property name="auth-retries" name_upper="AUTH_RETRIES" type="int32" default="-1" description="The number of retries for the authentication. Zero means to try indefinitely; -1 means to use a global default. If the global default is not set, the authentication retries for 3 times before failing the connection. Currently, this only applies to 802-1x authentication." /><property name="autoconnect" name_upper="AUTOCONNECT" type="boolean" default="TRUE" description="Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See "secondaries" as an alternative to automatically connect VPN profiles." /><property name="autoconnect-priority" name_upper="AUTOCONNECT_PRIORITY" type="int32" default="0" description="The autoconnect priority in range -999 to 999. If the connection is set to autoconnect, connections with higher priority will be preferred. The higher number means higher priority. Defaults to 0. Note that this property only matters if there are more than one candidate profile to select for autoconnect. In case of equal priority, the profile used most recently is chosen." /><property name="autoconnect-retries" name_upper="AUTOCONNECT_RETRIES" type="int32" default="-1" description="The number of times a connection should be tried when autoactivating before giving up. Zero means forever, -1 means the global default (4 times if not overridden). Setting this to 1 means to try activation only once before blocking autoconnect. Note that after a timeout, NetworkManager will try to autoconnect again." /><property name="autoconnect-slaves" name_upper="AUTOCONNECT_SLAVES" type="NMSettingConnectionAutoconnectSlaves (int32)" description="Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties "autoconnect", "autoconnect-priority" and "autoconnect-retries" are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0." /><property name="dns-over-tls" name_upper="DNS_OVER_TLS" type="int32" default="-1" description="Whether DNSOverTls (dns-over-tls) is enabled for the connection. DNSOverTls is a technology which uses TLS to encrypt dns traffic. The permitted values are: "yes" (2) use DNSOverTls and disabled fallback, "opportunistic" (1) use DNSOverTls but allow fallback to unencrypted resolution, "no" (0) don't ever use DNSOverTls. If unspecified "default" depends on the plugin used. Systemd-resolved uses global setting. This feature requires a plugin which supports DNSOverTls. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved." /><property name="gateway-ping-timeout" name_upper="GATEWAY_PING_TIMEOUT" type="uint32" default="0" description="If greater than zero, delay success of IP addressing until either the timeout is reached, or an IP gateway replies to a ping." /><property name="id" name_upper="ID" type="string" description="A human readable unique identifier for the connection, like "Work Wi-Fi" or "T-Mobile 3G"." /><property name="interface-name" name_upper="INTERFACE_NAME" type="string" description="The name of the network interface this connection is bound to. If not set, then the connection can be attached to any interface of the appropriate type (subject to restrictions imposed by other settings). For software devices this specifies the name of the created device. For connection types where interface names cannot easily be made persistent (e.g. mobile broadband or USB Ethernet), this property should not be used. Setting this property restricts the interfaces a connection can be used with, and if interface names change or are reordered the connection may be applied to the wrong interface." /><property name="lldp" name_upper="LLDP" type="int32" default="-1" description="Whether LLDP is enabled for the connection." /><property name="llmnr" name_upper="LLMNR" type="int32" default="-1" description="Whether Link-Local Multicast Name Resolution (LLMNR) is enabled for the connection. LLMNR is a protocol based on the Domain Name System (DNS) packet format that allows both IPv4 and IPv6 hosts to perform name resolution for hosts on the same local link. The permitted values are: "yes" (2) register hostname and resolving for the connection, "no" (0) disable LLMNR for the interface, "resolve" (1) do not register hostname but allow resolving of LLMNR host names If unspecified, "default" ultimately depends on the DNS plugin (which for systemd-resolved currently means "yes"). This feature requires a plugin which supports LLMNR. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved." /><property name="master" name_upper="MASTER" type="string" description="Interface name of the master device or UUID of the master connection." /><property name="mdns" name_upper="MDNS" type="int32" default="-1" description="Whether mDNS is enabled for the connection. The permitted values are: "yes" (2) register hostname and resolving for the connection, "no" (0) disable mDNS for the interface, "resolve" (1) do not register hostname but allow resolving of mDNS host names and "default" (-1) to allow lookup of a global default in NetworkManager.conf. If unspecified, "default" ultimately depends on the DNS plugin (which for systemd-resolved currently means "no"). This feature requires a plugin which supports mDNS. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved." /><property name="metered" name_upper="METERED" type="NMMetered (int32)" description="Whether the connection is metered. When updating this property on a currently activated connection, the change takes effect immediately." /><property name="mud-url" name_upper="MUD_URL" type="string" description="If configured, set to a Manufacturer Usage Description (MUD) URL that points to manufacturer-recommended network policies for IoT devices. It is transmitted as a DHCPv4 or DHCPv6 option. The value must be a valid URL starting with "https://". The special value "none" is allowed to indicate that no MUD URL is used. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the ultimate default is "none"." /><property name="multi-connect" name_upper="MULTI_CONNECT" type="int32" default="0" description="Specifies whether the profile can be active multiple times at a particular moment. The value is of type NMConnectionMultiConnect." /><property name="permissions" name_upper="PERMISSIONS" type="array of string" description="An array of strings defining what access a given user has to this connection. If this is NULL or empty, all users are allowed to access this connection; otherwise users are allowed if and only if they are in this list. When this is not empty, the connection can be active only when one of the specified users is logged into an active session. Each entry is of the form "[type]:[id]:[reserved]"; for example, "user:dcbw:blah". At this time only the "user" [type] is allowed. Any other values are ignored and reserved for future use. [id] is the username that this permission refers to, which may not contain the ":" character. Any [reserved] information present must be ignored and is reserved for future use. All of [type], [id], and [reserved] must be valid UTF-8." /><property name="read-only" name_upper="READ_ONLY" type="boolean" default="FALSE" description="FALSE if the connection can be modified using the provided settings service's D-Bus interface with the right privileges, or TRUE if the connection is read-only and cannot be modified." /><property name="secondaries" name_upper="SECONDARIES" type="array of string" description="List of connection UUIDs that should be activated when the base connection itself is activated. Currently, only VPN connections are supported." /><property name="slave-type" name_upper="SLAVE_TYPE" type="string" description="Setting name of the device type of this slave's master connection (eg, "bond"), or NULL if this connection is not a slave." /><property name="stable-id" name_upper="STABLE_ID" type="string" description="This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret-key. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are "${CONNECTION}", "${DEVICE}", "${MAC}", "${BOOT}", "${RANDOM}". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that "${DEVICE}" corresponds to the interface name of the device and "${MAC}" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as "$$". For example, set it to "${CONNECTION}-${BOOT}-${DEVICE}" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to "${CONNECTION}" and uses a unique, fixed ID for the connection." /><property name="timestamp" name_upper="TIMESTAMP" type="uint64" default="0" description="The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved)." /><property name="type" name_upper="TYPE" type="string" description="Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, "802-3-ethernet" or "802-11-wireless" or "bluetooth", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, "vpn" or "bridge", etc)." /><property name="uuid" name_upper="UUID" type="string" description="A universally unique identifier for the connection, for example generated with libuuid. It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network. For example, it should not be changed when the "id" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or "type" property changes. The UUID must be in the format "2815492f-7e56-435e-b2e9-246bd7cdc664" (ie, contains only hexadecimal characters and "-")." /><property name="wait-device-timeout" name_upper="WAIT_DEVICE_TIMEOUT" type="int32" default="-1" description="Timeout in milliseconds to wait for device at startup. During boot, devices may take a while to be detected by the driver. This property will cause to delay NetworkManager-wait-online.service and nm-online to give the device a chance to appear. This works by waiting for the given timeout until a compatible device for the profile is available and managed. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time." /><property name="zone" name_upper="ZONE" type="string" description="The trust level of a the connection. Free form case-insensitive string (for example "Home", "Work", "Public"). NULL or unspecified zone means the connection will be placed in the default zone as defined by the firewall. When updating this property on a currently activated connection, the change takes effect immediately." /></setting><setting name="6lowpan" description="6LoWPAN Settings" name_upper="6LOWPAN"><property name="parent" name_upper="PARENT" type="string" description="If given, specifies the parent interface name or parent connection UUID from which this 6LowPAN interface should be created." /></setting><setting name="802-1x" description="IEEE 802.1x Authentication Settings" name_upper="802_1X"><property name="altsubject-matches" name_upper="ALTSUBJECT_MATCHES" type="array of string" description="List of strings to be matched against the altSubjectName of the certificate presented by the authentication server. If the list is empty, no verification of the server certificate's altSubjectName is performed." /><property name="anonymous-identity" name_upper="ANONYMOUS_IDENTITY" type="string" description="Anonymous identity string for EAP authentication methods. Used as the unencrypted identity with EAP types that support different tunneled identity like EAP-TTLS." /><property name="auth-timeout" name_upper="AUTH_TIMEOUT" type="int32" default="0" description="A timeout for the authentication. Zero means the global default; if the global default is not set, the authentication timeout is 25 seconds." /><property name="ca-cert" name_upper="CA_CERT" type="byte array" description="Contains the CA certificate if used by the EAP method specified in the "eap" property. Certificate data is specified using a "scheme"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory." /><property name="ca-cert-password" name_upper="CA_CERT_PASSWORD" type="string" description="The password used to access the CA certificate stored in "ca-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login." /><property name="ca-cert-password-flags" name_upper="CA_CERT_PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "ca-cert-password" property." /><property name="ca-path" name_upper="CA_PATH" type="string" description="UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the "ca-cert" property. If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored." /><property name="client-cert" name_upper="CLIENT_CERT" type="byte array" description="Contains the client certificate if used by the EAP method specified in the "eap" property. Certificate data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte." /><property name="client-cert-password" name_upper="CLIENT_CERT_PASSWORD" type="string" description="The password used to access the client certificate stored in "client-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login." /><property name="client-cert-password-flags" name_upper="CLIENT_CERT_PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "client-cert-password" property." /><property name="domain-match" name_upper="DOMAIN_MATCH" type="string" description="Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a ";" delimited list." /><property name="domain-suffix-match" name_upper="DOMAIN_SUFFIX_MATCH" type="string" description="Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a ";" delimited list." /><property name="eap" name_upper="EAP" type="array of string" description="The allowed EAP method to be used when authenticating to the network with 802.1x. Valid methods are: "leap", "md5", "tls", "peap", "ttls", "pwd", and "fast". Each method requires different configuration using the properties of this setting; refer to wpa_supplicant documentation for the allowed combinations." /><property name="identity" name_upper="IDENTITY" type="string" description="Identity string for EAP authentication methods. Often the user's user or login name." /><property name="optional" name_upper="OPTIONAL" type="boolean" default="FALSE" description="Whether the 802.1X authentication is optional. If TRUE, the activation will continue even after a timeout or an authentication failure. Setting the property to TRUE is currently allowed only for Ethernet connections. If set to FALSE, the activation can continue only after a successful authentication." /><property name="pac-file" name_upper="PAC_FILE" type="string" description="UTF-8 encoded file path containing PAC for EAP-FAST." /><property name="password" name_upper="PASSWORD" type="string" description="UTF-8 encoded password used for EAP authentication methods. If both the "password" property and the "password-raw" property are specified, "password" is preferred." /><property name="password-flags" name_upper="PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "password" property." /><property name="password-raw" name_upper="PASSWORD_RAW" type="byte array" description="Password used for EAP authentication methods, given as a byte array to allow passwords in other encodings than UTF-8 to be used. If both the "password" property and the "password-raw" property are specified, "password" is preferred." /><property name="password-raw-flags" name_upper="PASSWORD_RAW_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "password-raw" property." /><property name="phase1-auth-flags" name_upper="PHASE1_AUTH_FLAGS" type="uint32" default="0" description="Specifies authentication flags to use in "phase 1" outer authentication using NMSetting8021xAuthFlags options. The individual TLS versions can be explicitly disabled. If a certain TLS disable flag is not set, it is up to the supplicant to allow or forbid it. The TLS options map to tls_disable_tlsv1_x settings. See the wpa_supplicant documentation for more details." /><property name="phase1-fast-provisioning" name_upper="PHASE1_FAST_PROVISIONING" type="string" description="Enables or disables in-line provisioning of EAP-FAST credentials when FAST is specified as the EAP method in the "eap" property. Recognized values are "0" (disabled), "1" (allow unauthenticated provisioning), "2" (allow authenticated provisioning), and "3" (allow both authenticated and unauthenticated provisioning). See the wpa_supplicant documentation for more details." /><property name="phase1-peaplabel" name_upper="PHASE1_PEAPLABEL" type="string" description="Forces use of the new PEAP label during key derivation. Some RADIUS servers may require forcing the new PEAP label to interoperate with PEAPv1. Set to "1" to force use of the new PEAP label. See the wpa_supplicant documentation for more details." /><property name="phase1-peapver" name_upper="PHASE1_PEAPVER" type="string" description="Forces which PEAP version is used when PEAP is set as the EAP method in the "eap" property. When unset, the version reported by the server will be used. Sometimes when using older RADIUS servers, it is necessary to force the client to use a particular PEAP version. To do so, this property may be set to "0" or "1" to force that specific PEAP version." /><property name="phase2-altsubject-matches" name_upper="PHASE2_ALTSUBJECT_MATCHES" type="array of string" description="List of strings to be matched against the altSubjectName of the certificate presented by the authentication server during the inner "phase 2" authentication. If the list is empty, no verification of the server certificate's altSubjectName is performed." /><property name="phase2-auth" name_upper="PHASE2_AUTH" type="string" description="Specifies the allowed "phase 2" inner authentication method when an EAP method that uses an inner TLS tunnel is specified in the "eap" property. For TTLS this property selects one of the supported non-EAP inner methods: "pap", "chap", "mschap", "mschapv2" while "phase2-autheap" selects an EAP inner method. For PEAP this selects an inner EAP method, one of: "gtc", "otp", "md5" and "tls". Each "phase 2" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details. Both "phase2-auth" and "phase2-autheap" cannot be specified." /><property name="phase2-autheap" name_upper="PHASE2_AUTHEAP" type="string" description="Specifies the allowed "phase 2" inner EAP-based authentication method when TTLS is specified in the "eap" property. Recognized EAP-based "phase 2" methods are "md5", "mschapv2", "otp", "gtc", and "tls". Each "phase 2" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details." /><property name="phase2-ca-cert" name_upper="PHASE2_CA_CERT" type="byte array" description="Contains the "phase 2" CA certificate if used by the EAP method specified in the "phase2-auth" or "phase2-autheap" properties. Certificate data is specified using a "scheme"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory." /><property name="phase2-ca-cert-password" name_upper="PHASE2_CA_CERT_PASSWORD" type="string" description="The password used to access the "phase2" CA certificate stored in "phase2-ca-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login." /><property name="phase2-ca-cert-password-flags" name_upper="PHASE2_CA_CERT_PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "phase2-ca-cert-password" property." /><property name="phase2-ca-path" name_upper="PHASE2_CA_PATH" type="string" description="UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the "phase2-ca-cert" property. If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored." /><property name="phase2-client-cert" name_upper="PHASE2_CLIENT_CERT" type="byte array" description="Contains the "phase 2" client certificate if used by the EAP method specified in the "phase2-auth" or "phase2-autheap" properties. Certificate data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended." /><property name="phase2-client-cert-password" name_upper="PHASE2_CLIENT_CERT_PASSWORD" type="string" description="The password used to access the "phase2" client certificate stored in "phase2-client-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login." /><property name="phase2-client-cert-password-flags" name_upper="PHASE2_CLIENT_CERT_PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "phase2-client-cert-password" property." /><property name="phase2-domain-match" name_upper="PHASE2_DOMAIN_MATCH" type="string" description="Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner "phase 2" authentication. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a ";" delimited list." /><property name="phase2-domain-suffix-match" name_upper="PHASE2_DOMAIN_SUFFIX_MATCH" type="string" description="Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner "phase 2" authentication. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a ";" delimited list." /><property name="phase2-private-key" name_upper="PHASE2_PRIVATE_KEY" type="byte array" description="Contains the "phase 2" inner private key when the "phase2-auth" or "phase2-autheap" property is set to "tls". Key data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the "phase2-private-key-password" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte, and as with the blob scheme the "phase2-private-key-password" property must be set to the password used to decode the PKCS#12 private key and certificate." /><property name="phase2-private-key-password" name_upper="PHASE2_PRIVATE_KEY_PASSWORD" type="string" description="The password used to decrypt the "phase 2" private key specified in the "phase2-private-key" property when the private key either uses the path scheme, or is a PKCS#12 format key." /><property name="phase2-private-key-password-flags" name_upper="PHASE2_PRIVATE_KEY_PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "phase2-private-key-password" property." /><property name="phase2-subject-match" name_upper="PHASE2_SUBJECT_MATCH" type="string" description="Substring to be matched against the subject of the certificate presented by the authentication server during the inner "phase 2" authentication. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:phase2-domain-suffix-match." /><property name="pin" name_upper="PIN" type="string" description="PIN used for EAP authentication methods." /><property name="pin-flags" name_upper="PIN_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "pin" property." /><property name="private-key" name_upper="PRIVATE_KEY" type="byte array" description="Contains the private key when the "eap" property is set to "tls". Key data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the "private-key-password" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte, and as with the blob scheme the "private-key-password" property must be set to the password used to decode the PKCS#12 private key and certificate. WARNING: "private-key" is not a "secret" property, and thus unencrypted private key data using the BLOB scheme may be readable by unprivileged users. Private keys should always be encrypted with a private key password to prevent unauthorized access to unencrypted private key data." /><property name="private-key-password" name_upper="PRIVATE_KEY_PASSWORD" type="string" description="The password used to decrypt the private key specified in the "private-key" property when the private key either uses the path scheme, or if the private key is a PKCS#12 format key." /><property name="private-key-password-flags" name_upper="PRIVATE_KEY_PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "private-key-password" property." /><property name="subject-match" name_upper="SUBJECT_MATCH" type="string" description="Substring to be matched against the subject of the certificate presented by the authentication server. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:domain-suffix-match." /><property name="system-ca-certs" name_upper="SYSTEM_CA_CERTS" type="boolean" default="FALSE" description="When TRUE, overrides the "ca-path" and "phase2-ca-path" properties using the system CA directory specified at configure time with the --system-ca-path switch. The certificates in this directory are added to the verification chain in addition to any certificates specified by the "ca-cert" and "phase2-ca-cert" properties. If the path provided with --system-ca-path is rather a file name (bundle of trusted CA certificates), it overrides "ca-cert" and "phase2-ca-cert" properties instead (sets ca_cert/ca_cert2 options for wpa_supplicant)." /></setting><setting name="adsl" description="ADSL Settings" name_upper="ADSL"><property name="encapsulation" name_upper="ENCAPSULATION" type="string" description="Encapsulation of ADSL connection. Can be "vcmux" or "llc"." /><property name="password" name_upper="PASSWORD" type="string" description="Password used to authenticate with the ADSL service." /><property name="password-flags" name_upper="PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "password" property." /><property name="protocol" name_upper="PROTOCOL" type="string" description="ADSL connection protocol. Can be "pppoa", "pppoe" or "ipoatm"." /><property name="username" name_upper="USERNAME" type="string" description="Username used to authenticate with the ADSL service." /><property name="vci" name_upper="VCI" type="uint32" default="0" description="VCI of ADSL connection" /><property name="vpi" name_upper="VPI" type="uint32" default="0" description="VPI of ADSL connection" /></setting><setting name="bluetooth" description="Bluetooth Settings" name_upper="BLUETOOTH"><property name="bdaddr" name_upper="BDADDR" type="byte array" description="The Bluetooth address of the device." /><property name="type" name_upper="TYPE" type="string" description="Either "dun" for Dial-Up Networking connections or "panu" for Personal Area Networking connections to devices supporting the NAP profile." /></setting><setting name="bond" description="Bonding Settings" name_upper="BOND"><property name="options" name_upper="OPTIONS" type="dict of string to string" default="{'mode': 'balance-rr'}" description="Dictionary of key/value pairs of bonding options. Both keys and values must be strings. Option names must contain only alphanumeric characters (ie, [a-zA-Z0-9])." /></setting><setting name="bridge" description="Bridging Settings" name_upper="BRIDGE"><property name="ageing-time" name_upper="AGEING_TIME" type="uint32" default="300" description="The Ethernet MAC address aging time, in seconds." /><property name="forward-delay" name_upper="FORWARD_DELAY" type="uint32" default="15" description="The Spanning Tree Protocol (STP) forwarding delay, in seconds." /><property name="group-address" name_upper="GROUP_ADDRESS" type="byte array" description="If specified, The MAC address of the multicast group this bridge uses for STP. The address must be a link-local address in standard Ethernet MAC address format, ie an address of the form 01:80:C2:00:00:0X, with X in [0, 4..F]. If not specified the default value is 01:80:C2:00:00:00." /><property name="group-forward-mask" name_upper="GROUP_FORWARD_MASK" type="uint32" default="0" description="A mask of group addresses to forward. Usually, group addresses in the range from 01:80:C2:00:00:00 to 01:80:C2:00:00:0F are not forwarded according to standards. This property is a mask of 16 bits, each corresponding to a group address in that range that must be forwarded. The mask can't have bits 0, 1 or 2 set because they are used for STP, MAC pause frames and LACP." /><property name="hello-time" name_upper="HELLO_TIME" type="uint32" default="2" description="The Spanning Tree Protocol (STP) hello time, in seconds." /><property name="mac-address" name_upper="MAC_ADDRESS" type="byte array" description="If specified, the MAC address of bridge. When creating a new bridge, this MAC address will be set. If this field is left unspecified, the "ethernet.cloned-mac-address" is referred instead to generate the initial MAC address. Note that setting "ethernet.cloned-mac-address" anyway overwrites the MAC address of the bridge later while activating the bridge. Hence, this property is deprecated. Deprecated: 1" /><property name="max-age" name_upper="MAX_AGE" type="uint32" default="20" description="The Spanning Tree Protocol (STP) maximum message age, in seconds." /><property name="multicast-hash-max" name_upper="MULTICAST_HASH_MAX" type="uint32" default="4096" description="Set maximum size of multicast hash table (value must be a power of 2)." /><property name="multicast-last-member-count" name_upper="MULTICAST_LAST_MEMBER_COUNT" type="uint32" default="2" description="Set the number of queries the bridge will send before stopping forwarding a multicast group after a "leave" message has been received." /><property name="multicast-last-member-interval" name_upper="MULTICAST_LAST_MEMBER_INTERVAL" type="uint64" default="100" description="Set interval (in deciseconds) between queries to find remaining members of a group, after a "leave" message is received." /><property name="multicast-membership-interval" name_upper="MULTICAST_MEMBERSHIP_INTERVAL" type="uint64" default="26000" description="Set delay (in deciseconds) after which the bridge will leave a group, if no membership reports for this group are received." /><property name="multicast-querier" name_upper="MULTICAST_QUERIER" type="boolean" default="FALSE" description="Enable or disable sending of multicast queries by the bridge. If not specified the option is disabled." /><property name="multicast-querier-interval" name_upper="MULTICAST_QUERIER_INTERVAL" type="uint64" default="25500" description="If no queries are seen after this delay (in deciseconds) has passed, the bridge will start to send its own queries." /><property name="multicast-query-interval" name_upper="MULTICAST_QUERY_INTERVAL" type="uint64" default="12500" description="Interval (in deciseconds) between queries sent by the bridge after the end of the startup phase." /><property name="multicast-query-response-interval" name_upper="MULTICAST_QUERY_RESPONSE_INTERVAL" type="uint64" default="1000" description="Set the Max Response Time/Max Response Delay (in deciseconds) for IGMP/MLD queries sent by the bridge." /><property name="multicast-query-use-ifaddr" name_upper="MULTICAST_QUERY_USE_IFADDR" type="boolean" default="FALSE" description="If enabled the bridge's own IP address is used as the source address for IGMP queries otherwise the default of 0.0.0.0 is used." /><property name="multicast-router" name_upper="MULTICAST_ROUTER" type="string" description="Sets bridge's multicast router. Multicast-snooping must be enabled for this option to work. Supported values are: 'auto', 'disabled', 'enabled' to which kernel assigns the numbers 1, 0, and 2, respectively. If not specified the default value is 'auto' (1)." /><property name="multicast-snooping" name_upper="MULTICAST_SNOOPING" type="boolean" default="TRUE" description="Controls whether IGMP snooping is enabled for this bridge. Note that if snooping was automatically disabled due to hash collisions, the system may refuse to enable the feature until the collisions are resolved." /><property name="multicast-startup-query-count" name_upper="MULTICAST_STARTUP_QUERY_COUNT" type="uint32" default="2" description="Set the number of IGMP queries to send during startup phase." /><property name="multicast-startup-query-interval" name_upper="MULTICAST_STARTUP_QUERY_INTERVAL" type="uint64" default="3125" description="Sets the time (in deciseconds) between queries sent out at startup to determine membership information." /><property name="priority" name_upper="PRIORITY" type="uint32" default="32768" description="Sets the Spanning Tree Protocol (STP) priority for this bridge. Lower values are "better"; the lowest priority bridge will be elected the root bridge." /><property name="stp" name_upper="STP" type="boolean" default="TRUE" description="Controls whether Spanning Tree Protocol (STP) is enabled for this bridge." /><property name="vlan-default-pvid" name_upper="VLAN_DEFAULT_PVID" type="uint32" default="1" description="The default PVID for the ports of the bridge, that is the VLAN id assigned to incoming untagged frames." /><property name="vlan-filtering" name_upper="VLAN_FILTERING" type="boolean" default="FALSE" description="Control whether VLAN filtering is enabled on the bridge." /><property name="vlan-protocol" name_upper="VLAN_PROTOCOL" type="string" description="If specified, the protocol used for VLAN filtering. Supported values are: '802.1Q', '802.1ad'. If not specified the default value is '802.1Q'." /><property name="vlan-stats-enabled" name_upper="VLAN_STATS_ENABLED" type="boolean" default="FALSE" description="Controls whether per-VLAN stats accounting is enabled." /><property name="vlans" name_upper="VLANS" type="array of vardict" description="Array of bridge VLAN objects. In addition to the VLANs specified here, the bridge will also have the default-pvid VLAN configured by the bridge.vlan-default-pvid property. In nmcli the VLAN list can be specified with the following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash." /></setting><setting name="bridge-port" description="Bridge Port Settings" name_upper="BRIDGE_PORT"><property name="hairpin-mode" name_upper="HAIRPIN_MODE" type="boolean" default="FALSE" description="Enables or disables "hairpin mode" for the port, which allows frames to be sent back out through the port the frame was received on." /><property name="path-cost" name_upper="PATH_COST" type="uint32" default="100" description="The Spanning Tree Protocol (STP) port cost for destinations via this port." /><property name="priority" name_upper="PRIORITY" type="uint32" default="32" description="The Spanning Tree Protocol (STP) priority of this bridge port." /><property name="vlans" name_upper="VLANS" type="array of vardict" description="Array of bridge VLAN objects. In addition to the VLANs specified here, the port will also have the default-pvid VLAN configured on the bridge by the bridge.vlan-default-pvid property. In nmcli the VLAN list can be specified with the following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash." /></setting><setting name="cdma" description="CDMA-based Mobile Broadband Settings" name_upper="CDMA"><property name="mtu" name_upper="MTU" type="uint32" default="0" description="If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames." /><property name="number" name_upper="NUMBER" type="string" description="The number to dial to establish the connection to the CDMA-based mobile broadband network, if any. If not specified, the default number (#777) is used when required." /><property name="password" name_upper="PASSWORD" type="string" description="The password used to authenticate with the network, if required. Many providers do not require a password, or accept any password. But if a password is required, it is specified here." /><property name="password-flags" name_upper="PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "password" property." /><property name="username" name_upper="USERNAME" type="string" description="The username used to authenticate with the network, if required. Many providers do not require a username, or accept any username. But if a username is required, it is specified here." /></setting><setting name="dcb" description="Data Center Bridging Settings" name_upper="DCB"><property name="app-fcoe-flags" name_upper="APP_FCOE_FLAGS" type="NMSettingDcbFlags (uint32)" description="Specifies the NMSettingDcbFlags for the DCB FCoE application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." /><property name="app-fcoe-mode" name_upper="APP_FCOE_MODE" type="string" description="The FCoE controller mode; either "fabric" or "vn2vn". Since 1.34, NULL is the default and means "fabric". Before 1.34, NULL was rejected as invalid and the default was "fabric"." /><property name="app-fcoe-priority" name_upper="APP_FCOE_PRIORITY" type="int32" default="-1" description="The highest User Priority (0 - 7) which FCoE frames should use, or -1 for default priority. Only used when the "app-fcoe-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag." /><property name="app-fip-flags" name_upper="APP_FIP_FLAGS" type="NMSettingDcbFlags (uint32)" description="Specifies the NMSettingDcbFlags for the DCB FIP application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." /><property name="app-fip-priority" name_upper="APP_FIP_PRIORITY" type="int32" default="-1" description="The highest User Priority (0 - 7) which FIP frames should use, or -1 for default priority. Only used when the "app-fip-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag." /><property name="app-iscsi-flags" name_upper="APP_ISCSI_FLAGS" type="NMSettingDcbFlags (uint32)" description="Specifies the NMSettingDcbFlags for the DCB iSCSI application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." /><property name="app-iscsi-priority" name_upper="APP_ISCSI_PRIORITY" type="int32" default="-1" description="The highest User Priority (0 - 7) which iSCSI frames should use, or -1 for default priority. Only used when the "app-iscsi-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag." /><property name="priority-bandwidth" name_upper="PRIORITY_BANDWIDTH" type="array of uint32" description="An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the percentage of bandwidth of the priority's assigned group that the priority may use. The sum of all percentages for priorities which belong to the same group must total 100 percents." /><property name="priority-flow-control" name_upper="PRIORITY_FLOW_CONTROL" type="array of uint32" description="An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the corresponding priority should transmit priority pause." /><property name="priority-flow-control-flags" name_upper="PRIORITY_FLOW_CONTROL_FLAGS" type="NMSettingDcbFlags (uint32)" description="Specifies the NMSettingDcbFlags for DCB Priority Flow Control (PFC). Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." /><property name="priority-group-bandwidth" name_upper="PRIORITY_GROUP_BANDWIDTH" type="array of uint32" description="An array of 8 uint values, where the array index corresponds to the Priority Group ID (0 - 7) and the value indicates the percentage of link bandwidth allocated to that group. Allowed values are 0 - 100, and the sum of all values must total 100 percents." /><property name="priority-group-flags" name_upper="PRIORITY_GROUP_FLAGS" type="NMSettingDcbFlags (uint32)" description="Specifies the NMSettingDcbFlags for DCB Priority Groups. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." /><property name="priority-group-id" name_upper="PRIORITY_GROUP_ID" type="array of uint32" description="An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the Priority Group ID. Allowed Priority Group ID values are 0 - 7 or 15 for the unrestricted group." /><property name="priority-strict-bandwidth" name_upper="PRIORITY_STRICT_BANDWIDTH" type="array of uint32" description="An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the priority may use all of the bandwidth allocated to its assigned group." /><property name="priority-traffic-class" name_upper="PRIORITY_TRAFFIC_CLASS" type="array of uint32" description="An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the traffic class (0 - 7) to which the priority is mapped." /></setting><setting name="dummy" description="Dummy Link Settings" name_upper="DUMMY" /><setting name="ethtool" description="Ethtool Ethernet Settings" name_upper="ETHTOOL" /><setting name="generic" description="Generic Link Settings" name_upper="GENERIC" /><setting name="gsm" description="GSM-based Mobile Broadband Settings" name_upper="GSM"><property name="apn" name_upper="APN" type="string" description="The GPRS Access Point Name specifying the APN used when establishing a data session with the GSM-based network. The APN often determines how the user will be billed for their network usage and whether the user has access to the Internet or just a provider-specific walled-garden, so it is important to use the correct APN for the user's mobile broadband plan. The APN may only be composed of the characters a-z, 0-9, ., and - per GSM 03.60 Section 14.9." /><property name="auto-config" name_upper="AUTO_CONFIG" type="boolean" default="FALSE" description="When TRUE, the settings such as APN, username, or password will default to values that match the network the modem will register to in the Mobile Broadband Provider database." /><property name="device-id" name_upper="DEVICE_ID" type="string" description="The device unique identifier (as given by the WWAN management service) which this connection applies to. If given, the connection will only apply to the specified device." /><property name="home-only" name_upper="HOME_ONLY" type="boolean" default="FALSE" description="When TRUE, only connections to the home network will be allowed. Connections to roaming networks will not be made." /><property name="mtu" name_upper="MTU" type="uint32" default="0" description="If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames." /><property name="network-id" name_upper="NETWORK_ID" type="string" description="The Network ID (GSM LAI format, ie MCC-MNC) to force specific network registration. If the Network ID is specified, NetworkManager will attempt to force the device to register only on the specified network. This can be used to ensure that the device does not roam when direct roaming control of the device is not otherwise possible." /><property name="number" name_upper="NUMBER" type="string" description="Legacy setting that used to help establishing PPP data sessions for GSM-based modems. Deprecated: 1" /><property name="password" name_upper="PASSWORD" type="string" description="The password used to authenticate with the network, if required. Many providers do not require a password, or accept any password. But if a password is required, it is specified here." /><property name="password-flags" name_upper="PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "password" property." /><property name="pin" name_upper="PIN" type="string" description="If the SIM is locked with a PIN it must be unlocked before any other operations are requested. Specify the PIN here to allow operation of the device." /><property name="pin-flags" name_upper="PIN_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "pin" property." /><property name="sim-id" name_upper="SIM_ID" type="string" description="The SIM card unique identifier (as given by the WWAN management service) which this connection applies to. If given, the connection will apply to any device also allowed by "device-id" which contains a SIM card matching the given identifier." /><property name="sim-operator-id" name_upper="SIM_OPERATOR_ID" type="string" description="A MCC/MNC string like "310260" or "21601" identifying the specific mobile network operator which this connection applies to. If given, the connection will apply to any device also allowed by "device-id" and "sim-id" which contains a SIM card provisioned by the given operator." /><property name="username" name_upper="USERNAME" type="string" description="The username used to authenticate with the network, if required. Many providers do not require a username, or accept any username. But if a username is required, it is specified here." /></setting><setting name="infiniband" description="Infiniband Settings" name_upper="INFINIBAND"><property name="mac-address" name_upper="MAC_ADDRESS" type="byte array" description="If specified, this connection will only apply to the IPoIB device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing)." /><property name="mtu" name_upper="MTU" type="uint32" default="0" description="If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames." /><property name="p-key" name_upper="P_KEY" type="int32" default="-1" description="The InfiniBand P_Key to use for this device. A value of -1 means to use the default P_Key (aka "the P_Key at index 0"). Otherwise, it is a 16-bit unsigned integer, whose high bit is set if it is a "full membership" P_Key." /><property name="parent" name_upper="PARENT" type="string" description="The interface name of the parent device of this device. Normally NULL, but if the "p_key" property is set, then you must specify the base device by setting either this property or "mac-address"." /><property name="transport-mode" name_upper="TRANSPORT_MODE" type="string" description="The IP-over-InfiniBand transport mode. Either "datagram" or "connected"." /></setting><setting name="ipv4" description="IPv4 Settings" name_upper="IP4_CONFIG"><property name="addresses" name_upper="ADDRESSES" type="a comma separated list of addresses" description="A list of IPv4 addresses and their prefix length. Multiple addresses can be separated by comma. For example "192.168.1.5/24, 10.1.0.5/24". The addresses are listed in decreasing priority, meaning the first address will be the primary address." /><property name="dad-timeout" name_upper="DAD_TIMEOUT" type="int32" default="-1" description="Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4." /><property name="dhcp-client-id" name_upper="DHCP_CLIENT_ID" type="string" description="A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string ('aa:bb:cc') it is interpreted as a binary client ID, in which case the first byte is assumed to be the 'type' field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the 'type' field is set to 0. The special values "mac" and "perm-mac" are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. The special value "ipv6-duid" uses the DUID from "ipv6.dhcp-duid" property as an RFC4361-compliant client identifier. As IAID it uses "ipv4.dhcp-iaid" and falls back to "ipv6.dhcp-iaid" if unset. The special value "duid" generates a RFC4361-compliant client identifier based on "ipv4.dhcp-iaid" and uses a DUID generated by hashing /etc/machine-id. The special value "stable" is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the "${DEVICE}" or "${MAC}" specifier to get a per-device key. If unset, a globally configured default is used. If still unset, the default depends on the DHCP plugin." /><property name="dhcp-fqdn" name_upper="DHCP_FQDN" type="string" description="If the "dhcp-send-hostname" property is TRUE, then the specified FQDN will be sent to the DHCP server when acquiring a lease. This property and "dhcp-hostname" are mutually exclusive and cannot be set at the same time." /><property name="dhcp-hostname" name_upper="DHCP_HOSTNAME" type="string" description="If the "dhcp-send-hostname" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and "dhcp-fqdn" are mutually exclusive and cannot be set at the same time." /><property name="dhcp-hostname-flags" name_upper="DHCP_HOSTNAME_FLAGS" type="uint32" default="0" description="Flags for the DHCP hostname and FQDN. Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4). When no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the standard FQDN flags are set in the request: NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6. When this property is set to the default value NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags described above are sent in the DHCP requests." /><property name="dhcp-iaid" name_upper="DHCP_IAID" type="string" description="A string containing the "Identity Association Identifier" (IAID) used by the DHCP client. The property is a 32-bit decimal value or a special value among "mac", "perm-mac", "ifname" and "stable". When set to "mac" (or "perm-mac"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to "ifname", the IAID is computed by hashing the interface name. The special value "stable" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be "ifname". Note that at the moment this property is ignored for IPv6 by dhclient, which always derives the IAID from the MAC address." /><property name="dhcp-reject-servers" name_upper="DHCP_REJECT_SERVERS" type="array of string" description="Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. "192.168.122.0/24"). This property is currently not implemented for DHCPv6." /><property name="dhcp-send-hostname" name_upper="DHCP_SEND_HOSTNAME" type="boolean" default="TRUE" description="If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer. If the "dhcp-hostname" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent." /><property name="dhcp-timeout" name_upper="DHCP_TIMEOUT" type="int32" default="0" description="A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity." /><property name="dhcp-vendor-class-identifier" name_upper="DHCP_VENDOR_CLASS_IDENTIFIER" type="string" description="The Vendor Class Identifier DHCP option (60). Special characters in the data string may be escaped using C-style escapes, nevertheless this property cannot contain nul bytes. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the DHCP option is not sent to the server. Since 1.28" /><property name="dns" name_upper="DNS" type="array of uint32" description="Array of IP addresses of DNS servers." /><property name="dns-options" name_upper="DNS_OPTIONS" type="array of string" description="Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties. The currently supported options are "attempts", "debug", "edns0", "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names", "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single-request", "single-request-reopen", "timeout", "trust-ad", "use-vc". The "trust-ad" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have "trust-ad" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then "edns0" and "trust-ad" are automatically added." /><property name="dns-priority" name_upper="DNS_PRIORITY" type="int32" default="0" description="DNS servers priority. The relative priority for DNS servers specified by this setting. A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections. It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the "rotate" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain. Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually). When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins. If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured." /><property name="dns-search" name_upper="DNS_SEARCH" type="array of string" description="Array of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting." /><property name="gateway" name_upper="GATEWAY" type="string" description="The gateway associated with this configuration. This is only meaningful if "addresses" is also set. The gateway's main purpose is to control the next hop of the standard default route on the device. Hence, the gateway property conflicts with "never-default" and will be automatically dropped if the IP configuration is set to never-default. As an alternative to set the gateway, configure a static default route with /0 as prefix length." /><property name="ignore-auto-dns" name_upper="IGNORE_AUTO_DNS" type="boolean" default="FALSE" description="When "method" is set to "auto" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the "dns" and "dns-search" properties, if any, are used." /><property name="ignore-auto-routes" name_upper="IGNORE_AUTO_ROUTES" type="boolean" default="FALSE" description="When "method" is set to "auto" and this property to TRUE, automatically configured routes are ignored and only routes specified in the "routes" property, if any, are used." /><property name="may-fail" name_upper="MAY_FAIL" type="boolean" default="TRUE" description="If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out. Note that at least one IP configuration must succeed or overall network configuration will still fail. For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully." /><property name="method" name_upper="METHOD" type="string" description="IP configuration method. NMSettingIP4Config and NMSettingIP6Config both support "disabled", "auto", "manual", and "link-local". See the subclass-specific documentation for other values. In general, for the "auto" method, properties such as "dns" and "routes" specify information that is added on to the information returned from automatic configuration. The "ignore-auto-routes" and "ignore-auto-dns" properties modify this behavior. For methods that imply no upstream network, such as "shared" or "link-local", these properties must be empty. For IPv4 method "shared", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared." /><property name="never-default" name_upper="NEVER_DEFAULT" type="boolean" default="FALSE" description="If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager." /><property name="required-timeout" name_upper="REQUIRED_TIMEOUT" type="int32" default="-1" description="The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. Note that if "may-fail" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero)." /><property name="route-metric" name_upper="ROUTE_METRIC" type="int64" default="-1" description="The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric." /><property name="route-table" name_upper="ROUTE_TABLE" type="uint32" default="0" description="Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager." /><property name="routes" name_upper="ROUTES" type="a comma separated list of routes" description="A list of IPv4 destination addresses, prefix length, optional IPv4 next hop addresses, optional route metric, optional attribute. The valid syntax is: "ip[/prefix] [next-hop] [metric] [attribute=val]...[,ip[/prefix]...]". For example "192.0.2.0/24 10.1.1.1 77, 198.51.100.0/24"."><description-docbook> +<nm-setting-docs><setting name="connection" description="General Connection Profile Settings" name_upper="CONNECTION"><property name="auth-retries" name_upper="AUTH_RETRIES" type="int32" default="-1" description="The number of retries for the authentication. Zero means to try indefinitely; -1 means to use a global default. If the global default is not set, the authentication retries for 3 times before failing the connection. Currently, this only applies to 802-1x authentication." /><property name="autoconnect" name_upper="AUTOCONNECT" type="boolean" default="TRUE" description="Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See "secondaries" as an alternative to automatically connect VPN profiles. If multiple profiles are ready to autoconnect on the same device, the one with the better "connection.autoconnect-priority" is chosen. If the priorities are equal, then the most recently connected profile is activated. If the profiles were not connected earlier or their "connection.timestamp" is identical, the choice is undefined. Depending on "connection.multi-connect", a profile can (auto)connect only once at a time or multiple times." /><property name="autoconnect-priority" name_upper="AUTOCONNECT_PRIORITY" type="int32" default="0" description="The autoconnect priority in range -999 to 999. If the connection is set to autoconnect, connections with higher priority will be preferred. The higher number means higher priority. Defaults to 0. Note that this property only matters if there are more than one candidate profile to select for autoconnect. In case of equal priority, the profile used most recently is chosen." /><property name="autoconnect-retries" name_upper="AUTOCONNECT_RETRIES" type="int32" default="-1" description="The number of times a connection should be tried when autoactivating before giving up. Zero means forever, -1 means the global default (4 times if not overridden). Setting this to 1 means to try activation only once before blocking autoconnect. Note that after a timeout, NetworkManager will try to autoconnect again." /><property name="autoconnect-slaves" name_upper="AUTOCONNECT_SLAVES" type="NMSettingConnectionAutoconnectSlaves (int32)" description="Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties "autoconnect", "autoconnect-priority" and "autoconnect-retries" are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0." /><property name="dns-over-tls" name_upper="DNS_OVER_TLS" type="int32" default="-1" description="Whether DNSOverTls (dns-over-tls) is enabled for the connection. DNSOverTls is a technology which uses TLS to encrypt dns traffic. The permitted values are: "yes" (2) use DNSOverTls and disabled fallback, "opportunistic" (1) use DNSOverTls but allow fallback to unencrypted resolution, "no" (0) don't ever use DNSOverTls. If unspecified "default" depends on the plugin used. Systemd-resolved uses global setting. This feature requires a plugin which supports DNSOverTls. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved." /><property name="gateway-ping-timeout" name_upper="GATEWAY_PING_TIMEOUT" type="uint32" default="0" description="If greater than zero, delay success of IP addressing until either the timeout is reached, or an IP gateway replies to a ping." /><property name="id" name_upper="ID" type="string" description="A human readable unique identifier for the connection, like "Work Wi-Fi" or "T-Mobile 3G"." /><property name="interface-name" name_upper="INTERFACE_NAME" type="string" description="The name of the network interface this connection is bound to. If not set, then the connection can be attached to any interface of the appropriate type (subject to restrictions imposed by other settings). For software devices this specifies the name of the created device. For connection types where interface names cannot easily be made persistent (e.g. mobile broadband or USB Ethernet), this property should not be used. Setting this property restricts the interfaces a connection can be used with, and if interface names change or are reordered the connection may be applied to the wrong interface." /><property name="lldp" name_upper="LLDP" type="int32" default="-1" description="Whether LLDP is enabled for the connection." /><property name="llmnr" name_upper="LLMNR" type="int32" default="-1" description="Whether Link-Local Multicast Name Resolution (LLMNR) is enabled for the connection. LLMNR is a protocol based on the Domain Name System (DNS) packet format that allows both IPv4 and IPv6 hosts to perform name resolution for hosts on the same local link. The permitted values are: "yes" (2) register hostname and resolving for the connection, "no" (0) disable LLMNR for the interface, "resolve" (1) do not register hostname but allow resolving of LLMNR host names If unspecified, "default" ultimately depends on the DNS plugin (which for systemd-resolved currently means "yes"). This feature requires a plugin which supports LLMNR. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved." /><property name="master" name_upper="MASTER" type="string" description="Interface name of the master device or UUID of the master connection." /><property name="mdns" name_upper="MDNS" type="int32" default="-1" description="Whether mDNS is enabled for the connection. The permitted values are: "yes" (2) register hostname and resolving for the connection, "no" (0) disable mDNS for the interface, "resolve" (1) do not register hostname but allow resolving of mDNS host names and "default" (-1) to allow lookup of a global default in NetworkManager.conf. If unspecified, "default" ultimately depends on the DNS plugin (which for systemd-resolved currently means "no"). This feature requires a plugin which supports mDNS. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved." /><property name="metered" name_upper="METERED" type="NMMetered (int32)" description="Whether the connection is metered. When updating this property on a currently activated connection, the change takes effect immediately." /><property name="mud-url" name_upper="MUD_URL" type="string" description="If configured, set to a Manufacturer Usage Description (MUD) URL that points to manufacturer-recommended network policies for IoT devices. It is transmitted as a DHCPv4 or DHCPv6 option. The value must be a valid URL starting with "https://". The special value "none" is allowed to indicate that no MUD URL is used. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the ultimate default is "none"." /><property name="multi-connect" name_upper="MULTI_CONNECT" type="int32" default="0" description="Specifies whether the profile can be active multiple times at a particular moment. The value is of type NMConnectionMultiConnect." /><property name="permissions" name_upper="PERMISSIONS" type="array of string" description="An array of strings defining what access a given user has to this connection. If this is NULL or empty, all users are allowed to access this connection; otherwise users are allowed if and only if they are in this list. When this is not empty, the connection can be active only when one of the specified users is logged into an active session. Each entry is of the form "[type]:[id]:[reserved]"; for example, "user:dcbw:blah". At this time only the "user" [type] is allowed. Any other values are ignored and reserved for future use. [id] is the username that this permission refers to, which may not contain the ":" character. Any [reserved] information present must be ignored and is reserved for future use. All of [type], [id], and [reserved] must be valid UTF-8." /><property name="read-only" name_upper="READ_ONLY" type="boolean" default="FALSE" description="FALSE if the connection can be modified using the provided settings service's D-Bus interface with the right privileges, or TRUE if the connection is read-only and cannot be modified." /><property name="secondaries" name_upper="SECONDARIES" type="array of string" description="List of connection UUIDs that should be activated when the base connection itself is activated. Currently, only VPN connections are supported." /><property name="slave-type" name_upper="SLAVE_TYPE" type="string" description="Setting name of the device type of this slave's master connection (eg, "bond"), or NULL if this connection is not a slave." /><property name="stable-id" name_upper="STABLE_ID" type="string" description="This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual about the secret-key and the host identity. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are "${CONNECTION}", "${DEVICE}", "${MAC}", "${BOOT}", "${RANDOM}". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that "${DEVICE}" corresponds to the interface name of the device and "${MAC}" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as "$$". For example, set it to "${CONNECTION}-${BOOT}-${DEVICE}" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to "${CONNECTION}" and uses a unique, fixed ID for the connection." /><property name="timestamp" name_upper="TIMESTAMP" type="uint64" default="0" description="The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved)." /><property name="type" name_upper="TYPE" type="string" description="Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, "802-3-ethernet" or "802-11-wireless" or "bluetooth", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, "vpn" or "bridge", etc)." /><property name="uuid" name_upper="UUID" type="string" description="A universally unique identifier for the connection, for example generated with libuuid. It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network. For example, it should not be changed when the "id" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or "type" property changes. The UUID must be in the format "2815492f-7e56-435e-b2e9-246bd7cdc664" (ie, contains only hexadecimal characters and "-")." /><property name="wait-device-timeout" name_upper="WAIT_DEVICE_TIMEOUT" type="int32" default="-1" description="Timeout in milliseconds to wait for device at startup. During boot, devices may take a while to be detected by the driver. This property will cause to delay NetworkManager-wait-online.service and nm-online to give the device a chance to appear. This works by waiting for the given timeout until a compatible device for the profile is available and managed. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time." /><property name="zone" name_upper="ZONE" type="string" description="The trust level of a the connection. Free form case-insensitive string (for example "Home", "Work", "Public"). NULL or unspecified zone means the connection will be placed in the default zone as defined by the firewall. When updating this property on a currently activated connection, the change takes effect immediately." /></setting><setting name="6lowpan" description="6LoWPAN Settings" name_upper="6LOWPAN"><property name="parent" name_upper="PARENT" type="string" description="If given, specifies the parent interface name or parent connection UUID from which this 6LowPAN interface should be created." /></setting><setting name="802-1x" description="IEEE 802.1x Authentication Settings" name_upper="802_1X"><property name="altsubject-matches" name_upper="ALTSUBJECT_MATCHES" type="array of string" description="List of strings to be matched against the altSubjectName of the certificate presented by the authentication server. If the list is empty, no verification of the server certificate's altSubjectName is performed." /><property name="anonymous-identity" name_upper="ANONYMOUS_IDENTITY" type="string" description="Anonymous identity string for EAP authentication methods. Used as the unencrypted identity with EAP types that support different tunneled identity like EAP-TTLS." /><property name="auth-timeout" name_upper="AUTH_TIMEOUT" type="int32" default="0" description="A timeout for the authentication. Zero means the global default; if the global default is not set, the authentication timeout is 25 seconds." /><property name="ca-cert" name_upper="CA_CERT" type="byte array" description="Contains the CA certificate if used by the EAP method specified in the "eap" property. Certificate data is specified using a "scheme"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory." /><property name="ca-cert-password" name_upper="CA_CERT_PASSWORD" type="string" description="The password used to access the CA certificate stored in "ca-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login." /><property name="ca-cert-password-flags" name_upper="CA_CERT_PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "ca-cert-password" property." /><property name="ca-path" name_upper="CA_PATH" type="string" description="UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the "ca-cert" property. If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored." /><property name="client-cert" name_upper="CLIENT_CERT" type="byte array" description="Contains the client certificate if used by the EAP method specified in the "eap" property. Certificate data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte." /><property name="client-cert-password" name_upper="CLIENT_CERT_PASSWORD" type="string" description="The password used to access the client certificate stored in "client-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login." /><property name="client-cert-password-flags" name_upper="CLIENT_CERT_PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "client-cert-password" property." /><property name="domain-match" name_upper="DOMAIN_MATCH" type="string" description="Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a ";" delimited list." /><property name="domain-suffix-match" name_upper="DOMAIN_SUFFIX_MATCH" type="string" description="Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a ";" delimited list." /><property name="eap" name_upper="EAP" type="array of string" description="The allowed EAP method to be used when authenticating to the network with 802.1x. Valid methods are: "leap", "md5", "tls", "peap", "ttls", "pwd", and "fast". Each method requires different configuration using the properties of this setting; refer to wpa_supplicant documentation for the allowed combinations." /><property name="identity" name_upper="IDENTITY" type="string" description="Identity string for EAP authentication methods. Often the user's user or login name." /><property name="optional" name_upper="OPTIONAL" type="boolean" default="FALSE" description="Whether the 802.1X authentication is optional. If TRUE, the activation will continue even after a timeout or an authentication failure. Setting the property to TRUE is currently allowed only for Ethernet connections. If set to FALSE, the activation can continue only after a successful authentication." /><property name="pac-file" name_upper="PAC_FILE" type="string" description="UTF-8 encoded file path containing PAC for EAP-FAST." /><property name="password" name_upper="PASSWORD" type="string" description="UTF-8 encoded password used for EAP authentication methods. If both the "password" property and the "password-raw" property are specified, "password" is preferred." /><property name="password-flags" name_upper="PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "password" property." /><property name="password-raw" name_upper="PASSWORD_RAW" type="byte array" description="Password used for EAP authentication methods, given as a byte array to allow passwords in other encodings than UTF-8 to be used. If both the "password" property and the "password-raw" property are specified, "password" is preferred." /><property name="password-raw-flags" name_upper="PASSWORD_RAW_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "password-raw" property." /><property name="phase1-auth-flags" name_upper="PHASE1_AUTH_FLAGS" type="uint32" default="0" description="Specifies authentication flags to use in "phase 1" outer authentication using NMSetting8021xAuthFlags options. The individual TLS versions can be explicitly disabled. If a certain TLS disable flag is not set, it is up to the supplicant to allow or forbid it. The TLS options map to tls_disable_tlsv1_x settings. See the wpa_supplicant documentation for more details." /><property name="phase1-fast-provisioning" name_upper="PHASE1_FAST_PROVISIONING" type="string" description="Enables or disables in-line provisioning of EAP-FAST credentials when FAST is specified as the EAP method in the "eap" property. Recognized values are "0" (disabled), "1" (allow unauthenticated provisioning), "2" (allow authenticated provisioning), and "3" (allow both authenticated and unauthenticated provisioning). See the wpa_supplicant documentation for more details." /><property name="phase1-peaplabel" name_upper="PHASE1_PEAPLABEL" type="string" description="Forces use of the new PEAP label during key derivation. Some RADIUS servers may require forcing the new PEAP label to interoperate with PEAPv1. Set to "1" to force use of the new PEAP label. See the wpa_supplicant documentation for more details." /><property name="phase1-peapver" name_upper="PHASE1_PEAPVER" type="string" description="Forces which PEAP version is used when PEAP is set as the EAP method in the "eap" property. When unset, the version reported by the server will be used. Sometimes when using older RADIUS servers, it is necessary to force the client to use a particular PEAP version. To do so, this property may be set to "0" or "1" to force that specific PEAP version." /><property name="phase2-altsubject-matches" name_upper="PHASE2_ALTSUBJECT_MATCHES" type="array of string" description="List of strings to be matched against the altSubjectName of the certificate presented by the authentication server during the inner "phase 2" authentication. If the list is empty, no verification of the server certificate's altSubjectName is performed." /><property name="phase2-auth" name_upper="PHASE2_AUTH" type="string" description="Specifies the allowed "phase 2" inner authentication method when an EAP method that uses an inner TLS tunnel is specified in the "eap" property. For TTLS this property selects one of the supported non-EAP inner methods: "pap", "chap", "mschap", "mschapv2" while "phase2-autheap" selects an EAP inner method. For PEAP this selects an inner EAP method, one of: "gtc", "otp", "md5" and "tls". Each "phase 2" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details. Both "phase2-auth" and "phase2-autheap" cannot be specified." /><property name="phase2-autheap" name_upper="PHASE2_AUTHEAP" type="string" description="Specifies the allowed "phase 2" inner EAP-based authentication method when TTLS is specified in the "eap" property. Recognized EAP-based "phase 2" methods are "md5", "mschapv2", "otp", "gtc", and "tls". Each "phase 2" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details." /><property name="phase2-ca-cert" name_upper="PHASE2_CA_CERT" type="byte array" description="Contains the "phase 2" CA certificate if used by the EAP method specified in the "phase2-auth" or "phase2-autheap" properties. Certificate data is specified using a "scheme"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory." /><property name="phase2-ca-cert-password" name_upper="PHASE2_CA_CERT_PASSWORD" type="string" description="The password used to access the "phase2" CA certificate stored in "phase2-ca-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login." /><property name="phase2-ca-cert-password-flags" name_upper="PHASE2_CA_CERT_PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "phase2-ca-cert-password" property." /><property name="phase2-ca-path" name_upper="PHASE2_CA_PATH" type="string" description="UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the "phase2-ca-cert" property. If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored." /><property name="phase2-client-cert" name_upper="PHASE2_CLIENT_CERT" type="byte array" description="Contains the "phase 2" client certificate if used by the EAP method specified in the "phase2-auth" or "phase2-autheap" properties. Certificate data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended." /><property name="phase2-client-cert-password" name_upper="PHASE2_CLIENT_CERT_PASSWORD" type="string" description="The password used to access the "phase2" client certificate stored in "phase2-client-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login." /><property name="phase2-client-cert-password-flags" name_upper="PHASE2_CLIENT_CERT_PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "phase2-client-cert-password" property." /><property name="phase2-domain-match" name_upper="PHASE2_DOMAIN_MATCH" type="string" description="Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner "phase 2" authentication. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a ";" delimited list." /><property name="phase2-domain-suffix-match" name_upper="PHASE2_DOMAIN_SUFFIX_MATCH" type="string" description="Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner "phase 2" authentication. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a ";" delimited list." /><property name="phase2-private-key" name_upper="PHASE2_PRIVATE_KEY" type="byte array" description="Contains the "phase 2" inner private key when the "phase2-auth" or "phase2-autheap" property is set to "tls". Key data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the "phase2-private-key-password" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte, and as with the blob scheme the "phase2-private-key-password" property must be set to the password used to decode the PKCS#12 private key and certificate." /><property name="phase2-private-key-password" name_upper="PHASE2_PRIVATE_KEY_PASSWORD" type="string" description="The password used to decrypt the "phase 2" private key specified in the "phase2-private-key" property when the private key either uses the path scheme, or is a PKCS#12 format key." /><property name="phase2-private-key-password-flags" name_upper="PHASE2_PRIVATE_KEY_PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "phase2-private-key-password" property." /><property name="phase2-subject-match" name_upper="PHASE2_SUBJECT_MATCH" type="string" description="Substring to be matched against the subject of the certificate presented by the authentication server during the inner "phase 2" authentication. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:phase2-domain-suffix-match." /><property name="pin" name_upper="PIN" type="string" description="PIN used for EAP authentication methods." /><property name="pin-flags" name_upper="PIN_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "pin" property." /><property name="private-key" name_upper="PRIVATE_KEY" type="byte array" description="Contains the private key when the "eap" property is set to "tls". Key data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the "private-key-password" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte, and as with the blob scheme the "private-key-password" property must be set to the password used to decode the PKCS#12 private key and certificate. WARNING: "private-key" is not a "secret" property, and thus unencrypted private key data using the BLOB scheme may be readable by unprivileged users. Private keys should always be encrypted with a private key password to prevent unauthorized access to unencrypted private key data." /><property name="private-key-password" name_upper="PRIVATE_KEY_PASSWORD" type="string" description="The password used to decrypt the private key specified in the "private-key" property when the private key either uses the path scheme, or if the private key is a PKCS#12 format key." /><property name="private-key-password-flags" name_upper="PRIVATE_KEY_PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "private-key-password" property." /><property name="subject-match" name_upper="SUBJECT_MATCH" type="string" description="Substring to be matched against the subject of the certificate presented by the authentication server. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:domain-suffix-match." /><property name="system-ca-certs" name_upper="SYSTEM_CA_CERTS" type="boolean" default="FALSE" description="When TRUE, overrides the "ca-path" and "phase2-ca-path" properties using the system CA directory specified at configure time with the --system-ca-path switch. The certificates in this directory are added to the verification chain in addition to any certificates specified by the "ca-cert" and "phase2-ca-cert" properties. If the path provided with --system-ca-path is rather a file name (bundle of trusted CA certificates), it overrides "ca-cert" and "phase2-ca-cert" properties instead (sets ca_cert/ca_cert2 options for wpa_supplicant)." /></setting><setting name="adsl" description="ADSL Settings" name_upper="ADSL"><property name="encapsulation" name_upper="ENCAPSULATION" type="string" description="Encapsulation of ADSL connection. Can be "vcmux" or "llc"." /><property name="password" name_upper="PASSWORD" type="string" description="Password used to authenticate with the ADSL service." /><property name="password-flags" name_upper="PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "password" property." /><property name="protocol" name_upper="PROTOCOL" type="string" description="ADSL connection protocol. Can be "pppoa", "pppoe" or "ipoatm"." /><property name="username" name_upper="USERNAME" type="string" description="Username used to authenticate with the ADSL service." /><property name="vci" name_upper="VCI" type="uint32" default="0" description="VCI of ADSL connection" /><property name="vpi" name_upper="VPI" type="uint32" default="0" description="VPI of ADSL connection" /></setting><setting name="bluetooth" description="Bluetooth Settings" name_upper="BLUETOOTH"><property name="bdaddr" name_upper="BDADDR" type="byte array" description="The Bluetooth address of the device." /><property name="type" name_upper="TYPE" type="string" description="Either "dun" for Dial-Up Networking connections or "panu" for Personal Area Networking connections to devices supporting the NAP profile." /></setting><setting name="bond" description="Bonding Settings" name_upper="BOND"><property name="options" name_upper="OPTIONS" type="dict of string to string" default="{'mode': 'balance-rr'}" description="Dictionary of key/value pairs of bonding options. Both keys and values must be strings. Option names must contain only alphanumeric characters (ie, [a-zA-Z0-9])." /></setting><setting name="bridge" description="Bridging Settings" name_upper="BRIDGE"><property name="ageing-time" name_upper="AGEING_TIME" type="uint32" default="300" description="The Ethernet MAC address aging time, in seconds." /><property name="forward-delay" name_upper="FORWARD_DELAY" type="uint32" default="15" description="The Spanning Tree Protocol (STP) forwarding delay, in seconds." /><property name="group-address" name_upper="GROUP_ADDRESS" type="byte array" description="If specified, The MAC address of the multicast group this bridge uses for STP. The address must be a link-local address in standard Ethernet MAC address format, ie an address of the form 01:80:C2:00:00:0X, with X in [0, 4..F]. If not specified the default value is 01:80:C2:00:00:00." /><property name="group-forward-mask" name_upper="GROUP_FORWARD_MASK" type="uint32" default="0" description="A mask of group addresses to forward. Usually, group addresses in the range from 01:80:C2:00:00:00 to 01:80:C2:00:00:0F are not forwarded according to standards. This property is a mask of 16 bits, each corresponding to a group address in that range that must be forwarded. The mask can't have bits 0, 1 or 2 set because they are used for STP, MAC pause frames and LACP." /><property name="hello-time" name_upper="HELLO_TIME" type="uint32" default="2" description="The Spanning Tree Protocol (STP) hello time, in seconds." /><property name="mac-address" name_upper="MAC_ADDRESS" type="byte array" description="If specified, the MAC address of bridge. When creating a new bridge, this MAC address will be set. If this field is left unspecified, the "ethernet.cloned-mac-address" is referred instead to generate the initial MAC address. Note that setting "ethernet.cloned-mac-address" anyway overwrites the MAC address of the bridge later while activating the bridge. Hence, this property is deprecated. Deprecated: 1" /><property name="max-age" name_upper="MAX_AGE" type="uint32" default="20" description="The Spanning Tree Protocol (STP) maximum message age, in seconds." /><property name="multicast-hash-max" name_upper="MULTICAST_HASH_MAX" type="uint32" default="4096" description="Set maximum size of multicast hash table (value must be a power of 2)." /><property name="multicast-last-member-count" name_upper="MULTICAST_LAST_MEMBER_COUNT" type="uint32" default="2" description="Set the number of queries the bridge will send before stopping forwarding a multicast group after a "leave" message has been received." /><property name="multicast-last-member-interval" name_upper="MULTICAST_LAST_MEMBER_INTERVAL" type="uint64" default="100" description="Set interval (in deciseconds) between queries to find remaining members of a group, after a "leave" message is received." /><property name="multicast-membership-interval" name_upper="MULTICAST_MEMBERSHIP_INTERVAL" type="uint64" default="26000" description="Set delay (in deciseconds) after which the bridge will leave a group, if no membership reports for this group are received." /><property name="multicast-querier" name_upper="MULTICAST_QUERIER" type="boolean" default="FALSE" description="Enable or disable sending of multicast queries by the bridge. If not specified the option is disabled." /><property name="multicast-querier-interval" name_upper="MULTICAST_QUERIER_INTERVAL" type="uint64" default="25500" description="If no queries are seen after this delay (in deciseconds) has passed, the bridge will start to send its own queries." /><property name="multicast-query-interval" name_upper="MULTICAST_QUERY_INTERVAL" type="uint64" default="12500" description="Interval (in deciseconds) between queries sent by the bridge after the end of the startup phase." /><property name="multicast-query-response-interval" name_upper="MULTICAST_QUERY_RESPONSE_INTERVAL" type="uint64" default="1000" description="Set the Max Response Time/Max Response Delay (in deciseconds) for IGMP/MLD queries sent by the bridge." /><property name="multicast-query-use-ifaddr" name_upper="MULTICAST_QUERY_USE_IFADDR" type="boolean" default="FALSE" description="If enabled the bridge's own IP address is used as the source address for IGMP queries otherwise the default of 0.0.0.0 is used." /><property name="multicast-router" name_upper="MULTICAST_ROUTER" type="string" description="Sets bridge's multicast router. Multicast-snooping must be enabled for this option to work. Supported values are: 'auto', 'disabled', 'enabled' to which kernel assigns the numbers 1, 0, and 2, respectively. If not specified the default value is 'auto' (1)." /><property name="multicast-snooping" name_upper="MULTICAST_SNOOPING" type="boolean" default="TRUE" description="Controls whether IGMP snooping is enabled for this bridge. Note that if snooping was automatically disabled due to hash collisions, the system may refuse to enable the feature until the collisions are resolved." /><property name="multicast-startup-query-count" name_upper="MULTICAST_STARTUP_QUERY_COUNT" type="uint32" default="2" description="Set the number of IGMP queries to send during startup phase." /><property name="multicast-startup-query-interval" name_upper="MULTICAST_STARTUP_QUERY_INTERVAL" type="uint64" default="3125" description="Sets the time (in deciseconds) between queries sent out at startup to determine membership information." /><property name="priority" name_upper="PRIORITY" type="uint32" default="32768" description="Sets the Spanning Tree Protocol (STP) priority for this bridge. Lower values are "better"; the lowest priority bridge will be elected the root bridge." /><property name="stp" name_upper="STP" type="boolean" default="TRUE" description="Controls whether Spanning Tree Protocol (STP) is enabled for this bridge." /><property name="vlan-default-pvid" name_upper="VLAN_DEFAULT_PVID" type="uint32" default="1" description="The default PVID for the ports of the bridge, that is the VLAN id assigned to incoming untagged frames." /><property name="vlan-filtering" name_upper="VLAN_FILTERING" type="boolean" default="FALSE" description="Control whether VLAN filtering is enabled on the bridge." /><property name="vlan-protocol" name_upper="VLAN_PROTOCOL" type="string" description="If specified, the protocol used for VLAN filtering. Supported values are: '802.1Q', '802.1ad'. If not specified the default value is '802.1Q'." /><property name="vlan-stats-enabled" name_upper="VLAN_STATS_ENABLED" type="boolean" default="FALSE" description="Controls whether per-VLAN stats accounting is enabled." /><property name="vlans" name_upper="VLANS" type="array of vardict" description="Array of bridge VLAN objects. In addition to the VLANs specified here, the bridge will also have the default-pvid VLAN configured by the bridge.vlan-default-pvid property. In nmcli the VLAN list can be specified with the following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash." /></setting><setting name="bridge-port" description="Bridge Port Settings" name_upper="BRIDGE_PORT"><property name="hairpin-mode" name_upper="HAIRPIN_MODE" type="boolean" default="FALSE" description="Enables or disables "hairpin mode" for the port, which allows frames to be sent back out through the port the frame was received on." /><property name="path-cost" name_upper="PATH_COST" type="uint32" default="100" description="The Spanning Tree Protocol (STP) port cost for destinations via this port." /><property name="priority" name_upper="PRIORITY" type="uint32" default="32" description="The Spanning Tree Protocol (STP) priority of this bridge port." /><property name="vlans" name_upper="VLANS" type="array of vardict" description="Array of bridge VLAN objects. In addition to the VLANs specified here, the port will also have the default-pvid VLAN configured on the bridge by the bridge.vlan-default-pvid property. In nmcli the VLAN list can be specified with the following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash." /></setting><setting name="cdma" description="CDMA-based Mobile Broadband Settings" name_upper="CDMA"><property name="mtu" name_upper="MTU" type="uint32" default="0" description="If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames." /><property name="number" name_upper="NUMBER" type="string" description="The number to dial to establish the connection to the CDMA-based mobile broadband network, if any. If not specified, the default number (#777) is used when required." /><property name="password" name_upper="PASSWORD" type="string" description="The password used to authenticate with the network, if required. Many providers do not require a password, or accept any password. But if a password is required, it is specified here." /><property name="password-flags" name_upper="PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "password" property." /><property name="username" name_upper="USERNAME" type="string" description="The username used to authenticate with the network, if required. Many providers do not require a username, or accept any username. But if a username is required, it is specified here." /></setting><setting name="dcb" description="Data Center Bridging Settings" name_upper="DCB"><property name="app-fcoe-flags" name_upper="APP_FCOE_FLAGS" type="NMSettingDcbFlags (uint32)" description="Specifies the NMSettingDcbFlags for the DCB FCoE application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." /><property name="app-fcoe-mode" name_upper="APP_FCOE_MODE" type="string" description="The FCoE controller mode; either "fabric" or "vn2vn". Since 1.34, NULL is the default and means "fabric". Before 1.34, NULL was rejected as invalid and the default was "fabric"." /><property name="app-fcoe-priority" name_upper="APP_FCOE_PRIORITY" type="int32" default="-1" description="The highest User Priority (0 - 7) which FCoE frames should use, or -1 for default priority. Only used when the "app-fcoe-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag." /><property name="app-fip-flags" name_upper="APP_FIP_FLAGS" type="NMSettingDcbFlags (uint32)" description="Specifies the NMSettingDcbFlags for the DCB FIP application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." /><property name="app-fip-priority" name_upper="APP_FIP_PRIORITY" type="int32" default="-1" description="The highest User Priority (0 - 7) which FIP frames should use, or -1 for default priority. Only used when the "app-fip-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag." /><property name="app-iscsi-flags" name_upper="APP_ISCSI_FLAGS" type="NMSettingDcbFlags (uint32)" description="Specifies the NMSettingDcbFlags for the DCB iSCSI application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." /><property name="app-iscsi-priority" name_upper="APP_ISCSI_PRIORITY" type="int32" default="-1" description="The highest User Priority (0 - 7) which iSCSI frames should use, or -1 for default priority. Only used when the "app-iscsi-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag." /><property name="priority-bandwidth" name_upper="PRIORITY_BANDWIDTH" type="array of uint32" description="An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the percentage of bandwidth of the priority's assigned group that the priority may use. The sum of all percentages for priorities which belong to the same group must total 100 percents." /><property name="priority-flow-control" name_upper="PRIORITY_FLOW_CONTROL" type="array of uint32" description="An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the corresponding priority should transmit priority pause." /><property name="priority-flow-control-flags" name_upper="PRIORITY_FLOW_CONTROL_FLAGS" type="NMSettingDcbFlags (uint32)" description="Specifies the NMSettingDcbFlags for DCB Priority Flow Control (PFC). Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." /><property name="priority-group-bandwidth" name_upper="PRIORITY_GROUP_BANDWIDTH" type="array of uint32" description="An array of 8 uint values, where the array index corresponds to the Priority Group ID (0 - 7) and the value indicates the percentage of link bandwidth allocated to that group. Allowed values are 0 - 100, and the sum of all values must total 100 percents." /><property name="priority-group-flags" name_upper="PRIORITY_GROUP_FLAGS" type="NMSettingDcbFlags (uint32)" description="Specifies the NMSettingDcbFlags for DCB Priority Groups. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4)." /><property name="priority-group-id" name_upper="PRIORITY_GROUP_ID" type="array of uint32" description="An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the Priority Group ID. Allowed Priority Group ID values are 0 - 7 or 15 for the unrestricted group." /><property name="priority-strict-bandwidth" name_upper="PRIORITY_STRICT_BANDWIDTH" type="array of uint32" description="An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the priority may use all of the bandwidth allocated to its assigned group." /><property name="priority-traffic-class" name_upper="PRIORITY_TRAFFIC_CLASS" type="array of uint32" description="An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the traffic class (0 - 7) to which the priority is mapped." /></setting><setting name="dummy" description="Dummy Link Settings" name_upper="DUMMY" /><setting name="ethtool" description="Ethtool Ethernet Settings" name_upper="ETHTOOL" /><setting name="generic" description="Generic Link Settings" name_upper="GENERIC" /><setting name="gsm" description="GSM-based Mobile Broadband Settings" name_upper="GSM"><property name="apn" name_upper="APN" type="string" description="The GPRS Access Point Name specifying the APN used when establishing a data session with the GSM-based network. The APN often determines how the user will be billed for their network usage and whether the user has access to the Internet or just a provider-specific walled-garden, so it is important to use the correct APN for the user's mobile broadband plan. The APN may only be composed of the characters a-z, 0-9, ., and - per GSM 03.60 Section 14.9." /><property name="auto-config" name_upper="AUTO_CONFIG" type="boolean" default="FALSE" description="When TRUE, the settings such as APN, username, or password will default to values that match the network the modem will register to in the Mobile Broadband Provider database." /><property name="device-id" name_upper="DEVICE_ID" type="string" description="The device unique identifier (as given by the WWAN management service) which this connection applies to. If given, the connection will only apply to the specified device." /><property name="home-only" name_upper="HOME_ONLY" type="boolean" default="FALSE" description="When TRUE, only connections to the home network will be allowed. Connections to roaming networks will not be made." /><property name="mtu" name_upper="MTU" type="uint32" default="0" description="If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames." /><property name="network-id" name_upper="NETWORK_ID" type="string" description="The Network ID (GSM LAI format, ie MCC-MNC) to force specific network registration. If the Network ID is specified, NetworkManager will attempt to force the device to register only on the specified network. This can be used to ensure that the device does not roam when direct roaming control of the device is not otherwise possible." /><property name="number" name_upper="NUMBER" type="string" description="Legacy setting that used to help establishing PPP data sessions for GSM-based modems. Deprecated: 1" /><property name="password" name_upper="PASSWORD" type="string" description="The password used to authenticate with the network, if required. Many providers do not require a password, or accept any password. But if a password is required, it is specified here." /><property name="password-flags" name_upper="PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "password" property." /><property name="pin" name_upper="PIN" type="string" description="If the SIM is locked with a PIN it must be unlocked before any other operations are requested. Specify the PIN here to allow operation of the device." /><property name="pin-flags" name_upper="PIN_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "pin" property." /><property name="sim-id" name_upper="SIM_ID" type="string" description="The SIM card unique identifier (as given by the WWAN management service) which this connection applies to. If given, the connection will apply to any device also allowed by "device-id" which contains a SIM card matching the given identifier." /><property name="sim-operator-id" name_upper="SIM_OPERATOR_ID" type="string" description="A MCC/MNC string like "310260" or "21601" identifying the specific mobile network operator which this connection applies to. If given, the connection will apply to any device also allowed by "device-id" and "sim-id" which contains a SIM card provisioned by the given operator." /><property name="username" name_upper="USERNAME" type="string" description="The username used to authenticate with the network, if required. Many providers do not require a username, or accept any username. But if a username is required, it is specified here." /></setting><setting name="infiniband" description="Infiniband Settings" name_upper="INFINIBAND"><property name="mac-address" name_upper="MAC_ADDRESS" type="byte array" description="If specified, this connection will only apply to the IPoIB device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing)." /><property name="mtu" name_upper="MTU" type="uint32" default="0" description="If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames." /><property name="p-key" name_upper="P_KEY" type="int32" default="-1" description="The InfiniBand P_Key to use for this device. A value of -1 means to use the default P_Key (aka "the P_Key at index 0"). Otherwise, it is a 16-bit unsigned integer, whose high bit is set if it is a "full membership" P_Key." /><property name="parent" name_upper="PARENT" type="string" description="The interface name of the parent device of this device. Normally NULL, but if the "p_key" property is set, then you must specify the base device by setting either this property or "mac-address"." /><property name="transport-mode" name_upper="TRANSPORT_MODE" type="string" description="The IP-over-InfiniBand transport mode. Either "datagram" or "connected"." /></setting><setting name="ipv4" description="IPv4 Settings" name_upper="IP4_CONFIG"><property name="addresses" name_upper="ADDRESSES" type="a comma separated list of addresses" description="A list of IPv4 addresses and their prefix length. Multiple addresses can be separated by comma. For example "192.168.1.5/24, 10.1.0.5/24". The addresses are listed in decreasing priority, meaning the first address will be the primary address." /><property name="dad-timeout" name_upper="DAD_TIMEOUT" type="int32" default="-1" description="Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4." /><property name="dhcp-client-id" name_upper="DHCP_CLIENT_ID" type="string" description="A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string ('aa:bb:cc') it is interpreted as a binary client ID, in which case the first byte is assumed to be the 'type' field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the 'type' field is set to 0. The special values "mac" and "perm-mac" are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. The special value "ipv6-duid" uses the DUID from "ipv6.dhcp-duid" property as an RFC4361-compliant client identifier. As IAID it uses "ipv4.dhcp-iaid" and falls back to "ipv6.dhcp-iaid" if unset. The special value "duid" generates a RFC4361-compliant client identifier based on "ipv4.dhcp-iaid" and uses a DUID generated by hashing /etc/machine-id. The special value "stable" is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the "${DEVICE}" or "${MAC}" specifier to get a per-device key. If unset, a globally configured default is used. If still unset, the default depends on the DHCP plugin." /><property name="dhcp-fqdn" name_upper="DHCP_FQDN" type="string" description="If the "dhcp-send-hostname" property is TRUE, then the specified FQDN will be sent to the DHCP server when acquiring a lease. This property and "dhcp-hostname" are mutually exclusive and cannot be set at the same time." /><property name="dhcp-hostname" name_upper="DHCP_HOSTNAME" type="string" description="If the "dhcp-send-hostname" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and "dhcp-fqdn" are mutually exclusive and cannot be set at the same time." /><property name="dhcp-hostname-flags" name_upper="DHCP_HOSTNAME_FLAGS" type="uint32" default="0" description="Flags for the DHCP hostname and FQDN. Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4). When no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the standard FQDN flags are set in the request: NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6. When this property is set to the default value NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags described above are sent in the DHCP requests." /><property name="dhcp-iaid" name_upper="DHCP_IAID" type="string" description="A string containing the "Identity Association Identifier" (IAID) used by the DHCP client. The property is a 32-bit decimal value or a special value among "mac", "perm-mac", "ifname" and "stable". When set to "mac" (or "perm-mac"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to "ifname", the IAID is computed by hashing the interface name. The special value "stable" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be "ifname". Note that at the moment this property is ignored for IPv6 by dhclient, which always derives the IAID from the MAC address." /><property name="dhcp-reject-servers" name_upper="DHCP_REJECT_SERVERS" type="array of string" description="Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. "192.168.122.0/24"). This property is currently not implemented for DHCPv6." /><property name="dhcp-send-hostname" name_upper="DHCP_SEND_HOSTNAME" type="boolean" default="TRUE" description="If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer. If the "dhcp-hostname" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent." /><property name="dhcp-timeout" name_upper="DHCP_TIMEOUT" type="int32" default="0" description="A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity." /><property name="dhcp-vendor-class-identifier" name_upper="DHCP_VENDOR_CLASS_IDENTIFIER" type="string" description="The Vendor Class Identifier DHCP option (60). Special characters in the data string may be escaped using C-style escapes, nevertheless this property cannot contain nul bytes. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the DHCP option is not sent to the server. Since 1.28" /><property name="dns" name_upper="DNS" type="array of uint32" description="Array of IP addresses of DNS servers." /><property name="dns-options" name_upper="DNS_OPTIONS" type="array of string" description="Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties. The currently supported options are "attempts", "debug", "edns0", "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names", "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single-request", "single-request-reopen", "timeout", "trust-ad", "use-vc". The "trust-ad" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have "trust-ad" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then "edns0" and "trust-ad" are automatically added." /><property name="dns-priority" name_upper="DNS_PRIORITY" type="int32" default="0" description="DNS servers priority. The relative priority for DNS servers specified by this setting. A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections. It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the "rotate" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain. Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually). When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins. If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured." /><property name="dns-search" name_upper="DNS_SEARCH" type="array of string" description="Array of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting." /><property name="gateway" name_upper="GATEWAY" type="string" description="The gateway associated with this configuration. This is only meaningful if "addresses" is also set. The gateway's main purpose is to control the next hop of the standard default route on the device. Hence, the gateway property conflicts with "never-default" and will be automatically dropped if the IP configuration is set to never-default. As an alternative to set the gateway, configure a static default route with /0 as prefix length." /><property name="ignore-auto-dns" name_upper="IGNORE_AUTO_DNS" type="boolean" default="FALSE" description="When "method" is set to "auto" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the "dns" and "dns-search" properties, if any, are used." /><property name="ignore-auto-routes" name_upper="IGNORE_AUTO_ROUTES" type="boolean" default="FALSE" description="When "method" is set to "auto" and this property to TRUE, automatically configured routes are ignored and only routes specified in the "routes" property, if any, are used." /><property name="may-fail" name_upper="MAY_FAIL" type="boolean" default="TRUE" description="If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out. Note that at least one IP configuration must succeed or overall network configuration will still fail. For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully." /><property name="method" name_upper="METHOD" type="string" description="IP configuration method. NMSettingIP4Config and NMSettingIP6Config both support "disabled", "auto", "manual", and "link-local". See the subclass-specific documentation for other values. In general, for the "auto" method, properties such as "dns" and "routes" specify information that is added on to the information returned from automatic configuration. The "ignore-auto-routes" and "ignore-auto-dns" properties modify this behavior. For methods that imply no upstream network, such as "shared" or "link-local", these properties must be empty. For IPv4 method "shared", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared." /><property name="never-default" name_upper="NEVER_DEFAULT" type="boolean" default="FALSE" description="If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager." /><property name="required-timeout" name_upper="REQUIRED_TIMEOUT" type="int32" default="-1" description="The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. Note that if "may-fail" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero)." /><property name="route-metric" name_upper="ROUTE_METRIC" type="int64" default="-1" description="The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric." /><property name="route-table" name_upper="ROUTE_TABLE" type="uint32" default="0" description="Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager." /><property name="routes" name_upper="ROUTES" type="a comma separated list of routes" description="A list of IPv4 destination addresses, prefix length, optional IPv4 next hop addresses, optional route metric, optional attribute. The valid syntax is: "ip[/prefix] [next-hop] [metric] [attribute=val]...[,ip[/prefix]...]". For example "192.0.2.0/24 10.1.1.1 77, 198.51.100.0/24"."><description-docbook> <para> A list of IPv4 destination addresses, prefix length, optional IPv4 next hop addresses, optional route metric, optional attribute. The valid syntax is: @@ -52,7 +52,8 @@ Various attributes are supported: </listitem> <listitem> <para><literal>"type"</literal> - one of <literal>unicast</literal>, <literal>local</literal>, <literal>blackhole</literal>, -<literal>unavailable</literal>, <literal>prohibit</literal>. The default is <literal>unicast</literal>.</para> +<literal>unavailable</literal>, <literal>prohibit</literal>, <literal>throw</literal>. +The default is <literal>unicast</literal>.</para> </listitem> <listitem> <para><literal>"window"</literal> - an unsigned 32 bit integer.</para> @@ -70,7 +71,7 @@ fixed priority. </para> <para> Example: <literal>priority 5 from 192.167.4.0/24 table 45</literal> -</para></description-docbook></property></setting><setting name="ipv6" description="IPv6 Settings" name_upper="IP6_CONFIG"><property name="addr-gen-mode" name_upper="ADDR_GEN_MODE" type="int32" default="1" description="Configure method for creating the address for use with RFC4862 IPv6 Stateless Address Autoconfiguration. The permitted values are: NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 (0) or NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY (1). If the property is set to EUI64, the addresses will be generated using the interface tokens derived from hardware address. This makes the host part of the address to stay constant, making it possible to track host's presence when it changes networks. The address changes when the interface hardware is replaced. The value of stable-privacy enables use of cryptographically secure hash of a secret host-specific key along with the connection's stable-id and the network address as specified by RFC7217. This makes it impossible to use the address track host's presence, and makes the address stable when the network interface hardware is replaced. On D-Bus, the absence of an addr-gen-mode setting equals enabling stable-privacy. For keyfile plugin, the absence of the setting on disk means EUI64 so that the property doesn't change on upgrade from older versions. Note that this setting is distinct from the Privacy Extensions as configured by "ip6-privacy" property and it does not affect the temporary addresses configured with this option." /><property name="addresses" name_upper="ADDRESSES" type="a comma separated list of addresses" description="A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example "2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64". The addresses are listed in increasing priority, meaning the last address will be the primary address." /><property name="dad-timeout" name_upper="DAD_TIMEOUT" type="int32" default="-1" description="Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4." /><property name="dhcp-duid" name_upper="DHCP_DUID" type="string" description="A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp client to identify itself to DHCPv6 servers (RFC 3315). The DUID is carried in the Client Identifier option. If the property is a hex string ('aa:bb:cc') it is interpreted as a binary DUID and filled as an opaque value in the Client Identifier option. The special value "lease" will retrieve the DUID previously used from the lease file belonging to the connection. If no DUID is found and "dhclient" is the configured dhcp client, the DUID is searched in the system-wide dhclient lease file. If still no DUID is found, or another dhcp client is used, a global and permanent DUID-UUID (RFC 6355) will be generated based on the machine-id. The special values "llt" and "ll" will generate a DUID of type LLT or LL (see RFC 3315) based on the current MAC address of the device. In order to try providing a stable DUID-LLT, the time field will contain a constant timestamp that is used globally (for all profiles) and persisted to disk. The special values "stable-llt", "stable-ll" and "stable-uuid" will generate a DUID of the corresponding type, derived from the connection's stable-id and a per-host unique key. You may want to include the "${DEVICE}" or "${MAC}" specifier in the stable-id, in case this profile gets activated on multiple devices. So, the link-layer address of "stable-ll" and "stable-llt" will be a generated address derived from the stable id. The DUID-LLT time value in the "stable-llt" option will be picked among a static timespan of three years (the upper bound of the interval is the same constant timestamp used in "llt"). When the property is unset, the global value provided for "ipv6.dhcp-duid" is used. If no global value is provided, the default "lease" value is assumed." /><property name="dhcp-hostname" name_upper="DHCP_HOSTNAME" type="string" description="If the "dhcp-send-hostname" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and "dhcp-fqdn" are mutually exclusive and cannot be set at the same time." /><property name="dhcp-hostname-flags" name_upper="DHCP_HOSTNAME_FLAGS" type="uint32" default="0" description="Flags for the DHCP hostname and FQDN. Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4). When no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the standard FQDN flags are set in the request: NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6. When this property is set to the default value NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags described above are sent in the DHCP requests." /><property name="dhcp-iaid" name_upper="DHCP_IAID" type="string" description="A string containing the "Identity Association Identifier" (IAID) used by the DHCP client. The property is a 32-bit decimal value or a special value among "mac", "perm-mac", "ifname" and "stable". When set to "mac" (or "perm-mac"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to "ifname", the IAID is computed by hashing the interface name. The special value "stable" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be "ifname". Note that at the moment this property is ignored for IPv6 by dhclient, which always derives the IAID from the MAC address." /><property name="dhcp-reject-servers" name_upper="DHCP_REJECT_SERVERS" type="array of string" description="Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. "192.168.122.0/24"). This property is currently not implemented for DHCPv6." /><property name="dhcp-send-hostname" name_upper="DHCP_SEND_HOSTNAME" type="boolean" default="TRUE" description="If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer. If the "dhcp-hostname" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent." /><property name="dhcp-timeout" name_upper="DHCP_TIMEOUT" type="int32" default="0" description="A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity." /><property name="dns" name_upper="DNS" type="array of byte array" description="Array of IP addresses of DNS servers." /><property name="dns-options" name_upper="DNS_OPTIONS" type="array of string" description="Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties. The currently supported options are "attempts", "debug", "edns0", "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names", "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single-request", "single-request-reopen", "timeout", "trust-ad", "use-vc". The "trust-ad" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have "trust-ad" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then "edns0" and "trust-ad" are automatically added." /><property name="dns-priority" name_upper="DNS_PRIORITY" type="int32" default="0" description="DNS servers priority. The relative priority for DNS servers specified by this setting. A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections. It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the "rotate" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain. Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually). When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins. If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured." /><property name="dns-search" name_upper="DNS_SEARCH" type="array of string" description="Array of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting." /><property name="gateway" name_upper="GATEWAY" type="string" description="The gateway associated with this configuration. This is only meaningful if "addresses" is also set. The gateway's main purpose is to control the next hop of the standard default route on the device. Hence, the gateway property conflicts with "never-default" and will be automatically dropped if the IP configuration is set to never-default. As an alternative to set the gateway, configure a static default route with /0 as prefix length." /><property name="ignore-auto-dns" name_upper="IGNORE_AUTO_DNS" type="boolean" default="FALSE" description="When "method" is set to "auto" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the "dns" and "dns-search" properties, if any, are used." /><property name="ignore-auto-routes" name_upper="IGNORE_AUTO_ROUTES" type="boolean" default="FALSE" description="When "method" is set to "auto" and this property to TRUE, automatically configured routes are ignored and only routes specified in the "routes" property, if any, are used." /><property name="ip6-privacy" name_upper="IP6_PRIVACY" type="NMSettingIP6ConfigPrivacy (int32)" description="Configure IPv6 Privacy Extensions for SLAAC, described in RFC4941. If enabled, it makes the kernel generate a temporary IPv6 address in addition to the public one generated from MAC address via modified EUI-64. This enhances privacy, but could cause problems in some applications, on the other hand. The permitted values are: -1: unknown, 0: disabled, 1: enabled (prefer public address), 2: enabled (prefer temporary addresses). Having a per-connection setting set to "-1" (unknown) means fallback to global configuration "ipv6.ip6-privacy". If also global configuration is unspecified or set to "-1", fallback to read "/proc/sys/net/ipv6/conf/default/use_tempaddr". Note that this setting is distinct from the Stable Privacy addresses that can be enabled with the "addr-gen-mode" property's "stable-privacy" setting as another way of avoiding host tracking with IPv6 addresses." /><property name="may-fail" name_upper="MAY_FAIL" type="boolean" default="TRUE" description="If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out. Note that at least one IP configuration must succeed or overall network configuration will still fail. For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully." /><property name="method" name_upper="METHOD" type="string" description="IP configuration method. NMSettingIP4Config and NMSettingIP6Config both support "disabled", "auto", "manual", and "link-local". See the subclass-specific documentation for other values. In general, for the "auto" method, properties such as "dns" and "routes" specify information that is added on to the information returned from automatic configuration. The "ignore-auto-routes" and "ignore-auto-dns" properties modify this behavior. For methods that imply no upstream network, such as "shared" or "link-local", these properties must be empty. For IPv4 method "shared", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared." /><property name="never-default" name_upper="NEVER_DEFAULT" type="boolean" default="FALSE" description="If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager." /><property name="ra-timeout" name_upper="RA_TIMEOUT" type="int32" default="0" description="A timeout for waiting Router Advertisements in seconds. If zero (the default), a globally configured default is used. If still unspecified, the timeout depends on the sysctl settings of the device. Set to 2147483647 (MAXINT32) for infinity." /><property name="required-timeout" name_upper="REQUIRED_TIMEOUT" type="int32" default="-1" description="The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. Note that if "may-fail" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero)." /><property name="route-metric" name_upper="ROUTE_METRIC" type="int64" default="-1" description="The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric." /><property name="route-table" name_upper="ROUTE_TABLE" type="uint32" default="0" description="Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager." /><property name="routes" name_upper="ROUTES" type="a comma separated list of routes" description="Array of IP routes."><description-docbook> +</para></description-docbook></property></setting><setting name="ipv6" description="IPv6 Settings" name_upper="IP6_CONFIG"><property name="addr-gen-mode" name_upper="ADDR_GEN_MODE" type="int32" default="1" description="Configure method for creating the address for use with RFC4862 IPv6 Stateless Address Autoconfiguration. The permitted values are: NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 (0) or NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY (1). If the property is set to EUI64, the addresses will be generated using the interface tokens derived from hardware address. This makes the host part of the address to stay constant, making it possible to track host's presence when it changes networks. The address changes when the interface hardware is replaced. The value of stable-privacy enables use of cryptographically secure hash of a secret host-specific key along with the connection's stable-id and the network address as specified by RFC7217. This makes it impossible to use the address track host's presence, and makes the address stable when the network interface hardware is replaced. On D-Bus, the absence of an addr-gen-mode setting equals enabling stable-privacy. For keyfile plugin, the absence of the setting on disk means EUI64 so that the property doesn't change on upgrade from older versions. Note that this setting is distinct from the Privacy Extensions as configured by "ip6-privacy" property and it does not affect the temporary addresses configured with this option." /><property name="addresses" name_upper="ADDRESSES" type="a comma separated list of addresses" description="A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example "2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64". The addresses are listed in decreasing priority, meaning the first address will be the primary address. This can make a difference with IPv6 source address selection (RFC 6724, section 5)." /><property name="dad-timeout" name_upper="DAD_TIMEOUT" type="int32" default="-1" description="Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4." /><property name="dhcp-duid" name_upper="DHCP_DUID" type="string" description="A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp client to identify itself to DHCPv6 servers (RFC 3315). The DUID is carried in the Client Identifier option. If the property is a hex string ('aa:bb:cc') it is interpreted as a binary DUID and filled as an opaque value in the Client Identifier option. The special value "lease" will retrieve the DUID previously used from the lease file belonging to the connection. If no DUID is found and "dhclient" is the configured dhcp client, the DUID is searched in the system-wide dhclient lease file. If still no DUID is found, or another dhcp client is used, a global and permanent DUID-UUID (RFC 6355) will be generated based on the machine-id. The special values "llt" and "ll" will generate a DUID of type LLT or LL (see RFC 3315) based on the current MAC address of the device. In order to try providing a stable DUID-LLT, the time field will contain a constant timestamp that is used globally (for all profiles) and persisted to disk. The special values "stable-llt", "stable-ll" and "stable-uuid" will generate a DUID of the corresponding type, derived from the connection's stable-id and a per-host unique key. You may want to include the "${DEVICE}" or "${MAC}" specifier in the stable-id, in case this profile gets activated on multiple devices. So, the link-layer address of "stable-ll" and "stable-llt" will be a generated address derived from the stable id. The DUID-LLT time value in the "stable-llt" option will be picked among a static timespan of three years (the upper bound of the interval is the same constant timestamp used in "llt"). When the property is unset, the global value provided for "ipv6.dhcp-duid" is used. If no global value is provided, the default "lease" value is assumed." /><property name="dhcp-hostname" name_upper="DHCP_HOSTNAME" type="string" description="If the "dhcp-send-hostname" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and "dhcp-fqdn" are mutually exclusive and cannot be set at the same time." /><property name="dhcp-hostname-flags" name_upper="DHCP_HOSTNAME_FLAGS" type="uint32" default="0" description="Flags for the DHCP hostname and FQDN. Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4). When no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the standard FQDN flags are set in the request: NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6. When this property is set to the default value NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags described above are sent in the DHCP requests." /><property name="dhcp-iaid" name_upper="DHCP_IAID" type="string" description="A string containing the "Identity Association Identifier" (IAID) used by the DHCP client. The property is a 32-bit decimal value or a special value among "mac", "perm-mac", "ifname" and "stable". When set to "mac" (or "perm-mac"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to "ifname", the IAID is computed by hashing the interface name. The special value "stable" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be "ifname". Note that at the moment this property is ignored for IPv6 by dhclient, which always derives the IAID from the MAC address." /><property name="dhcp-reject-servers" name_upper="DHCP_REJECT_SERVERS" type="array of string" description="Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. "192.168.122.0/24"). This property is currently not implemented for DHCPv6." /><property name="dhcp-send-hostname" name_upper="DHCP_SEND_HOSTNAME" type="boolean" default="TRUE" description="If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer. If the "dhcp-hostname" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent." /><property name="dhcp-timeout" name_upper="DHCP_TIMEOUT" type="int32" default="0" description="A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity." /><property name="dns" name_upper="DNS" type="array of byte array" description="Array of IP addresses of DNS servers." /><property name="dns-options" name_upper="DNS_OPTIONS" type="array of string" description="Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties. The currently supported options are "attempts", "debug", "edns0", "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names", "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single-request", "single-request-reopen", "timeout", "trust-ad", "use-vc". The "trust-ad" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have "trust-ad" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then "edns0" and "trust-ad" are automatically added." /><property name="dns-priority" name_upper="DNS_PRIORITY" type="int32" default="0" description="DNS servers priority. The relative priority for DNS servers specified by this setting. A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections. It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the "rotate" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain. Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually). When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins. If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured." /><property name="dns-search" name_upper="DNS_SEARCH" type="array of string" description="Array of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting." /><property name="gateway" name_upper="GATEWAY" type="string" description="The gateway associated with this configuration. This is only meaningful if "addresses" is also set. The gateway's main purpose is to control the next hop of the standard default route on the device. Hence, the gateway property conflicts with "never-default" and will be automatically dropped if the IP configuration is set to never-default. As an alternative to set the gateway, configure a static default route with /0 as prefix length." /><property name="ignore-auto-dns" name_upper="IGNORE_AUTO_DNS" type="boolean" default="FALSE" description="When "method" is set to "auto" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the "dns" and "dns-search" properties, if any, are used." /><property name="ignore-auto-routes" name_upper="IGNORE_AUTO_ROUTES" type="boolean" default="FALSE" description="When "method" is set to "auto" and this property to TRUE, automatically configured routes are ignored and only routes specified in the "routes" property, if any, are used." /><property name="ip6-privacy" name_upper="IP6_PRIVACY" type="NMSettingIP6ConfigPrivacy (int32)" description="Configure IPv6 Privacy Extensions for SLAAC, described in RFC4941. If enabled, it makes the kernel generate a temporary IPv6 address in addition to the public one generated from MAC address via modified EUI-64. This enhances privacy, but could cause problems in some applications, on the other hand. The permitted values are: -1: unknown, 0: disabled, 1: enabled (prefer public address), 2: enabled (prefer temporary addresses). Having a per-connection setting set to "-1" (unknown) means fallback to global configuration "ipv6.ip6-privacy". If also global configuration is unspecified or set to "-1", fallback to read "/proc/sys/net/ipv6/conf/default/use_tempaddr". Note that this setting is distinct from the Stable Privacy addresses that can be enabled with the "addr-gen-mode" property's "stable-privacy" setting as another way of avoiding host tracking with IPv6 addresses." /><property name="may-fail" name_upper="MAY_FAIL" type="boolean" default="TRUE" description="If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out. Note that at least one IP configuration must succeed or overall network configuration will still fail. For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully." /><property name="method" name_upper="METHOD" type="string" description="IP configuration method. NMSettingIP4Config and NMSettingIP6Config both support "disabled", "auto", "manual", and "link-local". See the subclass-specific documentation for other values. In general, for the "auto" method, properties such as "dns" and "routes" specify information that is added on to the information returned from automatic configuration. The "ignore-auto-routes" and "ignore-auto-dns" properties modify this behavior. For methods that imply no upstream network, such as "shared" or "link-local", these properties must be empty. For IPv4 method "shared", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared." /><property name="never-default" name_upper="NEVER_DEFAULT" type="boolean" default="FALSE" description="If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager." /><property name="ra-timeout" name_upper="RA_TIMEOUT" type="int32" default="0" description="A timeout for waiting Router Advertisements in seconds. If zero (the default), a globally configured default is used. If still unspecified, the timeout depends on the sysctl settings of the device. Set to 2147483647 (MAXINT32) for infinity." /><property name="required-timeout" name_upper="REQUIRED_TIMEOUT" type="int32" default="-1" description="The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. Note that if "may-fail" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero)." /><property name="route-metric" name_upper="ROUTE_METRIC" type="int64" default="-1" description="The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric." /><property name="route-table" name_upper="ROUTE_TABLE" type="uint32" default="0" description="Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager." /><property name="routes" name_upper="ROUTES" type="a comma separated list of routes" description="Array of IP routes."><description-docbook> <para> A list of IPv6 destination addresses, prefix length, optional IPv6 next hop addresses, optional route metric, optional attribute. The valid syntax is: @@ -120,7 +121,8 @@ Various attributes are supported: </listitem> <listitem> <para><literal>"type"</literal> - one of <literal>unicast</literal>, <literal>local</literal>, <literal>blackhole</literal>, -<literal>unavailable</literal>, <literal>prohibit</literal>. The default is <literal>unicast</literal>.</para> +<literal>unavailable</literal>, <literal>prohibit</literal>, <literal>throw</literal>. +The default is <literal>unicast</literal>.</para> </listitem> <listitem> <para><literal>"window"</literal> - an unsigned 32 bit integer.</para> @@ -138,7 +140,7 @@ fixed priority. </para> <para> Example: <literal>priority 5 from 1:2:3::5/128 table 45</literal> -</para></description-docbook></property><property name="token" name_upper="TOKEN" type="string" description="Configure the token for draft-chown-6man-tokenised-ipv6-identifiers-02 IPv6 tokenized interface identifiers. Useful with eui64 addr-gen-mode." /></setting><setting name="ip-tunnel" description="IP Tunneling Settings" name_upper="IP_TUNNEL"><property name="encapsulation-limit" name_upper="ENCAPSULATION_LIMIT" type="uint32" default="0" description="How many additional levels of encapsulation are permitted to be prepended to packets. This property applies only to IPv6 tunnels." /><property name="flags" name_upper="FLAGS" type="uint32" default="0" description="Tunnel flags. Currently, the following values are supported: NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT (0x1), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS (0x2), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL (0x4), NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV (0x8), NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY (0x10), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK (0x20). They are valid only for IPv6 tunnels." /><property name="flow-label" name_upper="FLOW_LABEL" type="uint32" default="0" description="The flow label to assign to tunnel packets. This property applies only to IPv6 tunnels." /><property name="input-key" name_upper="INPUT_KEY" type="string" description="The key used for tunnel input packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used." /><property name="local" name_upper="LOCAL" type="string" description="The local endpoint of the tunnel; the value can be empty, otherwise it must contain an IPv4 or IPv6 address." /><property name="mode" name_upper="MODE" type="uint32" default="0" description="The tunneling mode, for example NM_IP_TUNNEL_MODE_IPIP (1) or NM_IP_TUNNEL_MODE_GRE (2)." /><property name="mtu" name_upper="MTU" type="uint32" default="0" description="If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments." /><property name="output-key" name_upper="OUTPUT_KEY" type="string" description="The key used for tunnel output packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used." /><property name="parent" name_upper="PARENT" type="string" description="If given, specifies the parent interface name or parent connection UUID the new device will be bound to so that tunneled packets will only be routed via that interface." /><property name="path-mtu-discovery" name_upper="PATH_MTU_DISCOVERY" type="boolean" default="TRUE" description="Whether to enable Path MTU Discovery on this tunnel." /><property name="remote" name_upper="REMOTE" type="string" description="The remote endpoint of the tunnel; the value must contain an IPv4 or IPv6 address." /><property name="tos" name_upper="TOS" type="uint32" default="0" description="The type of service (IPv4) or traffic class (IPv6) field to be set on tunneled packets." /><property name="ttl" name_upper="TTL" type="uint32" default="0" description="The TTL to assign to tunneled packets. 0 is a special value meaning that packets inherit the TTL value." /></setting><setting name="macsec" description="MACSec Settings" name_upper="MACSEC"><property name="encrypt" name_upper="ENCRYPT" type="boolean" default="TRUE" description="Whether the transmitted traffic must be encrypted." /><property name="mka-cak" name_upper="MKA_CAK" type="string" description="The pre-shared CAK (Connectivity Association Key) for MACsec Key Agreement." /><property name="mka-cak-flags" name_upper="MKA_CAK_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "mka-cak" property." /><property name="mka-ckn" name_upper="MKA_CKN" type="string" description="The pre-shared CKN (Connectivity-association Key Name) for MACsec Key Agreement." /><property name="mode" name_upper="MODE" type="int32" default="0" description="Specifies how the CAK (Connectivity Association Key) for MKA (MACsec Key Agreement) is obtained." /><property name="parent" name_upper="PARENT" type="string" description="If given, specifies the parent interface name or parent connection UUID from which this MACSEC interface should be created. If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property." /><property name="port" name_upper="PORT" type="int32" default="1" description="The port component of the SCI (Secure Channel Identifier), between 1 and 65534." /><property name="send-sci" name_upper="SEND_SCI" type="boolean" default="TRUE" description="Specifies whether the SCI (Secure Channel Identifier) is included in every packet." /><property name="validation" name_upper="VALIDATION" type="int32" default="2" description="Specifies the validation mode for incoming frames." /></setting><setting name="macvlan" description="MAC VLAN Settings" name_upper="MACVLAN"><property name="mode" name_upper="MODE" type="uint32" default="0" description="The macvlan mode, which specifies the communication mechanism between multiple macvlans on the same lower device." /><property name="parent" name_upper="PARENT" type="string" description="If given, specifies the parent interface name or parent connection UUID from which this MAC-VLAN interface should be created. If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property." /><property name="promiscuous" name_upper="PROMISCUOUS" type="boolean" default="TRUE" description="Whether the interface should be put in promiscuous mode." /><property name="tap" name_upper="TAP" type="boolean" default="FALSE" description="Whether the interface should be a MACVTAP." /></setting><setting name="match" description="Match settings" name_upper="MATCH"><property name="driver" name_upper="DRIVER" type="array of string" description="A list of driver names to match. Each element is a shell wildcard pattern. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the pattern." /><property name="interface-name" name_upper="INTERFACE_NAME" type="array of string" description="A list of interface names to match. Each element is a shell wildcard pattern. An element can be prefixed with a pipe symbol (|) or an ampersand (&). The former means that the element is optional and the latter means that it is mandatory. If there are any optional elements, than the match evaluates to true if at least one of the optional element matches (logical OR). If there are any mandatory elements, then they all must match (logical AND). By default, an element is optional. This means that an element "foo" behaves the same as "|foo". An element can also be inverted with exclamation mark (!) between the pipe symbol (or the ampersand) and before the pattern. Note that "!foo" is a shortcut for the mandatory match "&!foo". Finally, a backslash can be used at the beginning of the element (after the optional special characters) to escape the start of the pattern. For example, "&\\!a" is an mandatory match for literally "!a"." /><property name="kernel-command-line" name_upper="KERNEL_COMMAND_LINE" type="array of string" description="A list of kernel command line arguments to match. This may be used to check whether a specific kernel command line option is set (or unset, if prefixed with the exclamation mark). The argument must either be a single word, or an assignment (i.e. two words, joined by "="). In the former case the kernel command line is searched for the word appearing as is, or as left hand side of an assignment. In the latter case, the exact assignment is looked for with right and left hand side matching. Wildcard patterns are not supported. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the match." /><property name="path" name_upper="PATH" type="array of string" description="A list of paths to match against the ID_PATH udev property of devices. ID_PATH represents the topological persistent path of a device. It typically contains a subsystem string (pci, usb, platform, etc.) and a subsystem-specific identifier. For PCI devices the path has the form "pci-$domain:$bus:$device.$function", where each variable is an hexadecimal value; for example "pci-0000:0a:00.0". The path of a device can be obtained with "udevadm info /sys/class/net/$dev | grep ID_PATH=" or by looking at the "path" property exported by NetworkManager ("nmcli -f general.path device show $dev"). Each element of the list is a shell wildcard pattern. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the pattern." /></setting><setting name="802-11-olpc-mesh" description="OLPC Wireless Mesh Settings" name_upper="OLPC_MESH"><property name="channel" name_upper="CHANNEL" type="uint32" default="0" description="Channel on which the mesh network to join is located." /><property name="dhcp-anycast-address" name_upper="DHCP_ANYCAST_ADDRESS" type="byte array" description="Anycast DHCP MAC address used when requesting an IP address via DHCP. The specific anycast address used determines which DHCP server class answers the request. This is currently only implemented by dhclient DHCP plugin." /><property name="ssid" name_upper="SSID" type="byte array" description="SSID of the mesh network to join." /></setting><setting name="ovs-bridge" description="OvsBridge Link Settings" name_upper="OVS_BRIDGE"><property name="datapath-type" name_upper="DATAPATH_TYPE" type="string" description="The data path type. One of "system", "netdev" or empty." /><property name="fail-mode" name_upper="FAIL_MODE" type="string" description="The bridge failure mode. One of "secure", "standalone" or empty." /><property name="mcast-snooping-enable" name_upper="MCAST_SNOOPING_ENABLE" type="boolean" default="FALSE" description="Enable or disable multicast snooping." /><property name="rstp-enable" name_upper="RSTP_ENABLE" type="boolean" default="FALSE" description="Enable or disable RSTP." /><property name="stp-enable" name_upper="STP_ENABLE" type="boolean" default="FALSE" description="Enable or disable STP." /></setting><setting name="ovs-dpdk" description="OvsDpdk Link Settings" name_upper="OVS_DPDK"><property name="devargs" name_upper="DEVARGS" type="string" description="Open vSwitch DPDK device arguments." /><property name="n-rxq" name_upper="N_RXQ" type="uint32" default="0" description="Open vSwitch DPDK number of rx queues. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures one queue." /></setting><setting name="ovs-interface" description="Open vSwitch Interface Settings" name_upper="OVS_INTERFACE"><property name="type" name_upper="TYPE" type="string" description="The interface type. Either "internal", "system", "patch", "dpdk", or empty." /></setting><setting name="ovs-patch" description="OvsPatch Link Settings" name_upper="OVS_PATCH"><property name="peer" name_upper="PEER" type="string" description="Specifies the name of the interface for the other side of the patch. The patch on the other side must also set this interface as peer." /></setting><setting name="ovs-port" description="OvsPort Link Settings" name_upper="OVS_PORT"><property name="bond-downdelay" name_upper="BOND_DOWNDELAY" type="uint32" default="0" description="The time port must be inactive in order to be considered down." /><property name="bond-mode" name_upper="BOND_MODE" type="string" description="Bonding mode. One of "active-backup", "balance-slb", or "balance-tcp"." /><property name="bond-updelay" name_upper="BOND_UPDELAY" type="uint32" default="0" description="The time port must be active before it starts forwarding traffic." /><property name="lacp" name_upper="LACP" type="string" description="LACP mode. One of "active", "off", or "passive"." /><property name="tag" name_upper="TAG" type="uint32" default="0" description="The VLAN tag in the range 0-4095." /><property name="vlan-mode" name_upper="VLAN_MODE" type="string" description="The VLAN mode. One of "access", "native-tagged", "native-untagged", "trunk" or unset." /></setting><setting name="ppp" description="Point-to-Point Protocol Settings" name_upper="PPP"><property name="baud" name_upper="BAUD" type="uint32" default="0" description="If non-zero, instruct pppd to set the serial port to the specified baudrate. This value should normally be left as 0 to automatically choose the speed." /><property name="crtscts" name_upper="CRTSCTS" type="boolean" default="FALSE" description="If TRUE, specify that pppd should set the serial port to use hardware flow control with RTS and CTS signals. This value should normally be set to FALSE." /><property name="lcp-echo-failure" name_upper="LCP_ECHO_FAILURE" type="uint32" default="0" description="If non-zero, instruct pppd to presume the connection to the peer has failed if the specified number of LCP echo-requests go unanswered by the peer. The "lcp-echo-interval" property must also be set to a non-zero value if this property is used." /><property name="lcp-echo-interval" name_upper="LCP_ECHO_INTERVAL" type="uint32" default="0" description="If non-zero, instruct pppd to send an LCP echo-request frame to the peer every n seconds (where n is the specified value). Note that some PPP peers will respond to echo requests and some will not, and it is not possible to autodetect this." /><property name="mppe-stateful" name_upper="MPPE_STATEFUL" type="boolean" default="FALSE" description="If TRUE, stateful MPPE is used. See pppd documentation for more information on stateful MPPE." /><property name="mru" name_upper="MRU" type="uint32" default="0" description="If non-zero, instruct pppd to request that the peer send packets no larger than the specified size. If non-zero, the MRU should be between 128 and 16384." /><property name="mtu" name_upper="MTU" type="uint32" default="0" description="If non-zero, instruct pppd to send packets no larger than the specified size." /><property name="no-vj-comp" name_upper="NO_VJ_COMP" type="boolean" default="FALSE" description="If TRUE, Van Jacobsen TCP header compression will not be requested." /><property name="noauth" name_upper="NOAUTH" type="boolean" default="TRUE" description="If TRUE, do not require the other side (usually the PPP server) to authenticate itself to the client. If FALSE, require authentication from the remote side. In almost all cases, this should be TRUE." /><property name="nobsdcomp" name_upper="NOBSDCOMP" type="boolean" default="FALSE" description="If TRUE, BSD compression will not be requested." /><property name="nodeflate" name_upper="NODEFLATE" type="boolean" default="FALSE" description="If TRUE, "deflate" compression will not be requested." /><property name="refuse-chap" name_upper="REFUSE_CHAP" type="boolean" default="FALSE" description="If TRUE, the CHAP authentication method will not be used." /><property name="refuse-eap" name_upper="REFUSE_EAP" type="boolean" default="FALSE" description="If TRUE, the EAP authentication method will not be used." /><property name="refuse-mschap" name_upper="REFUSE_MSCHAP" type="boolean" default="FALSE" description="If TRUE, the MSCHAP authentication method will not be used." /><property name="refuse-mschapv2" name_upper="REFUSE_MSCHAPV2" type="boolean" default="FALSE" description="If TRUE, the MSCHAPv2 authentication method will not be used." /><property name="refuse-pap" name_upper="REFUSE_PAP" type="boolean" default="FALSE" description="If TRUE, the PAP authentication method will not be used." /><property name="require-mppe" name_upper="REQUIRE_MPPE" type="boolean" default="FALSE" description="If TRUE, MPPE (Microsoft Point-to-Point Encryption) will be required for the PPP session. If either 64-bit or 128-bit MPPE is not available the session will fail. Note that MPPE is not used on mobile broadband connections." /><property name="require-mppe-128" name_upper="REQUIRE_MPPE_128" type="boolean" default="FALSE" description="If TRUE, 128-bit MPPE (Microsoft Point-to-Point Encryption) will be required for the PPP session, and the "require-mppe" property must also be set to TRUE. If 128-bit MPPE is not available the session will fail." /></setting><setting name="pppoe" description="PPP-over-Ethernet Settings" name_upper="PPPOE"><property name="parent" name_upper="PARENT" type="string" description="If given, specifies the parent interface name on which this PPPoE connection should be created. If this property is not specified, the connection is activated on the interface specified in "interface-name" of NMSettingConnection." /><property name="password" name_upper="PASSWORD" type="string" description="Password used to authenticate with the PPPoE service." /><property name="password-flags" name_upper="PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "password" property." /><property name="service" name_upper="SERVICE" type="string" description="If specified, instruct PPPoE to only initiate sessions with access concentrators that provide the specified service. For most providers, this should be left blank. It is only required if there are multiple access concentrators or a specific service is known to be required." /><property name="username" name_upper="USERNAME" type="string" description="Username used to authenticate with the PPPoE service." /></setting><setting name="proxy" description="WWW Proxy Settings" name_upper="PROXY"><property name="browser-only" name_upper="BROWSER_ONLY" type="boolean" default="FALSE" description="Whether the proxy configuration is for browser only." /><property name="method" name_upper="METHOD" type="int32" default="0" description="Method for proxy configuration, Default is NM_SETTING_PROXY_METHOD_NONE (0)" /><property name="pac-script" name_upper="PAC_SCRIPT" type="string" description="PAC script for the connection." /><property name="pac-url" name_upper="PAC_URL" type="string" description="PAC URL for obtaining PAC file." /></setting><setting name="serial" description="Serial Link Settings" name_upper="SERIAL"><property name="baud" name_upper="BAUD" type="uint32" default="57600" description="Speed to use for communication over the serial port. Note that this value usually has no effect for mobile broadband modems as they generally ignore speed settings and use the highest available speed." /><property name="bits" name_upper="BITS" type="uint32" default="8" description="Byte-width of the serial communication. The 8 in "8n1" for example." /><property name="parity" name_upper="PARITY" type="NMSettingSerialParity (byte)" description="Parity setting of the serial port." /><property name="send-delay" name_upper="SEND_DELAY" type="uint64" default="0" description="Time to delay between each byte sent to the modem, in microseconds." /><property name="stopbits" name_upper="STOPBITS" type="uint32" default="1" description="Number of stop bits for communication on the serial port. Either 1 or 2. The 1 in "8n1" for example." /></setting><setting name="sriov" description="SR-IOV settings" name_upper="SRIOV"><property name="autoprobe-drivers" name_upper="AUTOPROBE_DRIVERS" type="NMTernary (int32)" description="Whether to autoprobe virtual functions by a compatible driver. If set to NM_TERNARY_TRUE (1), the kernel will try to bind VFs to a compatible driver and if this succeeds a new network interface will be instantiated for each VF. If set to NM_TERNARY_FALSE (0), VFs will not be claimed and no network interfaces will be created for them. When set to NM_TERNARY_DEFAULT (-1), the global default is used; in case the global default is unspecified it is assumed to be NM_TERNARY_TRUE (1)." /><property name="total-vfs" name_upper="TOTAL_VFS" type="uint32" default="0" description="The total number of virtual functions to create. Note that when the sriov setting is present NetworkManager enforces the number of virtual functions on the interface (also when it is zero) during activation and resets it upon deactivation. To prevent any changes to SR-IOV parameters don't add a sriov setting to the connection." /><property name="vfs" name_upper="VFS" type="array of vardict" description="Array of virtual function descriptors. Each VF descriptor is a dictionary mapping attribute names to GVariant values. The 'index' entry is mandatory for each VF. When represented as string a VF is in the form: "INDEX [ATTR=VALUE[ ATTR=VALUE]...]". for example: "2 mac=00:11:22:33:44:55 spoof-check=true". Multiple VFs can be specified using a comma as separator. Currently, the following attributes are supported: mac, spoof-check, trust, min-tx-rate, max-tx-rate, vlans. The "vlans" attribute is represented as a semicolon-separated list of VLAN descriptors, where each descriptor has the form "ID[.PRIORITY[.PROTO]]". PROTO can be either 'q' for 802.1Q (the default) or 'ad' for 802.1ad." /></setting><setting name="tc" description="Linux Traffic Control Settings" name_upper="TC_CONFIG"><property name="qdiscs" name_upper="QDISCS" type="GPtrArray(NMTCQdisc)" description="Array of TC queueing disciplines. When the "tc" setting is present, qdiscs from this property are applied upon activation. If the property is empty, all qdiscs are removed and the device will only have the default qdisc assigned by kernel according to the "net.core.default_qdisc" sysctl. If the "tc" setting is not present, NetworkManager doesn't touch the qdiscs present on the interface."><description-docbook> +</para></description-docbook></property><property name="token" name_upper="TOKEN" type="string" description="Configure the token for draft-chown-6man-tokenised-ipv6-identifiers-02 IPv6 tokenized interface identifiers. Useful with eui64 addr-gen-mode." /></setting><setting name="ip-tunnel" description="IP Tunneling Settings" name_upper="IP_TUNNEL"><property name="encapsulation-limit" name_upper="ENCAPSULATION_LIMIT" type="uint32" default="0" description="How many additional levels of encapsulation are permitted to be prepended to packets. This property applies only to IPv6 tunnels." /><property name="flags" name_upper="FLAGS" type="uint32" default="0" description="Tunnel flags. Currently, the following values are supported: NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT (0x1), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS (0x2), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL (0x4), NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV (0x8), NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY (0x10), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK (0x20). They are valid only for IPv6 tunnels." /><property name="flow-label" name_upper="FLOW_LABEL" type="uint32" default="0" description="The flow label to assign to tunnel packets. This property applies only to IPv6 tunnels." /><property name="input-key" name_upper="INPUT_KEY" type="string" description="The key used for tunnel input packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used." /><property name="local" name_upper="LOCAL" type="string" description="The local endpoint of the tunnel; the value can be empty, otherwise it must contain an IPv4 or IPv6 address." /><property name="mode" name_upper="MODE" type="uint32" default="0" description="The tunneling mode, for example NM_IP_TUNNEL_MODE_IPIP (1) or NM_IP_TUNNEL_MODE_GRE (2)." /><property name="mtu" name_upper="MTU" type="uint32" default="0" description="If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments." /><property name="output-key" name_upper="OUTPUT_KEY" type="string" description="The key used for tunnel output packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used." /><property name="parent" name_upper="PARENT" type="string" description="If given, specifies the parent interface name or parent connection UUID the new device will be bound to so that tunneled packets will only be routed via that interface." /><property name="path-mtu-discovery" name_upper="PATH_MTU_DISCOVERY" type="boolean" default="TRUE" description="Whether to enable Path MTU Discovery on this tunnel." /><property name="remote" name_upper="REMOTE" type="string" description="The remote endpoint of the tunnel; the value must contain an IPv4 or IPv6 address." /><property name="tos" name_upper="TOS" type="uint32" default="0" description="The type of service (IPv4) or traffic class (IPv6) field to be set on tunneled packets." /><property name="ttl" name_upper="TTL" type="uint32" default="0" description="The TTL to assign to tunneled packets. 0 is a special value meaning that packets inherit the TTL value." /></setting><setting name="macsec" description="MACSec Settings" name_upper="MACSEC"><property name="encrypt" name_upper="ENCRYPT" type="boolean" default="TRUE" description="Whether the transmitted traffic must be encrypted." /><property name="mka-cak" name_upper="MKA_CAK" type="string" description="The pre-shared CAK (Connectivity Association Key) for MACsec Key Agreement." /><property name="mka-cak-flags" name_upper="MKA_CAK_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "mka-cak" property." /><property name="mka-ckn" name_upper="MKA_CKN" type="string" description="The pre-shared CKN (Connectivity-association Key Name) for MACsec Key Agreement." /><property name="mode" name_upper="MODE" type="int32" default="0" description="Specifies how the CAK (Connectivity Association Key) for MKA (MACsec Key Agreement) is obtained." /><property name="parent" name_upper="PARENT" type="string" description="If given, specifies the parent interface name or parent connection UUID from which this MACSEC interface should be created. If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property." /><property name="port" name_upper="PORT" type="int32" default="1" description="The port component of the SCI (Secure Channel Identifier), between 1 and 65534." /><property name="send-sci" name_upper="SEND_SCI" type="boolean" default="TRUE" description="Specifies whether the SCI (Secure Channel Identifier) is included in every packet." /><property name="validation" name_upper="VALIDATION" type="int32" default="2" description="Specifies the validation mode for incoming frames." /></setting><setting name="macvlan" description="MAC VLAN Settings" name_upper="MACVLAN"><property name="mode" name_upper="MODE" type="uint32" default="0" description="The macvlan mode, which specifies the communication mechanism between multiple macvlans on the same lower device." /><property name="parent" name_upper="PARENT" type="string" description="If given, specifies the parent interface name or parent connection UUID from which this MAC-VLAN interface should be created. If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property." /><property name="promiscuous" name_upper="PROMISCUOUS" type="boolean" default="TRUE" description="Whether the interface should be put in promiscuous mode." /><property name="tap" name_upper="TAP" type="boolean" default="FALSE" description="Whether the interface should be a MACVTAP." /></setting><setting name="match" description="Match settings" name_upper="MATCH"><property name="driver" name_upper="DRIVER" type="array of string" description="A list of driver names to match. Each element is a shell wildcard pattern. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the pattern." /><property name="interface-name" name_upper="INTERFACE_NAME" type="array of string" description="A list of interface names to match. Each element is a shell wildcard pattern. An element can be prefixed with a pipe symbol (|) or an ampersand (&). The former means that the element is optional and the latter means that it is mandatory. If there are any optional elements, than the match evaluates to true if at least one of the optional element matches (logical OR). If there are any mandatory elements, then they all must match (logical AND). By default, an element is optional. This means that an element "foo" behaves the same as "|foo". An element can also be inverted with exclamation mark (!) between the pipe symbol (or the ampersand) and before the pattern. Note that "!foo" is a shortcut for the mandatory match "&!foo". Finally, a backslash can be used at the beginning of the element (after the optional special characters) to escape the start of the pattern. For example, "&\\!a" is an mandatory match for literally "!a"." /><property name="kernel-command-line" name_upper="KERNEL_COMMAND_LINE" type="array of string" description="A list of kernel command line arguments to match. This may be used to check whether a specific kernel command line option is set (or unset, if prefixed with the exclamation mark). The argument must either be a single word, or an assignment (i.e. two words, joined by "="). In the former case the kernel command line is searched for the word appearing as is, or as left hand side of an assignment. In the latter case, the exact assignment is looked for with right and left hand side matching. Wildcard patterns are not supported. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the match." /><property name="path" name_upper="PATH" type="array of string" description="A list of paths to match against the ID_PATH udev property of devices. ID_PATH represents the topological persistent path of a device. It typically contains a subsystem string (pci, usb, platform, etc.) and a subsystem-specific identifier. For PCI devices the path has the form "pci-$domain:$bus:$device.$function", where each variable is an hexadecimal value; for example "pci-0000:0a:00.0". The path of a device can be obtained with "udevadm info /sys/class/net/$dev | grep ID_PATH=" or by looking at the "path" property exported by NetworkManager ("nmcli -f general.path device show $dev"). Each element of the list is a shell wildcard pattern. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the pattern." /></setting><setting name="802-11-olpc-mesh" description="OLPC Wireless Mesh Settings" name_upper="OLPC_MESH"><property name="channel" name_upper="CHANNEL" type="uint32" default="0" description="Channel on which the mesh network to join is located." /><property name="dhcp-anycast-address" name_upper="DHCP_ANYCAST_ADDRESS" type="byte array" description="Anycast DHCP MAC address used when requesting an IP address via DHCP. The specific anycast address used determines which DHCP server class answers the request. This is currently only implemented by dhclient DHCP plugin." /><property name="ssid" name_upper="SSID" type="byte array" description="SSID of the mesh network to join." /></setting><setting name="ovs-bridge" description="OvsBridge Link Settings" name_upper="OVS_BRIDGE"><property name="datapath-type" name_upper="DATAPATH_TYPE" type="string" description="The data path type. One of "system", "netdev" or empty." /><property name="fail-mode" name_upper="FAIL_MODE" type="string" description="The bridge failure mode. One of "secure", "standalone" or empty." /><property name="mcast-snooping-enable" name_upper="MCAST_SNOOPING_ENABLE" type="boolean" default="FALSE" description="Enable or disable multicast snooping." /><property name="rstp-enable" name_upper="RSTP_ENABLE" type="boolean" default="FALSE" description="Enable or disable RSTP." /><property name="stp-enable" name_upper="STP_ENABLE" type="boolean" default="FALSE" description="Enable or disable STP." /></setting><setting name="ovs-dpdk" description="OvsDpdk Link Settings" name_upper="OVS_DPDK"><property name="devargs" name_upper="DEVARGS" type="string" description="Open vSwitch DPDK device arguments." /><property name="n-rxq" name_upper="N_RXQ" type="uint32" default="0" description="Open vSwitch DPDK number of rx queues. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures one queue." /></setting><setting name="ovs-interface" description="Open vSwitch Interface Settings" name_upper="OVS_INTERFACE"><property name="type" name_upper="TYPE" type="string" description="The interface type. Either "internal", "system", "patch", "dpdk", or empty." /></setting><setting name="ovs-patch" description="OvsPatch Link Settings" name_upper="OVS_PATCH"><property name="peer" name_upper="PEER" type="string" description="Specifies the name of the interface for the other side of the patch. The patch on the other side must also set this interface as peer." /></setting><setting name="ovs-port" description="OvsPort Link Settings" name_upper="OVS_PORT"><property name="bond-downdelay" name_upper="BOND_DOWNDELAY" type="uint32" default="0" description="The time port must be inactive in order to be considered down." /><property name="bond-mode" name_upper="BOND_MODE" type="string" description="Bonding mode. One of "active-backup", "balance-slb", or "balance-tcp"." /><property name="bond-updelay" name_upper="BOND_UPDELAY" type="uint32" default="0" description="The time port must be active before it starts forwarding traffic." /><property name="lacp" name_upper="LACP" type="string" description="LACP mode. One of "active", "off", or "passive"." /><property name="tag" name_upper="TAG" type="uint32" default="0" description="The VLAN tag in the range 0-4095." /><property name="vlan-mode" name_upper="VLAN_MODE" type="string" description="The VLAN mode. One of "access", "native-tagged", "native-untagged", "trunk" or unset." /></setting><setting name="ppp" description="Point-to-Point Protocol Settings" name_upper="PPP"><property name="baud" name_upper="BAUD" type="uint32" default="0" description="If non-zero, instruct pppd to set the serial port to the specified baudrate. This value should normally be left as 0 to automatically choose the speed." /><property name="crtscts" name_upper="CRTSCTS" type="boolean" default="FALSE" description="If TRUE, specify that pppd should set the serial port to use hardware flow control with RTS and CTS signals. This value should normally be set to FALSE." /><property name="lcp-echo-failure" name_upper="LCP_ECHO_FAILURE" type="uint32" default="0" description="If non-zero, instruct pppd to presume the connection to the peer has failed if the specified number of LCP echo-requests go unanswered by the peer. The "lcp-echo-interval" property must also be set to a non-zero value if this property is used." /><property name="lcp-echo-interval" name_upper="LCP_ECHO_INTERVAL" type="uint32" default="0" description="If non-zero, instruct pppd to send an LCP echo-request frame to the peer every n seconds (where n is the specified value). Note that some PPP peers will respond to echo requests and some will not, and it is not possible to autodetect this." /><property name="mppe-stateful" name_upper="MPPE_STATEFUL" type="boolean" default="FALSE" description="If TRUE, stateful MPPE is used. See pppd documentation for more information on stateful MPPE." /><property name="mru" name_upper="MRU" type="uint32" default="0" description="If non-zero, instruct pppd to request that the peer send packets no larger than the specified size. If non-zero, the MRU should be between 128 and 16384." /><property name="mtu" name_upper="MTU" type="uint32" default="0" description="If non-zero, instruct pppd to send packets no larger than the specified size." /><property name="no-vj-comp" name_upper="NO_VJ_COMP" type="boolean" default="FALSE" description="If TRUE, Van Jacobsen TCP header compression will not be requested." /><property name="noauth" name_upper="NOAUTH" type="boolean" default="TRUE" description="If TRUE, do not require the other side (usually the PPP server) to authenticate itself to the client. If FALSE, require authentication from the remote side. In almost all cases, this should be TRUE." /><property name="nobsdcomp" name_upper="NOBSDCOMP" type="boolean" default="FALSE" description="If TRUE, BSD compression will not be requested." /><property name="nodeflate" name_upper="NODEFLATE" type="boolean" default="FALSE" description="If TRUE, "deflate" compression will not be requested." /><property name="refuse-chap" name_upper="REFUSE_CHAP" type="boolean" default="FALSE" description="If TRUE, the CHAP authentication method will not be used." /><property name="refuse-eap" name_upper="REFUSE_EAP" type="boolean" default="FALSE" description="If TRUE, the EAP authentication method will not be used." /><property name="refuse-mschap" name_upper="REFUSE_MSCHAP" type="boolean" default="FALSE" description="If TRUE, the MSCHAP authentication method will not be used." /><property name="refuse-mschapv2" name_upper="REFUSE_MSCHAPV2" type="boolean" default="FALSE" description="If TRUE, the MSCHAPv2 authentication method will not be used." /><property name="refuse-pap" name_upper="REFUSE_PAP" type="boolean" default="FALSE" description="If TRUE, the PAP authentication method will not be used." /><property name="require-mppe" name_upper="REQUIRE_MPPE" type="boolean" default="FALSE" description="If TRUE, MPPE (Microsoft Point-to-Point Encryption) will be required for the PPP session. If either 64-bit or 128-bit MPPE is not available the session will fail. Note that MPPE is not used on mobile broadband connections." /><property name="require-mppe-128" name_upper="REQUIRE_MPPE_128" type="boolean" default="FALSE" description="If TRUE, 128-bit MPPE (Microsoft Point-to-Point Encryption) will be required for the PPP session, and the "require-mppe" property must also be set to TRUE. If 128-bit MPPE is not available the session will fail." /></setting><setting name="pppoe" description="PPP-over-Ethernet Settings" name_upper="PPPOE"><property name="parent" name_upper="PARENT" type="string" description="If given, specifies the parent interface name on which this PPPoE connection should be created. If this property is not specified, the connection is activated on the interface specified in "interface-name" of NMSettingConnection." /><property name="password" name_upper="PASSWORD" type="string" description="Password used to authenticate with the PPPoE service." /><property name="password-flags" name_upper="PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description="Flags indicating how to handle the "password" property." /><property name="service" name_upper="SERVICE" type="string" description="If specified, instruct PPPoE to only initiate sessions with access concentrators that provide the specified service. For most providers, this should be left blank. It is only required if there are multiple access concentrators or a specific service is known to be required." /><property name="username" name_upper="USERNAME" type="string" description="Username used to authenticate with the PPPoE service." /></setting><setting name="proxy" description="WWW Proxy Settings" name_upper="PROXY"><property name="browser-only" name_upper="BROWSER_ONLY" type="boolean" default="FALSE" description="Whether the proxy configuration is for browser only." /><property name="method" name_upper="METHOD" type="int32" default="0" description="Method for proxy configuration, Default is NM_SETTING_PROXY_METHOD_NONE (0)" /><property name="pac-script" name_upper="PAC_SCRIPT" type="string" description="The PAC script. In the profile this must be an UTF-8 encoded javascript code that defines a FindProxyForURL() function. When setting the property in nmcli, a filename is accepted too. In that case, nmcli will read the content of the file and set the script. The prefixes "file://" and "js://" are supported to explicitly differentiate between the two." /><property name="pac-url" name_upper="PAC_URL" type="string" description="PAC URL for obtaining PAC file." /></setting><setting name="serial" description="Serial Link Settings" name_upper="SERIAL"><property name="baud" name_upper="BAUD" type="uint32" default="57600" description="Speed to use for communication over the serial port. Note that this value usually has no effect for mobile broadband modems as they generally ignore speed settings and use the highest available speed." /><property name="bits" name_upper="BITS" type="uint32" default="8" description="Byte-width of the serial communication. The 8 in "8n1" for example." /><property name="parity" name_upper="PARITY" type="NMSettingSerialParity (byte)" description="Parity setting of the serial port." /><property name="send-delay" name_upper="SEND_DELAY" type="uint64" default="0" description="Time to delay between each byte sent to the modem, in microseconds." /><property name="stopbits" name_upper="STOPBITS" type="uint32" default="1" description="Number of stop bits for communication on the serial port. Either 1 or 2. The 1 in "8n1" for example." /></setting><setting name="sriov" description="SR-IOV settings" name_upper="SRIOV"><property name="autoprobe-drivers" name_upper="AUTOPROBE_DRIVERS" type="NMTernary (int32)" description="Whether to autoprobe virtual functions by a compatible driver. If set to NM_TERNARY_TRUE (1), the kernel will try to bind VFs to a compatible driver and if this succeeds a new network interface will be instantiated for each VF. If set to NM_TERNARY_FALSE (0), VFs will not be claimed and no network interfaces will be created for them. When set to NM_TERNARY_DEFAULT (-1), the global default is used; in case the global default is unspecified it is assumed to be NM_TERNARY_TRUE (1)." /><property name="total-vfs" name_upper="TOTAL_VFS" type="uint32" default="0" description="The total number of virtual functions to create. Note that when the sriov setting is present NetworkManager enforces the number of virtual functions on the interface (also when it is zero) during activation and resets it upon deactivation. To prevent any changes to SR-IOV parameters don't add a sriov setting to the connection." /><property name="vfs" name_upper="VFS" type="array of vardict" description="Array of virtual function descriptors. Each VF descriptor is a dictionary mapping attribute names to GVariant values. The 'index' entry is mandatory for each VF. When represented as string a VF is in the form: "INDEX [ATTR=VALUE[ ATTR=VALUE]...]". for example: "2 mac=00:11:22:33:44:55 spoof-check=true". Multiple VFs can be specified using a comma as separator. Currently, the following attributes are supported: mac, spoof-check, trust, min-tx-rate, max-tx-rate, vlans. The "vlans" attribute is represented as a semicolon-separated list of VLAN descriptors, where each descriptor has the form "ID[.PRIORITY[.PROTO]]". PROTO can be either 'q' for 802.1Q (the default) or 'ad' for 802.1ad." /></setting><setting name="tc" description="Linux Traffic Control Settings" name_upper="TC_CONFIG"><property name="qdiscs" name_upper="QDISCS" type="GPtrArray(NMTCQdisc)" description="Array of TC queueing disciplines. When the "tc" setting is present, qdiscs from this property are applied upon activation. If the property is empty, all qdiscs are removed and the device will only have the default qdisc assigned by kernel according to the "net.core.default_qdisc" sysctl. If the "tc" setting is not present, NetworkManager doesn't touch the qdiscs present on the interface."><description-docbook> <para> Array of TC queueing disciplines. qdisc is a basic block in the Linux traffic control subsystem diff --git a/src/libnmc-setting/settings-docs.h b/src/libnmc-setting/settings-docs.h index 5ba6302d..2a582ad7 100644 --- a/src/libnmc-setting/settings-docs.h +++ b/src/libnmc-setting/settings-docs.h @@ -152,7 +152,7 @@ #define DESCRIBE_DOC_NM_SETTING_CDMA_PASSWORD_FLAGS N_("Flags indicating how to handle the \"password\" property.") #define DESCRIBE_DOC_NM_SETTING_CDMA_USERNAME N_("The username used to authenticate with the network, if required. Many providers do not require a username, or accept any username. But if a username is required, it is specified here.") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTH_RETRIES N_("The number of retries for the authentication. Zero means to try indefinitely; -1 means to use a global default. If the global default is not set, the authentication retries for 3 times before failing the connection. Currently, this only applies to 802-1x authentication.") -#define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT N_("Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See \"secondaries\" as an alternative to automatically connect VPN profiles.") +#define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT N_("Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See \"secondaries\" as an alternative to automatically connect VPN profiles. If multiple profiles are ready to autoconnect on the same device, the one with the better \"connection.autoconnect-priority\" is chosen. If the priorities are equal, then the most recently connected profile is activated. If the profiles were not connected earlier or their \"connection.timestamp\" is identical, the choice is undefined. Depending on \"connection.multi-connect\", a profile can (auto)connect only once at a time or multiple times.") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY N_("The autoconnect priority in range -999 to 999. If the connection is set to autoconnect, connections with higher priority will be preferred. The higher number means higher priority. Defaults to 0. Note that this property only matters if there are more than one candidate profile to select for autoconnect. In case of equal priority, the profile used most recently is chosen.") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES N_("The number of times a connection should be tried when autoactivating before giving up. Zero means forever, -1 means the global default (4 times if not overridden). Setting this to 1 means to try activation only once before blocking autoconnect. Note that after a timeout, NetworkManager will try to autoconnect again.") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES N_("Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties \"autoconnect\", \"autoconnect-priority\" and \"autoconnect-retries\" are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0.") @@ -171,7 +171,7 @@ #define DESCRIBE_DOC_NM_SETTING_CONNECTION_READ_ONLY N_("FALSE if the connection can be modified using the provided settings service's D-Bus interface with the right privileges, or TRUE if the connection is read-only and cannot be modified.") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_SECONDARIES N_("List of connection UUIDs that should be activated when the base connection itself is activated. Currently, only VPN connections are supported.") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_SLAVE_TYPE N_("Setting name of the device type of this slave's master connection (eg, \"bond\"), or NULL if this connection is not a slave.") -#define DESCRIBE_DOC_NM_SETTING_CONNECTION_STABLE_ID N_("This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret-key. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are \"${CONNECTION}\", \"${DEVICE}\", \"${MAC}\", \"${BOOT}\", \"${RANDOM}\". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that \"${DEVICE}\" corresponds to the interface name of the device and \"${MAC}\" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as \"$$\". For example, set it to \"${CONNECTION}-${BOOT}-${DEVICE}\" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to \"${CONNECTION}\" and uses a unique, fixed ID for the connection.") +#define DESCRIBE_DOC_NM_SETTING_CONNECTION_STABLE_ID N_("This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual about the secret-key and the host identity. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are \"${CONNECTION}\", \"${DEVICE}\", \"${MAC}\", \"${BOOT}\", \"${RANDOM}\". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that \"${DEVICE}\" corresponds to the interface name of the device and \"${MAC}\" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as \"$$\". For example, set it to \"${CONNECTION}-${BOOT}-${DEVICE}\" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to \"${CONNECTION}\" and uses a unique, fixed ID for the connection.") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TIMESTAMP N_("The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved).") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TYPE N_("Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, \"802-3-ethernet\" or \"802-11-wireless\" or \"bluetooth\", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, \"vpn\" or \"bridge\", etc).") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_UUID N_("A universally unique identifier for the connection, for example generated with libuuid. It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network. For example, it should not be changed when the \"id\" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or \"type\" property changes. The UUID must be in the format \"2815492f-7e56-435e-b2e9-246bd7cdc664\" (ie, contains only hexadecimal characters and \"-\").") @@ -255,7 +255,7 @@ #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTES N_("A list of IPv4 destination addresses, prefix length, optional IPv4 next hop addresses, optional route metric, optional attribute. The valid syntax is: \"ip[/prefix] [next-hop] [metric] [attribute=val]...[,ip[/prefix]...]\". For example \"192.0.2.0/24 10.1.1.1 77, 198.51.100.0/24\".") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTING_RULES N_("A comma separated list of routing rules for policy routing.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE N_("Configure method for creating the address for use with RFC4862 IPv6 Stateless Address Autoconfiguration. The permitted values are: NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 (0) or NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY (1). If the property is set to EUI64, the addresses will be generated using the interface tokens derived from hardware address. This makes the host part of the address to stay constant, making it possible to track host's presence when it changes networks. The address changes when the interface hardware is replaced. The value of stable-privacy enables use of cryptographically secure hash of a secret host-specific key along with the connection's stable-id and the network address as specified by RFC7217. This makes it impossible to use the address track host's presence, and makes the address stable when the network interface hardware is replaced. On D-Bus, the absence of an addr-gen-mode setting equals enabling stable-privacy. For keyfile plugin, the absence of the setting on disk means EUI64 so that the property doesn't change on upgrade from older versions. Note that this setting is distinct from the Privacy Extensions as configured by \"ip6-privacy\" property and it does not affect the temporary addresses configured with this option.") -#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDRESSES N_("A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64\". The addresses are listed in increasing priority, meaning the last address will be the primary address.") +#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDRESSES N_("A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64\". The addresses are listed in decreasing priority, meaning the first address will be the primary address. This can make a difference with IPv6 source address selection (RFC 6724, section 5).") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DAD_TIMEOUT N_("Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_DUID N_("A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp client to identify itself to DHCPv6 servers (RFC 3315). The DUID is carried in the Client Identifier option. If the property is a hex string ('aa:bb:cc') it is interpreted as a binary DUID and filled as an opaque value in the Client Identifier option. The special value \"lease\" will retrieve the DUID previously used from the lease file belonging to the connection. If no DUID is found and \"dhclient\" is the configured dhcp client, the DUID is searched in the system-wide dhclient lease file. If still no DUID is found, or another dhcp client is used, a global and permanent DUID-UUID (RFC 6355) will be generated based on the machine-id. The special values \"llt\" and \"ll\" will generate a DUID of type LLT or LL (see RFC 3315) based on the current MAC address of the device. In order to try providing a stable DUID-LLT, the time field will contain a constant timestamp that is used globally (for all profiles) and persisted to disk. The special values \"stable-llt\", \"stable-ll\" and \"stable-uuid\" will generate a DUID of the corresponding type, derived from the connection's stable-id and a per-host unique key. You may want to include the \"${DEVICE}\" or \"${MAC}\" specifier in the stable-id, in case this profile gets activated on multiple devices. So, the link-layer address of \"stable-ll\" and \"stable-llt\" will be a generated address derived from the stable id. The DUID-LLT time value in the \"stable-llt\" option will be picked among a static timespan of three years (the upper bound of the interval is the same constant timestamp used in \"llt\"). When the property is unset, the global value provided for \"ipv6.dhcp-duid\" is used. If no global value is provided, the default \"lease\" value is assumed.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-fqdn\" are mutually exclusive and cannot be set at the same time.") @@ -340,7 +340,7 @@ #define DESCRIBE_DOC_NM_SETTING_PPPOE_USERNAME N_("Username used to authenticate with the PPPoE service.") #define DESCRIBE_DOC_NM_SETTING_PROXY_BROWSER_ONLY N_("Whether the proxy configuration is for browser only.") #define DESCRIBE_DOC_NM_SETTING_PROXY_METHOD N_("Method for proxy configuration, Default is NM_SETTING_PROXY_METHOD_NONE (0)") -#define DESCRIBE_DOC_NM_SETTING_PROXY_PAC_SCRIPT N_("PAC script for the connection.") +#define DESCRIBE_DOC_NM_SETTING_PROXY_PAC_SCRIPT N_("The PAC script. In the profile this must be an UTF-8 encoded javascript code that defines a FindProxyForURL() function. When setting the property in nmcli, a filename is accepted too. In that case, nmcli will read the content of the file and set the script. The prefixes \"file://\" and \"js://\" are supported to explicitly differentiate between the two.") #define DESCRIBE_DOC_NM_SETTING_PROXY_PAC_URL N_("PAC URL for obtaining PAC file.") #define DESCRIBE_DOC_NM_SETTING_SERIAL_BAUD N_("Speed to use for communication over the serial port. Note that this value usually has no effect for mobile broadband modems as they generally ignore speed settings and use the highest available speed.") #define DESCRIBE_DOC_NM_SETTING_SERIAL_BITS N_("Byte-width of the serial communication. The 8 in \"8n1\" for example.") diff --git a/src/libnmc-setting/settings-docs.h.in b/src/libnmc-setting/settings-docs.h.in index 5ba6302d..2a582ad7 100644 --- a/src/libnmc-setting/settings-docs.h.in +++ b/src/libnmc-setting/settings-docs.h.in @@ -152,7 +152,7 @@ #define DESCRIBE_DOC_NM_SETTING_CDMA_PASSWORD_FLAGS N_("Flags indicating how to handle the \"password\" property.") #define DESCRIBE_DOC_NM_SETTING_CDMA_USERNAME N_("The username used to authenticate with the network, if required. Many providers do not require a username, or accept any username. But if a username is required, it is specified here.") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTH_RETRIES N_("The number of retries for the authentication. Zero means to try indefinitely; -1 means to use a global default. If the global default is not set, the authentication retries for 3 times before failing the connection. Currently, this only applies to 802-1x authentication.") -#define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT N_("Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See \"secondaries\" as an alternative to automatically connect VPN profiles.") +#define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT N_("Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See \"secondaries\" as an alternative to automatically connect VPN profiles. If multiple profiles are ready to autoconnect on the same device, the one with the better \"connection.autoconnect-priority\" is chosen. If the priorities are equal, then the most recently connected profile is activated. If the profiles were not connected earlier or their \"connection.timestamp\" is identical, the choice is undefined. Depending on \"connection.multi-connect\", a profile can (auto)connect only once at a time or multiple times.") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY N_("The autoconnect priority in range -999 to 999. If the connection is set to autoconnect, connections with higher priority will be preferred. The higher number means higher priority. Defaults to 0. Note that this property only matters if there are more than one candidate profile to select for autoconnect. In case of equal priority, the profile used most recently is chosen.") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES N_("The number of times a connection should be tried when autoactivating before giving up. Zero means forever, -1 means the global default (4 times if not overridden). Setting this to 1 means to try activation only once before blocking autoconnect. Note that after a timeout, NetworkManager will try to autoconnect again.") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES N_("Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties \"autoconnect\", \"autoconnect-priority\" and \"autoconnect-retries\" are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0.") @@ -171,7 +171,7 @@ #define DESCRIBE_DOC_NM_SETTING_CONNECTION_READ_ONLY N_("FALSE if the connection can be modified using the provided settings service's D-Bus interface with the right privileges, or TRUE if the connection is read-only and cannot be modified.") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_SECONDARIES N_("List of connection UUIDs that should be activated when the base connection itself is activated. Currently, only VPN connections are supported.") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_SLAVE_TYPE N_("Setting name of the device type of this slave's master connection (eg, \"bond\"), or NULL if this connection is not a slave.") -#define DESCRIBE_DOC_NM_SETTING_CONNECTION_STABLE_ID N_("This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret-key. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are \"${CONNECTION}\", \"${DEVICE}\", \"${MAC}\", \"${BOOT}\", \"${RANDOM}\". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that \"${DEVICE}\" corresponds to the interface name of the device and \"${MAC}\" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as \"$$\". For example, set it to \"${CONNECTION}-${BOOT}-${DEVICE}\" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to \"${CONNECTION}\" and uses a unique, fixed ID for the connection.") +#define DESCRIBE_DOC_NM_SETTING_CONNECTION_STABLE_ID N_("This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual about the secret-key and the host identity. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are \"${CONNECTION}\", \"${DEVICE}\", \"${MAC}\", \"${BOOT}\", \"${RANDOM}\". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that \"${DEVICE}\" corresponds to the interface name of the device and \"${MAC}\" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as \"$$\". For example, set it to \"${CONNECTION}-${BOOT}-${DEVICE}\" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to \"${CONNECTION}\" and uses a unique, fixed ID for the connection.") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TIMESTAMP N_("The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved).") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TYPE N_("Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, \"802-3-ethernet\" or \"802-11-wireless\" or \"bluetooth\", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, \"vpn\" or \"bridge\", etc).") #define DESCRIBE_DOC_NM_SETTING_CONNECTION_UUID N_("A universally unique identifier for the connection, for example generated with libuuid. It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network. For example, it should not be changed when the \"id\" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or \"type\" property changes. The UUID must be in the format \"2815492f-7e56-435e-b2e9-246bd7cdc664\" (ie, contains only hexadecimal characters and \"-\").") @@ -255,7 +255,7 @@ #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTES N_("A list of IPv4 destination addresses, prefix length, optional IPv4 next hop addresses, optional route metric, optional attribute. The valid syntax is: \"ip[/prefix] [next-hop] [metric] [attribute=val]...[,ip[/prefix]...]\". For example \"192.0.2.0/24 10.1.1.1 77, 198.51.100.0/24\".") #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTING_RULES N_("A comma separated list of routing rules for policy routing.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE N_("Configure method for creating the address for use with RFC4862 IPv6 Stateless Address Autoconfiguration. The permitted values are: NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 (0) or NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY (1). If the property is set to EUI64, the addresses will be generated using the interface tokens derived from hardware address. This makes the host part of the address to stay constant, making it possible to track host's presence when it changes networks. The address changes when the interface hardware is replaced. The value of stable-privacy enables use of cryptographically secure hash of a secret host-specific key along with the connection's stable-id and the network address as specified by RFC7217. This makes it impossible to use the address track host's presence, and makes the address stable when the network interface hardware is replaced. On D-Bus, the absence of an addr-gen-mode setting equals enabling stable-privacy. For keyfile plugin, the absence of the setting on disk means EUI64 so that the property doesn't change on upgrade from older versions. Note that this setting is distinct from the Privacy Extensions as configured by \"ip6-privacy\" property and it does not affect the temporary addresses configured with this option.") -#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDRESSES N_("A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64\". The addresses are listed in increasing priority, meaning the last address will be the primary address.") +#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDRESSES N_("A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64\". The addresses are listed in decreasing priority, meaning the first address will be the primary address. This can make a difference with IPv6 source address selection (RFC 6724, section 5).") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DAD_TIMEOUT N_("Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_DUID N_("A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp client to identify itself to DHCPv6 servers (RFC 3315). The DUID is carried in the Client Identifier option. If the property is a hex string ('aa:bb:cc') it is interpreted as a binary DUID and filled as an opaque value in the Client Identifier option. The special value \"lease\" will retrieve the DUID previously used from the lease file belonging to the connection. If no DUID is found and \"dhclient\" is the configured dhcp client, the DUID is searched in the system-wide dhclient lease file. If still no DUID is found, or another dhcp client is used, a global and permanent DUID-UUID (RFC 6355) will be generated based on the machine-id. The special values \"llt\" and \"ll\" will generate a DUID of type LLT or LL (see RFC 3315) based on the current MAC address of the device. In order to try providing a stable DUID-LLT, the time field will contain a constant timestamp that is used globally (for all profiles) and persisted to disk. The special values \"stable-llt\", \"stable-ll\" and \"stable-uuid\" will generate a DUID of the corresponding type, derived from the connection's stable-id and a per-host unique key. You may want to include the \"${DEVICE}\" or \"${MAC}\" specifier in the stable-id, in case this profile gets activated on multiple devices. So, the link-layer address of \"stable-ll\" and \"stable-llt\" will be a generated address derived from the stable id. The DUID-LLT time value in the \"stable-llt\" option will be picked among a static timespan of three years (the upper bound of the interval is the same constant timestamp used in \"llt\"). When the property is unset, the global value provided for \"ipv6.dhcp-duid\" is used. If no global value is provided, the default \"lease\" value is assumed.") #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-fqdn\" are mutually exclusive and cannot be set at the same time.") @@ -340,7 +340,7 @@ #define DESCRIBE_DOC_NM_SETTING_PPPOE_USERNAME N_("Username used to authenticate with the PPPoE service.") #define DESCRIBE_DOC_NM_SETTING_PROXY_BROWSER_ONLY N_("Whether the proxy configuration is for browser only.") #define DESCRIBE_DOC_NM_SETTING_PROXY_METHOD N_("Method for proxy configuration, Default is NM_SETTING_PROXY_METHOD_NONE (0)") -#define DESCRIBE_DOC_NM_SETTING_PROXY_PAC_SCRIPT N_("PAC script for the connection.") +#define DESCRIBE_DOC_NM_SETTING_PROXY_PAC_SCRIPT N_("The PAC script. In the profile this must be an UTF-8 encoded javascript code that defines a FindProxyForURL() function. When setting the property in nmcli, a filename is accepted too. In that case, nmcli will read the content of the file and set the script. The prefixes \"file://\" and \"js://\" are supported to explicitly differentiate between the two.") #define DESCRIBE_DOC_NM_SETTING_PROXY_PAC_URL N_("PAC URL for obtaining PAC file.") #define DESCRIBE_DOC_NM_SETTING_SERIAL_BAUD N_("Speed to use for communication over the serial port. Note that this value usually has no effect for mobile broadband modems as they generally ignore speed settings and use the highest available speed.") #define DESCRIBE_DOC_NM_SETTING_SERIAL_BITS N_("Byte-width of the serial communication. The 8 in \"8n1\" for example.") diff --git a/src/linux-headers/nl80211-vnd-intel.h b/src/linux-headers/nl80211-vnd-intel.h new file mode 100644 index 00000000..4ed7d0b2 --- /dev/null +++ b/src/linux-headers/nl80211-vnd-intel.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2013-2015 Intel Mobile Communications GmbH + * Copyright (C) 2016-2017 Intel Deutschland GmbH + */ +#ifndef __VENDOR_CMD_INTEL_H__ +#define __VENDOR_CMD_INTEL_H__ + +#define INTEL_OUI 0x001735 + +/** + * enum iwl_mvm_vendor_cmd - supported vendor commands + * @IWL_MVM_VENDOR_CMD_GET_CSME_CONN_INFO: reports CSME connection info. + * @IWL_MVM_VENDOR_CMD_HOST_GET_OWNERSHIP: asks for ownership on the device. + * This is useful when the CSME firmware owns the device and the kernel + * wants to use it. In case the CSME firmware has no connection active the + * kernel will manage on its own to get ownership of the device. + * When the CSME firmware has an active connection, the user space + * involvement is required. The kernel will assert the RFKILL signal with + * the "device not owned" reason so that nobody can touch the device. Then + * the user space can run the following flow to be able to get connected + * to the very same AP the CSME firmware is currently connected to: + * + * 1) The user space (NetworkManager) boots and sees that the device is + * in RFKILL because the host doesn't own the device + * 2) The user space asks the kernel what AP the CSME firmware is + * connected to (with %IWL_MVM_VENDOR_CMD_GET_CSME_CONN_INFO) + * 3) The user space checks if it has a profile that matches the reply + * from the CSME firmware + * 4) The user space installs a network to the wpa_supplicant with a + * specific BSSID and a specific frequency + * 5) The user space prevents any type of full scan + * 6) The user space asks iwlmei to request ownership on the device (with + * this command) + * 7) iwlmei requests ownership from the CSME firmware + * 8) The CSME firmware grants ownership + * 9) iwlmei tells iwlwifi to lift the RFKILL + * 10) RFKILL OFF is reported to user space + * 11) The host boots the device, loads the firwmare, and connects to a + * specific BSSID without scanning including IP as fast as it can + * 12) The host reports to the CSME firmware that there is a connection + * 13) The TCP connection is preserved and the host has connectivity + * + * @IWL_MVM_VENDOR_CMD_ROAMING_FORBIDDEN_EVENT: notifies if roaming is allowed. + * It contains a &IWL_MVM_VENDOR_ATTR_ROAMING_FORBIDDEN and a + * &IWL_MVM_VENDOR_ATTR_VIF_ADDR attributes. + */ + +enum iwl_mvm_vendor_cmd { + IWL_MVM_VENDOR_CMD_GET_CSME_CONN_INFO = 0x2d, + IWL_MVM_VENDOR_CMD_HOST_GET_OWNERSHIP = 0x30, + IWL_MVM_VENDOR_CMD_ROAMING_FORBIDDEN_EVENT = 0x32, +}; + +enum iwl_vendor_auth_akm_mode { + IWL_VENDOR_AUTH_OPEN, + IWL_VENDOR_AUTH_RSNA = 0x6, + IWL_VENDOR_AUTH_RSNA_PSK, + IWL_VENDOR_AUTH_SAE = 0x9, + IWL_VENDOR_AUTH_MAX, +}; + +/** + * enum iwl_mvm_vendor_attr - attributes used in vendor commands + * @__IWL_MVM_VENDOR_ATTR_INVALID: attribute 0 is invalid + * @IWL_MVM_VENDOR_ATTR_VIF_ADDR: interface MAC address + * @IWL_MVM_VENDOR_ATTR_ADDR: MAC address + * @IWL_MVM_VENDOR_ATTR_SSID: SSID (binary attribute, 0..32 octets) + * @IWL_MVM_VENDOR_ATTR_STA_CIPHER: the cipher to use for the station with the + * mac address specified in &IWL_MVM_VENDOR_ATTR_ADDR. + * @IWL_MVM_VENDOR_ATTR_ROAMING_FORBIDDEN: u8 attribute. Indicates whether + * roaming is forbidden or not. Value 1 means roaming is forbidden, + * 0 mean roaming is allowed. + * @IWL_MVM_VENDOR_ATTR_AUTH_MODE: u32 attribute. Authentication mode type + * as specified in &enum iwl_vendor_auth_akm_mode. + * @IWL_MVM_VENDOR_ATTR_CHANNEL_NUM: u8 attribute. Contains channel number. + * @IWL_MVM_VENDOR_ATTR_BAND: u8 attribute. + * 0 for 2.4 GHz band, 1 for 5.2GHz band and 2 for 6GHz band. + * @IWL_MVM_VENDOR_ATTR_COLLOC_CHANNEL: u32 attribute. Channel number of + * collocated AP. Relevant for 6GHz AP info. + * @IWL_MVM_VENDOR_ATTR_COLLOC_ADDR: MAC address of a collocated AP. + * Relevant for 6GHz AP info. + * + * @NUM_IWL_MVM_VENDOR_ATTR: number of vendor attributes + * @MAX_IWL_MVM_VENDOR_ATTR: highest vendor attribute number + + */ +enum iwl_mvm_vendor_attr { + __IWL_MVM_VENDOR_ATTR_INVALID = 0x00, + IWL_MVM_VENDOR_ATTR_VIF_ADDR = 0x02, + IWL_MVM_VENDOR_ATTR_ADDR = 0x0a, + IWL_MVM_VENDOR_ATTR_SSID = 0x3d, + IWL_MVM_VENDOR_ATTR_STA_CIPHER = 0x51, + IWL_MVM_VENDOR_ATTR_ROAMING_FORBIDDEN = 0x64, + IWL_MVM_VENDOR_ATTR_AUTH_MODE = 0x65, + IWL_MVM_VENDOR_ATTR_CHANNEL_NUM = 0x66, + IWL_MVM_VENDOR_ATTR_BAND = 0x69, + IWL_MVM_VENDOR_ATTR_COLLOC_CHANNEL = 0x70, + IWL_MVM_VENDOR_ATTR_COLLOC_ADDR = 0x71, + + NUM_IWL_MVM_VENDOR_ATTR, + MAX_IWL_MVM_VENDOR_ATTR = NUM_IWL_MVM_VENDOR_ATTR - 1, +}; + +#endif /* __VENDOR_CMD_INTEL_H__ */ diff --git a/src/meson.build b/src/meson.build index ab696915..f3c87f1a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -78,6 +78,7 @@ subdir('libnm-systemd-core') subdir('libnm-udev-aux') subdir('libnm-base') subdir('libnm-platform') +subdir('libnm-crypto') subdir('libnm-core-public') subdir('libnm-core-intern') subdir('libnm-core-aux-intern') diff --git a/src/n-dhcp4/src/n-dhcp4-c-connection.c b/src/n-dhcp4/src/n-dhcp4-c-connection.c index 2f660e3b..65328286 100644 --- a/src/n-dhcp4/src/n-dhcp4-c-connection.c +++ b/src/n-dhcp4/src/n-dhcp4-c-connection.c @@ -1147,16 +1147,21 @@ int n_dhcp4_c_connection_dispatch_timer(NDhcp4CConnection *connection, int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection, NDhcp4Incoming **messagep) { _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL; + _c_cleanup_(c_freep) uint8_t *buffer = NULL; char serv_addr[INET_ADDRSTRLEN]; char client_addr[INET_ADDRSTRLEN]; uint8_t type = 0; int r; + buffer = malloc(UINT16_MAX); + if (!buffer) + return -ENOMEM; + switch (connection->state) { case N_DHCP4_C_CONNECTION_STATE_PACKET: r = n_dhcp4_c_socket_packet_recv(connection->fd_packet, - connection->scratch_buffer, - sizeof(connection->scratch_buffer), + buffer, + UINT16_MAX, &message); if (!r) break; @@ -1165,8 +1170,8 @@ int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection, return N_DHCP4_E_AGAIN; case N_DHCP4_C_CONNECTION_STATE_DRAINING: r = n_dhcp4_c_socket_packet_recv(connection->fd_packet, - connection->scratch_buffer, - sizeof(connection->scratch_buffer), + buffer, + UINT16_MAX, &message); if (!r) break; @@ -1188,8 +1193,8 @@ int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection, /* fall-through */ case N_DHCP4_C_CONNECTION_STATE_UDP: r = n_dhcp4_c_socket_udp_recv(connection->fd_udp, - connection->scratch_buffer, - sizeof(connection->scratch_buffer), + buffer, + UINT16_MAX, &message); if (!r) break; diff --git a/src/n-dhcp4/src/n-dhcp4-private.h b/src/n-dhcp4/src/n-dhcp4-private.h index 191e946e..858c3d3a 100644 --- a/src/n-dhcp4/src/n-dhcp4-private.h +++ b/src/n-dhcp4/src/n-dhcp4-private.h @@ -334,15 +334,6 @@ struct NDhcp4CConnection { uint32_t client_ip; /* client IP address, or 0 */ uint32_t server_ip; /* server IP address, or 0 */ uint16_t mtu; /* client mtu, or 0 */ - - /* - * When we get DHCP packets from the kernel, we need a buffer to read - * the data into. Since UDP packets can be up to 2^16 bytes in size, we - * avoid placing it on the stack and instead read into this scratch - * buffer. It is purely meant as stack replacement, no data is returned - * through this buffer. - */ - uint8_t scratch_buffer[UINT16_MAX]; }; #define N_DHCP4_C_CONNECTION_NULL(_x) { \ diff --git a/src/nm-cloud-setup/main.c b/src/nm-cloud-setup/main.c index f6acadca..0c452acf 100644 --- a/src/nm-cloud-setup/main.c +++ b/src/nm-cloud-setup/main.c @@ -625,12 +625,7 @@ main(int argc, const char *const *argv) sigterm_cancellable = g_cancellable_new(); - sigterm_source = nm_g_source_attach(nm_g_unix_signal_source_new(SIGTERM, - G_PRIORITY_DEFAULT, - sigterm_handler, - sigterm_cancellable, - NULL), - NULL); + sigterm_source = nm_g_unix_signal_add_source(SIGTERM, sigterm_handler, sigterm_cancellable); provider = _provider_detect(sigterm_cancellable); if (!provider) diff --git a/src/nm-cloud-setup/nm-cloud-setup-utils.c b/src/nm-cloud-setup/nm-cloud-setup-utils.c index bf705549..e505f8bd 100644 --- a/src/nm-cloud-setup/nm-cloud-setup-utils.c +++ b/src/nm-cloud-setup/nm-cloud-setup-utils.c @@ -146,7 +146,7 @@ nmcs_wait_for_objects_iterate_until_done(GMainContext *context, int timeout_msec nm_auto_destroy_and_unref_gsource GSource *idle_source = NULL; idle_source = - nm_g_source_attach(nm_g_idle_source_new(G_PRIORITY_DEFAULT, + nm_g_source_attach(nm_g_idle_source_new(G_PRIORITY_DEFAULT_IDLE, _wait_for_objects_iterate_until_done_idle_cb, &data, NULL), @@ -364,7 +364,7 @@ nmcs_utils_poll(int poll_timeout_ms, } poll_task_data->source_next_poll = nm_g_source_attach( - nm_g_idle_source_new(G_PRIORITY_DEFAULT, _poll_start_cb, poll_task_data, NULL), + nm_g_idle_source_new(G_PRIORITY_DEFAULT_IDLE, _poll_start_cb, poll_task_data, NULL), poll_task_data->context); if (cancellable) { diff --git a/src/nm-cloud-setup/nm-http-client.c b/src/nm-cloud-setup/nm-http-client.c index 7ef9f38d..a0053d47 100644 --- a/src/nm-cloud-setup/nm-http-client.c +++ b/src/nm-cloud-setup/nm-http-client.c @@ -680,14 +680,13 @@ _mhandle_socketfunction_cb(CURL *e_handle, condition = 0; if (condition) { - source_socket = nm_g_unix_fd_source_new(fd, - condition, - G_PRIORITY_DEFAULT, - _mhandle_socket_cb, - self, - NULL); - g_source_attach(source_socket, priv->context); - + source_socket = _source_attach(self, + nm_g_unix_fd_source_new(fd, + condition, + G_PRIORITY_DEFAULT, + _mhandle_socket_cb, + self, + NULL)); g_hash_table_insert(priv->source_sockets_hashtable, GINT_TO_POINTER(fd), source_socket); } } diff --git a/src/nm-dispatcher/tests/test-dispatcher-envp.c b/src/nm-dispatcher/tests/test-dispatcher-envp.c index f53bb1c1..51d31544 100644 --- a/src/nm-dispatcher/tests/test-dispatcher-envp.c +++ b/src/nm-dispatcher/tests/test-dispatcher-envp.c @@ -530,8 +530,11 @@ test_generic(const char *file, const char *override_vpn_ip_iface) g_assert((!denv && error_message) || (denv && !error_message)); - if (error_message) + if (error_message) { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("FAILED: %s", error_message); + NM_PRAGMA_WARNING_REENABLE + } if (g_strv_length(denv) != g_hash_table_size(expected_env)) { _print_env(NM_CAST_STRV_CC(denv), expected_env); @@ -552,8 +555,10 @@ test_generic(const char *file, const char *override_vpn_ip_iface) foo = g_hash_table_lookup(expected_env, i_value); if (!foo) { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER _print_env(NM_CAST_STRV_CC(denv), expected_env); g_error("Failed to find %s in environment", i_value); + NM_PRAGMA_WARNING_REENABLE } } diff --git a/src/nm-online/nm-online.c b/src/nm-online/nm-online.c index 340c2d19..ee4477db 100644 --- a/src/nm-online/nm-online.c +++ b/src/nm-online/nm-online.c @@ -246,7 +246,8 @@ main(int argc, char *argv[]) 0, G_OPTION_ARG_INT, &t_secs, - N_("Time to wait for a connection, in seconds (without the option, default value is 30)"), + N_("Time to wait for a connection, in seconds (without the option, default value is 30). " + "Maximum value is 2073600 seconds."), "<timeout>"}, {"exit", 'x', @@ -289,7 +290,7 @@ main(int argc, char *argv[]) return EXIT_FAILURE_ERROR; } - if (t_secs < 0 || t_secs > 3600) { + if (t_secs < 0 || t_secs > 2073600) { g_printerr("%s: %s\n", argv[0], _("Invalid option. Please use --help to see a list of valid options.")); diff --git a/src/nmcli/common.c b/src/nmcli/common.c index 26398c84..f42f76e4 100644 --- a/src/nmcli/common.c +++ b/src/nmcli/common.c @@ -889,13 +889,7 @@ nmc_readline_helper(const NmcConfig *nmc_config, const char *prompt) nmc_set_in_readline(TRUE); - io_source = nm_g_unix_fd_source_new(STDIN_FILENO, - G_IO_IN, - G_PRIORITY_DEFAULT, - stdin_ready_cb, - NULL, - NULL); - g_source_attach(io_source, NULL); + io_source = nm_g_unix_fd_add_source(STDIN_FILENO, G_IO_IN, stdin_ready_cb, NULL); read_again: rl_string = NULL; diff --git a/src/nmcli/connections.c b/src/nmcli/connections.c index 025f96a1..ecf8e2e2 100644 --- a/src/nmcli/connections.c +++ b/src/nmcli/connections.c @@ -513,6 +513,40 @@ _con_show_fcn_get_type(NMConnection *c, NMActiveConnection *ac, NMMetaAccessorGe return connection_type_to_display(s, get_type); } +static const char * +_connection_check_deprecated(NMConnection *c) +{ + NMSettingWirelessSecurity *s_wsec; + const char *key_mgmt; + const char *type; + + type = nm_connection_get_connection_type(c); + + if (strcmp(type, NM_SETTING_WIMAX_SETTING_NAME) == 0) + return _("WiMax is no longer supported"); + + s_wsec = nm_connection_get_setting_wireless_security(c); + if (s_wsec) { + key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec); + if (NM_IN_STRSET(key_mgmt, "ieee8021x", "none")) + return _("WEP encryption is known to be insecure"); + } + + return NULL; +} + +static NMMetaColor +_connection_to_color(NMConnection *c, NMActiveConnection *ac) +{ + if (ac) + return nmc_active_connection_state_to_color(ac); + + if (_connection_check_deprecated(c)) + return NM_META_COLOR_CONNECTION_DEPRECATED; + + return NM_META_COLOR_CONNECTION_UNKNOWN; +} + static gconstpointer _metagen_con_show_get_fcn(NMC_META_GENERIC_INFO_GET_FCN_ARGS) { @@ -523,7 +557,7 @@ _metagen_con_show_get_fcn(NMC_META_GENERIC_INFO_GET_FCN_ARGS) const char *s; char *s_mut; - NMC_HANDLE_COLOR(nmc_active_connection_state_to_color(ac)); + NMC_HANDLE_COLOR(_connection_to_color(c, ac)); if (c) s_con = nm_connection_get_setting_connection(c); @@ -1197,10 +1231,10 @@ usage_connection_delete(void) { g_printerr(_("Usage: nmcli connection delete { ARGUMENTS | help }\n" "\n" - "ARGUMENTS := [id | uuid | path] <ID>\n" + "ARGUMENTS := [id | uuid | path] <ID>, ...\n" "\n" - "Delete a connection profile.\n" - "The profile is identified by its name, UUID or D-Bus path.\n\n")); + "Delete connection profiles.\n" + "The profiles are identified by their name, UUID or D-Bus path.\n\n")); } static void @@ -1261,6 +1295,17 @@ usage_connection_export(void) } static void +usage_connection_migrate(void) +{ + g_printerr(_("Usage: nmcli connection migrate { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [--plugin <plugin>] [id | uuid | path] <ID>, ...\n" + "\n" + "Migrate connection profiles to a different settings plugin,\n" + "such as \"keyfile\" (default) or \"ifcfg-rh\".\n\n")); +} + +static void quit(void) { if (nm_clear_g_source(&progress_id)) @@ -1410,13 +1455,13 @@ nmc_connection_profile_details(NMConnection *connection, NmCli *nmc) TRUE, &prop_array, &error); - if (error) { + if (!print_settings_array) { + g_return_val_if_fail(error, FALSE); g_string_printf(nmc->return_text, _("Error: 'connection show': %s"), error->message); g_error_free(error); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return FALSE; } - g_assert(print_settings_array); /* Main header */ { @@ -1478,9 +1523,6 @@ nmc_active_connection_state_to_color(NMActiveConnection *ac) { NMActiveConnectionState state; - if (!ac) - return NM_META_COLOR_CONNECTION_UNKNOWN; - if (NM_FLAGS_HAS(nm_active_connection_get_state_flags(ac), NM_ACTIVATION_STATE_FLAG_EXTERNAL)) return NM_META_COLOR_CONNECTION_EXTERNAL; @@ -1520,13 +1562,13 @@ nmc_active_connection_details(NMActiveConnection *acon, NmCli *nmc) TRUE, &group_fields, &error); - if (error) { + if (!print_groups) { + g_return_val_if_fail(error, FALSE); g_string_printf(nmc->return_text, _("Error: 'connection show': %s"), error->message); g_error_free(error); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return FALSE; } - g_assert(print_groups); /* Main header */ { @@ -1881,6 +1923,7 @@ con_show_get_items_cmp(gconstpointer pa, gconstpointer pb, gpointer user_data) } } + NM_CMP_DIRECT(!!_connection_check_deprecated(c_a), !!_connection_check_deprecated(c_b)); NM_CMP_DIRECT_STRCMP0(nm_connection_get_uuid(c_a), nm_connection_get_uuid(c_b)); NM_CMP_DIRECT_STRCMP0(nm_connection_get_path(c_a), nm_connection_get_path(c_b)); } @@ -2150,7 +2193,7 @@ do_connections_show(const NMCCommand *cmd, NmCli *nmc, int argc, const char *con goto finish; break; default: - g_assert_not_reached(); + g_return_if_reached(); break; } } @@ -2470,7 +2513,7 @@ find_device_for_connection(NmCli *nmc, g_return_val_if_fail(error == NULL || *error == NULL, FALSE); s_con = nm_connection_get_setting_connection(connection); - g_assert(s_con); + g_return_val_if_fail(s_con, FALSE); con_type = nm_setting_connection_get_connection_type(s_con); if (nm_streq(con_type, NM_SETTING_VPN_SETTING_NAME)) { @@ -2959,7 +3002,7 @@ do_connection_up(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const gs_free char *line = NULL; /* nmc_do_cmd() should not call this with argc=0. */ - g_assert(!nmc->complete); + g_return_if_fail(!nmc->complete); line = nmc_readline(&nmc->nmc_config, PROMPT_CONNECTION); nmc_string_to_arg_array(line, NULL, TRUE, &arg_arr, &arg_num); @@ -3215,7 +3258,7 @@ do_connection_down(const NMCCommand *cmd, NmCli *nmc, int argc, const char *cons if (argc == 0) { /* nmc_do_cmd() should not call this with argc=0. */ - g_assert(!nmc->complete); + g_return_if_fail(!nmc->complete); if (nmc->ask) { gs_free char *line = NULL; @@ -3660,7 +3703,8 @@ is_setting_mandatory(NMConnection *connection, NMSetting *setting) guint i; s_con = nm_connection_get_setting_connection(connection); - g_assert(s_con); + g_return_val_if_fail(s_con, FALSE); + c_type = nm_setting_connection_get_connection_type(s_con); s_type = nm_setting_connection_get_slave_type(s_con); @@ -3739,7 +3783,7 @@ normalized_master_for_slave(const GPtrArray *connections, for (i = 0; i < connections->len; i++) { connection = NM_CONNECTION(connections->pdata[i]); s_con = nm_connection_get_setting_connection(connection); - g_assert(s_con); + g_return_val_if_fail(s_con, NULL); con_type = nm_setting_connection_get_connection_type(s_con); if (type && !nm_streq0(con_type, type)) continue; @@ -4243,7 +4287,7 @@ con_settings(NMConnection *connection, g_return_val_if_fail(slv_settings, FALSE); s_con = nm_connection_get_setting_connection(connection); - g_assert(s_con); + g_return_val_if_fail(s_con, FALSE); con_type = nm_setting_connection_get_slave_type(s_con); *slv_settings = nm_meta_setting_info_valid_parts_for_slave_type(con_type, NULL); @@ -5209,14 +5253,52 @@ nmc_process_connection_properties(NmCli *nmc, } static void +connection_warnings(NmCli *nmc, NMConnection *connection) +{ + const GPtrArray *connections; + guint i, found; + const char *id; + const char *deprecated; + + deprecated = _connection_check_deprecated(NM_CONNECTION(connection)); + if (deprecated) + g_printerr(_("Warning: %s.\n"), deprecated); + + connections = nm_client_get_connections(nmc->client); + if (!connections) + return; + + id = nm_connection_get_id(connection); + found = 0; + for (i = 0; i < connections->len; i++) { + NMConnection *candidate = NM_CONNECTION(connections->pdata[i]); + + if ((NMConnection *) connection == candidate) + continue; + if (nm_streq0(nm_connection_get_id(candidate), id)) + found++; + } + + if (found > 0) { + g_printerr(g_dngettext(GETTEXT_PACKAGE, + "Warning: There is another connection with the name '%1$s'. " + "Reference the connection by its uuid '%2$s'\n", + "Warning: There are %3$u other connections with the name " + "'%1$s'. Reference the connection by its uuid '%2$s'\n", + found), + id, + nm_connection_get_uuid(NM_CONNECTION(connection)), + found); + } +} + +static void add_connection_cb(GObject *client, GAsyncResult *result, gpointer user_data) { nm_auto_free_add_connection_info AddConnectionInfo *info = user_data; NmCli *nmc = info->nmc; NMRemoteConnection *connection; GError *error = NULL; - const GPtrArray *connections; - guint i, found; connection = nm_client_add_connection2_finish(NM_CLIENT(client), result, NULL, &error); if (error) { @@ -5227,29 +5309,7 @@ add_connection_cb(GObject *client, GAsyncResult *result, gpointer user_data) g_error_free(error); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; } else { - connections = nm_client_get_connections(nmc->client); - if (connections) { - found = 0; - for (i = 0; i < connections->len; i++) { - NMConnection *candidate = NM_CONNECTION(connections->pdata[i]); - - if ((NMConnection *) connection == candidate) - continue; - if (nm_streq0(nm_connection_get_id(candidate), info->new_id)) - found++; - } - if (found > 0) { - g_printerr(g_dngettext(GETTEXT_PACKAGE, - "Warning: There is another connection with the name '%1$s'. " - "Reference the connection by its uuid '%2$s'\n", - "Warning: There are %3$u other connections with the name " - "'%1$s'. Reference the connection by its uuid '%2$s'\n", - found), - info->new_id, - nm_connection_get_uuid(NM_CONNECTION(connection)), - found); - } - } + connection_warnings(nmc, NM_CONNECTION(connection)); /* We print here human readable text, but as scripts might parse this output * (with LANG=C), this is important to not change in the future. At least @@ -7252,7 +7312,8 @@ editor_show_status_line(NMConnection *connection, gboolean dirty, gboolean temp) const char *con_type, *con_id, *con_uuid; s_con = nm_connection_get_setting_connection(connection); - g_assert(s_con); + g_return_if_fail(s_con); + con_type = nm_setting_connection_get_connection_type(s_con); con_id = nm_connection_get_id(connection); con_uuid = nm_connection_get_uuid(connection); @@ -7595,15 +7656,18 @@ confirm_connection_saving(const NmcConfig *nmc_config, NMConnection *local, NMCo gboolean confirmed = TRUE; s_con_loc = nm_connection_get_setting_connection(local); - g_assert(s_con_loc); + g_return_val_if_fail(s_con_loc, FALSE); + ac_local = nm_setting_connection_get_autoconnect(s_con_loc); if (remote) { s_con_rem = nm_connection_get_setting_connection(remote); - g_assert(s_con_rem); + g_return_val_if_fail(s_con_rem, FALSE); + ac_remote = nm_setting_connection_get_autoconnect(s_con_rem); - } else + } else { ac_remote = FALSE; + } if (ac_local && !ac_remote) { gs_free char *answer = NULL; @@ -8280,9 +8344,12 @@ editor_menu_main(NmCli *nmc, NMConnection *connection, const char *connection_ty connection_changed = FALSE; } - source = g_timeout_source_new(10 * NM_UTILS_MSEC_PER_SEC); - g_source_set_callback(source, editor_save_timeout, &timeout, NULL); - g_source_attach(source, g_main_loop_get_context(loop)); + source = nm_g_source_attach(nm_g_timeout_source_new(10 * NM_UTILS_MSEC_PER_SEC, + G_PRIORITY_DEFAULT, + editor_save_timeout, + &timeout, + NULL), + g_main_loop_get_context(loop)); while (!nmc_editor_cb_called && !timeout) g_main_context_iteration(NULL, TRUE); @@ -8527,7 +8594,8 @@ editor_init_new_connection(NmCli *nmc, NMConnection *connection, const char *sla const char *con_type; s_con = nm_connection_get_setting_connection(connection); - g_assert(s_con); + g_return_if_fail(s_con); + con_type = nm_setting_connection_get_connection_type(s_con); /* Initialize new connection according to its type using sensible defaults. */ @@ -8851,6 +8919,8 @@ modify_connection_cb(GObject *connection, GAsyncResult *result, gpointer user_da error->message); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; } else { + connection_warnings(nmc, NM_CONNECTION(connection)); + if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY) { g_print(_("Connection '%s' (%s) successfully modified.\n"), nm_connection_get_id(NM_CONNECTION(connection)), @@ -8955,7 +9025,7 @@ do_connection_clone(const NMCCommand *cmd, NmCli *nmc, int argc, const char *con gs_free char *line = NULL; /* nmc_do_cmd() should not call this with argc=0. */ - g_assert(!nmc->complete); + g_return_if_fail(!nmc->complete); line = nmc_readline(&nmc->nmc_config, PROMPT_CONNECTION); nmc_string_to_arg_array(line, NULL, TRUE, &arg_arr, &arg_num); @@ -9054,7 +9124,7 @@ do_connection_delete(const NMCCommand *cmd, NmCli *nmc, int argc, const char *co gs_free char *line = NULL; /* nmc_do_cmd() should not call this with argc=0. */ - g_assert(!nmc->complete); + g_return_if_fail(!nmc->complete); line = nmc_readline(&nmc->nmc_config, PROMPT_CONNECTIONS); nmc_string_to_arg_array(line, NULL, TRUE, &arg_arr, &arg_num); @@ -9079,8 +9149,11 @@ do_connection_delete(const NMCCommand *cmd, NmCli *nmc, int argc, const char *co nmc->return_value = error->code; g_clear_error(&error); - if (nmc->return_value != NMC_RESULT_ERROR_NOT_FOUND) + if (nmc->return_value != NMC_RESULT_ERROR_NOT_FOUND) { + g_string_free(invalid_cons, TRUE); + invalid_cons = NULL; goto finish; + } if (!invalid_cons) invalid_cons = g_string_new(NULL); @@ -9133,7 +9206,6 @@ finish: g_string_printf(nmc->return_text, _("Error: cannot delete unknown connection(s): %s."), invalid_cons->str); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; } } @@ -9192,7 +9264,7 @@ do_connection_monitor(const NMCCommand *cmd, NmCli *nmc, int argc, const char *c /* No connections specified. Monitor all. */ /* nmc_do_cmd() should not call this with argc=0. */ - g_assert(!nmc->complete); + g_return_if_fail(!nmc->complete); connections = nm_client_get_connections(nmc->client); } else { @@ -9349,7 +9421,7 @@ do_connection_import(const NMCCommand *cmd, NmCli *nmc, int argc, const char *co if (argc == 0) { /* nmc_do_cmd() should not call this with argc=0. */ - g_assert(!nmc->complete); + g_return_if_fail(!nmc->complete); if (nmc->ask) { type_ask = @@ -9492,7 +9564,7 @@ do_connection_export(const NMCCommand *cmd, NmCli *nmc, int argc, const char *co gs_free char *line = NULL; /* nmc_do_cmd() should not call this with argc=0. */ - g_assert(!nmc->complete); + g_return_if_fail(!nmc->complete); line = nmc_readline(&nmc->nmc_config, PROMPT_VPN_CONNECTION); nmc_string_to_arg_array(line, NULL, TRUE, &arg_arr, &arg_num); @@ -9586,6 +9658,154 @@ finish: unlink(path); } +static void +migrate_cb(GObject *obj, GAsyncResult *result, gpointer user_data) +{ + ConnectionCbInfo *info = (ConnectionCbInfo *) user_data; + NMConnection *connection = NM_CONNECTION(obj); + gs_unref_variant GVariant *res = NULL; + GError *error = NULL; + + res = nm_remote_connection_update2_finish(NM_REMOTE_CONNECTION(obj), result, &error); + if (!res) { + g_string_printf(info->nmc->return_text, _("Error: not all connections migrated.")); + g_printerr(_("Error: Connection migration failed: %s\n"), error->message); + g_error_free(error); + info->nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + } else { + g_print(_("Connection '%s' (%s) successfully migrated.\n"), + nm_connection_get_id(connection), + nm_connection_get_uuid(connection)); + } + connection_cb_info_finish(info, obj); +} + +static void +do_connection_migrate(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const *argv) +{ + NMConnection *connection; + ConnectionCbInfo *info = NULL; + gs_strfreev char **arg_arr = NULL; + const char *const *arg_ptr; + guint i; + int arg_num; + nm_auto_free_gstring GString *invalid_cons = NULL; + gs_unref_ptrarray GPtrArray *found_cons = NULL; + GError *error = NULL; + const char *plugin = "keyfile"; + const GPtrArray *connections = NULL; + int option; + + if (nmc->timeout == -1) + nmc->timeout = 10; + + while ((option = next_arg(nmc, &argc, &argv, "--plugin", NULL)) > 0) { + switch (option) { + case 1: /* --plugin */ + argc--; + argv++; + if (!argc) { + g_set_error_literal(&error, NMCLI_ERROR, 0, _("'--plugin' argument is missing")); + goto finish; + } + plugin = *argv; + break; + default: + g_return_if_reached(); + break; + } + } + + arg_ptr = argv; + arg_num = argc; + if (argc == 0) { + if (nmc->ask) { + gs_free char *line = NULL; + + /* nmc_do_cmd() should not call this with argc=0. */ + g_return_if_fail(!nmc->complete); + + line = nmc_readline(&nmc->nmc_config, PROMPT_CONNECTIONS); + nmc_string_to_arg_array(line, NULL, TRUE, &arg_arr, &arg_num); + arg_ptr = (const char *const *) arg_arr; + } + } + + while (arg_num > 0) { + const char *cur_selector, *cur_value; + + connection = + get_connection(nmc, &arg_num, &arg_ptr, &cur_selector, &cur_value, &found_cons, &error); + if (!connection) { + if (!nmc->complete) + g_printerr(_("Error: %s.\n"), error->message); + g_string_printf(nmc->return_text, _("Error: not all connections found.")); + nmc->return_value = error->code; + g_clear_error(&error); + + if (nmc->return_value != NMC_RESULT_ERROR_NOT_FOUND) { + g_string_free(invalid_cons, TRUE); + invalid_cons = NULL; + goto finish; + } + + if (!invalid_cons) + invalid_cons = g_string_new(NULL); + if (cur_selector) + g_string_append_printf(invalid_cons, "%s '%s', ", cur_selector, cur_value); + else + g_string_append_printf(invalid_cons, "'%s', ", cur_value); + } + } + + if (nmc->complete) + goto finish; + + if (invalid_cons) + goto finish; + + if (!found_cons) { + /* No connections specified explicitly? Fine, add all. */ + found_cons = g_ptr_array_new(); + connections = nm_client_get_connections(nmc->client); + for (i = 0; i < connections->len; i++) { + connection = connections->pdata[i]; + g_ptr_array_add(found_cons, connection); + } + } + + info = g_slice_new0(ConnectionCbInfo); + info->nmc = nmc; + info->obj_list = g_ptr_array_sized_new(found_cons->len); + for (i = 0; i < found_cons->len; i++) { + connection = found_cons->pdata[i]; + g_ptr_array_add(info->obj_list, g_object_ref(connection)); + } + info->timeout_id = g_timeout_add_seconds(nmc->timeout, connection_op_timeout_cb, info); + info->cancellable = g_cancellable_new(); + + nmc->nowait_flag = (nmc->timeout == 0); + nmc->should_wait++; + + for (i = 0; i < found_cons->len; i++) { + nm_remote_connection_update2(NM_REMOTE_CONNECTION(found_cons->pdata[i]), + NULL, + 0, + g_variant_new_parsed("{'plugin': <%s>}", plugin), + info->cancellable, + migrate_cb, + info); + } + +finish: + if (invalid_cons) { + g_string_truncate(invalid_cons, invalid_cons->len - 2); /* truncate trailing ", " */ + g_string_printf(nmc->return_text, + _("Error: cannot migrate unknown connection(s): %s."), + invalid_cons->str); + } +} + static char * gen_func_connection_names(const char *text, int state) { @@ -9693,6 +9913,7 @@ nmc_command_func_connection(const NMCCommand *cmd, NmCli *nmc, int argc, const c {"clone", do_connection_clone, usage_connection_clone, TRUE, TRUE}, {"import", do_connection_import, usage_connection_import, TRUE, TRUE}, {"export", do_connection_export, usage_connection_export, TRUE, TRUE}, + {"migrate", do_connection_migrate, usage_connection_migrate, TRUE, TRUE}, {"monitor", do_connection_monitor, usage_connection_monitor, TRUE, TRUE}, {NULL, do_connections_show, usage, TRUE, TRUE}, }; diff --git a/src/nmcli/devices.c b/src/nmcli/devices.c index e23ef681..2bfc4cec 100644 --- a/src/nmcli/devices.c +++ b/src/nmcli/devices.c @@ -1216,12 +1216,30 @@ get_device(NmCli *nmc, int *argc, const char *const **argv, GError **error) return devices[i]; } +static bool +_ap_is_wep(NMAccessPoint *ap) +{ + NM80211ApFlags flags = nm_access_point_get_flags(ap); + NM80211ApSecurityFlags wpa_flags = nm_access_point_get_wpa_flags(ap); + NM80211ApSecurityFlags rsn_flags = nm_access_point_get_rsn_flags(ap); + + if ((flags & NM_802_11_AP_FLAGS_PRIVACY) && (wpa_flags == NM_802_11_AP_SEC_NONE) + && (rsn_flags == NM_802_11_AP_SEC_NONE)) { + return TRUE; + } + + return FALSE; +} + static int compare_aps(gconstpointer a, gconstpointer b, gpointer user_data) { NMAccessPoint *apa = *(NMAccessPoint **) a; NMAccessPoint *apb = *(NMAccessPoint **) b; + /* Sort the deprecated WEP connections last. */ + NM_CMP_DIRECT(_ap_is_wep(apb), _ap_is_wep(apa)); + NM_CMP_DIRECT(nm_access_point_get_strength(apb), nm_access_point_get_strength(apa)); NM_CMP_DIRECT(nm_access_point_get_frequency(apa), nm_access_point_get_frequency(apb)); NM_CMP_DIRECT(nm_access_point_get_max_bitrate(apb), nm_access_point_get_max_bitrate(apa)); @@ -1249,22 +1267,19 @@ sort_access_points(const GPtrArray *aps) } typedef struct { - NmCli *nmc; - int index; - guint32 output_flags; - const char *active_bssid; - const char *device; - GPtrArray *output_data; + NmCli *nmc; + NMAccessPoint *active_ap; + const char *device; + GPtrArray *output_data; + int *p_index; + guint32 output_flags; } APInfo; static void -fill_output_access_point(gpointer data, gpointer user_data) +fill_output_access_point(NMAccessPoint *ap, const APInfo *info) { - NMAccessPoint *ap = NM_ACCESS_POINT(data); - APInfo *info = (APInfo *) user_data; NmcOutputField *arr; - gboolean active = FALSE; - NM80211ApFlags flags; + gboolean active; NM80211ApSecurityFlags wpa_flags, rsn_flags; guint32 freq, bitrate; guint8 strength; @@ -1284,14 +1299,9 @@ fill_output_access_point(gpointer data, gpointer user_data) const char *sig_bars; NMMetaColor color; - if (info->active_bssid) { - const char *current_bssid = nm_access_point_get_bssid(ap); - if (current_bssid && !strcmp(current_bssid, info->active_bssid)) - active = TRUE; - } + active = (info->active_ap == ap); /* Get AP properties */ - flags = nm_access_point_get_flags(ap); wpa_flags = nm_access_point_get_wpa_flags(ap); rsn_flags = nm_access_point_get_rsn_flags(ap); ssid = nm_access_point_get_ssid(ap); @@ -1320,26 +1330,27 @@ fill_output_access_point(gpointer data, gpointer user_data) security_str = g_string_new(NULL); - if ((flags & NM_802_11_AP_FLAGS_PRIVACY) && (wpa_flags == NM_802_11_AP_SEC_NONE) - && (rsn_flags == NM_802_11_AP_SEC_NONE)) { + if (_ap_is_wep(ap)) { g_string_append(security_str, "WEP "); - } - if (wpa_flags != NM_802_11_AP_SEC_NONE) { - g_string_append(security_str, "WPA1 "); - } - if ((rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) - || (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { - g_string_append(security_str, "WPA2 "); - } - if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE) { - g_string_append(security_str, "WPA3 "); - } - if (NM_FLAGS_ANY(rsn_flags, NM_802_11_AP_SEC_KEY_MGMT_OWE | NM_802_11_AP_SEC_KEY_MGMT_OWE_TM)) { - g_string_append(security_str, "OWE "); - } - if ((wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) - || (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { - g_string_append(security_str, "802.1X "); + } else { + if (wpa_flags != NM_802_11_AP_SEC_NONE) { + g_string_append(security_str, "WPA1 "); + } + if ((rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) + || (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + g_string_append(security_str, "WPA2 "); + } + if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE) { + g_string_append(security_str, "WPA3 "); + } + if (NM_FLAGS_ANY(rsn_flags, + NM_802_11_AP_SEC_KEY_MGMT_OWE | NM_802_11_AP_SEC_KEY_MGMT_OWE_TM)) { + g_string_append(security_str, "OWE "); + } + if ((wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + || (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + g_string_append(security_str, "802.1X "); + } } if (security_str->len > 0) @@ -1348,7 +1359,7 @@ fill_output_access_point(gpointer data, gpointer user_data) arr = nmc_dup_fields_array((const NMMetaAbstractInfo *const *) nmc_fields_dev_wifi_list, info->output_flags); - ap_name = g_strdup_printf("AP[%d]", info->index++); /* AP */ + ap_name = g_strdup_printf("AP[%d]", ++(*info->p_index)); /* AP */ set_val_str(arr, 0, ap_name); set_val_str(arr, 1, ssid_str); set_val_str(arr, 2, ssid_hex_str); @@ -1374,6 +1385,8 @@ fill_output_access_point(gpointer data, gpointer user_data) /* Set colors */ color = wifi_signal_to_color(strength); + if (_ap_is_wep(ap)) + color = NM_META_COLOR_WIFI_DEPRECATED; set_val_color_all(arr, color); if (active) arr[15].color = NM_META_COLOR_CONNECTION_ACTIVATED; @@ -1381,6 +1394,12 @@ fill_output_access_point(gpointer data, gpointer user_data) g_ptr_array_add(info->output_data, arr); } +static void +fill_output_access_point_void(gpointer data, gpointer user_data) +{ + fill_output_access_point(data, user_data); +} + static char * bluetooth_caps_to_string(NMBluetoothCapabilities caps) { @@ -1679,18 +1698,14 @@ show_device_info(NMDevice *device, NmCli *nmc) /* Wireless specific information */ if ((NM_IS_DEVICE_WIFI(device))) { - NMAccessPoint *active_ap = NULL; - const char *active_bssid = NULL; - /* section AP */ if (!g_ascii_strcasecmp(nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[4]->name)) { + NMAccessPoint *active_ap = NULL; NMC_OUTPUT_DATA_DEFINE_SCOPED(out); - if (state == NM_DEVICE_STATE_ACTIVATED) { - active_ap = nm_device_wifi_get_active_access_point(NM_DEVICE_WIFI(device)); - active_bssid = active_ap ? nm_access_point_get_bssid(active_ap) : NULL; - } + if (state == NM_DEVICE_STATE_ACTIVATED) + active_ap = nm_device_wifi_get_active_access_point(NM_DEVICE_WIFI(device)); tmpl = (const NMMetaAbstractInfo *const *) nmc_fields_dev_wifi_list; out_indices = @@ -1703,19 +1718,20 @@ show_device_info(NMDevice *device, NmCli *nmc) g_ptr_array_add(out.output_data, arr); { - gs_unref_ptrarray GPtrArray *aps = NULL; - APInfo info = { + gs_unref_ptrarray GPtrArray *aps = NULL; + int info_index = 0; + const APInfo info = { .nmc = nmc, - .index = 1, + .p_index = &info_index, .output_flags = NMC_OF_FLAG_SECTION_PREFIX, - .active_bssid = active_bssid, + .active_ap = active_ap, .device = nm_device_get_iface(device), .output_data = out.output_data, }; aps = sort_access_points( nm_device_wifi_get_access_points(NM_DEVICE_WIFI(device))); - g_ptr_array_foreach(aps, fill_output_access_point, &info); + g_ptr_array_foreach(aps, fill_output_access_point_void, (gpointer) &info); } print_data_prepare_width(out.output_data); @@ -2991,32 +3007,30 @@ find_ap_on_device(NMDevice *device, const char *bssid, const char *ssid, gboolea static void show_access_point_info(NMDeviceWifi *wifi, NmCli *nmc, NmcOutputData *out) { - NMAccessPoint *active_ap = NULL; - const char *active_bssid = NULL; + NMAccessPoint *active_ap = NULL; NmcOutputField *arr; - if (nm_device_get_state(NM_DEVICE(wifi)) == NM_DEVICE_STATE_ACTIVATED) { - active_ap = nm_device_wifi_get_active_access_point(wifi); - active_bssid = active_ap ? nm_access_point_get_bssid(active_ap) : NULL; - } + if (nm_device_get_state(NM_DEVICE(wifi)) == NM_DEVICE_STATE_ACTIVATED) + active_ap = nm_device_wifi_get_active_access_point(wifi); arr = nmc_dup_fields_array((const NMMetaAbstractInfo *const *) nmc_fields_dev_wifi_list, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); g_ptr_array_add(out->output_data, arr); { - gs_unref_ptrarray GPtrArray *aps = NULL; - APInfo info = { + gs_unref_ptrarray GPtrArray *aps = NULL; + int info_index = 0; + const APInfo info = { .nmc = nmc, - .index = 1, + .p_index = &info_index, .output_flags = 0, - .active_bssid = active_bssid, + .active_ap = active_ap, .device = nm_device_get_iface(NM_DEVICE(wifi)), .output_data = out->output_data, }; aps = sort_access_points(nm_device_wifi_get_access_points(wifi)); - g_ptr_array_foreach(aps, fill_output_access_point, &info); + g_ptr_array_foreach(aps, fill_output_access_point_void, (gpointer) &info); } print_data_prepare_width(out->output_data); @@ -3032,7 +3046,6 @@ wifi_print_aps(NMDeviceWifi *wifi, { NMAccessPoint *ap = NULL; const GPtrArray *aps; - APInfo *info; guint i; NmcOutputField *arr; const char *base_hdr = _("Wi-Fi scan list"); @@ -3061,23 +3074,23 @@ wifi_print_aps(NMDeviceWifi *wifi, ap = candidate_ap; } if (ap) { + int info_index = 0; + const APInfo info = { + .nmc = nmc, + .p_index = &info_index, + .output_flags = 0, + .device = nm_device_get_iface(NM_DEVICE(wifi)), + .output_data = out.output_data, + }; + /* Add headers (field names) */ arr = nmc_dup_fields_array(tmpl, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); g_ptr_array_add(out.output_data, arr); - info = g_malloc0(sizeof(APInfo)); - info->nmc = nmc; - info->index = 1; - info->output_flags = 0; - info->active_bssid = NULL; - info->device = nm_device_get_iface(NM_DEVICE(wifi)); - info->output_data = out.output_data; - - fill_output_access_point(ap, info); + fill_output_access_point(ap, &info); print_data_prepare_width(out.output_data); print_data(&nmc->nmc_config, &nmc->pager_data, out_indices, header_name, 0, &out); - g_free(info); *bssid_found = TRUE; empty_line = TRUE; diff --git a/src/nmcli/general.c b/src/nmcli/general.c index 65437e79..d5a6788f 100644 --- a/src/nmcli/general.c +++ b/src/nmcli/general.c @@ -145,12 +145,22 @@ _metagen_general_status_get_fcn(NMC_META_GENERIC_INFO_GET_FCN_ARGS) v_bool = nm_client_networking_get_enabled(nmc->client); goto enabled_out; case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_WIFI_HW: + if (!(nm_client_get_radio_flags(nmc->client) & NM_RADIO_FLAG_WLAN_AVAILABLE)) { + NMC_HANDLE_COLOR(NM_META_COLOR_NONE); + value = N_("missing"); + goto translate_and_out; + } v_bool = nm_client_wireless_hardware_get_enabled(nmc->client); goto enabled_out; case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_WIFI: v_bool = nm_client_wireless_get_enabled(nmc->client); goto enabled_out; case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_WWAN_HW: + if (!(nm_client_get_radio_flags(nmc->client) & NM_RADIO_FLAG_WWAN_AVAILABLE)) { + NMC_HANDLE_COLOR(NM_META_COLOR_NONE); + value = N_("missing"); + goto translate_and_out; + } v_bool = nm_client_wwan_hardware_get_enabled(nmc->client); goto enabled_out; case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_WWAN: diff --git a/src/nmcli/generate-docs-nm-settings-nmcli.xml b/src/nmcli/generate-docs-nm-settings-nmcli.xml index 1d329305..525b36b6 100644 --- a/src/nmcli/generate-docs-nm-settings-nmcli.xml +++ b/src/nmcli/generate-docs-nm-settings-nmcli.xml @@ -371,7 +371,7 @@ <property name="uuid" description="A universally unique identifier for the connection, for example generated with libuuid. It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network. For example, it should not be changed when the "id" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or "type" property changes. The UUID must be in the format "2815492f-7e56-435e-b2e9-246bd7cdc664" (ie, contains only hexadecimal characters and "-")." /> <property name="stable-id" - description="This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret-key. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are "${CONNECTION}", "${DEVICE}", "${MAC}", "${BOOT}", "${RANDOM}". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that "${DEVICE}" corresponds to the interface name of the device and "${MAC}" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as "$$". For example, set it to "${CONNECTION}-${BOOT}-${DEVICE}" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to "${CONNECTION}" and uses a unique, fixed ID for the connection." /> + description="This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual about the secret-key and the host identity. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are "${CONNECTION}", "${DEVICE}", "${MAC}", "${BOOT}", "${RANDOM}". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that "${DEVICE}" corresponds to the interface name of the device and "${MAC}" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as "$$". For example, set it to "${CONNECTION}-${BOOT}-${DEVICE}" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to "${CONNECTION}" and uses a unique, fixed ID for the connection." /> <property name="type" alias="type" description="Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, "802-3-ethernet" or "802-11-wireless" or "bluetooth", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, "vpn" or "bridge", etc)." /> @@ -380,7 +380,7 @@ description="The name of the network interface this connection is bound to. If not set, then the connection can be attached to any interface of the appropriate type (subject to restrictions imposed by other settings). For software devices this specifies the name of the created device. For connection types where interface names cannot easily be made persistent (e.g. mobile broadband or USB Ethernet), this property should not be used. Setting this property restricts the interfaces a connection can be used with, and if interface names change or are reordered the connection may be applied to the wrong interface." /> <property name="autoconnect" alias="autoconnect" - description="Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See "secondaries" as an alternative to automatically connect VPN profiles." /> + description="Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See "secondaries" as an alternative to automatically connect VPN profiles. If multiple profiles are ready to autoconnect on the same device, the one with the better "connection.autoconnect-priority" is chosen. If the priorities are equal, then the most recently connected profile is activated. If the profiles were not connected earlier or their "connection.timestamp" is identical, the choice is undefined. Depending on "connection.multi-connect", a profile can (auto)connect only once at a time or multiple times." /> <property name="autoconnect-priority" description="The autoconnect priority in range -999 to 999. If the connection is set to autoconnect, connections with higher priority will be preferred. The higher number means higher priority. Defaults to 0. Note that this property only matters if there are more than one candidate profile to select for autoconnect. In case of equal priority, the profile used most recently is chosen." /> <property name="autoconnect-retries" @@ -712,7 +712,7 @@ description="DNS servers priority. The relative priority for DNS servers specified by this setting. A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections. It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the "rotate" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain. Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually). When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins. If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured." /> <property name="addresses" alias="ip6" - description="A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example "2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64". The addresses are listed in increasing priority, meaning the last address will be the primary address." /> + description="A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example "2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64". The addresses are listed in decreasing priority, meaning the first address will be the primary address. This can make a difference with IPv6 source address selection (RFC 6724, section 5)." /> <property name="gateway" alias="gw6" description="The gateway associated with this configuration. This is only meaningful if "addresses" is also set. The gateway's main purpose is to control the next hop of the standard default route on the device. Hence, the gateway property conflicts with "never-default" and will be automatically dropped if the IP configuration is set to never-default. As an alternative to set the gateway, configure a static default route with /0 as prefix length." /> @@ -911,7 +911,7 @@ description="PAC URL for obtaining PAC file." /> <property name="pac-script" alias="pac-script" - description="PAC script for the connection." /> + description="The PAC script. In the profile this must be an UTF-8 encoded javascript code that defines a FindProxyForURL() function. When setting the property in nmcli, a filename is accepted too. In that case, nmcli will read the content of the file and set the script. The prefixes "file://" and "js://" are supported to explicitly differentiate between the two." /> </setting> <setting name="serial" > <property name="baud" diff --git a/src/nmcli/generate-docs-nm-settings-nmcli.xml.in b/src/nmcli/generate-docs-nm-settings-nmcli.xml.in index 1d329305..525b36b6 100644 --- a/src/nmcli/generate-docs-nm-settings-nmcli.xml.in +++ b/src/nmcli/generate-docs-nm-settings-nmcli.xml.in @@ -371,7 +371,7 @@ <property name="uuid" description="A universally unique identifier for the connection, for example generated with libuuid. It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network. For example, it should not be changed when the "id" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or "type" property changes. The UUID must be in the format "2815492f-7e56-435e-b2e9-246bd7cdc664" (ie, contains only hexadecimal characters and "-")." /> <property name="stable-id" - description="This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret-key. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are "${CONNECTION}", "${DEVICE}", "${MAC}", "${BOOT}", "${RANDOM}". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that "${DEVICE}" corresponds to the interface name of the device and "${MAC}" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as "$$". For example, set it to "${CONNECTION}-${BOOT}-${DEVICE}" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to "${CONNECTION}" and uses a unique, fixed ID for the connection." /> + description="This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual about the secret-key and the host identity. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are "${CONNECTION}", "${DEVICE}", "${MAC}", "${BOOT}", "${RANDOM}". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that "${DEVICE}" corresponds to the interface name of the device and "${MAC}" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as "$$". For example, set it to "${CONNECTION}-${BOOT}-${DEVICE}" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to "${CONNECTION}" and uses a unique, fixed ID for the connection." /> <property name="type" alias="type" description="Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, "802-3-ethernet" or "802-11-wireless" or "bluetooth", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, "vpn" or "bridge", etc)." /> @@ -380,7 +380,7 @@ description="The name of the network interface this connection is bound to. If not set, then the connection can be attached to any interface of the appropriate type (subject to restrictions imposed by other settings). For software devices this specifies the name of the created device. For connection types where interface names cannot easily be made persistent (e.g. mobile broadband or USB Ethernet), this property should not be used. Setting this property restricts the interfaces a connection can be used with, and if interface names change or are reordered the connection may be applied to the wrong interface." /> <property name="autoconnect" alias="autoconnect" - description="Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See "secondaries" as an alternative to automatically connect VPN profiles." /> + description="Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See "secondaries" as an alternative to automatically connect VPN profiles. If multiple profiles are ready to autoconnect on the same device, the one with the better "connection.autoconnect-priority" is chosen. If the priorities are equal, then the most recently connected profile is activated. If the profiles were not connected earlier or their "connection.timestamp" is identical, the choice is undefined. Depending on "connection.multi-connect", a profile can (auto)connect only once at a time or multiple times." /> <property name="autoconnect-priority" description="The autoconnect priority in range -999 to 999. If the connection is set to autoconnect, connections with higher priority will be preferred. The higher number means higher priority. Defaults to 0. Note that this property only matters if there are more than one candidate profile to select for autoconnect. In case of equal priority, the profile used most recently is chosen." /> <property name="autoconnect-retries" @@ -712,7 +712,7 @@ description="DNS servers priority. The relative priority for DNS servers specified by this setting. A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections. It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the "rotate" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain. Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually). When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins. If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured." /> <property name="addresses" alias="ip6" - description="A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example "2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64". The addresses are listed in increasing priority, meaning the last address will be the primary address." /> + description="A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example "2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64". The addresses are listed in decreasing priority, meaning the first address will be the primary address. This can make a difference with IPv6 source address selection (RFC 6724, section 5)." /> <property name="gateway" alias="gw6" description="The gateway associated with this configuration. This is only meaningful if "addresses" is also set. The gateway's main purpose is to control the next hop of the standard default route on the device. Hence, the gateway property conflicts with "never-default" and will be automatically dropped if the IP configuration is set to never-default. As an alternative to set the gateway, configure a static default route with /0 as prefix length." /> @@ -911,7 +911,7 @@ description="PAC URL for obtaining PAC file." /> <property name="pac-script" alias="pac-script" - description="PAC script for the connection." /> + description="The PAC script. In the profile this must be an UTF-8 encoded javascript code that defines a FindProxyForURL() function. When setting the property in nmcli, a filename is accepted too. In that case, nmcli will read the content of the file and set the script. The prefixes "file://" and "js://" are supported to explicitly differentiate between the two." /> </setting> <setting name="serial" > <property name="baud" diff --git a/src/nmcli/nmcli.c b/src/nmcli/nmcli.c index fae40584..96b8ec4a 100644 --- a/src/nmcli/nmcli.c +++ b/src/nmcli/nmcli.c @@ -11,7 +11,6 @@ #include <stdio.h> #include <stdlib.h> #include <signal.h> -#include <termios.h> #include <unistd.h> #include <locale.h> #if HAVE_EDITLINE_READLINE @@ -44,6 +43,7 @@ [NM_META_COLOR_CONNECTION_DISCONNECTING] = "31", \ [NM_META_COLOR_CONNECTION_INVISIBLE] = "2", \ [NM_META_COLOR_CONNECTION_EXTERNAL] = "32;2", \ + [NM_META_COLOR_CONNECTION_DEPRECATED] = "2", \ [NM_META_COLOR_CONNECTIVITY_FULL] = "32", \ [NM_META_COLOR_CONNECTIVITY_LIMITED] = "33", \ [NM_META_COLOR_CONNECTIVITY_NONE] = "31", \ @@ -74,6 +74,7 @@ [NM_META_COLOR_WIFI_SIGNAL_GOOD] = "33", \ [NM_META_COLOR_WIFI_SIGNAL_POOR] = "36", \ [NM_META_COLOR_WIFI_SIGNAL_UNKNOWN] = "2", \ + [NM_META_COLOR_WIFI_DEPRECATED] = "2", \ [NM_META_COLOR_ENABLED] = "32", \ [NM_META_COLOR_DISABLED] = "31", \ }, \ @@ -118,8 +119,7 @@ typedef struct { } ArgsInfo; /* --- Global variables --- */ -GMainLoop *loop = NULL; -struct termios termios_orig; +GMainLoop *loop = NULL; NM_CACHED_QUARK_FCN("nmcli-error-quark", nmcli_error_quark); @@ -551,6 +551,7 @@ static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE( {"connection-external", NM_META_COLOR_CONNECTION_EXTERNAL}, {"connection-invisible", NM_META_COLOR_CONNECTION_INVISIBLE}, {"connection-unknown", NM_META_COLOR_CONNECTION_UNKNOWN}, + {"connection-deprecated", NM_META_COLOR_CONNECTION_DEPRECATED}, {"connectivity-full", NM_META_COLOR_CONNECTIVITY_FULL}, {"connectivity-limited", NM_META_COLOR_CONNECTIVITY_LIMITED}, {"connectivity-none", NM_META_COLOR_CONNECTIVITY_NONE}, @@ -587,7 +588,8 @@ static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE( {"wifi-signal-fair", NM_META_COLOR_WIFI_SIGNAL_FAIR}, {"wifi-signal-good", NM_META_COLOR_WIFI_SIGNAL_GOOD}, {"wifi-signal-poor", NM_META_COLOR_WIFI_SIGNAL_POOR}, - {"wifi-signal-unknown", NM_META_COLOR_WIFI_SIGNAL_UNKNOWN}, ); + {"wifi-signal-unknown", NM_META_COLOR_WIFI_SIGNAL_UNKNOWN}, + {"wifi-deprecated", NM_META_COLOR_WIFI_DEPRECATED}, ); static gboolean parse_color_scheme(char *palette_buffer, NmcColorPalette *out_palette, GError **error) @@ -935,7 +937,6 @@ nmc_clear_sigint(void) void nmc_exit(void) { - tcsetattr(STDIN_FILENO, TCSADRAIN, &termios_orig); nmc_cleanup_readline(); exit(1); } @@ -1026,9 +1027,6 @@ main(int argc, char *argv[]) textdomain(GETTEXT_PACKAGE); #endif - /* Save terminal settings */ - tcgetattr(STDIN_FILENO, &termios_orig); - nm_cli.return_text = g_string_new(_("Success")); loop = g_main_loop_new(NULL, FALSE); diff --git a/src/nmcli/utils.c b/src/nmcli/utils.c index 7645a08c..209a5b69 100644 --- a/src/nmcli/utils.c +++ b/src/nmcli/utils.c @@ -156,7 +156,7 @@ next_arg(NmCli *nmc, int *argc, const char *const **argv, ...) va_list args; const char *cmd_option; - g_assert(*argc >= 0); + g_return_val_if_fail(*argc >= 0, -1); do { int cmd_option_pos = 1; @@ -1229,7 +1229,7 @@ _print_do(const NmcConfig *nmc_config, guint i_row, i_col; nm_auto_free_gstring GString *str = NULL; - g_assert(col_len); + g_return_if_fail(col_len); /* Main header */ if (nmc_config->print_output == NMC_PRINT_PRETTY && header_name_no_l10n) { @@ -1628,7 +1628,7 @@ print_required_fields(const NmcConfig *nmc_config, gboolean is_array = field_values[idx].value_is_array; /* section prefix can't be an array */ - g_assert(!is_array || !section_prefix || idx != 0); + g_return_if_fail(!is_array || !section_prefix || idx != 0); if (section_prefix && idx == 0) /* The first field is section prefix */ continue; diff --git a/src/nmtui/nmt-address-list.c b/src/nmtui/nmt-address-list.c index 1678f33f..dba8e790 100644 --- a/src/nmtui/nmt-address-list.c +++ b/src/nmtui/nmt-address-list.c @@ -121,19 +121,20 @@ nmt_address_list_create_widget(NmtWidgetList *list, int num) NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE(list); NmtNewtWidget *entry; - if (priv->list_type == NMT_ADDRESS_LIST_IP4_WITH_PREFIX) + if (priv->list_type == NMT_ADDRESS_LIST_IP4_WITH_PREFIX) { entry = nmt_ip_entry_new(25, AF_INET, TRUE, FALSE); - else if (priv->list_type == NMT_ADDRESS_LIST_IP4) + } else if (priv->list_type == NMT_ADDRESS_LIST_IP4) { entry = nmt_ip_entry_new(25, AF_INET, FALSE, FALSE); - else if (priv->list_type == NMT_ADDRESS_LIST_IP6_WITH_PREFIX) + } else if (priv->list_type == NMT_ADDRESS_LIST_IP6_WITH_PREFIX) { entry = nmt_ip_entry_new(25, AF_INET6, TRUE, FALSE); - else if (priv->list_type == NMT_ADDRESS_LIST_IP6) + } else if (priv->list_type == NMT_ADDRESS_LIST_IP6) { entry = nmt_ip_entry_new(25, AF_INET6, FALSE, FALSE); - else if (priv->list_type == NMT_ADDRESS_LIST_HOSTNAME) { + } else if (priv->list_type == NMT_ADDRESS_LIST_HOSTNAME) { entry = nmt_newt_entry_new(25, NMT_NEWT_ENTRY_NONEMPTY); nmt_newt_entry_set_filter(NMT_NEWT_ENTRY(entry), hostname_filter, list); - } else - g_assert_not_reached(); + } else { + g_return_val_if_reached(NULL); + } g_object_bind_property_full(list, "strings", diff --git a/src/nmtui/nmt-editor-grid.c b/src/nmtui/nmt-editor-grid.c index bb257805..b4beda92 100644 --- a/src/nmtui/nmt-editor-grid.c +++ b/src/nmtui/nmt-editor-grid.c @@ -232,7 +232,7 @@ nmt_editor_grid_get_components(NmtNewtWidget *widget) if (rows[i].label) { child_cos = nmt_newt_widget_get_components(rows[i].label); - g_assert(child_cos[0] && !child_cos[1]); + g_return_val_if_fail(child_cos[0] && !child_cos[1], NULL); g_ptr_array_add(cos, child_cos[0]); g_free(child_cos); } diff --git a/src/nmtui/nmt-editor.c b/src/nmtui/nmt-editor.c index 8e6a760c..427ac017 100644 --- a/src/nmtui/nmt-editor.c +++ b/src/nmtui/nmt-editor.c @@ -377,7 +377,7 @@ nmt_editor_constructed(GObject *object) else if (nm_connection_is_type(priv->edit_connection, NM_SETTING_WIREGUARD_SETTING_NAME)) page = nmt_page_wireguard_new(priv->edit_connection, deventry); else - g_assert_not_reached(); + g_return_if_reached(); add_sections_for_page(editor, grid, page); nmt_editor_grid_append(grid, NULL, nmt_newt_separator_new(), NULL); diff --git a/src/nmtui/nmt-mac-entry.h b/src/nmtui/nmt-mac-entry.h index 0ed5a28c..de44fb9d 100644 --- a/src/nmtui/nmt-mac-entry.h +++ b/src/nmtui/nmt-mac-entry.h @@ -9,9 +9,9 @@ #include "nm-utils.h" #include "libnmt-newt/nmt-newt.h" -typedef enum { /*< skip >*/ - NMT_MAC_ENTRY_TYPE_MAC, - NMT_MAC_ENTRY_TYPE_CLONED, +typedef enum { + NMT_MAC_ENTRY_TYPE_MAC, + NMT_MAC_ENTRY_TYPE_CLONED, } NmtMacEntryType; #define NMT_TYPE_MAC_ENTRY (nmt_mac_entry_get_type()) |