diff options
| author | Michael Biebl <biebl@debian.org> | 2021-10-01 23:05:04 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2021-10-01 23:05:04 +0200 |
| commit | e74c568b07b50b97873fb4ee1d776dedefbd54d6 (patch) | |
| tree | 3469f17ea9af91f7ff169b890633bda68b0cf76e /src/libnm-client-impl | |
| parent | bfe522304da217296e2a61040f58e35ec5d6f3f2 (diff) | |
New upstream version 1.32.12 upstream/1.32.12
Diffstat (limited to 'src/libnm-client-impl')
76 files changed, 41357 insertions, 0 deletions
diff --git a/src/libnm-client-impl/libnm.pc.in b/src/libnm-client-impl/libnm.pc.in new file mode 100644 index 00000000..afc5e4d9 --- /dev/null +++ b/src/libnm-client-impl/libnm.pc.in @@ -0,0 +1,14 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +vpnservicedir=@nmlibdir@/VPN + +Name: libnm +Description: Convenience library for clients of NetworkManager +Version: @VERSION@ +Requires: gio-2.0 +Cflags: -I${includedir}/libnm +Libs: -L${libdir} -lnm + + diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver new file mode 100644 index 00000000..89a99af8 --- /dev/null +++ b/src/libnm-client-impl/libnm.ver @@ -0,0 +1,1800 @@ +libnm_1_0_0 { +global: + nm_802_11_ap_flags_get_type; + nm_802_11_ap_security_flags_get_type; + nm_802_11_mode_get_type; + nm_access_point_connection_valid; + nm_access_point_filter_connections; + nm_access_point_get_bssid; + nm_access_point_get_flags; + nm_access_point_get_frequency; + nm_access_point_get_max_bitrate; + nm_access_point_get_mode; + nm_access_point_get_rsn_flags; + nm_access_point_get_ssid; + nm_access_point_get_strength; + nm_access_point_get_type; + nm_access_point_get_wpa_flags; + nm_active_connection_get_connection; + nm_active_connection_get_connection_type; + nm_active_connection_get_default6; + nm_active_connection_get_default; + nm_active_connection_get_devices; + nm_active_connection_get_dhcp4_config; + nm_active_connection_get_dhcp6_config; + nm_active_connection_get_id; + nm_active_connection_get_ip4_config; + nm_active_connection_get_ip6_config; + nm_active_connection_get_master; + nm_active_connection_get_specific_object_path; + nm_active_connection_get_state; + nm_active_connection_get_type; + nm_active_connection_get_uuid; + nm_active_connection_get_vpn; + nm_active_connection_state_get_type; + nm_agent_manager_error_get_type; + nm_agent_manager_error_quark; + nm_bluetooth_capabilities_get_type; + nm_client_activate_connection_async; + nm_client_activate_connection_finish; + nm_client_add_and_activate_connection_async; + nm_client_add_and_activate_connection_finish; + nm_client_add_connection_async; + nm_client_add_connection_finish; + nm_client_check_connectivity; + nm_client_check_connectivity_async; + nm_client_check_connectivity_finish; + nm_client_deactivate_connection; + nm_client_deactivate_connection_async; + nm_client_deactivate_connection_finish; + nm_client_error_get_type; + nm_client_error_quark; + nm_client_get_activating_connection; + nm_client_get_active_connections; + nm_client_get_connection_by_id; + nm_client_get_connection_by_path; + nm_client_get_connection_by_uuid; + nm_client_get_connections; + nm_client_get_connectivity; + nm_client_get_device_by_iface; + nm_client_get_device_by_path; + nm_client_get_devices; + nm_client_get_logging; + nm_client_get_nm_running; + nm_client_get_permission_result; + nm_client_get_primary_connection; + nm_client_get_startup; + nm_client_get_state; + nm_client_get_type; + nm_client_get_version; + nm_client_load_connections; + nm_client_load_connections_async; + nm_client_load_connections_finish; + nm_client_networking_get_enabled; + nm_client_networking_set_enabled; + nm_client_new; + nm_client_new_async; + nm_client_new_finish; + nm_client_permission_get_type; + nm_client_permission_result_get_type; + nm_client_reload_connections; + nm_client_reload_connections_async; + nm_client_reload_connections_finish; + nm_client_save_hostname; + nm_client_save_hostname_async; + nm_client_save_hostname_finish; + nm_client_set_logging; + nm_client_wimax_get_enabled; + nm_client_wimax_hardware_get_enabled; + nm_client_wimax_set_enabled; + nm_client_wireless_get_enabled; + nm_client_wireless_hardware_get_enabled; + nm_client_wireless_set_enabled; + nm_client_wwan_get_enabled; + nm_client_wwan_hardware_get_enabled; + nm_client_wwan_set_enabled; + nm_connection_add_setting; + nm_connection_clear_secrets; + nm_connection_clear_secrets_with_flags; + nm_connection_clear_settings; + nm_connection_compare; + nm_connection_diff; + nm_connection_dump; + nm_connection_error_get_type; + nm_connection_error_quark; + nm_connection_for_each_setting_value; + nm_connection_get_connection_type; + nm_connection_get_id; + nm_connection_get_interface_name; + nm_connection_get_path; + nm_connection_get_setting; + nm_connection_get_setting_802_1x; + nm_connection_get_setting_adsl; + nm_connection_get_setting_bluetooth; + nm_connection_get_setting_bond; + nm_connection_get_setting_bridge; + nm_connection_get_setting_bridge_port; + nm_connection_get_setting_by_name; + nm_connection_get_setting_cdma; + nm_connection_get_setting_connection; + nm_connection_get_setting_dcb; + nm_connection_get_setting_generic; + nm_connection_get_setting_gsm; + nm_connection_get_setting_infiniband; + nm_connection_get_setting_ip4_config; + nm_connection_get_setting_ip6_config; + nm_connection_get_setting_olpc_mesh; + nm_connection_get_setting_ppp; + nm_connection_get_setting_pppoe; + nm_connection_get_setting_serial; + nm_connection_get_setting_team; + nm_connection_get_setting_team_port; + nm_connection_get_setting_vlan; + nm_connection_get_setting_vpn; + nm_connection_get_setting_wimax; + nm_connection_get_setting_wired; + nm_connection_get_setting_wireless; + nm_connection_get_setting_wireless_security; + nm_connection_get_type; + nm_connection_get_uuid; + nm_connection_get_virtual_device_description; + nm_connection_is_type; + nm_connection_is_virtual; + nm_connection_need_secrets; + nm_connection_normalize; + nm_connection_remove_setting; + nm_connection_replace_settings; + nm_connection_replace_settings_from_connection; + nm_connection_serialization_flags_get_type; + nm_connection_set_path; + nm_connection_to_dbus; + nm_connection_update_secrets; + nm_connection_verify; + nm_connectivity_state_get_type; + nm_crypto_error_get_type; + nm_crypto_error_quark; + nm_device_adsl_get_carrier; + nm_device_adsl_get_type; + nm_device_bond_get_carrier; + nm_device_bond_get_hw_address; + nm_device_bond_get_slaves; + nm_device_bond_get_type; + nm_device_bridge_get_carrier; + nm_device_bridge_get_hw_address; + nm_device_bridge_get_slaves; + nm_device_bridge_get_type; + nm_device_bt_get_capabilities; + nm_device_bt_get_hw_address; + nm_device_bt_get_name; + nm_device_bt_get_type; + nm_device_capabilities_get_type; + nm_device_connection_compatible; + nm_device_connection_valid; + nm_device_delete; + nm_device_delete_async; + nm_device_delete_finish; + nm_device_disambiguate_names; + nm_device_disconnect; + nm_device_disconnect_async; + nm_device_disconnect_finish; + nm_device_error_get_type; + nm_device_error_quark; + nm_device_ethernet_get_carrier; + nm_device_ethernet_get_hw_address; + nm_device_ethernet_get_permanent_hw_address; + nm_device_ethernet_get_speed; + nm_device_ethernet_get_type; + nm_device_filter_connections; + nm_device_generic_get_hw_address; + nm_device_generic_get_type; + nm_device_get_active_connection; + nm_device_get_autoconnect; + nm_device_get_available_connections; + nm_device_get_capabilities; + nm_device_get_description; + nm_device_get_device_type; + nm_device_get_dhcp4_config; + nm_device_get_dhcp6_config; + nm_device_get_driver; + nm_device_get_driver_version; + nm_device_get_firmware_missing; + nm_device_get_firmware_version; + nm_device_get_hw_address; + nm_device_get_iface; + nm_device_get_ip4_config; + nm_device_get_ip6_config; + nm_device_get_ip_iface; + nm_device_get_managed; + nm_device_get_mtu; + nm_device_get_physical_port_id; + nm_device_get_product; + nm_device_get_setting_type; + nm_device_get_state; + nm_device_get_state_reason; + nm_device_get_type; + nm_device_get_type_description; + nm_device_get_udi; + nm_device_get_vendor; + nm_device_infiniband_get_carrier; + nm_device_infiniband_get_hw_address; + nm_device_infiniband_get_type; + nm_device_is_software; + nm_device_modem_capabilities_get_type; + nm_device_modem_get_current_capabilities; + nm_device_modem_get_modem_capabilities; + nm_device_modem_get_type; + nm_device_olpc_mesh_get_active_channel; + nm_device_olpc_mesh_get_companion; + nm_device_olpc_mesh_get_hw_address; + nm_device_olpc_mesh_get_type; + nm_device_set_autoconnect; + nm_device_state_get_type; + nm_device_state_reason_get_type; + nm_device_team_get_carrier; + nm_device_team_get_hw_address; + nm_device_team_get_slaves; + nm_device_team_get_type; + nm_device_type_get_type; + nm_device_vlan_get_carrier; + nm_device_vlan_get_hw_address; + nm_device_vlan_get_parent; + nm_device_vlan_get_type; + nm_device_vlan_get_vlan_id; + nm_device_wifi_capabilities_get_type; + nm_device_wifi_get_access_point_by_path; + nm_device_wifi_get_access_points; + nm_device_wifi_get_active_access_point; + nm_device_wifi_get_bitrate; + nm_device_wifi_get_capabilities; + nm_device_wifi_get_hw_address; + nm_device_wifi_get_mode; + nm_device_wifi_get_permanent_hw_address; + nm_device_wifi_get_type; + nm_device_wifi_request_scan; + nm_device_wifi_request_scan_async; + nm_device_wifi_request_scan_finish; + nm_device_wimax_get_active_nsp; + nm_device_wimax_get_bsid; + nm_device_wimax_get_center_frequency; + nm_device_wimax_get_cinr; + nm_device_wimax_get_hw_address; + nm_device_wimax_get_nsp_by_path; + nm_device_wimax_get_nsps; + nm_device_wimax_get_rssi; + nm_device_wimax_get_tx_power; + nm_device_wimax_get_type; + nm_dhcp_config_get_family; + nm_dhcp_config_get_one_option; + nm_dhcp_config_get_options; + nm_dhcp_config_get_type; + nm_ip_address_equal; + nm_ip_address_get_address; + nm_ip_address_get_address_binary; + nm_ip_address_get_attribute; + nm_ip_address_get_attribute_names; + nm_ip_address_get_family; + nm_ip_address_get_prefix; + nm_ip_address_get_type; + nm_ip_address_new; + nm_ip_address_new_binary; + nm_ip_address_ref; + nm_ip_address_set_address; + nm_ip_address_set_address_binary; + nm_ip_address_set_attribute; + nm_ip_address_set_prefix; + nm_ip_address_unref; + nm_ip_config_get_addresses; + nm_ip_config_get_domains; + nm_ip_config_get_family; + nm_ip_config_get_gateway; + nm_ip_config_get_nameservers; + nm_ip_config_get_routes; + nm_ip_config_get_searches; + nm_ip_config_get_type; + nm_ip_config_get_wins_servers; + nm_ip_route_equal; + nm_ip_route_get_attribute; + nm_ip_route_get_attribute_names; + nm_ip_route_get_dest; + nm_ip_route_get_dest_binary; + nm_ip_route_get_family; + nm_ip_route_get_metric; + nm_ip_route_get_next_hop; + nm_ip_route_get_next_hop_binary; + nm_ip_route_get_prefix; + nm_ip_route_get_type; + nm_ip_route_new; + nm_ip_route_new_binary; + nm_ip_route_ref; + nm_ip_route_set_attribute; + nm_ip_route_set_dest; + nm_ip_route_set_dest_binary; + nm_ip_route_set_metric; + nm_ip_route_set_next_hop; + nm_ip_route_set_next_hop_binary; + nm_ip_route_set_prefix; + nm_ip_route_unref; + nm_manager_error_get_type; + nm_manager_error_quark; + nm_object_get_path; + nm_object_get_type; + nm_remote_connection_commit_changes; + nm_remote_connection_commit_changes_async; + nm_remote_connection_commit_changes_finish; + nm_remote_connection_delete; + nm_remote_connection_delete_async; + nm_remote_connection_delete_finish; + nm_remote_connection_get_secrets; + nm_remote_connection_get_secrets_async; + nm_remote_connection_get_secrets_finish; + nm_remote_connection_get_type; + nm_remote_connection_get_unsaved; + nm_remote_connection_get_visible; + nm_remote_connection_save; + nm_remote_connection_save_async; + nm_remote_connection_save_finish; + nm_secret_agent_capabilities_get_type; + nm_secret_agent_error_get_type; + nm_secret_agent_error_quark; + nm_secret_agent_get_secrets_flags_get_type; + nm_secret_agent_old_delete_secrets; + nm_secret_agent_old_get_registered; + nm_secret_agent_old_get_secrets; + nm_secret_agent_old_get_type; + nm_secret_agent_old_register; + nm_secret_agent_old_register_async; + nm_secret_agent_old_register_finish; + nm_secret_agent_old_save_secrets; + nm_secret_agent_old_unregister; + nm_secret_agent_old_unregister_async; + nm_secret_agent_old_unregister_finish; + nm_setting_802_1x_add_altsubject_match; + nm_setting_802_1x_add_eap_method; + nm_setting_802_1x_add_phase2_altsubject_match; + nm_setting_802_1x_ck_format_get_type; + nm_setting_802_1x_ck_scheme_get_type; + nm_setting_802_1x_clear_altsubject_matches; + nm_setting_802_1x_clear_eap_methods; + nm_setting_802_1x_clear_phase2_altsubject_matches; + nm_setting_802_1x_get_altsubject_match; + nm_setting_802_1x_get_anonymous_identity; + nm_setting_802_1x_get_ca_cert_blob; + nm_setting_802_1x_get_ca_cert_path; + nm_setting_802_1x_get_ca_cert_scheme; + nm_setting_802_1x_get_ca_path; + nm_setting_802_1x_get_client_cert_blob; + nm_setting_802_1x_get_client_cert_path; + nm_setting_802_1x_get_client_cert_scheme; + nm_setting_802_1x_get_eap_method; + nm_setting_802_1x_get_identity; + nm_setting_802_1x_get_num_altsubject_matches; + nm_setting_802_1x_get_num_eap_methods; + nm_setting_802_1x_get_num_phase2_altsubject_matches; + nm_setting_802_1x_get_pac_file; + nm_setting_802_1x_get_password; + nm_setting_802_1x_get_password_flags; + nm_setting_802_1x_get_password_raw; + nm_setting_802_1x_get_password_raw_flags; + nm_setting_802_1x_get_phase1_fast_provisioning; + nm_setting_802_1x_get_phase1_peaplabel; + nm_setting_802_1x_get_phase1_peapver; + nm_setting_802_1x_get_phase2_altsubject_match; + nm_setting_802_1x_get_phase2_auth; + nm_setting_802_1x_get_phase2_autheap; + nm_setting_802_1x_get_phase2_ca_cert_blob; + nm_setting_802_1x_get_phase2_ca_cert_path; + nm_setting_802_1x_get_phase2_ca_cert_scheme; + nm_setting_802_1x_get_phase2_ca_path; + nm_setting_802_1x_get_phase2_client_cert_blob; + nm_setting_802_1x_get_phase2_client_cert_path; + nm_setting_802_1x_get_phase2_client_cert_scheme; + nm_setting_802_1x_get_phase2_private_key_blob; + nm_setting_802_1x_get_phase2_private_key_format; + nm_setting_802_1x_get_phase2_private_key_password; + nm_setting_802_1x_get_phase2_private_key_password_flags; + nm_setting_802_1x_get_phase2_private_key_path; + nm_setting_802_1x_get_phase2_private_key_scheme; + nm_setting_802_1x_get_phase2_subject_match; + nm_setting_802_1x_get_pin; + nm_setting_802_1x_get_pin_flags; + nm_setting_802_1x_get_private_key_blob; + nm_setting_802_1x_get_private_key_format; + nm_setting_802_1x_get_private_key_password; + nm_setting_802_1x_get_private_key_password_flags; + nm_setting_802_1x_get_private_key_path; + nm_setting_802_1x_get_private_key_scheme; + nm_setting_802_1x_get_subject_match; + nm_setting_802_1x_get_system_ca_certs; + nm_setting_802_1x_get_type; + nm_setting_802_1x_new; + nm_setting_802_1x_remove_altsubject_match; + nm_setting_802_1x_remove_altsubject_match_by_value; + nm_setting_802_1x_remove_eap_method; + nm_setting_802_1x_remove_eap_method_by_value; + nm_setting_802_1x_remove_phase2_altsubject_match; + nm_setting_802_1x_remove_phase2_altsubject_match_by_value; + nm_setting_802_1x_set_ca_cert; + nm_setting_802_1x_set_client_cert; + nm_setting_802_1x_set_phase2_ca_cert; + nm_setting_802_1x_set_phase2_client_cert; + nm_setting_802_1x_set_phase2_private_key; + nm_setting_802_1x_set_private_key; + nm_setting_adsl_get_encapsulation; + nm_setting_adsl_get_password; + nm_setting_adsl_get_password_flags; + nm_setting_adsl_get_protocol; + nm_setting_adsl_get_type; + nm_setting_adsl_get_username; + nm_setting_adsl_get_vci; + nm_setting_adsl_get_vpi; + nm_setting_adsl_new; + nm_setting_bluetooth_get_bdaddr; + nm_setting_bluetooth_get_connection_type; + nm_setting_bluetooth_get_type; + nm_setting_bluetooth_new; + nm_setting_bond_add_option; + nm_setting_bond_get_num_options; + nm_setting_bond_get_option; + nm_setting_bond_get_option_by_name; + nm_setting_bond_get_option_default; + nm_setting_bond_get_type; + nm_setting_bond_get_valid_options; + nm_setting_bond_new; + nm_setting_bond_remove_option; + nm_setting_bond_validate_option; + nm_setting_bridge_get_ageing_time; + nm_setting_bridge_get_forward_delay; + nm_setting_bridge_get_hello_time; + nm_setting_bridge_get_mac_address; + nm_setting_bridge_get_max_age; + nm_setting_bridge_get_priority; + nm_setting_bridge_get_stp; + nm_setting_bridge_get_type; + nm_setting_bridge_new; + nm_setting_bridge_port_get_hairpin_mode; + nm_setting_bridge_port_get_path_cost; + nm_setting_bridge_port_get_priority; + nm_setting_bridge_port_get_type; + nm_setting_bridge_port_new; + nm_setting_cdma_get_number; + nm_setting_cdma_get_password; + nm_setting_cdma_get_password_flags; + nm_setting_cdma_get_type; + nm_setting_cdma_get_username; + nm_setting_cdma_new; + nm_setting_compare; + nm_setting_compare_flags_get_type; + nm_setting_connection_add_permission; + nm_setting_connection_add_secondary; + nm_setting_connection_get_autoconnect; + nm_setting_connection_get_autoconnect_priority; + nm_setting_connection_get_connection_type; + nm_setting_connection_get_gateway_ping_timeout; + nm_setting_connection_get_id; + nm_setting_connection_get_interface_name; + nm_setting_connection_get_master; + nm_setting_connection_get_num_permissions; + nm_setting_connection_get_num_secondaries; + nm_setting_connection_get_permission; + nm_setting_connection_get_read_only; + nm_setting_connection_get_secondary; + nm_setting_connection_get_slave_type; + nm_setting_connection_get_timestamp; + nm_setting_connection_get_type; + nm_setting_connection_get_uuid; + nm_setting_connection_get_zone; + nm_setting_connection_is_slave_type; + nm_setting_connection_new; + nm_setting_connection_permissions_user_allowed; + nm_setting_connection_remove_permission; + nm_setting_connection_remove_permission_by_value; + nm_setting_connection_remove_secondary; + nm_setting_connection_remove_secondary_by_value; + nm_setting_dcb_flags_get_type; + nm_setting_dcb_get_app_fcoe_flags; + nm_setting_dcb_get_app_fcoe_mode; + nm_setting_dcb_get_app_fcoe_priority; + nm_setting_dcb_get_app_fip_flags; + nm_setting_dcb_get_app_fip_priority; + nm_setting_dcb_get_app_iscsi_flags; + nm_setting_dcb_get_app_iscsi_priority; + nm_setting_dcb_get_priority_bandwidth; + nm_setting_dcb_get_priority_flow_control; + nm_setting_dcb_get_priority_flow_control_flags; + nm_setting_dcb_get_priority_group_bandwidth; + nm_setting_dcb_get_priority_group_flags; + nm_setting_dcb_get_priority_group_id; + nm_setting_dcb_get_priority_strict_bandwidth; + nm_setting_dcb_get_priority_traffic_class; + nm_setting_dcb_get_type; + nm_setting_dcb_new; + nm_setting_dcb_set_priority_bandwidth; + nm_setting_dcb_set_priority_flow_control; + nm_setting_dcb_set_priority_group_bandwidth; + nm_setting_dcb_set_priority_group_id; + nm_setting_dcb_set_priority_strict_bandwidth; + nm_setting_dcb_set_priority_traffic_class; + nm_setting_diff; + nm_setting_diff_result_get_type; + nm_setting_duplicate; + nm_setting_enumerate_values; + nm_setting_generic_get_type; + nm_setting_generic_new; + nm_setting_get_dbus_property_type; + nm_setting_get_name; + nm_setting_get_secret_flags; + nm_setting_get_type; + nm_setting_gsm_get_apn; + nm_setting_gsm_get_home_only; + nm_setting_gsm_get_network_id; + nm_setting_gsm_get_number; + nm_setting_gsm_get_password; + nm_setting_gsm_get_password_flags; + nm_setting_gsm_get_pin; + nm_setting_gsm_get_pin_flags; + nm_setting_gsm_get_type; + nm_setting_gsm_get_username; + nm_setting_gsm_new; + nm_setting_infiniband_get_mac_address; + nm_setting_infiniband_get_mtu; + nm_setting_infiniband_get_p_key; + nm_setting_infiniband_get_parent; + nm_setting_infiniband_get_transport_mode; + nm_setting_infiniband_get_type; + nm_setting_infiniband_get_virtual_interface_name; + nm_setting_infiniband_new; + nm_setting_ip4_config_get_dhcp_client_id; + nm_setting_ip4_config_get_type; + nm_setting_ip4_config_new; + nm_setting_ip6_config_get_ip6_privacy; + nm_setting_ip6_config_get_type; + nm_setting_ip6_config_new; + nm_setting_ip6_config_privacy_get_type; + nm_setting_ip_config_add_address; + nm_setting_ip_config_add_dns; + nm_setting_ip_config_add_dns_search; + nm_setting_ip_config_add_route; + nm_setting_ip_config_clear_addresses; + nm_setting_ip_config_clear_dns; + nm_setting_ip_config_clear_dns_searches; + nm_setting_ip_config_clear_routes; + nm_setting_ip_config_get_address; + nm_setting_ip_config_get_dhcp_hostname; + nm_setting_ip_config_get_dhcp_send_hostname; + nm_setting_ip_config_get_dns; + nm_setting_ip_config_get_dns_search; + nm_setting_ip_config_get_gateway; + nm_setting_ip_config_get_ignore_auto_dns; + nm_setting_ip_config_get_ignore_auto_routes; + nm_setting_ip_config_get_may_fail; + nm_setting_ip_config_get_method; + nm_setting_ip_config_get_never_default; + nm_setting_ip_config_get_num_addresses; + nm_setting_ip_config_get_num_dns; + nm_setting_ip_config_get_num_dns_searches; + nm_setting_ip_config_get_num_routes; + nm_setting_ip_config_get_route; + nm_setting_ip_config_get_route_metric; + nm_setting_ip_config_get_type; + nm_setting_ip_config_remove_address; + nm_setting_ip_config_remove_address_by_value; + nm_setting_ip_config_remove_dns; + nm_setting_ip_config_remove_dns_by_value; + nm_setting_ip_config_remove_dns_search; + nm_setting_ip_config_remove_dns_search_by_value; + nm_setting_ip_config_remove_route; + nm_setting_ip_config_remove_route_by_value; + nm_setting_lookup_type; + nm_setting_olpc_mesh_get_channel; + nm_setting_olpc_mesh_get_dhcp_anycast_address; + nm_setting_olpc_mesh_get_ssid; + nm_setting_olpc_mesh_get_type; + nm_setting_olpc_mesh_new; + nm_setting_ppp_get_baud; + nm_setting_ppp_get_crtscts; + nm_setting_ppp_get_lcp_echo_failure; + nm_setting_ppp_get_lcp_echo_interval; + nm_setting_ppp_get_mppe_stateful; + nm_setting_ppp_get_mru; + nm_setting_ppp_get_mtu; + nm_setting_ppp_get_no_vj_comp; + nm_setting_ppp_get_noauth; + nm_setting_ppp_get_nobsdcomp; + nm_setting_ppp_get_nodeflate; + nm_setting_ppp_get_refuse_chap; + nm_setting_ppp_get_refuse_eap; + nm_setting_ppp_get_refuse_mschap; + nm_setting_ppp_get_refuse_mschapv2; + nm_setting_ppp_get_refuse_pap; + nm_setting_ppp_get_require_mppe; + nm_setting_ppp_get_require_mppe_128; + nm_setting_ppp_get_type; + nm_setting_ppp_new; + nm_setting_pppoe_get_password; + nm_setting_pppoe_get_password_flags; + nm_setting_pppoe_get_service; + nm_setting_pppoe_get_type; + nm_setting_pppoe_get_username; + nm_setting_pppoe_new; + nm_setting_secret_flags_get_type; + nm_setting_serial_get_baud; + nm_setting_serial_get_bits; + nm_setting_serial_get_parity; + nm_setting_serial_get_send_delay; + nm_setting_serial_get_stopbits; + nm_setting_serial_get_type; + nm_setting_serial_new; + nm_setting_serial_parity_get_type; + nm_setting_set_secret_flags; + nm_setting_team_get_config; + nm_setting_team_get_type; + nm_setting_team_new; + nm_setting_team_port_get_config; + nm_setting_team_port_get_type; + nm_setting_team_port_new; + nm_setting_to_string; + nm_setting_verify; + nm_setting_vlan_add_priority; + nm_setting_vlan_add_priority_str; + nm_setting_vlan_clear_priorities; + nm_setting_vlan_get_flags; + nm_setting_vlan_get_id; + nm_setting_vlan_get_num_priorities; + nm_setting_vlan_get_parent; + nm_setting_vlan_get_priority; + nm_setting_vlan_get_type; + nm_setting_vlan_new; + nm_setting_vlan_remove_priority; + nm_setting_vlan_remove_priority_by_value; + nm_setting_vlan_remove_priority_str_by_value; + nm_setting_vpn_add_data_item; + nm_setting_vpn_add_secret; + nm_setting_vpn_foreach_data_item; + nm_setting_vpn_foreach_secret; + nm_setting_vpn_get_data_item; + nm_setting_vpn_get_num_data_items; + nm_setting_vpn_get_num_secrets; + nm_setting_vpn_get_secret; + nm_setting_vpn_get_service_type; + nm_setting_vpn_get_type; + nm_setting_vpn_get_user_name; + nm_setting_vpn_new; + nm_setting_vpn_remove_data_item; + nm_setting_vpn_remove_secret; + nm_setting_wimax_get_mac_address; + nm_setting_wimax_get_network_name; + nm_setting_wimax_get_type; + nm_setting_wimax_new; + nm_setting_wired_add_mac_blacklist_item; + nm_setting_wired_add_s390_option; + nm_setting_wired_clear_mac_blacklist_items; + nm_setting_wired_get_auto_negotiate; + nm_setting_wired_get_cloned_mac_address; + nm_setting_wired_get_duplex; + nm_setting_wired_get_mac_address; + nm_setting_wired_get_mac_address_blacklist; + nm_setting_wired_get_mac_blacklist_item; + nm_setting_wired_get_mtu; + nm_setting_wired_get_num_mac_blacklist_items; + nm_setting_wired_get_num_s390_options; + nm_setting_wired_get_port; + nm_setting_wired_get_s390_nettype; + nm_setting_wired_get_s390_option; + nm_setting_wired_get_s390_option_by_key; + nm_setting_wired_get_s390_subchannels; + nm_setting_wired_get_speed; + nm_setting_wired_get_type; + nm_setting_wired_get_valid_s390_options; + nm_setting_wired_new; + nm_setting_wired_remove_mac_blacklist_item; + nm_setting_wired_remove_mac_blacklist_item_by_value; + nm_setting_wired_remove_s390_option; + nm_setting_wireless_add_mac_blacklist_item; + nm_setting_wireless_add_seen_bssid; + nm_setting_wireless_ap_security_compatible; + nm_setting_wireless_clear_mac_blacklist_items; + nm_setting_wireless_get_band; + nm_setting_wireless_get_bssid; + nm_setting_wireless_get_channel; + nm_setting_wireless_get_cloned_mac_address; + nm_setting_wireless_get_hidden; + nm_setting_wireless_get_mac_address; + nm_setting_wireless_get_mac_address_blacklist; + nm_setting_wireless_get_mac_blacklist_item; + nm_setting_wireless_get_mode; + nm_setting_wireless_get_mtu; + nm_setting_wireless_get_num_mac_blacklist_items; + nm_setting_wireless_get_num_seen_bssids; + nm_setting_wireless_get_rate; + nm_setting_wireless_get_seen_bssid; + nm_setting_wireless_get_ssid; + nm_setting_wireless_get_tx_power; + nm_setting_wireless_get_type; + nm_setting_wireless_new; + nm_setting_wireless_remove_mac_blacklist_item; + nm_setting_wireless_remove_mac_blacklist_item_by_value; + nm_setting_wireless_security_add_group; + nm_setting_wireless_security_add_pairwise; + nm_setting_wireless_security_add_proto; + nm_setting_wireless_security_clear_groups; + nm_setting_wireless_security_clear_pairwise; + nm_setting_wireless_security_clear_protos; + nm_setting_wireless_security_get_auth_alg; + nm_setting_wireless_security_get_group; + nm_setting_wireless_security_get_key_mgmt; + nm_setting_wireless_security_get_leap_password; + nm_setting_wireless_security_get_leap_password_flags; + nm_setting_wireless_security_get_leap_username; + nm_setting_wireless_security_get_num_groups; + nm_setting_wireless_security_get_num_pairwise; + nm_setting_wireless_security_get_num_protos; + nm_setting_wireless_security_get_pairwise; + nm_setting_wireless_security_get_proto; + nm_setting_wireless_security_get_psk; + nm_setting_wireless_security_get_psk_flags; + nm_setting_wireless_security_get_type; + nm_setting_wireless_security_get_wep_key; + nm_setting_wireless_security_get_wep_key_flags; + nm_setting_wireless_security_get_wep_key_type; + nm_setting_wireless_security_get_wep_tx_keyidx; + nm_setting_wireless_security_new; + nm_setting_wireless_security_remove_group; + nm_setting_wireless_security_remove_group_by_value; + nm_setting_wireless_security_remove_pairwise; + nm_setting_wireless_security_remove_pairwise_by_value; + nm_setting_wireless_security_remove_proto; + nm_setting_wireless_security_remove_proto_by_value; + nm_setting_wireless_security_set_wep_key; + nm_settings_error_get_type; + nm_settings_error_quark; + nm_simple_connection_get_type; + nm_simple_connection_new; + nm_simple_connection_new_clone; + nm_simple_connection_new_from_dbus; + nm_state_get_type; + nm_utils_ap_mode_security_valid; + nm_utils_bin2hexstr; + nm_utils_check_virtual_device_compatibility; + nm_utils_escape_ssid; + nm_utils_file_is_certificate; + nm_utils_file_is_pkcs12; + nm_utils_file_is_private_key; + nm_utils_file_search_in_paths; + nm_utils_hexstr2bin; + nm_utils_hwaddr_atoba; + nm_utils_hwaddr_aton; + nm_utils_hwaddr_canonical; + nm_utils_hwaddr_len; + nm_utils_hwaddr_matches; + nm_utils_hwaddr_ntoa; + nm_utils_hwaddr_valid; + nm_utils_iface_valid_name; + nm_utils_inet4_ntop; + nm_utils_inet6_ntop; + nm_utils_ip4_addresses_from_variant; + nm_utils_ip4_addresses_to_variant; + nm_utils_ip4_dns_from_variant; + nm_utils_ip4_dns_to_variant; + nm_utils_ip4_get_default_prefix; + nm_utils_ip4_netmask_to_prefix; + nm_utils_ip4_prefix_to_netmask; + nm_utils_ip4_routes_from_variant; + nm_utils_ip4_routes_to_variant; + nm_utils_ip6_addresses_from_variant; + nm_utils_ip6_addresses_to_variant; + nm_utils_ip6_dns_from_variant; + nm_utils_ip6_dns_to_variant; + nm_utils_ip6_routes_from_variant; + nm_utils_ip6_routes_to_variant; + nm_utils_ipaddr_valid; + nm_utils_is_empty_ssid; + nm_utils_is_uuid; + nm_utils_same_ssid; + nm_utils_security_type_get_type; + nm_utils_security_valid; + nm_utils_ssid_to_utf8; + nm_utils_uuid_generate; + nm_utils_wep_key_valid; + nm_utils_wifi_channel_to_freq; + nm_utils_wifi_find_next_channel; + nm_utils_wifi_freq_to_channel; + nm_utils_wifi_is_channel_valid; + nm_utils_wifi_strength_bars; + nm_utils_wpa_psk_valid; + nm_vlan_flags_get_type; + nm_vlan_priority_map_get_type; + nm_vpn_connection_get_banner; + nm_vpn_connection_get_type; + nm_vpn_connection_get_vpn_state; + nm_vpn_connection_state_get_type; + nm_vpn_connection_state_reason_get_type; + nm_vpn_editor_get_type; + nm_vpn_editor_get_widget; + nm_vpn_editor_plugin_capability_get_type; + nm_vpn_editor_plugin_export; + nm_vpn_editor_plugin_get_capabilities; + nm_vpn_editor_plugin_get_editor; + nm_vpn_editor_plugin_get_suggested_filename; + nm_vpn_editor_plugin_get_type; + nm_vpn_editor_plugin_import; + nm_vpn_editor_update_connection; + nm_vpn_plugin_error_get_type; + nm_vpn_plugin_error_quark; + nm_vpn_plugin_failure_get_type; + nm_vpn_plugin_old_disconnect; + nm_vpn_plugin_old_failure; + nm_vpn_plugin_old_get_connection; + nm_vpn_plugin_old_get_secret_flags; + nm_vpn_plugin_old_get_state; + nm_vpn_plugin_old_get_type; + nm_vpn_plugin_old_read_vpn_details; + nm_vpn_plugin_old_secrets_required; + nm_vpn_plugin_old_set_ip4_config; + nm_vpn_plugin_old_set_login_banner; + nm_vpn_plugin_old_set_state; + nm_vpn_service_state_get_type; + nm_wep_key_type_get_type; + nm_wimax_nsp_connection_valid; + nm_wimax_nsp_filter_connections; + nm_wimax_nsp_get_name; + nm_wimax_nsp_get_network_type; + nm_wimax_nsp_get_signal_quality; + nm_wimax_nsp_get_type; + nm_wimax_nsp_network_type_get_type; +local: + *; +}; + +libnm_1_0_4 { + #nm_setting_connection_autoconnect_slaves_get_type@libnm_1_0_4; + #nm_setting_connection_get_autoconnect_slaves@libnm_1_0_4; +} libnm_1_0_0; + +libnm_1_0_6 { + #nm_access_point_get_last_seen@libnm_1_0_6; + #nm_device_get_metered@libnm_1_0_6; + #nm_device_wifi_request_scan_options@libnm_1_0_6; + #nm_device_wifi_request_scan_options_async@libnm_1_0_6; + #nm_metered_get_type@libnm_1_0_6; + #nm_setting_connection_get_metered@libnm_1_0_6; + #nm_setting_wired_get_wake_on_lan@libnm_1_0_6; + #nm_setting_wired_get_wake_on_lan_password@libnm_1_0_6; + #nm_setting_wired_wake_on_lan_get_type@libnm_1_0_6; + #nm_utils_enum_from_str@libnm_1_0_6; + #nm_utils_enum_to_str@libnm_1_0_6; + #nm_utils_wifi_2ghz_freqs@libnm_1_0_6; + #nm_utils_wifi_5ghz_freqs@libnm_1_0_6; +} libnm_1_0_4; + +libnm_1_2_0 { +global: + nm_access_point_get_last_seen; + nm_client_get_all_devices; + nm_connection_get_setting_ip_tunnel; + nm_connection_get_setting_macvlan; + nm_connection_get_setting_vxlan; + nm_connection_verify_secrets; + nm_device_ethernet_get_s390_subchannels; + nm_device_get_applied_connection; + nm_device_get_applied_connection_async; + nm_device_get_applied_connection_finish; + nm_device_get_lldp_neighbors; + nm_device_get_metered; + nm_device_get_nm_plugin_missing; + nm_device_ip_tunnel_get_encapsulation_limit; + nm_device_ip_tunnel_get_flow_label; + nm_device_ip_tunnel_get_input_key; + nm_device_ip_tunnel_get_local; + nm_device_ip_tunnel_get_mode; + nm_device_ip_tunnel_get_output_key; + nm_device_ip_tunnel_get_parent; + nm_device_ip_tunnel_get_path_mtu_discovery; + nm_device_ip_tunnel_get_remote; + nm_device_ip_tunnel_get_tos; + nm_device_ip_tunnel_get_ttl; + nm_device_ip_tunnel_get_type; + nm_device_is_real; + nm_device_macvlan_get_hw_address; + nm_device_macvlan_get_mode; + nm_device_macvlan_get_no_promisc; + nm_device_macvlan_get_parent; + nm_device_macvlan_get_tap; + nm_device_macvlan_get_type; + nm_device_reapply; + nm_device_reapply_async; + nm_device_reapply_finish; + nm_device_set_managed; + nm_device_tun_get_group; + nm_device_tun_get_hw_address; + nm_device_tun_get_mode; + nm_device_tun_get_multi_queue; + nm_device_tun_get_owner; + nm_device_tun_get_no_pi; + nm_device_tun_get_type; + nm_device_tun_get_vnet_hdr; + nm_device_vxlan_get_ageing; + nm_device_vxlan_get_dst_port; + nm_device_vxlan_get_group; + nm_device_vxlan_get_hw_address; + nm_device_vxlan_get_id; + nm_device_vxlan_get_l2miss; + nm_device_vxlan_get_l3miss; + nm_device_vxlan_get_learning; + nm_device_vxlan_get_limit; + nm_device_vxlan_get_local; + nm_device_vxlan_get_parent; + nm_device_vxlan_get_proxy; + nm_device_vxlan_get_src_port_max; + nm_device_vxlan_get_src_port_min; + nm_device_vxlan_get_tos; + nm_device_vxlan_get_ttl; + nm_device_vxlan_get_type; + nm_device_wifi_request_scan_options; + nm_device_wifi_request_scan_options_async; + nm_ip_tunnel_mode_get_type; + nm_lldp_neighbor_get_attr_names; + nm_lldp_neighbor_get_attr_string_value; + nm_lldp_neighbor_get_attr_type; + nm_lldp_neighbor_get_attr_uint_value; + nm_lldp_neighbor_get_type; + nm_lldp_neighbor_new; + nm_lldp_neighbor_ref; + nm_lldp_neighbor_unref; + nm_metered_get_type; + nm_setting_802_1x_check_cert_scheme; + nm_setting_802_1x_get_domain_suffix_match; + nm_setting_802_1x_get_phase2_domain_suffix_match; + nm_setting_bridge_get_multicast_snooping; + nm_setting_connection_autoconnect_slaves_get_type; + nm_setting_connection_get_autoconnect_slaves; + nm_setting_connection_get_lldp; + nm_setting_connection_get_metered; + nm_setting_connection_lldp_get_type; + nm_setting_gsm_get_device_id; + nm_setting_gsm_get_sim_id; + nm_setting_gsm_get_sim_operator_id; + nm_setting_ip4_config_get_dhcp_fqdn; + nm_setting_ip6_config_addr_gen_mode_get_type; + nm_setting_ip6_config_get_addr_gen_mode; + nm_setting_ip_config_add_dns_option; + nm_setting_ip_config_clear_dns_options; + nm_setting_ip_config_get_dad_timeout; + nm_setting_ip_config_get_dhcp_timeout; + nm_setting_ip_config_get_dns_option; + nm_setting_ip_config_get_num_dns_options; + nm_setting_ip_config_has_dns_options; + nm_setting_ip_config_remove_dns_option; + nm_setting_ip_config_remove_dns_option_by_value; + nm_setting_ip_tunnel_get_input_key; + nm_setting_ip_tunnel_get_local; + nm_setting_ip_tunnel_get_mode; + nm_setting_ip_tunnel_get_mtu; + nm_setting_ip_tunnel_get_output_key; + nm_setting_ip_tunnel_get_parent; + nm_setting_ip_tunnel_get_path_mtu_discovery; + nm_setting_ip_tunnel_get_remote; + nm_setting_ip_tunnel_get_tos; + nm_setting_ip_tunnel_get_type; + nm_setting_ip_tunnel_get_ttl; + nm_setting_ip_tunnel_new; + nm_setting_mac_randomization_get_type; + nm_setting_tun_get_group; + nm_setting_tun_get_mode; + nm_setting_tun_get_multi_queue; + nm_setting_tun_get_owner; + nm_setting_tun_get_pi; + nm_setting_tun_get_type; + nm_setting_tun_get_vnet_hdr; + nm_setting_tun_mode_get_type; + nm_setting_tun_new; + nm_setting_verify_secrets; + nm_setting_vpn_get_timeout; + nm_setting_macvlan_get_mode; + nm_setting_macvlan_get_parent; + nm_setting_macvlan_get_promiscuous; + nm_setting_macvlan_get_tap; + nm_setting_macvlan_get_type; + nm_setting_macvlan_mode_get_type; + nm_setting_macvlan_new; + nm_setting_vxlan_get_ageing; + nm_setting_vxlan_get_destination_port; + nm_setting_vxlan_get_id; + nm_setting_vxlan_get_l2_miss; + nm_setting_vxlan_get_l3_miss; + nm_setting_vxlan_get_learning; + nm_setting_vxlan_get_limit; + nm_setting_vxlan_get_local; + nm_setting_vxlan_get_parent; + nm_setting_vxlan_get_proxy; + nm_setting_vxlan_get_remote; + nm_setting_vxlan_get_rsc; + nm_setting_vxlan_get_source_port_max; + nm_setting_vxlan_get_source_port_min; + nm_setting_vxlan_get_tos; + nm_setting_vxlan_get_ttl; + nm_setting_vxlan_get_type; + nm_setting_vxlan_new; + nm_setting_wired_get_wake_on_lan; + nm_setting_wired_get_wake_on_lan_password; + nm_setting_wired_wake_on_lan_get_type; + nm_setting_wireless_get_powersave; + nm_setting_wireless_get_mac_address_randomization; + nm_setting_wireless_powersave_get_type; + nm_utils_bond_mode_int_to_string; + nm_utils_bond_mode_string_to_int; + nm_utils_enum_from_str; + nm_utils_enum_to_str; + nm_utils_enum_get_values; + nm_utils_wifi_2ghz_freqs; + nm_utils_wifi_5ghz_freqs; + nm_vpn_editor_plugin_load_from_file; + nm_vpn_plugin_info_get_filename; + nm_vpn_plugin_info_get_editor_plugin; + nm_vpn_plugin_info_get_name; + nm_vpn_plugin_info_get_plugin; + nm_vpn_plugin_info_get_program; + nm_vpn_plugin_info_get_type; + nm_vpn_plugin_info_load_editor_plugin; + nm_vpn_plugin_info_lookup_property; + nm_vpn_plugin_info_new_from_file; + nm_vpn_plugin_info_new_with_data; + nm_vpn_plugin_info_set_editor_plugin; + nm_vpn_plugin_info_validate_filename; + nm_vpn_plugin_info_list_add; + nm_vpn_plugin_info_list_find_by_filename; + nm_vpn_plugin_info_list_find_by_name; + nm_vpn_plugin_info_list_find_by_service; + nm_vpn_plugin_info_list_load; + nm_vpn_plugin_info_list_remove; + nm_vpn_service_plugin_disconnect; + nm_vpn_service_plugin_failure; + nm_vpn_service_plugin_get_connection; + nm_vpn_service_plugin_get_secret_flags; + nm_vpn_service_plugin_get_type; + nm_vpn_service_plugin_read_vpn_details; + nm_vpn_service_plugin_secrets_required; + nm_vpn_service_plugin_set_config; + nm_vpn_service_plugin_set_ip4_config; + nm_vpn_service_plugin_set_ip6_config; + nm_vpn_service_plugin_set_login_banner; +} libnm_1_0_0; + +libnm_1_2_4 { + #nm_setting_ip_config_get_dns_priority@libnm_1_2_4; +} libnm_1_2_0; + +libnm_1_4_0 { +global: + nm_device_team_get_config; + nm_setting_connection_get_stable_id; + nm_setting_ip6_config_get_token; + nm_setting_ip_config_get_dns_priority; + nm_setting_wired_get_generate_mac_address_mask; + nm_setting_wireless_get_generate_mac_address_mask; + nm_vpn_editor_plugin_get_plugin_info; + nm_vpn_editor_plugin_get_vt; + nm_vpn_editor_plugin_load; + nm_vpn_editor_plugin_set_plugin_info; + nm_vpn_plugin_info_get_aliases; + nm_vpn_plugin_info_get_auth_dialog; + nm_vpn_plugin_info_get_service; + nm_vpn_plugin_info_list_get_service_types; + nm_vpn_plugin_info_list_find_service_type; + nm_vpn_plugin_info_new_search_file; + nm_vpn_plugin_info_supports_hints; +} libnm_1_2_0; + +libnm_1_6_0 { +global: + nm_capability_get_type; + nm_client_get_dns_configuration; + nm_client_get_dns_mode; + nm_client_get_dns_rc_manager; + nm_connection_get_setting_macsec; + nm_connection_get_setting_proxy; + nm_device_macsec_get_cipher_suite; + nm_device_macsec_get_encoding_sa; + nm_device_macsec_get_encrypt; + nm_device_macsec_get_es; + nm_device_macsec_get_hw_address; + nm_device_macsec_get_icv_length; + nm_device_macsec_get_include_sci; + nm_device_macsec_get_protect; + nm_device_macsec_get_replay_protect; + nm_device_macsec_get_scb; + nm_device_macsec_get_sci; + nm_device_macsec_get_type; + nm_device_macsec_get_validation; + nm_device_macsec_get_window; + nm_dns_entry_get_domains; + nm_dns_entry_get_interface; + nm_dns_entry_get_nameservers; + nm_dns_entry_get_priority; + nm_dns_entry_get_type; + nm_dns_entry_get_vpn; + nm_dns_entry_unref; + nm_setting_connection_get_autoconnect_retries; + nm_setting_macsec_get_encrypt; + nm_setting_macsec_get_mka_cak; + nm_setting_macsec_get_mka_cak_flags; + nm_setting_macsec_get_mka_ckn; + nm_setting_macsec_get_mode; + nm_setting_macsec_get_parent; + nm_setting_macsec_get_port; + nm_setting_macsec_get_type; + nm_setting_macsec_get_validation; + nm_setting_macsec_mode_get_type; + nm_setting_macsec_new; + nm_setting_macsec_validation_get_type; + nm_setting_proxy_get_type; + nm_setting_proxy_new; + nm_setting_proxy_get_method; + nm_setting_proxy_method_get_type; + nm_setting_proxy_get_browser_only; + nm_setting_proxy_get_pac_url; + nm_setting_proxy_get_pac_script; + nm_setting_802_1x_get_ca_cert_uri; + nm_setting_802_1x_get_phase2_ca_cert_uri; + nm_setting_802_1x_get_client_cert_uri; + nm_setting_802_1x_get_phase2_client_cert_uri; + nm_setting_802_1x_get_private_key_uri; + nm_setting_802_1x_get_phase2_private_key_uri; + nm_utils_is_json_object; + nm_utils_version; + nm_utils_is_valid_iface_name; +} libnm_1_4_0; + +libnm_1_8_0 { +global: + nm_active_connection_state_reason_get_type; + nm_active_connection_get_state_reason; + nm_connection_get_setting_dummy; + nm_device_dummy_get_type; + nm_ip_route_get_variant_attribute_spec; + nm_ip_route_attribute_validate; + nm_setting_802_1x_auth_flags_get_type; + nm_setting_802_1x_get_auth_timeout; + nm_setting_802_1x_get_ca_cert_password; + nm_setting_802_1x_get_ca_cert_password_flags; + nm_setting_802_1x_get_client_cert_password; + nm_setting_802_1x_get_client_cert_password_flags; + nm_setting_802_1x_get_phase1_auth_flags; + nm_setting_802_1x_get_phase2_ca_cert_password; + nm_setting_802_1x_get_phase2_ca_cert_password_flags; + nm_setting_802_1x_get_phase2_client_cert_password; + nm_setting_802_1x_get_phase2_client_cert_password_flags; + nm_setting_cdma_get_mtu; + nm_setting_dummy_get_type; + nm_setting_dummy_new; + nm_setting_gsm_get_mtu; + nm_setting_user_check_key; + nm_setting_user_check_val; + nm_setting_user_get_data; + nm_setting_user_get_keys; + nm_setting_user_get_type; + nm_setting_user_new; + nm_setting_user_set_data; + nm_utils_format_variant_attributes; + nm_utils_parse_variant_attributes; +} libnm_1_6_0; + +libnm_1_10_0 { +global: + nm_activation_state_flags_get_type; + nm_active_connection_get_state_flags; + nm_client_connectivity_check_get_available; + nm_client_connectivity_check_get_enabled; + nm_client_connectivity_check_set_enabled; + nm_connection_get_settings; + nm_device_dummy_get_hw_address; + nm_device_ovs_bridge_get_type; + nm_device_ovs_interface_get_type; + nm_device_ovs_port_get_type; + nm_device_ppp_get_type; + nm_ip_route_equal_full; + nm_setting_bridge_get_group_forward_mask; + nm_setting_connection_get_auth_retries; + nm_setting_ip_config_get_route_table; + nm_setting_ovs_bridge_get_fail_mode; + nm_setting_ovs_bridge_get_mcast_snooping_enable; + nm_setting_ovs_bridge_get_rstp_enable; + nm_setting_ovs_bridge_get_stp_enable; + nm_setting_ovs_bridge_get_type; + nm_setting_ovs_bridge_new; + nm_setting_ovs_interface_get_interface_type; + nm_setting_ovs_interface_get_type; + nm_setting_ovs_interface_new; + nm_setting_ovs_patch_get_peer; + nm_setting_ovs_patch_get_type; + nm_setting_ovs_patch_new; + nm_setting_ovs_port_get_bond_downdelay; + nm_setting_ovs_port_get_bond_mode; + nm_setting_ovs_port_get_bond_updelay; + nm_setting_ovs_port_get_lacp; + nm_setting_ovs_port_get_tag; + nm_setting_ovs_port_get_type; + nm_setting_ovs_port_get_vlan_mode; + nm_setting_ovs_port_new; + nm_setting_pppoe_get_parent; + nm_setting_wireless_security_get_pmf; + nm_setting_wireless_security_get_wps_method; + nm_setting_wireless_security_pmf_get_type; + nm_setting_wireless_security_wps_method_get_type; +} libnm_1_8_0; + +libnm_1_10_2 { +global: + nm_remote_connection_update2; + nm_remote_connection_update2_finish; + nm_settings_update2_flags_get_type; + nm_setting_tc_config_add_qdisc; + nm_setting_tc_config_add_tfilter; + nm_setting_tc_config_clear_qdiscs; + nm_setting_tc_config_clear_tfilters; + nm_setting_tc_config_get_num_qdiscs; + nm_setting_tc_config_get_num_tfilters; + nm_setting_tc_config_get_qdisc; + nm_setting_tc_config_get_tfilter; + nm_setting_tc_config_get_type; + nm_setting_tc_config_new; + nm_setting_tc_config_remove_qdisc; + nm_setting_tc_config_remove_qdisc_by_value; + nm_setting_tc_config_remove_tfilter; + nm_setting_tc_config_remove_tfilter_by_value; + nm_setting_team_add_link_watcher; + nm_setting_team_add_runner_tx_hash; + nm_setting_team_clear_link_watchers; + nm_setting_team_get_link_watcher; + nm_setting_team_get_mcast_rejoin_count; + nm_setting_team_get_mcast_rejoin_interval; + nm_setting_team_get_notify_peers_count; + nm_setting_team_get_notify_peers_interval; + nm_setting_team_get_num_link_watchers; + nm_setting_team_get_num_runner_tx_hash; + nm_setting_team_get_runner; + nm_setting_team_get_runner_active; + nm_setting_team_get_runner_agg_select_policy; + nm_setting_team_get_runner_fast_rate; + nm_setting_team_get_runner_hwaddr_policy; + nm_setting_team_get_runner_min_ports; + nm_setting_team_get_runner_sys_prio; + nm_setting_team_get_runner_tx_balancer; + nm_setting_team_get_runner_tx_balancer_interval; + nm_setting_team_get_runner_tx_hash; + nm_setting_team_port_add_link_watcher; + nm_setting_team_port_clear_link_watchers; + nm_setting_team_port_get_lacp_key; + nm_setting_team_port_get_lacp_prio; + nm_setting_team_port_get_link_watcher; + nm_setting_team_port_get_num_link_watchers; + nm_setting_team_port_get_prio; + nm_setting_team_port_get_queue_id; + nm_setting_team_port_get_sticky; + nm_setting_team_port_remove_link_watcher; + nm_setting_team_port_remove_link_watcher_by_value; + nm_setting_team_remove_link_watcher; + nm_setting_team_remove_link_watcher_by_value; + nm_setting_team_remove_runner_tx_hash; + nm_setting_team_remove_runner_tx_hash_by_value; + nm_tc_action_dup; + nm_tc_action_equal; + nm_tc_action_get_attribute; + nm_tc_action_get_attribute_names; + nm_tc_action_get_kind; + nm_tc_action_get_type; + nm_tc_action_new; + nm_tc_action_ref; + nm_tc_action_set_attribute; + nm_tc_action_unref; + nm_tc_qdisc_dup; + nm_tc_qdisc_equal; + nm_tc_qdisc_get_handle; + nm_tc_qdisc_get_kind; + nm_tc_qdisc_get_parent; + nm_tc_qdisc_get_type; + nm_tc_qdisc_new; + nm_tc_qdisc_ref; + nm_tc_qdisc_set_handle; + nm_tc_qdisc_unref; + nm_tc_tfilter_dup; + nm_tc_tfilter_equal; + nm_tc_tfilter_get_handle; + nm_tc_tfilter_get_kind; + nm_tc_tfilter_get_parent; + nm_tc_tfilter_get_type; + nm_tc_tfilter_new; + nm_tc_tfilter_ref; + nm_tc_tfilter_set_handle; + nm_tc_tfilter_unref; + nm_team_link_watcher_arp_ping_flags_get_type; + nm_team_link_watcher_dup; + nm_team_link_watcher_equal; + nm_team_link_watcher_get_delay_down; + nm_team_link_watcher_get_delay_up; + nm_team_link_watcher_get_flags; + nm_team_link_watcher_get_init_wait; + nm_team_link_watcher_get_interval; + nm_team_link_watcher_get_missed_max; + nm_team_link_watcher_get_name; + nm_team_link_watcher_get_source_host; + nm_team_link_watcher_get_target_host; + nm_team_link_watcher_get_type; + nm_team_link_watcher_new_arp_ping; + nm_team_link_watcher_new_ethtool; + nm_team_link_watcher_new_nsna_ping; + nm_team_link_watcher_ref; + nm_team_link_watcher_unref; + nm_utils_tc_action_from_str; + nm_utils_tc_action_to_str; + nm_utils_tc_qdisc_from_str; + nm_utils_tc_qdisc_to_str; + nm_utils_tc_tfilter_from_str; + nm_utils_tc_tfilter_to_str; +} libnm_1_10_0; + +libnm_1_10_14 { + #nm_setting_connection_get_mdns@libnm_1_10_14; + #nm_setting_connection_mdns_get_type@libnm_1_10_14; +} libnm_1_10_2; + +libnm_1_12_0 { +global: + nm_checkpoint_create_flags_get_type; + nm_checkpoint_get_created; + nm_checkpoint_get_devices; + nm_checkpoint_get_rollback_timeout; + nm_checkpoint_get_type; + nm_client_checkpoint_adjust_rollback_timeout; + nm_client_checkpoint_adjust_rollback_timeout_finish; + nm_client_checkpoint_create; + nm_client_checkpoint_create_finish; + nm_client_checkpoint_destroy; + nm_client_checkpoint_destroy_finish; + nm_client_checkpoint_rollback; + nm_client_checkpoint_rollback_finish; + nm_client_get_checkpoints; + nm_connection_get_setting_tc_config; + nm_device_ip_tunnel_get_flags; + nm_device_wifi_get_last_scan; + nm_ip_tunnel_flags_get_type; + nm_remote_connection_get_filename; + nm_remote_connection_get_flags; + nm_setting_connection_get_mdns; + nm_setting_connection_mdns_get_type; + nm_setting_ip_tunnel_get_flags; + nm_setting_ip6_config_get_dhcp_duid; + nm_setting_macsec_get_send_sci; + nm_setting_vpn_get_data_keys; + nm_setting_vpn_get_secret_keys; + nm_setting_wireless_security_get_fils; + nm_setting_wireless_security_fils_get_type; + nm_setting_wireless_get_wake_on_wlan; + nm_setting_wireless_wake_on_wlan_get_type; + nm_settings_connection_flags_get_type; + nm_utils_get_timestamp_msec; + nm_vpn_service_plugin_shutdown; +} libnm_1_10_0; + +libnm_1_12_2 { + nm_connection_get_setting_ovs_bridge; + nm_connection_get_setting_ovs_interface; + nm_connection_get_setting_ovs_patch; + nm_connection_get_setting_ovs_port; + nm_connection_get_setting_tun; + nm_device_ovs_bridge_get_slaves; + nm_device_ovs_port_get_slaves; +} libnm_1_12_0; + +libnm_1_14_0 { +global: + nm_connection_multi_connect_get_type; + nm_device_6lowpan_get_type; + nm_device_wireguard_get_fwmark; + nm_device_wireguard_get_listen_port; + nm_device_wireguard_get_public_key; + nm_device_wireguard_get_type; + nm_device_wpan_get_type; + nm_setting_6lowpan_get_type; + nm_setting_connection_get_llmnr; + nm_setting_connection_get_multi_connect; + nm_setting_connection_llmnr_get_type; + nm_setting_ethtool_clear_features; + nm_setting_ethtool_get_feature; + nm_setting_ethtool_get_type; + nm_setting_ethtool_new; + nm_setting_ethtool_set_feature; + nm_setting_match_add_interface_name; + nm_setting_match_clear_interface_names; + nm_setting_match_get_interface_name; + nm_setting_match_get_interface_names; + nm_setting_match_get_num_interface_names; + nm_setting_match_get_type; + nm_setting_match_remove_interface_name; + nm_setting_match_remove_interface_name_by_value; + nm_setting_sriov_add_vf; + nm_setting_sriov_clear_vfs; + nm_setting_sriov_get_autoprobe_drivers; + nm_setting_sriov_get_num_vfs; + nm_setting_sriov_get_total_vfs; + nm_setting_sriov_get_type; + nm_setting_sriov_get_vf; + nm_setting_sriov_new; + nm_setting_sriov_remove_vf; + nm_setting_sriov_remove_vf_by_index; + nm_setting_wpan_get_type; + nm_sriov_vf_add_vlan; + nm_sriov_vf_dup; + nm_sriov_vf_equal; + nm_sriov_vf_get_attribute; + nm_sriov_vf_get_attribute_names; + nm_sriov_vf_get_index; + nm_sriov_vf_get_type; + nm_sriov_vf_get_vlan_ids; + nm_sriov_vf_get_vlan_protocol; + nm_sriov_vf_get_vlan_qos; + nm_sriov_vf_new; + nm_sriov_vf_ref; + nm_sriov_vf_remove_vlan; + nm_sriov_vf_set_attribute; + nm_sriov_vf_set_vlan_protocol; + nm_sriov_vf_set_vlan_qos; + nm_sriov_vf_unref; + nm_sriov_vf_vlan_protocol_get_type; + nm_ternary_get_type; + nm_utils_sriov_vf_from_str; + nm_utils_sriov_vf_to_str; +} libnm_1_12_0; + +libnm_1_14_8 { + nm_ethtool_optname_is_feature; +} libnm_1_14_0; + +libnm_1_16_0 { +global: + nm_client_add_and_activate_connection2; + nm_client_add_and_activate_connection2_finish; + nm_device_get_connectivity; + nm_device_wifi_p2p_get_hw_address; + nm_device_wifi_p2p_get_peers; + nm_device_wifi_p2p_get_type; + nm_device_wifi_p2p_start_find; + nm_device_wifi_p2p_start_find_finish; + nm_device_wifi_p2p_stop_find; + nm_device_wifi_p2p_stop_find_finish; + nm_setting_wifi_p2p_get_peer; + nm_setting_wifi_p2p_get_type; + nm_setting_wifi_p2p_get_wfd_ies; + nm_setting_wifi_p2p_get_wps_method; + nm_setting_wifi_p2p_new; + nm_setting_wireguard_append_peer; + nm_setting_wireguard_clear_peers; + nm_setting_wireguard_get_fwmark; + nm_setting_wireguard_get_listen_port; + nm_setting_wireguard_get_mtu; + nm_setting_wireguard_get_peer; + nm_setting_wireguard_get_peer_by_public_key; + nm_setting_wireguard_get_peer_routes; + nm_setting_wireguard_get_peers_len; + nm_setting_wireguard_get_private_key; + nm_setting_wireguard_get_private_key_flags; + nm_setting_wireguard_get_type; + nm_setting_wireguard_new; + nm_setting_wireguard_remove_peer; + nm_setting_wireguard_set_peer; + nm_team_link_watcher_get_vlanid; + nm_team_link_watcher_new_arp_ping2; + nm_utils_base64secret_decode; + nm_wifi_p2p_peer_connection_valid; + nm_wifi_p2p_peer_filter_connections; + nm_wifi_p2p_peer_get_flags; + nm_wifi_p2p_peer_get_hw_address; + nm_wifi_p2p_peer_get_last_seen; + nm_wifi_p2p_peer_get_manufacturer; + nm_wifi_p2p_peer_get_model; + nm_wifi_p2p_peer_get_model_number; + nm_wifi_p2p_peer_get_name; + nm_wifi_p2p_peer_get_serial; + nm_wifi_p2p_peer_get_strength; + nm_wifi_p2p_peer_get_type; + nm_wifi_p2p_peer_get_wfd_ies; + nm_wireguard_peer_append_allowed_ip; + nm_wireguard_peer_clear_allowed_ips; + nm_wireguard_peer_cmp; + nm_wireguard_peer_get_allowed_ip; + nm_wireguard_peer_get_allowed_ips_len; + nm_wireguard_peer_get_endpoint; + nm_wireguard_peer_get_persistent_keepalive; + nm_wireguard_peer_get_preshared_key; + nm_wireguard_peer_get_preshared_key_flags; + nm_wireguard_peer_get_public_key; + nm_wireguard_peer_get_type; + nm_wireguard_peer_is_sealed; + nm_wireguard_peer_is_valid; + nm_wireguard_peer_new; + nm_wireguard_peer_new_clone; + nm_wireguard_peer_ref; + nm_wireguard_peer_remove_allowed_ip; + nm_wireguard_peer_seal; + nm_wireguard_peer_set_endpoint; + nm_wireguard_peer_set_persistent_keepalive; + nm_wireguard_peer_set_preshared_key; + nm_wireguard_peer_set_preshared_key_flags; + nm_wireguard_peer_set_public_key; + nm_wireguard_peer_unref; +} libnm_1_14_0; + +libnm_1_18_0 { +global: + nm_bridge_vlan_cmp; + nm_bridge_vlan_from_str; + nm_bridge_vlan_get_type; + nm_bridge_vlan_get_vid_range; + nm_bridge_vlan_is_pvid; + nm_bridge_vlan_is_sealed; + nm_bridge_vlan_is_untagged; + nm_bridge_vlan_new; + nm_bridge_vlan_new_clone; + nm_bridge_vlan_ref; + nm_bridge_vlan_seal; + nm_bridge_vlan_set_pvid; + nm_bridge_vlan_set_untagged; + nm_bridge_vlan_to_str; + nm_bridge_vlan_unref; + nm_ip_routing_rule_as_string_flags_get_type; + nm_ip_routing_rule_cmp; + nm_ip_routing_rule_from_string; + nm_ip_routing_rule_get_action; + nm_ip_routing_rule_get_addr_family; + nm_ip_routing_rule_get_destination_port_end; + nm_ip_routing_rule_get_destination_port_start; + nm_ip_routing_rule_get_from; + nm_ip_routing_rule_get_from_len; + nm_ip_routing_rule_get_fwmark; + nm_ip_routing_rule_get_fwmask; + nm_ip_routing_rule_get_iifname; + nm_ip_routing_rule_get_invert; + nm_ip_routing_rule_get_ipproto; + nm_ip_routing_rule_get_oifname; + nm_ip_routing_rule_get_priority; + nm_ip_routing_rule_get_source_port_end; + nm_ip_routing_rule_get_source_port_start; + nm_ip_routing_rule_get_table; + nm_ip_routing_rule_get_to; + nm_ip_routing_rule_get_to_len; + nm_ip_routing_rule_get_tos; + nm_ip_routing_rule_get_type; + nm_ip_routing_rule_is_sealed; + nm_ip_routing_rule_new; + nm_ip_routing_rule_new_clone; + nm_ip_routing_rule_ref; + nm_ip_routing_rule_seal; + nm_ip_routing_rule_set_action; + nm_ip_routing_rule_set_destination_port; + nm_ip_routing_rule_set_from; + nm_ip_routing_rule_set_fwmark; + nm_ip_routing_rule_set_iifname; + nm_ip_routing_rule_set_invert; + nm_ip_routing_rule_set_ipproto; + nm_ip_routing_rule_set_oifname; + nm_ip_routing_rule_set_priority; + nm_ip_routing_rule_set_source_port; + nm_ip_routing_rule_set_table; + nm_ip_routing_rule_set_to; + nm_ip_routing_rule_set_tos; + nm_ip_routing_rule_to_string; + nm_ip_routing_rule_unref; + nm_ip_routing_rule_validate; + nm_lldp_neighbor_get_attr_value; + nm_setting_bridge_add_vlan; + nm_setting_bridge_clear_vlans; + nm_setting_bridge_get_num_vlans; + nm_setting_bridge_get_vlan; + nm_setting_bridge_get_vlan_default_pvid; + nm_setting_bridge_get_vlan_filtering; + nm_setting_bridge_port_add_vlan; + nm_setting_bridge_port_clear_vlans; + nm_setting_bridge_port_get_num_vlans; + nm_setting_bridge_port_get_vlan; + nm_setting_bridge_port_remove_vlan; + nm_setting_bridge_port_remove_vlan_by_vid; + nm_setting_bridge_remove_vlan; + nm_setting_bridge_remove_vlan_by_vid; + nm_setting_ip_config_add_routing_rule; + nm_setting_ip_config_clear_routing_rules; + nm_setting_ip_config_get_num_routing_rules; + nm_setting_ip_config_get_routing_rule; + nm_setting_ip_config_remove_routing_rule; + nm_tc_qdisc_get_attribute; + nm_tc_qdisc_get_attribute_names; + nm_tc_qdisc_set_attribute; +} libnm_1_16_0; + +libnm_1_20_0 { +global: + nm_client_add_connection2; + nm_client_add_connection2_finish; + nm_client_connectivity_check_get_uri; + nm_device_modem_get_apn; + nm_device_modem_get_device_id; + nm_device_modem_get_operator_code; + nm_ip_routing_rule_get_suppress_prefixlength; + nm_ip_routing_rule_set_suppress_prefixlength; + nm_setting_connection_get_wait_device_timeout; + nm_setting_ethtool_get_optnames; + nm_setting_ovs_dpdk_get_devargs; + nm_setting_ovs_dpdk_get_type; + nm_setting_ovs_dpdk_new; + nm_setting_wireguard_get_ip4_auto_default_route; + nm_setting_wireguard_get_ip6_auto_default_route; + nm_settings_add_connection2_flags_get_type; +} libnm_1_18_0; + +libnm_1_20_6 { +global: + nm_setting_802_1x_get_optional; +} libnm_1_20_0; + +libnm_1_22_0 { +global: + nm_client_get_context_busy_watcher; + nm_client_get_dbus_connection; + nm_client_get_dbus_name_owner; + nm_client_get_main_context; + nm_client_get_metered; + nm_client_reload; + nm_client_reload_finish; + nm_device_get_interface_flags; + nm_device_interface_flags_get_type; + nm_dhcp_hostname_flags_get_type; + nm_ip_address_cmp_flags_get_type; + nm_ip_address_cmp_full; + nm_manager_reload_flags_get_type; + nm_setting_gsm_get_auto_config; + nm_setting_ip_config_get_dhcp_hostname_flags; +} libnm_1_20_0; + +libnm_1_22_2 { +global: + nm_client_get_capabilities; +} libnm_1_22_0; + +libnm_1_22_8 { +global: + nm_setting_ip6_config_get_ra_timeout; +} libnm_1_22_2; + +libnm_1_24_0 { +global: + nm_client_dbus_call; + nm_client_dbus_call_finish; + nm_client_dbus_set_property; + nm_client_dbus_set_property_finish; + nm_client_get_instance_flags; + nm_client_get_object_by_path; + nm_client_get_permissions_state; + nm_client_instance_flags_get_type; + nm_device_vrf_get_table; + nm_device_vrf_get_type; + nm_object_get_client; + nm_secret_agent_old_destroy; + nm_secret_agent_old_enable; + nm_secret_agent_old_get_context_busy_watcher; + nm_secret_agent_old_get_dbus_connection; + nm_secret_agent_old_get_dbus_name_owner; + nm_secret_agent_old_get_main_context; + nm_setting_802_1x_get_domain_match; + nm_setting_802_1x_get_phase2_domain_match; + nm_setting_bond_get_option_normalized; + nm_setting_bridge_get_group_address; + nm_setting_bridge_get_multicast_querier; + nm_setting_bridge_get_multicast_query_use_ifaddr; + nm_setting_bridge_get_multicast_router; + nm_setting_bridge_get_vlan_protocol; + nm_setting_bridge_get_vlan_stats_enabled; + nm_setting_vrf_get_table; + nm_setting_vrf_get_type; + nm_setting_vrf_new; +} libnm_1_22_8; + +libnm_1_26_0 { +global: + nm_device_get_path; + nm_ethtool_optname_is_coalesce; + nm_ethtool_optname_is_ring; + nm_setting_bridge_get_multicast_hash_max; + nm_setting_bridge_get_multicast_last_member_count; + nm_setting_bridge_get_multicast_last_member_interval; + nm_setting_bridge_get_multicast_membership_interval; + nm_setting_bridge_get_multicast_querier_interval; + nm_setting_bridge_get_multicast_query_interval; + nm_setting_bridge_get_multicast_query_response_interval; + nm_setting_bridge_get_multicast_startup_query_count; + nm_setting_bridge_get_multicast_startup_query_interval; + nm_setting_connection_get_mud_url; + nm_setting_match_add_driver; + nm_setting_match_add_kernel_command_line; + nm_setting_match_add_path; + nm_setting_match_clear_drivers; + nm_setting_match_clear_kernel_command_lines; + nm_setting_match_clear_paths; + nm_setting_match_get_driver; + nm_setting_match_get_drivers; + nm_setting_match_get_kernel_command_line; + nm_setting_match_get_kernel_command_lines; + nm_setting_match_get_num_drivers; + nm_setting_match_get_num_kernel_command_lines; + nm_setting_match_get_num_paths; + nm_setting_match_get_path; + nm_setting_match_get_paths; + nm_setting_match_remove_driver; + nm_setting_match_remove_driver_by_value; + nm_setting_match_remove_kernel_command_line; + nm_setting_match_remove_kernel_command_line_by_value; + nm_setting_match_remove_path; + nm_setting_match_remove_path_by_value; + nm_setting_option_clear_by_name; + nm_setting_option_get; + nm_setting_option_get_all_names; + nm_setting_option_get_boolean; + nm_setting_option_get_uint32; + nm_setting_option_set; + nm_setting_option_set_boolean; + nm_setting_option_set_uint32; +} libnm_1_24_0; + +libnm_1_26_4 { +global: + nm_setting_ip4_config_get_dhcp_vendor_class_identifier; +} libnm_1_26_0; + +libnm_1_28_0 { +global: + nm_setting_ip_config_add_dhcp_reject_server; + nm_setting_ip_config_clear_dhcp_reject_servers; + nm_setting_ip_config_get_dhcp_reject_servers; + nm_setting_ip_config_remove_dhcp_reject_server; + nm_setting_wireless_get_ap_isolation; +} libnm_1_26_4; + +libnm_1_30_0 { +global: + nm_device_veth_get_type; + nm_keyfile_handler_data_fail_with_error; + nm_keyfile_handler_data_get_context; + nm_keyfile_handler_data_warn_get; + nm_keyfile_handler_flags_get_type; + nm_keyfile_handler_type_get_type; + nm_keyfile_read; + nm_keyfile_warn_severity_get_type; + nm_keyfile_write; + nm_setting_hostname_get_from_dhcp; + nm_setting_hostname_get_from_dns_lookup; + nm_setting_hostname_get_only_from_default; + nm_setting_hostname_get_priority; + nm_setting_hostname_get_type; + nm_setting_ovs_external_ids_check_key; + nm_setting_ovs_external_ids_check_val; + nm_setting_ovs_external_ids_get_data; + nm_setting_ovs_external_ids_get_data_keys; + nm_setting_ovs_external_ids_get_type; + nm_setting_ovs_external_ids_new; + nm_setting_ovs_external_ids_set_data; + nm_setting_veth_get_peer; + nm_setting_veth_get_type; + nm_setting_veth_new; + nm_utils_print; +} libnm_1_28_0; + +libnm_1_32_0 { +global: + nm_ethtool_optname_is_pause; + nm_ip_address_dup; + nm_ip_route_dup; + nm_setting_match_new; + nm_setting_wired_get_accept_all_mac_addresses; +} libnm_1_30_0; + +libnm_1_32_4 { +global: + nm_setting_ip_config_get_required_timeout; +} libnm_1_32_0; diff --git a/src/libnm-client-impl/meson.build b/src/libnm-client-impl/meson.build new file mode 100644 index 00000000..21a01e0b --- /dev/null +++ b/src/libnm-client-impl/meson.build @@ -0,0 +1,231 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +libnm_client_impl_sources = files( + 'nm-access-point.c', + 'nm-active-connection.c', + 'nm-checkpoint.c', + 'nm-client.c', + 'nm-dbus-helpers.c', + 'nm-device-6lowpan.c', + 'nm-device-adsl.c', + 'nm-device-bond.c', + 'nm-device-bridge.c', + 'nm-device-bt.c', + 'nm-device-dummy.c', + 'nm-device-ethernet.c', + 'nm-device-generic.c', + 'nm-device-infiniband.c', + 'nm-device-ip-tunnel.c', + 'nm-device-macsec.c', + 'nm-device-macvlan.c', + 'nm-device-modem.c', + 'nm-device-olpc-mesh.c', + 'nm-device-ovs-bridge.c', + 'nm-device-ovs-interface.c', + 'nm-device-ovs-port.c', + 'nm-device-ppp.c', + 'nm-device-team.c', + 'nm-device-tun.c', + 'nm-device-veth.c', + 'nm-device-vlan.c', + 'nm-device-vrf.c', + 'nm-device-vxlan.c', + 'nm-device-wifi-p2p.c', + 'nm-device-wifi.c', + 'nm-device-wimax.c', + 'nm-device-wireguard.c', + 'nm-device-wpan.c', + 'nm-device.c', + 'nm-dhcp-config.c', + 'nm-dhcp4-config.c', + 'nm-dhcp6-config.c', + 'nm-dns-manager.c', + 'nm-ip-config.c', + 'nm-ip4-config.c', + 'nm-ip6-config.c', + 'nm-libnm-utils.c', + 'nm-object.c', + 'nm-remote-connection.c', + 'nm-secret-agent-old.c', + 'nm-vpn-connection.c', + 'nm-vpn-editor.c', + 'nm-vpn-plugin-old.c', + 'nm-vpn-service-plugin.c', + 'nm-wifi-p2p-peer.c', + 'nm-wimax-nsp.c', +) + +libnm_client_impl = static_library( + 'nm-client-impl', + sources: libnm_client_impl_sources + libnm_client_public_enum_sources, + dependencies: [ + libnmdbus_dep, + libnm_core_public_dep, + libnm_client_public_dep, + libudev_dep, + glib_dep, + ], +) + +linker_script = join_paths(meson.current_source_dir(), 'libnm.ver') + +libnm = shared_library( + 'nm', + version: libnm_version, + dependencies: [ + libnm_core_public_dep, + libnm_client_public_dep, + libudev_dep, + glib_dep, + ], + link_whole: [ + libnm_client_impl, + libnm_core_aux_intern, + libnm_core_impl, + libnm_crypto, + libnm_base, + libnm_udev_aux, + libnm_systemd_shared, + libnm_log_null, + libnm_glib_aux, + libnm_std_aux, + libnmdbus, + libc_siphash, + ], + link_args: '-Wl,--version-script,@0@'.format(linker_script), + link_depends: linker_script, + install: true, +) + +libnm_dep = declare_dependency( + dependencies: [ + libnm_core_public_dep, + libnm_client_public_dep, + glib_dep, + ], + link_with: libnm, +) + +pkg.generate( + libraries: libnm, + version: nm_version, + name: libnm_name, + description: 'Convenience library for clients of NetworkManager', + filebase: libnm_name, + subdirs: libnm_name, + requires: 'gio-2.0', + variables: [ + 'exec_prefix=${prefix}', + 'vpnservicedir=' + join_paths('${prefix}', 'lib', nm_name, 'VPN'), + ], +) + +if enable_tests + test( + 'check-local-exports-' + libnm_name, + check_exports, + args: [libnm.full_path(), linker_script], + ) +endif + +if enable_introspection + + libnm_gir = gnome.generate_gir( + libnm, + sources: libnm_core_settings_sources + libnm_core_impl_sources + libnm_core_public_enum_sources + libnm_core_headers + libnm_client_impl_sources + libnm_client_headers + libnm_client_public_enum_sources + [nm_version_macro_header], + includes: 'Gio-2.0', + include_directories: [ + libnm_core_public_inc, + libnm_client_public_inc, + src_inc, + top_inc, + ], + nsversion: nm_gir_version, + namespace: 'NM', + identifier_prefix: nm_id_prefix, + symbol_prefix: nm_id_prefix.to_lower(), + header: 'NetworkManager.h', + export_packages: libnm_name, + extra_args: [ + '-DNETWORKMANAGER_COMPILATION', + ], + install: true, + ) + + infos = [ 'dbus', 'nmcli', 'keyfile' ] + if enable_ifcfg_rh + infos += [ 'ifcfg-rh' ] + endif + foreach info: infos + t = custom_target( + 'nm-propery-infos-' + info + '.xml', + input: libnm_core_settings_sources, + output: 'nm-propery-infos-' + info + '.xml', + command: [ + python.path(), + join_paths(meson.source_root(), 'tools', 'generate-docs-nm-property-infos.py'), + info, + '@OUTPUT@', + '@INPUT@' + ], + ) + + # meson 0.47 doesn't support non-static keys for dicts + # nor extending dicts incrementally. Workaround. + if info == 'dbus' + nm_property_infos_xml_dbus = t + elif info == 'keyfile' + nm_property_infos_xml_keyfile = t + elif info == 'ifcfg-rh' + nm_property_infos_xml_ifcfg_rh = t + elif info == 'nmcli' + nm_property_infos_xml_nmcli = t + else + assert(false) + endif + endforeach + if enable_ifcfg_rh + nm_property_infos_xml = { + 'dbus': nm_property_infos_xml_dbus, + 'keyfile': nm_property_infos_xml_keyfile, + 'nmcli': nm_property_infos_xml_nmcli, + 'ifcfg-rh': nm_property_infos_xml_ifcfg_rh, + } + else + nm_property_infos_xml = { + 'dbus': nm_property_infos_xml_dbus, + 'keyfile': nm_property_infos_xml_keyfile, + 'nmcli': nm_property_infos_xml_nmcli, + } + endif + + gi_typelib_path = run_command('printenv', 'GI_TYPELIB_PATH').stdout() + if gi_typelib_path != '' + gi_typelib_path = ':' + gi_typelib_path + endif + gi_typelib_path = meson.current_build_dir() + gi_typelib_path + + ld_library_path = run_command('printenv', 'LD_LIBRARY_PATH').stdout() + if ld_library_path != '' + ld_library_path = ':' + ld_library_path + endif + ld_library_path = meson.current_build_dir() + ld_library_path + + nm_settings_docs_xml_gir = custom_target( + 'nm-settings-docs-gir.xml', + input: libnm_gir[0], + output: 'nm-settings-docs-gir.xml', + command: [ + 'env', + 'GI_TYPELIB_PATH=' + gi_typelib_path, + 'LD_LIBRARY_PATH=' + ld_library_path, + python.path(), + join_paths(meson.source_root(), 'tools', 'generate-docs-nm-settings-docs-gir.py'), + '--lib-path', meson.current_build_dir(), + '--gir', '@INPUT@', + '--output', '@OUTPUT@' + ], + depends: libnm_gir, + ) + +endif diff --git a/src/libnm-client-impl/nm-access-point.c b/src/libnm-client-impl/nm-access-point.c new file mode 100644 index 00000000..3d12aa45 --- /dev/null +++ b/src/libnm-client-impl/nm-access-point.c @@ -0,0 +1,654 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2011 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-access-point.h" + +#include <linux/if_ether.h> + +#include "nm-connection.h" +#include "nm-setting-connection.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" +#include "nm-utils.h" + +#include "nm-dbus-interface.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMAccessPoint, + PROP_FLAGS, + PROP_WPA_FLAGS, + PROP_RSN_FLAGS, + PROP_SSID, + PROP_FREQUENCY, + PROP_HW_ADDRESS, + PROP_MODE, + PROP_MAX_BITRATE, + PROP_STRENGTH, + PROP_BSSID, + PROP_LAST_SEEN, ); + +typedef struct { + GBytes *ssid; + char * bssid; + guint32 flags; + guint32 wpa_flags; + guint32 rsn_flags; + guint32 frequency; + guint32 mode; + guint32 max_bitrate; + gint32 last_seen; + guint8 strength; +} NMAccessPointPrivate; + +struct _NMAccessPoint { + NMObject parent; + NMAccessPointPrivate _priv; +}; + +struct _NMAccessPointClass { + NMObjectClass parent; +}; + +G_DEFINE_TYPE(NMAccessPoint, nm_access_point, NM_TYPE_OBJECT) + +#define NM_ACCESS_POINT_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMAccessPoint, NM_IS_ACCESS_POINT, NMObject) + +/*****************************************************************************/ + +/** + * nm_access_point_get_flags: + * @ap: a #NMAccessPoint + * + * Gets the flags of the access point. + * + * Returns: the flags + **/ +NM80211ApFlags +nm_access_point_get_flags(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), NM_802_11_AP_FLAGS_NONE); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->flags; +} + +/** + * nm_access_point_get_wpa_flags: + * @ap: a #NMAccessPoint + * + * Gets the WPA (version 1) flags of the access point. + * + * Returns: the WPA flags + **/ +NM80211ApSecurityFlags +nm_access_point_get_wpa_flags(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), NM_802_11_AP_SEC_NONE); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->wpa_flags; +} + +/** + * nm_access_point_get_rsn_flags: + * @ap: a #NMAccessPoint + * + * Gets the RSN (Robust Secure Network, ie WPA version 2) flags of the access + * point. + * + * Returns: the RSN flags + **/ +NM80211ApSecurityFlags +nm_access_point_get_rsn_flags(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), NM_802_11_AP_SEC_NONE); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->rsn_flags; +} + +/** + * nm_access_point_get_ssid: + * @ap: a #NMAccessPoint + * + * Gets the SSID of the access point. + * + * Returns: (transfer none): the #GBytes containing the SSID, or %NULL if the + * SSID is unknown. + **/ +GBytes * +nm_access_point_get_ssid(NMAccessPoint *ap) +{ + NMAccessPointPrivate *priv; + + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), NULL); + + priv = NM_ACCESS_POINT_GET_PRIVATE(ap); + nm_assert(!priv->ssid || g_bytes_get_size(priv->ssid) > 0); + return priv->ssid; +} + +/** + * nm_access_point_get_frequency: + * @ap: a #NMAccessPoint + * + * Gets the frequency of the access point in MHz. + * + * Returns: the frequency in MHz + **/ +guint32 +nm_access_point_get_frequency(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), 0); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->frequency; +} + +/** + * nm_access_point_get_bssid: + * @ap: a #NMAccessPoint + * + * Gets the Basic Service Set ID (BSSID) of the Wi-Fi access point. + * + * Returns: the BSSID of the access point. This is an internal string and must + * not be modified or freed. + **/ +const char * +nm_access_point_get_bssid(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), NULL); + + return _nml_coerce_property_str_not_empty(NM_ACCESS_POINT_GET_PRIVATE(ap)->bssid); +} + +/** + * nm_access_point_get_mode: + * @ap: a #NMAccessPoint + * + * Gets the mode of the access point. + * + * Returns: the mode + **/ +NM80211Mode +nm_access_point_get_mode(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), 0); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->mode; +} + +/** + * nm_access_point_get_max_bitrate: + * @ap: a #NMAccessPoint + * + * Gets the maximum bit rate of the access point in kbit/s. + * + * Returns: the maximum bit rate (kbit/s) + **/ +guint32 +nm_access_point_get_max_bitrate(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), 0); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->max_bitrate; +} + +/** + * nm_access_point_get_strength: + * @ap: a #NMAccessPoint + * + * Gets the current signal strength of the access point as a percentage. + * + * Returns: the signal strength (0 to 100) + **/ +guint8 +nm_access_point_get_strength(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), 0); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->strength; +} + +/** + * nm_access_point_get_last_seen: + * @ap: a #NMAccessPoint + * + * Returns the timestamp (in CLOCK_BOOTTIME seconds) for the last time the + * access point was found in scan results. A value of -1 means the access + * point has not been found in a scan. + * + * Returns: the last seen time in seconds + * + * Since: 1.2 + **/ +int +nm_access_point_get_last_seen(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), -1); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->last_seen; +} +NM_BACKPORT_SYMBOL(libnm_1_0_6, int, nm_access_point_get_last_seen, (NMAccessPoint * ap), (ap)); + +/** + * nm_access_point_connection_valid: + * @ap: an #NMAccessPoint to validate @connection against + * @connection: an #NMConnection to validate against @ap + * + * Validates a given connection against a given Wi-Fi access point to ensure that + * the connection may be activated with that AP. The connection must match the + * @ap's SSID, (if given) BSSID, and other attributes like security settings, + * channel, band, etc. + * + * Returns: %TRUE if the connection may be activated with this Wi-Fi AP, + * %FALSE if it cannot be. + **/ +gboolean +nm_access_point_connection_valid(NMAccessPoint *ap, NMConnection *connection) +{ + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity *s_wsec; + const char * ctype, *ap_bssid; + GBytes * setting_ssid; + GBytes * ap_ssid; + const char * setting_bssid; + const char * setting_mode; + NM80211Mode ap_mode; + const char * setting_band; + guint32 ap_freq, setting_chan, ap_chan; + + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), FALSE); + g_return_val_if_fail(NM_IS_CONNECTION(connection), FALSE); + + s_con = nm_connection_get_setting_connection(connection); + if (!s_con) + return FALSE; + + ctype = nm_setting_connection_get_connection_type(s_con); + if (!ctype || !nm_streq(ctype, NM_SETTING_WIRELESS_SETTING_NAME)) + return FALSE; + + s_wifi = nm_connection_get_setting_wireless(connection); + if (!s_wifi) + return FALSE; + + /* SSID checks */ + ap_ssid = nm_access_point_get_ssid(ap); + if (!ap_ssid) + return FALSE; + setting_ssid = nm_setting_wireless_get_ssid(s_wifi); + if (!setting_ssid || !g_bytes_equal(ap_ssid, setting_ssid)) + return FALSE; + + /* BSSID checks */ + ap_bssid = nm_access_point_get_bssid(ap); + if (!ap_bssid) + return FALSE; + setting_bssid = nm_setting_wireless_get_bssid(s_wifi); + if (setting_bssid) { + guint8 c[ETH_ALEN]; + + if (!nm_utils_hwaddr_aton(ap_bssid, c, ETH_ALEN) + || !nm_utils_hwaddr_matches(c, ETH_ALEN, setting_bssid, -1)) + return FALSE; + } + + /* Mode */ + ap_mode = nm_access_point_get_mode(ap); + if (ap_mode == NM_802_11_MODE_UNKNOWN) + return FALSE; + setting_mode = nm_setting_wireless_get_mode(s_wifi); + if (setting_mode && ap_mode) { + if (!strcmp(setting_mode, "infrastructure") && (ap_mode != NM_802_11_MODE_INFRA)) + return FALSE; + if (!strcmp(setting_mode, "adhoc") && (ap_mode != NM_802_11_MODE_ADHOC)) + return FALSE; + /* Hotspot never matches against APs as it's a device-specific mode. */ + if (!strcmp(setting_mode, "ap")) + return FALSE; + } + + /* Band and Channel/Frequency */ + ap_freq = nm_access_point_get_frequency(ap); + if (ap_freq) { + setting_band = nm_setting_wireless_get_band(s_wifi); + if (g_strcmp0(setting_band, "a") == 0) { + if (ap_freq < 4915 || ap_freq > 5825) + return FALSE; + } else if (g_strcmp0(setting_band, "bg") == 0) { + if (ap_freq < 2412 || ap_freq > 2484) + return FALSE; + } + + setting_chan = nm_setting_wireless_get_channel(s_wifi); + if (setting_chan) { + ap_chan = nm_utils_wifi_freq_to_channel(ap_freq); + if (setting_chan != ap_chan) + return FALSE; + } + } + + s_wsec = nm_connection_get_setting_wireless_security(connection); + if (!nm_setting_wireless_ap_security_compatible(s_wifi, + s_wsec, + nm_access_point_get_flags(ap), + nm_access_point_get_wpa_flags(ap), + nm_access_point_get_rsn_flags(ap), + ap_mode)) + return FALSE; + + return TRUE; +} + +/** + * nm_access_point_filter_connections: + * @ap: an #NMAccessPoint to filter connections for + * @connections: (element-type NMConnection): an array of #NMConnections to + * filter + * + * Filters a given array of connections for a given #NMAccessPoint object and + * returns connections which may be activated with the access point. Any + * returned connections will match the @ap's SSID and (if given) BSSID and + * other attributes like security settings, channel, etc. + * + * To obtain the list of connections that are compatible with this access point, + * use nm_client_get_connections() and then filter the returned list for a given + * #NMDevice using nm_device_filter_connections() and finally filter that list + * with this function. + * + * Returns: (transfer full) (element-type NMConnection): an array of + * #NMConnections that could be activated with the given @ap. The array should + * be freed with g_ptr_array_unref() when it is no longer required. + * + * WARNING: the transfer annotation for this function may not work correctly + * with bindings. See https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/305. + * You can filter the list yourself with nm_access_point_connection_valid(). + **/ +GPtrArray * +nm_access_point_filter_connections(NMAccessPoint *ap, const GPtrArray *connections) +{ + GPtrArray *filtered; + guint i; + + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), NULL); + + if (!connections) + return NULL; + + filtered = g_ptr_array_new_with_free_func(g_object_unref); + for (i = 0; i < connections->len; i++) { + NMConnection *candidate = connections->pdata[i]; + + if (nm_access_point_connection_valid(ap, candidate)) + g_ptr_array_add(filtered, g_object_ref(candidate)); + } + + return filtered; +} + +/*****************************************************************************/ + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_hw_address(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMAccessPoint * self = NM_ACCESS_POINT(dbobj->nmobj); + NMAccessPointPrivate *priv = NM_ACCESS_POINT_GET_PRIVATE(self); + + g_free(priv->bssid); + priv->bssid = value ? g_variant_dup_string(value, NULL) : 0u; + _notify(self, PROP_HW_ADDRESS); + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/*****************************************************************************/ + +static void +nm_access_point_init(NMAccessPoint *ap) +{ + NM_ACCESS_POINT_GET_PRIVATE(ap)->last_seen = -1; +} + +static void +finalize(GObject *object) +{ + NMAccessPointPrivate *priv = NM_ACCESS_POINT_GET_PRIVATE(object); + + if (priv->ssid) + g_bytes_unref(priv->ssid); + g_free(priv->bssid); + + G_OBJECT_CLASS(nm_access_point_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMAccessPoint *ap = NM_ACCESS_POINT(object); + + switch (prop_id) { + case PROP_FLAGS: + g_value_set_flags(value, nm_access_point_get_flags(ap)); + break; + case PROP_WPA_FLAGS: + g_value_set_flags(value, nm_access_point_get_wpa_flags(ap)); + break; + case PROP_RSN_FLAGS: + g_value_set_flags(value, nm_access_point_get_rsn_flags(ap)); + break; + case PROP_SSID: + g_value_set_boxed(value, nm_access_point_get_ssid(ap)); + break; + case PROP_FREQUENCY: + g_value_set_uint(value, nm_access_point_get_frequency(ap)); + break; + case PROP_HW_ADDRESS: + g_value_set_string(value, nm_access_point_get_bssid(ap)); + break; + case PROP_BSSID: + g_value_set_string(value, nm_access_point_get_bssid(ap)); + break; + case PROP_MODE: + g_value_set_enum(value, nm_access_point_get_mode(ap)); + break; + case PROP_MAX_BITRATE: + g_value_set_uint(value, nm_access_point_get_max_bitrate(ap)); + break; + case PROP_STRENGTH: + g_value_set_uchar(value, nm_access_point_get_strength(ap)); + break; + case PROP_LAST_SEEN: + g_value_set_int(value, nm_access_point_get_last_seen(ap)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_accesspoint = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_ACCESS_POINT, + nm_access_point_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_U("Flags", PROP_FLAGS, NMAccessPoint, _priv.flags), + NML_DBUS_META_PROPERTY_INIT_U("Frequency", PROP_FREQUENCY, NMAccessPoint, _priv.frequency), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + PROP_BSSID, + "s", + _notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_I("LastSeen", PROP_LAST_SEEN, NMAccessPoint, _priv.last_seen), + NML_DBUS_META_PROPERTY_INIT_U("MaxBitrate", + PROP_MAX_BITRATE, + NMAccessPoint, + _priv.max_bitrate), + NML_DBUS_META_PROPERTY_INIT_U("Mode", PROP_MODE, NMAccessPoint, _priv.mode), + NML_DBUS_META_PROPERTY_INIT_U("RsnFlags", PROP_RSN_FLAGS, NMAccessPoint, _priv.rsn_flags), + NML_DBUS_META_PROPERTY_INIT_AY("Ssid", PROP_SSID, NMAccessPoint, _priv.ssid), + NML_DBUS_META_PROPERTY_INIT_Y("Strength", PROP_STRENGTH, NMAccessPoint, _priv.strength), + NML_DBUS_META_PROPERTY_INIT_U("WpaFlags", + PROP_WPA_FLAGS, + NMAccessPoint, + _priv.wpa_flags), ), ); + +static void +nm_access_point_class_init(NMAccessPointClass *ap_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(ap_class); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + /** + * NMAccessPoint:flags: + * + * The flags of the access point. + **/ + obj_properties[PROP_FLAGS] = g_param_spec_flags(NM_ACCESS_POINT_FLAGS, + "", + "", + NM_TYPE_802_11_AP_FLAGS, + NM_802_11_AP_FLAGS_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:wpa-flags: + * + * The WPA flags of the access point. + **/ + obj_properties[PROP_WPA_FLAGS] = g_param_spec_flags(NM_ACCESS_POINT_WPA_FLAGS, + "", + "", + NM_TYPE_802_11_AP_SECURITY_FLAGS, + NM_802_11_AP_SEC_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:rsn-flags: + * + * The RSN flags of the access point. + **/ + obj_properties[PROP_RSN_FLAGS] = g_param_spec_flags(NM_ACCESS_POINT_RSN_FLAGS, + "", + "", + NM_TYPE_802_11_AP_SECURITY_FLAGS, + NM_802_11_AP_SEC_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:ssid: + * + * The SSID of the access point, or %NULL if it is not known. + **/ + obj_properties[PROP_SSID] = g_param_spec_boxed(NM_ACCESS_POINT_SSID, + "", + "", + G_TYPE_BYTES, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:frequency: + * + * The frequency of the access point. + **/ + obj_properties[PROP_FREQUENCY] = g_param_spec_uint(NM_ACCESS_POINT_FREQUENCY, + "", + "", + 0, + 10000, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:bssid: + * + * The BSSID of the access point. + **/ + obj_properties[PROP_BSSID] = g_param_spec_string(NM_ACCESS_POINT_BSSID, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:hw-address: + * + * Alias for #NMAccessPoint:bssid. + * + * Deprecated: 1.0: Use #NMAccessPoint:bssid. + **/ + obj_properties[PROP_HW_ADDRESS] = + g_param_spec_string(NM_ACCESS_POINT_HW_ADDRESS, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:mode: + * + * The mode of the access point; either "infrastructure" (a central + * coordinator of the wireless network allowing clients to connect) or + * "ad-hoc" (a network with no central controller). + **/ + obj_properties[PROP_MODE] = g_param_spec_enum(NM_ACCESS_POINT_MODE, + "", + "", + NM_TYPE_802_11_MODE, + NM_802_11_MODE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:max-bitrate: + * + * The maximum bit rate of the access point in kbit/s. + **/ + obj_properties[PROP_MAX_BITRATE] = g_param_spec_uint(NM_ACCESS_POINT_MAX_BITRATE, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:strength: + * + * The current signal strength of the access point. + **/ + obj_properties[PROP_STRENGTH] = g_param_spec_uchar(NM_ACCESS_POINT_STRENGTH, + "", + "", + 0, + G_MAXUINT8, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:last-seen: + * + * The timestamp (in CLOCK_BOOTTIME seconds) for the last time the + * access point was found in scan results. A value of -1 means the + * access point has not been found in a scan. + * + * Since: 1.2 + **/ + obj_properties[PROP_LAST_SEEN] = g_param_spec_int(NM_ACCESS_POINT_LAST_SEEN, + "", + "", + -1, + G_MAXINT, + -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_accesspoint); +} diff --git a/src/libnm-client-impl/nm-active-connection.c b/src/libnm-client-impl/nm-active-connection.c new file mode 100644 index 00000000..aa96219c --- /dev/null +++ b/src/libnm-client-impl/nm-active-connection.c @@ -0,0 +1,839 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2014 Red Hat, Inc. + * Copyright (C) 2008 Novell, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-active-connection.h" + +#include "nm-dbus-interface.h" +#include "nm-object-private.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-device.h" +#include "nm-connection.h" +#include "nm-vpn-connection.h" +#include "nm-dbus-helpers.h" +#include "nm-dhcp4-config.h" +#include "nm-dhcp6-config.h" +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" +#include "nm-remote-connection.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMActiveConnection, + PROP_CONNECTION, + PROP_ID, + PROP_UUID, + PROP_TYPE, + PROP_SPECIFIC_OBJECT_PATH, + PROP_DEVICES, + PROP_STATE, + PROP_STATE_FLAGS, + PROP_DEFAULT, + PROP_IP4_CONFIG, + PROP_DHCP4_CONFIG, + PROP_DEFAULT6, + PROP_IP6_CONFIG, + PROP_DHCP6_CONFIG, + PROP_VPN, + PROP_MASTER, ); + +enum { + STATE_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +enum { + PROPERTY_O_IDX_CONNECTION, + PROPERTY_O_IDX_MASTER, + PROPERTY_O_IDX_IP4_CONFIG, + PROPERTY_O_IDX_IP6_CONFIG, + PROPERTY_O_IDX_DHCP4_CONFIG, + PROPERTY_O_IDX_DHCP6_CONFIG, + _PROPERTY_O_IDX_NUM, +}; + +typedef struct _NMActiveConnectionPrivate { + NMLDBusPropertyO property_o[_PROPERTY_O_IDX_NUM]; + NMLDBusPropertyAO devices; + NMRefString * specific_object_path; + char * id; + char * uuid; + char * type; + + guint32 state; + guint32 state_flags; + + bool is_default; + bool is_default6; + bool is_vpn; + + guint32 reason; +} NMActiveConnectionPrivate; + +G_DEFINE_TYPE(NMActiveConnection, nm_active_connection, NM_TYPE_OBJECT); + +#define NM_ACTIVE_CONNECTION_GET_PRIVATE(self) \ + _NM_GET_PRIVATE_PTR(self, NMActiveConnection, NM_IS_ACTIVE_CONNECTION, NMObject) + +/*****************************************************************************/ + +/** + * nm_active_connection_get_connection: + * @connection: a #NMActiveConnection + * + * Gets the #NMRemoteConnection associated with @connection. + * + * Returns: (transfer none): the #NMRemoteConnection which this + * #NMActiveConnection is an active instance of. + **/ +NMRemoteConnection * +nm_active_connection_get_connection(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return nml_dbus_property_o_get_obj( + &NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->property_o[PROPERTY_O_IDX_CONNECTION]); +} + +/** + * nm_active_connection_get_id: + * @connection: a #NMActiveConnection + * + * Gets the #NMConnection's ID. + * + * Returns: the ID of the #NMConnection that backs the #NMActiveConnection. + * This is the internal string used by the connection, and must not be modified. + **/ +const char * +nm_active_connection_get_id(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return _nml_coerce_property_str_not_empty(NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->id); +} + +/** + * nm_active_connection_get_uuid: + * @connection: a #NMActiveConnection + * + * Gets the #NMConnection's UUID. + * + * Returns: the UUID of the #NMConnection that backs the #NMActiveConnection. + * This is the internal string used by the connection, and must not be modified. + **/ +const char * +nm_active_connection_get_uuid(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return _nml_coerce_property_str_not_empty(NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->uuid); +} + +/** + * nm_active_connection_get_connection_type: + * @connection: a #NMActiveConnection + * + * Gets the #NMConnection's type. + * + * Returns: the type of the #NMConnection that backs the #NMActiveConnection. + * This is the internal string used by the connection, and must not be modified. + **/ +const char * +nm_active_connection_get_connection_type(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return _nml_coerce_property_str_not_empty(NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->type); +} + +/** + * nm_active_connection_get_specific_object_path: + * @connection: a #NMActiveConnection + * + * Gets the path of the "specific object" used at activation. + * + * Currently, there is no single method that will allow you to automatically turn + * this into an appropriate #NMObject; you need to know what kind of object it + * is based on other information. (Eg, if @connection corresponds to a Wi-Fi + * connection, then the specific object will be an #NMAccessPoint, and you can + * resolve it with nm_device_wifi_get_access_point_by_path().) + * + * Returns: the specific object's D-Bus path. This is the internal string used + * by the connection, and must not be modified. + **/ +const char * +nm_active_connection_get_specific_object_path(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return _nml_coerce_property_object_path( + NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->specific_object_path); +} + +/** + * nm_active_connection_get_devices: + * @connection: a #NMActiveConnection + * + * Gets the #NMDevices used for the active connections. + * + * Returns: (element-type NMDevice): the #GPtrArray containing #NMDevices. + * This is the internal copy used by the connection, and must not be modified. + **/ +const GPtrArray * +nm_active_connection_get_devices(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->devices); +} + +/** + * nm_active_connection_get_state: + * @connection: a #NMActiveConnection + * + * Gets the active connection's state. + * + * Returns: the state + **/ +NMActiveConnectionState +nm_active_connection_get_state(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NM_ACTIVE_CONNECTION_STATE_UNKNOWN); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->state; +} + +/** + * nm_active_connection_get_state_flags: + * @connection: a #NMActiveConnection + * + * Gets the active connection's state flags. + * + * Returns: the state flags + * + * Since: 1.10 + **/ +NMActivationStateFlags +nm_active_connection_get_state_flags(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NM_ACTIVATION_STATE_FLAG_NONE); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->state_flags; +} + +/** + * nm_active_connection_get_state_reason: + * @connection: a #NMActiveConnection + * + * Gets the reason for active connection's state. + * + * Returns: the reason + * + * Since: 1.8 + **/ +NMActiveConnectionStateReason +nm_active_connection_get_state_reason(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), + NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->reason; +} + +/** + * nm_active_connection_get_default: + * @connection: a #NMActiveConnection + * + * Whether the active connection is the default IPv4 one (that is, is used for + * the default IPv4 route and DNS information). + * + * Returns: %TRUE if the active connection is the default IPv4 connection + **/ +gboolean +nm_active_connection_get_default(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), FALSE); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->is_default; +} + +/** + * nm_active_connection_get_ip4_config: + * @connection: an #NMActiveConnection + * + * Gets the current IPv4 #NMIPConfig associated with the #NMActiveConnection. + * + * Returns: (transfer none): the IPv4 #NMIPConfig, or %NULL if the connection is + * not in the %NM_ACTIVE_CONNECTION_STATE_ACTIVATED state. + **/ +NMIPConfig * +nm_active_connection_get_ip4_config(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return nml_dbus_property_o_get_obj( + &NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->property_o[PROPERTY_O_IDX_IP4_CONFIG]); +} + +/** + * nm_active_connection_get_dhcp4_config: + * @connection: an #NMActiveConnection + * + * Gets the current IPv4 #NMDhcpConfig (if any) associated with the + * #NMActiveConnection. + * + * Returns: (transfer none): the IPv4 #NMDhcpConfig, or %NULL if the connection + * does not use DHCP, or is not in the %NM_ACTIVE_CONNECTION_STATE_ACTIVATED + * state. + **/ +NMDhcpConfig * +nm_active_connection_get_dhcp4_config(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return nml_dbus_property_o_get_obj( + &NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->property_o[PROPERTY_O_IDX_DHCP4_CONFIG]); +} + +/** + * nm_active_connection_get_default6: + * @connection: a #NMActiveConnection + * + * Whether the active connection is the default IPv6 one (that is, is used for + * the default IPv6 route and DNS information). + * + * Returns: %TRUE if the active connection is the default IPv6 connection + **/ +gboolean +nm_active_connection_get_default6(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), FALSE); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->is_default6; +} + +/** + * nm_active_connection_get_ip6_config: + * @connection: an #NMActiveConnection + * + * Gets the current IPv6 #NMIPConfig associated with the #NMActiveConnection. + * + * Returns: (transfer none): the IPv6 #NMIPConfig, or %NULL if the connection is + * not in the %NM_ACTIVE_CONNECTION_STATE_ACTIVATED state. + **/ +NMIPConfig * +nm_active_connection_get_ip6_config(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return nml_dbus_property_o_get_obj( + &NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->property_o[PROPERTY_O_IDX_IP6_CONFIG]); +} + +/** + * nm_active_connection_get_dhcp6_config: + * @connection: an #NMActiveConnection + * + * Gets the current IPv6 #NMDhcpConfig (if any) associated with the + * #NMActiveConnection. + * + * Returns: (transfer none): the IPv6 #NMDhcpConfig, or %NULL if the connection + * does not use DHCPv6, or is not in the %NM_ACTIVE_CONNECTION_STATE_ACTIVATED + * state. + **/ +NMDhcpConfig * +nm_active_connection_get_dhcp6_config(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return nml_dbus_property_o_get_obj( + &NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->property_o[PROPERTY_O_IDX_DHCP6_CONFIG]); +} + +/** + * nm_active_connection_get_vpn: + * @connection: a #NMActiveConnection + * + * Whether the active connection is a VPN connection. + * + * Returns: %TRUE if the active connection is a VPN connection + **/ +gboolean +nm_active_connection_get_vpn(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), FALSE); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->is_vpn; +} + +/** + * nm_active_connection_get_master: + * @connection: a #NMActiveConnection + * + * Gets the master #NMDevice of the connection. + * + * Returns: (transfer none): the master #NMDevice of the #NMActiveConnection. + **/ +NMDevice * +nm_active_connection_get_master(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return nml_dbus_property_o_get_obj( + &NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->property_o[PROPERTY_O_IDX_MASTER]); +} + +/*****************************************************************************/ + +static void +_notify_event_state_changed(NMClient *client, NMClientNotifyEventWithPtr *notify_event) +{ + gs_unref_object NMActiveConnection *self = notify_event->user_data; + NMActiveConnectionPrivate * priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(self); + + /* we expose here the value cache in @priv. In practice, this is the same + * value as we received from the signal. In the unexpected case where they + * differ, the cached value of the current instance would still be more correct. */ + g_signal_emit(self, signals[STATE_CHANGED], 0, (guint) priv->state, (guint) priv->reason); +} + +void +_nm_active_connection_state_changed_commit(NMActiveConnection *self, guint32 state, guint32 reason) +{ + NMClient * client; + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(self); + + client = _nm_object_get_client(self); + + if (priv->state != state) { + priv->state = state; + _nm_client_queue_notify_object(client, self, obj_properties[PROP_STATE]); + } + + priv->reason = reason; + + _nm_client_notify_event_queue_with_ptr(client, + NM_CLIENT_NOTIFY_EVENT_PRIO_GPROP + 1, + _notify_event_state_changed, + g_object_ref(self)); +} +/*****************************************************************************/ + +static gboolean +is_ready(NMObject *nmobj) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(nmobj); + + /* Usually, we don't want to expose our NMObject instances until they are fully initialized. + * For NMRemoteSetting this means to wait until GetSettings() returns. + * + * Note that most object types reference each other (directly or indirectly). E.g. the + * NMActiveConnection refers to the NMRemoteConnection and the NMDevice instance. So, + * we don't want to hide them too long, otherwise basically the entire set of objects + * will be hidden until they are all initialized. So, usually, when a NMObject references + * objects that are not yet initialized, that reference will just be NULL but the object + * will be considered ready already. + * + * For NMActiveConnection referencing a NMRemoteConnection don't do that. Here we wait for the + * NMRemoteConnection to be ready as well. This is somewhat arbitrary special casing, but + * the effect is that when nm_client_add_and_activate*() returns, the NMActiveConnection already + * references a initialized NMRemoteConnection. + */ + if (!nml_dbus_property_o_is_ready_fully(&priv->property_o[PROPERTY_O_IDX_CONNECTION])) + return FALSE; + + return NM_OBJECT_CLASS(nm_active_connection_parent_class)->is_ready(nmobj); +} + +/*****************************************************************************/ + +static void +nm_active_connection_init(NMActiveConnection *self) +{ + NMActiveConnectionPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_ACTIVE_CONNECTION, NMActiveConnectionPrivate); + + self->_priv = priv; +} + +static void +finalize(GObject *object) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(object); + + g_free(priv->id); + g_free(priv->uuid); + g_free(priv->type); + nm_ref_string_unref(priv->specific_object_path); + + G_OBJECT_CLASS(nm_active_connection_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMActiveConnection *self = NM_ACTIVE_CONNECTION(object); + + switch (prop_id) { + case PROP_CONNECTION: + g_value_set_object(value, nm_active_connection_get_connection(self)); + break; + case PROP_ID: + g_value_set_string(value, nm_active_connection_get_id(self)); + break; + case PROP_UUID: + g_value_set_string(value, nm_active_connection_get_uuid(self)); + break; + case PROP_TYPE: + g_value_set_string(value, nm_active_connection_get_connection_type(self)); + break; + case PROP_SPECIFIC_OBJECT_PATH: + g_value_set_string(value, nm_active_connection_get_specific_object_path(self)); + break; + case PROP_DEVICES: + g_value_take_boxed(value, + _nm_utils_copy_object_array(nm_active_connection_get_devices(self))); + break; + case PROP_STATE: + g_value_set_enum(value, nm_active_connection_get_state(self)); + break; + case PROP_STATE_FLAGS: + g_value_set_uint(value, nm_active_connection_get_state_flags(self)); + break; + case PROP_DEFAULT: + g_value_set_boolean(value, nm_active_connection_get_default(self)); + break; + case PROP_IP4_CONFIG: + g_value_set_object(value, nm_active_connection_get_ip4_config(self)); + break; + case PROP_DHCP4_CONFIG: + g_value_set_object(value, nm_active_connection_get_dhcp4_config(self)); + break; + case PROP_DEFAULT6: + g_value_set_boolean(value, nm_active_connection_get_default6(self)); + break; + case PROP_IP6_CONFIG: + g_value_set_object(value, nm_active_connection_get_ip6_config(self)); + break; + case PROP_DHCP6_CONFIG: + g_value_set_object(value, nm_active_connection_get_dhcp6_config(self)); + break; + case PROP_VPN: + g_value_set_boolean(value, nm_active_connection_get_vpn(self)); + break; + case PROP_MASTER: + g_value_set_object(value, nm_active_connection_get_master(self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_connection_active = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_ACTIVE_CONNECTION, + nm_active_connection_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_10, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_O_PROP("Connection", + PROP_CONNECTION, + NMActiveConnectionPrivate, + property_o[PROPERTY_O_IDX_CONNECTION], + nm_remote_connection_get_type), + NML_DBUS_META_PROPERTY_INIT_B("Default", + PROP_DEFAULT, + NMActiveConnectionPrivate, + is_default), + NML_DBUS_META_PROPERTY_INIT_B("Default6", + PROP_DEFAULT6, + NMActiveConnectionPrivate, + is_default6), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Devices", + PROP_DEVICES, + NMActiveConnectionPrivate, + devices, + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Dhcp4Config", + PROP_DHCP4_CONFIG, + NMActiveConnectionPrivate, + property_o[PROPERTY_O_IDX_DHCP4_CONFIG], + nm_dhcp4_config_get_type), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Dhcp6Config", + PROP_DHCP6_CONFIG, + NMActiveConnectionPrivate, + property_o[PROPERTY_O_IDX_DHCP6_CONFIG], + nm_dhcp6_config_get_type), + NML_DBUS_META_PROPERTY_INIT_S("Id", PROP_ID, NMActiveConnectionPrivate, id), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Ip4Config", + PROP_IP4_CONFIG, + NMActiveConnectionPrivate, + property_o[PROPERTY_O_IDX_IP4_CONFIG], + nm_ip4_config_get_type), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Ip6Config", + PROP_IP6_CONFIG, + NMActiveConnectionPrivate, + property_o[PROPERTY_O_IDX_IP6_CONFIG], + nm_ip6_config_get_type), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Master", + PROP_MASTER, + NMActiveConnectionPrivate, + property_o[PROPERTY_O_IDX_MASTER], + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_O("SpecificObject", + PROP_SPECIFIC_OBJECT_PATH, + NMActiveConnectionPrivate, + specific_object_path), + NML_DBUS_META_PROPERTY_INIT_U("State", PROP_STATE, NMActiveConnectionPrivate, state), + NML_DBUS_META_PROPERTY_INIT_U("StateFlags", + PROP_STATE_FLAGS, + NMActiveConnectionPrivate, + state_flags), + NML_DBUS_META_PROPERTY_INIT_S("Type", PROP_TYPE, NMActiveConnectionPrivate, type), + NML_DBUS_META_PROPERTY_INIT_S("Uuid", PROP_UUID, NMActiveConnectionPrivate, uuid), + NML_DBUS_META_PROPERTY_INIT_B("Vpn", PROP_VPN, NMActiveConnectionPrivate, is_vpn), ), + .base_struct_offset = G_STRUCT_OFFSET(NMActiveConnection, _priv), ); + +static void +nm_active_connection_class_init(NMActiveConnectionClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof(NMActiveConnectionPrivate)); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + nm_object_class->is_ready = is_ready; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_INDIRECT(nm_object_class, NMActiveConnection); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_N(nm_object_class, + NMActiveConnectionPrivate, + property_o); + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMActiveConnectionPrivate, devices); + + /** + * NMActiveConnection:connection: + * + * The connection that this is an active instance of. + **/ + obj_properties[PROP_CONNECTION] = + g_param_spec_object(NM_ACTIVE_CONNECTION_CONNECTION, + "", + "", + NM_TYPE_REMOTE_CONNECTION, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:id: + * + * The active connection's ID + **/ + obj_properties[PROP_ID] = g_param_spec_string(NM_ACTIVE_CONNECTION_ID, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:uuid: + * + * The active connection's UUID + **/ + obj_properties[PROP_UUID] = g_param_spec_string(NM_ACTIVE_CONNECTION_UUID, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:type: + * + * The active connection's type + **/ + obj_properties[PROP_TYPE] = g_param_spec_string(NM_ACTIVE_CONNECTION_TYPE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:specific-object-path: + * + * The path to the "specific object" of the active connection; see + * nm_active_connection_get_specific_object_path() for more details. + **/ + obj_properties[PROP_SPECIFIC_OBJECT_PATH] = + g_param_spec_string(NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT_PATH, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:devices: (type GPtrArray(NMDevice)) + * + * The devices of the active connection. + **/ + obj_properties[PROP_DEVICES] = g_param_spec_boxed(NM_ACTIVE_CONNECTION_DEVICES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:state: + * + * The state of the active connection. + **/ + obj_properties[PROP_STATE] = g_param_spec_enum(NM_ACTIVE_CONNECTION_STATE, + "", + "", + NM_TYPE_ACTIVE_CONNECTION_STATE, + NM_ACTIVE_CONNECTION_STATE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:state-flags: + * + * The state flags of the active connection. + * + * Since: 1.10 + **/ + obj_properties[PROP_STATE_FLAGS] = g_param_spec_uint(NM_ACTIVE_CONNECTION_STATE_FLAGS, + "", + "", + 0, + G_MAXUINT32, + NM_ACTIVATION_STATE_FLAG_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:default: + * + * Whether the active connection is the default IPv4 one. + **/ + obj_properties[PROP_DEFAULT] = g_param_spec_boolean(NM_ACTIVE_CONNECTION_DEFAULT, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:ip4-config: + * + * The IPv4 #NMIPConfig of the connection. + **/ + obj_properties[PROP_IP4_CONFIG] = + g_param_spec_object(NM_ACTIVE_CONNECTION_IP4_CONFIG, + "", + "", + NM_TYPE_IP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:dhcp4-config: + * + * The IPv4 #NMDhcpConfig of the connection. + **/ + obj_properties[PROP_DHCP4_CONFIG] = + g_param_spec_object(NM_ACTIVE_CONNECTION_DHCP4_CONFIG, + "", + "", + NM_TYPE_DHCP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:default6: + * + * Whether the active connection is the default IPv6 one. + **/ + obj_properties[PROP_DEFAULT6] = g_param_spec_boolean(NM_ACTIVE_CONNECTION_DEFAULT6, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:ip6-config: + * + * The IPv6 #NMIPConfig of the connection. + **/ + obj_properties[PROP_IP6_CONFIG] = + g_param_spec_object(NM_ACTIVE_CONNECTION_IP6_CONFIG, + "", + "", + NM_TYPE_IP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:dhcp6-config: + * + * The IPv6 #NMDhcpConfig of the connection. + **/ + obj_properties[PROP_DHCP6_CONFIG] = + g_param_spec_object(NM_ACTIVE_CONNECTION_DHCP6_CONFIG, + "", + "", + NM_TYPE_DHCP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:vpn: + * + * Whether the active connection is a VPN connection. + **/ + obj_properties[PROP_VPN] = g_param_spec_boolean(NM_ACTIVE_CONNECTION_VPN, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:master: + * + * The master device if one exists. + **/ + obj_properties[PROP_MASTER] = g_param_spec_object(NM_ACTIVE_CONNECTION_MASTER, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_connection_active); + + /* TODO: the state reason should also be exposed as a property in libnm's NMActiveConnection, + * like done for NMDevice's state reason. */ + + /* TODO: the D-Bus API should also expose the state-reason as a property instead of + * a "StateChanged" signal. Like done for Device's "StateReason". */ + + /** + * NMActiveConnection::state-changed: + * @active_connection: the source #NMActiveConnection + * @state: the new state number (#NMActiveConnectionState) + * @reason: the state change reason (#NMActiveConnectionStateReason) + */ + signals[STATE_CHANGED] = g_signal_new("state-changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 2, + G_TYPE_UINT, + G_TYPE_UINT); +} diff --git a/src/libnm-client-impl/nm-checkpoint.c b/src/libnm-client-impl/nm-checkpoint.c new file mode 100644 index 00000000..c1f6b092 --- /dev/null +++ b/src/libnm-client-impl/nm-checkpoint.c @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-checkpoint.h" + +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-dbus-interface.h" +#include "nm-device.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_DEVICES, PROP_CREATED, PROP_ROLLBACK_TIMEOUT, ); + +typedef struct { + NMLDBusPropertyAO devices; + gint64 created; + guint32 rollback_timeout; +} NMCheckpointPrivate; + +struct _NMCheckpoint { + NMObject parent; + NMCheckpointPrivate _priv; +}; + +struct _NMCheckpointClass { + NMObjectClass parent; +}; + +G_DEFINE_TYPE(NMCheckpoint, nm_checkpoint, NM_TYPE_OBJECT) + +#define NM_CHECKPOINT_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMCheckpoint, NM_IS_CHECKPOINT, NMObject) + +/*****************************************************************************/ + +/** + * nm_checkpoint_get_devices: + * @checkpoint: a #NMCheckpoint + * + * The devices that are part of this checkpoint. + * + * Returns: (element-type NMDevice): the devices list. + * + * Since: 1.12 + **/ +const GPtrArray * +nm_checkpoint_get_devices(NMCheckpoint *checkpoint) +{ + g_return_val_if_fail(NM_IS_CHECKPOINT(checkpoint), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_CHECKPOINT_GET_PRIVATE(checkpoint)->devices); +} + +/** + * nm_checkpoint_get_created: + * @checkpoint: a #NMCheckpoint + * + * Gets the timestamp (in CLOCK_BOOTTIME milliseconds) of checkpoint creation. + * + * Use nm_utils_get_timestamp_msec() to obtain current time value suitable for + * comparing to this value. + * + * Returns: the timestamp of checkpoint creation. + * + * Since: 1.12 + **/ +gint64 +nm_checkpoint_get_created(NMCheckpoint *checkpoint) +{ + g_return_val_if_fail(NM_IS_CHECKPOINT(checkpoint), 0); + + return NM_CHECKPOINT_GET_PRIVATE(checkpoint)->created; +} + +/** + * nm_checkpoint_get_rollback_timeout: + * @checkpoint: a #NMCheckpoint + * + * Gets the timeout in seconds for automatic rollback. + * + * Returns: the rollback timeout. + * + * Since: 1.12 + **/ +guint32 +nm_checkpoint_get_rollback_timeout(NMCheckpoint *checkpoint) +{ + g_return_val_if_fail(NM_IS_CHECKPOINT(checkpoint), 0); + + return NM_CHECKPOINT_GET_PRIVATE(checkpoint)->rollback_timeout; +} + +/*****************************************************************************/ + +static void +nm_checkpoint_init(NMCheckpoint *checkpoint) +{} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMCheckpoint * checkpoint = NM_CHECKPOINT(object); + NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE(checkpoint); + + switch (prop_id) { + case PROP_DEVICES: + g_value_take_boxed(value, + _nm_utils_copy_object_array(nm_checkpoint_get_devices(checkpoint))); + break; + case PROP_CREATED: + g_value_set_int64(value, priv->created); + break; + case PROP_ROLLBACK_TIMEOUT: + g_value_set_uint(value, priv->rollback_timeout); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_checkpoint = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_CHECKPOINT, + nm_checkpoint_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_X("Created", PROP_CREATED, NMCheckpoint, _priv.created), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Devices", + PROP_DEVICES, + NMCheckpoint, + _priv.devices, + nm_device_get_type, + .is_always_ready = TRUE), + NML_DBUS_META_PROPERTY_INIT_U("RollbackTimeout", + PROP_ROLLBACK_TIMEOUT, + NMCheckpoint, + _priv.rollback_timeout), ), ); + +static void +nm_checkpoint_class_init(NMCheckpointClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMCheckpoint); + + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMCheckpointPrivate, devices); + + /** + * NMCheckpoint:devices: (type GPtrArray(NMDevice)) + * + * The devices that are part of this checkpoint. + * + * Since: 1.12 + **/ + obj_properties[PROP_DEVICES] = g_param_spec_boxed(NM_CHECKPOINT_DEVICES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMCheckpoint:created: + * + * The timestamp (in CLOCK_BOOTTIME milliseconds) of checkpoint creation. + * + * Since: 1.12 + **/ + obj_properties[PROP_CREATED] = g_param_spec_int64(NM_CHECKPOINT_CREATED, + "", + "", + G_MININT64, + G_MAXINT64, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMCheckpoint:rollback-timeout: + * + * Timeout in seconds for automatic rollback, or zero. + * + * Since: 1.12 + **/ + obj_properties[PROP_ROLLBACK_TIMEOUT] = + g_param_spec_uint(NM_CHECKPOINT_ROLLBACK_TIMEOUT, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_checkpoint); +} diff --git a/src/libnm-client-impl/nm-client.c b/src/libnm-client-impl/nm-client.c new file mode 100644 index 00000000..4cbd56dc --- /dev/null +++ b/src/libnm-client-impl/nm-client.c @@ -0,0 +1,8834 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2018 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-client.h" + +#include <libudev.h> + +#include "libnm-std-aux/c-list-util.h" +#include "libnm-glib-aux/nm-c-list.h" +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "libnm-core-aux-intern/nm-common-macros.h" + +#include "nm-access-point.h" +#include "nm-active-connection.h" +#include "nm-checkpoint.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-dbus-helpers.h" +#include "nm-device-6lowpan.h" +#include "nm-device-adsl.h" +#include "nm-device-bond.h" +#include "nm-device-bridge.h" +#include "nm-device-bt.h" +#include "nm-device-dummy.h" +#include "nm-device-ethernet.h" +#include "nm-device-generic.h" +#include "nm-device-infiniband.h" +#include "nm-device-ip-tunnel.h" +#include "nm-device-macsec.h" +#include "nm-device-macvlan.h" +#include "nm-device-modem.h" +#include "nm-device-olpc-mesh.h" +#include "nm-device-ovs-bridge.h" +#include "nm-device-ovs-interface.h" +#include "nm-device-ovs-port.h" +#include "nm-device-ppp.h" +#include "nm-device-team.h" +#include "nm-device-tun.h" +#include "nm-device-vlan.h" +#include "nm-device-vxlan.h" +#include "nm-device-wifi-p2p.h" +#include "nm-device-wifi.h" +#include "nm-device-wireguard.h" +#include "nm-device-wpan.h" +#include "nm-dhcp-config.h" +#include "nm-dhcp4-config.h" +#include "nm-dhcp6-config.h" +#include "nm-dns-manager.h" +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" +#include "nm-object-private.h" +#include "nm-remote-connection.h" +#include "nm-utils.h" +#include "nm-vpn-connection.h" +#include "nm-wifi-p2p-peer.h" + +/*****************************************************************************/ + +NM_CACHED_QUARK_FCN("nm-context-busy-watcher", nm_context_busy_watcher_quark); + +static void +_context_busy_watcher_attach_integration_source_cb(gpointer data, GObject *where_the_object_was) +{ + nm_g_source_destroy_and_unref(data); +} + +void +nm_context_busy_watcher_integrate_source(GMainContext *outer_context, + GMainContext *inner_context, + GObject * context_busy_watcher) +{ + GSource *source; + + nm_assert(outer_context); + nm_assert(inner_context); + nm_assert(outer_context != inner_context); + nm_assert(G_IS_OBJECT(context_busy_watcher)); + + source = nm_utils_g_main_context_create_integrate_source(inner_context); + g_source_attach(source, outer_context); + + /* The problem is... + * + * NMClient is associated with a GMainContext, just like its underlying GDBusConnection + * also queues signals and callbacks on that main context. During operation, NMClient + * will schedule async operations which will return asynchronously on that GMainContext. + * + * Note that depending on whether NMClient got initialized synchronously or asynchronously, + * it has an internal priv->dbus_context that is different from the outer priv->main_context. + * However, the problem is in both cases. + * + * So, as long as there are pending D-Bus calls, the GMainContext is referenced and kept alive. + * When NMClient gets destroyed, the pending calls get cancelled, but the async callback are still + * scheduled to return. + * That means, the main context stays alive until it gets iterated long enough so that all pending + * operations are completed. + * + * Note that pending operations don't keep NMClient alive, so NMClient can already be gone by + * then, but the user still should iterate the main context long enough to process the (cancelled) + * callbacks... at least, if the user cares about whether the remaining memory and file descriptors + * of the GMainContext can be reclaimed. + * + * In hindsight, maybe pending references should kept NMClient alive. But then NMClient would + * need a special "shutdown()" API that the user must invoke, because unrefing would no longer + * be enough to ensure a shutdown (imagine a situation where NMClient receives a constant flow + * of "CheckPermissions" signals, which keeps retriggering an async request). Anyway, we cannot + * add such a shutdown API now, as it would break client's expectations that they can just unref + * the NMClient to destroy it. + * + * So, we allow NMClient to unref, but the user is advised to keep iterating the main context. + * But for how long? Here comes nm_client_get_context_busy_watcher() into play. The user may + * subscribe a weak pointer to that instance and should keep iterating as long as the object + * exists. + * + * Now, back to synchronous initialization: here we have the internal priv->dbus_context context. + * We also cannot remove that context right away, instead we need to keep it integrated in the + * caller's priv->main_context as long as we have pending calls: that is, as long as the + * context-busy-watcher is alive. + */ + + g_object_weak_ref(context_busy_watcher, + _context_busy_watcher_attach_integration_source_cb, + source); +} + +/*****************************************************************************/ + +typedef struct { + /* It is quite wasteful to require 2 pointers per property (of an instance) only to track whether + * the property got changed. But it's convenient! */ + CList changed_prop_lst; + + GVariant *prop_data_value; +} NMLDBusObjPropData; + +typedef struct { + CList iface_lst; + union { + const NMLDBusMetaIface *meta; + NMRefString * name; + } dbus_iface; + + CList changed_prop_lst_head; + + /* We also keep track of non-well known interfaces. The presence of a D-Bus interface + * is what makes a D-Bus alive or not. As we should track all D-Bus objects, we also + * need to track whether there are any interfaces on it -- even if we otherwise don't + * care about the interface. */ + bool dbus_iface_is_wellknown : 1; + + /* if TRUE, the interface is about to be removed. */ + bool iface_removed : 1; + + bool nmobj_checked : 1; + bool nmobj_compatible : 1; + + NMLDBusObjPropData prop_datas[]; +} NMLDBusObjIfaceData; + +/* The dbus_path must be the first element, so when we hash the object by the dbus_path, + * we also can lookup the object by only having a NMRefString at hand + * using nm_pdirect_hash()/nm_pdirect_equal(). */ +G_STATIC_ASSERT(G_STRUCT_OFFSET(NMLDBusObject, dbus_path) == 0); + +typedef void (*NMLDBusObjWatchNotifyFcn)(NMClient *client, gpointer obj_watcher); + +struct _NMLDBusObjWatcher { + NMLDBusObject *dbobj; + struct { + CList watcher_lst; + NMLDBusObjWatchNotifyFcn notify_fcn; + } _priv; +}; + +typedef struct { + NMLDBusObjWatcher parent; + gpointer user_data; +} NMLDBusObjWatcherWithPtr; + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMClient, + PROP_DBUS_CONNECTION, + PROP_DBUS_NAME_OWNER, + PROP_VERSION, + PROP_INSTANCE_FLAGS, + PROP_STATE, + PROP_STARTUP, + PROP_NM_RUNNING, + PROP_NETWORKING_ENABLED, + PROP_WIRELESS_ENABLED, + PROP_WIRELESS_HARDWARE_ENABLED, + PROP_WWAN_ENABLED, + PROP_WWAN_HARDWARE_ENABLED, + PROP_WIMAX_ENABLED, + PROP_WIMAX_HARDWARE_ENABLED, + PROP_ACTIVE_CONNECTIONS, + PROP_CONNECTIVITY, + PROP_CONNECTIVITY_CHECK_URI, + PROP_CONNECTIVITY_CHECK_AVAILABLE, + PROP_CONNECTIVITY_CHECK_ENABLED, + PROP_PRIMARY_CONNECTION, + PROP_ACTIVATING_CONNECTION, + PROP_DEVICES, + PROP_ALL_DEVICES, + PROP_CONNECTIONS, + PROP_HOSTNAME, + PROP_CAN_MODIFY, + PROP_METERED, + PROP_DNS_MODE, + PROP_DNS_RC_MANAGER, + PROP_DNS_CONFIGURATION, + PROP_CHECKPOINTS, + PROP_CAPABILITIES, + PROP_PERMISSIONS_STATE, ); + +enum { + DEVICE_ADDED, + DEVICE_REMOVED, + ANY_DEVICE_ADDED, + ANY_DEVICE_REMOVED, + PERMISSION_CHANGED, + CONNECTION_ADDED, + CONNECTION_REMOVED, + ACTIVE_CONNECTION_ADDED, + ACTIVE_CONNECTION_REMOVED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +enum { + PROPERTY_O_IDX_NM_ACTIVATING_CONNECTION = 0, + PROPERTY_O_IDX_NM_PRIMAY_CONNECTION, + _PROPERTY_O_IDX_NM_NUM, +}; + +enum { + PROPERTY_AO_IDX_DEVICES = 0, + PROPERTY_AO_IDX_ALL_DEVICES, + PROPERTY_AO_IDX_ACTIVE_CONNECTIONS, + PROPERTY_AO_IDX_CHECKPOINTS, + _PROPERTY_AO_IDX_NM_NUM, +}; + +typedef struct { + struct udev * udev; + GMainContext * main_context; + GMainContext * dbus_context; + GObject * context_busy_watcher; + GDBusConnection *dbus_connection; + NMLInitData * init_data; + GHashTable * dbus_objects; + CList obj_changed_lst_head; + GCancellable * name_owner_get_cancellable; + GCancellable * get_managed_objects_cancellable; + + CList queue_notify_lst_head; + CList notify_event_lst_head; + + CList dbus_objects_lst_head_watched_only; + CList dbus_objects_lst_head_on_dbus; + CList dbus_objects_lst_head_with_nmobj_not_ready; + CList dbus_objects_lst_head_with_nmobj_ready; + + NMLDBusObject *dbobj_nm; + NMLDBusObject *dbobj_settings; + NMLDBusObject *dbobj_dns_manager; + + gsize log_call_counter; + + guint8 * permissions; + GCancellable *permissions_cancellable; + + char *name_owner; + guint name_owner_changed_id; + guint dbsid_nm_object_manager; + guint dbsid_dbus_properties_properties_changed; + guint dbsid_nm_settings_connection_updated; + guint dbsid_nm_connection_active_state_changed; + guint dbsid_nm_vpn_connection_state_changed; + guint dbsid_nm_check_permissions; + + NMClientInstanceFlags instance_flags : 3; + + NMTernary permissions_state : 3; + + bool instance_flags_constructed : 1; + + bool udev_inited : 1; + bool notify_event_lst_changed : 1; + bool check_dbobj_visible_all : 1; + bool nm_running : 1; + + struct { + NMLDBusPropertyO property_o[_PROPERTY_O_IDX_NM_NUM]; + NMLDBusPropertyAO property_ao[_PROPERTY_AO_IDX_NM_NUM]; + char * connectivity_check_uri; + char * version; + guint32 * capabilities_arr; + gsize capabilities_len; + guint32 connectivity; + guint32 state; + guint32 metered; + bool connectivity_check_available; + bool connectivity_check_enabled; + bool networking_enabled; + bool startup; + bool wireless_enabled; + bool wireless_hardware_enabled; + bool wwan_enabled; + bool wwan_hardware_enabled; + } nm; + + struct { + NMLDBusPropertyAO connections; + char * hostname; + bool can_modify; + } settings; + + struct { + GPtrArray *configuration; + char * mode; + char * rc_manager; + } dns_manager; + +} NMClientPrivate; + +struct _NMClient { + union { + GObject parent; + NMObjectBase obj_base; + }; + NMClientPrivate _priv; +}; + +struct _NMClientClass { + union { + GObjectClass parent; + NMObjectBaseClass obj_base; + }; +}; + +static void nm_client_initable_iface_init(GInitableIface *iface); +static void nm_client_async_initable_iface_init(GAsyncInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE(NMClient, + nm_client, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, nm_client_initable_iface_init); + G_IMPLEMENT_INTERFACE(G_TYPE_ASYNC_INITABLE, + nm_client_async_initable_iface_init);) + +#define NM_CLIENT_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMClient, NM_IS_CLIENT) + +/*****************************************************************************/ + +static void _init_start_check_complete(NMClient *self); + +static void name_owner_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data); + +static void name_owner_get_call(NMClient *self); + +static void _set_nm_running(NMClient *self); + +/*****************************************************************************/ + +static NMRefString *_dbus_path_nm = NULL; +static NMRefString *_dbus_path_settings = NULL; +static NMRefString *_dbus_path_dns_manager = NULL; + +/*****************************************************************************/ + +static NM_UTILS_LOOKUP_STR_DEFINE( + nml_dbus_obj_state_to_string, + NMLDBusObjState, + NM_UTILS_LOOKUP_DEFAULT_WARN("???"), + NM_UTILS_LOOKUP_ITEM(NML_DBUS_OBJ_STATE_UNLINKED, "unlinked"), + NM_UTILS_LOOKUP_ITEM(NML_DBUS_OBJ_STATE_WATCHED_ONLY, "watched-only"), + NM_UTILS_LOOKUP_ITEM(NML_DBUS_OBJ_STATE_ON_DBUS, "on-dbus"), + NM_UTILS_LOOKUP_ITEM(NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY, "not-ready"), + NM_UTILS_LOOKUP_ITEM(NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY, "ready"), ); + +/*****************************************************************************/ + +/** + * nm_client_error_quark: + * + * Registers an error quark for #NMClient if necessary. + * + * Returns: the error quark used for #NMClient errors. + **/ +NM_CACHED_QUARK_FCN("nm-client-error-quark", nm_client_error_quark); + +/*****************************************************************************/ + +NMLInitData * +nml_init_data_new_sync(GCancellable *cancellable, GMainLoop *main_loop, GError **error_location) +{ + NMLInitData *init_data; + + init_data = g_slice_new(NMLInitData); + *init_data = (NMLInitData){ + .cancellable = nm_g_object_ref(cancellable), + .is_sync = TRUE, + .data.sync = + { + .main_loop = main_loop, + .error_location = error_location, + }, + }; + return init_data; +} + +NMLInitData * +nml_init_data_new_async(GCancellable *cancellable, GTask *task_take) +{ + NMLInitData *init_data; + + init_data = g_slice_new(NMLInitData); + *init_data = (NMLInitData){ + .cancellable = nm_g_object_ref(cancellable), + .is_sync = FALSE, + .data.async = + { + .task = g_steal_pointer(&task_take), + }, + }; + return init_data; +} + +void +nml_init_data_return(NMLInitData *init_data, GError *error_take) +{ + nm_assert(init_data); + + nm_clear_pointer(&init_data->cancel_on_idle_source, nm_g_source_destroy_and_unref); + nm_clear_g_signal_handler(init_data->cancellable, &init_data->cancelled_id); + + if (init_data->is_sync) { + if (error_take) + g_propagate_error(init_data->data.sync.error_location, error_take); + g_main_loop_quit(init_data->data.sync.main_loop); + } else { + if (error_take) + g_task_return_error(init_data->data.async.task, error_take); + else + g_task_return_boolean(init_data->data.async.task, TRUE); + g_object_unref(init_data->data.async.task); + } + nm_g_object_unref(init_data->cancellable); + nm_g_slice_free(init_data); +} + +/*****************************************************************************/ + +GError * +_nm_client_new_error_nm_not_running(void) +{ + return g_error_new_literal(NM_CLIENT_ERROR, + NM_CLIENT_ERROR_MANAGER_NOT_RUNNING, + "NetworkManager is not running"); +} + +GError * +_nm_client_new_error_nm_not_cached(void) +{ + return g_error_new_literal(NM_CLIENT_ERROR, + NM_CLIENT_ERROR_FAILED, + "Object is no longer in the client cache"); +} + +static void +_nm_client_dbus_call_simple_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + GAsyncReadyCallback callback; + gpointer callback_user_data; + gs_unref_object GObject *context_busy_watcher = NULL; + gpointer obfuscated_self_ptr; + gpointer log_call_counter_ptr; + + nm_utils_user_data_unpack(user_data, + &callback, + &callback_user_data, + &context_busy_watcher, + &obfuscated_self_ptr, + &log_call_counter_ptr); + + NML_DBUS_LOG(_NML_NMCLIENT_LOG_LEVEL_COERCE(NML_DBUS_LOG_LEVEL_TRACE), + "nmclient[" NM_HASH_OBFUSCATE_PTR_FMT "]: call[%" G_GSIZE_FORMAT "] completed", + (guint64) GPOINTER_TO_SIZE(obfuscated_self_ptr), + GPOINTER_TO_SIZE(log_call_counter_ptr)); + + callback(source, result, callback_user_data); +} + +void +_nm_client_dbus_call_simple(NMClient * self, + GCancellable * cancellable, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + gs_free char * log_str = NULL; + gsize log_call_counter; + + nm_assert(priv->name_owner); + nm_assert(!cancellable || G_IS_CANCELLABLE(cancellable)); + nm_assert(callback); + nm_assert(object_path); + nm_assert(interface_name); + nm_assert(method_name); + nm_assert(parameters); + nm_assert(reply_type); + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); + + log_call_counter = ++priv->log_call_counter; + + NML_NMCLIENT_LOG_T(self, + "call[%" G_GSIZE_FORMAT "] D-Bus method on %s: %s, %s.%s -> %s (%s)", + log_call_counter, + priv->name_owner, + object_path, + interface_name, + method_name, + (const char *) reply_type ?: "???", + parameters ? (log_str = g_variant_print(parameters, TRUE)) : "NULL"); + + g_dbus_connection_call(priv->dbus_connection, + priv->name_owner, + object_path, + interface_name, + method_name, + parameters, + reply_type, + flags, + timeout_msec, + cancellable, + _nm_client_dbus_call_simple_cb, + nm_utils_user_data_pack(callback, + user_data, + g_object_ref(priv->context_busy_watcher), + GSIZE_TO_POINTER(NM_HASH_OBFUSCATE_PTR(self)), + GSIZE_TO_POINTER(log_call_counter))); +} + +void +_nm_client_dbus_call(NMClient * self, + gpointer source_obj, + gpointer source_tag, + GCancellable * cancellable, + GAsyncReadyCallback user_callback, + gpointer user_callback_data, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + GAsyncReadyCallback internal_callback) +{ + NMClientPrivate *priv; + gs_unref_object GTask *task = NULL; + + nm_assert(!source_obj || G_IS_OBJECT(source_obj)); + nm_assert(source_tag); + nm_assert(!cancellable || G_IS_CANCELLABLE(cancellable)); + nm_assert(internal_callback); + nm_assert(object_path); + nm_assert(interface_name); + nm_assert(method_name); + nm_assert(parameters); + nm_assert(reply_type); + + task = nm_g_task_new(source_obj, cancellable, source_tag, user_callback, user_callback_data); + + if (!self) { + nm_g_variant_unref_floating(parameters); + g_task_return_error(task, _nm_client_new_error_nm_not_cached()); + return; + } + + priv = NM_CLIENT_GET_PRIVATE(self); + if (!priv->name_owner) { + nm_g_variant_unref_floating(parameters); + g_task_return_error(task, _nm_client_new_error_nm_not_running()); + return; + } + + _nm_client_dbus_call_simple(self, + cancellable, + object_path, + interface_name, + method_name, + parameters, + reply_type, + flags, + timeout_msec, + internal_callback, + g_steal_pointer(&task)); +} + +GVariant * +_nm_client_dbus_call_sync(NMClient * self, + GCancellable * cancellable, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + gboolean strip_dbus_error, + GError ** error) +{ + NMClientPrivate *priv; + gs_unref_variant GVariant *ret = NULL; + + nm_assert(!cancellable || G_IS_CANCELLABLE(cancellable)); + nm_assert(!error || !*error); + nm_assert(object_path); + nm_assert(interface_name); + nm_assert(method_name); + nm_assert(parameters); + nm_assert(reply_type); + + if (!self) { + nm_g_variant_unref_floating(parameters); + nm_g_set_error_take_lazy(error, _nm_client_new_error_nm_not_cached()); + return NULL; + } + + priv = NM_CLIENT_GET_PRIVATE(self); + if (!priv->name_owner) { + nm_g_variant_unref_floating(parameters); + nm_g_set_error_take_lazy(error, _nm_client_new_error_nm_not_running()); + return NULL; + } + + ret = g_dbus_connection_call_sync(priv->dbus_connection, + priv->name_owner, + object_path, + interface_name, + method_name, + parameters, + reply_type, + flags, + timeout_msec, + cancellable, + error); + if (!ret) { + if (error && strip_dbus_error) + g_dbus_error_strip_remote_error(*error); + return NULL; + } + + return g_steal_pointer(&ret); +} + +gboolean +_nm_client_dbus_call_sync_void(NMClient * self, + GCancellable * cancellable, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + GDBusCallFlags flags, + int timeout_msec, + gboolean strip_dbus_error, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + + ret = _nm_client_dbus_call_sync(self, + cancellable, + object_path, + interface_name, + method_name, + parameters, + G_VARIANT_TYPE("()"), + flags, + timeout_msec, + strip_dbus_error, + error); + return !!ret; +} + +void +_nm_client_set_property_sync_legacy(NMClient * self, + const char *object_path, + const char *interface_name, + const char *property_name, + const char *format_string, + ...) +{ + NMClientPrivate *priv; + GVariant * val; + gs_unref_variant GVariant *ret = NULL; + va_list ap; + + nm_assert(!self || NM_IS_CLIENT(self)); + nm_assert(interface_name); + nm_assert(property_name); + nm_assert(format_string); + + if (!self) + return; + + priv = NM_CLIENT_GET_PRIVATE(self); + if (!priv->name_owner) + return; + + va_start(ap, format_string); + val = g_variant_new_va(format_string, NULL, &ap); + va_end(ap); + + nm_assert(val); + + /* A synchronous D-Bus call that is not cancellable an ignores the return value. + * This function only exists for backward compatibility. */ + ret = g_dbus_connection_call_sync(priv->dbus_connection, + priv->name_owner, + object_path, + DBUS_INTERFACE_PROPERTIES, + "Set", + g_variant_new("(ssv)", interface_name, property_name, val), + NULL, + G_DBUS_CALL_FLAGS_NONE, + 2000, + NULL, + NULL); +} + +/*****************************************************************************/ + +#define _assert_main_context_is_current_source(self, x_context) \ + G_STMT_START \ + { \ + if (NM_MORE_ASSERTS > 0) { \ + GSource *_source = g_main_current_source(); \ + \ + if (_source) { \ + NMClientPrivate *_priv = NM_CLIENT_GET_PRIVATE(self); \ + \ + nm_assert(g_source_get_context(_source) == _priv->x_context); \ + nm_assert(g_main_context_is_owner(_priv->x_context)); \ + } \ + } \ + } \ + G_STMT_END + +#define _assert_main_context_is_current_thread_default(self, x_context) \ + G_STMT_START \ + { \ + if (NM_MORE_ASSERTS > 0) { \ + NMClientPrivate *_priv = NM_CLIENT_GET_PRIVATE(self); \ + \ + nm_assert((g_main_context_get_thread_default() ?: g_main_context_default()) \ + == _priv->x_context); \ + nm_assert(g_main_context_is_owner(_priv->x_context)); \ + } \ + } \ + G_STMT_END + +/*****************************************************************************/ + +void +_nm_client_queue_notify_object(NMClient *self, gpointer nmobj, const GParamSpec *pspec) +{ + NMObjectBase *base; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(NM_IS_OBJECT(nmobj) || NM_IS_CLIENT(nmobj)); + + base = (NMObjectBase *) nmobj; + + if (base->is_disposing) { + /* Don't emit property changed signals once the NMClient + * instance is about to shut down. */ + nm_assert(nmobj == self); + return; + } + + if (c_list_is_empty(&base->queue_notify_lst)) { + c_list_link_tail(&NM_CLIENT_GET_PRIVATE(self)->queue_notify_lst_head, + &base->queue_notify_lst); + g_object_ref(nmobj); + g_object_freeze_notify(nmobj); + } + + if (pspec) + g_object_notify_by_pspec(nmobj, (GParamSpec *) pspec); +} + +/*****************************************************************************/ + +gpointer +_nm_client_notify_event_queue(NMClient * self, + int priority, + NMClientNotifyEventCb callback, + gsize event_size) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + NMClientNotifyEvent *notify_event; + + nm_assert(callback); + nm_assert(event_size > sizeof(NMClientNotifyEvent)); + + notify_event = g_malloc(event_size); + notify_event->priority = priority; + notify_event->callback = callback; + c_list_link_tail(&priv->notify_event_lst_head, ¬ify_event->lst); + priv->notify_event_lst_changed = TRUE; + return notify_event; +} + +NMClientNotifyEventWithPtr * +_nm_client_notify_event_queue_with_ptr(NMClient * self, + int priority, + NMClientNotifyEventWithPtrCb callback, + gpointer user_data) +{ + NMClientNotifyEventWithPtr *notify_event; + + notify_event = _nm_client_notify_event_queue(self, + priority, + (NMClientNotifyEventCb) callback, + sizeof(NMClientNotifyEventWithPtr)); + notify_event->user_data = user_data; + return notify_event; +} + +/*****************************************************************************/ + +typedef struct { + NMClientNotifyEvent parent; + GObject * source; + NMObject * obj; + guint signal_id; +} NMClientNotifyEventObjAddedRemove; + +static void +_nm_client_notify_event_queue_emit_obj_signal_cb(NMClient *self, gpointer notify_event_base) +{ + NMClientNotifyEventObjAddedRemove *notify_event = notify_event_base; + + NML_NMCLIENT_LOG_T( + self, + "[%s] emit \"%s\" signal for %s", + NM_IS_CLIENT(notify_event->source) ? "nmclient" : _nm_object_get_path(notify_event->source), + g_signal_name(notify_event->signal_id), + _nm_object_get_path(notify_event->obj)); + + nm_assert(NM_IS_OBJECT(notify_event->source) || NM_IS_CLIENT(notify_event->source)); + + g_signal_emit(notify_event->source, notify_event->signal_id, 0, notify_event->obj); + + g_object_unref(notify_event->obj); + g_object_unref(notify_event->source); +} + +void +_nm_client_notify_event_queue_emit_obj_signal(NMClient *self, + GObject * source, + NMObject *nmobj, + gboolean is_added /* or else removed */, + int prio_offset, + guint signal_id) +{ + NMClientNotifyEventObjAddedRemove *notify_event; + + nm_assert(prio_offset >= 0); + nm_assert(prio_offset < 20); + nm_assert(NM_IS_OBJECT(source) || NM_IS_CLIENT(source)); + nm_assert(NM_IS_OBJECT(nmobj)); + + if (((NMObjectBase *) source)->is_disposing) { + nm_assert(NM_IS_CLIENT(source)); + return; + } + + notify_event = _nm_client_notify_event_queue( + self, + is_added ? NM_CLIENT_NOTIFY_EVENT_PRIO_AFTER - 20 + prio_offset + : NM_CLIENT_NOTIFY_EVENT_PRIO_BEFORE + 20 - prio_offset, + _nm_client_notify_event_queue_emit_obj_signal_cb, + sizeof(NMClientNotifyEventObjAddedRemove)); + notify_event->source = g_object_ref(source); + notify_event->obj = g_object_ref(nmobj); + notify_event->signal_id = signal_id; +} + +/*****************************************************************************/ + +static int +_nm_client_notify_event_cmp(const CList *a, const CList *b, const void *user_data) +{ + NM_CMP_DIRECT(c_list_entry(a, NMClientNotifyEvent, lst)->priority, + c_list_entry(b, NMClientNotifyEvent, lst)->priority); + return 0; +} + +static void +_nm_client_notify_event_emit_parts(NMClient *self, int max_priority /* included! */) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + NMClientNotifyEvent *notify_event; + + while (TRUE) { + if (priv->notify_event_lst_changed) { + priv->notify_event_lst_changed = FALSE; + c_list_sort(&priv->notify_event_lst_head, _nm_client_notify_event_cmp, NULL); + } + notify_event = c_list_first_entry(&priv->notify_event_lst_head, NMClientNotifyEvent, lst); + if (!notify_event) + return; + if (notify_event->priority > max_priority) + return; + c_list_unlink_stale(¬ify_event->lst); + notify_event->callback(self, notify_event); + g_free(notify_event); + } +} + +static void +_nm_client_notify_event_emit(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + NMObjectBase * base; + + _nm_client_notify_event_emit_parts(self, NM_CLIENT_NOTIFY_EVENT_PRIO_GPROP); + + while ( + (base = c_list_first_entry(&priv->queue_notify_lst_head, NMObjectBase, queue_notify_lst))) { + c_list_unlink(&base->queue_notify_lst); + g_object_thaw_notify(G_OBJECT(base)); + g_object_unref(base); + } + + _nm_client_notify_event_emit_parts(self, G_MAXINT); +} + +/*****************************************************************************/ + +GDBusConnection * +_nm_client_get_dbus_connection(NMClient *self) +{ + return NM_CLIENT_GET_PRIVATE(self)->dbus_connection; +} + +const char * +_nm_client_get_dbus_name_owner(NMClient *self) +{ + return NM_CLIENT_GET_PRIVATE(self)->name_owner; +} + +GMainContext * +_nm_client_get_context_main(NMClient *self) +{ + return NM_CLIENT_GET_PRIVATE(self)->main_context; +} + +GMainContext * +_nm_client_get_context_dbus(NMClient *self) +{ + return NM_CLIENT_GET_PRIVATE(self)->dbus_context; +} + +/** + * nm_client_get_main_context: + * @self: the #NMClient instance + * + * The #NMClient instance is permanently associated with the current + * thread default #GMainContext, referenced the time when the instance + * was created. To receive events, the user must iterate this context + * and can use it to synchronize access to the client. + * + * Note that even after #NMClient instance got destroyed, there might + * still be pending sources registered in the context. That means, to fully + * clean up, the user must continue iterating the context as long as + * the nm_client_get_context_busy_watcher() object is alive. + * + * Returns: (transfer none): the #GMainContext of the client. + * + * Since: 1.22 + */ +GMainContext * +nm_client_get_main_context(NMClient *self) +{ + g_return_val_if_fail(NM_IS_CLIENT(self), NULL); + + return _nm_client_get_context_main(self); +} + +/** + * nm_client_get_context_busy_watcher: + * @self: the NMClient instance. + * + * Returns: (transfer none): a GObject that stays alive as long as there are pending + * D-Bus operations. + * + * NMClient will schedule asynchronous D-Bus requests which will complete on + * the GMainContext associated with the instance. When destroying the NMClient + * instance, those requests are cancelled right away, however their pending requests are + * still outstanding and queued in the GMainContext. These outstanding callbacks + * keep the GMainContext alive. In order to fully release all resources, + * the user must keep iterating the main context until all these callbacks + * are handled. Of course, at this point no more actual callbacks will be invoked + * for the user, those are all cancelled internally. + * + * This just leaves one problem: how long does the user need to keep the + * GMainContext running to ensure everything is cleaned up? The answer is + * this GObject. Subscribe a weak reference to the returned object and keep + * iterating the main context until the object got unreferenced. + * + * Note that after the NMClient instance gets destroyed, all outstanding operations + * will be cancelled right away. That means, the user needs to iterate the #GMainContext + * a bit longer, but it is guaranteed that the cleanup happens soon after. + * + * The way of using the context-busy-watch, is by registering a weak pointer to + * see when it gets destroyed. That means, user code should not take additional + * references on this object to not keep it alive longer. + * + * If you plan to exit the program after releasing the NMClient instance + * you may not need to worry about these "leaks". Also, if you anyway plan to continue + * iterating the #GMainContext afterwards, then you don't need to care when exactly + * NMClient is gone completely. + * + * Since: 1.22 + */ +GObject * +nm_client_get_context_busy_watcher(NMClient *self) +{ + GObject *w; + + g_return_val_if_fail(NM_IS_CLIENT(self), NULL); + + w = NM_CLIENT_GET_PRIVATE(self)->context_busy_watcher; + return g_object_get_qdata(w, nm_context_busy_watcher_quark()) ?: w; +} + +struct udev * +_nm_client_get_udev(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + if (G_UNLIKELY(!priv->udev_inited)) { + priv->udev_inited = TRUE; + /* for testing, we don't want to use udev in libnm. */ + if (!nm_streq0(g_getenv("LIBNM_USE_NO_UDEV"), "1")) + priv->udev = udev_new(); + } + + return priv->udev; +} + +/*****************************************************************************/ + +static void +_ASSERT_dbobj(NMLDBusObject *dbobj, NMClient *self) +{ +#if NM_MORE_ASSERTS > 5 + nm_assert(NM_IS_CLIENT(self)); + nm_assert(NML_IS_DBUS_OBJECT(dbobj)); + nm_assert(dbobj == g_hash_table_lookup(NM_CLIENT_GET_PRIVATE(self)->dbus_objects, dbobj)); +#endif +} + +static NMLDBusObject * +nml_dbus_object_new(NMRefString *dbus_path_take) +{ + NMLDBusObject *dbobj; + + nm_assert(NM_IS_REF_STRING(dbus_path_take)); + + dbobj = g_slice_new(NMLDBusObject); + *dbobj = (NMLDBusObject){ + .dbus_path = g_steal_pointer(&dbus_path_take), + .ref_count = 1, + .dbus_objects_lst = C_LIST_INIT(dbobj->dbus_objects_lst), + .iface_lst_head = C_LIST_INIT(dbobj->iface_lst_head), + .watcher_lst_head = C_LIST_INIT(dbobj->watcher_lst_head), + .obj_changed_lst = C_LIST_INIT(dbobj->obj_changed_lst), + .obj_state = NML_DBUS_OBJ_STATE_UNLINKED, + }; + return dbobj; +} + +NMLDBusObject * +nml_dbus_object_ref(NMLDBusObject *dbobj) +{ + nm_assert(dbobj); + nm_assert(dbobj->ref_count > 0); + + dbobj->ref_count++; + return dbobj; +} + +void +nml_dbus_object_unref(NMLDBusObject *dbobj) +{ + nm_assert(dbobj); + nm_assert(dbobj->ref_count > 0); + + if (--dbobj->ref_count > 0) + return; + + nm_assert(c_list_is_empty(&dbobj->obj_changed_lst)); + nm_assert(c_list_is_empty(&dbobj->iface_lst_head)); + nm_assert(c_list_is_empty(&dbobj->watcher_lst_head)); + nm_assert(!dbobj->nmobj); + + nm_ref_string_unref(dbobj->dbus_path); + nm_g_slice_free(dbobj); +} + +static NMLDBusObjIfaceData * +nml_dbus_object_iface_data_get(NMLDBusObject *dbobj, + const char * dbus_iface_name, + gboolean allow_create) +{ + const NMLDBusMetaIface *meta_iface; + NMLDBusObjIfaceData * db_iface_data; + NMLDBusObjPropData * db_prop_data; + guint count = 0; + guint i; + + nm_assert(NML_IS_DBUS_OBJECT(dbobj)); + nm_assert(dbus_iface_name); + +#if NM_MORE_ASSERTS > 10 + { + gboolean expect_well_known = TRUE; + + /* all well-known interfaces must come first in the list. */ + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + if (db_iface_data->dbus_iface_is_wellknown == expect_well_known) + continue; + nm_assert(expect_well_known); + expect_well_known = FALSE; + } + } +#endif + + meta_iface = nml_dbus_meta_iface_get(dbus_iface_name); + if (meta_iface) { + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + if (!db_iface_data->dbus_iface_is_wellknown) + break; + if (db_iface_data->iface_removed) + continue; + if (db_iface_data->dbus_iface.meta == meta_iface) + return db_iface_data; + count++; + } + } else { + c_list_for_each_entry_prev (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + if (db_iface_data->dbus_iface_is_wellknown) + break; + if (db_iface_data->iface_removed) + continue; + if (nm_streq(db_iface_data->dbus_iface.name->str, dbus_iface_name)) + return db_iface_data; + count++; + } + } + + if (!allow_create) + return NULL; + + if (count > 20) { + /* We track the list of interfaces that an object has in a linked list. + * That is efficient and convenient, if we assume that each object only has a small + * number of interfaces (which very much should be the case). Here, something is very + * odd, maybe there is a bug or the server side is misbehaving. Anyway, error out. */ + return NULL; + } + + db_iface_data = g_malloc( + G_STRUCT_OFFSET(NMLDBusObjIfaceData, prop_datas) + + (meta_iface ? (sizeof(NMLDBusObjPropData) * meta_iface->n_dbus_properties) : 0u)); + if (meta_iface) { + *db_iface_data = (NMLDBusObjIfaceData){ + .dbus_iface.meta = meta_iface, + .dbus_iface_is_wellknown = TRUE, + .changed_prop_lst_head = C_LIST_INIT(db_iface_data->changed_prop_lst_head), + .iface_removed = FALSE, + }; + db_prop_data = &db_iface_data->prop_datas[0]; + for (i = 0; i < meta_iface->n_dbus_properties; i++, db_prop_data++) { + *db_prop_data = (NMLDBusObjPropData){ + .prop_data_value = NULL, + .changed_prop_lst = C_LIST_INIT(db_prop_data->changed_prop_lst), + }; + } + c_list_link_front(&dbobj->iface_lst_head, &db_iface_data->iface_lst); + } else { + /* Intentionally don't initialize the other fields. We are not supposed + * to touch them, and a valgrind warning would be preferable. */ + db_iface_data->dbus_iface.name = nm_ref_string_new(dbus_iface_name); + db_iface_data->dbus_iface_is_wellknown = FALSE; + db_iface_data->iface_removed = FALSE; + c_list_link_tail(&dbobj->iface_lst_head, &db_iface_data->iface_lst); + } + + return db_iface_data; +} + +static void +nml_dbus_obj_iface_data_destroy(NMLDBusObjIfaceData *db_iface_data) +{ + guint i; + + nm_assert(db_iface_data); + nm_assert(c_list_is_empty(&db_iface_data->iface_lst)); + + if (db_iface_data->dbus_iface_is_wellknown) { + for (i = 0; i < db_iface_data->dbus_iface.meta->n_dbus_properties; i++) + nm_g_variant_unref(db_iface_data->prop_datas[i].prop_data_value); + } else + nm_ref_string_unref(db_iface_data->dbus_iface.name); + + g_free(db_iface_data); +} + +gpointer +nml_dbus_object_get_property_location(NMLDBusObject * dbobj, + const NMLDBusMetaIface * meta_iface, + const NMLDBusMetaProperty *meta_property) +{ + char *target_c; + + target_c = (char *) dbobj->nmobj; + if (meta_iface->base_struct_offset > 0) + target_c = *((gpointer *) (&target_c[meta_iface->base_struct_offset])); + return &target_c[meta_property->prop_struct_offset]; +} + +static void +nml_dbus_object_set_obj_state(NMLDBusObject *dbobj, NMLDBusObjState obj_state, NMClient *self) +{ + NMClientPrivate *priv; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(NML_IS_DBUS_OBJECT(dbobj)); + +#if NM_MORE_ASSERTS > 10 + priv = NM_CLIENT_GET_PRIVATE(self); + switch (dbobj->obj_state) { + case NML_DBUS_OBJ_STATE_UNLINKED: + nm_assert(c_list_is_empty(&dbobj->dbus_objects_lst)); + break; + case NML_DBUS_OBJ_STATE_WATCHED_ONLY: + nm_assert( + c_list_contains(&priv->dbus_objects_lst_head_watched_only, &dbobj->dbus_objects_lst)); + break; + case NML_DBUS_OBJ_STATE_ON_DBUS: + nm_assert(c_list_contains(&priv->dbus_objects_lst_head_on_dbus, &dbobj->dbus_objects_lst)); + break; + case NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY: + nm_assert(c_list_contains(&priv->dbus_objects_lst_head_with_nmobj_not_ready, + &dbobj->dbus_objects_lst)); + break; + case NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY: + nm_assert(c_list_contains(&priv->dbus_objects_lst_head_with_nmobj_ready, + &dbobj->dbus_objects_lst)); + break; + } +#endif + + if (dbobj->obj_state == obj_state) + return; + + NML_NMCLIENT_LOG_T(self, + "[%s]: set D-Bus object state %s", + dbobj->dbus_path->str, + nml_dbus_obj_state_to_string(obj_state)); + + priv = NM_CLIENT_GET_PRIVATE(self); + dbobj->obj_state = obj_state; + switch (obj_state) { + case NML_DBUS_OBJ_STATE_UNLINKED: + c_list_unlink(&dbobj->dbus_objects_lst); + c_list_unlink(&dbobj->obj_changed_lst); + dbobj->obj_changed_type = NML_DBUS_OBJ_CHANGED_TYPE_NONE; + break; + case NML_DBUS_OBJ_STATE_WATCHED_ONLY: + nm_c_list_move_tail(&priv->dbus_objects_lst_head_watched_only, &dbobj->dbus_objects_lst); + break; + case NML_DBUS_OBJ_STATE_ON_DBUS: + nm_c_list_move_tail(&priv->dbus_objects_lst_head_on_dbus, &dbobj->dbus_objects_lst); + break; + case NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY: + nm_c_list_move_tail(&priv->dbus_objects_lst_head_with_nmobj_not_ready, + &dbobj->dbus_objects_lst); + break; + case NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY: + nm_c_list_move_tail(&priv->dbus_objects_lst_head_with_nmobj_ready, + &dbobj->dbus_objects_lst); + break; + default: + nm_assert_not_reached(); + } +} + +/*****************************************************************************/ + +static void +nml_dbus_object_obj_changed_link(NMClient * self, + NMLDBusObject * dbobj, + NMLDBusObjChangedType changed_type) +{ + nm_assert(NM_IS_CLIENT(self)); + nm_assert(NML_IS_DBUS_OBJECT(dbobj)); + nm_assert(changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + + if (!NM_FLAGS_ALL((NMLDBusObjChangedType) dbobj->obj_changed_type, changed_type)) + NML_NMCLIENT_LOG_T(self, + "[%s]: changed-type 0x%02x linked", + dbobj->dbus_path->str, + (guint) changed_type); + + if (dbobj->obj_changed_type == NML_DBUS_OBJ_CHANGED_TYPE_NONE) { + NMClientPrivate *priv; + + /* We set the changed-type flag. Need to queue the object in the + * changed list. */ + nm_assert(c_list_is_empty(&dbobj->obj_changed_lst)); + priv = NM_CLIENT_GET_PRIVATE(self); + c_list_link_tail(&priv->obj_changed_lst_head, &dbobj->obj_changed_lst); + } else { + /* The object has changes flags and must be linked already. Note that + * this may be priv->obj_changed_lst_head, or a temporary list on the + * stack. + * + * This dance with the temporary list is done to ensure we can enqueue + * objects while we process the changes. */ + nm_assert(!c_list_is_empty(&dbobj->obj_changed_lst)); + } + + dbobj->obj_changed_type |= changed_type; + + nm_assert(NM_FLAGS_ALL(dbobj->obj_changed_type, changed_type)); +} + +static NMLDBusObjChangedType +nml_dbus_object_obj_changed_consume(NMClient * self, + NMLDBusObject * dbobj, + NMLDBusObjChangedType changed_type) +{ + NMClientPrivate * priv; + NMLDBusObjChangedType changed_type_res; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(NML_IS_DBUS_OBJECT(dbobj)); + nm_assert(changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + nm_assert(dbobj->obj_changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + nm_assert(!c_list_is_empty(&dbobj->obj_changed_lst)); + + changed_type_res = dbobj->obj_changed_type & changed_type; + + dbobj->obj_changed_type &= ~changed_type; + + if (dbobj->obj_changed_type == NML_DBUS_OBJ_CHANGED_TYPE_NONE) { + c_list_unlink(&dbobj->obj_changed_lst); + nm_assert(changed_type_res != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + NML_NMCLIENT_LOG_T(self, + "[%s]: changed-type 0x%02x consumed", + dbobj->dbus_path->str, + (guint) changed_type_res); + return changed_type_res; + } + + priv = NM_CLIENT_GET_PRIVATE(self); + + nm_assert(!c_list_contains(&priv->obj_changed_lst_head, &dbobj->obj_changed_lst)); + nm_c_list_move_tail(&priv->obj_changed_lst_head, &dbobj->obj_changed_lst); + NML_NMCLIENT_LOG_T(self, + "[%s]: changed-type 0x%02x consumed (still has 0x%02x)", + dbobj->dbus_path->str, + (guint) changed_type_res, + (guint) dbobj->obj_changed_type); + return changed_type_res; +} + +static gboolean +nml_dbus_object_obj_changed_any_linked(NMClient *self, NMLDBusObjChangedType changed_type) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + NMLDBusObject * dbobj; + + nm_assert(changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + + c_list_for_each_entry (dbobj, &priv->obj_changed_lst_head, obj_changed_lst) { + nm_assert(dbobj->obj_changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + if (NM_FLAGS_ANY(dbobj->obj_changed_type, changed_type)) + return TRUE; + } + return FALSE; +} + +/*****************************************************************************/ + +static void +_dbobjs_notify_watchers_for_dbobj(NMClient *self, NMLDBusObject *dbobj) +{ + NMLDBusObjWatcher *obj_watcher; + NMLDBusObjWatcher *obj_watcher_safe; + + c_list_for_each_entry_safe (obj_watcher, + obj_watcher_safe, + &dbobj->watcher_lst_head, + _priv.watcher_lst) + obj_watcher->_priv.notify_fcn(self, obj_watcher); +} + +static gboolean +_dbobjs_check_dbobj_ready(NMClient *self, NMLDBusObject *dbobj) +{ + nm_assert(NM_IS_CLIENT(self)); + nm_assert(NML_IS_DBUS_OBJECT(dbobj)); + nm_assert(G_IS_OBJECT(dbobj->nmobj)); + nm_assert(NM_IS_OBJECT(dbobj->nmobj) || NM_IS_CLIENT(dbobj->nmobj)); + nm_assert(NM_IN_SET((NMLDBusObjState) dbobj->obj_state, + NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY, + NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY)); + + if (G_LIKELY(dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY)) + return TRUE; + + if (!NM_OBJECT_GET_CLASS(dbobj->nmobj)->is_ready(NM_OBJECT(dbobj->nmobj))) + return FALSE; + + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY, self); + + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + _dbobjs_notify_watchers_for_dbobj(self, dbobj); + + return TRUE; +} + +void +_nm_client_notify_object_changed(NMClient *self, NMLDBusObject *dbobj) +{ + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + _dbobjs_notify_watchers_for_dbobj(self, dbobj); +} + +/*****************************************************************************/ + +static NMLDBusObject * +_dbobjs_dbobj_get_r(NMClient *self, NMRefString *dbus_path_r) +{ + nm_assert(NM_IS_REF_STRING(dbus_path_r)); + + return g_hash_table_lookup(NM_CLIENT_GET_PRIVATE(self)->dbus_objects, &dbus_path_r); +} + +static NMLDBusObject * +_dbobjs_dbobj_get_s(NMClient *self, const char *dbus_path) +{ + nm_auto_ref_string NMRefString *dbus_path_r = NULL; + + nm_assert(dbus_path); + dbus_path_r = nm_ref_string_new(dbus_path); + return _dbobjs_dbobj_get_r(self, dbus_path_r); +} + +static NMLDBusObject * +_dbobjs_dbobj_create(NMClient *self, NMRefString *dbus_path_take) +{ + nm_auto_ref_string NMRefString *dbus_path = g_steal_pointer(&dbus_path_take); + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + NMLDBusObject * dbobj; + + nm_assert(!_dbobjs_dbobj_get_r(self, dbus_path)); + + dbobj = nml_dbus_object_new(g_steal_pointer(&dbus_path)); + if (!g_hash_table_add(priv->dbus_objects, dbobj)) + nm_assert_not_reached(); + return dbobj; +} + +static NMLDBusObject * +_dbobjs_dbobj_get_or_create(NMClient *self, NMRefString *dbus_path_take) +{ + nm_auto_ref_string NMRefString *dbus_path = g_steal_pointer(&dbus_path_take); + NMLDBusObject * dbobj; + + dbobj = _dbobjs_dbobj_get_r(self, dbus_path); + if (dbobj) + return dbobj; + return _dbobjs_dbobj_create(self, g_steal_pointer(&dbus_path)); +} + +static NMLDBusObject * +_dbobjs_get_nmobj(NMClient *self, const char *dbus_path, GType gtype) +{ + NMLDBusObject *dbobj; + + nm_assert(gtype == G_TYPE_NONE || g_type_is_a(gtype, NM_TYPE_OBJECT)); + + dbobj = _dbobjs_dbobj_get_s(self, dbus_path); + + if (!dbobj) + return NULL; + if (!dbobj->nmobj) + return NULL; + + if (gtype != G_TYPE_NONE && !g_type_is_a(G_OBJECT_TYPE(dbobj->nmobj), gtype)) + return NULL; + + return dbobj; +} + +static gpointer +_dbobjs_get_nmobj_unpack_visible(NMClient *self, const char *dbus_path, GType gtype) +{ + NMLDBusObject *dbobj; + + dbobj = _dbobjs_get_nmobj(self, dbus_path, gtype); + if (!dbobj) + return NULL; + if (dbobj->obj_state != NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY) + return NULL; + return dbobj->nmobj; +} + +/*****************************************************************************/ + +static gpointer +_dbobjs_obj_watcher_register_o(NMClient * self, + NMLDBusObject * dbobj, + NMLDBusObjWatchNotifyFcn notify_fcn, + gsize struct_size) +{ + NMLDBusObjWatcher *obj_watcher; + + nm_assert(NM_IS_CLIENT(self)); + _ASSERT_dbobj(dbobj, self); + nm_assert(notify_fcn); + nm_assert(struct_size > sizeof(NMLDBusObjWatcher)); + + obj_watcher = g_malloc(struct_size); + obj_watcher->dbobj = dbobj; + obj_watcher->_priv.notify_fcn = notify_fcn; + + /* we must enqueue the item in the front of the list. That is, because while + * invoking notify_fcn(), we iterate the watchers front-to-end. As we want to + * allow the callee to register new watches and unregister itself, this is + * the right way to do it. */ + c_list_link_front(&dbobj->watcher_lst_head, &obj_watcher->_priv.watcher_lst); + + return obj_watcher; +} + +static gpointer +_dbobjs_obj_watcher_register_r(NMClient * self, + NMRefString * dbus_path_take, + NMLDBusObjWatchNotifyFcn notify_fcn, + gsize struct_size) +{ + nm_auto_ref_string NMRefString *dbus_path = g_steal_pointer(&dbus_path_take); + NMLDBusObject * dbobj; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(notify_fcn); + + dbobj = _dbobjs_dbobj_get_or_create(self, g_steal_pointer(&dbus_path)); + if (dbobj->obj_state == NML_DBUS_OBJ_STATE_UNLINKED) + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_WATCHED_ONLY, self); + return _dbobjs_obj_watcher_register_o(self, dbobj, notify_fcn, struct_size); +} + +static void +_dbobjs_obj_watcher_unregister(NMClient *self, gpointer obj_watcher_base) +{ + NMLDBusObjWatcher *obj_watcher = obj_watcher_base; + NMLDBusObject * dbobj; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(obj_watcher); + nm_assert(NML_IS_DBUS_OBJECT(obj_watcher->dbobj)); + nm_assert(g_hash_table_lookup(NM_CLIENT_GET_PRIVATE(self)->dbus_objects, obj_watcher->dbobj) + == obj_watcher->dbobj); + nm_assert( + c_list_contains(&obj_watcher->dbobj->watcher_lst_head, &obj_watcher->_priv.watcher_lst)); + + c_list_unlink(&obj_watcher->_priv.watcher_lst); + + dbobj = obj_watcher->dbobj; + + g_free(obj_watcher); + + if (c_list_is_empty(&dbobj->iface_lst_head) && c_list_is_empty(&dbobj->watcher_lst_head)) { + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + NML_NMCLIENT_LOG_T(self, "[%s]: drop D-Bus watcher", dbobj->dbus_path->str); + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_UNLINKED, self); + if (!g_hash_table_steal(priv->dbus_objects, dbobj)) + nm_assert_not_reached(); + nml_dbus_object_unref(dbobj); + } +} + +/*****************************************************************************/ + +typedef struct { + NMLDBusObjWatcher parent; + NMLDBusPropertyO *pr_o; +} PropertyOData; + +gpointer +nml_dbus_property_o_get_obj(NMLDBusPropertyO *pr_o) +{ + nm_assert(!pr_o->nmobj || nml_dbus_property_o_is_ready(pr_o)); + return pr_o->nmobj; +} + +gboolean +nml_dbus_property_o_is_ready(const NMLDBusPropertyO *pr_o) +{ + return pr_o->is_ready || !pr_o->owner_dbobj; +} + +gboolean +nml_dbus_property_o_is_ready_fully(const NMLDBusPropertyO *pr_o) +{ + return !pr_o->owner_dbobj || !pr_o->obj_watcher || pr_o->nmobj; +} + +static void +nml_dbus_property_o_notify_changed(NMLDBusPropertyO *pr_o, NMClient *self) +{ + const NMLDBusPropertVTableO *vtable; + GObject * nmobj = NULL; + gboolean is_ready = TRUE; + gboolean changed_ready; + GType gtype; + + nm_assert(pr_o); + nm_assert(NM_IS_CLIENT(self)); + + if (!pr_o->owner_dbobj) + return; + + if (!pr_o->is_changed) { + if (pr_o->is_ready) + return; + goto done; + } + + pr_o->is_changed = FALSE; + + if (!pr_o->obj_watcher) + goto done; + + if (!pr_o->obj_watcher->dbobj->nmobj) { + if (pr_o->obj_watcher->dbobj->obj_state >= NML_DBUS_OBJ_STATE_ON_DBUS) { + NML_NMCLIENT_LOG_W( + self, + "[%s]: property %s references %s but object is not created", + pr_o->owner_dbobj->dbus_path->str, + pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].dbus_property_name, + pr_o->obj_watcher->dbobj->dbus_path->str); + } else { + NML_NMCLIENT_LOG_E( + self, + "[%s]: property %s references %s but object is not present on D-Bus", + pr_o->owner_dbobj->dbus_path->str, + pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].dbus_property_name, + pr_o->obj_watcher->dbobj->dbus_path->str); + } + goto done; + } + + vtable = pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].extra.property_vtable_o; + + gtype = vtable->get_o_type_fcn(); + if (!g_type_is_a(G_OBJECT_TYPE(pr_o->obj_watcher->dbobj->nmobj), gtype)) { + NML_NMCLIENT_LOG_E( + self, + "[%s]: property %s references %s with unexpected GObject type %s instead of %s", + pr_o->owner_dbobj->dbus_path->str, + pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].dbus_property_name, + pr_o->obj_watcher->dbobj->dbus_path->str, + G_OBJECT_TYPE_NAME(pr_o->obj_watcher->dbobj->nmobj), + g_type_name(gtype)); + goto done; + } + + if (pr_o->obj_watcher->dbobj == pr_o->owner_dbobj) { + NML_NMCLIENT_LOG_W( + self, + "[%s]: property %s references itself", + pr_o->owner_dbobj->dbus_path->str, + pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].dbus_property_name); + nmobj = pr_o->owner_dbobj->nmobj; + goto done; + } + + pr_o->block_is_changed = TRUE; + is_ready = _dbobjs_check_dbobj_ready(self, pr_o->obj_watcher->dbobj); + pr_o->block_is_changed = FALSE; + + if (!is_ready) { + is_ready = vtable->is_always_ready; + goto done; + } + + nmobj = pr_o->obj_watcher->dbobj->nmobj; + +done: + changed_ready = FALSE; + if (!pr_o->is_ready && is_ready) { + pr_o->is_ready = TRUE; + changed_ready = TRUE; + } + if (pr_o->nmobj != nmobj) { + pr_o->nmobj = nmobj; + _nm_client_queue_notify_object( + self, + pr_o->owner_dbobj->nmobj, + pr_o->meta_iface->obj_properties + [pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].obj_properties_idx]); + } + if (changed_ready && pr_o->owner_dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY) + nml_dbus_object_obj_changed_link(self, pr_o->owner_dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); +} + +void +nml_dbus_property_o_notify_changed_many(NMLDBusPropertyO *ptr, guint len, NMClient *self) +{ + while (len-- > 0) + nml_dbus_property_o_notify_changed(ptr++, self); +} + +static void +nml_dbus_property_o_notify_watch_cb(NMClient *self, gpointer obj_watcher) +{ + PropertyOData * pr_o_data = obj_watcher; + NMLDBusPropertyO *pr_o = pr_o_data->pr_o; + + nm_assert(pr_o->obj_watcher == obj_watcher); + + if (!pr_o->block_is_changed && !pr_o->is_changed) { + pr_o->is_changed = TRUE; + nml_dbus_object_obj_changed_link(self, pr_o->owner_dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + } +} + +static NMLDBusNotifyUpdatePropFlags +nml_dbus_property_o_notify(NMClient * self, + NMLDBusPropertyO * pr_o, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + const char *dbus_path = NULL; + gboolean changed = FALSE; + + if (!pr_o->owner_dbobj) { + nm_assert(!pr_o->meta_iface); + nm_assert(pr_o->dbus_property_idx == 0); + nm_assert(!pr_o->is_ready); + pr_o->owner_dbobj = dbobj; + pr_o->meta_iface = meta_iface; + pr_o->dbus_property_idx = dbus_property_idx; + } else { + nm_assert(pr_o->owner_dbobj == dbobj); + nm_assert(pr_o->meta_iface == meta_iface); + nm_assert(pr_o->dbus_property_idx == dbus_property_idx); + } + + if (value) + dbus_path = nm_dbus_path_not_empty(g_variant_get_string(value, NULL)); + + if (pr_o->obj_watcher + && (!dbus_path || !nm_streq(dbus_path, pr_o->obj_watcher->dbobj->dbus_path->str))) { + _dbobjs_obj_watcher_unregister(self, g_steal_pointer(&pr_o->obj_watcher)); + changed = TRUE; + } + if (!pr_o->obj_watcher && dbus_path) { + pr_o->obj_watcher = _dbobjs_obj_watcher_register_r(self, + nm_ref_string_new(dbus_path), + nml_dbus_property_o_notify_watch_cb, + sizeof(PropertyOData)); + ((PropertyOData *) pr_o->obj_watcher)->pr_o = pr_o; + changed = TRUE; + } + + if (changed && !pr_o->is_changed) { + pr_o->is_changed = TRUE; + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + } + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; +} + +void +nml_dbus_property_o_clear(NMLDBusPropertyO *pr_o, NMClient *self) +{ + if (pr_o->obj_watcher) { + nm_assert(NM_IS_CLIENT(self)); + _dbobjs_obj_watcher_unregister(self, g_steal_pointer(&pr_o->obj_watcher)); + } + if (pr_o->nmobj && pr_o->owner_dbobj && pr_o->owner_dbobj->nmobj) { + _nm_client_queue_notify_object( + self, + pr_o->owner_dbobj->nmobj, + pr_o->meta_iface->obj_properties + [pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].obj_properties_idx]); + } + pr_o->owner_dbobj = NULL; + pr_o->meta_iface = NULL; + pr_o->dbus_property_idx = 0; + pr_o->is_ready = FALSE; +} + +void +nml_dbus_property_o_clear_many(NMLDBusPropertyO *pr_o, guint len, NMClient *self) +{ + while (len-- > 0) + nml_dbus_property_o_clear(pr_o++, self); +} + +/*****************************************************************************/ + +typedef struct _NMLDBusPropertyAOData { + NMLDBusObjWatcher obj_watcher; + NMLDBusPropertyAO * parent; + CList data_lst; + GObject * nmobj; + struct _NMLDBusPropertyAOData *changed_next; + bool is_ready : 1; + bool is_notified : 1; + bool is_changed : 1; + bool block_is_changed : 1; +} PropertyAOData; + +static void +_ASSERT_pr_ao(NMLDBusPropertyAO *pr_ao) +{ + nm_assert(pr_ao); + +#if NM_MORE_ASSERTS > 10 + if (pr_ao->owner_dbobj) { + guint n_not_ready = 0; + guint n_is_changed = 0; + guint n_is_changed_2; + PropertyAOData *pr_ao_data; + + c_list_for_each_entry (pr_ao_data, &pr_ao->data_lst_head, data_lst) { + if (pr_ao_data->is_changed) + n_is_changed++; + if (!pr_ao_data->is_ready) + n_not_ready++; + } + nm_assert(n_not_ready == pr_ao->n_not_ready); + + n_is_changed_2 = 0; + pr_ao_data = pr_ao->changed_head; + while (pr_ao_data) { + nm_assert(pr_ao_data->is_changed); + n_is_changed_2++; + pr_ao_data = pr_ao_data->changed_next; + } + nm_assert(n_is_changed == n_is_changed_2); + } +#endif +} + +static gboolean +nml_dbus_property_ao_notify_changed_ao(PropertyAOData *pr_ao_data, + NMClient * self, + gboolean is_added /* or else removed */) +{ + NMLDBusPropertyAO * pr_ao; + const NMLDBusPropertVTableAO *vtable; + + if (!pr_ao_data->nmobj) + return FALSE; + + nm_assert(pr_ao_data->is_ready); + + if (is_added) { + if (pr_ao_data->is_notified) + return FALSE; + pr_ao_data->is_notified = TRUE; + } else { + if (!pr_ao_data->is_notified) + return FALSE; + pr_ao_data->is_notified = FALSE; + } + + pr_ao = pr_ao_data->parent; + + vtable = pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].extra.property_vtable_ao; + + if (vtable->notify_changed_ao) + vtable->notify_changed_ao(pr_ao, self, NM_OBJECT(pr_ao_data->nmobj), is_added); + return TRUE; +} + +const GPtrArray * +nml_dbus_property_ao_get_objs_as_ptrarray(NMLDBusPropertyAO *pr_ao) +{ + if (!pr_ao->arr) { + PropertyAOData *pr_ao_data; + gsize n; + + n = 0; + if (pr_ao->owner_dbobj) { + c_list_for_each_entry (pr_ao_data, &pr_ao->data_lst_head, data_lst) { + if (pr_ao_data->nmobj) + n++; + } + } + + pr_ao->arr = g_ptr_array_new_full(n, g_object_unref); + if (pr_ao->owner_dbobj) { + c_list_for_each_entry (pr_ao_data, &pr_ao->data_lst_head, data_lst) { + if (pr_ao_data->nmobj) + g_ptr_array_add(pr_ao->arr, g_object_ref(pr_ao_data->nmobj)); + } + } + } + return pr_ao->arr; +} + +gboolean +nml_dbus_property_ao_is_ready(const NMLDBusPropertyAO *pr_ao) +{ + return pr_ao->n_not_ready == 0; +} + +static void +nml_dbus_property_ao_notify_changed(NMLDBusPropertyAO *pr_ao, NMClient *self) +{ + gboolean changed_prop = FALSE; + gboolean changed_ready = FALSE; + PropertyAOData *pr_ao_data; + + nm_assert(NM_IS_CLIENT(self)); + _ASSERT_pr_ao(pr_ao); + + if (!pr_ao->owner_dbobj) + return; + + if (!pr_ao->is_changed) { + if (pr_ao->n_not_ready == 0) + return; + goto done; + } + + pr_ao->is_changed = FALSE; + + while (pr_ao->changed_head) { + const NMLDBusPropertVTableAO *vtable; + GObject * nmobj = NULL; + gboolean is_ready = TRUE; + GType gtype; + + pr_ao_data = g_steal_pointer(&pr_ao->changed_head); + nm_assert(pr_ao_data->is_changed); + + pr_ao->changed_head = pr_ao_data->changed_next; + pr_ao_data->is_changed = FALSE; + + if (!pr_ao_data->obj_watcher.dbobj->nmobj) { + if (pr_ao_data->obj_watcher.dbobj->obj_state >= NML_DBUS_OBJ_STATE_ON_DBUS) { + NML_NMCLIENT_LOG_W( + self, + "[%s]: property %s references %s but object is not created", + pr_ao->owner_dbobj->dbus_path->str, + pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].dbus_property_name, + pr_ao_data->obj_watcher.dbobj->dbus_path->str); + } else { + NML_NMCLIENT_LOG_E( + self, + "[%s]: property %s references %s but object is not present on D-Bus", + pr_ao->owner_dbobj->dbus_path->str, + pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].dbus_property_name, + pr_ao_data->obj_watcher.dbobj->dbus_path->str); + } + goto done_pr_ao_data; + } + + vtable = + pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].extra.property_vtable_ao; + + gtype = vtable->get_o_type_fcn(); + if (!g_type_is_a(G_OBJECT_TYPE(pr_ao_data->obj_watcher.dbobj->nmobj), gtype)) { + NML_NMCLIENT_LOG_E( + self, + "[%s]: property %s references %s with unexpected GObject type %s instead of %s", + pr_ao->owner_dbobj->dbus_path->str, + pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].dbus_property_name, + pr_ao_data->obj_watcher.dbobj->dbus_path->str, + G_OBJECT_TYPE_NAME(pr_ao_data->obj_watcher.dbobj->nmobj), + g_type_name(gtype)); + goto done_pr_ao_data; + } + + if (pr_ao_data->obj_watcher.dbobj == pr_ao->owner_dbobj) { + NML_NMCLIENT_LOG_W( + self, + "[%s]: property %s references itself", + pr_ao->owner_dbobj->dbus_path->str, + pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].dbus_property_name); + nmobj = pr_ao->owner_dbobj->nmobj; + goto done_pr_ao_data; + } + + pr_ao_data->block_is_changed = TRUE; + is_ready = _dbobjs_check_dbobj_ready(self, pr_ao_data->obj_watcher.dbobj); + pr_ao_data->block_is_changed = FALSE; + + if (!is_ready) { + is_ready = vtable->is_always_ready; + goto done_pr_ao_data; + } + + if (vtable->check_nmobj_visible_fcn + && !vtable->check_nmobj_visible_fcn(pr_ao_data->obj_watcher.dbobj->nmobj)) { + is_ready = TRUE; + goto done_pr_ao_data; + } + + nmobj = pr_ao_data->obj_watcher.dbobj->nmobj; + +done_pr_ao_data: + + if (!pr_ao_data->is_ready && is_ready) { + nm_assert(pr_ao->n_not_ready > 0); + pr_ao->n_not_ready--; + pr_ao_data->is_ready = TRUE; + changed_ready = TRUE; + } + + if (pr_ao_data->nmobj != nmobj) { + if (nml_dbus_property_ao_notify_changed_ao(pr_ao_data, self, FALSE)) + changed_prop = TRUE; + pr_ao_data->nmobj = nmobj; + } + + if (!pr_ao_data->is_notified) { + if (nml_dbus_property_ao_notify_changed_ao(pr_ao_data, self, TRUE)) + changed_prop = TRUE; + } + } + + _ASSERT_pr_ao(pr_ao); + +done: + if (changed_prop) { + nm_clear_pointer(&pr_ao->arr, g_ptr_array_unref); + _nm_client_queue_notify_object( + self, + pr_ao->owner_dbobj->nmobj, + pr_ao->meta_iface->obj_properties + [pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].obj_properties_idx]); + } + if (changed_ready && pr_ao->n_not_ready == 0 + && pr_ao->owner_dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY) + nml_dbus_object_obj_changed_link(self, pr_ao->owner_dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); +} + +void +nml_dbus_property_ao_notify_changed_many(NMLDBusPropertyAO *ptr, guint len, NMClient *self) +{ + while (len-- > 0) + nml_dbus_property_ao_notify_changed(ptr++, self); +} + +static void +nml_dbus_property_ao_notify_watch_cb(NMClient *self, gpointer obj_watcher) +{ + PropertyAOData * pr_ao_data = obj_watcher; + NMLDBusPropertyAO *pr_ao = pr_ao_data->parent; + + nm_assert(g_hash_table_lookup(pr_ao->hash, pr_ao_data) == pr_ao_data); + + if (!pr_ao_data->block_is_changed && !pr_ao_data->is_changed) { + pr_ao_data->is_changed = TRUE; + pr_ao_data->changed_next = pr_ao->changed_head; + pr_ao->changed_head = pr_ao_data; + if (!pr_ao->is_changed) { + pr_ao->is_changed = TRUE; + nml_dbus_object_obj_changed_link(self, + pr_ao->owner_dbobj, + NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + } + } + + _ASSERT_pr_ao(pr_ao); +} + +static NMLDBusNotifyUpdatePropFlags +nml_dbus_property_ao_notify(NMClient * self, + NMLDBusPropertyAO * pr_ao, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + CList stale_lst_head = C_LIST_INIT(stale_lst_head); + PropertyAOData *pr_ao_data; + gboolean changed_prop = FALSE; + gboolean changed_obj = FALSE; + + if (!pr_ao->owner_dbobj) { + nm_assert(!pr_ao->data_lst_head.next); + nm_assert(!pr_ao->data_lst_head.prev); + nm_assert(!pr_ao->hash); + nm_assert(!pr_ao->meta_iface); + nm_assert(pr_ao->dbus_property_idx == 0); + nm_assert(pr_ao->n_not_ready == 0); + nm_assert(!pr_ao->changed_head); + nm_assert(!pr_ao->is_changed); + + c_list_init(&pr_ao->data_lst_head); + pr_ao->hash = g_hash_table_new(nm_ppdirect_hash, nm_ppdirect_equal); + pr_ao->owner_dbobj = dbobj; + pr_ao->meta_iface = meta_iface; + pr_ao->dbus_property_idx = dbus_property_idx; + } else { + nm_assert(pr_ao->data_lst_head.next); + nm_assert(pr_ao->data_lst_head.prev); + nm_assert(pr_ao->hash); + nm_assert(pr_ao->meta_iface == meta_iface); + nm_assert(pr_ao->dbus_property_idx == dbus_property_idx); + } + + c_list_splice(&stale_lst_head, &pr_ao->data_lst_head); + + if (value) { + GVariantIter iter; + const char * path; + + g_variant_iter_init(&iter, value); + while (g_variant_iter_next(&iter, "&o", &path)) { + nm_auto_ref_string NMRefString *dbus_path_r = NULL; + gpointer p_dbus_path_1; + + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(PropertyAOData, obj_watcher) == 0); + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMLDBusObjWatcher, dbobj) == 0); + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMLDBusObject, dbus_path) == 0); + + if (!nm_dbus_path_not_empty(path)) { + /* should not happen. Anyway, silently skip empty paths. */ + continue; + } + + dbus_path_r = nm_ref_string_new(path); + p_dbus_path_1 = &dbus_path_r; + pr_ao_data = g_hash_table_lookup(pr_ao->hash, &p_dbus_path_1); + + if (pr_ao_data) { + /* With this implementation we cannot track the same path multiple times. + * Of course, for none of the properties where we use this, the server + * should expose the same path more than once, so this limitation is fine + * (maybe even preferable to drop duplicates form NMClient's API). */ + nm_assert(pr_ao_data->obj_watcher.dbobj->dbus_path == dbus_path_r); + if (!changed_prop && pr_ao_data->is_notified) { + /* The order of a notified entry changed. That means, we need to signal + * a change of the property. This detection of a change is not always + * correct, in particular we might detect some changes when there were + * none. That's not a serious problem, and fixing it would be expensive + * to implement. */ + changed_prop = (c_list_first(&stale_lst_head) != &pr_ao_data->data_lst); + } + nm_c_list_move_tail(&pr_ao->data_lst_head, &pr_ao_data->data_lst); + } else { + pr_ao_data = _dbobjs_obj_watcher_register_r(self, + g_steal_pointer(&dbus_path_r), + nml_dbus_property_ao_notify_watch_cb, + sizeof(PropertyAOData)), + pr_ao_data->parent = pr_ao; + pr_ao_data->nmobj = NULL; + pr_ao_data->changed_next = NULL; + pr_ao_data->is_changed = TRUE; + pr_ao_data->block_is_changed = FALSE; + pr_ao_data->is_ready = FALSE; + pr_ao_data->is_notified = FALSE; + c_list_link_tail(&pr_ao->data_lst_head, &pr_ao_data->data_lst); + if (!g_hash_table_add(pr_ao->hash, pr_ao_data)) + nm_assert_not_reached(); + nm_assert(pr_ao->n_not_ready < G_MAXUINT); + pr_ao->n_not_ready++; + } + +#if NM_MORE_ASSERTS > 10 + { + nm_auto_ref_string NMRefString *p = nm_ref_string_new(path); + gpointer pp = &p; + + nm_assert(g_hash_table_lookup(pr_ao->hash, &pp) == pr_ao_data); + } +#endif + } + } + + pr_ao->changed_head = NULL; + c_list_for_each_entry (pr_ao_data, &pr_ao->data_lst_head, data_lst) { + if (pr_ao_data->is_changed) { + pr_ao_data->changed_next = pr_ao->changed_head; + pr_ao->changed_head = pr_ao_data; + changed_obj = TRUE; + } + } + + while ((pr_ao_data = c_list_first_entry(&stale_lst_head, PropertyAOData, data_lst))) { + changed_obj = TRUE; + c_list_unlink(&pr_ao_data->data_lst); + if (!g_hash_table_remove(pr_ao->hash, pr_ao_data)) + nm_assert_not_reached(); + if (!pr_ao_data->is_ready) { + nm_assert(pr_ao->n_not_ready > 0); + pr_ao->n_not_ready--; + } else { + if (nml_dbus_property_ao_notify_changed_ao(pr_ao_data, self, FALSE)) + changed_prop = TRUE; + } + _dbobjs_obj_watcher_unregister(self, pr_ao_data); + } + + _ASSERT_pr_ao(pr_ao); + + if (changed_obj) { + pr_ao->is_changed = TRUE; + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + } + + if (changed_prop) { + nm_clear_pointer(&pr_ao->arr, g_ptr_array_unref); + _nm_client_queue_notify_object( + self, + pr_ao->owner_dbobj->nmobj, + pr_ao->meta_iface->obj_properties + [pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].obj_properties_idx]); + } + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; +} + +void +nml_dbus_property_ao_clear(NMLDBusPropertyAO *pr_ao, NMClient *self) +{ + _ASSERT_pr_ao(pr_ao); + + if (!pr_ao->owner_dbobj) { + nm_assert(pr_ao->n_not_ready == 0); + nm_assert((!pr_ao->data_lst_head.next && !pr_ao->data_lst_head.prev) + || (pr_ao->data_lst_head.next == pr_ao->data_lst_head.prev)); + nm_assert(!pr_ao->hash); + nm_assert(!pr_ao->meta_iface); + nm_assert(pr_ao->dbus_property_idx == 0); + nm_assert(!pr_ao->is_changed); + } else { + PropertyAOData *pr_ao_data; + gboolean changed_prop = FALSE; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(pr_ao->data_lst_head.next); + nm_assert(pr_ao->data_lst_head.prev); + nm_assert(pr_ao->hash); + nm_assert(pr_ao->meta_iface); + + while ((pr_ao_data = c_list_first_entry(&pr_ao->data_lst_head, PropertyAOData, data_lst))) { + if (!pr_ao_data->is_ready) { + nm_assert(pr_ao->n_not_ready > 0); + pr_ao->n_not_ready--; + } else { + if (nml_dbus_property_ao_notify_changed_ao(pr_ao_data, self, FALSE)) + changed_prop = TRUE; + } + c_list_unlink(&pr_ao_data->data_lst); + if (!g_hash_table_remove(pr_ao->hash, pr_ao_data)) + nm_assert_not_reached(); + _dbobjs_obj_watcher_unregister(self, pr_ao_data); + } + + nm_assert(c_list_is_empty(&pr_ao->data_lst_head)); + nm_assert(pr_ao->n_not_ready == 0); + nm_assert(g_hash_table_size(pr_ao->hash) == 0); + + if (changed_prop && pr_ao->owner_dbobj->nmobj) { + _nm_client_queue_notify_object( + self, + pr_ao->owner_dbobj->nmobj, + pr_ao->meta_iface + ->obj_properties[pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx] + .obj_properties_idx]); + } + + nm_assert(c_list_is_empty(&pr_ao->data_lst_head)); + nm_assert(pr_ao->n_not_ready == 0); + nm_assert(g_hash_table_size(pr_ao->hash) == 0); + nm_clear_pointer(&pr_ao->hash, g_hash_table_unref); + pr_ao->owner_dbobj = NULL; + pr_ao->meta_iface = NULL; + pr_ao->dbus_property_idx = 0; + pr_ao->data_lst_head.next = NULL; + pr_ao->data_lst_head.prev = NULL; + pr_ao->is_changed = FALSE; + } + + nm_clear_pointer(&pr_ao->arr, g_ptr_array_unref); +} + +void +nml_dbus_property_ao_clear_many(NMLDBusPropertyAO *pr_ao, guint len, NMClient *self) +{ + while (len-- > 0) + nml_dbus_property_ao_clear(pr_ao++, self); +} + +/*****************************************************************************/ + +NMLDBusNotifyUpdatePropFlags +_nml_dbus_notify_update_prop_ignore(NMClient * self, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; +} + +NMLDBusNotifyUpdatePropFlags +_nml_dbus_notify_update_prop_o(NMClient * self, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + const char * path = NULL; + NMRefString **p_property; + + if (value) + path = g_variant_get_string(value, NULL); + + p_property = + nml_dbus_object_get_property_location(dbobj, + meta_iface, + &meta_iface->dbus_properties[dbus_property_idx]); + + if (!nm_streq0(nm_ref_string_get_str(*p_property), path)) { + nm_ref_string_unref(*p_property); + *p_property = nm_ref_string_new(path); + } + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/*****************************************************************************/ + +static void +_obj_handle_dbus_prop_changes(NMClient * self, + NMLDBusObject * dbobj, + NMLDBusObjIfaceData *db_iface_data, + guint dbus_property_idx, + GVariant * value) +{ + const NMLDBusMetaIface * meta_iface = db_iface_data->dbus_iface.meta; + const NMLDBusMetaProperty * meta_property = &meta_iface->dbus_properties[dbus_property_idx]; + gpointer p_property; + const char * dbus_type_s; + const GParamSpec * param_spec; + NMLDBusNotifyUpdatePropFlags notify_update_prop_flags; + + nm_assert(G_IS_OBJECT(dbobj->nmobj)); + + if (value && !g_variant_is_of_type(value, meta_property->dbus_type)) { + NML_NMCLIENT_LOG_E(self, + "[%s] property %s.%s expected of type \"%s\" but is \"%s\". Ignore", + dbobj->dbus_path->str, + meta_iface->dbus_iface_name, + meta_property->dbus_property_name, + (const char *) meta_property->dbus_type, + (const char *) g_variant_get_type(value)); + value = NULL; + } + + if (meta_property->use_notify_update_prop) { + notify_update_prop_flags = + meta_property->notify_update_prop(self, dbobj, meta_iface, dbus_property_idx, value); + if (notify_update_prop_flags == NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE) + return; + + nm_assert(notify_update_prop_flags == NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY); + nm_assert(G_IS_OBJECT(dbobj->nmobj)); + nm_assert(meta_iface->obj_properties); + nm_assert(meta_property->obj_properties_idx > 0); + param_spec = meta_iface->obj_properties[meta_property->obj_properties_idx]; + goto notify; + } + + p_property = nml_dbus_object_get_property_location(dbobj, meta_iface, meta_property); + + dbus_type_s = (const char *) meta_property->dbus_type; + + nm_assert(G_IS_OBJECT(dbobj->nmobj)); + nm_assert(meta_iface->obj_properties); + nm_assert(meta_property->obj_properties_idx > 0); + param_spec = meta_iface->obj_properties[meta_property->obj_properties_idx]; + + notify_update_prop_flags = NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; + + switch (dbus_type_s[0]) { + case 'b': + nm_assert(dbus_type_s[1] == '\0'); + if (value) + *((bool *) p_property) = g_variant_get_boolean(value); + else if (param_spec->value_type == G_TYPE_BOOLEAN) + *((bool *) p_property) = ((GParamSpecBoolean *) param_spec)->default_value; + else { + nm_assert_not_reached(); + *((bool *) p_property) = FALSE; + } + break; + case 'y': + nm_assert(dbus_type_s[1] == '\0'); + if (value) + *((guint8 *) p_property) = g_variant_get_byte(value); + else { + nm_assert(nm_utils_g_param_spec_is_default(param_spec)); + *((guint8 *) p_property) = 0; + } + break; + case 'q': + nm_assert(dbus_type_s[1] == '\0'); + if (value) + *((guint16 *) p_property) = g_variant_get_uint16(value); + else { + nm_assert(nm_utils_g_param_spec_is_default(param_spec)); + *((guint16 *) p_property) = 0; + } + break; + case 'i': + nm_assert(dbus_type_s[1] == '\0'); + if (value) + *((gint32 *) p_property) = g_variant_get_int32(value); + else if (param_spec->value_type == G_TYPE_INT) + *((gint32 *) p_property) = ((GParamSpecInt *) param_spec)->default_value; + else { + nm_assert(nm_utils_g_param_spec_is_default(param_spec)); + *((gint32 *) p_property) = 0; + } + break; + case 'u': + nm_assert(dbus_type_s[1] == '\0'); + if (value) + *((guint32 *) p_property) = g_variant_get_uint32(value); + else { + nm_assert(nm_utils_g_param_spec_is_default(param_spec)); + *((guint32 *) p_property) = 0; + } + break; + case 'x': + nm_assert(dbus_type_s[1] == '\0'); + if (value) + *((gint64 *) p_property) = g_variant_get_int64(value); + else if (param_spec->value_type == G_TYPE_INT64) + *((gint64 *) p_property) = ((GParamSpecInt64 *) param_spec)->default_value; + else { + nm_assert(nm_utils_g_param_spec_is_default(param_spec)); + *((gint64 *) p_property) = 0; + } + break; + case 't': + nm_assert(dbus_type_s[1] == '\0'); + if (value) + *((guint64 *) p_property) = g_variant_get_uint64(value); + else { + nm_assert(nm_utils_g_param_spec_is_default(param_spec)); + *((guint64 *) p_property) = 0; + } + break; + case 's': + nm_assert(dbus_type_s[1] == '\0'); + nm_clear_g_free((char **) p_property); + if (value) + *((char **) p_property) = g_variant_dup_string(value, NULL); + else { + nm_assert(nm_utils_g_param_spec_is_default(param_spec)); + *((char **) p_property) = NULL; + } + break; + case 'o': + nm_assert(dbus_type_s[1] == '\0'); + notify_update_prop_flags = nml_dbus_property_o_notify(self, + p_property, + dbobj, + meta_iface, + dbus_property_idx, + value); + break; + case 'a': + switch (dbus_type_s[1]) { + case 'y': + nm_assert(dbus_type_s[2] == '\0'); + { + gconstpointer v; + gsize l; + GBytes * b = NULL; + + if (value) { + v = g_variant_get_fixed_array(value, &l, 1); + + if (l > 0) { + /* empty arrays are coerced to NULL. */ + b = g_bytes_new(v, l); + } + } + + nm_clear_pointer((GBytes **) p_property, g_bytes_unref); + *((GBytes **) p_property) = b; + } + break; + case 's': + nm_assert(dbus_type_s[2] == '\0'); + nm_assert(param_spec->value_type == G_TYPE_STRV); + + g_strfreev(*((char ***) p_property)); + if (value) + *((char ***) p_property) = g_variant_dup_strv(value, NULL); + else + *((char ***) p_property) = NULL; + break; + case 'o': + nm_assert(dbus_type_s[2] == '\0'); + notify_update_prop_flags = nml_dbus_property_ao_notify(self, + p_property, + dbobj, + meta_iface, + dbus_property_idx, + value); + break; + default: + nm_assert_not_reached(); + } + break; + default: + nm_assert_not_reached(); + } + +notify: + if (NM_FLAGS_HAS(notify_update_prop_flags, NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY)) + g_object_notify_by_pspec(dbobj->nmobj, (GParamSpec *) param_spec); +} + +static void +_obj_handle_dbus_iface_changes(NMClient * self, + NMLDBusObject * dbobj, + NMLDBusObjIfaceData *db_iface_data) +{ + NMLDBusObjPropData *db_prop_data; + gboolean is_self = (G_OBJECT(self) == dbobj->nmobj); + gboolean is_removed; + gboolean type_compatible; + guint8 i_prop; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(is_self || NM_IS_OBJECT(dbobj->nmobj)); + + if (G_UNLIKELY(!db_iface_data->nmobj_checked)) { + db_iface_data->nmobj_checked = TRUE; + type_compatible = db_iface_data->dbus_iface.meta->get_type_fcn + && g_type_is_a(G_OBJECT_TYPE(dbobj->nmobj), + db_iface_data->dbus_iface.meta->get_type_fcn()); + db_iface_data->nmobj_compatible = type_compatible; + } else + type_compatible = db_iface_data->nmobj_compatible; + + if (!type_compatible) { + /* on D-Bus, we have this interface associate with the object, but apparently + * it is not compatible. This is either a bug, or NetworkManager exposed an + * unexpected interface on D-Bus object for which we create a certain NMObject + * type. */ + return; + } + + is_removed = c_list_is_empty(&db_iface_data->iface_lst); + + nm_assert(is_removed || !c_list_is_empty(&db_iface_data->changed_prop_lst_head)); + + _nm_client_queue_notify_object(self, dbobj->nmobj, NULL); + + if (is_removed) { + for (i_prop = 0; i_prop < db_iface_data->dbus_iface.meta->n_dbus_properties; i_prop++) { + _obj_handle_dbus_prop_changes(self, dbobj, db_iface_data, i_prop, NULL); + } + } else { + while ((db_prop_data = c_list_first_entry(&db_iface_data->changed_prop_lst_head, + NMLDBusObjPropData, + changed_prop_lst))) { + gs_unref_variant GVariant *prop_data_value = NULL; + + c_list_unlink(&db_prop_data->changed_prop_lst); + + nm_assert(db_prop_data >= db_iface_data->prop_datas); + nm_assert( + db_prop_data + < &db_iface_data->prop_datas[db_iface_data->dbus_iface.meta->n_dbus_properties]); + nm_assert(db_prop_data->prop_data_value); + + /* Currently, NMLDBusObject forgets about the variant. Theoretically, it could cache + * it, but there is no need because we update the property in nmobj (which extracts and + * keeps the property value itself). + * + * Note that we only consume the variant here when we process it. + * That implies that we already created a NMObject for the dbobj + * instance. Unless that happens, we cache the last seen property values. */ + prop_data_value = g_steal_pointer(&db_prop_data->prop_data_value); + + i_prop = (db_prop_data - &db_iface_data->prop_datas[0]); + _obj_handle_dbus_prop_changes(self, dbobj, db_iface_data, i_prop, prop_data_value); + } + } +} + +static void +_obj_handle_dbus_changes(NMClient *self, NMLDBusObject *dbobj) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + NMLDBusObjIfaceData *db_iface_data; + NMLDBusObjIfaceData *db_iface_data_safe; + gs_unref_object GObject *nmobj_unregistering = NULL; + + _ASSERT_dbobj(dbobj, self); + + /* In a first step we only remember all the changes that a D-Bus message brings + * and queue the object to process them. + * + * Here (in step 2) we look at what changed on D-Bus and propagate those changes + * to the NMObject instance. + * + * Note that here we still must not emit any GObject signals. That follows later, + * and again if the object changes, we will just queue that we handle the changes + * later. */ + + c_list_for_each_entry_safe (db_iface_data, + db_iface_data_safe, + &dbobj->iface_lst_head, + iface_lst) { + if (!db_iface_data->iface_removed) + continue; + c_list_unlink(&db_iface_data->iface_lst); + if (db_iface_data->dbus_iface_is_wellknown && dbobj->nmobj) + _obj_handle_dbus_iface_changes(self, dbobj, db_iface_data); + nml_dbus_obj_iface_data_destroy(db_iface_data); + } + + if (G_UNLIKELY(!dbobj->nmobj) && !c_list_is_empty(&dbobj->iface_lst_head)) { + /* Try to create a NMObject for this D-Bus object. Note that we detect the type + * based on the interfaces that it has, and if we make a choice once, we don't + * change. That means, one D-Bus object can only be of one type. */ + + if (NM_IN_SET(dbobj->dbus_path, + _dbus_path_nm, + _dbus_path_settings, + _dbus_path_dns_manager)) { + /* For the main types, we don't detect them based on the interfaces present, + * but on the path names. Of course, both should correspond anyway. */ + NML_NMCLIENT_LOG_T(self, + "[%s]: register NMClient for D-Bus object", + dbobj->dbus_path->str); + dbobj->nmobj = G_OBJECT(self); + if (dbobj->dbus_path == _dbus_path_nm) { + nm_assert(!priv->dbobj_nm); + priv->dbobj_nm = dbobj; + } else if (dbobj->dbus_path == _dbus_path_settings) { + nm_assert(!priv->dbobj_settings); + priv->dbobj_settings = dbobj; + } else { + nm_assert(dbobj->dbus_path == _dbus_path_dns_manager); + nm_assert(!priv->dbobj_dns_manager); + priv->dbobj_dns_manager = dbobj; + } + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY, self); + } else { + GType gtype = G_TYPE_NONE; + NMLDBusMetaInteracePrio curr_prio = NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_10 - 1; + + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + nm_assert(!db_iface_data->iface_removed); + if (!db_iface_data->dbus_iface_is_wellknown) + break; + if (db_iface_data->dbus_iface.meta->interface_prio <= curr_prio) + continue; + curr_prio = db_iface_data->dbus_iface.meta->interface_prio; + gtype = db_iface_data->dbus_iface.meta->get_type_fcn(); + } + if (gtype != G_TYPE_NONE) { + dbobj->nmobj = g_object_new(gtype, NULL); + + NML_NMCLIENT_LOG_T(self, + "[%s]: register new NMObject " NM_HASH_OBFUSCATE_PTR_FMT + " of type %s", + dbobj->dbus_path->str, + NM_HASH_OBFUSCATE_PTR(dbobj->nmobj), + g_type_name(gtype)); + + nm_assert(NM_IS_OBJECT(dbobj->nmobj)); + NM_OBJECT_GET_CLASS(dbobj->nmobj) + ->register_client(NM_OBJECT(dbobj->nmobj), self, dbobj); + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY, self); + } + } + } + + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + nm_assert(!db_iface_data->iface_removed); + if (!db_iface_data->dbus_iface_is_wellknown) + break; + if (c_list_is_empty(&db_iface_data->changed_prop_lst_head)) + continue; + if (dbobj->nmobj) + _obj_handle_dbus_iface_changes(self, dbobj, db_iface_data); + } + + if (c_list_is_empty(&dbobj->iface_lst_head) && dbobj->nmobj) { + if (dbobj->nmobj == G_OBJECT(self)) { + dbobj->nmobj = NULL; + NML_NMCLIENT_LOG_T(self, + "[%s]: unregister NMClient from D-Bus object", + dbobj->dbus_path->str); + if (dbobj->dbus_path == _dbus_path_nm) { + nm_assert(priv->dbobj_nm == dbobj); + priv->dbobj_nm = NULL; + nml_dbus_property_o_clear_many(priv->nm.property_o, + G_N_ELEMENTS(priv->nm.property_o), + self); + nml_dbus_property_ao_clear_many(priv->nm.property_ao, + G_N_ELEMENTS(priv->nm.property_ao), + self); + } else if (dbobj->dbus_path == _dbus_path_settings) { + nm_assert(priv->dbobj_settings == dbobj); + priv->dbobj_settings = NULL; + nml_dbus_property_ao_clear(&priv->settings.connections, self); + } else { + nm_assert(dbobj->dbus_path == _dbus_path_dns_manager); + nm_assert(priv->dbobj_dns_manager == dbobj); + priv->dbobj_dns_manager = NULL; + } + } else { + nmobj_unregistering = g_steal_pointer(&dbobj->nmobj); + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_WATCHED_ONLY, self); + NML_NMCLIENT_LOG_T(self, + "[%s]: unregister NMObject " NM_HASH_OBFUSCATE_PTR_FMT " of type %s", + dbobj->dbus_path->str, + NM_HASH_OBFUSCATE_PTR(nmobj_unregistering), + g_type_name(G_OBJECT_TYPE(nmobj_unregistering))); + NM_OBJECT_GET_CLASS(nmobj_unregistering) + ->unregister_client(NM_OBJECT(nmobj_unregistering), self, dbobj); + } + } + + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); +} + +/*****************************************************************************/ + +static void +_dbus_handle_obj_changed_nmobj(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + NMLDBusObject * dbobj; + CList obj_changed_tmp_lst_head = C_LIST_INIT(obj_changed_tmp_lst_head); + + nm_assert(!nml_dbus_object_obj_changed_any_linked(self, ~NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ)); + + /* First we notify all watchers that these objects changed. Note that we only do that + * here for the list before processing the changes below in a loop. That is, because + * processing changes can again enqueue changed objects, and we only want to want to + * notify watchers for the events that happened earlier (not repeatedly notify them). */ + c_list_splice(&obj_changed_tmp_lst_head, &priv->obj_changed_lst_head); + while ( + (dbobj = c_list_first_entry(&obj_changed_tmp_lst_head, NMLDBusObject, obj_changed_lst))) { + nm_c_list_move_tail(&priv->obj_changed_lst_head, &dbobj->obj_changed_lst); + _dbobjs_notify_watchers_for_dbobj(self, dbobj); + } + +again: + + nm_assert(!nml_dbus_object_obj_changed_any_linked(self, ~NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ)); + + c_list_splice(&obj_changed_tmp_lst_head, &priv->obj_changed_lst_head); + + while ( + (dbobj = c_list_first_entry(&obj_changed_tmp_lst_head, NMLDBusObject, obj_changed_lst))) { + if (!nml_dbus_object_obj_changed_consume(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ)) { + nm_assert_not_reached(); + continue; + } + + if (!dbobj->nmobj) + continue; + + if (dbobj->nmobj == G_OBJECT(self)) { + if (dbobj == priv->dbobj_nm) { + nml_dbus_property_o_notify_changed_many(priv->nm.property_o, + G_N_ELEMENTS(priv->nm.property_o), + self); + nml_dbus_property_ao_notify_changed_many(priv->nm.property_ao, + G_N_ELEMENTS(priv->nm.property_ao), + self); + } else if (dbobj == priv->dbobj_settings) + nml_dbus_property_ao_notify_changed(&priv->settings.connections, self); + else + nm_assert(dbobj == priv->dbobj_dns_manager); + } else + NM_OBJECT_GET_CLASS(dbobj->nmobj)->obj_changed_notify(NM_OBJECT(dbobj->nmobj)); + + _dbobjs_check_dbobj_ready(self, dbobj); + } + + if (!c_list_is_empty(&priv->obj_changed_lst_head)) { + nm_assert(nml_dbus_object_obj_changed_any_linked(self, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ)); + /* we got new changes enqueued. Need to check again. */ + goto again; + } +} + +static void +_dbus_handle_obj_changed_dbus(NMClient *self, const char *log_context) +{ + NMClientPrivate *priv; + NMLDBusObject * dbobj; + CList obj_changed_tmp_lst_head = C_LIST_INIT(obj_changed_tmp_lst_head); + + priv = NM_CLIENT_GET_PRIVATE(self); + + /* We move the changed list onto a temporary list and consume that. + * Note that nml_dbus_object_obj_changed_consume() will move the object + * back to the original list if there are changes of another type. + * + * This is done so that we can enqueue more changes while processing the + * change list. */ + c_list_splice(&obj_changed_tmp_lst_head, &priv->obj_changed_lst_head); + + while ( + (dbobj = c_list_first_entry(&obj_changed_tmp_lst_head, NMLDBusObject, obj_changed_lst))) { + nm_auto_unref_nml_dbusobj NMLDBusObject *dbobj_unref = NULL; + + if (!nml_dbus_object_obj_changed_consume(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_DBUS)) + continue; + + nm_assert(dbobj->obj_state >= NML_DBUS_OBJ_STATE_ON_DBUS); + + dbobj_unref = nml_dbus_object_ref(dbobj); + + _obj_handle_dbus_changes(self, dbobj); + + if (dbobj->obj_state == NML_DBUS_OBJ_STATE_UNLINKED) + continue; + + if (c_list_is_empty(&dbobj->iface_lst_head) && c_list_is_empty(&dbobj->watcher_lst_head)) { + NML_NMCLIENT_LOG_T(self, "[%s]: drop D-Bus instance", dbobj->dbus_path->str); + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_UNLINKED, self); + if (!g_hash_table_steal(priv->dbus_objects, dbobj)) + nm_assert_not_reached(); + nml_dbus_object_unref(dbobj); + } + } + + /* D-Bus changes can only be enqueued in an earlier stage. We don't expect + * anymore changes of type D-Bus at this point. */ + nm_assert(!nml_dbus_object_obj_changed_any_linked(self, NML_DBUS_OBJ_CHANGED_TYPE_DBUS)); +} + +static void +_dbus_handle_changes_commit(NMClient *self, gboolean allow_init_start_check_complete) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + + _dbus_handle_obj_changed_nmobj(self); + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->main_context); + + _nm_client_notify_event_emit(self); + + _set_nm_running(self); + + if (allow_init_start_check_complete) + _init_start_check_complete(self); +} + +static void +_dbus_handle_changes(NMClient * self, + const char *log_context, + gboolean allow_init_start_check_complete) +{ + _dbus_handle_obj_changed_dbus(self, log_context); + _dbus_handle_changes_commit(self, allow_init_start_check_complete); +} + +static gboolean +_dbus_handle_properties_changed(NMClient * self, + const char * log_context, + const char * object_path, + const char * interface_name, + gboolean allow_add_iface, + GVariant * changed_properties, + NMLDBusObject **inout_dbobj) +{ + NMLDBusObject * dbobj = NULL; + NMLDBusObjIfaceData *db_iface_data = NULL; + nm_auto_ref_string NMRefString *dbus_path = NULL; + + nm_assert(!changed_properties + || g_variant_is_of_type(changed_properties, G_VARIANT_TYPE("a{sv}"))); + + { + gs_free char *ss = NULL; + + NML_NMCLIENT_LOG_T(self, + "[%s]: %s: properties changed for interface %s %s%s%s", + object_path, + log_context, + interface_name, + NM_PRINT_FMT_QUOTED(changed_properties, + "{ ", + (ss = g_variant_print(changed_properties, TRUE)), + " }", + "(no changed properties)")); + } + + if (inout_dbobj) { + dbobj = *inout_dbobj; + nm_assert(!dbobj || nm_streq(object_path, dbobj->dbus_path->str)); + } + if (!dbobj) { + dbus_path = nm_ref_string_new(object_path); + dbobj = _dbobjs_dbobj_get_r(self, dbus_path); + } + + if (dbobj) { + nm_assert(dbobj->obj_state >= NML_DBUS_OBJ_STATE_WATCHED_ONLY); + db_iface_data = nml_dbus_object_iface_data_get(dbobj, interface_name, allow_add_iface); + if (db_iface_data && dbobj->obj_state == NML_DBUS_OBJ_STATE_WATCHED_ONLY) + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_ON_DBUS, self); + } else if (allow_add_iface) { + dbobj = _dbobjs_dbobj_create(self, g_steal_pointer(&dbus_path)); + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_ON_DBUS, self); + db_iface_data = nml_dbus_object_iface_data_get(dbobj, interface_name, TRUE); + nm_assert(db_iface_data); + } + + NM_SET_OUT(inout_dbobj, dbobj); + + if (!db_iface_data) { + if (allow_add_iface) + NML_NMCLIENT_LOG_E(self, + "%s: [%s] too many interfaces on object. Something is very wrong", + log_context, + object_path); + else + NML_NMCLIENT_LOG_E(self, + "%s: [%s] property changed signal for non existing interface %s", + log_context, + object_path, + interface_name); + nm_assert(!dbobj || dbobj->obj_state != NML_DBUS_OBJ_STATE_UNLINKED); + return FALSE; + } + + if (!db_iface_data->dbus_iface_is_wellknown) + NML_NMCLIENT_LOG_W(self, + "%s: [%s] ignore unknown interface %s", + log_context, + object_path, + interface_name); + else if (changed_properties) { + GVariantIter iter_prop; + const char * property_name; + GVariant * property_value_tmp; + + g_variant_iter_init(&iter_prop, changed_properties); + while (g_variant_iter_next(&iter_prop, "{&sv}", &property_name, &property_value_tmp)) { + _nm_unused gs_unref_variant GVariant *property_value = property_value_tmp; + const NMLDBusMetaProperty * meta_property; + NMLDBusObjPropData * db_propdata; + guint property_idx; + + meta_property = nml_dbus_meta_property_get(db_iface_data->dbus_iface.meta, + property_name, + &property_idx); + if (!meta_property) { + NML_NMCLIENT_LOG_W(self, + "%s: [%s]: ignore unknown property %s.%s", + log_context, + object_path, + interface_name, + property_name); + continue; + } + + db_propdata = &db_iface_data->prop_datas[property_idx]; + + NML_NMCLIENT_LOG_T(self, + "[%s]: %s: %s property %s.%s", + object_path, + log_context, + db_propdata->prop_data_value ? "update" : "set", + interface_name, + property_name); + + nm_g_variant_unref(db_propdata->prop_data_value); + db_propdata->prop_data_value = g_steal_pointer(&property_value); + nm_c_list_move_tail(&db_iface_data->changed_prop_lst_head, + &db_propdata->changed_prop_lst); + } + } + + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_DBUS); + return TRUE; +} + +static gboolean +_dbus_handle_interface_added(NMClient * self, + const char *log_context, + const char *object_path, + GVariant * ifaces) +{ + gboolean changed = FALSE; + const char * interface_name; + GVariant * changed_properties; + GVariantIter iter_ifaces; + NMLDBusObject *dbobj = NULL; + + nm_assert(g_variant_is_of_type(ifaces, G_VARIANT_TYPE("a{sa{sv}}"))); + + g_variant_iter_init(&iter_ifaces, ifaces); + while (g_variant_iter_next(&iter_ifaces, "{&s@a{sv}}", &interface_name, &changed_properties)) { + _nm_unused gs_unref_variant GVariant *changed_properties_free = changed_properties; + + if (_dbus_handle_properties_changed(self, + log_context, + object_path, + interface_name, + TRUE, + changed_properties, + &dbobj)) + changed = TRUE; + } + + return changed; +} + +static gboolean +_dbus_handle_interface_removed(NMClient * self, + const char * log_context, + const char * object_path, + NMLDBusObject ** inout_dbobj, + const char *const *removed_interfaces) +{ + gboolean changed = FALSE; + NMLDBusObject *dbobj; + gsize i; + + if (inout_dbobj && *inout_dbobj) { + dbobj = *inout_dbobj; + nm_assert(dbobj == _dbobjs_dbobj_get_s(self, object_path)); + } else { + dbobj = _dbobjs_dbobj_get_s(self, object_path); + if (!dbobj) { + NML_NMCLIENT_LOG_E(self, + "%s: [%s]: receive interface removed event for non existing object", + log_context, + object_path); + return FALSE; + } + NM_SET_OUT(inout_dbobj, dbobj); + } + + for (i = 0; removed_interfaces[i]; i++) { + NMLDBusObjIfaceData *db_iface_data; + const char * interface_name = removed_interfaces[i]; + + db_iface_data = nml_dbus_object_iface_data_get(dbobj, interface_name, FALSE); + if (!db_iface_data) { + NML_NMCLIENT_LOG_E( + self, + "%s: [%s] receive interface remove event for unexpected interface %s", + log_context, + object_path, + interface_name); + continue; + } + + NML_NMCLIENT_LOG_T(self, + "%s: [%s] receive interface remove event for interface %s", + log_context, + object_path, + interface_name); + db_iface_data->iface_removed = TRUE; + changed = TRUE; + } + + if (changed) + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_DBUS); + + return changed; +} + +static void +_dbus_managed_objects_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * arg_object_path, + const char * interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + const char * log_context; + gboolean changed; + + nm_assert(nm_streq0(interface_name, DBUS_INTERFACE_OBJECT_MANAGER)); + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (nm_streq(signal_name, "InterfacesAdded")) { + gs_unref_variant GVariant *interfaces_and_properties = NULL; + const char * object_path; + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(oa{sa{sv}})"))) + return; + + g_variant_get(parameters, "(&o@a{sa{sv}})", &object_path, &interfaces_and_properties); + + log_context = "interfaces-added"; + changed = + _dbus_handle_interface_added(self, log_context, object_path, interfaces_and_properties); + goto out; + } + + if (nm_streq(signal_name, "InterfacesRemoved")) { + gs_free const char **interfaces = NULL; + const char * object_path; + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(oas)"))) + return; + + g_variant_get(parameters, "(&o^a&s)", &object_path, &interfaces); + + log_context = "interfaces-removed"; + changed = _dbus_handle_interface_removed(self, log_context, object_path, NULL, interfaces); + goto out; + } + + return; + +out: + if (changed) + _dbus_handle_changes(self, log_context, TRUE); +} + +static void +_dbus_properties_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * signal_interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + const char * interface_name; + gs_unref_variant GVariant *changed_properties = NULL; + gs_free const char ** invalidated_properties = NULL; + const char * log_context = "properties-changed"; + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sa{sv}as)"))) + return; + + g_variant_get(parameters, + "(&s@a{sv}^a&s)", + &interface_name, + &changed_properties, + &invalidated_properties); + + if (invalidated_properties && invalidated_properties[0]) { + NML_NMCLIENT_LOG_W(self, + "%s: [%s] ignore invalidated properties on interface %s", + log_context, + object_path, + interface_name); + } + + if (_dbus_handle_properties_changed(self, + log_context, + object_path, + interface_name, + FALSE, + changed_properties, + NULL)) + _dbus_handle_changes(self, log_context, TRUE); +} + +static void +_dbus_get_managed_objects_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMClient * self; + NMClientPrivate *priv; + gs_unref_variant GVariant *ret = NULL; + gs_unref_variant GVariant *managed_objects = NULL; + gs_free_error GError *error = NULL; + gs_unref_object GObject *context_busy_watcher = NULL; + + nm_utils_user_data_unpack(user_data, &self, &context_busy_watcher); + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + + nm_assert((!!ret) != (!!error)); + + if (!ret && nm_utils_error_is_cancelled(error)) + return; + + priv = NM_CLIENT_GET_PRIVATE(self); + + if (ret) { + nm_assert(g_variant_is_of_type(ret, G_VARIANT_TYPE("(a{oa{sa{sv}}})"))); + managed_objects = g_variant_get_child_value(ret, 0); + } + + g_clear_object(&priv->get_managed_objects_cancellable); + + if (!managed_objects) { + NML_NMCLIENT_LOG_D(self, "GetManagedObjects() call failed: %s", error->message); + /* hm, now that's odd. Maybe NetworkManager just quit and we are about to get + * a name-owner changed signal soon. Treat this as if we got no managed objects at all. */ + } else + NML_NMCLIENT_LOG_D(self, "GetManagedObjects() completed"); + + if (managed_objects) { + GVariantIter iter; + const char * object_path; + GVariant * ifaces_tmp; + + g_variant_iter_init(&iter, managed_objects); + while (g_variant_iter_next(&iter, "{&o@a{sa{sv}}}", &object_path, &ifaces_tmp)) { + gs_unref_variant GVariant *ifaces = ifaces_tmp; + + _dbus_handle_interface_added(self, "get-managed-objects", object_path, ifaces); + } + } + + /* always call _dbus_handle_changes(), even if nothing changed. We need this to complete + * initialization. */ + _dbus_handle_changes(self, "get-managed-objects", TRUE); +} + +/*****************************************************************************/ + +static void +_nm_client_get_settings_call_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMRemoteConnection *remote_connection; + NMClient * self; + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *settings = NULL; + NMLDBusObject * dbobj; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (!ret && nm_utils_error_is_cancelled(error)) + return; + + remote_connection = user_data; + + self = _nm_object_get_client(remote_connection); + + dbobj = _nm_object_get_dbobj(remote_connection); + + _ASSERT_dbobj(dbobj, self); + + if (!ret) { + NML_NMCLIENT_LOG_T(self, + "[%s] GetSettings() completed with error: %s", + dbobj->dbus_path->str, + error->message); + } else { + NML_NMCLIENT_LOG_T(self, + "[%s] GetSettings() completed with success", + dbobj->dbus_path->str); + g_variant_get(ret, "(@a{sa{sv}})", &settings); + } + + _nm_remote_settings_get_settings_commit(remote_connection, settings); + + _dbus_handle_changes_commit(self, TRUE); +} + +void +_nm_client_get_settings_call(NMClient *self, NMLDBusObject *dbobj) +{ + GCancellable *cancellable; + + cancellable = _nm_remote_settings_get_settings_prepare(NM_REMOTE_CONNECTION(dbobj->nmobj)); + + _nm_client_dbus_call_simple(self, + cancellable, + dbobj->dbus_path->str, + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "GetSettings", + g_variant_new("()"), + G_VARIANT_TYPE("(a{sa{sv}})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _nm_client_get_settings_call_cb, + dbobj->nmobj); +} + +static void +_dbus_settings_updated_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * signal_interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + const char * log_context = "settings-updated"; + NMLDBusObject * dbobj; + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("()"))) + return; + + dbobj = _dbobjs_dbobj_get_s(self, object_path); + + if (!dbobj || !NM_IS_REMOTE_CONNECTION(dbobj->nmobj)) { + NML_NMCLIENT_LOG_W(self, + "%s: [%s] ignore Updated signal for non-existing setting", + log_context, + object_path); + return; + } + + NML_NMCLIENT_LOG_T(self, "%s: [%s] Updated signal received", log_context, object_path); + + _nm_client_get_settings_call(self, dbobj); +} + +/*****************************************************************************/ + +static void +_dbus_nm_connection_active_state_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * signal_interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + const char * log_context = "active-connection-state-changed"; + NMLDBusObject * dbobj; + guint32 state; + guint32 reason; + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(uu)"))) { + NML_NMCLIENT_LOG_E(self, + "%s: [%s] ignore StateChanged signal with unexpected signature", + log_context, + object_path); + return; + } + + dbobj = _dbobjs_dbobj_get_s(self, object_path); + + if (!dbobj || !NM_IS_ACTIVE_CONNECTION(dbobj->nmobj)) { + NML_NMCLIENT_LOG_E(self, + "%s: [%s] ignore StateChanged signal for non-existing active connection", + log_context, + object_path); + return; + } + + g_variant_get(parameters, "(uu)", &state, &reason); + + NML_NMCLIENT_LOG_T(self, "%s: [%s] StateChanged signal received", log_context, object_path); + + _nm_active_connection_state_changed_commit(NM_ACTIVE_CONNECTION(dbobj->nmobj), state, reason); + + _dbus_handle_changes_commit(self, TRUE); +} + +/*****************************************************************************/ + +static void +_dbus_nm_vpn_connection_state_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * signal_interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + const char * log_context = "vpn-connection-state-changed"; + NMLDBusObject * dbobj; + guint32 state; + guint32 reason; + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(uu)"))) { + NML_NMCLIENT_LOG_E(self, + "%s: [%s] ignore VpnStateChanged signal with unexpected signature", + log_context, + object_path); + return; + } + + dbobj = _dbobjs_dbobj_get_s(self, object_path); + + if (!dbobj || !NM_IS_VPN_CONNECTION(dbobj->nmobj)) { + NML_NMCLIENT_LOG_E(self, + "%s: [%s] ignore VpnStateChanged signal for non-existing vpn connection", + log_context, + object_path); + return; + } + + g_variant_get(parameters, "(uu)", &state, &reason); + + NML_NMCLIENT_LOG_T(self, "%s: [%s] VpnStateChanged signal received", log_context, object_path); + + _nm_vpn_connection_state_changed_commit(NM_VPN_CONNECTION(dbobj->nmobj), state, reason); + + _dbus_handle_changes_commit(self, TRUE); +} + +/*****************************************************************************/ + +static void +_emit_permissions_changed(NMClient *self, const guint8 *old_permissions, const guint8 *permissions) +{ + int i; + + if (self->obj_base.is_disposing) + return; + + if (old_permissions == permissions) + return; + + for (i = 0; i < (int) G_N_ELEMENTS(nm_auth_permission_sorted); i++) { + NMClientPermission perm = nm_auth_permission_sorted[i]; + NMClientPermissionResult perm_result = NM_CLIENT_PERMISSION_RESULT_UNKNOWN; + NMClientPermissionResult perm_result_old = NM_CLIENT_PERMISSION_RESULT_UNKNOWN; + + if (permissions) + perm_result = permissions[perm - 1]; + if (old_permissions) + perm_result_old = old_permissions[perm - 1]; + + if (perm_result == perm_result_old) + continue; + + g_signal_emit(self, signals[PERMISSION_CHANGED], 0, (guint) perm, (guint) perm_result); + } +} + +static void +_dbus_check_permissions_start_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + NMClient * self; + NMClientPrivate * priv; + gs_unref_variant GVariant *ret = NULL; + nm_auto_free_variant_iter GVariantIter *v_permissions = NULL; + gs_free guint8 *old_permissions = NULL; + gs_free_error GError *error = NULL; + const char * pkey; + const char * pvalue; + int i; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (!ret && nm_utils_error_is_cancelled(error)) + return; + + self = user_data; + priv = NM_CLIENT_GET_PRIVATE(self); + + g_clear_object(&priv->permissions_cancellable); + + old_permissions = g_steal_pointer(&priv->permissions); + + if (!ret) { + /* when the call completes, we always pretend success. Even a failure means + * that we fetched the permissions, however they are all unknown. */ + NML_NMCLIENT_LOG_T(self, "GetPermissions call failed: %s", error->message); + goto out; + } + + NML_NMCLIENT_LOG_T(self, "GetPermissions call finished with success"); + + g_variant_get(ret, "(a{ss})", &v_permissions); + while (g_variant_iter_next(v_permissions, "{&s&s}", &pkey, &pvalue)) { + NMClientPermission perm; + NMClientPermissionResult perm_result; + + perm = nm_auth_permission_from_string(pkey); + if (perm == NM_CLIENT_PERMISSION_NONE) + continue; + + perm_result = nm_client_permission_result_from_string(pvalue); + + if (!priv->permissions) { + if (perm_result == NM_CLIENT_PERMISSION_RESULT_UNKNOWN) + continue; + priv->permissions = g_new(guint8, G_N_ELEMENTS(nm_auth_permission_sorted)); + for (i = 0; i < (int) G_N_ELEMENTS(nm_auth_permission_sorted); i++) + priv->permissions[i] = NM_CLIENT_PERMISSION_RESULT_UNKNOWN; + } + priv->permissions[perm - 1] = perm_result; + } + +out: + priv->permissions_state = NM_TERNARY_TRUE; + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->main_context); + _emit_permissions_changed(self, old_permissions, priv->permissions); + _notify(self, PROP_PERMISSIONS_STATE); +} + +static void +_dbus_check_permissions_start(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + gboolean fetch; + + fetch = !NM_FLAGS_HAS((NMClientInstanceFlags) priv->instance_flags, + NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS); + + nm_clear_g_cancellable(&priv->permissions_cancellable); + + if (fetch) { + NML_NMCLIENT_LOG_T(self, "GetPermissions() call started..."); + + priv->permissions_cancellable = g_cancellable_new(); + _nm_client_dbus_call_simple(self, + priv->permissions_cancellable, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "GetPermissions", + g_variant_new("()"), + G_VARIANT_TYPE("(a{ss})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _dbus_check_permissions_start_cb, + self); + } +} + +static void +_dbus_nm_check_permissions_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * signal_interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("()"))) { + NML_NMCLIENT_LOG_E(self, + "ignore CheckPermissions signal with unexpected signature %s", + g_variant_get_type_string(parameters)); + return; + } + + _dbus_check_permissions_start(self); + + if (priv->permissions_state == NM_TERNARY_TRUE) + priv->permissions_state = NM_TERNARY_FALSE; + _notify(self, PROP_PERMISSIONS_STATE); +} + +/*****************************************************************************/ + +static void +_property_ao_notify_changed_connections_cb(NMLDBusPropertyAO *pr_ao, + NMClient * self, + NMObject * nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal(self, + G_OBJECT(self), + nmobj, + is_added, + 5, + is_added ? signals[CONNECTION_ADDED] + : signals[CONNECTION_REMOVED]); +} + +static void +_property_ao_notify_changed_all_devices_cb(NMLDBusPropertyAO *pr_ao, + NMClient * self, + NMObject * nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal(self, + G_OBJECT(self), + nmobj, + is_added, + 6, + is_added ? signals[ANY_DEVICE_ADDED] + : signals[ANY_DEVICE_REMOVED]); +} + +static void +_property_ao_notify_changed_devices_cb(NMLDBusPropertyAO *pr_ao, + NMClient * self, + NMObject * nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal(self, + G_OBJECT(self), + nmobj, + is_added, + 7, + is_added ? signals[DEVICE_ADDED] + : signals[DEVICE_REMOVED]); +} + +static void +_property_ao_notify_changed_active_connections_cb(NMLDBusPropertyAO *pr_ao, + NMClient * self, + NMObject * nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal(self, + G_OBJECT(self), + nmobj, + is_added, + 8, + is_added ? signals[ACTIVE_CONNECTION_ADDED] + : signals[ACTIVE_CONNECTION_REMOVED]); +} + +/*****************************************************************************/ + +typedef struct { + NMLDBusObjWatcherWithPtr *obj_watcher; + const char * op_name; + NMLDBusObject * dbobj; + GTask * task; + GVariant * extra_results; + gpointer result; + GType gtype; + gulong cancellable_id; +} RequestWaitData; + +static void +_request_wait_data_free(RequestWaitData *request_data) +{ + nm_assert(!request_data->obj_watcher); + nm_assert(request_data->cancellable_id == 0); + nm_assert(!request_data->task || G_IS_TASK(request_data->task)); + + nm_g_object_unref(request_data->task); + nm_g_object_unref(request_data->result); + nm_g_variant_unref(request_data->extra_results); + if (request_data->dbobj) + nml_dbus_object_unref(request_data->dbobj); + nm_g_slice_free(request_data); +} + +static void +_request_wait_task_return(RequestWaitData *request_data) +{ + gs_unref_object GTask *task = NULL; + + nm_assert(request_data); + nm_assert(G_IS_TASK(request_data->task)); + nm_assert(request_data->dbobj); + nm_assert(NM_IS_OBJECT(request_data->dbobj->nmobj)); + nm_assert(!request_data->result); + + task = g_steal_pointer(&request_data->task); + + request_data->result = g_object_ref(request_data->dbobj->nmobj); + nm_clear_g_signal_handler(g_task_get_cancellable(task), &request_data->cancellable_id); + nm_clear_pointer(&request_data->dbobj, nml_dbus_object_unref); + g_task_return_pointer(task, request_data, (GDestroyNotify) _request_wait_data_free); +} + +static gboolean +_request_wait_complete(NMClient *self, RequestWaitData *request_data, gboolean force_complete) +{ + NMLDBusObject *dbobj; + + nm_assert(request_data); + nm_assert(!request_data->result); + nm_assert(!request_data->obj_watcher); + nm_assert(request_data->dbobj); + + dbobj = request_data->dbobj; + + if (dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY) { + NML_NMCLIENT_LOG_D(self, + "%s() succeeded with %s", + request_data->op_name, + dbobj->dbus_path->str); + nm_assert(G_TYPE_CHECK_INSTANCE_TYPE(dbobj->nmobj, request_data->gtype)); + _request_wait_task_return(request_data); + return TRUE; + } + + if (force_complete || dbobj->obj_state != NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY) { + if (force_complete) + NML_NMCLIENT_LOG_D(self, + "%s() succeeded with %s but object is in an unsuitable state", + request_data->op_name, + dbobj->dbus_path->str); + else + NML_NMCLIENT_LOG_W(self, + "%s() succeeded with %s but object is in an unsuitable state", + request_data->op_name, + dbobj->dbus_path->str); + + g_task_return_error( + request_data->task, + g_error_new(NM_CLIENT_ERROR, + NM_CLIENT_ERROR_OBJECT_CREATION_FAILED, + _("request succeeded with %s but object is in an unsuitable state"), + dbobj->dbus_path->str)); + _request_wait_data_free(request_data); + return TRUE; + } + + return FALSE; +} + +static void +_request_wait_complete_cb(NMClient *self, NMClientNotifyEventWithPtr *notify_event) +{ + _request_wait_complete(self, notify_event->user_data, TRUE); +} + +static void +_request_wait_obj_watcher_cb(NMClient *self, gpointer obj_watcher_base) +{ + NMLDBusObjWatcherWithPtr *obj_watcher = obj_watcher_base; + RequestWaitData * request_data = obj_watcher->user_data; + NMLDBusObject * dbobj; + + dbobj = request_data->dbobj; + + if (dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY) + return; + + nm_assert(NM_IN_SET((NMLDBusObjState) dbobj->obj_state, + NML_DBUS_OBJ_STATE_WATCHED_ONLY, + NML_DBUS_OBJ_STATE_ON_DBUS, + NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY)); + + _dbobjs_obj_watcher_unregister(self, g_steal_pointer(&request_data->obj_watcher)); + + nm_clear_g_signal_handler(g_task_get_cancellable(request_data->task), + &request_data->cancellable_id); + + _nm_client_notify_event_queue_with_ptr(self, + NM_CLIENT_NOTIFY_EVENT_PRIO_AFTER + 30, + _request_wait_complete_cb, + request_data); +} + +static void +_request_wait_cancelled_cb(GCancellable *cancellable, gpointer user_data) +{ + RequestWaitData *request_data = user_data; + NMClient * self; + GError * error = NULL; + + nm_assert(cancellable == g_task_get_cancellable(request_data->task)); + + nm_utils_error_set_cancelled(&error, FALSE, NULL); + + self = g_task_get_source_object(request_data->task); + + nm_clear_g_signal_handler(cancellable, &request_data->cancellable_id); + + _dbobjs_obj_watcher_unregister(self, g_steal_pointer(&request_data->obj_watcher)); + + g_task_return_error(request_data->task, error); + + _request_wait_data_free(request_data); +} + +static void +_request_wait_start(GTask * task_take, + const char *op_name, + GType gtype, + const char *dbus_path, + GVariant * extra_results_take) +{ + NMClient * self; + gs_unref_object GTask *task = g_steal_pointer(&task_take); + RequestWaitData * request_data; + GCancellable * cancellable; + NMLDBusObject * dbobj; + + nm_assert(G_IS_TASK(task)); + + self = g_task_get_source_object(task); + + dbobj = _dbobjs_get_nmobj(self, dbus_path, gtype); + + if (!dbobj) { + NML_NMCLIENT_LOG_E(self, + "%s() succeeded with %s but object does not exist", + op_name, + dbus_path); + g_task_return_error(task, + g_error_new(NM_CLIENT_ERROR, + NM_CLIENT_ERROR_FAILED, + _("operation succeeded but object %s does not exist"), + dbus_path)); + return; + } + + request_data = g_slice_new(RequestWaitData); + *request_data = (RequestWaitData){ + .task = g_steal_pointer(&task), + .op_name = op_name, + .gtype = gtype, + .dbobj = nml_dbus_object_ref(dbobj), + .obj_watcher = NULL, + .extra_results = g_steal_pointer(&extra_results_take), + .result = NULL, + .cancellable_id = 0, + }; + + if (_request_wait_complete(self, request_data, FALSE)) + return; + + NML_NMCLIENT_LOG_T(self, + "%s() succeeded with %s. Wait for object to become ready", + op_name, + dbobj->dbus_path->str); + + request_data->obj_watcher = _dbobjs_obj_watcher_register_o(self, + dbobj, + _request_wait_obj_watcher_cb, + sizeof(NMLDBusObjWatcherWithPtr)); + request_data->obj_watcher->user_data = request_data; + + cancellable = g_task_get_cancellable(request_data->task); + if (cancellable) { + gulong id; + + id = g_cancellable_connect(cancellable, + G_CALLBACK(_request_wait_cancelled_cb), + request_data, + NULL); + if (id == 0) { + /* the callback was invoked synchronously, which destroyed @request_data. + * We must not touch @info anymore. */ + } else + request_data->cancellable_id = id; + } +} + +static gpointer +_request_wait_finish(NMClient * client, + GAsyncResult *result, + gpointer source_tag, + GVariant ** out_result, + GError ** error) +{ + RequestWaitData *request_data = NULL; + gpointer r; + + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(nm_g_task_is_valid(result, client, source_tag), NULL); + + request_data = g_task_propagate_pointer(G_TASK(result), error); + if (!request_data) { + NM_SET_OUT(out_result, NULL); + return NULL; + } + + nm_assert(NM_IS_OBJECT(request_data->result)); + + NM_SET_OUT(out_result, g_steal_pointer(&request_data->extra_results)); + r = g_steal_pointer(&request_data->result); + + nm_assert(NM_IS_OBJECT(r)); + + _request_wait_data_free(request_data); + return r; +} + +/*****************************************************************************/ + +/** + * nm_client_get_instance_flags: + * @self: the #NMClient instance. + * + * Returns: the #NMClientInstanceFlags flags. + * + * Since: 1.24 + */ +NMClientInstanceFlags +nm_client_get_instance_flags(NMClient *self) +{ + g_return_val_if_fail(NM_IS_CLIENT(self), NM_CLIENT_INSTANCE_FLAGS_NONE); + + return NM_CLIENT_GET_PRIVATE(self)->instance_flags; +} + +/** + * nm_client_get_dbus_connection: + * @client: a #NMClient + * + * Gets the %GDBusConnection of the instance. This can be either passed when + * constructing the instance (as "dbus-connection" property), or it will be + * automatically initialized during async/sync init. + * + * Returns: (transfer none): the D-Bus connection of the client, or %NULL if none is set. + * + * Since: 1.22 + **/ +GDBusConnection * +nm_client_get_dbus_connection(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return NM_CLIENT_GET_PRIVATE(client)->dbus_connection; +} + +/** + * nm_client_get_dbus_name_owner: + * @client: a #NMClient + * + * Returns: (transfer none): the current name owner of the D-Bus service of NetworkManager. + * + * Since: 1.22 + **/ +const char * +nm_client_get_dbus_name_owner(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return NM_CLIENT_GET_PRIVATE(client)->name_owner; +} + +/** + * nm_client_get_version: + * @client: a #NMClient + * + * Gets NetworkManager version. + * + * Returns: string with the version (or %NULL if NetworkManager is not running) + **/ +const char * +nm_client_get_version(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return NM_CLIENT_GET_PRIVATE(client)->nm.version; +} + +/** + * nm_client_get_state: + * @client: a #NMClient + * + * Gets the current daemon state. + * + * Returns: the current %NMState + **/ +NMState +nm_client_get_state(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NM_STATE_UNKNOWN); + + return NM_CLIENT_GET_PRIVATE(client)->nm.state; +} + +/** + * nm_client_get_startup: + * @client: a #NMClient + * + * Tests whether the daemon is still in the process of activating + * connections at startup. + * + * Returns: whether the daemon is still starting up + **/ +gboolean +nm_client_get_startup(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.startup; +} + +static void +_set_nm_running(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + gboolean nm_running; + + nm_running = priv->name_owner && !priv->get_managed_objects_cancellable; + if (priv->nm_running != nm_running) { + priv->nm_running = nm_running; + _notify(self, PROP_NM_RUNNING); + } +} + +/** + * nm_client_get_nm_running: + * @client: a #NMClient + * + * Determines whether the daemon is running. + * + * Returns: %TRUE if the daemon is running + **/ +gboolean +nm_client_get_nm_running(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm_running; +} + +/** + * nm_client_get_object_by_path: + * @client: the #NMClient instance + * @dbus_path: the D-Bus path of the object to look up + * + * Returns: (transfer none): the #NMObject instance that is + * cached under @dbus_path, or %NULL if no such object exists. + * + * Since: 1.24 + */ +NMObject * +nm_client_get_object_by_path(NMClient *client, const char *dbus_path) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(dbus_path, NULL); + + return _dbobjs_get_nmobj_unpack_visible(client, dbus_path, G_TYPE_NONE); +} + +/** + * nm_client_get_metered: + * @client: a #NMClient + * + * Returns: whether the default route is metered. + * + * Since: 1.22 + */ +NMMetered +nm_client_get_metered(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NM_METERED_UNKNOWN); + + return NM_CLIENT_GET_PRIVATE(client)->nm.metered; +} + +/** + * nm_client_networking_get_enabled: + * @client: a #NMClient + * + * Whether networking is enabled or disabled. + * + * Returns: %TRUE if networking is enabled, %FALSE if networking is disabled + **/ +gboolean +nm_client_networking_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.networking_enabled; +} + +/** + * nm_client_networking_set_enabled: + * @client: a #NMClient + * @enabled: %TRUE to set networking enabled, %FALSE to set networking disabled + * @error: (allow-none): return location for a #GError, or %NULL + * + * Enables or disables networking. When networking is disabled, all controlled + * interfaces are disconnected and deactivated. When networking is enabled, + * all controlled interfaces are available for activation. + * + * Returns: %TRUE on success, %FALSE otherwise + * + * Deprecated: 1.22: Use the async command nm_client_dbus_call() on %NM_DBUS_PATH, + * %NM_DBUS_INTERFACE to call "Enable" with "(b)" arguments and no return value. + **/ +gboolean +nm_client_networking_set_enabled(NMClient *client, gboolean enable, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return _nm_client_dbus_call_sync_void(client, + NULL, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "Enable", + g_variant_new("(b)", enable), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_client_wireless_get_enabled: + * @client: a #NMClient + * + * Determines whether the wireless is enabled. + * + * Returns: %TRUE if wireless is enabled + **/ +gboolean +nm_client_wireless_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.wireless_enabled; +} + +/** + * nm_client_wireless_set_enabled: + * @client: a #NMClient + * @enabled: %TRUE to enable wireless + * + * Enables or disables wireless devices. + * + * Deprecated: 1.22: Use the async command nm_client_dbus_set_property() on %NM_DBUS_PATH, + * %NM_DBUS_INTERFACE to set "WirelessEnabled" property to a "(b)" value. + */ +void +nm_client_wireless_set_enabled(NMClient *client, gboolean enabled) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + + _nm_client_set_property_sync_legacy(client, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "WirelessEnabled", + "b", + enabled); +} + +/** + * nm_client_wireless_hardware_get_enabled: + * @client: a #NMClient + * + * Determines whether the wireless hardware is enabled. + * + * Returns: %TRUE if the wireless hardware is enabled + **/ +gboolean +nm_client_wireless_hardware_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.wireless_hardware_enabled; +} + +/** + * nm_client_wwan_get_enabled: + * @client: a #NMClient + * + * Determines whether WWAN is enabled. + * + * Returns: %TRUE if WWAN is enabled + **/ +gboolean +nm_client_wwan_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.wwan_enabled; +} + +/** + * nm_client_wwan_set_enabled: + * @client: a #NMClient + * @enabled: %TRUE to enable WWAN + * + * Enables or disables WWAN devices. + * + * Deprecated: 1.22: Use the async command nm_client_dbus_set_property() on %NM_DBUS_PATH, + * %NM_DBUS_INTERFACE to set "WwanEnabled" property to a "(b)" value. + **/ +void +nm_client_wwan_set_enabled(NMClient *client, gboolean enabled) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + + _nm_client_set_property_sync_legacy(client, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "WwanEnabled", + "b", + enabled); +} + +/** + * nm_client_wwan_hardware_get_enabled: + * @client: a #NMClient + * + * Determines whether the WWAN hardware is enabled. + * + * Returns: %TRUE if the WWAN hardware is enabled + **/ +gboolean +nm_client_wwan_hardware_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.wwan_hardware_enabled; +} + +/** + * nm_client_wimax_get_enabled: + * @client: a #NMClient + * + * Determines whether WiMAX is enabled. + * + * Returns: %TRUE if WiMAX is enabled + * + * Deprecated: 1.22: This function always returns FALSE because WiMax is no longer supported. + **/ +gboolean +nm_client_wimax_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return FALSE; +} + +/** + * nm_client_wimax_set_enabled: + * @client: a #NMClient + * @enabled: %TRUE to enable WiMAX + * + * Enables or disables WiMAX devices. + * + * Deprecated: 1.22: This function does nothing because WiMax is no longer supported. + **/ +void +nm_client_wimax_set_enabled(NMClient *client, gboolean enabled) +{ + g_return_if_fail(NM_IS_CLIENT(client)); +} + +/** + * nm_client_wimax_hardware_get_enabled: + * @client: a #NMClient + * + * Determines whether the WiMAX hardware is enabled. + * + * Returns: %TRUE if the WiMAX hardware is enabled + * + * Deprecated: 1.22: This function always returns FALSE because WiMax is no longer supported. + **/ +gboolean +nm_client_wimax_hardware_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return FALSE; +} + +/** + * nm_client_connectivity_check_get_available: + * @client: a #NMClient + * + * Determine whether connectivity checking is available. This + * requires that the URI of a connectivity service has been set in the + * configuration file. + * + * Returns: %TRUE if connectivity checking is available. + * + * Since: 1.10 + */ +gboolean +nm_client_connectivity_check_get_available(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.connectivity_check_available; +} + +/** + * nm_client_connectivity_check_get_enabled: + * @client: a #NMClient + * + * Determine whether connectivity checking is enabled. + * + * Returns: %TRUE if connectivity checking is enabled. + * + * Since: 1.10 + */ +gboolean +nm_client_connectivity_check_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.connectivity_check_enabled; +} + +/** + * nm_client_connectivity_check_set_enabled: + * @client: a #NMClient + * @enabled: %TRUE to enable connectivity checking + * + * Enable or disable connectivity checking. Note that if a + * connectivity checking URI has not been configured, this will not + * have any effect. + * + * Since: 1.10 + * + * Deprecated: 1.22: Use the async command nm_client_dbus_set_property() on %NM_DBUS_PATH, + * %NM_DBUS_INTERFACE to set "ConnectivityCheckEnabled" property to a "(b)" value. + */ +void +nm_client_connectivity_check_set_enabled(NMClient *client, gboolean enabled) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + + _nm_client_set_property_sync_legacy(client, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "ConnectivityCheckEnabled", + "b", + enabled); +} + +/** + * nm_client_connectivity_check_get_uri: + * @client: a #NMClient + * + * Get the URI that will be queried to determine if there is internet + * connectivity. + * + * Returns: (transfer none): the connectivity URI in use + * + * Since: 1.20 + */ +const char * +nm_client_connectivity_check_get_uri(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return NM_CLIENT_GET_PRIVATE(client)->nm.connectivity_check_uri; +} + +/** + * nm_client_get_logging: + * @client: a #NMClient + * @level: (allow-none): return location for logging level string + * @domains: (allow-none): return location for log domains string. The string is + * a list of domains separated by "," + * @error: (allow-none): return location for a #GError, or %NULL + * + * Gets NetworkManager current logging level and domains. + * + * Returns: %TRUE on success, %FALSE otherwise + * + * Deprecated: 1.22: Use the async command nm_client_dbus_call() on %NM_DBUS_PATH, + * %NM_DBUS_INTERFACE to call "GetLogging" with no arguments to get "(ss)" for level + * and domains. + **/ +gboolean +nm_client_get_logging(NMClient *client, char **level, char **domains, GError **error) +{ + gs_unref_variant GVariant *ret = NULL; + + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(level == NULL || *level == NULL, FALSE); + g_return_val_if_fail(domains == NULL || *domains == NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + ret = _nm_client_dbus_call_sync(client, + NULL, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "GetLogging", + g_variant_new("()"), + G_VARIANT_TYPE("(ss)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) + return FALSE; + + g_variant_get(ret, "(ss)", level, domains); + return TRUE; +} + +/** + * nm_client_set_logging: + * @client: a #NMClient + * @level: (allow-none): logging level to set (%NULL or an empty string for no change) + * @domains: (allow-none): logging domains to set. The string should be a list of log + * domains separated by ",". (%NULL or an empty string for no change) + * @error: (allow-none): return location for a #GError, or %NULL + * + * Sets NetworkManager logging level and/or domains. + * + * Returns: %TRUE on success, %FALSE otherwise + * + * Deprecated: 1.22: Use the async command nm_client_dbus_call() on %NM_DBUS_PATH, + * %NM_DBUS_INTERFACE to call "SetLogging" with "(ss)" arguments for level and domains. + **/ +gboolean +nm_client_set_logging(NMClient *client, const char *level, const char *domains, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + return _nm_client_dbus_call_sync_void(client, + NULL, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "SetLogging", + g_variant_new("(ss)", level ?: "", domains ?: ""), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_client_get_permission_result: + * @client: a #NMClient + * @permission: the permission for which to return the result, one of #NMClientPermission + * + * Requests the result of a specific permission, which indicates whether the + * client can or cannot perform the action the permission represents + * + * Returns: the permission's result, one of #NMClientPermissionResult + **/ +NMClientPermissionResult +nm_client_get_permission_result(NMClient *client, NMClientPermission permission) +{ + NMClientPrivate * priv; + NMClientPermissionResult result = NM_CLIENT_PERMISSION_RESULT_UNKNOWN; + + g_return_val_if_fail(NM_IS_CLIENT(client), NM_CLIENT_PERMISSION_RESULT_UNKNOWN); + + if (permission > NM_CLIENT_PERMISSION_NONE && permission <= NM_CLIENT_PERMISSION_LAST) { + priv = NM_CLIENT_GET_PRIVATE(client); + if (priv->permissions) + result = priv->permissions[permission - 1]; + } + + return result; +} + +/** + * nm_client_get_permissions_state: + * @self: the #NMClient instance + * + * Returns: the state of the cached permissions. %NM_TERNARY_DEFAULT + * means that no permissions result was yet received. All permissions + * are unknown. %NM_TERNARY_TRUE means that the permissions got received + * and are cached. %%NM_TERNARY_FALSE means that permissions are cached, + * but they are invalided as "CheckPermissions" signal was received + * in the meantime. + * + * Since: 1.24 + */ +NMTernary +nm_client_get_permissions_state(NMClient *self) +{ + g_return_val_if_fail(NM_IS_CLIENT(self), NM_TERNARY_DEFAULT); + + return NM_CLIENT_GET_PRIVATE(self)->permissions_state; +} + +/** + * nm_client_get_connectivity: + * @client: an #NMClient + * + * Gets the current network connectivity state. Contrast + * nm_client_check_connectivity() and + * nm_client_check_connectivity_async(), which re-check the + * connectivity state first before returning any information. + * + * Returns: the current connectivity state + */ +NMConnectivityState +nm_client_get_connectivity(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NM_CONNECTIVITY_UNKNOWN); + + return NM_CLIENT_GET_PRIVATE(client)->nm.connectivity; +} + +/** + * nm_client_check_connectivity: + * @client: an #NMClient + * @cancellable: a #GCancellable + * @error: return location for a #GError + * + * Updates the network connectivity state and returns the (new) + * current state. Contrast nm_client_get_connectivity(), which returns + * the most recent known state without re-checking. + * + * This is a blocking call; use nm_client_check_connectivity_async() + * if you do not want to block. + * + * Returns: the (new) current connectivity state + * + * Deprecated: 1.22: Use nm_client_check_connectivity_async() or GDBusConnection. + */ +NMConnectivityState +nm_client_check_connectivity(NMClient *client, GCancellable *cancellable, GError **error) +{ + NMClientPrivate *priv; + gs_unref_variant GVariant *ret = NULL; + guint32 connectivity; + + g_return_val_if_fail(NM_IS_CLIENT(client), NM_CONNECTIVITY_UNKNOWN); + + ret = _nm_client_dbus_call_sync(client, + cancellable, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckConnectivity", + g_variant_new("()"), + G_VARIANT_TYPE("(u)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) + return NM_CONNECTIVITY_UNKNOWN; + + g_variant_get(ret, "(u)", &connectivity); + + /* upon receiving the synchronous response, we hack the NMClient state + * and update the property outside the ordered D-Bus messages (like + * "PropertiesChanged" signals). + * + * This is really ugly, we shouldn't do this. */ + + priv = NM_CLIENT_GET_PRIVATE(client); + + if (priv->nm.connectivity != connectivity) { + priv->nm.connectivity = connectivity; + _notify(client, PROP_CONNECTIVITY); + } + + return connectivity; +} + +/** + * nm_client_check_connectivity_async: + * @client: an #NMClient + * @cancellable: a #GCancellable + * @callback: callback to call with the result + * @user_data: data for @callback. + * + * Asynchronously updates the network connectivity state and invokes + * @callback when complete. Contrast nm_client_get_connectivity(), + * which (immediately) returns the most recent known state without + * re-checking, and nm_client_check_connectivity(), which blocks. + */ +void +nm_client_check_connectivity_async(NMClient * client, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(client, + client, + nm_client_check_connectivity_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckConnectivity", + g_variant_new("()"), + G_VARIANT_TYPE("(u)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); +} + +/** + * nm_client_check_connectivity_finish: + * @client: an #NMClient + * @result: the #GAsyncResult + * @error: return location for a #GError + * + * Retrieves the result of an nm_client_check_connectivity_async() + * call. + * + * Returns: the (new) current connectivity state + */ +NMConnectivityState +nm_client_check_connectivity_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + gs_unref_variant GVariant *ret = NULL; + guint32 connectivity; + + g_return_val_if_fail(NM_IS_CLIENT(client), NM_CONNECTIVITY_UNKNOWN); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_check_connectivity_async), + NM_CONNECTIVITY_UNKNOWN); + + ret = g_task_propagate_pointer(G_TASK(result), error); + if (!ret) + return NM_CONNECTIVITY_UNKNOWN; + + g_variant_get(ret, "(u)", &connectivity); + return connectivity; +} + +/** + * nm_client_save_hostname: + * @client: the %NMClient + * @hostname: (allow-none): the new persistent hostname to set, or %NULL to + * clear any existing persistent hostname + * @cancellable: a #GCancellable, or %NULL + * @error: return location for #GError + * + * Requests that the machine's persistent hostname be set to the specified value + * or cleared. + * + * Returns: %TRUE if the request was successful, %FALSE if it failed + * + * Deprecated: 1.22: Use nm_client_save_hostname_async() or GDBusConnection. + **/ +gboolean +nm_client_save_hostname(NMClient * client, + const char * hostname, + GCancellable *cancellable, + GError ** error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + + return _nm_client_dbus_call_sync_void(client, + cancellable, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "SaveHostname", + g_variant_new("(s)", hostname ?: ""), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_client_save_hostname_async: + * @client: the %NMClient + * @hostname: (allow-none): the new persistent hostname to set, or %NULL to + * clear any existing persistent hostname + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Requests that the machine's persistent hostname be set to the specified value + * or cleared. + **/ +void +nm_client_save_hostname_async(NMClient * client, + const char * hostname, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(client, + client, + nm_client_save_hostname_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "SaveHostname", + g_variant_new("(s)", hostname ?: ""), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_client_save_hostname_finish: + * @client: the %NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: return location for #GError + * + * Gets the result of an nm_client_save_hostname_async() call. + * + * Returns: %TRUE if the request was successful, %FALSE if it failed + **/ +gboolean +nm_client_save_hostname_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_save_hostname_async), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ +/* Devices */ +/*****************************************************************************/ + +/** + * nm_client_get_devices: + * @client: a #NMClient + * + * Gets all the known network devices. Use nm_device_get_type() or the + * <literal>NM_IS_DEVICE_XXXX</literal> functions to determine what kind of + * device member of the returned array is, and then you may use device-specific + * methods such as nm_device_ethernet_get_hw_address(). + * + * Returns: (transfer none) (element-type NMDevice): a #GPtrArray + * containing all the #NMDevices. The returned array is owned by the + * #NMClient object and should not be modified. + **/ +const GPtrArray * +nm_client_get_devices(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_CLIENT_GET_PRIVATE(client)->nm.property_ao[PROPERTY_AO_IDX_DEVICES]); +} + +/** + * nm_client_get_all_devices: + * @client: a #NMClient + * + * Gets both real devices and device placeholders (eg, software devices which + * do not currently exist, but could be created automatically by NetworkManager + * if one of their NMDevice::ActivatableConnections was activated). Use + * nm_device_is_real() to determine whether each device is a real device or + * a placeholder. + * + * Use nm_device_get_type() or the NM_IS_DEVICE_XXXX() functions to determine + * what kind of device each member of the returned array is, and then you may + * use device-specific methods such as nm_device_ethernet_get_hw_address(). + * + * Returns: (transfer none) (element-type NMDevice): a #GPtrArray + * containing all the #NMDevices. The returned array is owned by the + * #NMClient object and should not be modified. + * + * Since: 1.2 + **/ +const GPtrArray * +nm_client_get_all_devices(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_CLIENT_GET_PRIVATE(client)->nm.property_ao[PROPERTY_AO_IDX_ALL_DEVICES]); +} + +/** + * nm_client_get_device_by_path: + * @client: a #NMClient + * @object_path: the object path to search for + * + * Gets a #NMDevice from a #NMClient. + * + * Returns: (transfer none): the #NMDevice for the given @object_path or %NULL if none is found. + **/ +NMDevice * +nm_client_get_device_by_path(NMClient *client, const char *object_path) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(object_path, NULL); + + return _dbobjs_get_nmobj_unpack_visible(client, object_path, NM_TYPE_DEVICE); +} + +/** + * nm_client_get_device_by_iface: + * @client: a #NMClient + * @iface: the interface name to search for + * + * Gets a #NMDevice from a #NMClient. + * + * Returns: (transfer none): the #NMDevice for the given @iface or %NULL if none is found. + **/ +NMDevice * +nm_client_get_device_by_iface(NMClient *client, const char *iface) +{ + const GPtrArray *devices; + guint i; + + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(iface, NULL); + + devices = nm_client_get_devices(client); + for (i = 0; i < devices->len; i++) { + NMDevice *candidate = g_ptr_array_index(devices, i); + + if (nm_streq0(nm_device_get_iface(candidate), iface)) + return candidate; + } + + return NULL; +} + +/*****************************************************************************/ +/* Active Connections */ +/*****************************************************************************/ + +/** + * nm_client_get_active_connections: + * @client: a #NMClient + * + * Gets the active connections. + * + * Returns: (transfer none) (element-type NMActiveConnection): a #GPtrArray + * containing all the active #NMActiveConnections. + * The returned array is owned by the client and should not be modified. + **/ +const GPtrArray * +nm_client_get_active_connections(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_CLIENT_GET_PRIVATE(client)->nm.property_ao[PROPERTY_AO_IDX_ACTIVE_CONNECTIONS]); +} + +/** + * nm_client_get_primary_connection: + * @client: an #NMClient + * + * Gets the #NMActiveConnection corresponding to the primary active + * network device. + * + * In particular, when there is no VPN active, or the VPN does not + * have the default route, this returns the active connection that has + * the default route. If there is a VPN active with the default route, + * then this function returns the active connection that contains the + * route to the VPN endpoint. + * + * If there is no default route, or the default route is over a + * non-NetworkManager-recognized device, this will return %NULL. + * + * Returns: (transfer none): the appropriate #NMActiveConnection, if + * any + */ +NMActiveConnection * +nm_client_get_primary_connection(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return nml_dbus_property_o_get_obj( + &NM_CLIENT_GET_PRIVATE(client)->nm.property_o[PROPERTY_O_IDX_NM_PRIMAY_CONNECTION]); +} + +/** + * nm_client_get_activating_connection: + * @client: an #NMClient + * + * Gets the #NMActiveConnection corresponding to a + * currently-activating connection that is expected to become the new + * #NMClient:primary-connection upon successful activation. + * + * Returns: (transfer none): the appropriate #NMActiveConnection, if + * any. + */ +NMActiveConnection * +nm_client_get_activating_connection(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return nml_dbus_property_o_get_obj( + &NM_CLIENT_GET_PRIVATE(client)->nm.property_o[PROPERTY_O_IDX_NM_ACTIVATING_CONNECTION]); +} + +/*****************************************************************************/ + +static void +activate_connection_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + gs_unref_object GTask *task = user_data; + gs_unref_variant GVariant *ret = NULL; + const char * v_active_connection; + GError * error = NULL; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), result, &error); + if (!ret) { + if (!nm_utils_error_is_cancelled(error)) + g_dbus_error_strip_remote_error(error); + g_task_return_error(task, error); + return; + } + + g_variant_get(ret, "(&o)", &v_active_connection); + + _request_wait_start(g_steal_pointer(&task), + "ActivateConnection", + NM_TYPE_ACTIVE_CONNECTION, + v_active_connection, + NULL); +} + +/** + * nm_client_activate_connection_async: + * @client: a #NMClient + * @connection: (allow-none): an #NMConnection + * @device: (allow-none): the #NMDevice + * @specific_object: (allow-none): the object path of a connection-type-specific + * object this activation should use. This parameter is currently ignored for + * wired and mobile broadband connections, and the value of %NULL should be used + * (ie, no specific object). For Wi-Fi or WiMAX connections, pass the object + * path of a #NMAccessPoint or #NMWimaxNsp owned by @device, which you can + * get using nm_object_get_path(), and which will be used to complete the + * details of the newly added connection. + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the activation has started + * @user_data: caller-specific data passed to @callback + * + * Asynchronously starts a connection to a particular network using the + * configuration settings from @connection and the network device @device. + * Certain connection types also take a "specific object" which is the object + * path of a connection- specific object, like an #NMAccessPoint for Wi-Fi + * connections, or an #NMWimaxNsp for WiMAX connections, to which you wish to + * connect. If the specific object is not given, NetworkManager can, in some + * cases, automatically determine which network to connect to given the settings + * in @connection. + * + * If @connection is not given for a device-based activation, NetworkManager + * picks the best available connection for the device and activates it. + * + * Note that the callback is invoked when NetworkManager has started activating + * the new connection, not when it finishes. You can use the returned + * #NMActiveConnection object (in particular, #NMActiveConnection:state) to + * track the activation to its completion. + **/ +void +nm_client_activate_connection_async(NMClient * client, + NMConnection * connection, + NMDevice * device, + const char * specific_object, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + const char *arg_connection = NULL; + const char *arg_device = NULL; + + g_return_if_fail(NM_IS_CLIENT(client)); + + if (connection) { + g_return_if_fail(NM_IS_CONNECTION(connection)); + arg_connection = nm_connection_get_path(connection); + g_return_if_fail(arg_connection); + } + + if (device) { + g_return_if_fail(NM_IS_DEVICE(device)); + arg_device = nm_object_get_path(NM_OBJECT(device)); + g_return_if_fail(arg_device); + } + + NML_NMCLIENT_LOG_T( + client, + "ActivateConnection() for connection \"%s\", device \"%s\", specific_object \"%s", + arg_connection ?: "/", + arg_device ?: "/", + specific_object ?: "/"); + + _nm_client_dbus_call( + client, + client, + nm_client_activate_connection_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "ActivateConnection", + g_variant_new("(ooo)", arg_connection ?: "/", arg_device ?: "/", specific_object ?: "/"), + G_VARIANT_TYPE("(o)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + activate_connection_cb); +} + +/** + * nm_client_activate_connection_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_activate_connection_async(). + * + * Returns: (transfer full): the new #NMActiveConnection on success, %NULL on + * failure, in which case @error will be set. + **/ +NMActiveConnection * +nm_client_activate_connection_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + return NM_ACTIVE_CONNECTION( + _request_wait_finish(client, result, nm_client_activate_connection_async, NULL, error)); +} + +/*****************************************************************************/ + +static void +_add_and_activate_connection_done(GObject * object, + GAsyncResult *result, + gboolean use_add_and_activate_v2, + GTask * task_take) +{ + _nm_unused gs_unref_object GTask *task = task_take; + gs_unref_variant GVariant *ret = NULL; + GError * error = NULL; + gs_unref_variant GVariant *v_result = NULL; + const char * v_active_connection; + const char * v_path; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), result, &error); + if (!ret) { + if (!nm_utils_error_is_cancelled(error)) + g_dbus_error_strip_remote_error(error); + g_task_return_error(task, error); + return; + } + + if (use_add_and_activate_v2) { + g_variant_get(ret, "(&o&o@a{sv})", &v_path, &v_active_connection, &v_result); + } else { + g_variant_get(ret, "(&o&o)", &v_path, &v_active_connection); + } + + _request_wait_start(g_steal_pointer(&task), + "AddAndActivateConnection", + NM_TYPE_ACTIVE_CONNECTION, + v_active_connection, + g_steal_pointer(&v_result)); +} + +static void +_add_and_activate_connection_v1_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + _add_and_activate_connection_done(object, result, FALSE, user_data); +} + +static void +_add_and_activate_connection_v2_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + _add_and_activate_connection_done(object, result, TRUE, user_data); +} + +static void +_add_and_activate_connection(NMClient * self, + gboolean is_v2, + NMConnection * partial, + NMDevice * device, + const char * specific_object, + GVariant * options, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GVariant * arg_connection = NULL; + gboolean use_add_and_activate_v2 = FALSE; + const char *arg_device = NULL; + gpointer source_tag; + + g_return_if_fail(NM_IS_CLIENT(self)); + g_return_if_fail(!partial || NM_IS_CONNECTION(partial)); + + if (device) { + g_return_if_fail(NM_IS_DEVICE(device)); + arg_device = nm_object_get_path(NM_OBJECT(device)); + g_return_if_fail(arg_device); + } + + if (partial) + arg_connection = nm_connection_to_dbus(partial, NM_CONNECTION_SERIALIZE_ALL); + if (!arg_connection) + arg_connection = nm_g_variant_singleton_aLsaLsvII(); + + if (is_v2) { + if (!options) + options = nm_g_variant_singleton_aLsvI(); + use_add_and_activate_v2 = TRUE; + source_tag = nm_client_add_and_activate_connection2; + } else { + if (options) { + if (g_variant_n_children(options) > 0) + use_add_and_activate_v2 = TRUE; + else + nm_clear_pointer(&options, nm_g_variant_unref_floating); + } + source_tag = nm_client_add_and_activate_connection_async; + } + + NML_NMCLIENT_LOG_D(self, "AddAndActivateConnection() started..."); + + if (use_add_and_activate_v2) { + _nm_client_dbus_call(self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "AddAndActivateConnection2", + g_variant_new("(@a{sa{sv}}oo@a{sv})", + arg_connection, + arg_device ?: "/", + specific_object ?: "/", + options), + G_VARIANT_TYPE("(ooa{sv})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_and_activate_connection_v2_cb); + } else { + _nm_client_dbus_call(self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "AddAndActivateConnection", + g_variant_new("(@a{sa{sv}}oo)", + arg_connection, + arg_device ?: "/", + specific_object ?: "/"), + G_VARIANT_TYPE("(oo)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_and_activate_connection_v1_cb); + } +} + +/** + * nm_client_add_and_activate_connection_async: + * @client: a #NMClient + * @partial: (allow-none): an #NMConnection to add; the connection may be + * partially filled (or even %NULL) and will be completed by NetworkManager + * using the given @device and @specific_object before being added + * @device: the #NMDevice + * @specific_object: (allow-none): the object path of a connection-type-specific + * object this activation should use. This parameter is currently ignored for + * wired and mobile broadband connections, and the value of %NULL should be used + * (ie, no specific object). For Wi-Fi or WiMAX connections, pass the object + * path of a #NMAccessPoint or #NMWimaxNsp owned by @device, which you can + * get using nm_object_get_path(), and which will be used to complete the + * details of the newly added connection. + * If the variant is floating, it will be consumed. + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the activation has started + * @user_data: caller-specific data passed to @callback + * + * Adds a new connection using the given details (if any) as a template, + * automatically filling in missing settings with the capabilities of the given + * device and specific object. The new connection is then asynchronously + * activated as with nm_client_activate_connection_async(). Cannot be used for + * VPN connections at this time. + * + * Note that the callback is invoked when NetworkManager has started activating + * the new connection, not when it finishes. You can used the returned + * #NMActiveConnection object (in particular, #NMActiveConnection:state) to + * track the activation to its completion. + **/ +void +nm_client_add_and_activate_connection_async(NMClient * client, + NMConnection * partial, + NMDevice * device, + const char * specific_object, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + _add_and_activate_connection(client, + FALSE, + partial, + device, + specific_object, + NULL, + cancellable, + callback, + user_data); +} + +/** + * nm_client_add_and_activate_connection_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_add_and_activate_connection_async(). + * + * You can call nm_active_connection_get_connection() on the returned + * #NMActiveConnection to find the path of the created #NMConnection. + * + * Returns: (transfer full): the new #NMActiveConnection on success, %NULL on + * failure, in which case @error will be set. + **/ +NMActiveConnection * +nm_client_add_and_activate_connection_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + return NM_ACTIVE_CONNECTION(_request_wait_finish(client, + result, + nm_client_add_and_activate_connection_async, + NULL, + error)); +} + +/** + * nm_client_add_and_activate_connection2: + * @client: a #NMClient + * @partial: (allow-none): an #NMConnection to add; the connection may be + * partially filled (or even %NULL) and will be completed by NetworkManager + * using the given @device and @specific_object before being added + * @device: the #NMDevice + * @specific_object: (allow-none): the object path of a connection-type-specific + * object this activation should use. This parameter is currently ignored for + * wired and mobile broadband connections, and the value of %NULL should be used + * (i.e., no specific object). For Wi-Fi or WiMAX connections, pass the object + * path of a #NMAccessPoint or #NMWimaxNsp owned by @device, which you can + * get using nm_object_get_path(), and which will be used to complete the + * details of the newly added connection. + * @options: a #GVariant containing a dictionary with options, or %NULL + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the activation has started + * @user_data: caller-specific data passed to @callback + * + * Adds a new connection using the given details (if any) as a template, + * automatically filling in missing settings with the capabilities of the given + * device and specific object. The new connection is then asynchronously + * activated as with nm_client_activate_connection_async(). Cannot be used for + * VPN connections at this time. + * + * Note that the callback is invoked when NetworkManager has started activating + * the new connection, not when it finishes. You can used the returned + * #NMActiveConnection object (in particular, #NMActiveConnection:state) to + * track the activation to its completion. + * + * This is identical to nm_client_add_and_activate_connection_async() but takes + * a further @options parameter. Currently, the following options are supported + * by the daemon: + * * "persist": A string describing how the connection should be stored. + * The default is "disk", but it can be modified to "memory" (until + * the daemon quits) or "volatile" (will be deleted on disconnect). + * * "bind-activation": Bind the connection lifetime to something. The default is "none", + * meaning an explicit disconnect is needed. The value "dbus-client" + * means the connection will automatically be deactivated when the calling + * D-Bus client disappears from the system bus. + * + * Since: 1.16 + **/ +void +nm_client_add_and_activate_connection2(NMClient * client, + NMConnection * partial, + NMDevice * device, + const char * specific_object, + GVariant * options, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + _add_and_activate_connection(client, + TRUE, + partial, + device, + specific_object, + options, + cancellable, + callback, + user_data); +} + +/** + * nm_client_add_and_activate_connection2_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * @out_result: (allow-none) (transfer full): the output result + * of type "a{sv}" returned by D-Bus' AddAndActivate2 call. Currently, no + * output is implemented yet. + * + * Gets the result of a call to nm_client_add_and_activate_connection2(). + * + * You can call nm_active_connection_get_connection() on the returned + * #NMActiveConnection to find the path of the created #NMConnection. + * + * Returns: (transfer full): the new #NMActiveConnection on success, %NULL on + * failure, in which case @error will be set. + **/ +NMActiveConnection * +nm_client_add_and_activate_connection2_finish(NMClient * client, + GAsyncResult *result, + GVariant ** out_result, + GError ** error) +{ + return NM_ACTIVE_CONNECTION(_request_wait_finish(client, + result, + nm_client_add_and_activate_connection2, + out_result, + error)); +} + +/*****************************************************************************/ + +/** + * nm_client_deactivate_connection: + * @client: a #NMClient + * @active: the #NMActiveConnection to deactivate + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Deactivates an active #NMActiveConnection. + * + * Returns: success or failure + * + * Deprecated: 1.22: Use nm_client_deactivate_connection_async() or GDBusConnection. + **/ +gboolean +nm_client_deactivate_connection(NMClient * client, + NMActiveConnection *active, + GCancellable * cancellable, + GError ** error) +{ + const char *active_path; + + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(active), FALSE); + + active_path = nm_object_get_path(NM_OBJECT(active)); + g_return_val_if_fail(active_path, FALSE); + + return _nm_client_dbus_call_sync_void(client, + cancellable, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "DeactivateConnection", + g_variant_new("(o)", active_path), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_client_deactivate_connection_async: + * @client: a #NMClient + * @active: the #NMActiveConnection to deactivate + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the deactivation has completed + * @user_data: caller-specific data passed to @callback + * + * Asynchronously deactivates an active #NMActiveConnection. + **/ +void +nm_client_deactivate_connection_async(NMClient * client, + NMActiveConnection *active, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + const char *active_path; + + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(NM_IS_ACTIVE_CONNECTION(active)); + + active_path = nm_object_get_path(NM_OBJECT(active)); + g_return_if_fail(active_path); + + _nm_client_dbus_call(client, + client, + nm_client_deactivate_connection_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "DeactivateConnection", + g_variant_new("(o)", active_path), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_client_deactivate_connection_finish: + * @client: a #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_deactivate_connection_async(). + * + * Returns: success or failure + **/ +gboolean +nm_client_deactivate_connection_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_deactivate_connection_async), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ +/* Connections */ +/*****************************************************************************/ + +/** + * nm_client_get_connections: + * @client: the %NMClient + * + * Returns: (transfer none) (element-type NMRemoteConnection): an array + * containing all connections provided by the remote settings service. The + * returned array is owned by the #NMClient object and should not be modified. + * + * The connections are as received from D-Bus and might not validate according + * to nm_connection_verify(). + **/ +const GPtrArray * +nm_client_get_connections(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_CLIENT_GET_PRIVATE(client)->settings.connections); +} + +/** + * nm_client_get_connection_by_id: + * @client: the %NMClient + * @id: the id of the remote connection + * + * Returns the first matching %NMRemoteConnection matching a given @id. + * + * Returns: (transfer none): the remote connection object on success, or %NULL if no + * matching object was found. + * + * The connection is as received from D-Bus and might not validate according + * to nm_connection_verify(). + **/ +NMRemoteConnection * +nm_client_get_connection_by_id(NMClient *client, const char *id) +{ + const GPtrArray *arr; + guint i; + + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(id, NULL); + + arr = nm_client_get_connections(client); + for (i = 0; i < arr->len; i++) { + NMRemoteConnection *c = NM_REMOTE_CONNECTION(arr->pdata[i]); + + if (nm_streq0(id, nm_connection_get_id(NM_CONNECTION(c)))) + return c; + } + return NULL; +} + +/** + * nm_client_get_connection_by_path: + * @client: the %NMClient + * @path: the D-Bus object path of the remote connection + * + * Returns the %NMRemoteConnection representing the connection at @path. + * + * Returns: (transfer none): the remote connection object on success, or %NULL if the object was + * not known + * + * The connection is as received from D-Bus and might not validate according + * to nm_connection_verify(). + **/ +NMRemoteConnection * +nm_client_get_connection_by_path(NMClient *client, const char *path) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(path != NULL, NULL); + + return _dbobjs_get_nmobj_unpack_visible(client, path, NM_TYPE_REMOTE_CONNECTION); +} + +/** + * nm_client_get_connection_by_uuid: + * @client: the %NMClient + * @uuid: the UUID of the remote connection + * + * Returns the %NMRemoteConnection identified by @uuid. + * + * Returns: (transfer none): the remote connection object on success, or %NULL if the object was + * not known + * + * The connection is as received from D-Bus and might not validate according + * to nm_connection_verify(). + **/ +NMRemoteConnection * +nm_client_get_connection_by_uuid(NMClient *client, const char *uuid) +{ + const GPtrArray *arr; + guint i; + + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(uuid, NULL); + + arr = nm_client_get_connections(client); + for (i = 0; i < arr->len; i++) { + NMRemoteConnection *c = NM_REMOTE_CONNECTION(arr->pdata[i]); + + if (nm_streq0(uuid, nm_connection_get_uuid(NM_CONNECTION(c)))) + return c; + } + return NULL; +} + +/*****************************************************************************/ + +static void +_add_connection_cb(GObject * source, + GAsyncResult *result, + gboolean with_extra_arg, + gpointer user_data) +{ + gs_unref_variant GVariant *ret = NULL; + gs_unref_object GTask *task = user_data; + gs_unref_variant GVariant *v_result = NULL; + const char * v_path; + GError * error = NULL; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (!ret) { + if (!nm_utils_error_is_cancelled(error)) + g_dbus_error_strip_remote_error(error); + g_task_return_error(task, error); + return; + } + + if (with_extra_arg) { + g_variant_get(ret, "(&o@a{sv})", &v_path, &v_result); + } else { + g_variant_get(ret, "(&o)", &v_path); + } + + _request_wait_start(g_steal_pointer(&task), + "AddConnection", + NM_TYPE_REMOTE_CONNECTION, + v_path, + g_steal_pointer(&v_result)); +} + +static void +_add_connection_cb_without_extra_result(GObject *object, GAsyncResult *result, gpointer user_data) +{ + _add_connection_cb(object, result, FALSE, user_data); +} + +static void +_add_connection_cb_with_extra_result(GObject *object, GAsyncResult *result, gpointer user_data) +{ + _add_connection_cb(object, result, TRUE, user_data); +} + +static void +_add_connection_call(NMClient * self, + gpointer source_tag, + gboolean ignore_out_result, + GVariant * settings, + NMSettingsAddConnection2Flags flags, + GVariant * args, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(self)); + g_return_if_fail(!settings || g_variant_is_of_type(settings, G_VARIANT_TYPE("a{sa{sv}}"))); + g_return_if_fail(!args || g_variant_is_of_type(args, G_VARIANT_TYPE("a{sv}"))); + + NML_NMCLIENT_LOG_D(self, "AddConnection() started..."); + + if (!settings) + settings = nm_g_variant_singleton_aLsaLsvII(); + + /* Although AddConnection2() being capable to handle also AddConnection() and + * AddConnectionUnsaved() variants, we prefer to use the old D-Bus methods when + * they are sufficient. The reason is that libnm should avoid hard dependencies + * on 1.20 API whenever possible. */ + if (ignore_out_result && flags == NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK) { + _nm_client_dbus_call(self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "AddConnection", + g_variant_new("(@a{sa{sv}})", settings), + G_VARIANT_TYPE("(o)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_connection_cb_without_extra_result); + } else if (ignore_out_result && flags == NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY) { + _nm_client_dbus_call(self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "AddConnectionUnsaved", + g_variant_new("(@a{sa{sv}})", settings), + G_VARIANT_TYPE("(o)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_connection_cb_without_extra_result); + } else { + _nm_client_dbus_call(self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "AddConnection2", + g_variant_new("(@a{sa{sv}}u@a{sv})", + settings, + (guint32) flags, + args ?: nm_g_variant_singleton_aLsvI()), + G_VARIANT_TYPE("(oa{sv})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_connection_cb_with_extra_result); + } +} + +/** + * nm_client_add_connection_async: + * @client: the %NMClient + * @connection: the connection to add. Note that this object's settings will be + * added, not the object itself + * @save_to_disk: whether to immediately save the connection to disk + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Requests that the remote settings service add the given settings to a new + * connection. If @save_to_disk is %TRUE, the connection is immediately written + * to disk; otherwise it is initially only stored in memory, but may be saved + * later by calling the connection's nm_remote_connection_commit_changes() + * method. + * + * @connection is untouched by this function and only serves as a template of + * the settings to add. The #NMRemoteConnection object that represents what + * NetworkManager actually added is returned to @callback when the addition + * operation is complete. + * + * Note that the #NMRemoteConnection returned in @callback may not contain + * identical settings to @connection as NetworkManager may perform automatic + * completion and/or normalization of connection properties. + **/ +void +nm_client_add_connection_async(NMClient * client, + NMConnection * connection, + gboolean save_to_disk, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CONNECTION(connection)); + + _add_connection_call(client, + nm_client_add_connection_async, + TRUE, + nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL), + save_to_disk ? NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK + : NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY, + NULL, + cancellable, + callback, + user_data); +} + +/** + * nm_client_add_connection_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_add_connection_async(). + * + * Returns: (transfer full): the new #NMRemoteConnection on success, %NULL on + * failure, in which case @error will be set. + **/ +NMRemoteConnection * +nm_client_add_connection_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + return NM_REMOTE_CONNECTION( + _request_wait_finish(client, result, nm_client_add_connection_async, NULL, error)); +} + +/** + * nm_client_add_connection2: + * @client: the %NMClient + * @settings: the "a{sa{sv}}" #GVariant with the content of the setting. + * @flags: the %NMSettingsAddConnection2Flags argument. + * @args: (allow-none): the "a{sv}" #GVariant with extra argument or %NULL + * for no extra arguments. + * @ignore_out_result: this function wraps AddConnection2(), which has an + * additional result "a{sv}" output parameter. By setting this to %TRUE, + * you signal that you are not interested in that output parameter. + * This allows the function to fall back to AddConnection() and AddConnectionUnsaved(), + * which is interesting if you run against an older server version that does + * not yet provide AddConnection2(). By setting this to %FALSE, the function + * under the hood always calls AddConnection2(). + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Call AddConnection2() D-Bus API asynchronously. + * + * Since: 1.20 + **/ +void +nm_client_add_connection2(NMClient * client, + GVariant * settings, + NMSettingsAddConnection2Flags flags, + GVariant * args, + gboolean ignore_out_result, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + _add_connection_call(client, + nm_client_add_connection2, + ignore_out_result, + settings, + flags, + args, + cancellable, + callback, + user_data); +} + +/** + * nm_client_add_connection2_finish: + * @client: the #NMClient + * @result: the #GAsyncResult + * @out_result: (allow-none) (transfer full) (out): the output #GVariant + * from AddConnection2(). + * If you care about the output result, then the "ignore_out_result" + * parameter of nm_client_add_connection2() must not be set to %TRUE. + * @error: (allow-none): the error argument. + * + * Returns: (transfer full): on success, a pointer to the added + * #NMRemoteConnection. + * + * Since: 1.20 + */ +NMRemoteConnection * +nm_client_add_connection2_finish(NMClient * client, + GAsyncResult *result, + GVariant ** out_result, + GError ** error) +{ + return NM_REMOTE_CONNECTION( + _request_wait_finish(client, result, nm_client_add_connection2, out_result, error)); +} + +/*****************************************************************************/ + +/** + * nm_client_load_connections: + * @client: the %NMClient + * @filenames: (array zero-terminated=1): %NULL-terminated array of filenames to load + * @failures: (out) (transfer full): on return, a %NULL-terminated array of + * filenames that failed to load + * @cancellable: a #GCancellable, or %NULL + * @error: return location for #GError + * + * Requests that the remote settings service load or reload the given files, + * adding or updating the connections described within. + * + * The changes to the indicated files will not yet be reflected in + * @client's connections array when the function returns. + * + * If all of the indicated files were successfully loaded, the + * function will return %TRUE, and @failures will be set to %NULL. If + * NetworkManager tried to load the files, but some (or all) failed, + * then @failures will be set to a %NULL-terminated array of the + * filenames that failed to load. + * + * Returns: %TRUE on success. + * + * Warning: before libnm 1.22, the boolean return value was inconsistent. + * That is made worse, because when running against certain server versions + * before 1.20, the server would return wrong values for success/failure. + * This means, if you use this function in libnm before 1.22, you are advised + * to ignore the boolean return value and only look at @failures and @error. + * With libnm >= 1.22, the boolean return value corresponds to whether @error was + * set. Note that even in the success case, you might have individual @failures. + * With 1.22, the return value is consistent with nm_client_load_connections_finish(). + * + * Deprecated: 1.22: Use nm_client_load_connections_async() or GDBusConnection. + **/ +gboolean +nm_client_load_connections(NMClient * client, + char ** filenames, + char *** failures, + GCancellable *cancellable, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + + ret = _nm_client_dbus_call_sync(client, + cancellable, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "LoadConnections", + g_variant_new("(^as)", filenames ?: NM_PTRARRAY_EMPTY(char *)), + G_VARIANT_TYPE("(bas)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) { + *failures = NULL; + return FALSE; + } + + g_variant_get(ret, "(b^as)", NULL, failures); + + return TRUE; +} + +/** + * nm_client_load_connections_async: + * @client: the %NMClient + * @filenames: (array zero-terminated=1): %NULL-terminated array of filenames to load + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Requests that the remote settings service asynchronously load or reload the + * given files, adding or updating the connections described within. + * + * See nm_client_load_connections() for more details. + **/ +void +nm_client_load_connections_async(NMClient * client, + char ** filenames, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(client, + client, + nm_client_load_connections_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "LoadConnections", + g_variant_new("(^as)", filenames ?: NM_PTRARRAY_EMPTY(char *)), + G_VARIANT_TYPE("(bas)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); +} + +/** + * nm_client_load_connections_finish: + * @client: the %NMClient + * @failures: (out) (transfer full) (array zero-terminated=1): on return, a + * %NULL-terminated array of filenames that failed to load + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of an nm_client_load_connections_async() call. + + * See nm_client_load_connections() for more details. + * + * Returns: %TRUE on success. + * Note that even in the success case, you might have individual @failures. + **/ +gboolean +nm_client_load_connections_finish(NMClient * client, + char *** failures, + GAsyncResult *result, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_load_connections_async), + FALSE); + + ret = g_task_propagate_pointer(G_TASK(result), error); + if (!ret) { + *failures = NULL; + return FALSE; + } + + g_variant_get(ret, "(b^as)", NULL, &failures); + + return TRUE; +} + +/** + * nm_client_reload_connections: + * @client: the #NMClient + * @cancellable: a #GCancellable, or %NULL + * @error: return location for #GError + * + * Requests that the remote settings service reload all connection + * files from disk, adding, updating, and removing connections until + * the in-memory state matches the on-disk state. + * + * Return value: %TRUE on success, %FALSE on failure + * + * Deprecated: 1.22: Use nm_client_reload_connections_async() or GDBusConnection. + **/ +gboolean +nm_client_reload_connections(NMClient *client, GCancellable *cancellable, GError **error) +{ + gs_unref_variant GVariant *ret = NULL; + + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + + ret = _nm_client_dbus_call_sync(client, + cancellable, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "ReloadConnections", + g_variant_new("()"), + G_VARIANT_TYPE("(b)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) + return FALSE; + + return TRUE; +} + +/** + * nm_client_reload_connections_async: + * @client: the #NMClient + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the reload operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Requests that the remote settings service begin reloading all connection + * files from disk, adding, updating, and removing connections until the + * in-memory state matches the on-disk state. + **/ +void +nm_client_reload_connections_async(NMClient * client, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(client, + client, + nm_client_reload_connections_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "ReloadConnections", + g_variant_new("()"), + G_VARIANT_TYPE("(b)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); +} + +/** + * nm_client_reload_connections_finish: + * @client: the #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: return location for #GError + * + * Gets the result of an nm_client_reload_connections_async() call. + * + * Return value: %TRUE on success, %FALSE on failure + **/ +gboolean +nm_client_reload_connections_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + gs_unref_variant GVariant *ret = NULL; + + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_reload_connections_async), + FALSE); + + ret = g_task_propagate_pointer(G_TASK(result), error); + if (!ret) + return FALSE; + + return TRUE; +} + +/*****************************************************************************/ + +/** + * nm_client_get_dns_mode: + * @client: the #NMClient + * + * Gets the current DNS processing mode. + * + * Return value: the DNS processing mode, or %NULL in case the + * value is not available. + * + * Since: 1.6 + **/ +const char * +nm_client_get_dns_mode(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return NM_CLIENT_GET_PRIVATE(client)->dns_manager.mode; +} + +/** + * nm_client_get_dns_rc_manager: + * @client: the #NMClient + * + * Gets the current DNS resolv.conf manager. + * + * Return value: the resolv.conf manager or %NULL in case the + * value is not available. + * + * Since: 1.6 + **/ +const char * +nm_client_get_dns_rc_manager(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return NM_CLIENT_GET_PRIVATE(client)->dns_manager.rc_manager; +} + +/** + * nm_client_get_dns_configuration: + * @client: a #NMClient + * + * Gets the current DNS configuration + * + * Returns: (transfer none) (element-type NMDnsEntry): a #GPtrArray + * containing #NMDnsEntry elements or %NULL in case the value is not + * available. The returned array is owned by the #NMClient object + * and should not be modified. + * + * Since: 1.6 + **/ +const GPtrArray * +nm_client_get_dns_configuration(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return NM_CLIENT_GET_PRIVATE(client)->dns_manager.configuration; +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_dns_manager_configuration(NMClient * self, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + gs_unref_ptrarray GPtrArray *configuration_old = NULL; + gs_unref_ptrarray GPtrArray *configuration_new = NULL; + + nm_assert(G_OBJECT(self) == dbobj->nmobj); + + if (value) { + GVariant * entry_var_tmp; + GVariantIter iter; + GPtrArray * array; + + configuration_new = g_ptr_array_new_with_free_func((GDestroyNotify) nm_dns_entry_unref); + + g_variant_iter_init(&iter, value); + while (g_variant_iter_next(&iter, "@a{sv}", &entry_var_tmp)) { + gs_unref_variant GVariant *entry_var = entry_var_tmp; + nm_auto_free_variant_iter GVariantIter *iterp_nameservers = NULL; + nm_auto_free_variant_iter GVariantIter *iterp_domains = NULL; + gs_free char ** nameservers = NULL; + gs_free char ** domains = NULL; + gboolean vpn = FALSE; + NMDnsEntry * entry; + char * interface = NULL; + char * str; + gint32 priority = 0; + + if (!g_variant_lookup(entry_var, "nameservers", "as", &iterp_nameservers) + || !g_variant_lookup(entry_var, "priority", "i", &priority)) { + g_warning("Ignoring invalid DNS configuration"); + continue; + } + + array = g_ptr_array_new(); + while (g_variant_iter_next(iterp_nameservers, "&s", &str)) + g_ptr_array_add(array, str); + g_ptr_array_add(array, NULL); + nameservers = (char **) g_ptr_array_free(array, FALSE); + + if (g_variant_lookup(entry_var, "domains", "as", &iterp_domains)) { + array = g_ptr_array_new(); + while (g_variant_iter_next(iterp_domains, "&s", &str)) + g_ptr_array_add(array, str); + g_ptr_array_add(array, NULL); + domains = (char **) g_ptr_array_free(array, FALSE); + } + + g_variant_lookup(entry_var, "interface", "&s", &interface); + g_variant_lookup(entry_var, "vpn", "b", &vpn); + + entry = nm_dns_entry_new(interface, + (const char *const *) nameservers, + (const char *const *) domains, + priority, + vpn); + if (!entry) { + g_warning("Ignoring invalid DNS entry"); + continue; + } + + g_ptr_array_add(configuration_new, entry); + } + } + + configuration_old = priv->dns_manager.configuration; + priv->dns_manager.configuration = g_steal_pointer(&configuration_new); + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/** + * nm_client_get_capabilities: + * @client: the #NMClient instance + * @length: (out) (allow-none): the number of returned capabilities. + * + * Returns: (transfer none) (array length=length): the + * list of capabilities reported by the server or %NULL + * if the capabilities are unknown. + * The numeric values correspond to #NMCapability enum. + * The array is terminated by a numeric zero sentinel + * at position @length. + * + * Since: 1.24 + */ +const guint32 * +nm_client_get_capabilities(NMClient *client, gsize *length) +{ + NMClientPrivate *priv; + + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(length, NULL); + + priv = NM_CLIENT_GET_PRIVATE(client); + + NM_SET_OUT(length, priv->nm.capabilities_len); + return priv->nm.capabilities_arr; +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_nm_capabilities(NMClient * self, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + nm_assert(G_OBJECT(self) == dbobj->nmobj); + + nm_clear_g_free(&priv->nm.capabilities_arr); + priv->nm.capabilities_len = 0; + + if (value) { + const guint32 *arr; + gsize len; + + arr = g_variant_get_fixed_array(value, &len, sizeof(guint32)); + priv->nm.capabilities_len = len; + priv->nm.capabilities_arr = g_new(guint32, len + 1); + if (len > 0) + memcpy(priv->nm.capabilities_arr, arr, len * sizeof(guint32)); + priv->nm.capabilities_arr[len] = 0; + } + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/*****************************************************************************/ + +/** + * nm_client_get_checkpoints: + * @client: a #NMClient + * + * Gets all the active checkpoints. + * + * Returns: (transfer none) (element-type NMCheckpoint): a #GPtrArray + * containing all the #NMCheckpoint. The returned array is owned by the + * #NMClient object and should not be modified. + * + * Since: 1.12 + **/ +const GPtrArray * +nm_client_get_checkpoints(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_CLIENT_GET_PRIVATE(client)->nm.property_ao[PROPERTY_AO_IDX_CHECKPOINTS]); +} + +static void +checkpoint_create_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + gs_unref_object GTask *task = user_data; + gs_unref_variant GVariant *ret = NULL; + const char * v_checkpoint_path; + GError * error = NULL; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), result, &error); + if (!ret) { + if (!nm_utils_error_is_cancelled(error)) + g_dbus_error_strip_remote_error(error); + g_task_return_error(task, error); + return; + } + + g_variant_get(ret, "(&o)", &v_checkpoint_path); + + _request_wait_start(g_steal_pointer(&task), + "CheckpointCreate", + NM_TYPE_CHECKPOINT, + v_checkpoint_path, + NULL); +} + +/** + * nm_client_checkpoint_create: + * @client: the %NMClient + * @devices: (element-type NMDevice): a list of devices for which a + * checkpoint should be created. + * @rollback_timeout: the rollback timeout in seconds + * @flags: creation flags + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Creates a checkpoint of the current networking configuration + * for given interfaces. An empty @devices argument means all + * devices. If @rollback_timeout is not zero, a rollback is + * automatically performed after the given timeout. + * + * Since: 1.12 + **/ +void +nm_client_checkpoint_create(NMClient * client, + const GPtrArray * devices, + guint32 rollback_timeout, + NMCheckpointCreateFlags flags, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + gs_free const char **paths = NULL; + guint i; + + g_return_if_fail(NM_IS_CLIENT(client)); + + if (devices && devices->len > 0) { + paths = g_new(const char *, devices->len + 1); + for (i = 0; i < devices->len; i++) + paths[i] = nm_object_get_path(NM_OBJECT(devices->pdata[i])); + paths[i] = NULL; + } + + _nm_client_dbus_call( + client, + client, + nm_client_checkpoint_create, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckpointCreate", + g_variant_new("(^aouu)", paths ?: NM_PTRARRAY_EMPTY(const char *), rollback_timeout, flags), + G_VARIANT_TYPE("(o)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + checkpoint_create_cb); +} + +/** + * nm_client_checkpoint_create_finish: + * @client: the #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_checkpoint_create(). + * + * Returns: (transfer full): the new #NMCheckpoint on success, %NULL on + * failure, in which case @error will be set. + * + * Since: 1.12 + **/ +NMCheckpoint * +nm_client_checkpoint_create_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + return NM_CHECKPOINT( + _request_wait_finish(client, result, nm_client_checkpoint_create, NULL, error)); +} + +/** + * nm_client_checkpoint_destroy: + * @client: the %NMClient + * @checkpoint_path: the D-Bus path for the checkpoint + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Destroys an existing checkpoint without performing a rollback. + * + * Since: 1.12 + **/ +void +nm_client_checkpoint_destroy(NMClient * client, + const char * checkpoint_path, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(checkpoint_path && checkpoint_path[0] == '/'); + + _nm_client_dbus_call(client, + client, + nm_client_checkpoint_destroy, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckpointDestroy", + g_variant_new("(o)", checkpoint_path), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_client_checkpoint_destroy_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_checkpoint_destroy(). + * + * Returns: %TRUE on success or %FALSE on failure, in which case + * @error will be set. + * + * Since: 1.12 + **/ +gboolean +nm_client_checkpoint_destroy_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_checkpoint_destroy), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/** + * nm_client_checkpoint_rollback: + * @client: the %NMClient + * @checkpoint_path: the D-Bus path to the checkpoint + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Performs the rollback of a checkpoint before the timeout is reached. + * + * Since: 1.12 + **/ +void +nm_client_checkpoint_rollback(NMClient * client, + const char * checkpoint_path, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(checkpoint_path && checkpoint_path[0] == '/'); + + _nm_client_dbus_call(client, + client, + nm_client_checkpoint_rollback, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckpointRollback", + g_variant_new("(o)", checkpoint_path), + G_VARIANT_TYPE("(a{su})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); +} + +/** + * nm_client_checkpoint_rollback_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_checkpoint_rollback(). + * + * Returns: (transfer full) (element-type utf8 guint32): an hash table of + * devices and results. Devices are represented by their original + * D-Bus path; each result is a #NMRollbackResult. + * + * Since: 1.12 + **/ +GHashTable * +nm_client_checkpoint_rollback_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + gs_unref_variant GVariant *ret = NULL; + gs_unref_variant GVariant *v_result = NULL; + GVariantIter iter; + GHashTable * hash; + const char * path; + guint32 r; + + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_checkpoint_rollback), NULL); + + ret = g_task_propagate_pointer(G_TASK(result), error); + if (!ret) + return NULL; + + g_variant_get(ret, "(@a{su})", &v_result); + + hash = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, NULL); + + g_variant_iter_init(&iter, v_result); + while (g_variant_iter_next(&iter, "{&su}", &path, &r)) + g_hash_table_insert(hash, g_strdup(path), GUINT_TO_POINTER(r)); + + return hash; +} + +/** + * nm_client_checkpoint_adjust_rollback_timeout: + * @client: the %NMClient + * @checkpoint_path: a D-Bus path to a checkpoint + * @add_timeout: the timeout in seconds counting from now. + * Set to zero, to disable the timeout. + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Resets the timeout for the checkpoint with path @checkpoint_path + * to @timeout_add. + * + * Since: 1.12 + **/ +void +nm_client_checkpoint_adjust_rollback_timeout(NMClient * client, + const char * checkpoint_path, + guint32 add_timeout, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(checkpoint_path && checkpoint_path[0] == '/'); + + _nm_client_dbus_call(client, + client, + nm_client_checkpoint_adjust_rollback_timeout, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckpointAdjustRollbackTimeout", + g_variant_new("(ou)", checkpoint_path, add_timeout), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_client_checkpoint_adjust_rollback_timeout_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_checkpoint_adjust_rollback_timeout(). + * + * Returns: %TRUE on success or %FALSE on failure. + * + * Since: 1.12 + **/ +gboolean +nm_client_checkpoint_adjust_rollback_timeout_finish(NMClient * client, + GAsyncResult *result, + GError ** error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail( + nm_g_task_is_valid(result, client, nm_client_checkpoint_adjust_rollback_timeout), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/** + * nm_client_reload: + * @client: the %NMClient + * @flags: flags indicating what to reload. + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Reload NetworkManager's configuration and perform certain updates, like + * flushing caches or rewriting external state to disk. This is similar to + * sending SIGHUP to NetworkManager but it allows for more fine-grained control + * over what to reload (see @flags). It also allows non-root access via + * PolicyKit and contrary to signals it is synchronous. + * + * Since: 1.22 + **/ +void +nm_client_reload(NMClient * client, + NMManagerReloadFlags flags, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + + _nm_client_dbus_call(client, + client, + nm_client_reload, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "Reload", + g_variant_new("(u)", (guint32) flags), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_client_reload_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_reload(). + * + * Returns: %TRUE on success or %FALSE on failure. + * + * Since: 1.22 + **/ +gboolean +nm_client_reload_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_reload), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +/** + * nm_client_dbus_call: + * @client: the #NMClient + * @object_path: path of remote object + * @interface_name: D-Bus interface to invoke method on + * @method_name: the name of the method to invoke + * @parameters: (nullable): a #GVariant tuple with parameters for the method + * or %NULL if not passing parameters + * @reply_type: (nullable): the expected type of the reply (which will be a + * tuple), or %NULL + * @timeout_msec: the timeout in milliseconds, -1 to use the default + * timeout or %G_MAXINT for no timeout + * @cancellable: (nullable): a #GCancellable or %NULL + * @callback: (nullable): a #GAsyncReadyCallback to call when the request + * is satisfied or %NULL if you don't care about the result of the + * method invocation + * @user_data: the data to pass to @callback + * + * Call g_dbus_connection_call() on the current name owner with the specified + * arguments. Most importantly, this invokes g_dbus_connection_call() with the + * client's #GMainContext, so that the response is always in order with other + * events D-Bus events. Of course, the call uses #GTask and will invoke the + * callback on the current g_main_context_get_thread_default(). + * + * This API is merely a convenient wrapper for g_dbus_connection_call(). You can + * also use g_dbus_connection_call() directly, with the same effect. + * + * Since: 1.24 + **/ +void +nm_client_dbus_call(NMClient * client, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + int timeout_msec, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + + _nm_client_dbus_call(client, + client, + nm_client_dbus_call, + cancellable, + callback, + user_data, + object_path, + interface_name, + method_name, + parameters, + reply_type, + G_DBUS_CALL_FLAGS_NONE, + timeout_msec == -1 ? NM_DBUS_DEFAULT_TIMEOUT_MSEC : timeout_msec, + nm_dbus_connection_call_finish_variant_cb); +} + +/** + * nm_client_dbus_call_finish: + * @client: the #NMClient instance + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_dbus_call(). + * + * Returns: (transfer full): the result #GVariant or %NULL on error. + * + * Since: 1.24 + **/ +GVariant * +nm_client_dbus_call_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_dbus_call), FALSE); + + return g_task_propagate_pointer(G_TASK(result), error); +} + +/*****************************************************************************/ + +/** + * nm_client_dbus_set_property: + * @client: the #NMClient + * @object_path: path of remote object + * @interface_name: D-Bus interface for the property to set. + * @property_name: the name of the property to set + * @value: a #GVariant with the value to set. + * @timeout_msec: the timeout in milliseconds, -1 to use the default + * timeout or %G_MAXINT for no timeout + * @cancellable: (nullable): a #GCancellable or %NULL + * @callback: (nullable): a #GAsyncReadyCallback to call when the request + * is satisfied or %NULL if you don't care about the result of the + * method invocation + * @user_data: the data to pass to @callback + * + * Like nm_client_dbus_call() but calls "Set" on the standard "org.freedesktop.DBus.Properties" + * D-Bus interface. + * + * Since: 1.24 + **/ +void +nm_client_dbus_set_property(NMClient * client, + const char * object_path, + const char * interface_name, + const char * property_name, + GVariant * value, + int timeout_msec, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(interface_name); + g_return_if_fail(property_name); + g_return_if_fail(value); + + _nm_client_dbus_call(client, + client, + nm_client_dbus_set_property, + cancellable, + callback, + user_data, + object_path, + DBUS_INTERFACE_PROPERTIES, + "Set", + g_variant_new("(ssv)", interface_name, property_name, value), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + timeout_msec == -1 ? NM_DBUS_DEFAULT_TIMEOUT_MSEC : timeout_msec, + nm_dbus_connection_call_finish_void_cb); +} + +/** + * nm_client_dbus_set_property_finish: + * @client: the #NMClient instance + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_dbus_set_property(). + * + * Returns: %TRUE on success or %FALSE on failure. + * + * Since: 1.24 + **/ +gboolean +nm_client_dbus_set_property_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_dbus_set_property), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +static void +_init_fetch_all(NMClient *self) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); + + NML_NMCLIENT_LOG_D(self, "fetch all"); + + nm_assert(!priv->get_managed_objects_cancellable); + + priv->get_managed_objects_cancellable = g_cancellable_new(); + + priv->dbsid_nm_object_manager = + nm_dbus_connection_signal_subscribe_object_manager(priv->dbus_connection, + priv->name_owner, + "/org/freedesktop", + NULL, + _dbus_managed_objects_changed_cb, + self, + NULL); + + priv->dbsid_dbus_properties_properties_changed = + nm_dbus_connection_signal_subscribe_properties_changed(priv->dbus_connection, + priv->name_owner, + NULL, + NULL, + _dbus_properties_changed_cb, + self, + NULL); + + priv->dbsid_nm_settings_connection_updated = + g_dbus_connection_signal_subscribe(priv->dbus_connection, + priv->name_owner, + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Updated", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _dbus_settings_updated_cb, + self, + NULL); + + priv->dbsid_nm_connection_active_state_changed = + g_dbus_connection_signal_subscribe(priv->dbus_connection, + priv->name_owner, + NM_DBUS_INTERFACE_ACTIVE_CONNECTION, + "StateChanged", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _dbus_nm_connection_active_state_changed_cb, + self, + NULL); + + priv->dbsid_nm_vpn_connection_state_changed = + g_dbus_connection_signal_subscribe(priv->dbus_connection, + priv->name_owner, + NM_DBUS_INTERFACE_VPN_CONNECTION, + "VpnStateChanged", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _dbus_nm_vpn_connection_state_changed_cb, + self, + NULL); + + priv->dbsid_nm_check_permissions = + g_dbus_connection_signal_subscribe(priv->dbus_connection, + priv->name_owner, + NM_DBUS_INTERFACE, + "CheckPermissions", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _dbus_nm_check_permissions_cb, + self, + NULL); + + g_dbus_connection_call(priv->dbus_connection, + priv->name_owner, + "/org/freedesktop", + DBUS_INTERFACE_OBJECT_MANAGER, + "GetManagedObjects", + NULL, + G_VARIANT_TYPE("(a{oa{sa{sv}}})"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + priv->get_managed_objects_cancellable, + _dbus_get_managed_objects_cb, + nm_utils_user_data_pack(self, g_object_ref(priv->context_busy_watcher))); + + _dbus_check_permissions_start(self); +} + +static void +_init_release_all(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + CList ** dbus_objects_lst_heads; + NMLDBusObject * dbobj; + int i; + gboolean permissions_state_changed = FALSE; + + NML_NMCLIENT_LOG_D(self, "release all"); + + nm_clear_g_cancellable(&priv->permissions_cancellable); + nm_clear_g_cancellable(&priv->get_managed_objects_cancellable); + + nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->dbsid_nm_object_manager); + nm_clear_g_dbus_connection_signal(priv->dbus_connection, + &priv->dbsid_dbus_properties_properties_changed); + nm_clear_g_dbus_connection_signal(priv->dbus_connection, + &priv->dbsid_nm_settings_connection_updated); + nm_clear_g_dbus_connection_signal(priv->dbus_connection, + &priv->dbsid_nm_connection_active_state_changed); + nm_clear_g_dbus_connection_signal(priv->dbus_connection, + &priv->dbsid_nm_vpn_connection_state_changed); + nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->dbsid_nm_check_permissions); + + if (priv->permissions_state != NM_TERNARY_DEFAULT) { + priv->permissions_state = NM_TERNARY_DEFAULT; + permissions_state_changed = TRUE; + } + + if (priv->permissions) { + gs_free guint8 *old_permissions = g_steal_pointer(&priv->permissions); + + _emit_permissions_changed(self, old_permissions, NULL); + } + + if (permissions_state_changed) + _notify(self, PROP_PERMISSIONS_STATE); + + nm_assert(c_list_is_empty(&priv->obj_changed_lst_head)); + + dbus_objects_lst_heads = ((CList *[]){ + &priv->dbus_objects_lst_head_on_dbus, + &priv->dbus_objects_lst_head_with_nmobj_not_ready, + &priv->dbus_objects_lst_head_with_nmobj_ready, + NULL, + }); + for (i = 0; dbus_objects_lst_heads[i]; i++) { + c_list_for_each_entry (dbobj, dbus_objects_lst_heads[i], dbus_objects_lst) { + NMLDBusObjIfaceData *db_iface_data; + + nm_assert(c_list_is_empty(&dbobj->obj_changed_lst)); + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) + db_iface_data->iface_removed = TRUE; + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_DBUS); + } + } + + _dbus_handle_changes(self, "release-all", FALSE); + + /* We require that when we remove all D-Bus interfaces, that all object will go + * away. Note that a NMLDBusObject can be alive due to a NMLDBusObjWatcher, but + * even those should be all cleaned up. */ + nm_assert(c_list_is_empty(&priv->obj_changed_lst_head)); + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_watched_only)); + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_on_dbus)); + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_with_nmobj_not_ready)); + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_with_nmobj_ready)); + nm_assert(nm_g_hash_table_size(priv->dbus_objects) == 0); +} + +/*****************************************************************************/ + +static void +name_owner_changed(NMClient *self, const char *name_owner) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + gboolean changed; + gs_free char * old_name_owner_free = NULL; + const char * old_name_owner; + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + + name_owner = nm_str_not_empty(name_owner); + + changed = !nm_streq0(priv->name_owner, name_owner); + + if (!name_owner && priv->main_context != priv->dbus_context) { + gs_unref_object GObject *old_context_busy_watcher = NULL; + + NML_NMCLIENT_LOG_D(self, "resync main context as we have no name owner"); + + nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); + + /* Our instance was initialized synchronously. Usually we must henceforth + * stick to a internal main context. But now we have no name-owner... + * at this point, we anyway are going to do a full resync. Swap the main + * contexts again. */ + + old_context_busy_watcher = g_steal_pointer(&priv->context_busy_watcher); + priv->context_busy_watcher = g_object_ref( + g_object_get_qdata(old_context_busy_watcher, nm_context_busy_watcher_quark())); + + g_main_context_ref(priv->main_context); + g_main_context_unref(priv->dbus_context); + priv->dbus_context = priv->main_context; + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); + + /* we need to sync again... */ + + _assert_main_context_is_current_thread_default(self, dbus_context); + + priv->name_owner_changed_id = + nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection, + NM_DBUS_SERVICE, + name_owner_changed_cb, + self, + NULL); + name_owner_get_call(self); + } else + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); + + if (changed) { + NML_NMCLIENT_LOG_D(self, + "name owner changed: %s%s%s -> %s%s%s", + NM_PRINT_FMT_QUOTE_STRING(priv->name_owner), + NM_PRINT_FMT_QUOTE_STRING(name_owner)); + old_name_owner_free = priv->name_owner; + priv->name_owner = g_strdup(name_owner); + old_name_owner = old_name_owner_free; + } else + old_name_owner = priv->name_owner; + + if (changed) + _notify(self, PROP_DBUS_NAME_OWNER); + + if (changed && old_name_owner) + _init_release_all(self); + + if (changed && priv->name_owner) + _init_fetch_all(self); + + _set_nm_running(self); + + if (priv->init_data) { + nm_auto_pop_gmaincontext GMainContext *main_context = NULL; + + if (priv->main_context != priv->dbus_context) + main_context = nm_g_main_context_push_thread_default_if_necessary(priv->main_context); + _init_start_check_complete(self); + } +} + +static void +name_owner_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv; + const char * new_owner; + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) + return; + + priv = NM_CLIENT_GET_PRIVATE(self); + if (priv->name_owner_get_cancellable) + return; + + g_variant_get(parameters, "(&s&s&s)", NULL, NULL, &new_owner); + + name_owner_changed(self, new_owner); +} + +static void +name_owner_get_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMClient * self; + NMClientPrivate *priv; + gs_unref_object GObject *context_busy_watcher = NULL; + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + const char * name_owner = NULL; + + nm_utils_user_data_unpack(user_data, &self, &context_busy_watcher); + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + + if (!ret && nm_utils_error_is_cancelled(error)) + return; + + priv = NM_CLIENT_GET_PRIVATE(self); + + g_clear_object(&priv->name_owner_get_cancellable); + + if (ret) + g_variant_get(ret, "(&s)", &name_owner); + + name_owner_changed(self, name_owner); +} + +static void +name_owner_get_call(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + nm_assert(!priv->name_owner_get_cancellable); + priv->name_owner_get_cancellable = g_cancellable_new(); + + g_dbus_connection_call(priv->dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetNameOwner", + g_variant_new("(s)", NM_DBUS_SERVICE), + G_VARIANT_TYPE("(s)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + priv->name_owner_get_cancellable, + name_owner_get_cb, + nm_utils_user_data_pack(self, g_object_ref(priv->context_busy_watcher))); +} + +/*****************************************************************************/ + +static inline gboolean +_nml_cleanup_context_busy_watcher_on_idle_cb(gpointer user_data) +{ + nm_auto_unref_gmaincontext GMainContext *context = NULL; + gs_unref_object GObject *context_busy_watcher = NULL; + + nm_utils_user_data_unpack(user_data, &context, &context_busy_watcher); + + nm_assert(context); + nm_assert(G_IS_OBJECT(context_busy_watcher)); + return G_SOURCE_REMOVE; +} + +void +nml_cleanup_context_busy_watcher_on_idle(GObject *context_busy_watcher_take, GMainContext *context) +{ + gs_unref_object GObject *context_busy_watcher = g_steal_pointer(&context_busy_watcher_take); + GSource * cleanup_source; + + nm_assert(G_IS_OBJECT(context_busy_watcher)); + nm_assert(context); + + /* Technically, we cancelled all pending actions (and these actions + * (GTask) keep the context_busy_watcher object alive). Also, we passed + * no destroy notify to g_dbus_connection_signal_subscribe(). + * That means, there should be no other unaccounted GSource'es left. + * + * Another reason is g_dbus_connection_signal_unsubscribe() which does + * not guarantee that everything was unsubscribed right away. Instead, + * there might still be idle actions queued (which will be thrown away + * once processed). Still, that means, the caller must continue to iterate + * the main context afterwards. Maybe we should pass a GDestroyNotify to + * g_dbus_connection_signal_subscribe() which handles an additional reference + * to the context_busy_watcher object. That would be the proper way to handle + * this. We don't do that, so in practice at this point there might still + * be some idle actions (with G_PRIORITY_DEFAULT) pending. As we schedule here + * another idle action with lower priority, we handle those cases without + * using a GDestroyNotify. + * + * However, we really need to be sure that the context_busy_watcher's + * lifetime matches the time that the context is busy. That is especially + * important with synchronous initialization, where the context-busy-watcher + * keeps the inner GMainContext integrated in the caller's. + * We must not g_source_destroy() that integration too early. + * + * So to be really sure all this is given, always schedule one last + * cleanup idle action with low priority. This should be the last + * thing related to this instance that keeps the context busy. + * + * Note that we could also *not* take a reference on @context + * and unref @context_busy_watcher via the GDestroyNotify. That would + * allow for the context to be wrapped up early, and when the last user + * gives up the reference to the context, the destroy notify could complete + * without even invoke the idle handler. However, that destroy notify may + * not be called in the right thread. So, we want to be sure that we unref + * the context-busy-watcher in the right context. Hence, we always take an + * additional reference and always cleanup in the idle handler. This means: + * the user *MUST* always keep iterating the context after NMClient got destroyed. + * But that is not a severe limitation, because the user anyway must be prepared + * to do that. That is because in many cases it is necessary anyway (and the user + * wouldn't know a priory when not). This way, it is just always necessary. + * + * It might be nice to use G_DEFAULT_PRIORITY here to minimize how long the + * instance stays alive. Probably that would be fine, even without passing + * a GDestroyNotify to g_dbus_connection_signal_subscribe(). But to be extra + * careful, use a low priority. + **/ + + cleanup_source = + nm_g_idle_source_new(G_PRIORITY_LOW + 10, + _nml_cleanup_context_busy_watcher_on_idle_cb, + nm_utils_user_data_pack(g_main_context_ref(context), + g_steal_pointer(&context_busy_watcher)), + NULL); + g_source_attach(cleanup_source, context); + g_source_unref(cleanup_source); +} + +/*****************************************************************************/ + +static void +_init_start_complete(NMClient *self, GError *error_take) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + NML_NMCLIENT_LOG_D( + self, + "%s init complete with %s%s%s", + priv->init_data->is_sync ? "sync" : "async", + NM_PRINT_FMT_QUOTED(error_take, "error: ", error_take->message, "", "success")); + + nml_init_data_return(g_steal_pointer(&priv->init_data), error_take); +} + +static void +_init_start_check_complete(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + _assert_main_context_is_current_thread_default(self, main_context); + + if (!priv->init_data) + return; + + if (priv->get_managed_objects_cancellable) { + /* still initializing. Wait. */ + return; + } + +#if NM_MORE_ASSERTS > 10 + { + NMLDBusObject *dbobj; + + c_list_for_each_entry (dbobj, + &priv->dbus_objects_lst_head_with_nmobj_not_ready, + dbus_objects_lst) { + NML_NMCLIENT_LOG_T(self, "init-start waiting for %s", dbobj->dbus_path->str); + break; + } + } +#endif + + if (!c_list_is_empty(&priv->dbus_objects_lst_head_with_nmobj_not_ready)) + return; + + _init_start_complete(self, NULL); +} + +static void +_init_start_cancelled_cb(GCancellable *cancellable, gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + GError * error; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(priv->init_data); + nm_assert(priv->init_data->cancellable == cancellable); + + if (priv->init_data->cancelled_id == 0) { + /* this only can happen if the cancellable was already cancelled initially. + * In this case we are called synchronously. Do nothing, and let the caller + * handle it. */ + return; + } + + nm_utils_error_set_cancelled(&error, FALSE, NULL); + _init_start_complete(self, error); +} + +static gboolean +_init_start_cancel_on_idle_cb(gpointer user_data) +{ + NMClient *self = user_data; + GError * error; + + nm_utils_error_set_cancelled(&error, FALSE, NULL); + _init_start_complete(self, error); + return G_SOURCE_CONTINUE; +} + +static void +_init_start_with_bus(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + if (priv->init_data->cancellable) { + gulong id; + + id = g_cancellable_connect(priv->init_data->cancellable, + G_CALLBACK(_init_start_cancelled_cb), + 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); + g_source_attach(priv->init_data->cancel_on_idle_source, priv->main_context); + return; + } + + priv->init_data->cancelled_id = id; + } + + _assert_main_context_is_current_thread_default(self, dbus_context); + + priv->name_owner_changed_id = + nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection, + NM_DBUS_SERVICE, + name_owner_changed_cb, + self, + NULL); + name_owner_get_call(self); +} + +static void +_init_start_bus_get_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv; + GDBusConnection *dbus_connection; + GError * error = NULL; + + nm_assert(NM_IS_CLIENT(self)); + + dbus_connection = g_bus_get_finish(result, &error); + + if (!dbus_connection) { + _init_start_complete(self, error); + return; + } + + priv = NM_CLIENT_GET_PRIVATE(self); + priv->dbus_connection = dbus_connection; + + _init_start_with_bus(self); + + _notify(self, PROP_DBUS_CONNECTION); +} + +static void +_init_start(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + NML_NMCLIENT_LOG_D(self, + "starting %s initialization...", + priv->init_data->is_sync ? "sync" : "async"); + + if (!priv->dbus_connection) { + g_bus_get(_nm_dbus_bus_type(), priv->init_data->cancellable, _init_start_bus_get_cb, self); + return; + } + + _init_start_with_bus(self); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMClient * self = NM_CLIENT(object); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_INSTANCE_FLAGS: + g_value_set_uint(value, priv->instance_flags); + break; + case PROP_DBUS_CONNECTION: + g_value_set_object(value, priv->dbus_connection); + break; + case PROP_DBUS_NAME_OWNER: + g_value_set_string(value, nm_client_get_dbus_name_owner(self)); + break; + case PROP_NM_RUNNING: + g_value_set_boolean(value, nm_client_get_nm_running(self)); + break; + + /* Manager properties. */ + case PROP_VERSION: + g_value_set_string(value, nm_client_get_version(self)); + break; + case PROP_STATE: + g_value_set_enum(value, nm_client_get_state(self)); + break; + case PROP_STARTUP: + g_value_set_boolean(value, nm_client_get_startup(self)); + break; + case PROP_NETWORKING_ENABLED: + g_value_set_boolean(value, nm_client_networking_get_enabled(self)); + break; + case PROP_WIRELESS_ENABLED: + g_value_set_boolean(value, nm_client_wireless_get_enabled(self)); + break; + case PROP_WIRELESS_HARDWARE_ENABLED: + g_value_set_boolean(value, nm_client_wireless_hardware_get_enabled(self)); + break; + case PROP_WWAN_ENABLED: + g_value_set_boolean(value, nm_client_wwan_get_enabled(self)); + break; + case PROP_WWAN_HARDWARE_ENABLED: + g_value_set_boolean(value, nm_client_wwan_hardware_get_enabled(self)); + break; + case PROP_WIMAX_ENABLED: + g_value_set_boolean(value, FALSE); + break; + case PROP_WIMAX_HARDWARE_ENABLED: + g_value_set_boolean(value, FALSE); + break; + case PROP_ACTIVE_CONNECTIONS: + g_value_take_boxed(value, + _nm_utils_copy_object_array(nm_client_get_active_connections(self))); + break; + case PROP_CONNECTIVITY: + g_value_set_enum(value, nm_client_get_connectivity(self)); + break; + case PROP_CONNECTIVITY_CHECK_AVAILABLE: + g_value_set_boolean(value, nm_client_connectivity_check_get_available(self)); + break; + case PROP_CONNECTIVITY_CHECK_ENABLED: + g_value_set_boolean(value, nm_client_connectivity_check_get_enabled(self)); + break; + case PROP_CONNECTIVITY_CHECK_URI: + g_value_set_string(value, nm_client_connectivity_check_get_uri(self)); + break; + case PROP_PRIMARY_CONNECTION: + g_value_set_object(value, nm_client_get_primary_connection(self)); + break; + case PROP_ACTIVATING_CONNECTION: + g_value_set_object(value, nm_client_get_activating_connection(self)); + break; + case PROP_DEVICES: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_client_get_devices(self))); + break; + case PROP_METERED: + g_value_set_uint(value, nm_client_get_metered(self)); + break; + case PROP_ALL_DEVICES: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_client_get_all_devices(self))); + break; + case PROP_CHECKPOINTS: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_client_get_checkpoints(self))); + break; + case PROP_CAPABILITIES: + { + const guint32 *arr; + GArray * out; + gsize len; + + arr = nm_client_get_capabilities(self, &len); + if (arr) { + out = g_array_new(TRUE, FALSE, sizeof(guint32)); + g_array_append_vals(out, arr, len); + } else + out = NULL; + g_value_take_boxed(value, out); + } break; + case PROP_PERMISSIONS_STATE: + g_value_set_enum(value, priv->permissions_state); + break; + + /* Settings properties. */ + case PROP_CONNECTIONS: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_client_get_connections(self))); + break; + case PROP_HOSTNAME: + g_value_set_string(value, priv->settings.hostname); + break; + case PROP_CAN_MODIFY: + g_value_set_boolean(value, priv->settings.can_modify); + break; + + /* DNS properties */ + case PROP_DNS_MODE: + g_value_set_string(value, nm_client_get_dns_mode(self)); + break; + case PROP_DNS_RC_MANAGER: + g_value_set_string(value, nm_client_get_dns_rc_manager(self)); + break; + case PROP_DNS_CONFIGURATION: + g_value_take_boxed(value, + _nm_utils_copy_array(nm_client_get_dns_configuration(self), + (NMUtilsCopyFunc) nm_dns_entry_dup, + (GDestroyNotify) nm_dns_entry_unref)); + 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) +{ + NMClient * self = NM_CLIENT(object); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + gboolean b; + guint v_uint; + + switch (prop_id) { + case PROP_INSTANCE_FLAGS: + /* construct */ + + v_uint = g_value_get_uint(value); + g_return_if_fail(!NM_FLAGS_ANY(v_uint, ~((guint) NM_CLIENT_INSTANCE_FLAGS_ALL))); + v_uint &= ((guint) NM_CLIENT_INSTANCE_FLAGS_ALL); + + if (!priv->instance_flags_constructed) { + priv->instance_flags_constructed = TRUE; + priv->instance_flags = v_uint; + nm_assert((guint) priv->instance_flags == v_uint); + } else { + NMClientInstanceFlags flags = v_uint; + + /* After object construction, we only allow to toggle certain flags and + * ignore all other flags. */ + + if ((priv->instance_flags ^ flags) + & NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS) { + if (NM_FLAGS_HAS(flags, NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS)) + priv->instance_flags |= NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS; + else + priv->instance_flags &= ~NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS; + if (priv->dbsid_nm_check_permissions != 0) + _dbus_check_permissions_start(self); + } + } + break; + + case PROP_DBUS_CONNECTION: + /* construct-only */ + priv->dbus_connection = g_value_dup_object(value); + break; + + case PROP_NETWORKING_ENABLED: + b = g_value_get_boolean(value); + if (priv->nm.networking_enabled != b) { + nm_client_networking_set_enabled(self, b, NULL); + /* Let the property value flip when we get the change signal from NM */ + } + break; + case PROP_WIRELESS_ENABLED: + b = g_value_get_boolean(value); + if (priv->nm.wireless_enabled != b) { + nm_client_wireless_set_enabled(self, b); + /* Let the property value flip when we get the change signal from NM */ + } + break; + case PROP_WWAN_ENABLED: + b = g_value_get_boolean(value); + if (priv->nm.wwan_enabled != b) { + nm_client_wwan_set_enabled(self, b); + /* Let the property value flip when we get the change signal from NM */ + } + break; + case PROP_CONNECTIVITY_CHECK_ENABLED: + b = g_value_get_boolean(value); + if (priv->nm.connectivity_check_enabled != b) { + nm_client_connectivity_check_set_enabled(self, b); + /* Let the property value flip when we get the change signal from NM */ + } + break; + case PROP_WIMAX_ENABLED: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static gboolean +init_sync(GInitable *initable, GCancellable *cancellable, GError **error) +{ + gs_unref_object NMClient *self = NULL; + NMClientPrivate * priv; + GMainContext * dbus_context; + GError * local_error = NULL; + GMainLoop * main_loop; + GObject * parent_context_busy_watcher; + + g_return_val_if_fail(NM_IS_CLIENT(initable), FALSE); + + self = g_object_ref(NM_CLIENT(initable)); /* keep instance alive. */ + + priv = NM_CLIENT_GET_PRIVATE(self); + + g_return_val_if_fail(!priv->dbus_context, FALSE); + + /* when using init_sync(), we use a separate internal GMainContext for + * all D-Bus operations and use our regular async-init code. That means, + * also in sync-init, we don't actually block waiting for our D-Bus requests, + * instead, we only block (g_main_loop_run()) for the overall result. + * + * Doing this has a performance overhead. Also, we cannot ever fall back + * to the regular main-context (not unless we lose the main-owner and + * need to re-initialize). The reason is that we receive events on our + * dbus_context, and this cannot be brought in sync -- short of full + * reinitalizing. Therefor, using sync init not only is slower during + * construction of the object, but NMClient will stick to the dual GMainContext + * mode. + * + * Aside from this downside, the solution is good: + * + * - we don't duplicate the implementation of async-init. + * - we don't iterate the main-context of the caller while waiting for + * initialization to happen + * - we still invoke all changes under the main_context of the caller. + * - all D-Bus events strictly go through dbus_context and are in order. + */ + + dbus_context = g_main_context_new(); + priv->dbus_context = g_main_context_ref(dbus_context); + + /* We have an inner context. Note that if we loose the name owner, we have a chance + * to resync and drop the inner context. That means, requests made against the inner + * context have a different lifetime. Hence, we create a separate tracking + * object. This "wraps" the outer context-busy-watcher and references it, so + * that the work together. Grep for nm_context_busy_watcher_quark() to + * see how this works. */ + parent_context_busy_watcher = g_steal_pointer(&priv->context_busy_watcher); + priv->context_busy_watcher = g_object_new(G_TYPE_OBJECT, NULL); + g_object_set_qdata_full(priv->context_busy_watcher, + nm_context_busy_watcher_quark(), + parent_context_busy_watcher, + g_object_unref); + + g_main_context_push_thread_default(dbus_context); + + main_loop = g_main_loop_new(dbus_context, FALSE); + + priv->init_data = nml_init_data_new_sync(cancellable, main_loop, &local_error); + + _init_start(self); + + g_main_loop_run(main_loop); + + g_main_loop_unref(main_loop); + + g_main_context_pop_thread_default(dbus_context); + + if (priv->main_context != priv->dbus_context) { + nm_context_busy_watcher_integrate_source(priv->main_context, + priv->dbus_context, + priv->context_busy_watcher); + } + + g_main_context_unref(dbus_context); + + if (local_error) { + g_propagate_error(error, local_error); + return FALSE; + } + return TRUE; +} + +/*****************************************************************************/ + +static void +init_async(GAsyncInitable * initable, + int io_priority, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMClientPrivate * priv; + NMClient * self; + nm_auto_pop_gmaincontext GMainContext *context = NULL; + GTask * task; + + g_return_if_fail(NM_IS_CLIENT(initable)); + + self = NM_CLIENT(initable); + priv = NM_CLIENT_GET_PRIVATE(self); + + g_return_if_fail(!priv->dbus_context); + + priv->dbus_context = g_main_context_ref(priv->main_context); + + context = nm_g_main_context_push_thread_default_if_necessary(priv->main_context); + + task = nm_g_task_new(self, cancellable, init_async, callback, user_data); + g_task_set_priority(task, io_priority); + + priv->init_data = nml_init_data_new_async(cancellable, g_steal_pointer(&task)); + + _init_start(self); +} + +static gboolean +init_finish(GAsyncInitable *initable, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(initable), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, initable, init_async), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +static void +nm_client_init(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + priv->permissions_state = NM_TERNARY_DEFAULT; + + priv->context_busy_watcher = g_object_new(G_TYPE_OBJECT, NULL); + + c_list_init(&self->obj_base.queue_notify_lst); + c_list_init(&priv->queue_notify_lst_head); + c_list_init(&priv->notify_event_lst_head); + + priv->dbus_objects = g_hash_table_new(nm_pdirect_hash, nm_pdirect_equal); + c_list_init(&priv->dbus_objects_lst_head_watched_only); + c_list_init(&priv->dbus_objects_lst_head_on_dbus); + c_list_init(&priv->dbus_objects_lst_head_with_nmobj_not_ready); + c_list_init(&priv->dbus_objects_lst_head_with_nmobj_ready); + c_list_init(&priv->obj_changed_lst_head); +} + +/** + * nm_client_new: + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Creates a new #NMClient synchronously. + * + * Note that this will block until a NMClient instance is fully initialized. + * This does nothing beside calling g_initable_new(). You are free to call + * g_initable_new() or g_object_new()/g_initable_init() directly for more + * control, to set GObject properties or get access to the NMClient instance + * while it is still initializing. + * + * Using the synchronous initialization creates an #NMClient instance + * that uses an internal #GMainContext. This context is invisible to the + * user. This introduces an additional overhead that is payed not + * only during object initialization, but for the entire lifetime of + * this object. + * Also, due to this internal #GMainContext, the events are no longer + * in sync with other messages from #GDBusConnection (but all events + * of the NMClient will themselves still be ordered). + * For a serious program, you should therefore avoid these problems by + * using g_async_initable_init_async() or nm_client_new_async() instead. + * The sync initialization is still useful for simple scripts or interactive + * testing for example via pygobject. + * + * Creating an #NMClient instance can only fail for two reasons. First, if you didn't + * provide a %NM_CLIENT_DBUS_CONNECTION and the call to g_bus_get() + * fails. You can avoid that by using g_initable_new() directly and + * set a D-Bus connection. + * Second, if you cancelled the creation. If you do that, then note + * that after the failure there might still be idle actions pending + * which keep nm_client_get_main_context() alive. That means, + * in that case you must continue iterating the context to avoid + * leaks. See nm_client_get_context_busy_watcher(). + * + * Creating an #NMClient instance when NetworkManager is not running + * does not cause a failure. + * + * Returns: a new #NMClient or NULL on an error + **/ +NMClient * +nm_client_new(GCancellable *cancellable, GError **error) +{ + return g_initable_new(NM_TYPE_CLIENT, cancellable, error, NULL); +} + +/** + * nm_client_new_async: + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the client is created + * @user_data: data for @callback + * + * Creates a new #NMClient asynchronously. + * @callback will be called when it is done. Use + * nm_client_new_finish() to get the result. + * + * This does nothing beside calling g_async_initable_new_async(). You are free to + * call g_async_initable_new_async() or g_object_new()/g_async_initable_init_async() + * directly for more control, to set GObject properties or get access to the NMClient + * instance while it is still initializing. + * + * Creating an #NMClient instance can only fail for two reasons. First, if you didn't + * provide a %NM_CLIENT_DBUS_CONNECTION and the call to g_bus_get() + * fails. You can avoid that by using g_async_initable_new_async() directly and + * set a D-Bus connection. + * Second, if you cancelled the creation. If you do that, then note + * that after the failure there might still be idle actions pending + * which keep nm_client_get_main_context() alive. That means, + * in that case you must continue iterating the context to avoid + * leaks. See nm_client_get_context_busy_watcher(). + * + * Creating an #NMClient instance when NetworkManager is not running + * does not cause a failure. + **/ +void +nm_client_new_async(GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) +{ + g_async_initable_new_async(NM_TYPE_CLIENT, + G_PRIORITY_DEFAULT, + cancellable, + callback, + user_data, + NULL); +} + +/** + * nm_client_new_finish: + * @result: a #GAsyncResult + * @error: location for a #GError, or %NULL + * + * Gets the result of an nm_client_new_async() call. + * + * Returns: a new #NMClient, or %NULL on error + **/ +NMClient * +nm_client_new_finish(GAsyncResult *result, GError **error) +{ + gs_unref_object GObject *source_object = NULL; + GObject * object; + + source_object = g_async_result_get_source_object(result); + g_return_val_if_fail(source_object, NULL); + + object = g_async_initable_new_finish(G_ASYNC_INITABLE(source_object), result, error); + g_return_val_if_fail(!object || NM_IS_CLIENT(object), FALSE); + + return NM_CLIENT(object); +} + +static void +constructed(GObject *object) +{ + NMClient * self = NM_CLIENT(object); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + priv->main_context = g_main_context_ref_thread_default(); + + G_OBJECT_CLASS(nm_client_parent_class)->constructed(object); + + NML_NMCLIENT_LOG_D(self, "new NMClient instance"); +} + +static void +dispose(GObject *object) +{ + NMClient * self = NM_CLIENT(object); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + nm_assert(!priv->init_data); + + self->obj_base.is_disposing = TRUE; + + nm_clear_g_cancellable(&priv->name_owner_get_cancellable); + + nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); + + nm_clear_g_free(&priv->name_owner); + + _init_release_all(self); + + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_watched_only)); + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_on_dbus)); + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_with_nmobj_not_ready)); + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_with_nmobj_ready)); + + nm_assert(c_list_is_empty(&priv->queue_notify_lst_head)); + nm_assert(c_list_is_empty(&priv->notify_event_lst_head)); + nm_assert(c_list_is_empty(&self->obj_base.queue_notify_lst)); + nm_assert(nm_g_hash_table_size(priv->dbus_objects) == 0); + + nml_dbus_property_o_clear_many(priv->nm.property_o, G_N_ELEMENTS(priv->nm.property_o), NULL); + nml_dbus_property_ao_clear_many(priv->nm.property_ao, G_N_ELEMENTS(priv->nm.property_ao), NULL); + + nm_clear_g_free(&priv->nm.connectivity_check_uri); + nm_clear_g_free(&priv->nm.version); + + nml_dbus_property_ao_clear(&priv->settings.connections, NULL); + nm_clear_g_free(&priv->settings.hostname); + + nm_clear_pointer(&priv->dns_manager.configuration, g_ptr_array_unref); + nm_clear_g_free(&priv->dns_manager.mode); + nm_clear_g_free(&priv->dns_manager.rc_manager); + + nm_clear_pointer(&priv->dbus_objects, g_hash_table_destroy); + + G_OBJECT_CLASS(nm_client_parent_class)->dispose(object); + + nm_clear_pointer(&priv->udev, udev_unref); + + if (priv->context_busy_watcher && priv->dbus_context) { + nml_cleanup_context_busy_watcher_on_idle(g_steal_pointer(&priv->context_busy_watcher), + priv->dbus_context); + } + + nm_clear_pointer(&priv->dbus_context, g_main_context_unref); + nm_clear_pointer(&priv->main_context, g_main_context_unref); + + nm_clear_g_free(&priv->permissions); + + g_clear_object(&priv->dbus_connection); + + g_clear_object(&priv->context_busy_watcher); + + nm_clear_g_free(&priv->name_owner); + + priv->nm.capabilities_len = 0; + nm_clear_g_free(&priv->nm.capabilities_arr); + + NML_NMCLIENT_LOG_D(self, "disposed"); +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_agentmanager = + NML_DBUS_META_IFACE_INIT(NM_DBUS_INTERFACE_AGENT_MANAGER, + NULL, + NML_DBUS_META_INTERFACE_PRIO_NONE, ); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE, + nm_client_get_type, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_O_PROP( + "ActivatingConnection", + PROP_ACTIVATING_CONNECTION, + NMClient, + _priv.nm.property_o[PROPERTY_O_IDX_NM_ACTIVATING_CONNECTION], + nm_active_connection_get_type), + NML_DBUS_META_PROPERTY_INIT_AO_PROP( + "ActiveConnections", + PROP_ACTIVE_CONNECTIONS, + NMClient, + _priv.nm.property_ao[PROPERTY_AO_IDX_ACTIVE_CONNECTIONS], + nm_active_connection_get_type, + .notify_changed_ao = _property_ao_notify_changed_active_connections_cb), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("AllDevices", + PROP_ALL_DEVICES, + NMClient, + _priv.nm.property_ao[PROPERTY_AO_IDX_ALL_DEVICES], + nm_device_get_type, + .notify_changed_ao = + _property_ao_notify_changed_all_devices_cb), + NML_DBUS_META_PROPERTY_INIT_FCN("Capabilities", + PROP_CAPABILITIES, + "au", + _notify_update_prop_nm_capabilities, ), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Checkpoints", + PROP_CHECKPOINTS, + NMClient, + _priv.nm.property_ao[PROPERTY_AO_IDX_CHECKPOINTS], + nm_checkpoint_get_type), + NML_DBUS_META_PROPERTY_INIT_U("Connectivity", + PROP_CONNECTIVITY, + NMClient, + _priv.nm.connectivity), + NML_DBUS_META_PROPERTY_INIT_B("ConnectivityCheckAvailable", + PROP_CONNECTIVITY_CHECK_AVAILABLE, + NMClient, + _priv.nm.connectivity_check_available), + NML_DBUS_META_PROPERTY_INIT_B("ConnectivityCheckEnabled", + PROP_CONNECTIVITY_CHECK_ENABLED, + NMClient, + _priv.nm.connectivity_check_enabled), + NML_DBUS_META_PROPERTY_INIT_S("ConnectivityCheckUri", + PROP_CONNECTIVITY_CHECK_URI, + NMClient, + _priv.nm.connectivity_check_uri), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Devices", + PROP_DEVICES, + NMClient, + _priv.nm.property_ao[PROPERTY_AO_IDX_DEVICES], + nm_device_get_type, + .notify_changed_ao = + _property_ao_notify_changed_devices_cb), + NML_DBUS_META_PROPERTY_INIT_IGNORE("GlobalDnsConfiguration", "a{sv}"), + NML_DBUS_META_PROPERTY_INIT_U("Metered", PROP_METERED, NMClient, _priv.nm.metered), + NML_DBUS_META_PROPERTY_INIT_B("NetworkingEnabled", + PROP_NETWORKING_ENABLED, + NMClient, + _priv.nm.networking_enabled), + NML_DBUS_META_PROPERTY_INIT_O_PROP("PrimaryConnection", + PROP_PRIMARY_CONNECTION, + NMClient, + _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_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), + NML_DBUS_META_PROPERTY_INIT_IGNORE("WimaxEnabled", "b"), + NML_DBUS_META_PROPERTY_INIT_IGNORE("WimaxHardwareEnabled", "b"), + NML_DBUS_META_PROPERTY_INIT_B("WirelessEnabled", + PROP_WIRELESS_ENABLED, + NMClient, + _priv.nm.wireless_enabled), + NML_DBUS_META_PROPERTY_INIT_B("WirelessHardwareEnabled", + PROP_WIRELESS_HARDWARE_ENABLED, + NMClient, + _priv.nm.wireless_hardware_enabled), + NML_DBUS_META_PROPERTY_INIT_B("WwanEnabled", + PROP_WWAN_ENABLED, + NMClient, + _priv.nm.wwan_enabled), + NML_DBUS_META_PROPERTY_INIT_B("WwanHardwareEnabled", + PROP_WWAN_HARDWARE_ENABLED, + NMClient, + _priv.nm.wwan_hardware_enabled), ), ); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_settings = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_SETTINGS, + nm_client_get_type, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("CanModify", + PROP_CAN_MODIFY, + NMClient, + _priv.settings.can_modify), + NML_DBUS_META_PROPERTY_INIT_AO_PROP( + "Connections", + PROP_CONNECTIONS, + NMClient, + _priv.settings.connections, + nm_remote_connection_get_type, + .notify_changed_ao = _property_ao_notify_changed_connections_cb, + .check_nmobj_visible_fcn = (gboolean(*)(GObject *)) nm_remote_connection_get_visible), + NML_DBUS_META_PROPERTY_INIT_S("Hostname", + PROP_HOSTNAME, + NMClient, + _priv.settings.hostname), ), ); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_dnsmanager = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DNS_MANAGER, + nm_client_get_type, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("Configuration", + PROP_DNS_CONFIGURATION, + "aa{sv}", + _notify_update_prop_dns_manager_configuration), + NML_DBUS_META_PROPERTY_INIT_S("Mode", PROP_DNS_MODE, NMClient, _priv.dns_manager.mode), + NML_DBUS_META_PROPERTY_INIT_S("RcManager", + PROP_DNS_RC_MANAGER, + NMClient, + _priv.dns_manager.rc_manager), ), ); + +static void +nm_client_class_init(NMClientClass *client_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(client_class); + + _dbus_path_nm = nm_ref_string_new(NM_DBUS_PATH); + _dbus_path_settings = nm_ref_string_new(NM_DBUS_PATH_SETTINGS); + _dbus_path_dns_manager = nm_ref_string_new(NM_DBUS_PATH_DNS_MANAGER); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->constructed = constructed; + object_class->dispose = dispose; + + /** + * NMClient:dbus-connection: + * + * The #GDBusConnection to use. + * + * If this is not set during object construction, the D-Bus connection will + * automatically be chosen during async/sync initalization via g_bus_get(). + * + * Since: 1.22 + */ + obj_properties[PROP_DBUS_CONNECTION] = g_param_spec_object( + NM_CLIENT_DBUS_CONNECTION, + "", + "", + G_TYPE_DBUS_CONNECTION, + G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:instance-flags: + * + * #NMClientInstanceFlags for the instance. These affect behavior of #NMClient. + * This is a construct property and you may only set most flags only during + * construction. + * + * The flag %NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS can be toggled any time, + * even after constructing the instance. Note that you may want to watch NMClient:permissions-state + * property to know whether permissions are ready. Note that permissions are only fetched + * when NMClient has a D-Bus name owner. + * + * Since: 1.24 + */ + obj_properties[PROP_INSTANCE_FLAGS] = g_param_spec_uint( + NM_CLIENT_INSTANCE_FLAGS, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:dbus-name-owner: + * + * The name owner of the NetworkManager D-Bus service. + * + * Since: 1.22 + **/ + obj_properties[PROP_DBUS_NAME_OWNER] = + g_param_spec_string(NM_CLIENT_DBUS_NAME_OWNER, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:version: + * + * The NetworkManager version. + **/ + obj_properties[PROP_VERSION] = g_param_spec_string(NM_CLIENT_VERSION, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:state: + * + * The current daemon state. + **/ + obj_properties[PROP_STATE] = g_param_spec_enum(NM_CLIENT_STATE, + "", + "", + NM_TYPE_STATE, + NM_STATE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:startup: + * + * Whether the daemon is still starting up. + **/ + obj_properties[PROP_STARTUP] = g_param_spec_boolean(NM_CLIENT_STARTUP, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:nm-running: + * + * Whether the daemon is running. + **/ + obj_properties[PROP_NM_RUNNING] = + g_param_spec_boolean(NM_CLIENT_NM_RUNNING, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:networking-enabled: + * + * Whether networking is enabled. + * + * The property setter is a synchronous D-Bus call. This is deprecated since 1.22. + */ + obj_properties[PROP_NETWORKING_ENABLED] = + g_param_spec_boolean(NM_CLIENT_NETWORKING_ENABLED, + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:wireless-enabled: + * + * Whether wireless is enabled. + * + * The property setter is a synchronous D-Bus call. This is deprecated since 1.22. + **/ + obj_properties[PROP_WIRELESS_ENABLED] = + g_param_spec_boolean(NM_CLIENT_WIRELESS_ENABLED, + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:wireless-hardware-enabled: + * + * Whether the wireless hardware is enabled. + **/ + obj_properties[PROP_WIRELESS_HARDWARE_ENABLED] = + g_param_spec_boolean(NM_CLIENT_WIRELESS_HARDWARE_ENABLED, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:wwan-enabled: + * + * Whether WWAN functionality is enabled. + * + * The property setter is a synchronous D-Bus call. This is deprecated since 1.22. + */ + obj_properties[PROP_WWAN_ENABLED] = + g_param_spec_boolean(NM_CLIENT_WWAN_ENABLED, + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:wwan-hardware-enabled: + * + * Whether the WWAN hardware is enabled. + **/ + obj_properties[PROP_WWAN_HARDWARE_ENABLED] = + g_param_spec_boolean(NM_CLIENT_WWAN_HARDWARE_ENABLED, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:wimax-enabled: + * + * Whether WiMAX functionality is enabled. + * + * Deprecated: 1.22: WiMAX is no longer supported and this always returns FALSE. The setter has no effect. + */ + obj_properties[PROP_WIMAX_ENABLED] = + g_param_spec_boolean(NM_CLIENT_WIMAX_ENABLED, + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:wimax-hardware-enabled: + * + * Whether the WiMAX hardware is enabled. + * + * Deprecated: 1.22: WiMAX is no longer supported and this always returns FALSE. + **/ + obj_properties[PROP_WIMAX_HARDWARE_ENABLED] = + g_param_spec_boolean(NM_CLIENT_WIMAX_HARDWARE_ENABLED, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:active-connections: (type GPtrArray(NMActiveConnection)) + * + * The active connections. + **/ + obj_properties[PROP_ACTIVE_CONNECTIONS] = + g_param_spec_boxed(NM_CLIENT_ACTIVE_CONNECTIONS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:connectivity: + * + * The network connectivity state. + */ + obj_properties[PROP_CONNECTIVITY] = + g_param_spec_enum(NM_CLIENT_CONNECTIVITY, + "", + "", + NM_TYPE_CONNECTIVITY_STATE, + NM_CONNECTIVITY_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient::connectivity-check-available + * + * Whether a connectivity checking service has been configured. + * + * Since: 1.10 + */ + obj_properties[PROP_CONNECTIVITY_CHECK_AVAILABLE] = + g_param_spec_boolean(NM_CLIENT_CONNECTIVITY_CHECK_AVAILABLE, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient::connectivity-check-enabled + * + * Whether a connectivity checking service has been enabled. + * + * Since: 1.10 + * + * The property setter is a synchronous D-Bus call. This is deprecated since 1.22. + */ + obj_properties[PROP_CONNECTIVITY_CHECK_ENABLED] = + g_param_spec_boolean(NM_CLIENT_CONNECTIVITY_CHECK_ENABLED, + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:connectivity-check-uri: + * + * The used URI for connectivity checking. + * + * Since: 1.22 + **/ + obj_properties[PROP_CONNECTIVITY_CHECK_URI] = + g_param_spec_string(NM_CLIENT_CONNECTIVITY_CHECK_URI, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:primary-connection: + * + * The #NMActiveConnection of the device with the default route; + * see nm_client_get_primary_connection() for more details. + **/ + obj_properties[PROP_PRIMARY_CONNECTION] = + g_param_spec_object(NM_CLIENT_PRIMARY_CONNECTION, + "", + "", + NM_TYPE_ACTIVE_CONNECTION, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:activating-connection: + * + * The #NMActiveConnection of the activating connection that is + * likely to become the new #NMClient:primary-connection. + **/ + obj_properties[PROP_ACTIVATING_CONNECTION] = + g_param_spec_object(NM_CLIENT_ACTIVATING_CONNECTION, + "", + "", + NM_TYPE_ACTIVE_CONNECTION, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:devices: (type GPtrArray(NMDevice)) + * + * List of real network devices. Does not include placeholder devices. + **/ + obj_properties[PROP_DEVICES] = g_param_spec_boxed(NM_CLIENT_DEVICES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:all-devices: (type GPtrArray(NMDevice)) + * + * List of both real devices and device placeholders. + * Since: 1.2 + **/ + obj_properties[PROP_ALL_DEVICES] = + g_param_spec_boxed(NM_CLIENT_ALL_DEVICES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:connections: (type GPtrArray(NMRemoteConnection)) + * + * The list of configured connections that are available to the user. (Note + * that this differs from the underlying D-Bus property, which may also + * contain the object paths of connections that the user does not have + * permission to read the details of.) + */ + obj_properties[PROP_CONNECTIONS] = + g_param_spec_boxed(NM_CLIENT_CONNECTIONS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:hostname: + * + * The machine hostname stored in persistent configuration. This can be + * modified by calling nm_client_save_hostname(). + */ + obj_properties[PROP_HOSTNAME] = g_param_spec_string(NM_CLIENT_HOSTNAME, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:can-modify: + * + * If %TRUE, adding and modifying connections is supported. + */ + obj_properties[PROP_CAN_MODIFY] = + g_param_spec_boolean(NM_CLIENT_CAN_MODIFY, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:metered: + * + * Whether the connectivity is metered. + * + * Since: 1.2 + **/ + obj_properties[PROP_METERED] = g_param_spec_uint(NM_CLIENT_METERED, + "", + "", + 0, + G_MAXUINT32, + NM_METERED_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:dns-mode: + * + * The current DNS processing mode. + * + * Since: 1.6 + **/ + obj_properties[PROP_DNS_MODE] = g_param_spec_string(NM_CLIENT_DNS_MODE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:dns-rc-manager: + * + * The current resolv.conf management mode. + * + * Since: 1.6 + **/ + obj_properties[PROP_DNS_RC_MANAGER] = + g_param_spec_string(NM_CLIENT_DNS_RC_MANAGER, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:dns-configuration: (type GPtrArray(NMDnsEntry)) + * + * The current DNS configuration, represented as an array + * of #NMDnsEntry objects. + * + * Since: 1.6 + **/ + obj_properties[PROP_DNS_CONFIGURATION] = + g_param_spec_boxed(NM_CLIENT_DNS_CONFIGURATION, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:checkpoints: (type GPtrArray(NMCheckpoint)) + * + * The list of active checkpoints. + * + * Since: 1.12 + */ + obj_properties[PROP_CHECKPOINTS] = + g_param_spec_boxed(NM_CLIENT_CHECKPOINTS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:capabilities: (type GArray(guint32)) + * + * The list of capabilities numbers as guint32 or %NULL if + * there are no capabilities. The numeric value correspond + * to %NMCapability enum. + * + * Since: 1.24 + */ + obj_properties[PROP_CAPABILITIES] = + g_param_spec_boxed(NM_CLIENT_CAPABILITIES, + "", + "", + G_TYPE_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:permissions-state: + * + * The state of the cached permissions. The value %NM_TERNARY_DEFAULT + * means that no permissions are yet received (or not yet requested). + * %NM_TERNARY_TRUE means that permissions are received, cached and up + * to date. %NM_TERNARY_FALSE means that permissions were received and are + * cached, but in the meantime a "CheckPermissions" signal was received + * that invalidated the cached permissions. + * Note that NMClient will always emit a notify::permissions-state signal + * when a "CheckPermissions" signal got received or after new permissions + * got received (that is regardless whether the value of the permission state + * actually changed). With this you can watch the permissions-state property + * to know whether the permissions are ready. Note that while NMClient has + * no D-Bus name owner, no permissions are fetched (and this property won't + * change). + * + * Since: 1.24 + */ + obj_properties[PROP_PERMISSIONS_STATE] = + g_param_spec_enum(NM_CLIENT_PERMISSIONS_STATE, + "", + "", + NM_TYPE_TERNARY, + NM_TERNARY_DEFAULT, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm, + &_nml_dbus_meta_iface_nm_settings, + &_nml_dbus_meta_iface_nm_dnsmanager); + + /** + * NMClient::device-added: + * @client: the client that received the signal + * @device: (type NMDevice): the new device + * + * Notifies that a #NMDevice is added. This signal is not emitted for + * placeholder devices. + **/ + signals[DEVICE_ADDED] = g_signal_new(NM_CLIENT_DEVICE_ADDED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + /** + * NMClient::device-removed: + * @client: the client that received the signal + * @device: (type NMDevice): the removed device + * + * Notifies that a #NMDevice is removed. This signal is not emitted for + * placeholder devices. + **/ + signals[DEVICE_REMOVED] = g_signal_new(NM_CLIENT_DEVICE_REMOVED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + /** + * NMClient::any-device-added: + * @client: the client that received the signal + * @device: (type NMDevice): the new device + * + * Notifies that a #NMDevice is added. This signal is emitted for both + * regular devices and placeholder devices. + **/ + signals[ANY_DEVICE_ADDED] = g_signal_new(NM_CLIENT_ANY_DEVICE_ADDED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + /** + * NMClient::any-device-removed: + * @client: the client that received the signal + * @device: (type NMDevice): the removed device + * + * Notifies that a #NMDevice is removed. This signal is emitted for both + * regular devices and placeholder devices. + **/ + signals[ANY_DEVICE_REMOVED] = g_signal_new(NM_CLIENT_ANY_DEVICE_REMOVED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + /** + * NMClient::permission-changed: + * @client: the client that received the signal + * @permission: a permission from #NMClientPermission + * @result: the permission's result, one of #NMClientPermissionResult + * + * Notifies that a permission has changed + **/ + signals[PERMISSION_CHANGED] = g_signal_new(NM_CLIENT_PERMISSION_CHANGED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 2, + G_TYPE_UINT, + G_TYPE_UINT); + /** + * NMClient::connection-added: + * @client: the settings object that received the signal + * @connection: the new connection + * + * Notifies that a #NMConnection has been added. + **/ + signals[CONNECTION_ADDED] = g_signal_new(NM_CLIENT_CONNECTION_ADDED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + NM_TYPE_REMOTE_CONNECTION); + + /** + * NMClient::connection-removed: + * @client: the settings object that received the signal + * @connection: the removed connection + * + * Notifies that a #NMConnection has been removed. + **/ + signals[CONNECTION_REMOVED] = g_signal_new(NM_CLIENT_CONNECTION_REMOVED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + NM_TYPE_REMOTE_CONNECTION); + + /** + * NMClient::active-connection-added: + * @client: the settings object that received the signal + * @active_connection: the new active connection + * + * Notifies that a #NMActiveConnection has been added. + **/ + signals[ACTIVE_CONNECTION_ADDED] = g_signal_new(NM_CLIENT_ACTIVE_CONNECTION_ADDED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + NM_TYPE_ACTIVE_CONNECTION); + + /** + * NMClient::active-connection-removed: + * @client: the settings object that received the signal + * @active_connection: the removed active connection + * + * Notifies that a #NMActiveConnection has been removed. + **/ + signals[ACTIVE_CONNECTION_REMOVED] = g_signal_new(NM_CLIENT_ACTIVE_CONNECTION_REMOVED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + NM_TYPE_ACTIVE_CONNECTION); +} + +static void +nm_client_initable_iface_init(GInitableIface *iface) +{ + iface->init = init_sync; +} + +static void +nm_client_async_initable_iface_init(GAsyncInitableIface *iface) +{ + iface->init_async = init_async; + iface->init_finish = init_finish; +} + +/***************************************************************************** + * Backported symbols. Usually, new API is only added in new major versions + * of NetworkManager (that is, on "master" branch). Sometimes however, we might + * have to backport some API to an older stable branch. In that case, we backport + * the symbols with a different version corresponding to the minor API. + * + * To allow upgrading from such a extended minor-release, "master" contains these + * backported symbols too. + * + * For example, 1.2.0 added nm_setting_connection_autoconnect_slaves_get_type. + * This was backported for 1.0.4 as nm_setting_connection_autoconnect_slaves_get_type@libnm_1_0_4 + * To allow an application that was linked against 1.0.4 to seamlessly upgrade to + * a newer major version, the same symbols is also exposed on "master". Note, that + * a user can only seamlessly upgrade to a newer major version, that is released + * *after* 1.0.4 is out. In this example, 1.2.0 was released after 1.4.0, and thus + * a 1.0.4 user can upgrade to 1.2.0 ABI. + *****************************************************************************/ + +NM_BACKPORT_SYMBOL(libnm_1_0_4, + NMSettingConnectionAutoconnectSlaves, + nm_setting_connection_get_autoconnect_slaves, + (NMSettingConnection * setting), + (setting)); + +NM_BACKPORT_SYMBOL(libnm_1_0_4, + GType, + nm_setting_connection_autoconnect_slaves_get_type, + (void), + ()); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, + NMMetered, + nm_setting_connection_get_metered, + (NMSettingConnection * setting), + (setting)); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, GType, nm_metered_get_type, (void), ()); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, + NMSettingWiredWakeOnLan, + nm_setting_wired_get_wake_on_lan, + (NMSettingWired * setting), + (setting)); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, + const char *, + nm_setting_wired_get_wake_on_lan_password, + (NMSettingWired * setting), + (setting)); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, GType, nm_setting_wired_wake_on_lan_get_type, (void), ()); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, const guint *, nm_utils_wifi_2ghz_freqs, (void), ()); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, const guint *, nm_utils_wifi_5ghz_freqs, (void), ()); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, + char *, + nm_utils_enum_to_str, + (GType type, int value), + (type, value)); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, + gboolean, + nm_utils_enum_from_str, + (GType type, const char *str, int *out_value, char **err_token), + (type, str, out_value, err_token)); + +NM_BACKPORT_SYMBOL(libnm_1_2_4, + int, + nm_setting_ip_config_get_dns_priority, + (NMSettingIPConfig * setting), + (setting)); + +NM_BACKPORT_SYMBOL(libnm_1_10_14, + NMSettingConnectionMdns, + nm_setting_connection_get_mdns, + (NMSettingConnection * setting), + (setting)); +NM_BACKPORT_SYMBOL(libnm_1_10_14, GType, nm_setting_connection_mdns_get_type, (void), ()); diff --git a/src/libnm-client-impl/nm-dbus-helpers.c b/src/libnm-client-impl/nm-dbus-helpers.c new file mode 100644 index 00000000..3b5f9ff8 --- /dev/null +++ b/src/libnm-client-impl/nm-dbus-helpers.c @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-dbus-helpers.h" + +#include "nm-dbus-interface.h" + +GBusType +_nm_dbus_bus_type(void) +{ + static volatile int bus_type = G_BUS_TYPE_NONE; + int v; + + v = g_atomic_int_get(&bus_type); + if (G_UNLIKELY(v == G_BUS_TYPE_NONE)) { + v = G_BUS_TYPE_SYSTEM; + if (g_getenv("LIBNM_USE_SESSION_BUS")) + v = G_BUS_TYPE_SESSION; + if (!g_atomic_int_compare_and_exchange(&bus_type, G_BUS_TYPE_NONE, v)) + v = g_atomic_int_get(&bus_type); + nm_assert(v != G_BUS_TYPE_NONE); + } + return v; +} + +/* Binds the properties on a generated server-side GDBus object to the + * corresponding properties on the public object. + */ +void +_nm_dbus_bind_properties(gpointer object, gpointer skeleton) +{ + GParamSpec **properties; + guint n_properties; + int i; + + properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(skeleton), &n_properties); + for (i = 0; i < n_properties; i++) { + if (g_str_has_prefix(properties[i]->name, "g-")) + continue; + + g_object_bind_property(object, + properties[i]->name, + skeleton, + properties[i]->name, + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + } + g_free(properties); +} + +static char * +signal_name_from_method_name(const char *method_name) +{ + GString * signal_name; + const char *p; + + signal_name = g_string_new("handle"); + for (p = method_name; *p; p++) { + if (g_ascii_isupper(*p)) + g_string_append_c(signal_name, '-'); + g_string_append_c(signal_name, g_ascii_tolower(*p)); + } + + return g_string_free(signal_name, FALSE); +} + +static void +_nm_dbus_method_meta_marshal(GClosure * closure, + GValue * return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + closure->marshal(closure, + return_value, + n_param_values, + param_values, + invocation_hint, + ((GCClosure *) closure)->callback); + + g_value_set_boolean(return_value, TRUE); +} + +/* Takes (method_name, handler_func) pairs and connects the handlers to the + * signals on skeleton, with object as the user_data, but swapped so it comes + * first in the argument list, and handling the return value automatically. + */ +void +_nm_dbus_bind_methods(gpointer object, gpointer skeleton, ...) +{ + va_list ap; + const char *method_name; + char * signal_name; + GCallback handler; + GClosure * closure; + + va_start(ap, skeleton); + while ((method_name = va_arg(ap, const char *)) && (handler = va_arg(ap, GCallback))) { + signal_name = signal_name_from_method_name(method_name); + closure = g_cclosure_new_swap(handler, object, NULL); + g_closure_set_meta_marshal(closure, NULL, _nm_dbus_method_meta_marshal); + g_signal_connect_closure(skeleton, signal_name, closure, FALSE); + g_free(signal_name); + } + va_end(ap); +} diff --git a/src/libnm-client-impl/nm-dbus-helpers.h b/src/libnm-client-impl/nm-dbus-helpers.h new file mode 100644 index 00000000..e519f733 --- /dev/null +++ b/src/libnm-client-impl/nm-dbus-helpers.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef __NM_DBUS_HELPERS_PRIVATE_H__ +#define __NM_DBUS_HELPERS_PRIVATE_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#include "libnm-std-aux/nm-dbus-compat.h" + +#define NM_DBUS_DEFAULT_TIMEOUT_MSEC 25000 + +GBusType _nm_dbus_bus_type(void); + +void _nm_dbus_bind_properties(gpointer object, gpointer skeleton); + +void _nm_dbus_bind_methods(gpointer object, gpointer skeleton, ...) G_GNUC_NULL_TERMINATED; + +#endif /* __NM_DBUS_HELPERS_PRIVATE_H__ */ diff --git a/src/libnm-client-impl/nm-default-libnm.h b/src/libnm-client-impl/nm-default-libnm.h new file mode 100644 index 00000000..5b3a8e51 --- /dev/null +++ b/src/libnm-client-impl/nm-default-libnm.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NM_DEFAULT_LIBNM_H__ +#define __NM_DEFAULT_LIBNM_H__ + +/*****************************************************************************/ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#undef NETWORKMANAGER_COMPILATION +#define NETWORKMANAGER_COMPILATION NM_NETWORKMANAGER_COMPILATION_LIBNM + +/*****************************************************************************/ + +#include "nm-version.h" +#include "nm-libnm-utils.h" + +/*****************************************************************************/ + +#endif /* __NM_DEFAULT_LIBNM_H__ */ diff --git a/src/libnm-client-impl/nm-device-6lowpan.c b/src/libnm-client-impl/nm-device-6lowpan.c new file mode 100644 index 00000000..e03b4a00 --- /dev/null +++ b/src/libnm-client-impl/nm-device-6lowpan.c @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-6lowpan.h" + +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PARENT, ); + +typedef struct { + NMLDBusPropertyO parent; +} NMDevice6LowpanPrivate; + +struct _NMDevice6Lowpan { + NMDevice parent; + NMDevice6LowpanPrivate _priv; +}; + +struct _NMDevice6LowpanClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDevice6Lowpan, nm_device_6lowpan, NM_TYPE_DEVICE) + +#define NM_DEVICE_6LOWPAN_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDevice6Lowpan, NM_IS_DEVICE_6LOWPAN, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_6lowpan_get_parent: + * @device: a #NMDevice6Lowpan + * + * Returns: (transfer none): the device's parent device + * + * Since: 1.14 + **/ +NMDevice * +nm_device_6lowpan_get_parent(NMDevice6Lowpan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_6LOWPAN(device), NULL); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_6LOWPAN_GET_PRIVATE(device)->parent); +} + +/** + * nm_device_6lowpan_get_hw_address: (skip) + * @device: a #NMDevice6Lowpan + * + * Gets the hardware (MAC) address of the #NMDevice6Lowpan + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.14 + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_6lowpan_get_hw_address(NMDevice6Lowpan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_6LOWPAN(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/*****************************************************************************/ + +static void +nm_device_6lowpan_init(NMDevice6Lowpan *device) +{} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDevice6Lowpan *device = NM_DEVICE_6LOWPAN(object); + + switch (prop_id) { + case PROP_PARENT: + g_value_set_object(value, nm_device_6lowpan_get_parent(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_lowpan = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_6LOWPAN, + nm_device_6lowpan_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Parent", + PROP_PARENT, + NMDevice6Lowpan, + _priv.parent, + nm_device_get_type), ), ); + +static void +nm_device_6lowpan_class_init(NMDevice6LowpanClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDevice6Lowpan); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDevice6LowpanPrivate, parent); + + /** + * NMDevice6Lowpan:parent: + * + * The devices's parent device. + * + * Since: 1.14 + **/ + obj_properties[PROP_PARENT] = g_param_spec_object(NM_DEVICE_6LOWPAN_PARENT, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_lowpan); +} diff --git a/src/libnm-client-impl/nm-device-adsl.c b/src/libnm-client-impl/nm-device-adsl.c new file mode 100644 index 00000000..65f87545 --- /dev/null +++ b/src/libnm-client-impl/nm-device-adsl.c @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Author: Pantelis Koukousoulas <pktoss@gmail.com> + * Copyright (C) 2009 - 2011 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-adsl.h" + +#include "nm-object-private.h" +#include "nm-setting-adsl.h" +#include "nm-setting-connection.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CARRIER, ); + +typedef struct { + bool carrier; +} NMDeviceAdslPrivate; + +struct _NMDeviceAdsl { + NMDevice parent; + NMDeviceAdslPrivate _priv; +}; + +struct _NMDeviceAdslClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceAdsl, nm_device_adsl, NM_TYPE_DEVICE) + +#define NM_DEVICE_ADSL_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceAdsl, NM_IS_DEVICE_ADSL, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_adsl_get_carrier: + * @device: a #NMDeviceAdsl + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + **/ +gboolean +nm_device_adsl_get_carrier(NMDeviceAdsl *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_ADSL(device), FALSE); + + return NM_DEVICE_ADSL_GET_PRIVATE(device)->carrier; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_adsl_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_ADSL_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not an ADSL connection.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_ADSL; +} + +/*****************************************************************************/ + +static void +nm_device_adsl_init(NMDeviceAdsl *device) +{} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceAdsl *device = NM_DEVICE_ADSL(object); + + switch (prop_id) { + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_adsl_get_carrier(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_adsl = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_ADSL, + nm_device_adsl_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("Carrier", PROP_CARRIER, NMDeviceAdsl, _priv.carrier), ), ); + +static void +nm_device_adsl_class_init(NMDeviceAdslClass *adsl_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(adsl_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS(adsl_class); + + object_class->get_property = get_property; + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceAdsl:carrier: + * + * Whether the device has carrier. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_ADSL_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_adsl); +} diff --git a/src/libnm-client-impl/nm-device-bond.c b/src/libnm-client-impl/nm-device-bond.c new file mode 100644 index 00000000..48dfd9b2 --- /dev/null +++ b/src/libnm-client-impl/nm-device-bond.c @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-setting-bond.h" + +#include "nm-setting-connection.h" +#include "nm-utils.h" +#include "nm-device-bond.h" +#include "nm-object-private.h" +#include "libnm-core-intern/nm-core-internal.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CARRIER, PROP_SLAVES, ); + +typedef struct { + NMLDBusPropertyAO slaves; + char * hw_address; + bool carrier; +} NMDeviceBondPrivate; + +struct _NMDeviceBond { + NMDevice parent; + NMDeviceBondPrivate _priv; +}; + +struct _NMDeviceBondClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceBond, nm_device_bond, NM_TYPE_DEVICE) + +#define NM_DEVICE_BOND_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceBond, NM_IS_DEVICE_BOND, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_bond_get_hw_address: (skip) + * @device: a #NMDeviceBond + * + * Gets the hardware (MAC) address of the #NMDeviceBond + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_bond_get_hw_address(NMDeviceBond *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BOND(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_bond_get_carrier: + * @device: a #NMDeviceBond + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + **/ +gboolean +nm_device_bond_get_carrier(NMDeviceBond *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BOND(device), FALSE); + + return NM_DEVICE_BOND_GET_PRIVATE(device)->carrier; +} + +/** + * nm_device_bond_get_slaves: + * @device: a #NMDeviceBond + * + * Gets the devices currently enslaved to @device. + * + * Returns: (element-type NMDevice): the #GPtrArray containing + * #NMDevices that are slaves of @device. This is the internal + * copy used by the device, and must not be modified. + **/ +const GPtrArray * +nm_device_bond_get_slaves(NMDeviceBond *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BOND(device), FALSE); + + return nml_dbus_property_ao_get_objs_as_ptrarray(&NM_DEVICE_BOND_GET_PRIVATE(device)->slaves); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_bond_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_BOND_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a bond connection.")); + return FALSE; + } + + /* FIXME: check slaves? */ + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_BOND; +} + +/*****************************************************************************/ + +static void +nm_device_bond_init(NMDeviceBond *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE(object); + + g_free(priv->hw_address); + + G_OBJECT_CLASS(nm_device_bond_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceBond *device = NM_DEVICE_BOND(object); + + switch (prop_id) { + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_bond_get_carrier(device)); + break; + case PROP_SLAVES: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_device_bond_get_slaves(device))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bond = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_BOND, + nm_device_bond_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("Carrier", PROP_CARRIER, NMDeviceBond, _priv.carrier), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Slaves", + PROP_SLAVES, + NMDeviceBond, + _priv.slaves, + nm_device_get_type), ), ); + +static void +nm_device_bond_class_init(NMDeviceBondClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceBond); + + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMDeviceBondPrivate, slaves); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceBond:carrier: + * + * Whether the device has carrier. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_BOND_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceBond:slaves: (type GPtrArray(NMDevice)) + * + * The devices enslaved to the bond device. + **/ + obj_properties[PROP_SLAVES] = g_param_spec_boxed(NM_DEVICE_BOND_SLAVES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_bond); +} diff --git a/src/libnm-client-impl/nm-device-bridge.c b/src/libnm-client-impl/nm-device-bridge.c new file mode 100644 index 00000000..9bc41ac8 --- /dev/null +++ b/src/libnm-client-impl/nm-device-bridge.c @@ -0,0 +1,204 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-setting-bridge.h" + +#include "nm-setting-connection.h" +#include "nm-utils.h" +#include "nm-device-bridge.h" +#include "nm-object-private.h" +#include "libnm-core-intern/nm-core-internal.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CARRIER, PROP_SLAVES, ); + +typedef struct { + NMLDBusPropertyAO slaves; + bool carrier; +} NMDeviceBridgePrivate; + +struct _NMDeviceBridge { + NMDevice parent; + NMDeviceBridgePrivate _priv; +}; + +struct _NMDeviceBridgeClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceBridge, nm_device_bridge, NM_TYPE_DEVICE) + +#define NM_DEVICE_BRIDGE_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceBridge, NM_IS_DEVICE_BRIDGE, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_bridge_get_hw_address: (skip) + * @device: a #NMDeviceBridge + * + * Gets the hardware (MAC) address of the #NMDeviceBridge + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_bridge_get_hw_address(NMDeviceBridge *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BRIDGE(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_bridge_get_carrier: + * @device: a #NMDeviceBridge + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + **/ +gboolean +nm_device_bridge_get_carrier(NMDeviceBridge *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BRIDGE(device), FALSE); + + return NM_DEVICE_BRIDGE_GET_PRIVATE(device)->carrier; +} + +/** + * nm_device_bridge_get_slaves: + * @device: a #NMDeviceBridge + * + * Gets the devices currently enslaved to @device. + * + * Returns: (element-type NMDevice): the #GPtrArray containing + * #NMDevices that are slaves of @device. This is the internal + * copy used by the device, and must not be modified. + **/ +const GPtrArray * +nm_device_bridge_get_slaves(NMDeviceBridge *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BRIDGE(device), FALSE); + + return nml_dbus_property_ao_get_objs_as_ptrarray(&NM_DEVICE_BRIDGE_GET_PRIVATE(device)->slaves); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_bridge_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_BRIDGE_SETTING_NAME)) { + if (_nm_connection_get_setting_bluetooth_for_nap(connection) + && nm_connection_is_type(connection, NM_SETTING_BLUETOOTH_SETTING_NAME)) { + /* a bluetooth NAP setting is a compatible connection for a bridge. */ + } else { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a bridge connection.")); + return FALSE; + } + } + + /* FIXME: check ports? */ + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_BRIDGE; +} + +/*****************************************************************************/ + +static void +nm_device_bridge_init(NMDeviceBridge *device) +{} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceBridge *device = NM_DEVICE_BRIDGE(object); + + switch (prop_id) { + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_bridge_get_carrier(device)); + break; + case PROP_SLAVES: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_device_bridge_get_slaves(device))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bridge = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_BRIDGE, + nm_device_bridge_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("Carrier", PROP_CARRIER, NMDeviceBridge, _priv.carrier), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Slaves", + PROP_SLAVES, + NMDeviceBridge, + _priv.slaves, + nm_device_get_type), ), ); + +static void +nm_device_bridge_class_init(NMDeviceBridgeClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceBridge); + + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMDeviceBridgePrivate, slaves); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceBridge:carrier: + * + * Whether the device has carrier. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_BRIDGE_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceBridge:slaves: (type GPtrArray(NMDevice)) + * + * The devices enslaved to the bridge device. + **/ + obj_properties[PROP_SLAVES] = g_param_spec_boxed(NM_DEVICE_BRIDGE_SLAVES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_bridge); +} diff --git a/src/libnm-client-impl/nm-device-bt.c b/src/libnm-client-impl/nm-device-bt.c new file mode 100644 index 00000000..074ada66 --- /dev/null +++ b/src/libnm-client-impl/nm-device-bt.c @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-bt.h" + +#include <linux/if_ether.h> + +#include "nm-setting-connection.h" +#include "nm-setting-bluetooth.h" +#include "nm-utils.h" +#include "nm-object-private.h" +#include "nm-enum-types.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_NAME, PROP_BT_CAPABILITIES, ); + +typedef struct { + char * name; + guint32 bt_capabilities; +} NMDeviceBtPrivate; + +struct _NMDeviceBt { + NMDevice parent; + NMDeviceBtPrivate _priv; +}; + +struct _NMDeviceBtClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE) + +#define NM_DEVICE_BT_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceBt, NM_IS_DEVICE_BT, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_bt_get_hw_address: (skip) + * @device: a #NMDeviceBt + * + * Gets the hardware (MAC) address of the #NMDeviceBt + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_bt_get_hw_address(NMDeviceBt *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BT(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_bt_get_name: + * @device: a #NMDeviceBt + * + * Gets the name of the #NMDeviceBt. + * + * Returns: the name of the device + **/ +const char * +nm_device_bt_get_name(NMDeviceBt *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BT(device), NULL); + + return NM_DEVICE_BT_GET_PRIVATE(device)->name; +} + +/** + * nm_device_bt_get_capabilities: + * @device: a #NMDeviceBt + * + * Returns the Bluetooth device's usable capabilities. + * + * Returns: a combination of #NMBluetoothCapabilities + **/ +NMBluetoothCapabilities +nm_device_bt_get_capabilities(NMDeviceBt *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BT(device), NM_BT_CAPABILITY_NONE); + + return NM_DEVICE_BT_GET_PRIVATE(device)->bt_capabilities; +} + +static NMBluetoothCapabilities +get_connection_bt_type(NMConnection *connection) +{ + NMSettingBluetooth *s_bt; + const char * bt_type; + + s_bt = nm_connection_get_setting_bluetooth(connection); + if (!s_bt) + return NM_BT_CAPABILITY_NONE; + + bt_type = nm_setting_bluetooth_get_connection_type(s_bt); + g_assert(bt_type); + + if (!strcmp(bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)) + return NM_BT_CAPABILITY_DUN; + else if (!strcmp(bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) + return NM_BT_CAPABILITY_NAP; + + return NM_BT_CAPABILITY_NONE; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingBluetooth * s_bt; + const char * hw_addr, *setting_addr; + NMBluetoothCapabilities dev_caps; + NMBluetoothCapabilities bt_type; + + if (!NM_DEVICE_CLASS(nm_device_bt_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_BLUETOOTH_SETTING_NAME) + || !(s_bt = nm_connection_get_setting_bluetooth(connection))) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a Bluetooth connection.")); + return FALSE; + } + + if (nm_streq0(nm_setting_bluetooth_get_connection_type(s_bt), NM_SETTING_BLUETOOTH_TYPE_NAP)) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection is of Bluetooth NAP type.")); + return FALSE; + } + + /* Check BT address */ + hw_addr = nm_device_get_hw_address(device); + if (hw_addr) { + if (!nm_utils_hwaddr_valid(hw_addr, ETH_ALEN)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + _("Invalid device Bluetooth address.")); + return FALSE; + } + setting_addr = nm_setting_bluetooth_get_bdaddr(s_bt); + if (setting_addr && !nm_utils_hwaddr_matches(setting_addr, -1, hw_addr, -1)) { + g_set_error_literal( + error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The Bluetooth addresses of the device and the connection didn't match.")); + return FALSE; + } + } + + dev_caps = nm_device_bt_get_capabilities(NM_DEVICE_BT(device)); + bt_type = get_connection_bt_type(connection); + if (!(bt_type & dev_caps)) { + g_set_error_literal( + error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The device is lacking Bluetooth capabilities required by the connection.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_BLUETOOTH; +} + +/*****************************************************************************/ + +static void +nm_device_bt_init(NMDeviceBt *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE(object); + + g_free(priv->name); + + G_OBJECT_CLASS(nm_device_bt_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceBt *device = NM_DEVICE_BT(object); + + switch (prop_id) { + case PROP_NAME: + g_value_set_string(value, nm_device_bt_get_name(device)); + break; + case PROP_BT_CAPABILITIES: + g_value_set_flags(value, nm_device_bt_get_capabilities(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bluetooth = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_BLUETOOTH, + nm_device_bt_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_U("BtCapabilities", + PROP_BT_CAPABILITIES, + NMDeviceBt, + _priv.bt_capabilities), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_S("Name", PROP_NAME, NMDeviceBt, _priv.name), ), ); + +static void +nm_device_bt_class_init(NMDeviceBtClass *bt_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(bt_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS(bt_class); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceBt:name: + * + * The name of the bluetooth device. + **/ + obj_properties[PROP_NAME] = g_param_spec_string(NM_DEVICE_BT_NAME, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceBt:bt-capabilities: + * + * The device's bluetooth capabilities, a combination of #NMBluetoothCapabilities. + **/ + obj_properties[PROP_BT_CAPABILITIES] = + g_param_spec_flags(NM_DEVICE_BT_CAPABILITIES, + "", + "", + NM_TYPE_BLUETOOTH_CAPABILITIES, + NM_BT_CAPABILITY_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_bluetooth); +} diff --git a/src/libnm-client-impl/nm-device-dummy.c b/src/libnm-client-impl/nm-device-dummy.c new file mode 100644 index 00000000..d56f6cda --- /dev/null +++ b/src/libnm-client-impl/nm-device-dummy.c @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-dummy.h" + +#include "nm-object-private.h" +#include "nm-setting-dummy.h" +#include "nm-setting-connection.h" + +/*****************************************************************************/ + +struct _NMDeviceDummy { + NMDevice parent; +}; + +struct _NMDeviceDummyClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceDummy, nm_device_dummy, NM_TYPE_DEVICE) + +#define NM_DEVICE_DUMMY_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceDummy, NM_IS_DEVICE_DUMMY, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_dummy_get_hw_address: (skip) + * @device: a #NMDeviceDummy + * + * Gets the hardware (MAC) address of the #NMDeviceDummy + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.10 + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_dummy_get_hw_address(NMDeviceDummy *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_DUMMY(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + const char *iface_name; + + if (!NM_DEVICE_CLASS(nm_device_dummy_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_DUMMY_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a dummy connection.")); + return FALSE; + } + + iface_name = nm_connection_get_interface_name(connection); + if (!iface_name) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection did not specify an interface name.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_DUMMY; +} + +/*****************************************************************************/ + +static void +nm_device_dummy_init(NMDeviceDummy *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_dummy = NML_DBUS_META_IFACE_INIT( + NM_DBUS_INTERFACE_DEVICE_DUMMY, + nm_device_dummy_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), ), ); + +static void +nm_device_dummy_class_init(NMDeviceDummyClass *dummy_class) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS(dummy_class); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; +} diff --git a/src/libnm-client-impl/nm-device-ethernet.c b/src/libnm-client-impl/nm-device-ethernet.c new file mode 100644 index 00000000..b32decde --- /dev/null +++ b/src/libnm-client-impl/nm-device-ethernet.c @@ -0,0 +1,404 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-ethernet.h" + +#include <linux/if_ether.h> + +#include "nm-libnm-utils.h" +#include "nm-setting-connection.h" +#include "nm-setting-wired.h" +#include "nm-setting-pppoe.h" +#include "nm-setting-veth.h" +#include "nm-utils.h" +#include "nm-object-private.h" +#include "nm-device-veth.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PERM_HW_ADDRESS, + PROP_SPEED, + PROP_CARRIER, + PROP_S390_SUBCHANNELS, ); + +typedef struct _NMDeviceEthernetPrivate { + char ** s390_subchannels; + char * perm_hw_address; + guint32 speed; + bool carrier; +} NMDeviceEthernetPrivate; + +G_DEFINE_TYPE(NMDeviceEthernet, nm_device_ethernet, NM_TYPE_DEVICE) + +#define NM_DEVICE_ETHERNET_GET_PRIVATE(self) \ + _NM_GET_PRIVATE_PTR(self, NMDeviceEthernet, NM_IS_DEVICE_ETHERNET, NMObject) + +/*****************************************************************************/ + +/** + * nm_device_ethernet_get_hw_address: (skip) + * @device: a #NMDeviceEthernet + * + * Gets the active hardware (MAC) address of the #NMDeviceEthernet + * + * Returns: the active hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_ethernet_get_hw_address(NMDeviceEthernet *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_ETHERNET(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_ethernet_get_permanent_hw_address: + * @device: a #NMDeviceEthernet + * + * Gets the permanent hardware (MAC) address of the #NMDeviceEthernet + * + * Returns: the permanent hardware address. This is the internal string used by the + * device, and must not be modified. + **/ +const char * +nm_device_ethernet_get_permanent_hw_address(NMDeviceEthernet *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_ETHERNET(device), NULL); + + return _nml_coerce_property_str_not_empty( + NM_DEVICE_ETHERNET_GET_PRIVATE(device)->perm_hw_address); +} + +/** + * nm_device_ethernet_get_speed: + * @device: a #NMDeviceEthernet + * + * Gets the speed of the #NMDeviceEthernet in Mbit/s. + * + * Returns: the speed of the device in Mbit/s + **/ +guint32 +nm_device_ethernet_get_speed(NMDeviceEthernet *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_ETHERNET(device), 0); + + return NM_DEVICE_ETHERNET_GET_PRIVATE(device)->speed; +} + +/** + * nm_device_ethernet_get_carrier: + * @device: a #NMDeviceEthernet + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + **/ +gboolean +nm_device_ethernet_get_carrier(NMDeviceEthernet *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_ETHERNET(device), FALSE); + + return NM_DEVICE_ETHERNET_GET_PRIVATE(device)->carrier; +} + +/** + * nm_device_ethernet_get_s390_subchannels: + * @device: a #NMDeviceEthernet + * + * Return the list of s390 subchannels if the device supports them. + * + * Returns: (transfer none) (element-type utf8): array of strings, each specifying + * one subchannel the s390 device uses to communicate to the host. + * + * Since: 1.2 + **/ +const char *const * +nm_device_ethernet_get_s390_subchannels(NMDeviceEthernet *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_ETHERNET(device), NULL); + + return (const char *const *) NM_DEVICE_ETHERNET_GET_PRIVATE(device)->s390_subchannels; +} + +static gboolean +match_subchans(NMDeviceEthernet *self, NMSettingWired *s_wired, gboolean *try_mac) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE(self); + const char *const * subchans; + gsize num1, num2; + gsize i, j; + + *try_mac = TRUE; + + subchans = nm_setting_wired_get_s390_subchannels(s_wired); + num1 = NM_PTRARRAY_LEN(subchans); + num2 = NM_PTRARRAY_LEN(priv->s390_subchannels); + /* connection has no subchannels */ + if (num1 == 0) + return TRUE; + /* connection requires subchannels but the device has none */ + if (num2 == 0) + return FALSE; + /* number of subchannels differ */ + if (num1 != num2) + return FALSE; + + /* Make sure each subchannel in the connection is a subchannel of this device */ + for (i = 0; subchans[i]; i++) { + const char *candidate = subchans[i]; + gboolean found = FALSE; + + for (j = 0; priv->s390_subchannels[j]; j++) { + if (!g_strcmp0(priv->s390_subchannels[j], candidate)) + found = TRUE; + } + if (!found) + return FALSE; /* a subchannel was not found */ + } + + *try_mac = FALSE; + return TRUE; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingWired *s_wired; + + if (!NM_DEVICE_CLASS(nm_device_ethernet_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (nm_connection_is_type(connection, NM_SETTING_PPPOE_SETTING_NAME) + || nm_connection_is_type(connection, NM_SETTING_WIRED_SETTING_NAME) + || (nm_connection_is_type(connection, NM_SETTING_VETH_SETTING_NAME) + && NM_IS_DEVICE_VETH(device))) { + /* NOP */ + } else { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not an Ethernet or PPPoE connection.")); + return FALSE; + } + + s_wired = nm_connection_get_setting_wired(connection); + /* Wired setting optional for PPPoE */ + if (s_wired) { + const char * perm_addr, *s_mac; + gboolean try_mac = TRUE; + const char *const *mac_blacklist; + int i; + + /* Check s390 subchannels */ + if (!match_subchans(NM_DEVICE_ETHERNET(device), s_wired, &try_mac)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection and device differ in S390 subchannels.")); + return FALSE; + } + + /* Check MAC address */ + perm_addr = nm_device_ethernet_get_permanent_hw_address(NM_DEVICE_ETHERNET(device)); + s_mac = nm_setting_wired_get_mac_address(s_wired); + if (perm_addr) { + /* Virtual devices will have empty permanent addr but they should not be excluded + * from the MAC address check specified in the connection */ + if (*perm_addr == 0) + perm_addr = nm_device_get_hw_address(NM_DEVICE(device)); + + if (!nm_utils_hwaddr_valid(perm_addr, ETH_ALEN)) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + _("Invalid device MAC address %s."), + perm_addr); + return FALSE; + } + if (try_mac && s_mac && !nm_utils_hwaddr_matches(s_mac, -1, perm_addr, -1)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The MACs of the device and the connection do not match.")); + return FALSE; + } + + /* Check for MAC address blacklist */ + mac_blacklist = nm_setting_wired_get_mac_address_blacklist(s_wired); + for (i = 0; mac_blacklist[i]; i++) { + if (!nm_utils_hwaddr_valid(mac_blacklist[i], ETH_ALEN)) { + g_warn_if_reached(); + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("Invalid MAC in the blacklist: %s."), + mac_blacklist[i]); + return FALSE; + } + + if (nm_utils_hwaddr_matches(mac_blacklist[i], -1, perm_addr, -1)) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("Device MAC (%s) is blacklisted by the connection."), + perm_addr); + return FALSE; + } + } + } + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_WIRED; +} + +/*****************************************************************************/ + +static void +nm_device_ethernet_init(NMDeviceEthernet *device) +{ + NMDeviceEthernetPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(device, NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetPrivate); + + device->_priv = priv; +} + +static void +finalize(GObject *object) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE(object); + + g_free(priv->perm_hw_address); + g_strfreev(priv->s390_subchannels); + + G_OBJECT_CLASS(nm_device_ethernet_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceEthernet * device = NM_DEVICE_ETHERNET(object); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE(device); + + switch (prop_id) { + case PROP_PERM_HW_ADDRESS: + g_value_set_string(value, nm_device_ethernet_get_permanent_hw_address(device)); + break; + case PROP_SPEED: + g_value_set_uint(value, nm_device_ethernet_get_speed(device)); + break; + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_ethernet_get_carrier(device)); + break; + case PROP_S390_SUBCHANNELS: + g_value_set_boxed(value, priv->s390_subchannels); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wired = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_WIRED, + nm_device_ethernet_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_20, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("Carrier", PROP_CARRIER, NMDeviceEthernetPrivate, carrier), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_S("PermHwAddress", + PROP_PERM_HW_ADDRESS, + NMDeviceEthernetPrivate, + perm_hw_address), + NML_DBUS_META_PROPERTY_INIT_AS("S390Subchannels", + PROP_S390_SUBCHANNELS, + NMDeviceEthernetPrivate, + s390_subchannels), + NML_DBUS_META_PROPERTY_INIT_U("Speed", PROP_SPEED, NMDeviceEthernetPrivate, speed), ), + .base_struct_offset = G_STRUCT_OFFSET(NMDeviceEthernet, _priv), ); + +static void +nm_device_ethernet_class_init(NMDeviceEthernetClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + g_type_class_add_private(klass, sizeof(NMDeviceEthernetPrivate)); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceEthernet:perm-hw-address: + * + * The permanent hardware (MAC) address of the device. + **/ + obj_properties[PROP_PERM_HW_ADDRESS] = + g_param_spec_string(NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceEthernet:speed: + * + * The speed of the device. + **/ + obj_properties[PROP_SPEED] = g_param_spec_uint(NM_DEVICE_ETHERNET_SPEED, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceEthernet:carrier: + * + * Whether the device has carrier. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_ETHERNET_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceEthernet:s390-subchannels: + * + * Identifies subchannels of this network device used for + * communication with z/VM or s390 host. + * + * Since: 1.2 + **/ + obj_properties[PROP_S390_SUBCHANNELS] = + g_param_spec_boxed(NM_DEVICE_ETHERNET_S390_SUBCHANNELS, + "", + "", + G_TYPE_STRV, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_wired); +} diff --git a/src/libnm-client-impl/nm-device-generic.c b/src/libnm-client-impl/nm-device-generic.c new file mode 100644 index 00000000..9dcc8c91 --- /dev/null +++ b/src/libnm-client-impl/nm-device-generic.c @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-generic.h" + +#include "nm-object-private.h" +#include "nm-setting-generic.h" +#include "nm-setting-connection.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_TYPE_DESCRIPTION, ); + +typedef struct { + char *type_description; +} NMDeviceGenericPrivate; + +struct _NMDeviceGeneric { + NMDevice parent; + NMDeviceGenericPrivate _priv; +}; + +struct _NMDeviceGenericClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceGeneric, nm_device_generic, NM_TYPE_DEVICE) + +#define NM_DEVICE_GENERIC_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceGeneric, NM_IS_DEVICE_GENERIC, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_generic_get_hw_address: (skip) + * @device: a #NMDeviceGeneric + * + * Gets the hardware address of the #NMDeviceGeneric + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_generic_get_hw_address(NMDeviceGeneric *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_GENERIC(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/*****************************************************************************/ + +static const char * +get_type_description(NMDevice *device) +{ + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE(device); + + return _nml_coerce_property_str_not_empty(priv->type_description); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + const char *iface_name; + + if (!NM_DEVICE_CLASS(nm_device_generic_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_GENERIC_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a generic connection.")); + return FALSE; + } + + iface_name = nm_connection_get_interface_name(connection); + if (!iface_name) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection did not specify an interface name.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_GENERIC; +} + +/*****************************************************************************/ + +static void +nm_device_generic_init(NMDeviceGeneric *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE(object); + + g_free(priv->type_description); + + G_OBJECT_CLASS(nm_device_generic_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceGeneric *self = NM_DEVICE_GENERIC(object); + + switch (prop_id) { + case PROP_TYPE_DESCRIPTION: + g_value_set_string(value, get_type_description((NMDevice *) self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_generic = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_GENERIC, + nm_device_generic_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_S("TypeDescription", + PROP_TYPE_DESCRIPTION, + NMDeviceGeneric, + _priv.type_description), ), ); + +static void +nm_device_generic_class_init(NMDeviceGenericClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + device_class->get_type_description = get_type_description; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceGeneric:type-description: + * + * A description of the specific type of device this is, or %NULL + * if not known. + **/ + obj_properties[PROP_TYPE_DESCRIPTION] = + g_param_spec_string(NM_DEVICE_GENERIC_TYPE_DESCRIPTION, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_generic); +} diff --git a/src/libnm-client-impl/nm-device-infiniband.c b/src/libnm-client-impl/nm-device-infiniband.c new file mode 100644 index 00000000..7e4cc054 --- /dev/null +++ b/src/libnm-client-impl/nm-device-infiniband.c @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2011 - 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-infiniband.h" + +#include <linux/if_infiniband.h> + +#include "nm-setting-connection.h" +#include "nm-setting-infiniband.h" +#include "nm-utils.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CARRIER, ); + +typedef struct { + bool carrier; +} NMDeviceInfinibandPrivate; + +struct _NMDeviceInfiniband { + NMDevice parent; + NMDeviceInfinibandPrivate _priv; +}; + +struct _NMDeviceInfinibandClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceInfiniband, nm_device_infiniband, NM_TYPE_DEVICE) + +#define NM_DEVICE_INFINIBAND_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceInfiniband, NM_IS_DEVICE_INFINIBAND, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_infiniband_get_hw_address: (skip) + * @device: a #NMDeviceInfiniband + * + * Gets the hardware (MAC) address of the #NMDeviceInfiniband + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_infiniband_get_hw_address(NMDeviceInfiniband *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_INFINIBAND(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_infiniband_get_carrier: + * @device: a #NMDeviceInfiniband + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + **/ +gboolean +nm_device_infiniband_get_carrier(NMDeviceInfiniband *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_INFINIBAND(device), FALSE); + + return NM_DEVICE_INFINIBAND_GET_PRIVATE(device)->carrier; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingInfiniband *s_infiniband; + const char * hwaddr, *setting_hwaddr; + + if (!NM_DEVICE_CLASS(nm_device_infiniband_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_INFINIBAND_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not an InfiniBand connection.")); + return FALSE; + } + + hwaddr = nm_device_get_hw_address(NM_DEVICE(device)); + if (hwaddr) { + if (!nm_utils_hwaddr_valid(hwaddr, INFINIBAND_ALEN)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + _("Invalid device MAC address.")); + return FALSE; + } + + s_infiniband = nm_connection_get_setting_infiniband(connection); + setting_hwaddr = nm_setting_infiniband_get_mac_address(s_infiniband); + if (setting_hwaddr && !nm_utils_hwaddr_matches(setting_hwaddr, -1, hwaddr, -1)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The MACs of the device and the connection didn't match.")); + return FALSE; + } + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_INFINIBAND; +} + +/*****************************************************************************/ + +static void +nm_device_infiniband_init(NMDeviceInfiniband *device) +{} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceInfiniband *device = NM_DEVICE_INFINIBAND(object); + + switch (prop_id) { + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_infiniband_get_carrier(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_infiniband = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_INFINIBAND, + nm_device_infiniband_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("Carrier", PROP_CARRIER, NMDeviceInfiniband, _priv.carrier), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), ), ); + +static void +nm_device_infiniband_class_init(NMDeviceInfinibandClass *ib_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(ib_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS(ib_class); + + object_class->get_property = get_property; + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceInfiniband:carrier: + * + * Whether the device has carrier. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_INFINIBAND_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_infiniband); +} diff --git a/src/libnm-client-impl/nm-device-ip-tunnel.c b/src/libnm-client-impl/nm-device-ip-tunnel.c new file mode 100644 index 00000000..14c72282 --- /dev/null +++ b/src/libnm-client-impl/nm-device-ip-tunnel.c @@ -0,0 +1,578 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-ip-tunnel.h" + +#include "nm-setting-connection.h" +#include "nm-setting-ip-tunnel.h" +#include "nm-utils.h" +#include "nm-object-private.h" +#include "libnm-core-intern/nm-core-internal.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_MODE, + PROP_PARENT, + PROP_LOCAL, + PROP_REMOTE, + PROP_TTL, + PROP_TOS, + PROP_PATH_MTU_DISCOVERY, + PROP_INPUT_KEY, + PROP_OUTPUT_KEY, + PROP_ENCAPSULATION_LIMIT, + PROP_FLOW_LABEL, + PROP_FLAGS, ); + +typedef struct { + NMLDBusPropertyO parent; + char * local; + char * remote; + char * input_key; + char * output_key; + guint32 mode; + guint32 flow_label; + guint32 flags; + guint8 ttl; + guint8 tos; + guint8 encapsulation_limit; + bool path_mtu_discovery; +} NMDeviceIPTunnelPrivate; + +struct _NMDeviceIPTunnel { + NMDevice parent; + NMDeviceIPTunnelPrivate _priv; +}; + +struct _NMDeviceIPTunnelClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceIPTunnel, nm_device_ip_tunnel, NM_TYPE_DEVICE) + +#define NM_DEVICE_IP_TUNNEL_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceIPTunnel, NM_IS_DEVICE_IP_TUNNEL, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_ip_tunnel_get_mode: + * @device: a #NMDeviceIPTunnel + * + * Returns: the tunneling mode + * + * Since: 1.2 + **/ +NMIPTunnelMode +nm_device_ip_tunnel_get_mode(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), 0); + + return NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->mode; +} + +/** + * nm_device_ip_tunnel_get_parent: + * @device: a #NMDeviceIPTunnel + * + * Returns: (transfer none): the device's parent device + * + * Since: 1.2 + **/ +NMDevice * +nm_device_ip_tunnel_get_parent(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), NULL); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->parent); +} + +/** + * nm_device_ip_tunnel_get_local: + * @device: a #NMDeviceIPTunnel + * + * Returns: the local endpoint of the tunnel + * + * Since: 1.2 + **/ +const char * +nm_device_ip_tunnel_get_local(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->local); +} + +/** + * nm_device_ip_tunnel_get_remote: + * @device: a #NMDeviceIPTunnel + * + * Returns: the remote endpoint of the tunnel + * + * Since: 1.2 + **/ +const char * +nm_device_ip_tunnel_get_remote(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->remote); +} + +/** + * nm_device_ip_tunnel_get_ttl: + * @device: a #NMDeviceIPTunnel + * + * Returns: the TTL assigned to tunneled packets + * + * Since: 1.2 + **/ +guint8 +nm_device_ip_tunnel_get_ttl(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), 0); + + return NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->ttl; +} + +/** + * nm_device_ip_tunnel_get_tos: + * @device: a #NMDeviceIPTunnel + * + * Returns: type of service (IPv4) or traffic class (IPv6) assigned + * to tunneled packets. + * + * Since: 1.2 + **/ +guint8 +nm_device_ip_tunnel_get_tos(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), 0); + + return NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->tos; +} + +/** + * nm_device_ip_tunnel_get_path_mtu_discovery: + * @device: a #NMDeviceIPTunnel + * + * Returns: whether path MTU discovery is enabled + * + * Since: 1.2 + **/ +gboolean +nm_device_ip_tunnel_get_path_mtu_discovery(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), TRUE); + + return NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->path_mtu_discovery; +} + +/** + * nm_device_ip_tunnel_get_input_key: + * @device: a #NMDeviceIPTunnel + * + * Returns: the key used for incoming packets + * + * Since: 1.2 + **/ +const char * +nm_device_ip_tunnel_get_input_key(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->input_key); +} + +/** + * nm_device_ip_tunnel_get_output_key: + * @device: a #NMDeviceIPTunnel + * + * Returns: the key used for outgoing packets + * + * Since: 1.2 + **/ +const char * +nm_device_ip_tunnel_get_output_key(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->output_key); +} + +/** + * nm_device_ip_tunnel_get_encapsulation_limit: + * @device: a #NMDeviceIPTunnel + * + * Returns: the maximum permitted encapsulation level + * + * Since: 1.2 + **/ +guint8 +nm_device_ip_tunnel_get_encapsulation_limit(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), 0); + + return NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->encapsulation_limit; +} + +/** + * nm_device_ip_tunnel_get_flow_label: + * @device: a #NMDeviceIPTunnel + * + * Returns: the flow label assigned to tunnel packets + * + * Since: 1.2 + **/ +guint +nm_device_ip_tunnel_get_flow_label(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), 0); + + return NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->flow_label; +} + +/** + * nm_device_ip_tunnel_get_flags: + * @device: a #NMDeviceIPTunnel + * + * Returns: the tunnel flags + * + * Since: 1.12 + **/ +NMIPTunnelFlags +nm_device_ip_tunnel_get_flags(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), NM_IP_TUNNEL_FLAG_NONE); + + return NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->flags; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_ip_tunnel_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_IP_TUNNEL_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not an IP tunnel connection.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_IP_TUNNEL; +} + +/*****************************************************************************/ + +static void +nm_device_ip_tunnel_init(NMDeviceIPTunnel *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE(object); + + g_free(priv->local); + g_free(priv->remote); + g_free(priv->input_key); + g_free(priv->output_key); + + G_OBJECT_CLASS(nm_device_ip_tunnel_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceIPTunnel *device = NM_DEVICE_IP_TUNNEL(object); + + switch (prop_id) { + case PROP_PARENT: + g_value_set_object(value, nm_device_ip_tunnel_get_parent(device)); + break; + case PROP_MODE: + g_value_set_uint(value, nm_device_ip_tunnel_get_mode(device)); + break; + case PROP_LOCAL: + g_value_set_string(value, nm_device_ip_tunnel_get_local(device)); + break; + case PROP_REMOTE: + g_value_set_string(value, nm_device_ip_tunnel_get_remote(device)); + break; + case PROP_TTL: + g_value_set_uint(value, nm_device_ip_tunnel_get_ttl(device)); + break; + case PROP_TOS: + g_value_set_uint(value, nm_device_ip_tunnel_get_tos(device)); + break; + case PROP_PATH_MTU_DISCOVERY: + g_value_set_boolean(value, nm_device_ip_tunnel_get_path_mtu_discovery(device)); + break; + case PROP_INPUT_KEY: + g_value_set_string(value, nm_device_ip_tunnel_get_input_key(device)); + break; + case PROP_OUTPUT_KEY: + g_value_set_string(value, nm_device_ip_tunnel_get_output_key(device)); + break; + case PROP_ENCAPSULATION_LIMIT: + g_value_set_uint(value, nm_device_ip_tunnel_get_encapsulation_limit(device)); + break; + case PROP_FLOW_LABEL: + g_value_set_uint(value, nm_device_ip_tunnel_get_flow_label(device)); + break; + case PROP_FLAGS: + g_value_set_uint(value, nm_device_ip_tunnel_get_flags(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_iptunnel = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL, + nm_device_ip_tunnel_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_Y("EncapsulationLimit", + PROP_ENCAPSULATION_LIMIT, + NMDeviceIPTunnel, + _priv.encapsulation_limit), + NML_DBUS_META_PROPERTY_INIT_U("Flags", PROP_FLAGS, NMDeviceIPTunnel, _priv.flags), + NML_DBUS_META_PROPERTY_INIT_U("FlowLabel", + PROP_FLOW_LABEL, + NMDeviceIPTunnel, + _priv.flow_label), + NML_DBUS_META_PROPERTY_INIT_S("InputKey", + PROP_INPUT_KEY, + NMDeviceIPTunnel, + _priv.input_key), + NML_DBUS_META_PROPERTY_INIT_S("Local", PROP_LOCAL, NMDeviceIPTunnel, _priv.local), + NML_DBUS_META_PROPERTY_INIT_U("Mode", PROP_MODE, NMDeviceIPTunnel, _priv.mode), + NML_DBUS_META_PROPERTY_INIT_S("OutputKey", + PROP_OUTPUT_KEY, + NMDeviceIPTunnel, + _priv.output_key), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Parent", + PROP_PARENT, + NMDeviceIPTunnel, + _priv.parent, + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_B("PathMtuDiscovery", + PROP_PATH_MTU_DISCOVERY, + NMDeviceIPTunnel, + _priv.path_mtu_discovery), + NML_DBUS_META_PROPERTY_INIT_S("Remote", PROP_REMOTE, NMDeviceIPTunnel, _priv.remote), + NML_DBUS_META_PROPERTY_INIT_Y("Tos", PROP_TOS, NMDeviceIPTunnel, _priv.tos), + NML_DBUS_META_PROPERTY_INIT_Y("Ttl", PROP_TTL, NMDeviceIPTunnel, _priv.ttl), ), ); + +static void +nm_device_ip_tunnel_class_init(NMDeviceIPTunnelClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceIPTunnel); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceIPTunnelPrivate, parent); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceIPTunnel:mode: + * + * The tunneling mode of the device. + * + * Since: 1.2 + **/ + obj_properties[PROP_MODE] = g_param_spec_uint(NM_DEVICE_IP_TUNNEL_MODE, + "", + "", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:parent: + * + * The devices's parent device. + * + * Since: 1.2 + **/ + obj_properties[PROP_PARENT] = g_param_spec_object(NM_DEVICE_IP_TUNNEL_PARENT, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:local: + * + * The local endpoint of the tunnel. + * + * Since: 1.2 + **/ + obj_properties[PROP_LOCAL] = g_param_spec_string(NM_DEVICE_IP_TUNNEL_LOCAL, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:remote: + * + * The remote endpoint of the tunnel. + * + * Since: 1.2 + **/ + obj_properties[PROP_REMOTE] = g_param_spec_string(NM_DEVICE_IP_TUNNEL_REMOTE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:ttl: + * + * The TTL assigned to tunneled packets. 0 is a special value + * meaning that packets inherit the TTL value + * + * Since: 1.2 + **/ + obj_properties[PROP_TTL] = g_param_spec_uchar(NM_DEVICE_IP_TUNNEL_TTL, + "", + "", + 0, + 255, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:tos: + * + * The type of service (IPv4) or traffic class (IPv6) assigned to + * tunneled packets. + * + * Since: 1.2 + **/ + obj_properties[PROP_TOS] = g_param_spec_uchar(NM_DEVICE_IP_TUNNEL_TOS, + "", + "", + 0, + 255, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:path-mtu-discovery: + * + * Whether path MTU discovery is enabled on this tunnel. + * + * Since: 1.2 + **/ + obj_properties[PROP_PATH_MTU_DISCOVERY] = + g_param_spec_boolean(NM_DEVICE_IP_TUNNEL_PATH_MTU_DISCOVERY, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:input-key: + * + * The key used for tunneled input packets, if applicable. + * + * Since: 1.2 + **/ + obj_properties[PROP_INPUT_KEY] = g_param_spec_string(NM_DEVICE_IP_TUNNEL_INPUT_KEY, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:output-key: + * + * The key used for tunneled output packets, if applicable. + * + * Since: 1.2 + **/ + obj_properties[PROP_OUTPUT_KEY] = + g_param_spec_string(NM_DEVICE_IP_TUNNEL_OUTPUT_KEY, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:encapsulation-limit: + * + * How many additional levels of encapsulation are permitted to + * be prepended to packets. This property applies only to IPv6 + * tunnels. + * + * Since: 1.2 + **/ + obj_properties[PROP_ENCAPSULATION_LIMIT] = + g_param_spec_uchar(NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT, + "", + "", + 0, + 255, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:flow-label: + * + * The flow label to assign to tunnel packets. This property + * applies only to IPv6 tunnels. + * + * Since: 1.2 + **/ + obj_properties[PROP_FLOW_LABEL] = g_param_spec_uint(NM_DEVICE_IP_TUNNEL_FLOW_LABEL, + "", + "", + 0, + (1 << 20) - 1, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:flags: + * + * Tunnel flags. + * + * Since: 1.12 + **/ + obj_properties[PROP_FLAGS] = g_param_spec_uint(NM_DEVICE_IP_TUNNEL_FLAGS, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_iptunnel); +} diff --git a/src/libnm-client-impl/nm-device-macsec.c b/src/libnm-client-impl/nm-device-macsec.c new file mode 100644 index 00000000..faa5be7e --- /dev/null +++ b/src/libnm-client-impl/nm-device-macsec.c @@ -0,0 +1,631 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-macsec.h" + +#include "nm-device-private.h" +#include "nm-object-private.h" +#include "nm-utils.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PARENT, + PROP_SCI, + PROP_CIPHER_SUITE, + PROP_ICV_LENGTH, + PROP_WINDOW, + PROP_ENCODING_SA, + PROP_ENCRYPT, + PROP_PROTECT, + PROP_INCLUDE_SCI, + PROP_ES, + PROP_SCB, + PROP_REPLAY_PROTECT, + PROP_VALIDATION, ); + +typedef struct { + NMLDBusPropertyO parent; + char * validation; + guint64 sci; + guint64 cipher_suite; + guint32 window; + guint8 icv_length; + guint8 encoding_sa; + bool encrypt; + bool protect; + bool include_sci; + bool es; + bool scb; + bool replay_protect; +} NMDeviceMacsecPrivate; + +struct _NMDeviceMacsec { + NMDevice parent; + NMDeviceMacsecPrivate _priv; +}; + +struct _NMDeviceMacsecClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceMacsec, nm_device_macsec, NM_TYPE_DEVICE) + +#define NM_DEVICE_MACSEC_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceMacsec, NM_IS_DEVICE_MACSEC, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_macsec_get_parent: + * @device: a #NMDeviceMacsec + * + * Returns: (transfer none): the device's parent device + * + * Since: 1.6 + **/ +NMDevice * +nm_device_macsec_get_parent(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), NULL); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_MACSEC_GET_PRIVATE(device)->parent); +} + +/** + * nm_device_macsec_get_hw_address: (skip) + * @device: a #NMDeviceMacsec + * + * Gets the hardware (MAC) address of the #NMDeviceMacsec + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.6 + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_macsec_get_hw_address(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_macsec_get_sci: + * @device: a #NMDeviceMacsec + * + * Gets the Secure Channel Identifier in use + * + * Returns: the SCI + * + * Since: 1.6 + **/ +guint64 +nm_device_macsec_get_sci(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), 0); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->sci; +} + +/** + * nm_device_macsec_get_icv_length: + * @device: a #NMDeviceMacsec + * + * Gets the length of ICV (Integrity Check Value) + * + * Returns: the length of ICV + * + * Since: 1.6 + **/ +guint8 +nm_device_macsec_get_icv_length(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), 0); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->icv_length; +} + +/** + * nm_device_macsec_get_cipher_suite: + * @device: a #NMDeviceMacsec + * + * Gets the set of cryptographic algorithms in use + * + * Returns: the set of cryptographic algorithms in use + * + * Since: 1.6 + **/ +guint64 +nm_device_macsec_get_cipher_suite(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), 0); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->cipher_suite; +} + +/** + * nm_device_macsec_get_window: + * @device: a #NMDeviceMacsec + * + * Gets the size of the replay window + * + * Returns: size of the replay window + * + * Since: 1.6 + **/ +guint +nm_device_macsec_get_window(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), 0); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->window; +} + +/** + * nm_device_macsec_get_encoding_sa: + * @device: a #NMDeviceMacsec + * + * Gets the value of the Association Number (0..3) for the Security + * Association in use. + * + * Returns: the current Security Association + * + * Since: 1.6 + **/ +guint8 +nm_device_macsec_get_encoding_sa(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), 0); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->encoding_sa; +} + +/** + * nm_device_macsec_get_validation: + * @device: a #NMDeviceMacsec + * + * Gets the validation mode for incoming packets (strict, check, + * disabled) + * + * Returns: the validation mode + * + * Since: 1.6 + **/ +const char * +nm_device_macsec_get_validation(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), NULL); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->validation; +} + +/** + * nm_device_macsec_get_encrypt: + * @device: a #NMDeviceMacsec + * + * Gets whether encryption of transmitted frames is enabled + * + * Returns: whether encryption is enabled + * + * Since: 1.6 + **/ +gboolean +nm_device_macsec_get_encrypt(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), FALSE); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->encrypt; +} + +/** + * nm_device_macsec_get_protect: + * @device: a #NMDeviceMacsec + * + * Gets whether protection of transmitted frames is enabled + * + * Returns: whether protection is enabled + * + * Since: 1.6 + **/ +gboolean +nm_device_macsec_get_protect(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), FALSE); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->protect; +} + +/** + * nm_device_macsec_get_include_sci: + * @device: a #NMDeviceMacsec + * + * Gets whether the SCI is always included in SecTAG for transmitted + * frames + * + * Returns: whether the SCI is always included + * + * Since: 1.6 + **/ +gboolean +nm_device_macsec_get_include_sci(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), FALSE); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->include_sci; +} + +/** + * nm_device_macsec_get_es: + * @device: a #NMDeviceMacsec + * + * Gets whether the ES (End station) bit is enabled in SecTAG for + * transmitted frames + * + * Returns: whether the ES (End station) bit is enabled + * + * Since: 1.6 + **/ +gboolean +nm_device_macsec_get_es(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), FALSE); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->es; +} + +/** + * nm_device_macsec_get_scb: + * @device: a #NMDeviceMacsec + * + * Gets whether the SCB (Single Copy Broadcast) bit is enabled in + * SecTAG for transmitted frames + * + * Returns: whether the SCB (Single Copy Broadcast) bit is enabled + * + * Since: 1.6 + **/ +gboolean +nm_device_macsec_get_scb(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), FALSE); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->scb; +} + +/** + * nm_device_macsec_get_replay_protect: + * @device: a #NMDeviceMacsec + * + * Gets whether replay protection is enabled + * + * Returns: whether replay protection is enabled + * + * Since: 1.6 + **/ +gboolean +nm_device_macsec_get_replay_protect(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), FALSE); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->replay_protect; +} + +/***********************************************************/ + +static void +nm_device_macsec_init(NMDeviceMacsec *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE(object); + + g_free(priv->validation); + + G_OBJECT_CLASS(nm_device_macsec_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceMacsec *device = NM_DEVICE_MACSEC(object); + + switch (prop_id) { + case PROP_PARENT: + g_value_set_object(value, nm_device_macsec_get_parent(device)); + break; + case PROP_SCI: + g_value_set_uint64(value, nm_device_macsec_get_sci(device)); + break; + case PROP_ICV_LENGTH: + g_value_set_uchar(value, nm_device_macsec_get_icv_length(device)); + break; + case PROP_CIPHER_SUITE: + g_value_set_uint64(value, nm_device_macsec_get_cipher_suite(device)); + break; + case PROP_WINDOW: + g_value_set_uint(value, nm_device_macsec_get_window(device)); + break; + case PROP_ENCODING_SA: + g_value_set_uchar(value, nm_device_macsec_get_encoding_sa(device)); + break; + case PROP_VALIDATION: + g_value_set_string(value, nm_device_macsec_get_validation(device)); + break; + case PROP_ENCRYPT: + g_value_set_boolean(value, nm_device_macsec_get_encrypt(device)); + break; + case PROP_PROTECT: + g_value_set_boolean(value, nm_device_macsec_get_protect(device)); + break; + case PROP_INCLUDE_SCI: + g_value_set_boolean(value, nm_device_macsec_get_include_sci(device)); + break; + case PROP_ES: + g_value_set_boolean(value, nm_device_macsec_get_es(device)); + break; + case PROP_SCB: + g_value_set_boolean(value, nm_device_macsec_get_scb(device)); + break; + case PROP_REPLAY_PROTECT: + g_value_set_boolean(value, nm_device_macsec_get_replay_protect(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_macsec = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_MACSEC, + nm_device_macsec_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_T("CipherSuite", + PROP_CIPHER_SUITE, + NMDeviceMacsec, + _priv.cipher_suite), + NML_DBUS_META_PROPERTY_INIT_Y("EncodingSa", + PROP_ENCODING_SA, + NMDeviceMacsec, + _priv.encoding_sa), + NML_DBUS_META_PROPERTY_INIT_B("Encrypt", PROP_ENCRYPT, NMDeviceMacsec, _priv.encrypt), + NML_DBUS_META_PROPERTY_INIT_B("Es", PROP_ES, NMDeviceMacsec, _priv.es), + NML_DBUS_META_PROPERTY_INIT_Y("IcvLength", + PROP_ICV_LENGTH, + NMDeviceMacsec, + _priv.icv_length), + NML_DBUS_META_PROPERTY_INIT_B("IncludeSci", + PROP_INCLUDE_SCI, + NMDeviceMacsec, + _priv.include_sci), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Parent", + PROP_PARENT, + NMDeviceMacsec, + _priv.parent, + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_B("Protect", PROP_PROTECT, NMDeviceMacsec, _priv.protect), + NML_DBUS_META_PROPERTY_INIT_B("ReplayProtect", + PROP_REPLAY_PROTECT, + NMDeviceMacsec, + _priv.replay_protect), + NML_DBUS_META_PROPERTY_INIT_B("Scb", PROP_SCB, NMDeviceMacsec, _priv.scb), + NML_DBUS_META_PROPERTY_INIT_T("Sci", PROP_SCI, NMDeviceMacsec, _priv.sci), + NML_DBUS_META_PROPERTY_INIT_S("Validation", + PROP_VALIDATION, + NMDeviceMacsec, + _priv.validation), + NML_DBUS_META_PROPERTY_INIT_U("Window", PROP_WINDOW, NMDeviceMacsec, _priv.window), ), ); + +static void +nm_device_macsec_class_init(NMDeviceMacsecClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceMacsec); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceMacsecPrivate, parent); + + /** + * NMDeviceMacsec:parent: + * + * The devices's parent device. + * + * Since: 1.6 + **/ + obj_properties[PROP_PARENT] = g_param_spec_object(NM_DEVICE_MACSEC_PARENT, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:sci: + * + * The Secure Channel Identifier in use. + * + * Since: 1.6 + **/ + obj_properties[PROP_SCI] = g_param_spec_uint64(NM_DEVICE_MACSEC_SCI, + "", + "", + 0, + G_MAXUINT64, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:icv-length: + * + * The length of ICV (Integrity Check Value). + * + * Since: 1.6 + **/ + obj_properties[PROP_ICV_LENGTH] = g_param_spec_uchar(NM_DEVICE_MACSEC_ICV_LENGTH, + "", + "", + 0, + G_MAXUINT8, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:cipher-suite: + * + * The set of cryptographic algorithms in use. + * + * Since: 1.6 + **/ + obj_properties[PROP_CIPHER_SUITE] = + g_param_spec_uint64(NM_DEVICE_MACSEC_CIPHER_SUITE, + "", + "", + 0, + G_MAXUINT64, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:window: + * + * The size of the replay window. + * + * Since: 1.6 + **/ + obj_properties[PROP_WINDOW] = g_param_spec_uint(NM_DEVICE_MACSEC_WINDOW, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:encoding-sa: + * + * The value of the Association Number (0..3) for the Security + * Association in use. + * + * Since: 1.6 + **/ + obj_properties[PROP_ENCODING_SA] = + g_param_spec_uchar(NM_DEVICE_MACSEC_ENCODING_SA, + "", + "", + 0, + G_MAXUINT8, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:validation: + * + * The validation mode for incoming packets (strict, check, + * disabled). + * + * Since: 1.6 + **/ + obj_properties[PROP_VALIDATION] = + g_param_spec_string(NM_DEVICE_MACSEC_VALIDATION, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:encrypt: + * + * Whether encryption of transmitted frames is enabled. + * + * Since: 1.6 + **/ + obj_properties[PROP_ENCRYPT] = g_param_spec_boolean(NM_DEVICE_MACSEC_ENCRYPT, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:protect: + * + * Whether protection of transmitted frames is enabled. + * + * Since: 1.6 + **/ + obj_properties[PROP_PROTECT] = g_param_spec_boolean(NM_DEVICE_MACSEC_PROTECT, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:include-sci: + * + * Whether the SCI is always included in SecTAG for transmitted + * frames. + * + * Since: 1.6 + **/ + obj_properties[PROP_INCLUDE_SCI] = + g_param_spec_boolean(NM_DEVICE_MACSEC_INCLUDE_SCI, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:es: + * + * Whether the ES (End station) bit is enabled in SecTAG for + * transmitted frames. + * + * Since: 1.6 + **/ + obj_properties[PROP_ES] = g_param_spec_boolean(NM_DEVICE_MACSEC_ES, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:scb: + * + * Whether the SCB (Single Copy Broadcast) bit is enabled in + * SecTAG for transmitted frames. + * + * Since: 1.6 + **/ + obj_properties[PROP_SCB] = g_param_spec_boolean(NM_DEVICE_MACSEC_SCB, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:replay-protect: + * + * Whether replay protection is enabled. + * + * Since: 1.6 + **/ + obj_properties[PROP_REPLAY_PROTECT] = + g_param_spec_boolean(NM_DEVICE_MACSEC_REPLAY_PROTECT, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_macsec); +} diff --git a/src/libnm-client-impl/nm-device-macvlan.c b/src/libnm-client-impl/nm-device-macvlan.c new file mode 100644 index 00000000..ddb36bab --- /dev/null +++ b/src/libnm-client-impl/nm-device-macvlan.c @@ -0,0 +1,299 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-setting-macvlan.h" + +#include "nm-setting-connection.h" +#include "nm-setting-wired.h" +#include "nm-utils.h" +#include "nm-device-macvlan.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PARENT, PROP_MODE, PROP_NO_PROMISC, PROP_TAP, ); + +typedef struct { + NMLDBusPropertyO parent; + char * mode; + bool no_promisc; + bool tap; +} NMDeviceMacvlanPrivate; + +struct _NMDeviceMacvlan { + NMDevice parent; + NMDeviceMacvlanPrivate _priv; +}; + +struct _NMDeviceMacvlanClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceMacvlan, nm_device_macvlan, NM_TYPE_DEVICE) + +#define NM_DEVICE_MACVLAN_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceMacvlan, NM_IS_DEVICE_MACVLAN, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_macvlan_get_parent: + * @device: a #NMDeviceMacvlan + * + * Returns: (transfer none): the device's parent device + * + * Since: 1.2 + **/ +NMDevice * +nm_device_macvlan_get_parent(NMDeviceMacvlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACVLAN(device), FALSE); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_MACVLAN_GET_PRIVATE(device)->parent); +} + +/** + * nm_device_macvlan_get_mode: + * @device: a #NMDeviceMacvlan + * + * Gets the MACVLAN mode of the device. + * + * Returns: the MACVLAN mode. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.2 + **/ +const char * +nm_device_macvlan_get_mode(NMDeviceMacvlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACVLAN(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_MACVLAN_GET_PRIVATE(device)->mode); +} + +/** + * nm_device_macvlan_get_no_promisc + * @device: a #NMDeviceMacvlan + * + * Gets the no-promiscuous flag of the device. + * + * Returns: the no-promiscuous flag of the device. + * + * Since: 1.2 + **/ +gboolean +nm_device_macvlan_get_no_promisc(NMDeviceMacvlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACVLAN(device), FALSE); + + return NM_DEVICE_MACVLAN_GET_PRIVATE(device)->no_promisc; +} + +/** + * nm_device_macvlan_get_tap: + * @device: a #NMDeviceMacvlan + * + * Gets the device type (MACVLAN or MACVTAP). + * + * Returns: %TRUE if the device is a MACVTAP, %FALSE if it is a MACVLAN. + * + * Since: 1.2 + **/ +gboolean +nm_device_macvlan_get_tap(NMDeviceMacvlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACVLAN(device), FALSE); + + return NM_DEVICE_MACVLAN_GET_PRIVATE(device)->tap; +} + +/** + * nm_device_macvlan_get_hw_address: (skip) + * @device: a #NMDeviceMacvlan + * + * Gets the hardware (MAC) address of the #NMDeviceMacvlan + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.2 + * + * This property is not implemented yet, and the function always return NULL. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_macvlan_get_hw_address(NMDeviceMacvlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACVLAN(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE(device); + NMSettingMacvlan * s_macvlan; + + if (!NM_DEVICE_CLASS(nm_device_macvlan_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_MACVLAN_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a MAC-VLAN connection.")); + return FALSE; + } + + s_macvlan = nm_connection_get_setting_macvlan(connection); + if (s_macvlan) { + if (nm_setting_macvlan_get_tap(s_macvlan) != priv->tap) + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_MACVLAN; +} + +/*****************************************************************************/ + +static void +nm_device_macvlan_init(NMDeviceMacvlan *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE(object); + + g_free(priv->mode); + + G_OBJECT_CLASS(nm_device_macvlan_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceMacvlan *device = NM_DEVICE_MACVLAN(object); + + switch (prop_id) { + case PROP_PARENT: + g_value_set_object(value, nm_device_macvlan_get_parent(device)); + break; + case PROP_MODE: + g_value_set_string(value, nm_device_macvlan_get_mode(device)); + break; + case PROP_NO_PROMISC: + g_value_set_boolean(value, nm_device_macvlan_get_no_promisc(device)); + break; + case PROP_TAP: + g_value_set_boolean(value, nm_device_macvlan_get_tap(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_macvlan = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_MACVLAN, + nm_device_macvlan_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_S("Mode", PROP_MODE, NMDeviceMacvlan, _priv.mode), + NML_DBUS_META_PROPERTY_INIT_B("NoPromisc", + PROP_NO_PROMISC, + NMDeviceMacvlan, + _priv.no_promisc), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Parent", + PROP_PARENT, + NMDeviceMacvlan, + _priv.parent, + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_B("Tap", PROP_TAP, NMDeviceMacvlan, _priv.tap), ), ); + +static void +nm_device_macvlan_class_init(NMDeviceMacvlanClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceMacvlan); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceMacvlanPrivate, parent); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceMacvlan:parent: + * + * The devices's parent device. + * + * Since: 1.2 + **/ + obj_properties[PROP_PARENT] = g_param_spec_object(NM_DEVICE_MACVLAN_PARENT, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacvlan:mode: + * + * The MACVLAN mode. + * + * Since: 1.2 + **/ + obj_properties[PROP_MODE] = g_param_spec_string(NM_DEVICE_MACVLAN_MODE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacvlan:no-promisc: + * + * Whether the device has the no-promiscuos flag. + * + * Since: 1.2 + **/ + obj_properties[PROP_NO_PROMISC] = + g_param_spec_boolean(NM_DEVICE_MACVLAN_NO_PROMISC, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacvlan:tap: + * + * Whether the device is a MACVTAP. + * + * Since: 1.2 + **/ + obj_properties[PROP_TAP] = g_param_spec_boolean(NM_DEVICE_MACVLAN_TAP, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_macvlan); +} diff --git a/src/libnm-client-impl/nm-device-modem.c b/src/libnm-client-impl/nm-device-modem.c new file mode 100644 index 00000000..57e09bc6 --- /dev/null +++ b/src/libnm-client-impl/nm-device-modem.c @@ -0,0 +1,360 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2011 - 2012 Red Hat, Inc. + * Copyright (C) 2008 Novell, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-modem.h" + +#include "nm-setting-connection.h" +#include "nm-setting-gsm.h" +#include "nm-setting-cdma.h" +#include "nm-object-private.h" +#include "nm-enum-types.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_MODEM_CAPABILITIES, + PROP_CURRENT_CAPABILITIES, + PROP_DEVICE_ID, + PROP_OPERATOR_CODE, + PROP_APN, ); + +typedef struct { + char * device_id; + char * operator_code; + char * apn; + guint32 modem_capabilities; + guint32 current_capabilities; +} NMDeviceModemPrivate; + +struct _NMDeviceModem { + NMDevice parent; + NMDeviceModemPrivate _priv; +}; + +struct _NMDeviceModemClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE) + +#define NM_DEVICE_MODEM_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceModem, NM_IS_DEVICE_MODEM, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_modem_get_modem_capabilities: + * @self: a #NMDeviceModem + * + * Returns a bitfield of the generic access technology families the modem + * supports. Not all capabilities are available concurrently however; some + * may require a firmware reload or reinitialization. + * + * Returns: the generic access technology families the modem supports + **/ +NMDeviceModemCapabilities +nm_device_modem_get_modem_capabilities(NMDeviceModem *self) +{ + g_return_val_if_fail(NM_IS_DEVICE_MODEM(self), NM_DEVICE_MODEM_CAPABILITY_NONE); + + return NM_DEVICE_MODEM_GET_PRIVATE(self)->modem_capabilities; +} + +/** + * nm_device_modem_get_current_capabilities: + * @self: a #NMDeviceModem + * + * Returns a bitfield of the generic access technology families the modem + * supports without a firmware reload or reinitialization. This value + * represents the network types the modem can immediately connect to. + * + * Returns: the generic access technology families the modem supports without + * a firmware reload or other reinitialization + **/ +NMDeviceModemCapabilities +nm_device_modem_get_current_capabilities(NMDeviceModem *self) +{ + g_return_val_if_fail(NM_IS_DEVICE_MODEM(self), NM_DEVICE_MODEM_CAPABILITY_NONE); + + return NM_DEVICE_MODEM_GET_PRIVATE(self)->current_capabilities; +} + +/** + * nm_device_modem_get_device_id: + * @self: a #NMDeviceModem + * + * An identifier used by the modem backend (ModemManager) that aims to + * uniquely identify the a device. Can be used to match a connection to a + * particular device. + * + * Returns: a device-id string + * + * Since: 1.20 + **/ +const char * +nm_device_modem_get_device_id(NMDeviceModem *self) +{ + g_return_val_if_fail(NM_IS_DEVICE_MODEM(self), NULL); + + return NM_DEVICE_MODEM_GET_PRIVATE(self)->device_id; +} + +/** + * nm_device_modem_get_operator_code: + * @self: a #NMDeviceModem + * + * The MCC and MNC (concatenated) of the network the modem is connected to. + * + * Returns: the operator code or %NULL if disconnected or not a 3GPP modem. + * + * Since: 1.20 + **/ +const char * +nm_device_modem_get_operator_code(NMDeviceModem *self) +{ + g_return_val_if_fail(NM_IS_DEVICE_MODEM(self), NULL); + + return NM_DEVICE_MODEM_GET_PRIVATE(self)->operator_code; +} + +/** + * nm_device_modem_get_apn: + * @self: a #NMDeviceModem + * + * The access point name the modem is connected to. + * + * Returns: the APN name or %NULL if disconnected + * + * Since: 1.20 + **/ +const char * +nm_device_modem_get_apn(NMDeviceModem *self) +{ + g_return_val_if_fail(NM_IS_DEVICE_MODEM(self), NULL); + + return NM_DEVICE_MODEM_GET_PRIVATE(self)->apn; +} + +static const char * +get_type_description(NMDevice *device) +{ + NMDeviceModemCapabilities caps; + + caps = nm_device_modem_get_current_capabilities(NM_DEVICE_MODEM(device)); + if (caps & NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS) + return "gsm"; + else if (caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) + return "cdma"; + else + return NULL; +} + +#define MODEM_CAPS_3GPP(caps) \ + (caps & (NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS | NM_DEVICE_MODEM_CAPABILITY_LTE)) + +#define MODEM_CAPS_3GPP2(caps) (caps & (NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)) + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingGsm * s_gsm; + NMSettingCdma * s_cdma; + NMDeviceModemCapabilities current_caps; + + if (!NM_DEVICE_CLASS(nm_device_modem_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_GSM_SETTING_NAME) + && !nm_connection_is_type(connection, NM_SETTING_CDMA_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a modem connection.")); + return FALSE; + } + + s_gsm = nm_connection_get_setting_gsm(connection); + s_cdma = nm_connection_get_setting_cdma(connection); + if (!s_cdma && !s_gsm) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection was not a valid modem connection.")); + return FALSE; + } + + current_caps = nm_device_modem_get_current_capabilities(NM_DEVICE_MODEM(device)); + if (!(s_gsm && MODEM_CAPS_3GPP(current_caps)) && !(s_cdma && MODEM_CAPS_3GPP2(current_caps))) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The device is lacking capabilities required by the connection.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + NMDeviceModemCapabilities caps; + + caps = nm_device_modem_get_current_capabilities(NM_DEVICE_MODEM(device)); + if (caps & (NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS | NM_DEVICE_MODEM_CAPABILITY_LTE)) + return NM_TYPE_SETTING_GSM; + else if (caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) + return NM_TYPE_SETTING_CDMA; + else + return G_TYPE_INVALID; +} + +/*****************************************************************************/ + +static void +nm_device_modem_init(NMDeviceModem *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE(object); + + g_free(priv->device_id); + g_free(priv->operator_code); + g_free(priv->apn); + + G_OBJECT_CLASS(nm_device_modem_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceModem *self = NM_DEVICE_MODEM(object); + + switch (prop_id) { + case PROP_MODEM_CAPABILITIES: + g_value_set_flags(value, nm_device_modem_get_modem_capabilities(self)); + break; + case PROP_CURRENT_CAPABILITIES: + g_value_set_flags(value, nm_device_modem_get_current_capabilities(self)); + break; + case PROP_DEVICE_ID: + g_value_set_string(value, nm_device_modem_get_device_id(self)); + break; + case PROP_OPERATOR_CODE: + g_value_set_string(value, nm_device_modem_get_operator_code(self)); + break; + case PROP_APN: + g_value_set_string(value, nm_device_modem_get_apn(self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_modem = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_MODEM, + nm_device_modem_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_S("Apn", PROP_APN, NMDeviceModem, _priv.apn), + NML_DBUS_META_PROPERTY_INIT_U("CurrentCapabilities", + PROP_CURRENT_CAPABILITIES, + NMDeviceModem, + _priv.current_capabilities), + NML_DBUS_META_PROPERTY_INIT_S("DeviceId", PROP_DEVICE_ID, NMDeviceModem, _priv.device_id), + NML_DBUS_META_PROPERTY_INIT_U("ModemCapabilities", + PROP_MODEM_CAPABILITIES, + NMDeviceModem, + _priv.modem_capabilities), + NML_DBUS_META_PROPERTY_INIT_S("OperatorCode", + PROP_OPERATOR_CODE, + NMDeviceModem, + _priv.operator_code), ), ); + +static void +nm_device_modem_class_init(NMDeviceModemClass *modem_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(modem_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS(modem_class); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + device_class->get_type_description = get_type_description; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceModem:modem-capabilities: + * + * The generic family of access technologies the modem supports. Not all + * capabilities are available at the same time however; some modems require + * a firmware reload or other reinitialization to switch between eg + * CDMA/EVDO and GSM/UMTS. + **/ + obj_properties[PROP_MODEM_CAPABILITIES] = + g_param_spec_flags(NM_DEVICE_MODEM_MODEM_CAPABILITIES, + "", + "", + NM_TYPE_DEVICE_MODEM_CAPABILITIES, + NM_DEVICE_MODEM_CAPABILITY_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceModem:current-capabilities: + * + * The generic family of access technologies the modem currently supports + * without a firmware reload or reinitialization. + **/ + obj_properties[PROP_CURRENT_CAPABILITIES] = + g_param_spec_flags(NM_DEVICE_MODEM_CURRENT_CAPABILITIES, + "", + "", + NM_TYPE_DEVICE_MODEM_CAPABILITIES, + NM_DEVICE_MODEM_CAPABILITY_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceModem:device-id: + * + * Since: 1.20 + **/ + obj_properties[PROP_DEVICE_ID] = g_param_spec_string(NM_DEVICE_MODEM_DEVICE_ID, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceModem:operator-code: + * + * Since: 1.20 + **/ + obj_properties[PROP_OPERATOR_CODE] = + g_param_spec_string(NM_DEVICE_MODEM_OPERATOR_CODE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceModem:apn: + * + * Since: 1.20 + **/ + obj_properties[PROP_APN] = g_param_spec_string(NM_DEVICE_MODEM_APN, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_modem); +} diff --git a/src/libnm-client-impl/nm-device-olpc-mesh.c b/src/libnm-client-impl/nm-device-olpc-mesh.c new file mode 100644 index 00000000..779990aa --- /dev/null +++ b/src/libnm-client-impl/nm-device-olpc-mesh.c @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-olpc-mesh.h" + +#include "nm-setting-connection.h" +#include "nm-setting-olpc-mesh.h" +#include "nm-object-private.h" +#include "nm-device-wifi.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_COMPANION, PROP_ACTIVE_CHANNEL, ); + +typedef struct { + NMLDBusPropertyO companion; + guint32 active_channel; +} NMDeviceOlpcMeshPrivate; + +struct _NMDeviceOlpcMesh { + NMDevice parent; + NMDeviceOlpcMeshPrivate _priv; +}; + +struct _NMDeviceOlpcMeshClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE) + +#define NM_DEVICE_OLPC_MESH_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceOlpcMesh, NM_IS_DEVICE_OLPC_MESH, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_olpc_mesh_get_hw_address: (skip) + * @device: a #NMDeviceOlpcMesh + * + * Gets the hardware (MAC) address of the #NMDeviceOlpcMesh + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_olpc_mesh_get_hw_address(NMDeviceOlpcMesh *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_OLPC_MESH(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_olpc_mesh_get_companion: + * @device: a #NMDeviceOlpcMesh + * + * Gets the companion device of the #NMDeviceOlpcMesh. + * + * Returns: (transfer none): the companion of the device of %NULL + **/ +NMDeviceWifi * +nm_device_olpc_mesh_get_companion(NMDeviceOlpcMesh *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_OLPC_MESH(device), NULL); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_OLPC_MESH_GET_PRIVATE(device)->companion); +} + +/** + * nm_device_olpc_mesh_get_active_channel: + * @device: a #NMDeviceOlpcMesh + * + * Returns the active channel of the #NMDeviceOlpcMesh device. + * + * Returns: active channel of the device + **/ +guint32 +nm_device_olpc_mesh_get_active_channel(NMDeviceOlpcMesh *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_OLPC_MESH(device), 0); + + return NM_DEVICE_OLPC_MESH_GET_PRIVATE(device)->active_channel; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_olpc_mesh_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_OLPC_MESH_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not an OLPC Mesh connection.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_OLPC_MESH; +} + +/*****************************************************************************/ + +static void +nm_device_olpc_mesh_init(NMDeviceOlpcMesh *device) +{} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceOlpcMesh *device = NM_DEVICE_OLPC_MESH(object); + + switch (prop_id) { + case PROP_COMPANION: + g_value_set_object(value, nm_device_olpc_mesh_get_companion(device)); + break; + case PROP_ACTIVE_CHANNEL: + g_value_set_uint(value, nm_device_olpc_mesh_get_active_channel(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_olpcmesh = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_OLPC_MESH, + nm_device_olpc_mesh_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_U("ActiveChannel", + PROP_ACTIVE_CHANNEL, + NMDeviceOlpcMesh, + _priv.active_channel), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Companion", + PROP_COMPANION, + NMDeviceOlpcMesh, + _priv.companion, + nm_device_wifi_get_type), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), ), ); + +static void +nm_device_olpc_mesh_class_init(NMDeviceOlpcMeshClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceOlpcMesh); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceOlpcMeshPrivate, companion); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceOlpcMesh:companion: + * + * The companion device. + **/ + obj_properties[PROP_COMPANION] = g_param_spec_object(NM_DEVICE_OLPC_MESH_COMPANION, + "", + "", + NM_TYPE_DEVICE_WIFI, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceOlpcMesh:active-channel: + * + * The device's active channel. + **/ + obj_properties[PROP_ACTIVE_CHANNEL] = + g_param_spec_uint(NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_olpcmesh); +} diff --git a/src/libnm-client-impl/nm-device-ovs-bridge.c b/src/libnm-client-impl/nm-device-ovs-bridge.c new file mode 100644 index 00000000..898a41fa --- /dev/null +++ b/src/libnm-client-impl/nm-device-ovs-bridge.c @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017, 2018 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-ovs-bridge.h" + +#include "nm-object-private.h" +#include "nm-setting-ovs-bridge.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-connection.h" +#include "libnm-core-intern/nm-core-internal.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_SLAVES, ); + +typedef struct { + NMLDBusPropertyAO slaves; +} NMDeviceOvsBridgePrivate; + +struct _NMDeviceOvsBridge { + NMDevice parent; + NMDeviceOvsBridgePrivate _priv; +}; + +struct _NMDeviceOvsBridgeClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceOvsBridge, nm_device_ovs_bridge, NM_TYPE_DEVICE) + +#define NM_DEVICE_OVS_BRIDGE_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceOvsBridge, NM_IS_DEVICE_OVS_BRIDGE, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_ovs_bridge_get_slaves: + * @device: a #NMDeviceOvsBridge + * + * Gets the ports currently enslaved to @device. + * + * Returns: (element-type NMDevice): the #GPtrArray containing + * #NMDevices that are slaves of @device. This is the internal + * copy used by the device, and must not be modified. + * + * Since: 1.14 + **/ +const GPtrArray * +nm_device_ovs_bridge_get_slaves(NMDeviceOvsBridge *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_OVS_BRIDGE(device), FALSE); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_DEVICE_OVS_BRIDGE_GET_PRIVATE(device)->slaves); +} + +static const char * +get_type_description(NMDevice *device) +{ + return "ovs-bridge"; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + const char *iface_name; + + if (!NM_DEVICE_CLASS(nm_device_ovs_bridge_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a ovs_bridge connection.")); + return FALSE; + } + + iface_name = nm_connection_get_interface_name(connection); + if (!iface_name) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection did not specify an interface name.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_OVS_BRIDGE; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceOvsBridge *device = NM_DEVICE_OVS_BRIDGE(object); + + switch (prop_id) { + case PROP_SLAVES: + g_value_take_boxed(value, + _nm_utils_copy_object_array(nm_device_ovs_bridge_get_slaves(device))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_device_ovs_bridge_init(NMDeviceOvsBridge *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ovsbridge = + NML_DBUS_META_IFACE_INIT_PROP(NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE, + nm_device_ovs_bridge_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Slaves", + PROP_SLAVES, + NMDeviceOvsBridge, + _priv.slaves, + nm_device_get_type), ), ); + +static void +nm_device_ovs_bridge_class_init(NMDeviceOvsBridgeClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceOvsBridge); + + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMDeviceOvsBridgePrivate, slaves); + + device_class->get_type_description = get_type_description; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceOvsBridge:slaves: (type GPtrArray(NMDevice)) + * + * Gets the ports currently enslaved to the device. + * + * Since: 1.22 + */ + obj_properties[PROP_SLAVES] = g_param_spec_boxed(NM_DEVICE_OVS_BRIDGE_SLAVES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_ovsbridge); +} diff --git a/src/libnm-client-impl/nm-device-ovs-interface.c b/src/libnm-client-impl/nm-device-ovs-interface.c new file mode 100644 index 00000000..8632057a --- /dev/null +++ b/src/libnm-client-impl/nm-device-ovs-interface.c @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-ovs-interface.h" + +#include "nm-object-private.h" +#include "nm-setting-ovs-interface.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-connection.h" + +/*****************************************************************************/ + +struct _NMDeviceOvsInterface { + NMDevice parent; +}; + +struct _NMDeviceOvsInterfaceClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceOvsInterface, nm_device_ovs_interface, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static const char * +get_type_description(NMDevice *device) +{ + return "ovs-interface"; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + const char *iface_name; + + if (!NM_DEVICE_CLASS(nm_device_ovs_interface_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_OVS_INTERFACE_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a ovs_interface connection.")); + return FALSE; + } + + iface_name = nm_connection_get_interface_name(connection); + if (!iface_name) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection did not specify an interface name.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_OVS_INTERFACE; +} + +/*****************************************************************************/ + +static void +nm_device_ovs_interface_init(NMDeviceOvsInterface *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ovsinterface = + NML_DBUS_META_IFACE_INIT(NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE, + nm_device_ovs_interface_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, ); + +static void +nm_device_ovs_interface_class_init(NMDeviceOvsInterfaceClass *ovs_interface_class) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS(ovs_interface_class); + + device_class->get_type_description = get_type_description; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; +} diff --git a/src/libnm-client-impl/nm-device-ovs-port.c b/src/libnm-client-impl/nm-device-ovs-port.c new file mode 100644 index 00000000..db30e1c7 --- /dev/null +++ b/src/libnm-client-impl/nm-device-ovs-port.c @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017, 2018 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-ovs-port.h" + +#include "nm-object-private.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-connection.h" +#include "libnm-core-intern/nm-core-internal.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_SLAVES, ); + +typedef struct { + NMLDBusPropertyAO slaves; +} NMDeviceOvsPortPrivate; + +struct _NMDeviceOvsPort { + NMDevice parent; + NMDeviceOvsPortPrivate _priv; +}; + +struct _NMDeviceOvsPortClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceOvsPort, nm_device_ovs_port, NM_TYPE_DEVICE) + +#define NM_DEVICE_OVS_PORT_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceOvsPort, NM_IS_DEVICE_OVS_PORT, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_ovs_port_get_slaves: + * @device: a #NMDeviceOvsPort + * + * Gets the interfaces currently enslaved to @device. + * + * Returns: (element-type NMDevice): the #GPtrArray containing + * #NMDevices that are slaves of @device. This is the internal + * copy used by the device, and must not be modified. + * + * Since: 1.14 + **/ +const GPtrArray * +nm_device_ovs_port_get_slaves(NMDeviceOvsPort *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_OVS_PORT(device), FALSE); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_DEVICE_OVS_PORT_GET_PRIVATE(device)->slaves); +} + +static const char * +get_type_description(NMDevice *device) +{ + return "ovs-port"; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + const char *iface_name; + + if (!NM_DEVICE_CLASS(nm_device_ovs_port_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_OVS_PORT_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a ovs_port connection.")); + return FALSE; + } + + iface_name = nm_connection_get_interface_name(connection); + if (!iface_name) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection did not specify an interface name.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_OVS_PORT; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceOvsPort *device = NM_DEVICE_OVS_PORT(object); + + switch (prop_id) { + case PROP_SLAVES: + g_value_take_boxed(value, + _nm_utils_copy_object_array(nm_device_ovs_port_get_slaves(device))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_device_ovs_port_init(NMDeviceOvsPort *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ovsport = + NML_DBUS_META_IFACE_INIT_PROP(NM_DBUS_INTERFACE_DEVICE_OVS_PORT, + nm_device_ovs_port_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Slaves", + PROP_SLAVES, + NMDeviceOvsPort, + _priv.slaves, + nm_device_get_type), ), ); + +static void +nm_device_ovs_port_class_init(NMDeviceOvsPortClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceOvsPort); + + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMDeviceOvsPortPrivate, slaves); + + device_class->get_type_description = get_type_description; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceOvsPort:slaves: (type GPtrArray(NMDevice)) + * + * Gets the interfaces currently enslaved to the device. + * + * Since: 1.22 + */ + obj_properties[PROP_SLAVES] = g_param_spec_boxed(NM_DEVICE_OVS_PORT_SLAVES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_ovsport); +} diff --git a/src/libnm-client-impl/nm-device-ppp.c b/src/libnm-client-impl/nm-device-ppp.c new file mode 100644 index 00000000..1dd275cf --- /dev/null +++ b/src/libnm-client-impl/nm-device-ppp.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-ppp.h" + +#include "nm-device.h" + +/*****************************************************************************/ + +struct _NMDevicePpp { + NMDevice parent; +}; + +struct _NMDevicePppClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDevicePpp, nm_device_ppp, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static void +nm_device_ppp_init(NMDevicePpp *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ppp = + NML_DBUS_META_IFACE_INIT(NM_DBUS_INTERFACE_DEVICE_PPP, + nm_device_ppp_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, ); + +static void +nm_device_ppp_class_init(NMDevicePppClass *klass) +{} diff --git a/src/libnm-client-impl/nm-device-private.h b/src/libnm-client-impl/nm-device-private.h new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/libnm-client-impl/nm-device-private.h diff --git a/src/libnm-client-impl/nm-device-team.c b/src/libnm-client-impl/nm-device-team.c new file mode 100644 index 00000000..ca29c9f7 --- /dev/null +++ b/src/libnm-client-impl/nm-device-team.c @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2013 Jiri Pirko <jiri@resnulli.us> + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-team.h" + +#include "nm-setting-connection.h" +#include "nm-setting-team.h" +#include "nm-utils.h" +#include "nm-object-private.h" +#include "libnm-core-intern/nm-core-internal.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CARRIER, PROP_SLAVES, PROP_CONFIG, ); + +typedef struct { + NMLDBusPropertyAO slaves; + char * config; + bool carrier; +} NMDeviceTeamPrivate; + +struct _NMDeviceTeam { + NMDevice parent; + NMDeviceTeamPrivate _priv; +}; + +struct _NMDeviceTeamClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceTeam, nm_device_team, NM_TYPE_DEVICE) + +#define NM_DEVICE_TEAM_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceTeam, NM_IS_DEVICE_TEAM, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_team_get_hw_address: (skip) + * @device: a #NMDeviceTeam + * + * Gets the hardware (MAC) address of the #NMDeviceTeam + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_team_get_hw_address(NMDeviceTeam *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TEAM(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_team_get_carrier: + * @device: a #NMDeviceTeam + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + **/ +gboolean +nm_device_team_get_carrier(NMDeviceTeam *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TEAM(device), FALSE); + + return NM_DEVICE_TEAM_GET_PRIVATE(device)->carrier; +} + +/** + * nm_device_team_get_slaves: + * @device: a #NMDeviceTeam + * + * Gets the devices currently enslaved to @device. + * + * Returns: (element-type NMDevice): the #GPtrArray containing + * #NMDevices that are slaves of @device. This is the internal + * copy used by the device, and must not be modified. + **/ +const GPtrArray * +nm_device_team_get_slaves(NMDeviceTeam *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TEAM(device), FALSE); + + return nml_dbus_property_ao_get_objs_as_ptrarray(&NM_DEVICE_TEAM_GET_PRIVATE(device)->slaves); +} + +/** + * nm_device_team_get_config: + * @device: a #NMDeviceTeam + * + * Gets the current JSON configuration of the #NMDeviceTeam + * + * Returns: the current configuration. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.4 + **/ +const char * +nm_device_team_get_config(NMDeviceTeam *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TEAM(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_TEAM_GET_PRIVATE(device)->config); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_team_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_TEAM_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection was not a team connection.")); + return FALSE; + } + + /* FIXME: check slaves? */ + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_TEAM; +} + +/*****************************************************************************/ + +static void +nm_device_team_init(NMDeviceTeam *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(object); + + g_free(priv->config); + + G_OBJECT_CLASS(nm_device_team_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceTeam *device = NM_DEVICE_TEAM(object); + + switch (prop_id) { + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_team_get_carrier(device)); + break; + case PROP_SLAVES: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_device_team_get_slaves(device))); + break; + case PROP_CONFIG: + g_value_set_string(value, nm_device_team_get_config(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_team = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_TEAM, + nm_device_team_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("Carrier", PROP_CARRIER, NMDeviceTeam, _priv.carrier), + NML_DBUS_META_PROPERTY_INIT_S("Config", PROP_CONFIG, NMDeviceTeam, _priv.config), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Slaves", + PROP_SLAVES, + NMDeviceTeam, + _priv.slaves, + nm_device_get_type), ), ); + +static void +nm_device_team_class_init(NMDeviceTeamClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceTeam); + + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMDeviceTeamPrivate, slaves); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceTeam:carrier: + * + * Whether the device has carrier. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_TEAM_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceTeam:slaves: (type GPtrArray(NMDevice)) + * + * The devices enslaved to the team device. + **/ + obj_properties[PROP_SLAVES] = g_param_spec_boxed(NM_DEVICE_TEAM_SLAVES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceTeam:config: + * + * The current JSON configuration of the device. + * + * Since: 1.4 + **/ + obj_properties[PROP_CONFIG] = g_param_spec_string(NM_DEVICE_TEAM_CONFIG, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_team); +} diff --git a/src/libnm-client-impl/nm-device-tun.c b/src/libnm-client-impl/nm-device-tun.c new file mode 100644 index 00000000..8b8ca0fe --- /dev/null +++ b/src/libnm-client-impl/nm-device-tun.c @@ -0,0 +1,394 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-tun.h" + +#include <arpa/inet.h> + +#include "nm-setting-connection.h" +#include "nm-setting-tun.h" +#include "nm-utils.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_MODE, + PROP_OWNER, + PROP_GROUP, + PROP_NO_PI, + PROP_VNET_HDR, + PROP_MULTI_QUEUE, ); + +typedef struct { + char * mode; + gint64 owner; + gint64 group; + bool no_pi; + bool vnet_hdr; + bool multi_queue; +} NMDeviceTunPrivate; + +struct _NMDeviceTun { + NMDevice parent; + NMDeviceTunPrivate _priv; +}; + +struct _NMDeviceTunClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceTun, nm_device_tun, NM_TYPE_DEVICE) + +#define NM_DEVICE_TUN_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceTun, NM_IS_DEVICE_TUN, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_tun_get_hw_address: (skip) + * @device: a #NMDeviceTun + * + * Gets the hardware (MAC) address of the #NMDeviceTun + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.2 + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_tun_get_hw_address(NMDeviceTun *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TUN(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_tun_get_mode: + * @device: a #NMDeviceTun + * + * Returns the TUN/TAP mode for the device. + * + * Returns: 'tun' or 'tap' + * + * Since: 1.2 + **/ +const char * +nm_device_tun_get_mode(NMDeviceTun *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TUN(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_TUN_GET_PRIVATE(device)->mode); +} + +/** + * nm_device_tun_get_owner: + * @device: a #NMDeviceTun + * + * Gets the tunnel owner. + * + * Returns: the uid of the tunnel owner, or -1 if it has no owner. + * + * Since: 1.2 + **/ +gint64 +nm_device_tun_get_owner(NMDeviceTun *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TUN(device), -1); + + return NM_DEVICE_TUN_GET_PRIVATE(device)->owner; +} + +/** + * nm_device_tun_get_group: + * @device: a #NMDeviceTun + * + * Gets the tunnel group. + * + * Returns: the gid of the tunnel group, or -1 if it has no owner. + * + * Since: 1.2 + **/ +gint64 +nm_device_tun_get_group(NMDeviceTun *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TUN(device), -1); + + return NM_DEVICE_TUN_GET_PRIVATE(device)->group; +} + +/** + * nm_device_tun_get_pi: + * @device: a #NMDeviceTun + * + * Returns whether the #NMDeviceTun has the IFF_NO_PI flag. + * + * Returns: %TRUE if the device has the flag, %FALSE otherwise + * + * Since: 1.2 + **/ +gboolean +nm_device_tun_get_no_pi(NMDeviceTun *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TUN(device), FALSE); + + return NM_DEVICE_TUN_GET_PRIVATE(device)->no_pi; +} + +/** + * nm_device_tun_get_vnet_hdr: + * @device: a #NMDeviceTun + * + * Returns whether the #NMDeviceTun has the IFF_VNET_HDR flag. + * + * Returns: %TRUE if the device has the flag, %FALSE otherwise + * + * Since: 1.2 + **/ +gboolean +nm_device_tun_get_vnet_hdr(NMDeviceTun *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TUN(device), FALSE); + + return NM_DEVICE_TUN_GET_PRIVATE(device)->vnet_hdr; +} + +/** + * nm_device_tun_get_multi_queue: + * @device: a #NMDeviceTun + * + * Returns whether the #NMDeviceTun has the IFF_MULTI_QUEUE flag. + * + * Returns: %TRUE if the device doesn't have the flag, %FALSE otherwise + * + * Since: 1.2 + **/ +gboolean +nm_device_tun_get_multi_queue(NMDeviceTun *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TUN(device), FALSE); + + return NM_DEVICE_TUN_GET_PRIVATE(device)->multi_queue; +} + +static int +tun_mode_from_string(const char *string) +{ + if (!g_strcmp0(string, "tap")) + return NM_SETTING_TUN_MODE_TAP; + else + return NM_SETTING_TUN_MODE_TUN; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE(device); + NMSettingTunMode mode; + NMSettingTun * s_tun; + + if (!NM_DEVICE_CLASS(nm_device_tun_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_TUN_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a tun connection.")); + return FALSE; + } + + s_tun = nm_connection_get_setting_tun(connection); + + mode = tun_mode_from_string(priv->mode); + if (s_tun && mode != nm_setting_tun_get_mode(s_tun)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The mode of the device and the connection didn't match")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_TUN; +} + +/*****************************************************************************/ + +static void +nm_device_tun_init(NMDeviceTun *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE(object); + + g_free(priv->mode); + + G_OBJECT_CLASS(nm_device_tun_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceTun *device = NM_DEVICE_TUN(object); + + switch (prop_id) { + case PROP_MODE: + g_value_set_string(value, nm_device_tun_get_mode(device)); + break; + case PROP_OWNER: + g_value_set_int64(value, nm_device_tun_get_owner(device)); + break; + case PROP_GROUP: + g_value_set_int64(value, nm_device_tun_get_group(device)); + break; + case PROP_NO_PI: + g_value_set_boolean(value, nm_device_tun_get_no_pi(device)); + break; + case PROP_VNET_HDR: + g_value_set_boolean(value, nm_device_tun_get_vnet_hdr(device)); + break; + case PROP_MULTI_QUEUE: + g_value_set_boolean(value, nm_device_tun_get_multi_queue(device)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_tun = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_TUN, + nm_device_tun_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_X("Group", PROP_GROUP, NMDeviceTun, _priv.group), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_S("Mode", PROP_MODE, NMDeviceTun, _priv.mode), + NML_DBUS_META_PROPERTY_INIT_B("MultiQueue", + PROP_MULTI_QUEUE, + NMDeviceTun, + _priv.multi_queue), + NML_DBUS_META_PROPERTY_INIT_B("NoPi", PROP_NO_PI, NMDeviceTun, _priv.no_pi), + NML_DBUS_META_PROPERTY_INIT_X("Owner", PROP_OWNER, NMDeviceTun, _priv.owner), + NML_DBUS_META_PROPERTY_INIT_B("VnetHdr", PROP_VNET_HDR, NMDeviceTun, _priv.vnet_hdr), ), ); + +static void +nm_device_tun_class_init(NMDeviceTunClass *gre_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(gre_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS(gre_class); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceTun:mode: + * + * The tunnel mode, either "tun" or "tap". + * + * Since: 1.2 + **/ + obj_properties[PROP_MODE] = g_param_spec_string(NM_DEVICE_TUN_MODE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceTun:owner: + * + * The uid of the tunnel owner, or -1 if it has no owner. + * + * Since: 1.2 + **/ + obj_properties[PROP_OWNER] = g_param_spec_int64(NM_DEVICE_TUN_OWNER, + "", + "", + -1, + G_MAXUINT32, + -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceTun:group: + * + * The gid of the tunnel group, or -1 if it has no owner. + * + * Since: 1.2 + **/ + obj_properties[PROP_GROUP] = g_param_spec_int64(NM_DEVICE_TUN_GROUP, + "", + "", + -1, + G_MAXUINT32, + -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceTun:no-pi: + * + * The tunnel's "TUN_NO_PI" flag; true if no protocol info is + * prepended to the tunnel packets. + * + * Since: 1.2 + **/ + obj_properties[PROP_NO_PI] = g_param_spec_boolean(NM_DEVICE_TUN_NO_PI, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceTun:vnet-hdr: + * + * The tunnel's "TUN_VNET_HDR" flag; true if the tunnel packets + * include a virtio network header. + * + * Since: 1.2 + **/ + obj_properties[PROP_VNET_HDR] = g_param_spec_boolean(NM_DEVICE_TUN_VNET_HDR, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceTun:multi-queue: + * + * The tunnel's "TUN_TAP_MQ" flag; true if callers can connect to + * the tap device multiple times, for multiple send/receive + * queues. + * + * Since: 1.2 + **/ + obj_properties[PROP_MULTI_QUEUE] = + g_param_spec_boolean(NM_DEVICE_TUN_MULTI_QUEUE, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_tun); +} diff --git a/src/libnm-client-impl/nm-device-veth.c b/src/libnm-client-impl/nm-device-veth.c new file mode 100644 index 00000000..f35f09b1 --- /dev/null +++ b/src/libnm-client-impl/nm-device-veth.c @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-veth.h" + +#include "nm-setting-connection.h" +#include "nm-setting-veth.h" +#include "nm-setting-wired.h" +#include "nm-utils.h" +#include "nm-device-ethernet.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PEER, ); + +typedef struct { + NMLDBusPropertyO peer; +} NMDeviceVethPrivate; + +struct _NMDeviceVeth { + NMDeviceEthernet parent; + NMDeviceVethPrivate _priv; +}; + +struct _NMDeviceVethClass { + NMDeviceEthernetClass parent; +}; + +G_DEFINE_TYPE(NMDeviceVeth, nm_device_veth, NM_TYPE_DEVICE_ETHERNET) + +#define NM_DEVICE_VETH_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceVeth, NM_IS_DEVICE_VETH, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_veth_get_peer: + * @device: a #NMDeviceVeth + * + * Returns: (transfer none): the device's peer device + * + * Since: 1.30 + **/ +NMDevice * +nm_device_veth_get_peer(NMDeviceVeth *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VETH(device), FALSE); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_VETH_GET_PRIVATE(device)->peer); +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_VETH; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceVeth *device = NM_DEVICE_VETH(object); + + switch (prop_id) { + case PROP_PEER: + g_value_set_object(value, nm_device_veth_get_peer(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nm_device_veth_init(NMDeviceVeth *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_veth = + NML_DBUS_META_IFACE_INIT_PROP(NM_DBUS_INTERFACE_DEVICE_VETH, + nm_device_veth_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_O_PROP("Peer", + PROP_PEER, + NMDeviceVeth, + _priv.peer, + nm_device_get_type), ), ); + +static void +nm_device_veth_class_init(NMDeviceVethClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceVeth); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceVethPrivate, peer); + + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceVeth:peer: + * + * The device's peer device. + * + * Since: 1.30 + **/ + obj_properties[PROP_PEER] = g_param_spec_object(NM_DEVICE_VETH_PEER, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_veth); +} diff --git a/src/libnm-client-impl/nm-device-vlan.c b/src/libnm-client-impl/nm-device-vlan.c new file mode 100644 index 00000000..4c2a435b --- /dev/null +++ b/src/libnm-client-impl/nm-device-vlan.c @@ -0,0 +1,257 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-vlan.h" + +#include "nm-setting-connection.h" +#include "nm-setting-vlan.h" +#include "nm-setting-wired.h" +#include "nm-utils.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CARRIER, PROP_PARENT, PROP_VLAN_ID, ); + +typedef struct { + NMLDBusPropertyO parent; + guint32 vlan_id; + bool carrier; +} NMDeviceVlanPrivate; + +struct _NMDeviceVlan { + NMDevice parent; + NMDeviceVlanPrivate _priv; +}; + +struct _NMDeviceVlanClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceVlan, nm_device_vlan, NM_TYPE_DEVICE) + +#define NM_DEVICE_VLAN_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceVlan, NM_IS_DEVICE_VLAN, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_vlan_get_hw_address: (skip) + * @device: a #NMDeviceVlan + * + * Gets the hardware (MAC) address of the #NMDeviceVlan + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_vlan_get_hw_address(NMDeviceVlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VLAN(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_vlan_get_carrier: + * @device: a #NMDeviceVlan + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + **/ +gboolean +nm_device_vlan_get_carrier(NMDeviceVlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VLAN(device), FALSE); + + return NM_DEVICE_VLAN_GET_PRIVATE(device)->carrier; +} + +/** + * nm_device_vlan_get_parent: + * @device: a #NMDeviceVlan + * + * Returns: (transfer none): the device's parent device + **/ +NMDevice * +nm_device_vlan_get_parent(NMDeviceVlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VLAN(device), FALSE); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_VLAN_GET_PRIVATE(device)->parent); +} + +/** + * nm_device_vlan_get_vlan_id: + * @device: a #NMDeviceVlan + * + * Returns: the device's VLAN ID + **/ +guint +nm_device_vlan_get_vlan_id(NMDeviceVlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VLAN(device), FALSE); + + return NM_DEVICE_VLAN_GET_PRIVATE(device)->vlan_id; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingVlan * s_vlan; + NMSettingWired *s_wired; + const char * setting_hwaddr; + const char * hw_address; + + if (!NM_DEVICE_CLASS(nm_device_vlan_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_VLAN_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a VLAN connection.")); + return FALSE; + } + + s_vlan = nm_connection_get_setting_vlan(connection); + if (nm_setting_vlan_get_id(s_vlan) != nm_device_vlan_get_vlan_id(NM_DEVICE_VLAN(device))) { + g_set_error_literal( + error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The VLAN identifiers of the device and the connection didn't match.")); + return FALSE; + } + + s_wired = nm_connection_get_setting_wired(connection); + if (s_wired) + setting_hwaddr = nm_setting_wired_get_mac_address(s_wired); + else + setting_hwaddr = NULL; + if (setting_hwaddr) { + hw_address = nm_device_get_hw_address(NM_DEVICE(device)); + + if (!hw_address || !nm_utils_hwaddr_matches(setting_hwaddr, -1, hw_address, -1)) { + g_set_error_literal( + error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The hardware address of the device and the connection didn't match.")); + } + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_VLAN; +} + +/*****************************************************************************/ + +static void +nm_device_vlan_init(NMDeviceVlan *device) +{} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceVlan *device = NM_DEVICE_VLAN(object); + + switch (prop_id) { + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_vlan_get_carrier(device)); + break; + case PROP_PARENT: + g_value_set_object(value, nm_device_vlan_get_parent(device)); + break; + case PROP_VLAN_ID: + g_value_set_uint(value, nm_device_vlan_get_vlan_id(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_vlan = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_VLAN, + nm_device_vlan_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("Carrier", PROP_CARRIER, NMDeviceVlan, _priv.carrier), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Parent", + PROP_PARENT, + NMDeviceVlan, + _priv.parent, + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_U("VlanId", PROP_VLAN_ID, NMDeviceVlan, _priv.vlan_id), ), ); + +static void +nm_device_vlan_class_init(NMDeviceVlanClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceVlan); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceVlanPrivate, parent); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceVlan:carrier: + * + * Whether the device has carrier. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_VLAN_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVlan:parent: + * + * The devices's parent device. + **/ + obj_properties[PROP_PARENT] = g_param_spec_object(NM_DEVICE_VLAN_PARENT, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVlan:vlan-id: + * + * The device's VLAN ID. + **/ + obj_properties[PROP_VLAN_ID] = g_param_spec_uint(NM_DEVICE_VLAN_VLAN_ID, + "", + "", + 0, + 4095, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_vlan); +} diff --git a/src/libnm-client-impl/nm-device-vrf.c b/src/libnm-client-impl/nm-device-vrf.c new file mode 100644 index 00000000..bbf37810 --- /dev/null +++ b/src/libnm-client-impl/nm-device-vrf.c @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-vrf.h" + +#include "nm-setting-connection.h" +#include "nm-setting-vrf.h" +#include "nm-utils.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_TABLE, ); + +typedef struct { + guint32 table; +} NMDeviceVrfPrivate; + +struct _NMDeviceVrf { + NMDevice parent; + NMDeviceVrfPrivate _priv; +}; + +struct _NMDeviceVrfClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceVrf, nm_device_vrf, NM_TYPE_DEVICE) + +#define NM_DEVICE_VRF_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceVrf, NM_IS_DEVICE_VRF, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_vrf_get_table: + * @device: a #NMDeviceVrf + * + * Returns: the device's VRF routing table. + * + * Since: 1.24 + **/ +guint32 +nm_device_vrf_get_table(NMDeviceVrf *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VRF(device), 0); + + return NM_DEVICE_VRF_GET_PRIVATE(device)->table; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingVrf *s_vrf; + + if (!NM_DEVICE_CLASS(nm_device_vrf_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_VRF_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a VRF connection.")); + return FALSE; + } + + s_vrf = (NMSettingVrf *) nm_connection_get_setting(connection, NM_TYPE_SETTING_VRF); + if (nm_setting_vrf_get_table(s_vrf) != nm_device_vrf_get_table(NM_DEVICE_VRF(device))) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The VRF table of the device and the connection didn't match.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_VRF; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceVrf *device = NM_DEVICE_VRF(object); + + switch (prop_id) { + case PROP_TABLE: + g_value_set_uint(value, nm_device_vrf_get_table(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nm_device_vrf_init(NMDeviceVrf *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_vrf = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_VRF, + nm_device_vrf_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_U("Table", PROP_TABLE, NMDeviceVrf, _priv.table), ), ); + +static void +nm_device_vrf_class_init(NMDeviceVrfClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceVrf); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceVrf:table: + * + * The device's VRF table. + * + * Since: 1.24 + **/ + obj_properties[PROP_TABLE] = g_param_spec_uint(NM_DEVICE_VRF_TABLE, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_vrf); +} diff --git a/src/libnm-client-impl/nm-device-vxlan.c b/src/libnm-client-impl/nm-device-vxlan.c new file mode 100644 index 00000000..6fac3b71 --- /dev/null +++ b/src/libnm-client-impl/nm-device-vxlan.c @@ -0,0 +1,787 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-vxlan.h" + +#include "nm-setting-connection.h" +#include "nm-setting-vxlan.h" +#include "nm-utils.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CARRIER, + PROP_PARENT, + PROP_ID, + PROP_GROUP, + PROP_LOCAL, + PROP_TOS, + PROP_TTL, + PROP_LIMIT, + PROP_LEARNING, + PROP_AGEING, + PROP_DST_PORT, + PROP_SRC_PORT_MIN, + PROP_SRC_PORT_MAX, + PROP_PROXY, + PROP_RSC, + PROP_L2MISS, + PROP_L3MISS, ); + +typedef struct { + NMLDBusPropertyO parent; + char * group; + char * local; + guint32 id; + guint32 limit; + guint32 ageing; + guint16 src_port_min; + guint16 src_port_max; + guint16 dst_port; + guint8 tos; + guint8 ttl; + bool learning; + bool proxy; + bool rsc; + bool l2miss; + bool l3miss; +} NMDeviceVxlanPrivate; + +struct _NMDeviceVxlan { + NMDevice parent; + NMDeviceVxlanPrivate _priv; +}; + +struct _NMDeviceVxlanClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceVxlan, nm_device_vxlan, NM_TYPE_DEVICE) + +#define NM_DEVICE_VXLAN_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceVxlan, NM_IS_DEVICE_VXLAN, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_vxlan_get_hw_address: (skip) + * @device: a #NMDeviceVxlan + * + * Gets the hardware (MAC) address of the #NMDeviceVxlan + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.2 + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_vxlan_get_hw_address(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_vxlan_get_carrier: + * @device: a #NMDeviceVxlan + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier. + * + * Since: 1.2 + * + * This property is not implemented yet, and the function always returns + * FALSE. + **/ +gboolean +nm_device_vxlan_get_carrier(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), FALSE); + + return FALSE; +} + +/** + * nm_device_vxlan_get_parent: + * @device: a #NMDeviceVxlan + * + * Returns: (transfer none): the device's parent device + * + * Since: 1.2 + **/ +NMDevice * +nm_device_vxlan_get_parent(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), NULL); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_VXLAN_GET_PRIVATE(device)->parent); +} + +/** + * nm_device_vxlan_get_id: + * @device: a #NMDeviceVxlan + * + * Returns: the device's VXLAN ID. + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_id(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->id; +} + +/** + * nm_device_vxlan_get_group: + * @device: a #NMDeviceVxlan + * + * Returns: The unicast destination IP address or the multicast + * IP address joined + * + * Since: 1.2 + **/ +const char * +nm_device_vxlan_get_group(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_VXLAN_GET_PRIVATE(device)->group); +} + +/** + * nm_device_vxlan_get_local: + * @device: a #NMDeviceVxlan + * + * Returns: the source IP address to use in outgoing packets + * + * Since: 1.2 + **/ +const char * +nm_device_vxlan_get_local(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_VXLAN_GET_PRIVATE(device)->local); +} + +/** + * nm_device_vxlan_get_src_port_min: + * @device: a #NMDeviceVxlan + * + * Returns: the minimum UDP source port + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_src_port_min(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->src_port_min; +} + +/** + * nm_device_vxlan_get_src_port_max: + * @device: a #NMDeviceVxlan + * + * Returns: the maximum UDP source port + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_src_port_max(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->src_port_max; +} + +/** + * nm_device_vxlan_get_dst_port: + * @device: a #NMDeviceVxlan + * + * Returns: the UDP destination port + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_dst_port(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->dst_port; +} + +/** + * nm_device_vxlan_get_learning: + * @device: a #NMDeviceVxlan + * + * Returns: whether address learning is enabled + * + * Since: 1.2 + **/ +gboolean +nm_device_vxlan_get_learning(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), FALSE); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->learning; +} + +/** + * nm_device_vxlan_get_ageing: + * @device: a #NMDeviceVxlan + * + * Returns: the lifetime in seconds of FDB entries learnt by the kernel + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_ageing(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->ageing; +} + +/** + * nm_device_vxlan_get_tos: + * @device: a #NMDeviceVxlan + * + * Returns: the TOS value to use in outgoing packets + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_tos(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->tos; +} + +/** + * nm_device_vxlan_get_ttl: + * @device: a #NMDeviceVxlan + * + * Returns: the time-to-live value to use in outgoing packets + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_ttl(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->ttl; +} + +/** + * nm_device_vxlan_get_limit: + * @device: a #NMDeviceVxlan + * + * Returns: the maximum number of entries that can be added to the + * forwarding table + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_limit(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->limit; +} + +/** + * nm_device_vxlan_get_proxy: + * @device: a #NMDeviceVxlan + * + * Returns: whether ARP proxy is turned on + * + * Since: 1.2 + **/ +gboolean +nm_device_vxlan_get_proxy(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), FALSE); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->proxy; +} + +/** + * nm_device_vxlan_get_rsc: + * @device: a #NMDeviceVxlan + * + * Returns: whether route short circuit is turned on + * + * Since: 1.2 + **/ +gboolean +nm_device_vxlan_get_rsc(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), FALSE); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->rsc; +} + +/** + * nm_device_vxlan_get_l2miss: + * @device: a #NMDeviceVxlan + * + * Returns: whether netlink LL ADDR miss notifications are generated + * + * Since: 1.2 + **/ +gboolean +nm_device_vxlan_get_l2miss(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), FALSE); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->l2miss; +} + +/** + * nm_device_vxlan_get_l3miss: + * @device: a #NMDeviceVxlan + * + * Returns: whether netlink IP ADDR miss notifications are generated + * + * Since: 1.2 + **/ +gboolean +nm_device_vxlan_get_l3miss(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), FALSE); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->l3miss; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingVxlan *s_vxlan; + + if (!NM_DEVICE_CLASS(nm_device_vxlan_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_VXLAN_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a VXLAN connection.")); + return FALSE; + } + + s_vxlan = nm_connection_get_setting_vxlan(connection); + if (nm_setting_vxlan_get_id(s_vxlan) != nm_device_vxlan_get_id(NM_DEVICE_VXLAN(device))) { + g_set_error_literal( + error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The VXLAN identifiers of the device and the connection didn't match.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_VXLAN; +} + +/*****************************************************************************/ + +static void +nm_device_vxlan_init(NMDeviceVxlan *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE(object); + + g_free(priv->group); + g_free(priv->local); + + G_OBJECT_CLASS(nm_device_vxlan_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceVxlan *device = NM_DEVICE_VXLAN(object); + + switch (prop_id) { + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_vxlan_get_carrier(device)); + break; + case PROP_PARENT: + g_value_set_object(value, nm_device_vxlan_get_parent(device)); + break; + case PROP_ID: + g_value_set_uint(value, nm_device_vxlan_get_id(device)); + break; + case PROP_GROUP: + g_value_set_string(value, nm_device_vxlan_get_group(device)); + break; + case PROP_LOCAL: + g_value_set_string(value, nm_device_vxlan_get_local(device)); + break; + case PROP_TOS: + g_value_set_uint(value, nm_device_vxlan_get_tos(device)); + break; + case PROP_TTL: + g_value_set_uint(value, nm_device_vxlan_get_ttl(device)); + break; + case PROP_LIMIT: + g_value_set_uint(value, nm_device_vxlan_get_limit(device)); + break; + case PROP_LEARNING: + g_value_set_boolean(value, nm_device_vxlan_get_learning(device)); + break; + case PROP_AGEING: + g_value_set_uint(value, nm_device_vxlan_get_ageing(device)); + break; + case PROP_DST_PORT: + g_value_set_uint(value, nm_device_vxlan_get_dst_port(device)); + break; + case PROP_SRC_PORT_MIN: + g_value_set_uint(value, nm_device_vxlan_get_src_port_min(device)); + break; + case PROP_SRC_PORT_MAX: + g_value_set_uint(value, nm_device_vxlan_get_src_port_max(device)); + break; + case PROP_PROXY: + g_value_set_boolean(value, nm_device_vxlan_get_proxy(device)); + break; + case PROP_RSC: + g_value_set_boolean(value, nm_device_vxlan_get_rsc(device)); + break; + case PROP_L2MISS: + g_value_set_boolean(value, nm_device_vxlan_get_l2miss(device)); + break; + case PROP_L3MISS: + g_value_set_boolean(value, nm_device_vxlan_get_l3miss(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_vxlan = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_VXLAN, + nm_device_vxlan_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_U("Ageing", PROP_AGEING, NMDeviceVxlan, _priv.ageing), + NML_DBUS_META_PROPERTY_INIT_Q("DstPort", PROP_DST_PORT, NMDeviceVxlan, _priv.dst_port), + NML_DBUS_META_PROPERTY_INIT_S("Group", PROP_GROUP, NMDeviceVxlan, _priv.group), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_U("Id", PROP_ID, NMDeviceVxlan, _priv.id), + NML_DBUS_META_PROPERTY_INIT_B("L2miss", PROP_L2MISS, NMDeviceVxlan, _priv.l2miss), + NML_DBUS_META_PROPERTY_INIT_B("L3miss", PROP_L3MISS, NMDeviceVxlan, _priv.l3miss), + NML_DBUS_META_PROPERTY_INIT_B("Learning", PROP_LEARNING, NMDeviceVxlan, _priv.learning), + NML_DBUS_META_PROPERTY_INIT_U("Limit", PROP_LIMIT, NMDeviceVxlan, _priv.limit), + NML_DBUS_META_PROPERTY_INIT_S("Local", PROP_LOCAL, NMDeviceVxlan, _priv.local), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Parent", + PROP_PARENT, + NMDeviceVxlan, + _priv.parent, + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_B("Proxy", PROP_PROXY, NMDeviceVxlan, _priv.proxy), + NML_DBUS_META_PROPERTY_INIT_B("Rsc", PROP_RSC, NMDeviceVxlan, _priv.rsc), + NML_DBUS_META_PROPERTY_INIT_Q("SrcPortMax", + PROP_SRC_PORT_MAX, + NMDeviceVxlan, + _priv.src_port_max), + NML_DBUS_META_PROPERTY_INIT_Q("SrcPortMin", + PROP_SRC_PORT_MIN, + NMDeviceVxlan, + _priv.src_port_min), + NML_DBUS_META_PROPERTY_INIT_Y("Tos", PROP_TOS, NMDeviceVxlan, _priv.tos), + NML_DBUS_META_PROPERTY_INIT_Y("Ttl", PROP_TTL, NMDeviceVxlan, _priv.ttl), ), ); + +static void +nm_device_vxlan_class_init(NMDeviceVxlanClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceVxlan); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceVxlanPrivate, parent); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceVxlan:carrier: + * + * Whether the device has carrier. + * + * Since: 1.2 + * + * This property is not implemented yet, and the property is always FALSE. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_VXLAN_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:parent: + * + * The devices's parent device. + * + * Since: 1.2 + **/ + obj_properties[PROP_PARENT] = g_param_spec_object(NM_DEVICE_VXLAN_PARENT, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:id: + * + * The device's VXLAN ID. + * + * Since: 1.2 + **/ + obj_properties[PROP_ID] = g_param_spec_uint(NM_DEVICE_VXLAN_ID, + "", + "", + 0, + (1 << 24) - 1, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:group: + * + * The unicast destination IP address used in outgoing packets when the + * destination link layer address is not known in the VXLAN device + * forwarding database or the multicast IP address joined. + * + * Since: 1.2 + */ + obj_properties[PROP_GROUP] = g_param_spec_string(NM_DEVICE_VXLAN_GROUP, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:local: + * + * The source IP address to use in outgoing packets. + * + * Since: 1.2 + */ + obj_properties[PROP_LOCAL] = g_param_spec_string(NM_DEVICE_VXLAN_LOCAL, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:tos: + * + * The TOS value to use in outgoing packets. + * + * Since: 1.2 + */ + obj_properties[PROP_TOS] = g_param_spec_uchar(NM_DEVICE_VXLAN_TOS, + "", + "", + 0, + 255, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:ttl: + * + * The time-to-live value to use in outgoing packets. + * + * Since: 1.2 + */ + obj_properties[PROP_TTL] = g_param_spec_uchar(NM_DEVICE_VXLAN_TTL, + "", + "", + 0, + 255, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:learning: + * + * Whether unknown source link layer addresses and IP addresses are entered + * into the VXLAN device forwarding database. + * + * Since: 1.2 + */ + obj_properties[PROP_LEARNING] = g_param_spec_boolean(NM_DEVICE_VXLAN_LEARNING, + "", + "", + TRUE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:ageing: + * + * The lifetime in seconds of FDB entries learnt by the kernel. + * + * Since: 1.2 + */ + obj_properties[PROP_AGEING] = g_param_spec_uint(NM_DEVICE_VXLAN_AGEING, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:limit: + * + * The maximum number of entries that can be added to the forwarding table. + * + * Since: 1.2 + */ + obj_properties[PROP_LIMIT] = g_param_spec_uint(NM_DEVICE_VXLAN_LIMIT, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:dst-port: + * + * The UDP destination port used to communicate with the remote VXLAN tunnel + * endpoint. + * + * Since: 1.2 + */ + obj_properties[PROP_DST_PORT] = g_param_spec_uint(NM_DEVICE_VXLAN_DST_PORT, + "", + "", + 0, + 65535, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:src-port-min: + * + * The minimum UDP source port used to communicate with the remote VXLAN + * tunnel endpoint. + * + * Since: 1.2 + */ + obj_properties[PROP_SRC_PORT_MIN] = + g_param_spec_uint(NM_DEVICE_VXLAN_SRC_PORT_MIN, + "", + "", + 0, + 65535, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:src-port-max: + * + * The maximum UDP source port used to communicate with the remote VXLAN + * tunnel endpoint. + * + * Since: 1.2 + */ + obj_properties[PROP_SRC_PORT_MAX] = + g_param_spec_uint(NM_DEVICE_VXLAN_SRC_PORT_MAX, + "", + "", + 0, + 65535, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:proxy: + * + * Whether ARP proxy is turned on. + * + * Since: 1.2 + */ + obj_properties[PROP_PROXY] = g_param_spec_boolean(NM_DEVICE_VXLAN_PROXY, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:rsc: + * + * Whether route short circuit is turned on. + * + * Since: 1.2 + */ + obj_properties[PROP_RSC] = g_param_spec_boolean(NM_DEVICE_VXLAN_RSC, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:l2miss: + * + * Whether netlink LL ADDR miss notifications are generated. + * + * Since: 1.2 + */ + obj_properties[PROP_L2MISS] = g_param_spec_boolean(NM_DEVICE_VXLAN_L2MISS, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:l3miss: + * + * Whether netlink IP ADDR miss notifications are generated. + * + * Since: 1.2 + */ + obj_properties[PROP_L3MISS] = g_param_spec_boolean(NM_DEVICE_VXLAN_L3MISS, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_vxlan); +} diff --git a/src/libnm-client-impl/nm-device-wifi-p2p.c b/src/libnm-client-impl/nm-device-wifi-p2p.c new file mode 100644 index 00000000..2daaf774 --- /dev/null +++ b/src/libnm-client-impl/nm-device-wifi-p2p.c @@ -0,0 +1,414 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018 - 2019 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-wifi-p2p.h" + +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "nm-setting-connection.h" +#include "nm-setting-wifi-p2p.h" +#include "nm-utils.h" +#include "nm-wifi-p2p-peer.h" +#include "nm-object-private.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-dbus-helpers.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PEERS, ); + +enum { + PEER_ADDED, + PEER_REMOVED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +typedef struct { + NMLDBusPropertyAO peers; +} NMDeviceWifiP2PPrivate; + +struct _NMDeviceWifiP2P { + NMDevice parent; + NMDeviceWifiP2PPrivate _priv; +}; + +struct _NMDeviceWifiP2PClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceWifiP2P, nm_device_wifi_p2p, NM_TYPE_DEVICE) + +#define NM_DEVICE_WIFI_P2P_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceWifiP2P, NM_IS_DEVICE_WIFI_P2P, NMDevice, NMObject) + +/*****************************************************************************/ + +/** + * nm_device_wifi_p2p_get_hw_address: (skip) + * @device: a #NMDeviceWifiP2P + * + * Gets the actual hardware (MAC) address of the #NMDeviceWifiP2P + * + * Returns: the actual hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.16 + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_wifi_p2p_get_hw_address(NMDeviceWifiP2P *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI_P2P(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_wifi_p2p_get_peers: + * @device: a #NMDeviceWifiP2P + * + * Gets all the found peers of the #NMDeviceWifiP2P. + * + * Returns: (element-type NMWifiP2PPeer): a #GPtrArray containing all the + * found #NMWifiP2PPeers. + * The returned array is owned by the client and should not be modified. + * + * Since: 1.16 + **/ +const GPtrArray * +nm_device_wifi_p2p_get_peers(NMDeviceWifiP2P *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI_P2P(device), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_DEVICE_WIFI_P2P_GET_PRIVATE(device)->peers); +} + +/** + * nm_device_wifi_p2p_get_peer_by_path: + * @device: a #NMDeviceWifiP2P + * @path: the object path of the peer + * + * Gets a #NMWifiP2PPeer by path. + * + * Returns: (transfer none): the peer or %NULL if none is found. + * + * Since: 1.16 + **/ +NMWifiP2PPeer * +nm_device_wifi_p2p_get_peer_by_path(NMDeviceWifiP2P *device, const char *path) +{ + const GPtrArray *peers; + int i; + NMWifiP2PPeer * peer = NULL; + + g_return_val_if_fail(NM_IS_DEVICE_WIFI_P2P(device), NULL); + g_return_val_if_fail(path != NULL, NULL); + + peers = nm_device_wifi_p2p_get_peers(device); + if (!peers) + return NULL; + + for (i = 0; i < peers->len; i++) { + NMWifiP2PPeer *candidate = g_ptr_array_index(peers, i); + if (!strcmp(nm_object_get_path(NM_OBJECT(candidate)), path)) { + peer = candidate; + break; + } + } + + return peer; +} + +/** + * nm_device_wifi_p2p_start_find: + * @device: a #NMDeviceWifiP2P + * @options: (allow-none): optional options passed to StartFind. + * @cancellable: a #GCancellable, or %NULL + * @callback: a #GAsyncReadyCallback, or %NULL + * @user_data: user_data for @callback + * + * Request NM to search for Wi-Fi P2P peers on @device. Note that the call + * returns immediately after requesting the find, and it may take some time + * after that for peers to be found. + * + * The find operation will run for 30s by default. You can stop it earlier + * using nm_device_p2p_wifi_stop_find(). + * + * Since: 1.16 + **/ +void +nm_device_wifi_p2p_start_find(NMDeviceWifiP2P * device, + GVariant * options, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_DEVICE_WIFI_P2P(device)); + g_return_if_fail(!options || g_variant_is_of_type(options, G_VARIANT_TYPE_VARDICT)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + if (!options) + options = nm_g_variant_singleton_aLsvI(); + + _nm_client_dbus_call(_nm_object_get_client(device), + device, + nm_device_wifi_p2p_start_find, + cancellable, + callback, + user_data, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE_WIFI_P2P, + "StartFind", + g_variant_new("(@a{sv})", options), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_cb); +} + +/** + * nm_device_wifi_p2p_start_find_finish: + * @device: a #NMDeviceWifiP2P + * @result: the #GAsyncResult + * @error: #GError return address + * + * Finish an operation started by nm_device_wifi_p2p_start_find(). + * + * Returns: %TRUE if the call was successful + * + * Since: 1.16 + **/ +gboolean +nm_device_wifi_p2p_start_find_finish(NMDeviceWifiP2P *device, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI_P2P(device), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_wifi_p2p_start_find), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/** + * nm_device_wifi_p2p_stop_find: + * @device: a #NMDeviceWifiP2P + * @cancellable: a #GCancellable, or %NULL + * @callback: a #GAsyncReadyCallback, or %NULL + * @user_data: user_data for @callback + * + * Request NM to stop any ongoing find operation for Wi-Fi P2P peers on @device. + * + * Since: 1.16 + **/ +void +nm_device_wifi_p2p_stop_find(NMDeviceWifiP2P * device, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_DEVICE_WIFI_P2P(device)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(_nm_object_get_client(device), + device, + nm_device_wifi_p2p_stop_find, + cancellable, + callback, + user_data, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE_WIFI_P2P, + "StopFind", + g_variant_new("()"), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_cb); +} + +/** + * nm_device_wifi_p2p_stop_find_finish: + * @device: a #NMDeviceWifiP2P + * @result: the #GAsyncResult + * @error: #GError return address + * + * Finish an operation started by nm_device_wifi_p2p_stop_find(). + * + * Returns: %TRUE if the call was successful + * + * Since: 1.16 + **/ +gboolean +nm_device_wifi_p2p_stop_find_finish(NMDeviceWifiP2P *device, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI_P2P(device), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_wifi_p2p_stop_find), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_wifi_p2p_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_WIFI_P2P_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a Wi-Fi P2P connection.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_WIRELESS; +} + +static const char * +get_type_description(NMDevice *device) +{ + return "wifi-p2p"; +} + +/*****************************************************************************/ + +static void +_property_ao_notify_changed_peers_cb(NMLDBusPropertyAO *pr_ao, + NMClient * client, + NMObject * nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal(client, + G_OBJECT(pr_ao->owner_dbobj->nmobj), + nmobj, + is_added, + 10, + is_added ? signals[PEER_ADDED] + : signals[PEER_REMOVED]); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P(object); + + switch (prop_id) { + case PROP_PEERS: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_device_wifi_p2p_get_peers(self))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_device_wifi_p2p_init(NMDeviceWifiP2P *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wifip2p = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_WIFI_P2P, + nm_device_wifi_p2p_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Peers", + PROP_PEERS, + NMDeviceWifiP2P, + _priv.peers, + nm_wifi_p2p_peer_get_type, + .notify_changed_ao = + _property_ao_notify_changed_peers_cb), ), ); + +static void +nm_device_wifi_p2p_class_init(NMDeviceWifiP2PClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceWifiP2P); + + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMDeviceWifiP2PPrivate, peers); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + device_class->get_type_description = get_type_description; + + /** + * NMDeviceWifiP2P:peers: (type GPtrArray(NMWifiP2PPeer)) + * + * List of all Wi-Fi P2P peers the device can see. + * + * Since: 1.16 + **/ + obj_properties[PROP_PEERS] = g_param_spec_boxed(NM_DEVICE_WIFI_P2P_PEERS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_wifip2p); + + /** + * NMDeviceWifiP2P::peer-added: + * @device: the Wi-Fi P2P device that received the signal + * @peer: the new access point + * + * Notifies that a #NMWifiP2PPeer is added to the Wi-Fi P2P device. + * + * Since: 1.16 + **/ + signals[PEER_ADDED] = g_signal_new("peer-added", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + /** + * NMDeviceWifiP2P::peer-removed: + * @device: the Wi-Fi P2P device that received the signal + * @peer: the removed access point + * + * Notifies that a #NMWifiP2PPeer is removed from the Wi-Fi P2P device. + * + * Since: 1.16 + **/ + signals[PEER_REMOVED] = g_signal_new("peer-removed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); +} diff --git a/src/libnm-client-impl/nm-device-wifi.c b/src/libnm-client-impl/nm-device-wifi.c new file mode 100644 index 00000000..fee7e8a2 --- /dev/null +++ b/src/libnm-client-impl/nm-device-wifi.c @@ -0,0 +1,789 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-wifi.h" + +#include <linux/if_ether.h> + +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "nm-setting-connection.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" +#include "nm-utils.h" +#include "nm-access-point.h" +#include "nm-object-private.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-dbus-helpers.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PERM_HW_ADDRESS, + PROP_MODE, + PROP_BITRATE, + PROP_ACCESS_POINTS, + PROP_ACTIVE_ACCESS_POINT, + PROP_WIRELESS_CAPABILITIES, + PROP_LAST_SCAN, ); + +typedef struct { + NMLDBusPropertyAO access_points; + NMLDBusPropertyO active_access_point; + char * perm_hw_address; + gint64 last_scan; + guint32 mode; + guint32 bitrate; + guint32 wireless_capabilities; +} NMDeviceWifiPrivate; + +enum { + ACCESS_POINT_ADDED, + ACCESS_POINT_REMOVED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +struct _NMDeviceWifi { + NMDevice parent; + NMDeviceWifiPrivate _priv; +}; + +struct _NMDeviceWifiClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE) + +#define NM_DEVICE_WIFI_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceWifi, NM_IS_DEVICE_WIFI, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_wifi_get_hw_address: (skip) + * @device: a #NMDeviceWifi + * + * Gets the actual hardware (MAC) address of the #NMDeviceWifi + * + * Returns: the actual hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_wifi_get_hw_address(NMDeviceWifi *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_wifi_get_permanent_hw_address: + * @device: a #NMDeviceWifi + * + * Gets the permanent hardware (MAC) address of the #NMDeviceWifi + * + * Returns: the permanent hardware address. This is the internal string used by the + * device, and must not be modified. + **/ +const char * +nm_device_wifi_get_permanent_hw_address(NMDeviceWifi *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_WIFI_GET_PRIVATE(device)->perm_hw_address); +} + +/** + * nm_device_wifi_get_mode: + * @device: a #NMDeviceWifi + * + * Gets the #NMDeviceWifi mode. + * + * Returns: the mode + **/ +NM80211Mode +nm_device_wifi_get_mode(NMDeviceWifi *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), 0); + + return NM_DEVICE_WIFI_GET_PRIVATE(device)->mode; +} + +/** + * nm_device_wifi_get_bitrate: + * @device: a #NMDeviceWifi + * + * Gets the bit rate of the #NMDeviceWifi in kbit/s. + * + * Returns: the bit rate (kbit/s) + **/ +guint32 +nm_device_wifi_get_bitrate(NMDeviceWifi *device) +{ + NMDeviceState state; + + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), 0); + + state = nm_device_get_state(NM_DEVICE(device)); + switch (state) { + case NM_DEVICE_STATE_IP_CONFIG: + case NM_DEVICE_STATE_IP_CHECK: + case NM_DEVICE_STATE_SECONDARIES: + case NM_DEVICE_STATE_ACTIVATED: + case NM_DEVICE_STATE_DEACTIVATING: + break; + default: + return 0; + } + + return NM_DEVICE_WIFI_GET_PRIVATE(device)->bitrate; +} + +/** + * nm_device_wifi_get_capabilities: + * @device: a #NMDeviceWifi + * + * Gets the Wi-Fi capabilities of the #NMDeviceWifi. + * + * Returns: the capabilities + **/ +NMDeviceWifiCapabilities +nm_device_wifi_get_capabilities(NMDeviceWifi *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), 0); + + return NM_DEVICE_WIFI_GET_PRIVATE(device)->wireless_capabilities; +} + +/** + * nm_device_wifi_get_active_access_point: + * @device: a #NMDeviceWifi + * + * Gets the active #NMAccessPoint. + * + * Returns: (transfer none): the access point or %NULL if none is active + **/ +NMAccessPoint * +nm_device_wifi_get_active_access_point(NMDeviceWifi *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), NULL); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_WIFI_GET_PRIVATE(device)->active_access_point); +} + +/** + * nm_device_wifi_get_access_points: + * @device: a #NMDeviceWifi + * + * Gets all the scanned access points of the #NMDeviceWifi. + * + * Returns: (element-type NMAccessPoint): a #GPtrArray containing all the + * scanned #NMAccessPoints. + * The returned array is owned by the client and should not be modified. + **/ +const GPtrArray * +nm_device_wifi_get_access_points(NMDeviceWifi *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_DEVICE_WIFI_GET_PRIVATE(device)->access_points); +} + +/** + * nm_device_wifi_get_access_point_by_path: + * @device: a #NMDeviceWifi + * @path: the object path of the access point + * + * Gets a #NMAccessPoint by path. + * + * Returns: (transfer none): the access point or %NULL if none is found. + **/ +NMAccessPoint * +nm_device_wifi_get_access_point_by_path(NMDeviceWifi *device, const char *path) +{ + const GPtrArray *aps; + int i; + NMAccessPoint * ap = NULL; + + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), NULL); + g_return_val_if_fail(path != NULL, NULL); + + aps = nm_device_wifi_get_access_points(device); + if (!aps) + return NULL; + + for (i = 0; i < aps->len; i++) { + NMAccessPoint *candidate = g_ptr_array_index(aps, i); + if (!strcmp(nm_object_get_path(NM_OBJECT(candidate)), path)) { + ap = candidate; + break; + } + } + + return ap; +} + +/** + * nm_device_wifi_get_last_scan: + * @device: a #NMDeviceWifi + * + * Returns the timestamp (in CLOCK_BOOTTIME milliseconds) for the last finished + * network scan. A value of -1 means the device never scanned for access points. + * + * Use nm_utils_get_timestamp_msec() to obtain current time value suitable for + * comparing to this value. + * + * Returns: the last scan time in seconds + * + * Since: 1.12 + **/ +gint64 +nm_device_wifi_get_last_scan(NMDeviceWifi *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), -1); + + return NM_DEVICE_WIFI_GET_PRIVATE(device)->last_scan; +} + +/** + * nm_device_wifi_request_scan: + * @device: a #NMDeviceWifi + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Request NM to scan for access points on @device. Note that the function + * returns immediately after requesting the scan, and it may take some time + * after that for the scan to complete. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be + * set. + * + * Deprecated: 1.22: Use nm_device_wifi_request_scan_async() or GDBusConnection. + **/ +gboolean +nm_device_wifi_request_scan(NMDeviceWifi *device, GCancellable *cancellable, GError **error) +{ + return nm_device_wifi_request_scan_options(device, NULL, cancellable, error); +} + +/** + * nm_device_wifi_request_scan_options: + * @device: a #NMDeviceWifi + * @options: dictionary with options for RequestScan(), or %NULL + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Request NM to scan for access points on @device. Note that the function + * returns immediately after requesting the scan, and it may take some time + * after that for the scan to complete. + * This is the same as @nm_device_wifi_request_scan except it accepts @options + * for the scanning. The argument is the dictionary passed to RequestScan() + * D-Bus call. Valid options inside the dictionary are: + * 'ssids' => array of SSIDs (saay) + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be + * set. + * + * Since: 1.2 + * + * Deprecated: 1.22: Use nm_device_wifi_request_scan_options_async() or GDBusConnection. + **/ +gboolean +nm_device_wifi_request_scan_options(NMDeviceWifi *device, + GVariant * options, + GCancellable *cancellable, + GError ** error) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), FALSE); + g_return_val_if_fail(!options || g_variant_is_of_type(options, G_VARIANT_TYPE_VARDICT), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + if (!options) + options = nm_g_variant_singleton_aLsvI(); + + return _nm_client_dbus_call_sync_void(_nm_object_get_client(device), + cancellable, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE_WIRELESS, + "RequestScan", + g_variant_new("(@a{sv})", options), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +NM_BACKPORT_SYMBOL( + libnm_1_0_6, + gboolean, + nm_device_wifi_request_scan_options, + (NMDeviceWifi * device, GVariant *options, GCancellable *cancellable, GError **error), + (device, options, cancellable, error)); + +/** + * nm_device_wifi_request_scan_async: + * @device: a #NMDeviceWifi + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the scan has been requested + * @user_data: caller-specific data passed to @callback + * + * Request NM to scan for access points on @device. Note that @callback will be + * called immediately after requesting the scan, and it may take some time after + * that for the scan to complete. + **/ +void +nm_device_wifi_request_scan_async(NMDeviceWifi * device, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + nm_device_wifi_request_scan_options_async(device, NULL, cancellable, callback, user_data); +} + +/** + * nm_device_wifi_request_scan_options_async: + * @device: a #NMDeviceWifi + * @options: dictionary with options for RequestScan(), or %NULL + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the scan has been requested + * @user_data: caller-specific data passed to @callback + * + * Request NM to scan for access points on @device. Note that @callback will be + * called immediately after requesting the scan, and it may take some time after + * that for the scan to complete. + * This is the same as @nm_device_wifi_request_scan_async except it accepts @options + * for the scanning. The argument is the dictionary passed to RequestScan() + * D-Bus call. Valid options inside the dictionary are: + * 'ssids' => array of SSIDs (saay) + * + * To complete the request call nm_device_wifi_request_scan_finish(). + * + * Since: 1.2 + **/ +void +nm_device_wifi_request_scan_options_async(NMDeviceWifi * device, + GVariant * options, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_DEVICE_WIFI(device)); + g_return_if_fail(!options || g_variant_is_of_type(options, G_VARIANT_TYPE_VARDICT)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + if (!options) + options = nm_g_variant_singleton_aLsvI(); + + _nm_client_dbus_call(_nm_object_get_client(device), + device, + nm_device_wifi_request_scan_async, + cancellable, + callback, + user_data, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE_WIRELESS, + "RequestScan", + g_variant_new("(@a{sv})", options), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +NM_BACKPORT_SYMBOL(libnm_1_0_6, + void, + nm_device_wifi_request_scan_options_async, + (NMDeviceWifi * device, + GVariant * options, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data), + (device, options, cancellable, callback, user_data)); + +/** + * nm_device_wifi_request_scan_finish: + * @device: a #NMDeviceWifi + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_device_wifi_request_scan_async() and + * nm_device_wifi_request_scan_options_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be + * set. + **/ +gboolean +nm_device_wifi_request_scan_finish(NMDeviceWifi *device, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_wifi_request_scan_async), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +#define WPA_CAPS \ + (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_WPA \ + | NM_WIFI_DEVICE_CAP_RSN) + +#define RSN_CAPS (NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_RSN) + +static gboolean +has_proto(NMSettingWirelessSecurity *s_wsec, const char *proto) +{ + int i; + + for (i = 0; i < nm_setting_wireless_security_get_num_protos(s_wsec); i++) { + if (g_strcmp0(proto, nm_setting_wireless_security_get_proto(s_wsec, i)) == 0) + return TRUE; + } + return FALSE; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity *s_wsec; + const char * hwaddr, *setting_hwaddr; + NMDeviceWifiCapabilities wifi_caps; + const char * key_mgmt; + + if (!NM_DEVICE_CLASS(nm_device_wifi_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_WIRELESS_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a Wi-Fi connection.")); + return FALSE; + } + + /* Check MAC address */ + hwaddr = nm_device_wifi_get_permanent_hw_address(NM_DEVICE_WIFI(device)); + if (hwaddr) { + if (!nm_utils_hwaddr_valid(hwaddr, ETH_ALEN)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + _("Invalid device MAC address.")); + return FALSE; + } + s_wifi = nm_connection_get_setting_wireless(connection); + setting_hwaddr = nm_setting_wireless_get_mac_address(s_wifi); + if (setting_hwaddr && !nm_utils_hwaddr_matches(setting_hwaddr, -1, hwaddr, -1)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The MACs of the device and the connection didn't match.")); + return FALSE; + } + } + + /* Check device capabilities; we assume all devices can do WEP at least */ + + s_wsec = nm_connection_get_setting_wireless_security(connection); + if (s_wsec) { + /* Connection has security, verify it against the device's capabilities */ + key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec); + if (nm_streq(key_mgmt, "wpa-psk") || nm_streq(key_mgmt, "wpa-eap") + || nm_streq(key_mgmt, "wpa-eap-suite-b-192")) { + wifi_caps = nm_device_wifi_get_capabilities(NM_DEVICE_WIFI(device)); + + /* Is device only WEP capable? */ + if (!(wifi_caps & WPA_CAPS)) { + g_set_error_literal( + error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The device is lacking WPA capabilities required by the connection.")); + return FALSE; + } + + /* Make sure WPA2/RSN-only connections don't get chosen for WPA-only cards */ + if (has_proto(s_wsec, "rsn") && !has_proto(s_wsec, "wpa") && !(wifi_caps & RSN_CAPS)) { + g_set_error_literal( + error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The device is lacking WPA2/RSN capabilities required by the connection.")); + return FALSE; + } + } + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_WIRELESS; +} + +/*****************************************************************************/ + +static void +_property_ao_notify_changed_access_points_cb(NMLDBusPropertyAO *pr_ao, + NMClient * client, + NMObject * nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal(client, + G_OBJECT(pr_ao->owner_dbobj->nmobj), + nmobj, + is_added, + 10, + is_added ? signals[ACCESS_POINT_ADDED] + : signals[ACCESS_POINT_REMOVED]); +} + +/*****************************************************************************/ + +static void +nm_device_wifi_init(NMDeviceWifi *device) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(device); + + priv->last_scan = -1; +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI(object); + + switch (prop_id) { + case PROP_PERM_HW_ADDRESS: + g_value_set_string(value, nm_device_wifi_get_permanent_hw_address(self)); + break; + case PROP_MODE: + g_value_set_enum(value, nm_device_wifi_get_mode(self)); + break; + case PROP_BITRATE: + g_value_set_uint(value, nm_device_wifi_get_bitrate(self)); + break; + case PROP_ACTIVE_ACCESS_POINT: + g_value_set_object(value, nm_device_wifi_get_active_access_point(self)); + break; + case PROP_WIRELESS_CAPABILITIES: + g_value_set_flags(value, nm_device_wifi_get_capabilities(self)); + break; + case PROP_ACCESS_POINTS: + g_value_take_boxed(value, + _nm_utils_copy_object_array(nm_device_wifi_get_access_points(self))); + break; + case PROP_LAST_SCAN: + g_value_set_int64(value, nm_device_wifi_get_last_scan(self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +finalize(GObject *object) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(object); + + g_free(priv->perm_hw_address); + + G_OBJECT_CLASS(nm_device_wifi_parent_class)->finalize(object); +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wireless = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_WIRELESS, + nm_device_wifi_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_AO_PROP("AccessPoints", + PROP_ACCESS_POINTS, + NMDeviceWifi, + _priv.access_points, + nm_access_point_get_type, + .notify_changed_ao = + _property_ao_notify_changed_access_points_cb), + NML_DBUS_META_PROPERTY_INIT_O_PROP("ActiveAccessPoint", + PROP_ACTIVE_ACCESS_POINT, + NMDeviceWifi, + _priv.active_access_point, + nm_access_point_get_type), + NML_DBUS_META_PROPERTY_INIT_U("Bitrate", PROP_BITRATE, NMDeviceWifi, _priv.bitrate), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_X("LastScan", PROP_LAST_SCAN, NMDeviceWifi, _priv.last_scan), + NML_DBUS_META_PROPERTY_INIT_U("Mode", PROP_MODE, NMDeviceWifi, _priv.mode), + NML_DBUS_META_PROPERTY_INIT_S("PermHwAddress", + PROP_PERM_HW_ADDRESS, + NMDeviceWifi, + _priv.perm_hw_address), + NML_DBUS_META_PROPERTY_INIT_U("WirelessCapabilities", + PROP_WIRELESS_CAPABILITIES, + NMDeviceWifi, + _priv.wireless_capabilities), ), ); + +static void +nm_device_wifi_class_init(NMDeviceWifiClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceWifi); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, + NMDeviceWifiPrivate, + active_access_point); + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMDeviceWifiPrivate, access_points); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceWifi:perm-hw-address: + * + * The hardware (MAC) address of the device. + **/ + obj_properties[PROP_PERM_HW_ADDRESS] = + g_param_spec_string(NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWifi:mode: + * + * The mode of the device. + **/ + obj_properties[PROP_MODE] = g_param_spec_enum(NM_DEVICE_WIFI_MODE, + "", + "", + NM_TYPE_802_11_MODE, + NM_802_11_MODE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWifi:bitrate: + * + * The bit rate of the device in kbit/s. + **/ + obj_properties[PROP_BITRATE] = g_param_spec_uint(NM_DEVICE_WIFI_BITRATE, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWifi:active-access-point: + * + * The active #NMAccessPoint of the device. + **/ + obj_properties[PROP_ACTIVE_ACCESS_POINT] = + g_param_spec_object(NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT, + "", + "", + NM_TYPE_ACCESS_POINT, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWifi:wireless-capabilities: + * + * The wireless capabilities of the device. + **/ + obj_properties[PROP_WIRELESS_CAPABILITIES] = + g_param_spec_flags(NM_DEVICE_WIFI_CAPABILITIES, + "", + "", + NM_TYPE_DEVICE_WIFI_CAPABILITIES, + NM_WIFI_DEVICE_CAP_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWifi:access-points: (type GPtrArray(NMAccessPoint)) + * + * List of all Wi-Fi access points the device can see. + **/ + obj_properties[PROP_ACCESS_POINTS] = + g_param_spec_boxed(NM_DEVICE_WIFI_ACCESS_POINTS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWifi:last-scan: + * + * The timestamp (in CLOCK_BOOTTIME seconds) for the last finished + * network scan. A value of -1 means the device never scanned for + * access points. + * + * Since: 1.12 + **/ + obj_properties[PROP_LAST_SCAN] = g_param_spec_int64(NM_DEVICE_WIFI_LAST_SCAN, + "", + "", + -1, + G_MAXINT64, + -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_wireless); + + /** + * NMDeviceWifi::access-point-added: + * @device: the Wi-Fi device that received the signal + * @ap: the new access point + * + * Notifies that a #NMAccessPoint is added to the Wi-Fi device. + **/ + signals[ACCESS_POINT_ADDED] = g_signal_new("access-point-added", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + /** + * NMDeviceWifi::access-point-removed: + * @device: the Wi-Fi device that received the signal + * @ap: the removed access point + * + * Notifies that a #NMAccessPoint is removed from the Wi-Fi device. + **/ + signals[ACCESS_POINT_REMOVED] = g_signal_new("access-point-removed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); +} diff --git a/src/libnm-client-impl/nm-device-wimax.c b/src/libnm-client-impl/nm-device-wimax.c new file mode 100644 index 00000000..b9db20bb --- /dev/null +++ b/src/libnm-client-impl/nm-device-wimax.c @@ -0,0 +1,389 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2011 - 2012 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-wimax.h" + +#include "nm-wimax-nsp.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_HW_ADDRESS, + PROP_ACTIVE_NSP, + PROP_CENTER_FREQ, + PROP_RSSI, + PROP_CINR, + PROP_TX_POWER, + PROP_BSID, + PROP_NSPS, ); + +enum { + NSP_ADDED, + NSP_REMOVED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +struct _NMDeviceWimax { + NMDevice parent; +}; + +struct _NMDeviceWimaxClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceWimax, nm_device_wimax, NM_TYPE_DEVICE) + +#define NM_DEVICE_WIMAX_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceWimax, NM_IS_DEVICE_WIMAX, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_wimax_get_hw_address: + * @wimax: a #NMDeviceWimax + * + * Gets the hardware (MAC) address of the #NMDeviceWimax + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +const char * +nm_device_wimax_get_hw_address(NMDeviceWimax *wimax) +{ + g_return_val_if_reached(NULL); +} + +/** + * nm_device_wimax_get_active_nsp: + * @wimax: a #NMDeviceWimax + * + * Gets the active #NMWimaxNsp. + * + * Returns: (transfer full): the access point or %NULL if none is active + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +NMWimaxNsp * +nm_device_wimax_get_active_nsp(NMDeviceWimax *wimax) +{ + g_return_val_if_reached(NULL); +} + +/** + * nm_device_wimax_get_nsps: + * @wimax: a #NMDeviceWimax + * + * Gets all the scanned NSPs of the #NMDeviceWimax. + * + * Returns: (element-type NMWimaxNsp): a #GPtrArray containing + * all the scanned #NMWimaxNsps. + * The returned array is owned by the client and should not be modified. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +const GPtrArray * +nm_device_wimax_get_nsps(NMDeviceWimax *wimax) +{ + g_return_val_if_reached(NULL); +} + +/** + * nm_device_wimax_get_nsp_by_path: + * @wimax: a #NMDeviceWimax + * @path: the object path of the NSP + * + * Gets a #NMWimaxNsp by path. + * + * Returns: (transfer none): the access point or %NULL if none is found. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +NMWimaxNsp * +nm_device_wimax_get_nsp_by_path(NMDeviceWimax *wimax, const char *path) +{ + g_return_val_if_reached(NULL); +} + +/** + * nm_device_wimax_get_center_frequency: + * @self: a #NMDeviceWimax + * + * Gets the center frequency (in KHz) of the radio channel the device is using + * to communicate with the network when connected. Has no meaning when the + * device is not connected. + * + * Returns: the center frequency in KHz, or 0 + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +guint +nm_device_wimax_get_center_frequency(NMDeviceWimax *self) +{ + g_return_val_if_reached(0); +} + +/** + * nm_device_wimax_get_rssi: + * @self: a #NMDeviceWimax + * + * Gets the RSSI of the current radio link in dBm. This value indicates how + * strong the raw received RF signal from the base station is, but does not + * indicate the overall quality of the radio link. Has no meaning when the + * device is not connected. + * + * Returns: the RSSI in dBm, or 0 + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +int +nm_device_wimax_get_rssi(NMDeviceWimax *self) +{ + g_return_val_if_reached(0); +} + +/** + * nm_device_wimax_get_cinr: + * @self: a #NMDeviceWimax + * + * Gets the CINR (Carrier to Interference + Noise Ratio) of the current radio + * link in dB. CINR is a more accurate measure of radio link quality. Has no + * meaning when the device is not connected. + * + * Returns: the CINR in dB, or 0 + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +int +nm_device_wimax_get_cinr(NMDeviceWimax *self) +{ + g_return_val_if_reached(0); +} + +/** + * nm_device_wimax_get_tx_power: + * @self: a #NMDeviceWimax + * + * Average power of the last burst transmitted by the device, in units of + * 0.5 dBm. i.e. a TxPower of -11 represents an actual device TX power of + * -5.5 dBm. Has no meaning when the device is not connected. + * + * Returns: the TX power in dBm, or 0 + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +int +nm_device_wimax_get_tx_power(NMDeviceWimax *self) +{ + g_return_val_if_reached(0); +} + +/** + * nm_device_wimax_get_bsid: + * @self: a #NMDeviceWimax + * + * Gets the ID of the serving Base Station when the device is connected. + * + * Returns: the ID of the serving Base Station, or %NULL + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +const char * +nm_device_wimax_get_bsid(NMDeviceWimax *self) +{ + g_return_val_if_reached(0); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + g_return_if_reached(); +} + +static void +nm_device_wimax_init(NMDeviceWimax *device) +{ + g_return_if_reached(); +} + +static void +nm_device_wimax_class_init(NMDeviceWimaxClass *wimax_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(wimax_class); + + object_class->get_property = get_property; + + /** + * NMDeviceWimax:hw-address: + * + * The hardware (MAC) address of the device. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + obj_properties[PROP_HW_ADDRESS] = + g_param_spec_string(NM_DEVICE_WIMAX_HW_ADDRESS, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWimax:active-nsp: + * + * The active #NMWimaxNsp of the device. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + obj_properties[PROP_ACTIVE_NSP] = + g_param_spec_object(NM_DEVICE_WIMAX_ACTIVE_NSP, + "", + "", + NM_TYPE_WIMAX_NSP, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWimax:center-frequency: + * + * The center frequency (in KHz) of the radio channel the device is using to + * communicate with the network when connected. Has no meaning when the + * device is not connected. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + obj_properties[PROP_CENTER_FREQ] = g_param_spec_uint(NM_DEVICE_WIMAX_CENTER_FREQUENCY, + "", + "", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWimax:rssi: + * + * RSSI of the current radio link in dBm. This value indicates how strong + * the raw received RF signal from the base station is, but does not + * indicate the overall quality of the radio link. Has no meaning when the + * device is not connected. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + obj_properties[PROP_RSSI] = g_param_spec_int(NM_DEVICE_WIMAX_RSSI, + "", + "", + G_MININT, + G_MAXINT, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWimax:cinr: + * + * CINR (Carrier to Interference + Noise Ratio) of the current radio link + * in dB. CINR is a more accurate measure of radio link quality. Has no + * meaning when the device is not connected. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + obj_properties[PROP_CINR] = g_param_spec_int(NM_DEVICE_WIMAX_CINR, + "", + "", + G_MININT, + G_MAXINT, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWimax:tx-power: + * + * Average power of the last burst transmitted by the device, in units of + * 0.5 dBm. i.e. a TxPower of -11 represents an actual device TX power of + * -5.5 dBm. Has no meaning when the device is not connected. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + obj_properties[PROP_TX_POWER] = g_param_spec_int(NM_DEVICE_WIMAX_TX_POWER, + "", + "", + G_MININT, + G_MAXINT, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWimax:bsid: + * + * The ID of the serving base station as received from the network. Has + * no meaning when the device is not connected. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + obj_properties[PROP_BSID] = g_param_spec_string(NM_DEVICE_WIMAX_BSID, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWimax:nsps: (type GPtrArray(NMWimaxNsp)) + * + * List of all WiMAX Network Service Providers the device can see. + **/ + obj_properties[PROP_NSPS] = g_param_spec_boxed(NM_DEVICE_WIMAX_NSPS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + /** + * NMDeviceWimax::nsp-added: + * @self: the wimax device that received the signal + * @nsp: the new NSP + * + * Notifies that a #NMWimaxNsp is added to the wimax device. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + signals[NSP_ADDED] = g_signal_new("nsp-added", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + /** + * NMDeviceWimax::nsp-removed: + * @self: the wimax device that received the signal + * @nsp: the removed NSP + * + * Notifies that a #NMWimaxNsp is removed from the wimax device. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + signals[NSP_REMOVED] = g_signal_new("nsp-removed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); +} diff --git a/src/libnm-client-impl/nm-device-wireguard.c b/src/libnm-client-impl/nm-device-wireguard.c new file mode 100644 index 00000000..cbbd904b --- /dev/null +++ b/src/libnm-client-impl/nm-device-wireguard.c @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018 Javier Arteaga <jarteaga@jbeta.is> + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-wireguard.h" + +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PUBLIC_KEY, PROP_LISTEN_PORT, PROP_FWMARK, ); + +typedef struct { + GBytes *public_key; + guint32 fwmark; + guint16 listen_port; +} NMDeviceWireGuardPrivate; + +struct _NMDeviceWireGuard { + NMDevice parent; + NMDeviceWireGuardPrivate _priv; +}; + +struct _NMDeviceWireGuardClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceWireGuard, nm_device_wireguard, NM_TYPE_DEVICE) + +#define NM_DEVICE_WIREGUARD_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceWireGuard, NM_IS_DEVICE_WIREGUARD, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_wireguard_get_public_key: + * @device: a #NMDeviceWireGuard + * + * Gets the public key for this interface + * + * Returns: (transfer none): the #GBytes containing the 32-byte public key + * + * Since: 1.14 + **/ +GBytes * +nm_device_wireguard_get_public_key(NMDeviceWireGuard *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIREGUARD(device), NULL); + + return NM_DEVICE_WIREGUARD_GET_PRIVATE(device)->public_key; +} + +/** + * nm_device_wireguard_get_listen_port: + * @device: a #NMDeviceWireGuard + * + * Gets the local UDP port this interface listens on + * + * Returns: UDP listen port + * + * Since: 1.14 + **/ +guint16 +nm_device_wireguard_get_listen_port(NMDeviceWireGuard *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIREGUARD(device), 0); + + return NM_DEVICE_WIREGUARD_GET_PRIVATE(device)->listen_port; +} + +/** + * nm_device_wireguard_get_fwmark: + * @device: a #NMDeviceWireGuard + * + * Gets the fwmark (firewall mark) for this interface. + * It can be used to set routing policy for outgoing encrypted packets. + * See: ip-rule(8) + * + * Returns: 0 if fwmark not in use, 32-bit fwmark value otherwise + * + * Since: 1.14 + **/ +guint32 +nm_device_wireguard_get_fwmark(NMDeviceWireGuard *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIREGUARD(device), 0); + + return NM_DEVICE_WIREGUARD_GET_PRIVATE(device)->fwmark; +} + +/***********************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceWireGuard *device = NM_DEVICE_WIREGUARD(object); + + switch (prop_id) { + case PROP_PUBLIC_KEY: + g_value_set_boxed(value, nm_device_wireguard_get_public_key(device)); + break; + case PROP_LISTEN_PORT: + g_value_set_uint(value, nm_device_wireguard_get_listen_port(device)); + break; + case PROP_FWMARK: + g_value_set_uint(value, nm_device_wireguard_get_fwmark(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nm_device_wireguard_init(NMDeviceWireGuard *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(object); + + g_bytes_unref(priv->public_key); + + G_OBJECT_CLASS(nm_device_wireguard_parent_class)->finalize(object); +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wireguard = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_WIREGUARD, + nm_device_wireguard_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_U("FwMark", PROP_FWMARK, NMDeviceWireGuard, _priv.fwmark), + NML_DBUS_META_PROPERTY_INIT_Q("ListenPort", + PROP_LISTEN_PORT, + NMDeviceWireGuard, + _priv.listen_port), + NML_DBUS_META_PROPERTY_INIT_AY("PublicKey", + PROP_PUBLIC_KEY, + NMDeviceWireGuard, + _priv.public_key), ), ); + +static void +nm_device_wireguard_class_init(NMDeviceWireGuardClass *wireguard_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(wireguard_class); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + /** + * NMDeviceWireGuard:public-key: + * + * 32-byte public key, derived from the current private key. + * + * Since: 1.14 + **/ + obj_properties[PROP_PUBLIC_KEY] = g_param_spec_boxed(NM_DEVICE_WIREGUARD_PUBLIC_KEY, + "", + "", + G_TYPE_BYTES, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWireGuard:listen-port: + * + * Local UDP listen port. + * Set to 0 to allow a random port to be chosen (default). + * + * Since: 1.14 + **/ + obj_properties[PROP_LISTEN_PORT] = g_param_spec_uint(NM_DEVICE_WIREGUARD_LISTEN_PORT, + "", + "", + 0, + G_MAXUINT16, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWireGuard:fwmark: + * + * Optional firewall mark - see ip-rule(8). + * Used when setting routing policy for outgoing encrypted packets. + * Set to 0 to disable the mark (default). + * + * Since: 1.14 + **/ + obj_properties[PROP_FWMARK] = g_param_spec_uint(NM_DEVICE_WIREGUARD_FWMARK, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_wireguard); +} diff --git a/src/libnm-client-impl/nm-device-wpan.c b/src/libnm-client-impl/nm-device-wpan.c new file mode 100644 index 00000000..30c1ea16 --- /dev/null +++ b/src/libnm-client-impl/nm-device-wpan.c @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018 Lubomir Rintel <lkundrak@v3.sk> + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-wpan.h" + +#include "nm-object-private.h" +#include "nm-setting-wpan.h" +#include "nm-setting-connection.h" + +/*****************************************************************************/ + +struct _NMDeviceWpan { + NMDevice parent; +}; + +struct _NMDeviceWpanClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceWpan, nm_device_wpan, NM_TYPE_DEVICE) +/*****************************************************************************/ + +/** + * nm_device_wpan_get_hw_address: (skip) + * @device: a #NMDeviceWpan + * + * Gets the active hardware (MAC) address of the #NMDeviceWpan + * + * Returns: the active hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_wpan_get_hw_address(NMDeviceWpan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WPAN(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_wpan_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_WPAN_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a wpan connection.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_WPAN; +} + +/*****************************************************************************/ + +static void +nm_device_wpan_init(NMDeviceWpan *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wpan = NML_DBUS_META_IFACE_INIT( + NM_DBUS_INTERFACE_DEVICE_WPAN, + nm_device_wpan_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), ), ); + +static void +nm_device_wpan_class_init(NMDeviceWpanClass *wpan_class) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS(wpan_class); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; +} diff --git a/src/libnm-client-impl/nm-device.c b/src/libnm-client-impl/nm-device.c new file mode 100644 index 00000000..4d097935 --- /dev/null +++ b/src/libnm-client-impl/nm-device.c @@ -0,0 +1,3080 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2018 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device.h" + +#include <libudev.h> + +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "nm-dbus-interface.h" +#include "nm-active-connection.h" +#include "nm-device-bt.h" +#include "nm-dhcp4-config.h" +#include "nm-dhcp6-config.h" +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" +#include "nm-object-private.h" +#include "nm-remote-connection.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-utils.h" +#include "nm-dbus-helpers.h" +#include "nm-device-tun.h" +#include "nm-setting-connection.h" +#include "libnm-udev-aux/nm-udev-utils.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMDevice, + PROP_INTERFACE, + PROP_UDI, + PROP_PATH, + PROP_DRIVER, + PROP_DRIVER_VERSION, + PROP_FIRMWARE_VERSION, + PROP_CAPABILITIES, + PROP_REAL, + PROP_MANAGED, + PROP_AUTOCONNECT, + PROP_FIRMWARE_MISSING, + PROP_NM_PLUGIN_MISSING, + PROP_IP4_CONFIG, + PROP_DHCP4_CONFIG, + PROP_IP6_CONFIG, + PROP_STATE, + PROP_STATE_REASON, + PROP_PRODUCT, + PROP_VENDOR, + PROP_DHCP6_CONFIG, + PROP_IP_INTERFACE, + PROP_DEVICE_TYPE, + PROP_ACTIVE_CONNECTION, + PROP_AVAILABLE_CONNECTIONS, + PROP_PHYSICAL_PORT_ID, + PROP_MTU, + PROP_METERED, + PROP_LLDP_NEIGHBORS, + PROP_IP4_CONNECTIVITY, + PROP_IP6_CONNECTIVITY, + PROP_INTERFACE_FLAGS, + PROP_HW_ADDRESS, ); + +enum { + STATE_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +enum { + PROPERTY_O_IDX_ACTIVE_CONNECTION, + PROPERTY_O_IDX_IP4_CONFIG, + PROPERTY_O_IDX_IP6_CONFIG, + PROPERTY_O_IDX_DHCP4_CONFIG, + PROPERTY_O_IDX_DHCP6_CONFIG, + _PROPERTY_O_IDX_NUM, +}; + +typedef struct _NMDevicePrivate { + NMLDBusPropertyO property_o[_PROPERTY_O_IDX_NUM]; + NMLDBusPropertyAO available_connections; + GPtrArray * lldp_neighbors; + char * driver; + char * driver_version; + char * hw_address; + char * interface; + char * ip_interface; + char * firmware_version; + char * physical_port_id; + char * udi; + char * path; + guint32 capabilities; + guint32 device_type; + guint32 ip4_connectivity; + guint32 ip6_connectivity; + guint32 metered; + guint32 mtu; + guint32 state; + guint32 state_reason; + guint32 interface_flags; + bool firmware_missing; + bool nm_plugin_missing; + bool autoconnect; + bool managed; + bool real; + + bool hw_address_is_new : 1; + + guint32 old_state; + + struct udev *udev; + char * type_description; + char * product; + char * vendor; + char * short_vendor; + char * description; + char * bus_name; + +} NMDevicePrivate; + +G_DEFINE_ABSTRACT_TYPE(NMDevice, nm_device, NM_TYPE_OBJECT); + +#define NM_DEVICE_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMDevice, NM_IS_DEVICE, NMObject) + +/*****************************************************************************/ + +static gboolean connection_compatible(NMDevice *device, NMConnection *connection, GError **error); +static NMLldpNeighbor *_nm_lldp_neighbor_dup(NMLldpNeighbor *neighbor); + +/*****************************************************************************/ + +struct _NMLldpNeighbor { + int refcount; + GHashTable *attrs; +}; + +G_DEFINE_BOXED_TYPE(NMLldpNeighbor, nm_lldp_neighbor, _nm_lldp_neighbor_dup, nm_lldp_neighbor_unref) + +/*****************************************************************************/ + +static void +nm_device_init(NMDevice *self) +{ + NMDevicePrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_DEVICE, NMDevicePrivate); + + self->_priv = priv; + + priv->old_state = NM_DEVICE_STATE_UNKNOWN; +} + +/*****************************************************************************/ + +static void +_notify_event_state_changed(NMClient *client, NMClientNotifyEventWithPtr *notify_event) +{ + gs_unref_object NMDevice *self = notify_event->user_data; + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + + NML_NMCLIENT_LOG_T(client, + "[%s] emit Device's StateChanged signal %u -> %u, reason: %u", + _nm_object_get_path(self), + (guint) priv->old_state, + (guint) priv->state, + (guint) priv->state_reason); + + g_signal_emit(self, + signals[STATE_CHANGED], + 0, + (guint) priv->state, + (guint) priv->old_state, + (guint) priv->state_reason); +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_state_reason(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMDevice * self = NM_DEVICE(dbobj->nmobj); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + guint32 new_state = NM_DEVICE_STATE_UNKNOWN; + guint32 reason = NM_DEVICE_STATE_REASON_NONE; + + /* We ignore the "State" property and the "StateChanged" signal of the device. + * This information is redundant to the "StateReason" property, and we rely + * on that one alone. In the best case, the information is identical. If it + * would not be, then we stick to the information from "StateReason" property. */ + + if (value) + g_variant_get(value, "(uu)", &new_state, &reason); + + if (priv->state == new_state && priv->state_reason == reason) { + /* no changes. */ + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; + } + + if (priv->state != new_state) { + priv->old_state = priv->state; + priv->state = new_state; + _nm_client_queue_notify_object(client, self, obj_properties[PROP_STATE]); + } + + if (priv->state_reason != reason) { + priv->state_reason = reason; + _nm_client_queue_notify_object(client, self, obj_properties[PROP_STATE_REASON]); + } + + _nm_client_notify_event_queue_with_ptr(client, + NM_CLIENT_NOTIFY_EVENT_PRIO_GPROP + 1, + _notify_event_state_changed, + g_object_ref(self)); + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_lldp_neighbors(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMDevice * self = NM_DEVICE(dbobj->nmobj); + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + gs_unref_ptrarray GPtrArray *old = NULL; + gs_unref_ptrarray GPtrArray *new = NULL; + GVariantIter * attrs_iter; + GVariantIter iter; + + new = g_ptr_array_new_with_free_func((GDestroyNotify) nm_lldp_neighbor_unref); + + if (value) { + g_variant_iter_init(&iter, value); + while (g_variant_iter_next(&iter, "a{sv}", &attrs_iter)) { + GVariant * attr_variant; + const char * attr_name; + NMLldpNeighbor *neigh; + + /* Note that there is no public API to mutate a NMLldpNeighbor instance. + * This is the only place where we actually mutate it. */ + neigh = nm_lldp_neighbor_new(); + while (g_variant_iter_next(attrs_iter, "{&sv}", &attr_name, &attr_variant)) { + if (attr_name[0] == '\0') { + g_variant_unref(attr_variant); + continue; + } + g_hash_table_insert(neigh->attrs, g_strdup(attr_name), attr_variant); + } + + g_ptr_array_add(new, neigh); + + g_variant_iter_free(attrs_iter); + } + } + + old = g_steal_pointer(&priv->lldp_neighbors); + priv->lldp_neighbors = g_steal_pointer(&new); + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/*****************************************************************************/ + +static NMDeviceType +coerce_type(NMDeviceType type) +{ + switch (type) { + case NM_DEVICE_TYPE_ETHERNET: + case NM_DEVICE_TYPE_WIFI: + case NM_DEVICE_TYPE_BT: + case NM_DEVICE_TYPE_OLPC_MESH: + case NM_DEVICE_TYPE_OVS_INTERFACE: + case NM_DEVICE_TYPE_OVS_PORT: + case NM_DEVICE_TYPE_OVS_BRIDGE: + case NM_DEVICE_TYPE_WIMAX: + case NM_DEVICE_TYPE_MODEM: + case NM_DEVICE_TYPE_INFINIBAND: + case NM_DEVICE_TYPE_BOND: + case NM_DEVICE_TYPE_TEAM: + case NM_DEVICE_TYPE_BRIDGE: + case NM_DEVICE_TYPE_VLAN: + case NM_DEVICE_TYPE_ADSL: + case NM_DEVICE_TYPE_MACSEC: + case NM_DEVICE_TYPE_MACVLAN: + case NM_DEVICE_TYPE_VXLAN: + case NM_DEVICE_TYPE_IP_TUNNEL: + case NM_DEVICE_TYPE_TUN: + case NM_DEVICE_TYPE_VETH: + case NM_DEVICE_TYPE_GENERIC: + case NM_DEVICE_TYPE_UNUSED1: + case NM_DEVICE_TYPE_UNUSED2: + case NM_DEVICE_TYPE_UNKNOWN: + case NM_DEVICE_TYPE_DUMMY: + case NM_DEVICE_TYPE_PPP: + case NM_DEVICE_TYPE_WPAN: + case NM_DEVICE_TYPE_6LOWPAN: + case NM_DEVICE_TYPE_WIREGUARD: + case NM_DEVICE_TYPE_WIFI_P2P: + case NM_DEVICE_TYPE_VRF: + return type; + } + return NM_DEVICE_TYPE_UNKNOWN; +} + +/*****************************************************************************/ + +static void +register_client(NMObject *nmobj, NMClient *client, NMLDBusObject *dbobj) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(nmobj); + + priv->udev = _nm_client_get_udev(client); + if (priv->udev) + udev_ref(priv->udev); + + NM_OBJECT_CLASS(nm_device_parent_class)->register_client(nmobj, client, dbobj); +} + +/*****************************************************************************/ + +static void +finalize(GObject *object) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(object); + + nm_clear_pointer(&priv->lldp_neighbors, g_ptr_array_unref); + + g_free(priv->interface); + g_free(priv->ip_interface); + g_free(priv->udi); + g_free(priv->path); + g_free(priv->driver); + g_free(priv->driver_version); + g_free(priv->firmware_version); + g_free(priv->product); + g_free(priv->vendor); + g_free(priv->short_vendor); + g_free(priv->description); + g_free(priv->bus_name); + g_free(priv->type_description); + g_free(priv->physical_port_id); + g_free(priv->hw_address); + + nm_clear_pointer(&priv->udev, udev_unref); + + G_OBJECT_CLASS(nm_device_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDevice *device = NM_DEVICE(object); + + switch (prop_id) { + case PROP_DEVICE_TYPE: + g_value_set_enum(value, nm_device_get_device_type(device)); + break; + case PROP_UDI: + g_value_set_string(value, nm_device_get_udi(device)); + break; + case PROP_PATH: + g_value_set_string(value, nm_device_get_path(device)); + break; + case PROP_INTERFACE: + g_value_set_string(value, nm_device_get_iface(device)); + break; + case PROP_IP_INTERFACE: + g_value_set_string(value, nm_device_get_ip_iface(device)); + break; + case PROP_DRIVER: + g_value_set_string(value, nm_device_get_driver(device)); + break; + case PROP_DRIVER_VERSION: + g_value_set_string(value, nm_device_get_driver_version(device)); + break; + case PROP_FIRMWARE_VERSION: + g_value_set_string(value, nm_device_get_firmware_version(device)); + break; + case PROP_CAPABILITIES: + g_value_set_flags(value, nm_device_get_capabilities(device)); + break; + case PROP_REAL: + g_value_set_boolean(value, nm_device_is_real(device)); + break; + case PROP_MANAGED: + g_value_set_boolean(value, nm_device_get_managed(device)); + break; + case PROP_AUTOCONNECT: + g_value_set_boolean(value, nm_device_get_autoconnect(device)); + break; + case PROP_FIRMWARE_MISSING: + g_value_set_boolean(value, nm_device_get_firmware_missing(device)); + break; + case PROP_NM_PLUGIN_MISSING: + g_value_set_boolean(value, nm_device_get_nm_plugin_missing(device)); + break; + case PROP_IP4_CONFIG: + g_value_set_object(value, nm_device_get_ip4_config(device)); + break; + case PROP_DHCP4_CONFIG: + g_value_set_object(value, nm_device_get_dhcp4_config(device)); + break; + case PROP_IP6_CONFIG: + g_value_set_object(value, nm_device_get_ip6_config(device)); + break; + case PROP_DHCP6_CONFIG: + g_value_set_object(value, nm_device_get_dhcp6_config(device)); + break; + case PROP_STATE: + g_value_set_enum(value, nm_device_get_state(device)); + break; + case PROP_STATE_REASON: + g_value_set_uint(value, nm_device_get_state_reason(device)); + break; + case PROP_ACTIVE_CONNECTION: + g_value_set_object(value, nm_device_get_active_connection(device)); + break; + case PROP_AVAILABLE_CONNECTIONS: + g_value_take_boxed( + value, + _nm_utils_copy_object_array(nm_device_get_available_connections(device))); + break; + case PROP_PRODUCT: + g_value_set_string(value, nm_device_get_product(device)); + break; + case PROP_VENDOR: + g_value_set_string(value, nm_device_get_vendor(device)); + break; + case PROP_PHYSICAL_PORT_ID: + g_value_set_string(value, nm_device_get_physical_port_id(device)); + break; + case PROP_MTU: + g_value_set_uint(value, nm_device_get_mtu(device)); + break; + case PROP_METERED: + g_value_set_uint(value, nm_device_get_metered(device)); + break; + case PROP_LLDP_NEIGHBORS: + g_value_set_boxed(value, nm_device_get_lldp_neighbors(device)); + break; + case PROP_IP4_CONNECTIVITY: + g_value_set_enum(value, nm_device_get_connectivity(device, AF_INET)); + break; + case PROP_IP6_CONNECTIVITY: + g_value_set_enum(value, nm_device_get_connectivity(device, AF_INET6)); + break; + case PROP_INTERFACE_FLAGS: + g_value_set_uint(value, nm_device_get_interface_flags(device)); + break; + case PROP_HW_ADDRESS: + g_value_set_string(value, nm_device_get_hw_address(device)); + 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) +{ + NMDevice * self = NM_DEVICE(object); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gboolean b; + + switch (prop_id) { + case PROP_AUTOCONNECT: + b = g_value_get_boolean(value); + if (priv->autoconnect != b) + nm_device_set_autoconnect(NM_DEVICE(object), b); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/* TODO: statistics interface not yet implemented. */ +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_statistics = NML_DBUS_META_IFACE_INIT( + NM_DBUS_INTERFACE_DEVICE_STATISTICS, + NULL, + NML_DBUS_META_INTERFACE_PRIO_NONE, + NML_DBUS_META_IFACE_DBUS_PROPERTIES(NML_DBUS_META_PROPERTY_INIT_TODO("RefreshRateMs", "u"), + NML_DBUS_META_PROPERTY_INIT_TODO("RxBytes", "t"), + NML_DBUS_META_PROPERTY_INIT_TODO("TxBytes", "t"), ), ); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE, + nm_device_get_type, + NML_DBUS_META_INTERFACE_PRIO_PARENT_TYPE, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_O_PROP("ActiveConnection", + PROP_ACTIVE_CONNECTION, + NMDevicePrivate, + property_o[PROPERTY_O_IDX_ACTIVE_CONNECTION], + nm_active_connection_get_type, + .is_always_ready = TRUE), + NML_DBUS_META_PROPERTY_INIT_B("Autoconnect", + PROP_AUTOCONNECT, + NMDevicePrivate, + autoconnect), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("AvailableConnections", + PROP_AVAILABLE_CONNECTIONS, + NMDevicePrivate, + available_connections, + nm_remote_connection_get_type, + .is_always_ready = TRUE), + NML_DBUS_META_PROPERTY_INIT_U("Capabilities", + PROP_CAPABILITIES, + NMDevicePrivate, + capabilities), + NML_DBUS_META_PROPERTY_INIT_U("DeviceType", PROP_DEVICE_TYPE, NMDevicePrivate, device_type), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Dhcp4Config", + PROP_DHCP4_CONFIG, + NMDevicePrivate, + property_o[PROPERTY_O_IDX_DHCP4_CONFIG], + nm_dhcp4_config_get_type), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Dhcp6Config", + PROP_DHCP6_CONFIG, + NMDevicePrivate, + property_o[PROPERTY_O_IDX_DHCP6_CONFIG], + nm_dhcp6_config_get_type), + NML_DBUS_META_PROPERTY_INIT_S("Driver", PROP_DRIVER, NMDevicePrivate, driver), + NML_DBUS_META_PROPERTY_INIT_S("DriverVersion", + PROP_DRIVER_VERSION, + NMDevicePrivate, + driver_version), + NML_DBUS_META_PROPERTY_INIT_B("FirmwareMissing", + PROP_FIRMWARE_MISSING, + NMDevicePrivate, + firmware_missing), + NML_DBUS_META_PROPERTY_INIT_S("FirmwareVersion", + PROP_FIRMWARE_VERSION, + NMDevicePrivate, + firmware_version), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_S("Interface", PROP_INTERFACE, NMDevicePrivate, interface), + NML_DBUS_META_PROPERTY_INIT_U("InterfaceFlags", + PROP_INTERFACE_FLAGS, + NMDevicePrivate, + interface_flags), + NML_DBUS_META_PROPERTY_INIT_IGNORE("Ip4Address", "u"), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Ip4Config", + PROP_IP4_CONFIG, + NMDevicePrivate, + property_o[PROPERTY_O_IDX_IP4_CONFIG], + nm_ip4_config_get_type), + NML_DBUS_META_PROPERTY_INIT_U("Ip4Connectivity", + PROP_IP4_CONNECTIVITY, + NMDevicePrivate, + ip4_connectivity), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Ip6Config", + PROP_IP6_CONFIG, + NMDevicePrivate, + property_o[PROPERTY_O_IDX_IP6_CONFIG], + nm_ip6_config_get_type), + NML_DBUS_META_PROPERTY_INIT_U("Ip6Connectivity", + PROP_IP6_CONNECTIVITY, + NMDevicePrivate, + ip6_connectivity), + NML_DBUS_META_PROPERTY_INIT_S("IpInterface", + PROP_IP_INTERFACE, + NMDevicePrivate, + ip_interface), + NML_DBUS_META_PROPERTY_INIT_FCN("LldpNeighbors", + PROP_LLDP_NEIGHBORS, + "aa{sv}", + _notify_update_prop_lldp_neighbors), + NML_DBUS_META_PROPERTY_INIT_B("Managed", PROP_MANAGED, NMDevicePrivate, managed), + NML_DBUS_META_PROPERTY_INIT_U("Metered", PROP_METERED, NMDevicePrivate, metered), + NML_DBUS_META_PROPERTY_INIT_U("Mtu", PROP_MTU, NMDevicePrivate, mtu), + NML_DBUS_META_PROPERTY_INIT_B("NmPluginMissing", + PROP_NM_PLUGIN_MISSING, + NMDevicePrivate, + nm_plugin_missing), + NML_DBUS_META_PROPERTY_INIT_S("Path", PROP_PATH, NMDevicePrivate, path), + NML_DBUS_META_PROPERTY_INIT_S("PhysicalPortId", + PROP_PHYSICAL_PORT_ID, + NMDevicePrivate, + physical_port_id), + NML_DBUS_META_PROPERTY_INIT_B("Real", PROP_REAL, NMDevicePrivate, real), + NML_DBUS_META_PROPERTY_INIT_IGNORE("State", "u"), + NML_DBUS_META_PROPERTY_INIT_FCN("StateReason", + PROP_STATE_REASON, + "(uu)", + _notify_update_prop_state_reason), + NML_DBUS_META_PROPERTY_INIT_S("Udi", PROP_UDI, NMDevicePrivate, udi), ), + .base_struct_offset = G_STRUCT_OFFSET(NMDevice, _priv), ); + +static void +nm_device_class_init(NMDeviceClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof(NMDevicePrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + nm_object_class->register_client = register_client; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_INDIRECT(nm_object_class, NMDevice); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_N(nm_object_class, NMDevicePrivate, property_o); + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, + NMDevicePrivate, + available_connections); + + klass->connection_compatible = connection_compatible; + + /** + * NMDevice:interface: + * + * The interface of the device. + **/ + obj_properties[PROP_INTERFACE] = g_param_spec_string(NM_DEVICE_INTERFACE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:ip-interface: + * + * The IP interface of the device which should be used for all IP-related + * operations like addressing and routing. + **/ + obj_properties[PROP_IP_INTERFACE] = + g_param_spec_string(NM_DEVICE_IP_INTERFACE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:device-type: + * + * The numeric type of the device. + **/ + obj_properties[PROP_DEVICE_TYPE] = g_param_spec_enum(NM_DEVICE_DEVICE_TYPE, + "", + "", + NM_TYPE_DEVICE_TYPE, + NM_DEVICE_TYPE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + /** + * NMDevice:udi: + * + * An operating-system specific device hardware identifier; this is not + * unique to a specific hardware device across reboots or hotplugs. It + * is an opaque string which for some device types (Bluetooth, Modem) + * contains an identifier provided by the underlying hardware service daemon + * such as Bluez or ModemManager, and clients can use this property to + * request more information about the device from those services. + **/ + obj_properties[PROP_UDI] = + g_param_spec_string(NM_DEVICE_UDI, "", "", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:path: + * + * The device path as exposed by the udev property ID_PATH. + * + * The string is backslash escaped (C escaping) for invalid + * characters. The escaping can be reverted with g_strcompress(), + * however the result may not be valid UTF-8. + * + * Since: 1.26 + **/ + obj_properties[PROP_PATH] = g_param_spec_string(NM_DEVICE_PATH, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:driver: + * + * The driver of the device. + **/ + obj_properties[PROP_DRIVER] = g_param_spec_string(NM_DEVICE_DRIVER, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:driver-version: + * + * The version of the device driver. + **/ + obj_properties[PROP_DRIVER_VERSION] = + g_param_spec_string(NM_DEVICE_DRIVER_VERSION, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:firmware-version: + * + * The firmware version of the device. + **/ + obj_properties[PROP_FIRMWARE_VERSION] = + g_param_spec_string(NM_DEVICE_FIRMWARE_VERSION, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:capabilities: + * + * The capabilities of the device. + **/ + obj_properties[PROP_CAPABILITIES] = + g_param_spec_flags(NM_DEVICE_CAPABILITIES, + "", + "", + NM_TYPE_DEVICE_CAPABILITIES, + NM_DEVICE_CAP_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:real: + * + * Whether the device is real or is a placeholder device that could + * be created automatically by NetworkManager if one of its + * #NMDevice:available-connections was activated. + * + * Since: 1.2 + **/ + obj_properties[PROP_REAL] = g_param_spec_boolean(NM_DEVICE_REAL, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:managed: + * + * Whether the device is managed by NetworkManager. + **/ + obj_properties[PROP_MANAGED] = g_param_spec_boolean(NM_DEVICE_MANAGED, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:autoconnect: + * + * Whether the device can auto-activate a connection. + * + * The property setter is a synchronous D-Bus call. This is deprecated since 1.22. + **/ + obj_properties[PROP_AUTOCONNECT] = + g_param_spec_boolean(NM_DEVICE_AUTOCONNECT, + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:firmware-missing: + * + * When %TRUE indicates the device is likely missing firmware required + * for its operation. + **/ + obj_properties[PROP_FIRMWARE_MISSING] = + g_param_spec_boolean(NM_DEVICE_FIRMWARE_MISSING, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:nm-plugin-missing: + * + * When %TRUE indicates that the NetworkManager plugin for the device + * is not installed. + * + * Since: 1.2 + **/ + obj_properties[PROP_NM_PLUGIN_MISSING] = + g_param_spec_boolean(NM_DEVICE_NM_PLUGIN_MISSING, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:ip4-config: + * + * The #NMIP4Config of the device. + **/ + obj_properties[PROP_IP4_CONFIG] = + g_param_spec_object(NM_DEVICE_IP4_CONFIG, + "", + "", + NM_TYPE_IP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:dhcp4-config: + * + * The IPv4 #NMDhcpConfig of the device. + **/ + obj_properties[PROP_DHCP4_CONFIG] = + g_param_spec_object(NM_DEVICE_DHCP4_CONFIG, + "", + "", + NM_TYPE_DHCP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:ip6-config: + * + * The IPv6 #NMIPConfig of the device. + **/ + obj_properties[PROP_IP6_CONFIG] = + g_param_spec_object(NM_DEVICE_IP6_CONFIG, + "", + "", + NM_TYPE_IP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:dhcp6-config: + * + * The IPv6 #NMDhcpConfig of the device. + **/ + obj_properties[PROP_DHCP6_CONFIG] = + g_param_spec_object(NM_DEVICE_DHCP6_CONFIG, + "", + "", + NM_TYPE_DHCP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:ip4-connectivity: + * + * The IPv4 connectivity state of the device. + * + * Since: 1.16 + **/ + obj_properties[PROP_IP4_CONNECTIVITY] = + g_param_spec_enum(NM_DEVICE_IP4_CONNECTIVITY, + "", + "", + NM_TYPE_CONNECTIVITY_STATE, + NM_CONNECTIVITY_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:ip6-connectivity: + * + * The IPv6 connectivity state of the device. + * + * Since: 1.16 + **/ + obj_properties[PROP_IP6_CONNECTIVITY] = + g_param_spec_enum(NM_DEVICE_IP6_CONNECTIVITY, + "", + "", + NM_TYPE_CONNECTIVITY_STATE, + NM_CONNECTIVITY_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:state: + * + * The state of the device. + **/ + obj_properties[PROP_STATE] = g_param_spec_enum(NM_DEVICE_STATE, + "", + "", + NM_TYPE_DEVICE_STATE, + NM_DEVICE_STATE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:state-reason: + * + * The reason for the device state. + **/ + obj_properties[PROP_STATE_REASON] = + g_param_spec_uint(NM_DEVICE_STATE_REASON, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:active-connection: + * + * The #NMActiveConnection object that "owns" this device during activation. + **/ + obj_properties[PROP_ACTIVE_CONNECTION] = + g_param_spec_object(NM_DEVICE_ACTIVE_CONNECTION, + "", + "", + NM_TYPE_ACTIVE_CONNECTION, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:available-connections: (type GPtrArray(NMRemoteConnection)) + * + * The available connections of the device + **/ + obj_properties[PROP_AVAILABLE_CONNECTIONS] = + g_param_spec_boxed(NM_DEVICE_AVAILABLE_CONNECTIONS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:vendor: + * + * The vendor string of the device. + **/ + obj_properties[PROP_VENDOR] = g_param_spec_string(NM_DEVICE_VENDOR, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:product: + * + * The product string of the device. + **/ + obj_properties[PROP_PRODUCT] = g_param_spec_string(NM_DEVICE_PRODUCT, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:physical-port-id: + * + * The physical port ID of the device. (See + * nm_device_get_physical_port_id().) + **/ + obj_properties[PROP_PHYSICAL_PORT_ID] = + g_param_spec_string(NM_DEVICE_PHYSICAL_PORT_ID, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:mtu: + * + * The MTU of the device. + **/ + obj_properties[PROP_MTU] = g_param_spec_uint(NM_DEVICE_MTU, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:metered: + * + * Whether the device is metered. + * + * Since: 1.2 + **/ + obj_properties[PROP_METERED] = g_param_spec_uint(NM_DEVICE_METERED, + "", + "", + 0, + G_MAXUINT32, + NM_METERED_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:lldp-neighbors: + * + * The LLDP neighbors. + **/ + obj_properties[PROP_LLDP_NEIGHBORS] = + g_param_spec_boxed(NM_DEVICE_LLDP_NEIGHBORS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:interface-flags: + * + * The interface flags. + * + * Since: 1.22 + **/ + obj_properties[PROP_INTERFACE_FLAGS] = + g_param_spec_uint(NM_DEVICE_INTERFACE_FLAGS, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:hw-address: + * + * The hardware address of the device. + * + * Since: 1.24 + **/ + obj_properties[PROP_HW_ADDRESS] = + g_param_spec_string(NM_DEVICE_HW_ADDRESS, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device); + + /** + * NMDevice::state-changed: + * @device: the device object that received the signal + * @new_state: the new state of the device + * @old_state: the previous state of the device + * @reason: the reason describing the state change + * + * Notifies the state change of a #NMDevice. + **/ + signals[STATE_CHANGED] = g_signal_new("state-changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 3, + G_TYPE_UINT, + G_TYPE_UINT, + G_TYPE_UINT); +} + +/** + * nm_device_get_iface: + * @device: a #NMDevice + * + * Gets the interface name of the #NMDevice. + * + * Returns: the interface of the device. This is the internal string used by the + * device, and must not be modified. + **/ +const char * +nm_device_get_iface(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->interface); +} + +/** + * nm_device_get_ip_iface: + * @device: a #NMDevice + * + * Gets the IP interface name of the #NMDevice over which IP traffic flows + * when the device is in the ACTIVATED state. + * + * Returns: the IP traffic interface of the device. This is the internal string + * used by the device, and must not be modified. + **/ +const char * +nm_device_get_ip_iface(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->ip_interface); +} + +/** + * nm_device_get_device_type: + * @device: a #NMDevice + * + * Returns the numeric type of the #NMDevice, ie Ethernet, Wi-Fi, etc. + * + * Returns: the device type + **/ +NMDeviceType +nm_device_get_device_type(NMDevice *self) +{ + g_return_val_if_fail(NM_IS_DEVICE(self), NM_DEVICE_TYPE_UNKNOWN); + + return coerce_type(NM_DEVICE_GET_PRIVATE(self)->device_type); +} + +/** + * nm_device_get_udi: + * @device: a #NMDevice + * + * Gets the Unique Device Identifier of the #NMDevice. + * + * Returns: the Unique Device Identifier of the device. This identifier may be + * used to gather more information about the device from various operating + * system services like udev or sysfs. + **/ +const char * +nm_device_get_udi(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->udi); +} + +/** + * nm_device_get_path: + * @device: a #NMDevice + * + * Gets the path of the #NMDevice as exposed by the udev property ID_PATH. + * + * Returns: the path of the device. + * + * The string is backslash escaped (C escaping) for invalid characters. The escaping + * can be reverted with g_strcompress(), however the result may not be valid UTF-8. + * + * Since: 1.26 + **/ +const char * +nm_device_get_path(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->path); +} + +/** + * nm_device_get_driver: + * @device: a #NMDevice + * + * Gets the driver of the #NMDevice. + * + * Returns: the driver of the device. This is the internal string used by the + * device, and must not be modified. + **/ +const char * +nm_device_get_driver(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->driver); +} + +/** + * nm_device_get_driver_version: + * @device: a #NMDevice + * + * Gets the driver version of the #NMDevice. + * + * Returns: the version of the device driver. This is the internal string used by the + * device, and must not be modified. + **/ +const char * +nm_device_get_driver_version(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->driver_version); +} + +/** + * nm_device_get_firmware_version: + * @device: a #NMDevice + * + * Gets the firmware version of the #NMDevice. + * + * Returns: the firmware version of the device. This is the internal string used by the + * device, and must not be modified. + **/ +const char * +nm_device_get_firmware_version(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->firmware_version); +} + +/** + * nm_device_get_type_description: + * @device: a #NMDevice + * + * Gets a (non-localized) description of the type of device that + * @device is. + * + * Returns: the type description of the device. This is the internal + * string used by the device, and must not be modified. + **/ +const char * +nm_device_get_type_description(NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(device); + const char * desc, *typename; + + /* BEWARE: this function should return the same value + * as nm_device_get_type_description() in nm-core. */ + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + if (priv->type_description) + return _nml_coerce_property_str_not_empty(priv->type_description); + + if (NM_DEVICE_GET_CLASS(device)->get_type_description) { + desc = NM_DEVICE_GET_CLASS(device)->get_type_description(device); + if (desc) + return desc; + } + + typename = G_OBJECT_TYPE_NAME(device); + if (g_str_has_prefix(typename, "NMDevice")) { + typename += 8; + if (nm_streq(typename, "Veth")) + typename = "Ethernet"; + } + priv->type_description = g_ascii_strdown(typename, -1); + + return _nml_coerce_property_str_not_empty(priv->type_description); +} + +NMLDBusNotifyUpdatePropFlags +_nm_device_notify_update_prop_hw_address(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMDevice * self = NM_DEVICE(dbobj->nmobj); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gboolean is_new = (meta_iface == &_nml_dbus_meta_iface_nm_device); + gboolean changed = FALSE; + + if (!is_new && priv->hw_address_is_new) { + /* once the instance is marked to honor the new property, the + * changed signal for the old variant gets ignored. */ + goto out; + } + + if (!value) { + if (nm_clear_g_free(&priv->hw_address)) + changed = TRUE; + goto out; + } + + priv->hw_address_is_new = is_new; + + nm_utils_strdup_reset(&priv->hw_address, + _nml_coerce_property_str_not_empty(g_variant_get_string(value, NULL))); + + /* always emit a changed signal here, even if "priv->hw_address" might be unchanged. + * We want to emit the signal because we received a PropertiesChanged signal on D-Bus, + * even if nothing actually changed. */ + changed = TRUE; + +out: + if (changed) { + _nm_client_queue_notify_object(client, self, obj_properties[PROP_HW_ADDRESS]); + } + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; +} + +/** + * nm_device_get_hw_address: + * @device: a #NMDevice + * + * Gets the current a hardware address (MAC) for the @device. + * + * Returns: the current MAC of the device, or %NULL. + * This is the internal string used by the device, and must not be modified. + **/ +const char * +nm_device_get_hw_address(NMDevice *device) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + priv = NM_DEVICE_GET_PRIVATE(device); + + nm_assert(!nm_streq0(priv->hw_address, "")); + + return priv->hw_address; +} + +/** + * nm_device_get_capabilities: + * @device: a #NMDevice + * + * Gets the device' capabilities. + * + * Returns: the capabilities + **/ +NMDeviceCapabilities +nm_device_get_capabilities(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), 0); + + return NM_DEVICE_GET_PRIVATE(device)->capabilities; +} + +/** + * nm_device_get_managed: + * @device: a #NMDevice + * + * Whether the #NMDevice is managed by NetworkManager. + * + * Returns: %TRUE if the device is managed by NetworkManager + **/ +gboolean +nm_device_get_managed(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), 0); + + return NM_DEVICE_GET_PRIVATE(device)->managed; +} + +/** + * nm_device_set_managed: + * @device: a #NMDevice + * @managed: %TRUE to make the device managed by NetworkManager. + * + * Enables or disables management of #NMDevice by NetworkManager. + * + * Since: 1.2 + * + * Deprecated: 1.22: Use the async command nm_client_dbus_set_property() on + * nm_object_get_path(), interface %NM_DBUS_INTERFACE_DEVICE to set the + * "Managed" property to a "(b)" boolean value. + * This function is deprecated because it calls a synchronous D-Bus method + * and modifies the content of the NMClient cache client side. Also, it does + * not emit a property changed signal. + **/ +void +nm_device_set_managed(NMDevice *device, gboolean managed) +{ + g_return_if_fail(NM_IS_DEVICE(device)); + + managed = !!managed; + + NM_DEVICE_GET_PRIVATE(device)->managed = managed; + + _nm_client_set_property_sync_legacy(_nm_object_get_client(device), + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Managed", + "b", + managed); +} + +/** + * nm_device_get_autoconnect: + * @device: a #NMDevice + * + * Whether the #NMDevice can be autoconnected. + * + * Returns: %TRUE if the device is allowed to be autoconnected + **/ +gboolean +nm_device_get_autoconnect(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + + return NM_DEVICE_GET_PRIVATE(device)->autoconnect; +} + +/** + * nm_device_set_autoconnect: + * @device: a #NMDevice + * @autoconnect: %TRUE to enable autoconnecting + * + * Enables or disables automatic activation of the #NMDevice. + * + * Deprecated: 1.22: Use the async command nm_client_dbus_set_property() on + * nm_object_get_path(), %NM_DBUS_INTERFACE_DEVICE to set "Autoconnect" property to a "(b)" value. + * This function is deprecated because it calls a synchronous D-Bus method + * and modifies the content of the NMClient cache client side. + **/ +void +nm_device_set_autoconnect(NMDevice *device, gboolean autoconnect) +{ + g_return_if_fail(NM_IS_DEVICE(device)); + + NM_DEVICE_GET_PRIVATE(device)->autoconnect = autoconnect; + + _nm_client_set_property_sync_legacy(_nm_object_get_client(device), + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Autoconnect", + "b", + autoconnect); +} + +/** + * nm_device_get_firmware_missing: + * @device: a #NMDevice + * + * Indicates that firmware required for the device's operation is likely + * to be missing. + * + * Returns: %TRUE if firmware required for the device's operation is likely + * to be missing. + **/ +gboolean +nm_device_get_firmware_missing(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), 0); + + return NM_DEVICE_GET_PRIVATE(device)->firmware_missing; +} + +/** + * nm_device_get_nm_plugin_missing: + * @device: a #NMDevice + * + * Indicates that the NetworkManager plugin for the device is not installed. + * + * Returns: %TRUE if the device plugin not installed. + * + * Since: 1.2 + **/ +gboolean +nm_device_get_nm_plugin_missing(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + + return NM_DEVICE_GET_PRIVATE(device)->nm_plugin_missing; +} + +/** + * nm_device_get_ip4_config: + * @device: a #NMDevice + * + * Gets the current IPv4 #NMIPConfig associated with the #NMDevice. + * + * You can alternatively use nm_active_connection_get_ip4_config(), which also + * works with VPN connections. + * + * Returns: (transfer none): the IPv4 #NMIPConfig, or %NULL if the device is not + * activated. + **/ +NMIPConfig * +nm_device_get_ip4_config(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return nml_dbus_property_o_get_obj( + &NM_DEVICE_GET_PRIVATE(device)->property_o[PROPERTY_O_IDX_IP4_CONFIG]); +} + +/** + * nm_device_get_dhcp4_config: + * @device: a #NMDevice + * + * Gets the current IPv4 #NMDhcpConfig associated with the #NMDevice. + * + * You can alternatively use nm_active_connection_get_dhcp4_config(), which also + * works with VPN connections. + * + * Returns: (transfer none): the IPv4 #NMDhcpConfig, or %NULL if the device is + * not activated or not using DHCP. + **/ +NMDhcpConfig * +nm_device_get_dhcp4_config(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return nml_dbus_property_o_get_obj( + &NM_DEVICE_GET_PRIVATE(device)->property_o[PROPERTY_O_IDX_DHCP4_CONFIG]); +} + +/** + * nm_device_get_ip6_config: + * @device: a #NMDevice + * + * Gets the current IPv6 #NMIPConfig associated with the #NMDevice. + * + * You can alternatively use nm_active_connection_get_ip6_config(), which also + * works with VPN connections. + * + * Returns: (transfer none): the IPv6 #NMIPConfig or %NULL if the device is not activated. + **/ +NMIPConfig * +nm_device_get_ip6_config(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return nml_dbus_property_o_get_obj( + &NM_DEVICE_GET_PRIVATE(device)->property_o[PROPERTY_O_IDX_IP6_CONFIG]); +} + +/** + * nm_device_get_dhcp6_config: + * @device: a #NMDevice + * + * Gets the current IPv6 #NMDhcpConfig associated with the #NMDevice. + * + * You can alternatively use nm_active_connection_get_dhcp6_config(), which also + * works with VPN connections. + * + * Returns: (transfer none): the IPv6 #NMDhcpConfig, or %NULL if the device is + * not activated or not using DHCPv6. + **/ +NMDhcpConfig * +nm_device_get_dhcp6_config(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return nml_dbus_property_o_get_obj( + &NM_DEVICE_GET_PRIVATE(device)->property_o[PROPERTY_O_IDX_DHCP6_CONFIG]); +} + +/** + * nm_device_get_connectivity: + * @device: a #NMDevice + * @addr_family: network address family + * + * The connectivity state of the device for given address family. + * Supported address families are %AF_INET for IPv4, %AF_INET6 + * for IPv6 or %AF_UNSPEC for any. + * + * Returns: the current connectivity state + * + * Since: 1.16 + **/ +NMConnectivityState +nm_device_get_connectivity(NMDevice *device, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(device); + + switch (addr_family) { + case AF_INET: + return priv->ip4_connectivity; + case AF_INET6: + return priv->ip6_connectivity; + case AF_UNSPEC: + return NM_MAX(priv->ip4_connectivity, priv->ip6_connectivity); + default: + g_return_val_if_reached(NM_CONNECTIVITY_UNKNOWN); + } +} + +/** + * nm_device_get_interface_flags: + * @device: a #NMDevice + * + * Gets the interface flags of the device. + * + * Returns: the flags + * + * Since: 1.22 + **/ +NMDeviceInterfaceFlags +nm_device_get_interface_flags(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NM_DEVICE_INTERFACE_FLAG_NONE); + + return NM_DEVICE_GET_PRIVATE(device)->interface_flags; +} + +/** + * nm_device_get_state: + * @device: a #NMDevice + * + * Gets the current #NMDevice state. + * + * Returns: the current device state + **/ +NMDeviceState +nm_device_get_state(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NM_DEVICE_STATE_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE(device)->state; +} + +/** + * nm_device_get_state_reason: + * @device: a #NMDevice + * + * Gets the reason for entering the current #NMDevice state. + * + * Returns: the reason for entering the current device state + **/ +NMDeviceStateReason +nm_device_get_state_reason(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NM_DEVICE_STATE_REASON_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE(device)->state_reason; +} + +/** + * nm_device_get_active_connection: + * @device: a #NMDevice + * + * Gets the #NMActiveConnection object which owns this device during activation. + * + * Returns: (transfer none): the #NMActiveConnection or %NULL if the device is + * not part of an active connection + **/ +NMActiveConnection * +nm_device_get_active_connection(NMDevice *device) +{ + NMActiveConnection *ac; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + ac = nml_dbus_property_o_get_obj( + &NM_DEVICE_GET_PRIVATE(device)->property_o[PROPERTY_O_IDX_ACTIVE_CONNECTION]); + + nm_assert(!ac || NM_IS_ACTIVE_CONNECTION(ac)); + return ac; +} + +/** + * nm_device_get_available_connections: + * @device: a #NMDevice + * + * Gets the #NMRemoteConnections currently known to the daemon that could + * be activated on @device. + * + * Returns: (element-type NMRemoteConnection): the #GPtrArray + * containing #NMRemoteConnections. This is the internal copy used by + * the connection, and must not be modified. + **/ +const GPtrArray * +nm_device_get_available_connections(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_DEVICE_GET_PRIVATE(device)->available_connections); +} + +static const char * +get_type_name(NMDevice *device) +{ + switch (nm_device_get_device_type(device)) { + case NM_DEVICE_TYPE_ETHERNET: + return _("Ethernet"); + case NM_DEVICE_TYPE_WIFI: + return _("Wi-Fi"); + case NM_DEVICE_TYPE_BT: + return _("Bluetooth"); + case NM_DEVICE_TYPE_OLPC_MESH: + return _("OLPC Mesh"); + case NM_DEVICE_TYPE_OVS_INTERFACE: + return _("Open vSwitch Interface"); + case NM_DEVICE_TYPE_OVS_PORT: + return _("Open vSwitch Port"); + case NM_DEVICE_TYPE_OVS_BRIDGE: + return _("Open vSwitch Bridge"); + case NM_DEVICE_TYPE_WIMAX: + return _("WiMAX"); + case NM_DEVICE_TYPE_MODEM: + return _("Mobile Broadband"); + case NM_DEVICE_TYPE_INFINIBAND: + return _("InfiniBand"); + case NM_DEVICE_TYPE_BOND: + return _("Bond"); + case NM_DEVICE_TYPE_TEAM: + return _("Team"); + case NM_DEVICE_TYPE_BRIDGE: + return _("Bridge"); + case NM_DEVICE_TYPE_VLAN: + return _("VLAN"); + case NM_DEVICE_TYPE_ADSL: + return _("ADSL"); + case NM_DEVICE_TYPE_MACVLAN: + return _("MACVLAN"); + case NM_DEVICE_TYPE_VXLAN: + return _("VXLAN"); + case NM_DEVICE_TYPE_IP_TUNNEL: + return _("IPTunnel"); + case NM_DEVICE_TYPE_TUN: + return _("Tun"); + case NM_DEVICE_TYPE_VETH: + return _("Veth"); + case NM_DEVICE_TYPE_MACSEC: + return _("MACsec"); + case NM_DEVICE_TYPE_DUMMY: + return _("Dummy"); + case NM_DEVICE_TYPE_PPP: + return _("PPP"); + case NM_DEVICE_TYPE_WPAN: + return _("IEEE 802.15.4"); + case NM_DEVICE_TYPE_6LOWPAN: + return _("6LoWPAN"); + case NM_DEVICE_TYPE_WIREGUARD: + return _("WireGuard"); + case NM_DEVICE_TYPE_WIFI_P2P: + return _("Wi-Fi P2P"); + case NM_DEVICE_TYPE_VRF: + return _("VRF"); + case NM_DEVICE_TYPE_GENERIC: + case NM_DEVICE_TYPE_UNUSED1: + case NM_DEVICE_TYPE_UNUSED2: + case NM_DEVICE_TYPE_UNKNOWN: + break; + } + return _("Unknown"); +} + +static char * +get_device_type_name_with_iface(NMDevice *device) +{ + const char *type_name = get_type_name(device); + + switch (nm_device_get_device_type(device)) { + case NM_DEVICE_TYPE_BOND: + case NM_DEVICE_TYPE_TEAM: + case NM_DEVICE_TYPE_BRIDGE: + case NM_DEVICE_TYPE_VLAN: + return g_strdup_printf("%s (%s)", type_name, nm_device_get_iface(device)); + default: + return g_strdup(type_name); + } +} + +static char * +get_device_generic_type_name_with_iface(NMDevice *device) +{ + switch (nm_device_get_device_type(device)) { + case NM_DEVICE_TYPE_ETHERNET: + case NM_DEVICE_TYPE_INFINIBAND: + return g_strdup(_("Wired")); + default: + return get_device_type_name_with_iface(device); + } +} + +static const char * +get_bus_name(NMDevice *device) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(device); + struct udev_device *udevice; + const char * ifname; + const char * bus; + + if (priv->bus_name) + goto out; + + if (!priv->udev) + return NULL; + + ifname = nm_device_get_iface(device); + if (!ifname) + return NULL; + + udevice = udev_device_new_from_subsystem_sysname(priv->udev, "net", ifname); + if (!udevice) { + udevice = udev_device_new_from_subsystem_sysname(priv->udev, "tty", ifname); + if (!udevice) + return NULL; + } + bus = udev_device_get_property_value(udevice, "ID_BUS"); + if (!g_strcmp0(bus, "pci")) + priv->bus_name = g_strdup(_("PCI")); + else if (!g_strcmp0(bus, "usb")) + priv->bus_name = g_strdup(_("USB")); + else { + /* Use "" instead of NULL so we can tell later that we've + * already tried. + */ + priv->bus_name = g_strdup(""); + } + udev_device_unref(udevice); + +out: + if (*priv->bus_name) + return priv->bus_name; + else + return NULL; +} + +static char * +_get_udev_property(NMDevice * device, + const char *enc_prop, /* ID_XXX_ENC */ + const char *db_prop) /* ID_XXX_FROM_DATABASE */ +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(device); + struct udev_device *udev_device; + struct udev_device *tmpdev; + const char * ifname; + guint32 count = 0; + char * enc_value = NULL; + char * db_value = NULL; + + if (!priv->udev) + return NULL; + + ifname = nm_device_get_iface(device); + if (!ifname) + return NULL; + + udev_device = udev_device_new_from_subsystem_sysname(priv->udev, "net", ifname); + if (!udev_device) { + udev_device = udev_device_new_from_subsystem_sysname(priv->udev, "tty", ifname); + if (!udev_device) + return NULL; + } + /* Walk up the chain of the device and its parents a few steps to grab + * vendor and device ID information off it. + */ + tmpdev = udev_device; + while ((count++ < 3) && tmpdev && !enc_value) { + if (!enc_value) + enc_value = + nm_udev_utils_property_decode_cp(udev_device_get_property_value(tmpdev, enc_prop)); + if (!db_value) + db_value = g_strdup(udev_device_get_property_value(tmpdev, db_prop)); + + tmpdev = udev_device_get_parent(tmpdev); + } + udev_device_unref(udev_device); + + /* Prefer the hwdata database value over what comes directly + * from the device. */ + if (db_value) { + g_free(enc_value); + return db_value; + } + + return enc_value; +} + +static char * +_get_udev_property_utf8safe(NMDevice * device, + const char *enc_prop, /* ID_XXX_ENC */ + const char *db_prop) /* ID_XXX_FROM_DATABASE */ +{ + return nm_utils_str_utf8safe_escape_take(_get_udev_property(device, enc_prop, db_prop), + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL); +} + +/** + * nm_device_get_product: + * @device: a #NMDevice + * + * Gets the product string of the #NMDevice. + * + * Returns: the product name of the device. This is the internal string used by the + * device, and must not be modified. + * + * The string is backslash escaped (C escaping) for invalid characters. The escaping + * can be reverted with g_strcompress(), however the result may not be valid UTF-8. + **/ +const char * +nm_device_get_product(NMDevice *device) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + priv = NM_DEVICE_GET_PRIVATE(device); + if (!priv->product) { + priv->product = + _get_udev_property_utf8safe(device, "ID_MODEL_ENC", "ID_MODEL_FROM_DATABASE"); + + /* Sometimes ID_PRODUCT_FROM_DATABASE is used? */ + if (!priv->product) + priv->product = + _get_udev_property_utf8safe(device, "ID_MODEL_ENC", "ID_PRODUCT_FROM_DATABASE"); + + if (!priv->product) + priv->product = g_strdup(""); + } + + return priv->product; +} + +/** + * nm_device_get_vendor: + * @device: a #NMDevice + * + * Gets the vendor string of the #NMDevice. + * + * Returns: the vendor name of the device. This is the internal string used by the + * device, and must not be modified. + * + * The string is backslash escaped (C escaping) for invalid characters. The escaping + * can be reverted with g_strcompress(), however the result may not be valid UTF-8. + **/ +const char * +nm_device_get_vendor(NMDevice *device) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + priv = NM_DEVICE_GET_PRIVATE(device); + + if (!priv->vendor) + priv->vendor = + _get_udev_property_utf8safe(device, "ID_VENDOR_ENC", "ID_VENDOR_FROM_DATABASE"); + + if (!priv->vendor) + priv->vendor = g_strdup(""); + + return priv->vendor; +} + +static void +ensure_description(NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(device); + GParamSpec * name_prop; + gs_free char * short_product = NULL; + + priv->short_vendor = nm_str_realloc(nm_utils_fixup_vendor_string(nm_device_get_vendor(device))); + + /* Grab device's preferred name, if any */ + name_prop = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(device)), "name"); + if (name_prop) { + g_object_get(device, "name", &priv->description, NULL); + if (priv->description && priv->description[0]) + return; + nm_clear_g_free(&priv->description); + } + + if (!priv->short_vendor) { + priv->description = g_strdup(nm_device_get_iface(device) ?: ""); + return; + } + + short_product = nm_utils_fixup_product_string(nm_device_get_product(device)); + if (short_product == NULL) + short_product = g_strdup(get_type_name(device)); + + /* Another quick hack; if all of the fixed up vendor string + * is found in product, ignore the vendor. + */ + { + gs_free char *pdown = g_ascii_strdown(short_product, -1); + gs_free char *vdown = g_ascii_strdown(priv->short_vendor, -1); + + if (!strstr(pdown, vdown)) + priv->description = g_strconcat(priv->short_vendor, " ", short_product, NULL); + else + priv->description = g_steal_pointer(&short_product); + } +} + +static const char * +get_short_vendor(NMDevice *device) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + priv = NM_DEVICE_GET_PRIVATE(device); + + if (!priv->description) + ensure_description(device); + + return priv->short_vendor; +} + +/** + * nm_device_get_description: + * @device: an #NMDevice + * + * Gets a description of @device, based on its vendor and product names. + * + * Returns: a description of @device. If either the vendor or the + * product name is unknown, this returns the interface name. + */ +const char * +nm_device_get_description(NMDevice *device) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + priv = NM_DEVICE_GET_PRIVATE(device); + + if (!priv->description) + ensure_description(device); + + return priv->description; +} + +static gboolean +find_duplicates(char **names, gboolean *duplicates, int num_devices) +{ + int i, j; + gboolean found_any = FALSE; + + memset(duplicates, 0, num_devices * sizeof(gboolean)); + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) + continue; + for (j = i + 1; j < num_devices; j++) { + if (duplicates[j]) + continue; + if (!strcmp(names[i], names[j])) + duplicates[i] = duplicates[j] = found_any = TRUE; + } + } + + return found_any; +} + +/** + * nm_device_disambiguate_names: + * @devices: (array length=num_devices): an array of #NMDevice + * @num_devices: length of @devices + * + * Generates a list of short-ish unique presentation names for the + * devices in @devices. + * + * Returns: (transfer full) (array zero-terminated=1): the device names + */ +char ** +nm_device_disambiguate_names(NMDevice **devices, int num_devices) +{ + char ** names; + gboolean *duplicates; + int i; + + names = g_new(char *, num_devices + 1); + duplicates = g_new(gboolean, num_devices); + + /* Generic device name */ + for (i = 0; i < num_devices; i++) + names[i] = get_device_generic_type_name_with_iface(devices[i]); + if (!find_duplicates(names, duplicates, num_devices)) + goto done; + + /* Try specific names (eg, "Ethernet" and "InfiniBand" rather + * than "Wired") + */ + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) { + g_free(names[i]); + names[i] = get_device_type_name_with_iface(devices[i]); + } + } + if (!find_duplicates(names, duplicates, num_devices)) + goto done; + + /* Try prefixing bus name (eg, "PCI Ethernet" vs "USB Ethernet") */ + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) { + const char *bus = get_bus_name(devices[i]); + char * name; + + if (!bus) + continue; + + g_free(names[i]); + name = get_device_type_name_with_iface(devices[i]); + /* TRANSLATORS: the first %s is a bus name (eg, "USB") or + * product name, the second is a device type (eg, + * "Ethernet"). You can change this to something like + * "%2$s (%1$s)" if there's no grammatical way to combine + * the strings otherwise. + */ + names[i] = g_strdup_printf(C_("long device name", "%s %s"), bus, name); + g_free(name); + } + } + if (!find_duplicates(names, duplicates, num_devices)) + goto done; + + /* Try prefixing vendor name */ + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) { + const char *vendor = get_short_vendor(devices[i]); + char * name; + + if (!vendor) + continue; + + g_free(names[i]); + name = get_device_type_name_with_iface(devices[i]); + names[i] = + g_strdup_printf(C_("long device name", "%s %s"), vendor, get_type_name(devices[i])); + g_free(name); + } + } + if (!find_duplicates(names, duplicates, num_devices)) + goto done; + + /* If dealing with Bluetooth devices, try to distinguish them by + * device name. + */ + for (i = 0; i < num_devices; i++) { + if (duplicates[i] && NM_IS_DEVICE_BT(devices[i])) { + const char *devname = nm_device_bt_get_name(NM_DEVICE_BT(devices[i])); + char * name; + + if (!devname) + continue; + + g_free(names[i]); + name = get_device_type_name_with_iface(devices[i]); + names[i] = g_strdup_printf("%s (%s)", name, devname); + g_free(name); + } + } + if (!find_duplicates(names, duplicates, num_devices)) + goto done; + + /* We have multiple identical network cards, so we have to differentiate + * them by interface name. + */ + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) { + const char *interface = nm_device_get_iface(devices[i]); + + if (!interface) + continue; + + g_free(names[i]); + names[i] = g_strdup_printf("%s (%s)", get_type_name(devices[i]), interface); + } + } + +done: + g_free(duplicates); + names[num_devices] = NULL; + return names; +} + +/** + * nm_device_get_physical_port_id: + * @device: a #NMDevice + * + * Gets the physical port ID of the #NMDevice. If non-%NULL, this is + * an opaque string that can be used to recognize when + * seemingly-unrelated #NMDevices are actually just different virtual + * ports on a single physical port. (Eg, NPAR / SR-IOV.) + * + * Returns: the physical port ID of the device, or %NULL if the port + * ID is unknown. This is the internal string used by the device and + * must not be modified. + **/ +const char * +nm_device_get_physical_port_id(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->physical_port_id); +} + +/** + * nm_device_get_mtu: + * @device: a #NMDevice + * + * Gets the MTU of the #NMDevice. + * + * Returns: the MTU of the device in bytes. + **/ +guint32 +nm_device_get_mtu(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), 0); + + return NM_DEVICE_GET_PRIVATE(device)->mtu; +} + +/** + * nm_device_get_metered: + * @device: a #NMDevice + * + * Gets the metered setting of a #NMDevice. + * + * Returns: the metered setting. + * + * Since: 1.2 + **/ +NMMetered +nm_device_get_metered(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NM_METERED_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE(device)->metered; +} + +NM_BACKPORT_SYMBOL(libnm_1_0_6, NMMetered, nm_device_get_metered, (NMDevice * device), (device)); + +/** + * nm_device_get_lldp_neighbors: + * @device: a #NMDevice + * + * Gets the list of neighbors discovered through LLDP. + * + * Returns: (element-type NMLldpNeighbor) (transfer none): the #GPtrArray + * containing #NMLldpNeighbor<!-- -->s. This is the internal copy used by the + * device and must not be modified. The library never modifies the returned + * array and thus it is safe for callers to reference and keep using it. + * + * Since: 1.2 + **/ +GPtrArray * +nm_device_get_lldp_neighbors(NMDevice *device) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + priv = NM_DEVICE_GET_PRIVATE(device); + if (!priv->lldp_neighbors) + priv->lldp_neighbors = + g_ptr_array_new_with_free_func((GDestroyNotify) nm_lldp_neighbor_unref); + return priv->lldp_neighbors; +} + +/** + * nm_device_is_real: + * @device: a #NMDevice + * + * Returns: %TRUE if the device exists, or %FALSE if it is a placeholder device + * that could be automatically created by NetworkManager if one of its + * #NMDevice:available-connections was activated. + * + * Since: 1.2 + **/ +gboolean +nm_device_is_real(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + + return NM_DEVICE_GET_PRIVATE(device)->real; +} + +/** + * nm_device_is_software: + * @device: a #NMDevice + * + * Whether the device is a software device. + * + * Returns: %TRUE if @device is a software device, %FALSE if it is a hardware device. + **/ +gboolean +nm_device_is_software(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + + return !!(NM_DEVICE_GET_PRIVATE(device)->capabilities & NM_DEVICE_CAP_IS_SOFTWARE); +} + +/** + * nm_device_reapply: + * @device: a #NMDevice + * @connection: (allow-none): the #NMConnection to replace the applied + * settings with or %NULL to reuse existing + * @version_id: zero or the expected version id of the applied connection. + * If specified and the version id mismatches, the call fails without + * modification. This allows to catch concurrent accesses. + * @flags: always set this to zero + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Attempts to update device with changes to the currently active connection + * made since it was last applied. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + * + * Since: 1.2 + * + * Deprecated: 1.22: Use nm_device_reapply_async() or GDBusConnection. + **/ +gboolean +nm_device_reapply(NMDevice * device, + NMConnection *connection, + guint64 version_id, + guint32 flags, + GCancellable *cancellable, + GError ** error) +{ + GVariant *arg_connection = NULL; + + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + g_return_val_if_fail(!connection || NM_IS_CONNECTION(connection), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + if (connection) + arg_connection = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL); + if (!arg_connection) + arg_connection = nm_g_variant_singleton_aLsaLsvII(); + + return _nm_client_dbus_call_sync_void( + _nm_object_get_client(device), + cancellable, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Reapply", + g_variant_new("(@a{sa{sv}}tu)", arg_connection, version_id, flags), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_device_reapply_async: + * @device: a #NMDevice + * @connection: (allow-none): the #NMConnection to replace the applied + * settings with or %NULL to reuse existing + * @version_id: zero or the expected version id of the applied + * connection. If specified and the version id mismatches, the call + * fails without modification. This allows to catch concurrent + * accesses. + * @flags: always set this to zero + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the reapply operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously begins an attempt to update device with changes to the + * currently active connection made since it was last applied. + * + * Since: 1.2 + **/ +void +nm_device_reapply_async(NMDevice * device, + NMConnection * connection, + guint64 version_id, + guint32 flags, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GVariant *arg_connection = NULL; + + g_return_if_fail(NM_IS_DEVICE(device)); + g_return_if_fail(!connection || NM_IS_CONNECTION(connection)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + if (connection) + arg_connection = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL); + if (!arg_connection) + arg_connection = nm_g_variant_singleton_aLsaLsvII(); + + _nm_client_dbus_call(_nm_object_get_client(device), + device, + nm_device_reapply_async, + cancellable, + callback, + user_data, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Reapply", + g_variant_new("(@a{sa{sv}}tu)", arg_connection, version_id, flags), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_device_reapply_finish: + * @device: a #NMDevice + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_device_reapply_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error + * will be set. + * + * Since: 1.2 + **/ +gboolean +nm_device_reapply_finish(NMDevice *device, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_reapply_async), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +/** + * nm_device_get_applied_connection: + * @device: a #NMDevice + * @flags: the flags argument. Currently, this value must always be zero. + * @version_id: (out) (allow-none): returns the current version id of + * the applied connection + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Fetch the currently applied connection on the device. + * + * Returns: (transfer full): a %NMConnection with the currently applied settings + * or %NULL on error. + * + * The connection is as received from D-Bus and might not validate according + * to nm_connection_verify(). + * + * Since: 1.2 + * + * Deprecated: 1.22: Use nm_device_get_applied_connection_async() or GDBusConnection. + **/ +NMConnection * +nm_device_get_applied_connection(NMDevice * device, + guint32 flags, + guint64 * version_id, + GCancellable *cancellable, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + gs_unref_variant GVariant *v_connection = NULL; + guint64 v_version_id; + NMConnection * connection; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(!error || !*error, NULL); + + ret = _nm_client_dbus_call_sync(_nm_object_get_client(device), + cancellable, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "GetAppliedConnection", + g_variant_new("(u)", flags), + G_VARIANT_TYPE("(a{sa{sv}}t)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) + return NULL; + + g_variant_get(ret, "(@a{sa{sv}}t)", &v_connection, &v_version_id); + + connection = _nm_simple_connection_new_from_dbus(v_connection, + NM_SETTING_PARSE_FLAGS_BEST_EFFORT, + error); + if (!connection) + return NULL; + + NM_SET_OUT(version_id, v_version_id); + return connection; +} + +/** + * nm_device_get_applied_connection_async: + * @device: a #NMDevice + * @flags: the flags argument. Currently, this value must always be zero. + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the reapply operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously begins and gets the currently applied connection. + * + * Since: 1.2 + **/ +void +nm_device_get_applied_connection_async(NMDevice * device, + guint32 flags, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_DEVICE(device)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(_nm_object_get_client(device), + device, + nm_device_get_applied_connection_async, + cancellable, + callback, + user_data, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "GetAppliedConnection", + g_variant_new("(u)", flags), + G_VARIANT_TYPE("(a{sa{sv}}t)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); +} + +/** + * nm_device_get_applied_connection_finish: + * @device: a #NMDevice + * @result: the result passed to the #GAsyncReadyCallback + * @version_id: (out) (allow-none): the current version id of the applied + * connection. + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_device_get_applied_connection_async(). + * + * Returns: (transfer full): a currently applied %NMConnection or %NULL in case + * of error. + * + * The connection is as received from D-Bus and might not validate according + * to nm_connection_verify(). + * + * Since: 1.2 + **/ +NMConnection * +nm_device_get_applied_connection_finish(NMDevice * device, + GAsyncResult *result, + guint64 * version_id, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + gs_unref_variant GVariant *v_connection = NULL; + guint64 v_version_id; + NMConnection * connection; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_get_applied_connection_async), + NULL); + g_return_val_if_fail(!error || !*error, NULL); + + ret = g_task_propagate_pointer(G_TASK(result), error); + if (!ret) + return NULL; + + g_variant_get(ret, "(@a{sa{sv}}t)", &v_connection, &v_version_id); + + connection = _nm_simple_connection_new_from_dbus(v_connection, + NM_SETTING_PARSE_FLAGS_BEST_EFFORT, + error); + if (!connection) + return NULL; + + NM_SET_OUT(version_id, v_version_id); + return connection; +} + +/*****************************************************************************/ + +/** + * nm_device_disconnect: + * @device: a #NMDevice + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Disconnects the device if currently connected, and prevents the device from + * automatically connecting to networks until the next manual network connection + * request. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + * + * Deprecated: 1.22: Use nm_device_disconnect_async() or GDBusConnection. + **/ +gboolean +nm_device_disconnect(NMDevice *device, GCancellable *cancellable, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + return _nm_client_dbus_call_sync_void(_nm_object_get_client(device), + cancellable, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Disconnect", + g_variant_new("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_device_disconnect_async: + * @device: a #NMDevice + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the disconnect operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously begins disconnecting the device if currently connected, and + * prevents the device from automatically connecting to networks until the next + * manual network connection request. + **/ +void +nm_device_disconnect_async(NMDevice * device, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_DEVICE(device)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(_nm_object_get_client(device), + device, + nm_device_disconnect_async, + cancellable, + callback, + user_data, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Disconnect", + g_variant_new("()"), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_device_disconnect_finish: + * @device: a #NMDevice + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_device_disconnect_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error + * will be set. + **/ +gboolean +nm_device_disconnect_finish(NMDevice *device, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_disconnect_async), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/** + * nm_device_delete: + * @device: a #NMDevice + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Deletes the software device. Hardware devices can't be deleted. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error + * will be set. + * + * Deprecated: 1.22: Use nm_device_delete_async() or GDBusConnection. + **/ +gboolean +nm_device_delete(NMDevice *device, GCancellable *cancellable, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + return _nm_client_dbus_call_sync_void(_nm_object_get_client(device), + cancellable, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Delete", + g_variant_new("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_device_delete_async: + * @device: a #NMDevice + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when delete operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously begins deleting the software device. Hardware devices can't + * be deleted. + **/ +void +nm_device_delete_async(NMDevice * device, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_DEVICE(device)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(_nm_object_get_client(device), + device, + nm_device_delete_async, + cancellable, + callback, + user_data, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Delete", + g_variant_new("()"), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_device_delete_finish: + * @device: a #NMDevice + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_device_delete_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error + * will be set. + **/ +gboolean +nm_device_delete_finish(NMDevice *device, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_delete_async), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/** + * nm_device_connection_valid: + * @device: an #NMDevice to validate @connection against + * @connection: an #NMConnection to validate against @device + * + * Validates a given connection for a given #NMDevice object and returns + * whether the connection may be activated with the device. For example if + * @device is a Wi-Fi device that supports only WEP encryption, the connection + * will only be valid if it is a Wi-Fi connection which describes a WEP or open + * network, and will not be valid if it describes a WPA network, or if it is + * an Ethernet, Bluetooth, WWAN, etc connection that is incompatible with the + * device. + * + * Returns: %TRUE if the connection may be activated with this device, %FALSE + * if is incompatible with the device's capabilities and characteristics. + **/ +gboolean +nm_device_connection_valid(NMDevice *device, NMConnection *connection) +{ + return nm_device_connection_compatible(device, connection, NULL); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + const char *config_iface, *device_iface; + GError * local = NULL; + + if (!nm_connection_verify(connection, &local)) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection was not valid: %s"), + local->message); + g_error_free(local); + return FALSE; + } + + config_iface = nm_connection_get_interface_name(connection); + device_iface = nm_device_get_iface(device); + if (config_iface && g_strcmp0(config_iface, device_iface) != 0) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The interface names of the device and the connection didn't match.")); + return FALSE; + } + + return TRUE; +} + +/** + * nm_device_connection_compatible: + * @device: an #NMDevice to validate @connection against + * @connection: an #NMConnection to validate against @device + * @error: return location for a #GError, or %NULL + * + * Validates a given connection for a given #NMDevice object and returns + * whether the connection may be activated with the device. For example if + * @device is a Wi-Fi device that supports only WEP encryption, the connection + * will only be valid if it is a Wi-Fi connection which describes a WEP or open + * network, and will not be valid if it describes a WPA network, or if it is + * an Ethernet, Bluetooth, WWAN, etc connection that is incompatible with the + * device. + * + * This function does the same as nm_device_connection_valid(), i.e. checking + * compatibility of the given device and connection. But, in addition, it sets + * GError when FALSE is returned. + * + * Returns: %TRUE if the connection may be activated with this device, %FALSE + * if is incompatible with the device's capabilities and characteristics. + **/ +gboolean +nm_device_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + g_return_val_if_fail(NM_IS_CONNECTION(connection), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + return NM_DEVICE_GET_CLASS(device)->connection_compatible(device, connection, error); +} + +/** + * nm_device_filter_connections: + * @device: an #NMDevice to filter connections for + * @connections: (element-type NMConnection): an array of #NMConnections to filter + * + * Filters a given array of connections for a given #NMDevice object and returns + * connections which may be activated with the device. For example if @device + * is a Wi-Fi device that supports only WEP encryption, the returned array will + * contain any Wi-Fi connections in @connections that allow connection to + * unencrypted or WEP-enabled SSIDs. The returned array will not contain + * Ethernet, Bluetooth, Wi-Fi WPA connections, or any other connection that is + * incompatible with the device. To get the full list of connections see + * nm_client_get_connections(). + * + * Returns: (transfer full) (element-type NMConnection): an array of + * #NMConnections that could be activated with the given @device. The array + * should be freed with g_ptr_array_unref() when it is no longer required. + * + * WARNING: the transfer annotation for this function may not work correctly + * with bindings. See https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/305. + * You can filter the list yourself with nm_device_connection_valid(). + **/ +GPtrArray * +nm_device_filter_connections(NMDevice *device, const GPtrArray *connections) +{ + GPtrArray *filtered; + int i; + + filtered = g_ptr_array_new_with_free_func(g_object_unref); + for (i = 0; i < connections->len; i++) { + NMConnection *candidate = connections->pdata[i]; + + /* Connection applies to this device */ + if (nm_device_connection_valid(device, candidate)) + g_ptr_array_add(filtered, g_object_ref(candidate)); + } + + return filtered; +} + +/** + * nm_device_get_setting_type: + * @device: an #NMDevice + * + * Gets the (primary) #NMSetting subtype associated with connections + * that can be used on @device. + * + * Returns: @device's associated #NMSetting type + */ +GType +nm_device_get_setting_type(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), G_TYPE_INVALID); + g_return_val_if_fail(NM_DEVICE_GET_CLASS(device)->get_setting_type != NULL, G_TYPE_INVALID); + + return NM_DEVICE_GET_CLASS(device)->get_setting_type(device); +} + +/*****************************************************************************/ + +static gboolean +NM_IS_LLDP_NEIGHBOR(const NMLldpNeighbor *self) +{ + nm_assert(!self || (self->refcount > 0 && self->attrs)); + return self && self->refcount > 0; +} + +/** + * nm_lldp_neighbor_new: + * + * Creates a new #NMLldpNeighbor object. + * + * Note that #NMLldpNeighbor has no public API for mutating + * an instance. Also, libnm will not internally mutate a + * once exposed object. They are guaranteed to be immutable. + * Since 1.32, ref-counting is thread-safe. + * + * This function is not useful, as there is no public API to + * actually modify the (empty) instance. + * + * Returns: (transfer full): the new #NMLldpNeighbor object. + * + * Since: 1.2 + **/ +NMLldpNeighbor * +nm_lldp_neighbor_new(void) +{ + NMLldpNeighbor *neigh; + + neigh = g_slice_new(NMLldpNeighbor); + *neigh = (NMLldpNeighbor){ + .refcount = 1, + .attrs = g_hash_table_new_full(nm_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_variant_unref), + }; + + return neigh; +} + +static NMLldpNeighbor * +_nm_lldp_neighbor_dup(NMLldpNeighbor *neighbor) +{ + /* There is no public API for mutating a NMLldpNeighbor. Nor should + * we ever add one, because immutable types (or at least, sealable types) + * are great. + * + * As such, dup is merely taking another ref. */ + nm_lldp_neighbor_ref(neighbor); + return neighbor; +} + +/** + * nm_lldp_neighbor_ref: + * @neighbor: the #NMLldpNeighbor + * + * Increases the reference count of the object. + * + * Since 1.32, ref-counting of #NMLldpNeighbor is thread-safe. + * + * Since: 1.2 + **/ +void +nm_lldp_neighbor_ref(NMLldpNeighbor *neighbor) +{ + g_return_if_fail(NM_IS_LLDP_NEIGHBOR(neighbor)); + + g_atomic_int_inc(&neighbor->refcount); +} + +/** + * nm_lldp_neighbor_unref: + * @neighbor: the #NMLldpNeighbor + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + * + * Since 1.32, ref-counting of #NMLldpNeighbor is thread-safe. + * + * Since: 1.2 + **/ +void +nm_lldp_neighbor_unref(NMLldpNeighbor *neighbor) +{ + g_return_if_fail(NM_IS_LLDP_NEIGHBOR(neighbor)); + + if (g_atomic_int_dec_and_test(&neighbor->refcount)) { + g_hash_table_unref(neighbor->attrs); + nm_g_slice_free(neighbor); + } +} + +/** + * nm_lldp_neighbor_get_attr_value: + * @neighbor: the #NMLldpNeighbor + * @name: the attribute name + * + * Gets the value (as a GVariant) of attribute with name @name on @neighbor + * + * Returns: (transfer none): the value or %NULL if the attribute with @name was + * not found. + * + * Since: 1.18 + **/ +GVariant * +nm_lldp_neighbor_get_attr_value(NMLldpNeighbor *neighbor, const char *name) +{ + g_return_val_if_fail(NM_IS_LLDP_NEIGHBOR(neighbor), FALSE); + g_return_val_if_fail(name && name[0], FALSE); + + return g_hash_table_lookup(neighbor->attrs, name); +} + +/** + * nm_lldp_neighbor_get_attr_names: + * @neighbor: the #NMLldpNeighbor + * + * Gets an array of attribute names available for @neighbor. + * + * Returns: (transfer full): a %NULL-terminated array of attribute names. + * + * Since: 1.2 + **/ +char ** +nm_lldp_neighbor_get_attr_names(NMLldpNeighbor *neighbor) +{ + const char **keys; + + g_return_val_if_fail(NM_IS_LLDP_NEIGHBOR(neighbor), NULL); + + keys = nm_utils_strdict_get_keys(neighbor->attrs, TRUE, NULL); + + return nm_utils_strv_make_deep_copied_nonnull(keys); +} + +/** + * nm_lldp_neighbor_get_attr_string_value: + * @neighbor: the #NMLldpNeighbor + * @name: the attribute name + * @out_value: (out) (allow-none) (transfer none): on return, the attribute value + * + * Gets the string value of attribute with name @name on @neighbor + * + * Returns: %TRUE if a string attribute with name @name was found, %FALSE otherwise + * + * Since: 1.2 + **/ +gboolean +nm_lldp_neighbor_get_attr_string_value(NMLldpNeighbor *neighbor, + const char * name, + const char ** out_value) +{ + GVariant *variant; + + variant = nm_lldp_neighbor_get_attr_value(neighbor, name); + if (!variant || !g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)) + return FALSE; + + NM_SET_OUT(out_value, g_variant_get_string(variant, NULL)); + return TRUE; +} + +/** + * nm_lldp_neighbor_get_attr_uint_value: + * @neighbor: the #NMLldpNeighbor + * @name: the attribute name + * @out_value: (out) (allow-none): on return, the attribute value + * + * Gets the uint32 value of attribute with name @name on @neighbor + * + * Returns: %TRUE if a uint32 attribute with name @name was found, %FALSE otherwise + * + * Since: 1.2 + **/ +gboolean +nm_lldp_neighbor_get_attr_uint_value(NMLldpNeighbor *neighbor, const char *name, guint *out_value) +{ + GVariant *variant; + + variant = nm_lldp_neighbor_get_attr_value(neighbor, name); + if (!variant || !g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32)) + return FALSE; + + NM_SET_OUT(out_value, g_variant_get_uint32(variant)); + return TRUE; +} + +/** + * nm_lldp_neighbor_get_attr_type: + * @neighbor: the #NMLldpNeighbor + * @name: the attribute name + * + * Get the type of an attribute. + * + * Returns: the #GVariantType of the attribute with name @name + * + * Since: 1.2 + **/ +const GVariantType * +nm_lldp_neighbor_get_attr_type(NMLldpNeighbor *neighbor, const char *name) +{ + GVariant *variant; + + variant = nm_lldp_neighbor_get_attr_value(neighbor, name); + if (!variant) + return NULL; + + return g_variant_get_type(variant); +} diff --git a/src/libnm-client-impl/nm-dhcp-config.c b/src/libnm-client-impl/nm-dhcp-config.c new file mode 100644 index 00000000..c7f9a87a --- /dev/null +++ b/src/libnm-client-impl/nm-dhcp-config.c @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2008 - 2014 Red Hat, Inc. + * Copyright (C) 2008 Novell, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-dhcp-config.h" + +#include "nm-dhcp4-config.h" +#include "nm-dhcp6-config.h" +#include "nm-dbus-interface.h" +#include "nm-object-private.h" +#include "nm-utils.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMDhcpConfig, PROP_FAMILY, PROP_OPTIONS, ); + +typedef struct _NMDhcpConfigPrivate { + GHashTable *options; +} NMDhcpConfigPrivate; + +G_DEFINE_ABSTRACT_TYPE(NMDhcpConfig, nm_dhcp_config, NM_TYPE_OBJECT) + +#define NM_DHCP_CONFIG_GET_PRIVATE(self) \ + _NM_GET_PRIVATE_PTR(self, NMDhcpConfig, NM_IS_DHCP_CONFIG, NMObject) + +/*****************************************************************************/ + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_options(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMDhcpConfig * self = NM_DHCP_CONFIG(dbobj->nmobj); + NMDhcpConfigPrivate *priv = NM_DHCP_CONFIG_GET_PRIVATE(self); + + g_hash_table_remove_all(priv->options); + + if (value) { + GVariantIter iter; + const char * key; + GVariant * opt; + + g_variant_iter_init(&iter, value); + while (g_variant_iter_next(&iter, "{&sv}", &key, &opt)) { + if (g_variant_is_of_type(opt, G_VARIANT_TYPE_STRING)) + g_hash_table_insert(priv->options, g_strdup(key), g_variant_dup_string(opt, NULL)); + g_variant_unref(opt); + } + } + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/*****************************************************************************/ + +static void +nm_dhcp_config_init(NMDhcpConfig *self) +{ + NMDhcpConfigPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_DHCP_CONFIG, NMDhcpConfigPrivate); + + self->_priv = priv; + + priv->options = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); +} + +static void +finalize(GObject *object) +{ + NMDhcpConfigPrivate *priv = NM_DHCP_CONFIG_GET_PRIVATE(object); + + g_hash_table_destroy(priv->options); + + G_OBJECT_CLASS(nm_dhcp_config_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDhcpConfig *self = NM_DHCP_CONFIG(object); + + switch (prop_id) { + case PROP_FAMILY: + g_value_set_int(value, nm_dhcp_config_get_family(self)); + break; + case PROP_OPTIONS: + g_value_set_boxed(value, nm_dhcp_config_get_options(self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_dhcp4config = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DHCP4_CONFIG, + nm_dhcp4_config_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("Options", + PROP_OPTIONS, + "a{sv}", + _notify_update_prop_options), ), + .base_struct_offset = G_STRUCT_OFFSET(NMDhcpConfig, _priv), ); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_dhcp6config = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DHCP6_CONFIG, + nm_dhcp6_config_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("Options", + PROP_OPTIONS, + "a{sv}", + _notify_update_prop_options), ), + .base_struct_offset = G_STRUCT_OFFSET(NMDhcpConfig, _priv), ); + +static void +nm_dhcp_config_class_init(NMDhcpConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(config_class); + + g_type_class_add_private(config_class, sizeof(NMDhcpConfigPrivate)); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + /** + * NMDhcpConfig:family: + * + * The IP address family of the configuration; either + * <literal>AF_INET</literal> or <literal>AF_INET6</literal>. + **/ + obj_properties[PROP_FAMILY] = g_param_spec_int(NM_DHCP_CONFIG_FAMILY, + "", + "", + 0, + 255, + AF_UNSPEC, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDhcpConfig:options: (type GHashTable(utf8,utf8)): + * + * The #GHashTable containing options of the configuration. + **/ + obj_properties[PROP_OPTIONS] = g_param_spec_boxed(NM_DHCP_CONFIG_OPTIONS, + "", + "", + G_TYPE_HASH_TABLE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_dhcp4config, + &_nml_dbus_meta_iface_nm_dhcp6config); +} + +/** + * nm_dhcp_config_get_family: + * @config: a #NMDhcpConfig + * + * Gets the IP address family of the configuration + * + * Returns: the IP address family; either <literal>AF_INET</literal> or + * <literal>AF_INET6</literal> + **/ +int +nm_dhcp_config_get_family(NMDhcpConfig *config) +{ + g_return_val_if_fail(NM_IS_DHCP_CONFIG(config), AF_UNSPEC); + + return NM_IS_DHCP4_CONFIG(config) ? AF_INET : AF_INET6; +} + +/** + * nm_dhcp_config_get_options: + * @config: a #NMDhcpConfig + * + * Gets all the options contained in the configuration. + * + * Returns: (transfer none) (element-type utf8 utf8): the #GHashTable containing + * strings for keys and values. This is the internal copy used by the + * configuration, and must not be modified. + **/ +GHashTable * +nm_dhcp_config_get_options(NMDhcpConfig *config) +{ + g_return_val_if_fail(NM_IS_DHCP_CONFIG(config), NULL); + + return NM_DHCP_CONFIG_GET_PRIVATE(config)->options; +} + +/** + * nm_dhcp_config_get_one_option: + * @config: a #NMDhcpConfig + * @option: the option to retrieve + * + * Gets one option by option name. + * + * Returns: the configuration option's value. This is the internal string used by the + * configuration, and must not be modified. + **/ +const char * +nm_dhcp_config_get_one_option(NMDhcpConfig *config, const char *option) +{ + g_return_val_if_fail(NM_IS_DHCP_CONFIG(config), NULL); + + return g_hash_table_lookup(nm_dhcp_config_get_options(config), option); +} diff --git a/src/libnm-client-impl/nm-dhcp4-config.c b/src/libnm-client-impl/nm-dhcp4-config.c new file mode 100644 index 00000000..8ce8415f --- /dev/null +++ b/src/libnm-client-impl/nm-dhcp4-config.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-dhcp4-config.h" + +/*****************************************************************************/ + +struct _NMDhcp4Config { + NMDhcpConfig parent; +}; + +struct _NMDhcp4ConfigClass { + NMDhcpConfigClass parent; +}; + +G_DEFINE_TYPE(NMDhcp4Config, nm_dhcp4_config, NM_TYPE_DHCP_CONFIG) + +/*****************************************************************************/ + +static void +nm_dhcp4_config_init(NMDhcp4Config *config) +{} + +static void +nm_dhcp4_config_class_init(NMDhcp4ConfigClass *config_class) +{} diff --git a/src/libnm-client-impl/nm-dhcp4-config.h b/src/libnm-client-impl/nm-dhcp4-config.h new file mode 100644 index 00000000..d0ea2b46 --- /dev/null +++ b/src/libnm-client-impl/nm-dhcp4-config.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#ifndef __NM_DHCP4_CONFIG_H__ +#define __NM_DHCP4_CONFIG_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#include "nm-dhcp-config.h" + +#define NM_TYPE_DHCP4_CONFIG (nm_dhcp4_config_get_type()) +#define NM_DHCP4_CONFIG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP4_CONFIG, NMDhcp4Config)) +#define NM_DHCP4_CONFIG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP4_CONFIG, NMDhcp4ConfigClass)) +#define NM_IS_DHCP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP4_CONFIG)) +#define NM_IS_DHCP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DHCP4_CONFIG)) +#define NM_DHCP4_CONFIG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DHCP4_CONFIG, NMDhcp4ConfigClass)) + +/** + * NMDhcp4Config: + */ +typedef struct _NMDhcp4Config NMDhcp4Config; +typedef struct _NMDhcp4ConfigClass NMDhcp4ConfigClass; + +GType nm_dhcp4_config_get_type(void); + +#endif /* __NM_DHCP4_CONFIG_H__ */ diff --git a/src/libnm-client-impl/nm-dhcp6-config.c b/src/libnm-client-impl/nm-dhcp6-config.c new file mode 100644 index 00000000..7db02640 --- /dev/null +++ b/src/libnm-client-impl/nm-dhcp6-config.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-dhcp6-config.h" + +/*****************************************************************************/ + +struct _NMDhcp6Config { + NMDhcpConfig parent; +}; + +struct _NMDhcp6ConfigClass { + NMDhcpConfigClass parent; +}; + +G_DEFINE_TYPE(NMDhcp6Config, nm_dhcp6_config, NM_TYPE_DHCP_CONFIG) + +/*****************************************************************************/ + +static void +nm_dhcp6_config_init(NMDhcp6Config *config) +{} + +static void +nm_dhcp6_config_class_init(NMDhcp6ConfigClass *config_class) +{} diff --git a/src/libnm-client-impl/nm-dhcp6-config.h b/src/libnm-client-impl/nm-dhcp6-config.h new file mode 100644 index 00000000..253594e6 --- /dev/null +++ b/src/libnm-client-impl/nm-dhcp6-config.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#ifndef __NM_DHCP6_CONFIG_H__ +#define __NM_DHCP6_CONFIG_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#include "nm-dhcp-config.h" + +#define NM_TYPE_DHCP6_CONFIG (nm_dhcp6_config_get_type()) +#define NM_DHCP6_CONFIG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP6_CONFIG, NMDhcp6Config)) +#define NM_DHCP6_CONFIG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP6_CONFIG, NMDhcp6ConfigClass)) +#define NM_IS_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP6_CONFIG)) +#define NM_IS_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DHCP6_CONFIG)) +#define NM_DHCP6_CONFIG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DHCP6_CONFIG, NMDhcp6ConfigClass)) + +/** + * NMDhcp6Config: + */ +typedef struct _NMDhcp6Config NMDhcp6Config; +typedef struct _NMDhcp6ConfigClass NMDhcp6ConfigClass; + +GType nm_dhcp6_config_get_type(void); + +#endif /* __NM_DHCP6_CONFIG_H__ */ diff --git a/src/libnm-client-impl/nm-dns-manager.c b/src/libnm-client-impl/nm-dns-manager.c new file mode 100644 index 00000000..f7c564ff --- /dev/null +++ b/src/libnm-client-impl/nm-dns-manager.c @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2016 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-dns-manager.h" + +#include "nm-dbus-interface.h" +#include "nm-connection.h" +#include "nm-client.h" +#include "nm-object-private.h" +#include "nm-dbus-helpers.h" +#include "libnm-core-intern/nm-core-internal.h" + +/***************************************************************************** + * NMDnsEntry + *****************************************************************************/ + +G_DEFINE_BOXED_TYPE(NMDnsEntry, nm_dns_entry, nm_dns_entry_dup, nm_dns_entry_unref) + +struct NMDnsEntry { + guint refcount; + + char * interface; + char ** nameservers; + char ** domains; + int priority; + gboolean vpn; +}; + +/** + * nm_dns_entry_new: + * + * Creates a new #NMDnsEntry object. + * + * Returns: (transfer full): the new #NMDnsEntry object, or %NULL on error + **/ +NMDnsEntry * +nm_dns_entry_new(const char * interface, + const char *const *nameservers, + const char *const *domains, + int priority, + gboolean vpn) +{ + NMDnsEntry *entry; + guint i, len; + + entry = g_slice_new0(NMDnsEntry); + entry->refcount = 1; + + entry->interface = g_strdup(interface); + + if (nameservers) { + len = g_strv_length((char **) nameservers); + entry->nameservers = g_new(char *, len + 1); + for (i = 0; i < len + 1; i++) + entry->nameservers[i] = g_strdup(nameservers[i]); + } + + if (domains) { + len = g_strv_length((char **) domains); + entry->domains = g_new(char *, len + 1); + for (i = 0; i < len + 1; i++) + entry->domains[i] = g_strdup(domains[i]); + } + + entry->priority = priority; + entry->vpn = vpn; + + return entry; +} + +/** + * nm_dns_entry_dup: + * @entry: the #NMDnsEntry + * + * Creates a copy of @entry + * + * Returns: (transfer full): a copy of @entry + **/ +NMDnsEntry * +nm_dns_entry_dup(NMDnsEntry *entry) +{ + NMDnsEntry *copy; + + g_return_val_if_fail(entry != NULL, NULL); + g_return_val_if_fail(entry->refcount > 0, NULL); + + copy = nm_dns_entry_new(entry->interface, + (const char *const *) entry->nameservers, + (const char *const *) entry->domains, + entry->priority, + entry->vpn); + + return copy; +} + +/** + * nm_dns_entry_unref: + * @entry: the #NMDnsEntry + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + * + * Since: 1.6 + **/ +void +nm_dns_entry_unref(NMDnsEntry *entry) +{ + g_return_if_fail(entry != NULL); + g_return_if_fail(entry->refcount > 0); + + entry->refcount--; + if (entry->refcount == 0) { + g_free(entry->interface); + g_strfreev(entry->nameservers); + g_strfreev(entry->domains); + g_slice_free(NMDnsEntry, entry); + } +} + +/** + * nm_dns_entry_get_interface: + * @entry: the #NMDnsEntry + * + * Gets the interface on which name servers are contacted. + * + * Returns: (transfer none): the interface name + * + * Since: 1.6 + **/ +const char * +nm_dns_entry_get_interface(NMDnsEntry *entry) +{ + g_return_val_if_fail(entry, 0); + g_return_val_if_fail(entry->refcount > 0, 0); + + return entry->interface; +} + +/** + * nm_dns_entry_get_nameservers: + * @entry: the #NMDnsEntry + * + * Gets the list of name servers for this entry. + * + * Returns: (transfer none): the list of name servers + * + * Since: 1.6 + **/ +const char *const * +nm_dns_entry_get_nameservers(NMDnsEntry *entry) +{ + g_return_val_if_fail(entry, 0); + g_return_val_if_fail(entry->refcount > 0, 0); + + return (const char *const *) entry->nameservers; +} + +/** + * nm_dns_entry_get_domains: + * @entry: the #NMDnsEntry + * + * Gets the list of DNS domains. + * + * Returns: (transfer none): the list of DNS domains + * + * Since: 1.6 + **/ +const char *const * +nm_dns_entry_get_domains(NMDnsEntry *entry) +{ + g_return_val_if_fail(entry, 0); + g_return_val_if_fail(entry->refcount > 0, 0); + + return (const char *const *) entry->domains; +} + +/** + * nm_dns_entry_get_vpn: + * @entry: the #NMDnsEntry + * + * Gets whether the entry refers to VPN name servers. + * + * Returns: %TRUE if the entry refers to VPN name servers + * + * Since: 1.6 + **/ +gboolean +nm_dns_entry_get_vpn(NMDnsEntry *entry) +{ + g_return_val_if_fail(entry, 0); + g_return_val_if_fail(entry->refcount > 0, 0); + + return entry->vpn; +} + +/** + * nm_dns_entry_get_priority: + * @entry: the #NMDnsEntry + * + * Gets the priority of the entry + * + * Returns: the priority of the entry + * + * Since: 1.6 + **/ +int +nm_dns_entry_get_priority(NMDnsEntry *entry) +{ + g_return_val_if_fail(entry, 0); + g_return_val_if_fail(entry->refcount > 0, 0); + + return entry->priority; +} diff --git a/src/libnm-client-impl/nm-dns-manager.h b/src/libnm-client-impl/nm-dns-manager.h new file mode 100644 index 00000000..8053b243 --- /dev/null +++ b/src/libnm-client-impl/nm-dns-manager.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2016 Red Hat, Inc. + */ + +#ifndef __NM_DNS_MANAGER_H__ +#define __NM_DNS_MANAGER_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#include "nm-client.h" + +NMDnsEntry *nm_dns_entry_new(const char * interface, + const char *const *nameservers, + const char *const *domains, + int priority, + gboolean vpn); +NMDnsEntry *nm_dns_entry_dup(NMDnsEntry *entry); + +#endif /* __NM_DNS_MANAGER_H__ */ diff --git a/src/libnm-client-impl/nm-ip-config.c b/src/libnm-client-impl/nm-ip-config.c new file mode 100644 index 00000000..099dd088 --- /dev/null +++ b/src/libnm-client-impl/nm-ip-config.c @@ -0,0 +1,644 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2011 Novell, Inc. + * Copyright (C) 2008 - 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-ip-config.h" + +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" +#include "nm-setting-ip-config.h" +#include "nm-dbus-interface.h" +#include "nm-object-private.h" +#include "nm-utils.h" +#include "libnm-core-intern/nm-core-internal.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMIPConfig, + PROP_FAMILY, + PROP_GATEWAY, + PROP_ADDRESSES, + PROP_ROUTES, + PROP_NAMESERVERS, + PROP_DOMAINS, + PROP_SEARCHES, + PROP_WINS_SERVERS, ); + +typedef struct _NMIPConfigPrivate { + GPtrArray *addresses; + GPtrArray *routes; + char ** nameservers; + char ** domains; + char ** searches; + char ** wins_servers; + char * gateway; + + bool addresses_new_style : 1; + bool routes_new_style : 1; + bool nameservers_new_style : 1; + bool wins_servers_new_style : 1; +} NMIPConfigPrivate; + +G_DEFINE_ABSTRACT_TYPE(NMIPConfig, nm_ip_config, NM_TYPE_OBJECT) + +#define NM_IP_CONFIG_GET_PRIVATE(self) \ + _NM_GET_PRIVATE_PTR(self, NMIPConfig, NM_IS_IP_CONFIG, NMObject) + +/*****************************************************************************/ + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_addresses(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMIPConfig * self = NM_IP_CONFIG(dbobj->nmobj); + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + gs_unref_ptrarray GPtrArray *addresses_old = NULL; + gs_unref_ptrarray GPtrArray *addresses_new = NULL; + int addr_family = meta_iface == &_nml_dbus_meta_iface_nm_ip4config ? AF_INET : AF_INET6; + gboolean new_style; + + new_style = + (((const char *) meta_iface->dbus_properties[dbus_property_idx].dbus_type)[2] == '{'); + + if (priv->addresses_new_style) { + if (!new_style) + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; + } else + priv->addresses_new_style = new_style; + + if (value) { + if (new_style) + addresses_new = nm_utils_ip_addresses_from_variant(value, addr_family); + else if (addr_family == AF_INET) + addresses_new = nm_utils_ip4_addresses_from_variant(value, NULL); + else + addresses_new = nm_utils_ip6_addresses_from_variant(value, NULL); + nm_assert(addresses_new); + } + if (!addresses_new) + addresses_new = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_address_unref); + + addresses_old = priv->addresses; + priv->addresses = g_steal_pointer(&addresses_new); + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_routes(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMIPConfig * self = NM_IP_CONFIG(dbobj->nmobj); + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + gs_unref_ptrarray GPtrArray *routes_old = NULL; + gs_unref_ptrarray GPtrArray *routes_new = NULL; + int addr_family = meta_iface == &_nml_dbus_meta_iface_nm_ip4config ? AF_INET : AF_INET6; + gboolean new_style; + + new_style = + (((const char *) meta_iface->dbus_properties[dbus_property_idx].dbus_type)[2] == '{'); + + if (priv->routes_new_style) { + if (!new_style) + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; + } else + priv->routes_new_style = new_style; + + if (value) { + if (new_style) + routes_new = nm_utils_ip_routes_from_variant(value, addr_family); + else if (addr_family == AF_INET) + routes_new = nm_utils_ip4_routes_from_variant(value); + else + routes_new = nm_utils_ip6_routes_from_variant(value); + nm_assert(routes_new); + } + if (!routes_new) + routes_new = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_route_unref); + + routes_old = priv->routes; + priv->routes = g_steal_pointer(&routes_new); + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_nameservers(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMIPConfig * self = NM_IP_CONFIG(dbobj->nmobj); + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + gs_strfreev char **nameservers_new = NULL; + gboolean new_style = TRUE; + int addr_family = meta_iface == &_nml_dbus_meta_iface_nm_ip4config ? AF_INET : AF_INET6; + + if (addr_family == AF_INET) { + new_style = + (((const char *) meta_iface->dbus_properties[dbus_property_idx].dbus_type)[1] == 'a'); + + if (priv->nameservers_new_style) { + if (!new_style) + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; + } else + priv->nameservers_new_style = new_style; + } + + if (value) { + if (addr_family == AF_INET6) + nameservers_new = nm_utils_ip6_dns_from_variant(value); + else if (!new_style) + nameservers_new = nm_utils_ip4_dns_from_variant(value); + else { + GVariantIter iter; + GVariantIter * iter_v; + gs_unref_ptrarray GPtrArray *arr = NULL; + + g_variant_iter_init(&iter, value); + while (g_variant_iter_next(&iter, "a{sv}", &iter_v)) { + const char *key; + GVariant * val; + + while (g_variant_iter_next(iter_v, "{&sv}", &key, &val)) { + if (nm_streq(key, "address")) { + gs_free char *val_str = NULL; + + if (!g_variant_is_of_type(val, G_VARIANT_TYPE_STRING)) + goto next; + if (!nm_utils_parse_inaddr(AF_INET, + g_variant_get_string(val, NULL), + &val_str)) + goto next; + if (!arr) + arr = g_ptr_array_new(); + g_ptr_array_add(arr, g_steal_pointer(&val_str)); + goto next; + } +next: + g_variant_unref(val); + } + g_variant_iter_free(iter_v); + } + if (arr && arr->len > 0) + nameservers_new = nm_utils_strv_dup((char **) arr->pdata, arr->len, FALSE); + else + nameservers_new = g_new0(char *, 1); + } + nm_assert(nameservers_new); + } + + g_strfreev(priv->nameservers); + priv->nameservers = g_steal_pointer(&nameservers_new); + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_wins_servers(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMIPConfig * self = NM_IP_CONFIG(dbobj->nmobj); + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + gs_strfreev char **wins_servers_new = NULL; + gboolean new_style; + + new_style = + (((const char *) meta_iface->dbus_properties[dbus_property_idx].dbus_type)[1] == 's'); + + if (priv->wins_servers_new_style) { + if (!new_style) + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; + } else + priv->wins_servers_new_style = new_style; + + if (value) { + if (new_style) + wins_servers_new = g_variant_dup_strv(value, NULL); + else + wins_servers_new = nm_utils_ip4_dns_from_variant(value); + nm_assert(wins_servers_new); + } + + g_strfreev(priv->wins_servers); + priv->wins_servers = g_steal_pointer(&wins_servers_new); + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMIPConfig *self = NM_IP_CONFIG(object); + + switch (prop_id) { + case PROP_FAMILY: + g_value_set_int(value, nm_ip_config_get_family(self)); + break; + case PROP_GATEWAY: + g_value_set_string(value, nm_ip_config_get_gateway(self)); + break; + case PROP_ADDRESSES: + g_value_take_boxed(value, + _nm_utils_copy_array(nm_ip_config_get_addresses(self), + (NMUtilsCopyFunc) nm_ip_address_dup, + (GDestroyNotify) nm_ip_address_unref)); + break; + case PROP_ROUTES: + g_value_take_boxed(value, + _nm_utils_copy_array(nm_ip_config_get_routes(self), + (NMUtilsCopyFunc) nm_ip_route_dup, + (GDestroyNotify) nm_ip_route_unref)); + break; + case PROP_NAMESERVERS: + g_value_set_boxed(value, (char **) nm_ip_config_get_nameservers(self)); + break; + case PROP_DOMAINS: + g_value_set_boxed(value, (char **) nm_ip_config_get_domains(self)); + break; + case PROP_SEARCHES: + g_value_set_boxed(value, (char **) nm_ip_config_get_searches(self)); + break; + case PROP_WINS_SERVERS: + g_value_set_boxed(value, (char **) nm_ip_config_get_wins_servers(self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_ip_config_init(NMIPConfig *self) +{ + NMIPConfigPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_IP_CONFIG, NMIPConfigPrivate); + + self->_priv = priv; + + priv->addresses = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_address_unref); + priv->routes = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_route_unref); +} + +static void +finalize(GObject *object) +{ + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(object); + + g_free(priv->gateway); + + g_ptr_array_unref(priv->routes); + g_ptr_array_unref(priv->addresses); + + g_strfreev(priv->nameservers); + g_strfreev(priv->domains); + g_strfreev(priv->searches); + g_strfreev(priv->wins_servers); + + G_OBJECT_CLASS(nm_ip_config_parent_class)->finalize(object); +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_ip4config = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_IP4_CONFIG, + nm_ip4_config_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("AddressData", + PROP_ADDRESSES, + "aa{sv}", + _notify_update_prop_addresses), + NML_DBUS_META_PROPERTY_INIT_FCN("Addresses", + PROP_ADDRESSES, + "aau", + _notify_update_prop_addresses, + .obj_property_no_reverse_idx = TRUE), + NML_DBUS_META_PROPERTY_INIT_TODO("DnsOptions", "as"), + NML_DBUS_META_PROPERTY_INIT_TODO("DnsPriority", "i"), + NML_DBUS_META_PROPERTY_INIT_AS("Domains", PROP_DOMAINS, NMIPConfigPrivate, domains), + NML_DBUS_META_PROPERTY_INIT_S("Gateway", PROP_GATEWAY, NMIPConfigPrivate, gateway), + NML_DBUS_META_PROPERTY_INIT_FCN("NameserverData", + PROP_NAMESERVERS, + "aa{sv}", + _notify_update_prop_nameservers), + NML_DBUS_META_PROPERTY_INIT_FCN("Nameservers", + PROP_NAMESERVERS, + "au", + _notify_update_prop_nameservers, + .obj_property_no_reverse_idx = TRUE), + NML_DBUS_META_PROPERTY_INIT_FCN("RouteData", + PROP_ROUTES, + "aa{sv}", + _notify_update_prop_routes), + NML_DBUS_META_PROPERTY_INIT_FCN("Routes", + PROP_ROUTES, + "aau", + _notify_update_prop_routes, + .obj_property_no_reverse_idx = TRUE), + NML_DBUS_META_PROPERTY_INIT_AS("Searches", PROP_SEARCHES, NMIPConfigPrivate, searches), + NML_DBUS_META_PROPERTY_INIT_FCN("WinsServerData", + PROP_WINS_SERVERS, + "as", + _notify_update_prop_wins_servers), + NML_DBUS_META_PROPERTY_INIT_FCN("WinsServers", + PROP_WINS_SERVERS, + "au", + _notify_update_prop_wins_servers, + .obj_property_no_reverse_idx = TRUE), ), + .base_struct_offset = G_STRUCT_OFFSET(NMIPConfig, _priv), ); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_ip6config = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_IP6_CONFIG, + nm_ip6_config_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("AddressData", + PROP_ADDRESSES, + "aa{sv}", + _notify_update_prop_addresses), + NML_DBUS_META_PROPERTY_INIT_FCN("Addresses", + PROP_ADDRESSES, + "a(ayuay)", + _notify_update_prop_addresses, + .obj_property_no_reverse_idx = TRUE), + NML_DBUS_META_PROPERTY_INIT_TODO("DnsOptions", "as"), + NML_DBUS_META_PROPERTY_INIT_TODO("DnsPriority", "i"), + NML_DBUS_META_PROPERTY_INIT_AS("Domains", PROP_DOMAINS, NMIPConfigPrivate, domains), + NML_DBUS_META_PROPERTY_INIT_S("Gateway", PROP_GATEWAY, NMIPConfigPrivate, gateway), + NML_DBUS_META_PROPERTY_INIT_FCN("Nameservers", + PROP_NAMESERVERS, + "aay", + _notify_update_prop_nameservers), + NML_DBUS_META_PROPERTY_INIT_FCN("RouteData", + PROP_ROUTES, + "aa{sv}", + _notify_update_prop_routes), + NML_DBUS_META_PROPERTY_INIT_FCN("Routes", + PROP_ROUTES, + "a(ayuayu)", + _notify_update_prop_routes, + .obj_property_no_reverse_idx = TRUE), + NML_DBUS_META_PROPERTY_INIT_AS("Searches", PROP_SEARCHES, NMIPConfigPrivate, searches), ), + .base_struct_offset = G_STRUCT_OFFSET(NMIPConfig, _priv), ); + +static void +nm_ip_config_class_init(NMIPConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(config_class); + + g_type_class_add_private(config_class, sizeof(NMIPConfigPrivate)); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + /** + * NMIPConfig:family: + * + * The IP address family of the configuration; either + * <literal>AF_INET</literal> or <literal>AF_INET6</literal>. + **/ + obj_properties[PROP_FAMILY] = g_param_spec_int(NM_IP_CONFIG_FAMILY, + "", + "", + 0, + 255, + AF_UNSPEC, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMIPConfig:gateway: + * + * The IP gateway address of the configuration as string. + **/ + obj_properties[PROP_GATEWAY] = g_param_spec_string(NM_IP_CONFIG_GATEWAY, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMIPConfig:addresses: + * + * A #GPtrArray containing the addresses (#NMIPAddress) of the configuration. + **/ + obj_properties[PROP_ADDRESSES] = g_param_spec_boxed(NM_IP_CONFIG_ADDRESSES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMIPConfig:routes: (type GPtrArray(NMIPRoute)) + * + * A #GPtrArray containing the routes (#NMIPRoute) of the configuration. + **/ + obj_properties[PROP_ROUTES] = g_param_spec_boxed(NM_IP_CONFIG_ROUTES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMIPConfig:nameservers: + * + * The array containing name server IP addresses of the configuration. + **/ + obj_properties[PROP_NAMESERVERS] = + g_param_spec_boxed(NM_IP_CONFIG_NAMESERVERS, + "", + "", + G_TYPE_STRV, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMIPConfig:domains: + * + * The array containing domain strings of the configuration. + **/ + obj_properties[PROP_DOMAINS] = g_param_spec_boxed(NM_IP_CONFIG_DOMAINS, + "", + "", + G_TYPE_STRV, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMIPConfig:searches: + * + * The array containing DNS search strings of the configuration. + **/ + obj_properties[PROP_SEARCHES] = g_param_spec_boxed(NM_IP_CONFIG_SEARCHES, + "", + "", + G_TYPE_STRV, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMIPConfig:wins-servers: + * + * The array containing WINS server IP addresses of the configuration. + * (This will always be empty for IPv6 configurations.) + **/ + obj_properties[PROP_WINS_SERVERS] = + g_param_spec_boxed(NM_IP_CONFIG_WINS_SERVERS, + "", + "", + G_TYPE_STRV, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_ip4config, + &_nml_dbus_meta_iface_nm_ip6config); +} + +/** + * nm_ip_config_get_family: + * @config: a #NMIPConfig + * + * Gets the IP address family + * + * Returns: the IP address family; either <literal>AF_INET</literal> or + * <literal>AF_INET6</literal> + **/ +int +nm_ip_config_get_family(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), AF_UNSPEC); + + return NM_IS_IP4_CONFIG(config) ? AF_INET : AF_INET6; +} + +/** + * nm_ip_config_get_gateway: + * @config: a #NMIPConfig + * + * Gets the IP gateway address. + * + * Returns: (transfer none): the IP address of the gateway. + **/ +const char * +nm_ip_config_get_gateway(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), NULL); + + return _nml_coerce_property_str_not_empty(NM_IP_CONFIG_GET_PRIVATE(config)->gateway); +} + +/** + * nm_ip_config_get_addresses: + * @config: a #NMIPConfig + * + * Gets the IP addresses (containing the address, prefix, and gateway). + * + * Returns: (element-type NMIPAddress) (transfer none): the #GPtrArray + * containing #NMIPAddress<!-- -->es. This is the internal copy used by the + * configuration and must not be modified. The library never modifies the + * returned array and thus it is safe for callers to reference and keep using it. + **/ +GPtrArray * +nm_ip_config_get_addresses(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), NULL); + + return NM_IP_CONFIG_GET_PRIVATE(config)->addresses; +} + +/** + * nm_ip_config_get_nameservers: + * @config: a #NMIPConfig + * + * Gets the domain name servers (DNS). + * + * Returns: (transfer none): the array of nameserver IP addresses + **/ +const char *const * +nm_ip_config_get_nameservers(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), NULL); + + return _nml_coerce_property_strv_not_null(NM_IP_CONFIG_GET_PRIVATE(config)->nameservers); +} + +/** + * nm_ip_config_get_domains: + * @config: a #NMIPConfig + * + * Gets the domain names. + * + * Returns: (transfer none): the array of domains. + * (This is never %NULL, though it may be 0-length). + **/ +const char *const * +nm_ip_config_get_domains(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), NULL); + + return _nml_coerce_property_strv_not_null(NM_IP_CONFIG_GET_PRIVATE(config)->domains); +} + +/** + * nm_ip_config_get_searches: + * @config: a #NMIPConfig + * + * Gets the DNS searches. + * + * Returns: (transfer none): the array of DNS search strings. + * (This is never %NULL, though it may be 0-length). + **/ +const char *const * +nm_ip_config_get_searches(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), NULL); + + return _nml_coerce_property_strv_not_null(NM_IP_CONFIG_GET_PRIVATE(config)->searches); +} + +/** + * nm_ip_config_get_wins_servers: + * @config: a #NMIPConfig + * + * Gets the Windows Internet Name Service servers (WINS). + * + * Returns: (transfer none): the arry of WINS server IP address strings. + * (This is never %NULL, though it may be 0-length.) + **/ +const char *const * +nm_ip_config_get_wins_servers(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), NULL); + + return _nml_coerce_property_strv_not_null(NM_IP_CONFIG_GET_PRIVATE(config)->wins_servers); +} + +/** + * nm_ip_config_get_routes: + * @config: a #NMIPConfig + * + * Gets the routes. + * + * Returns: (element-type NMIPRoute) (transfer none): the #GPtrArray containing + * #NMIPRoute<!-- -->s. This is the internal copy used by the configuration, and must + * not be modified. The library never modifies the returned array and thus it is + * safe for callers to reference and keep using it. + * + **/ +GPtrArray * +nm_ip_config_get_routes(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), NULL); + + return NM_IP_CONFIG_GET_PRIVATE(config)->routes; +} diff --git a/src/libnm-client-impl/nm-ip4-config.c b/src/libnm-client-impl/nm-ip4-config.c new file mode 100644 index 00000000..0f2fdb04 --- /dev/null +++ b/src/libnm-client-impl/nm-ip4-config.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-ip4-config.h" + +/*****************************************************************************/ + +struct _NMIP4Config { + NMIPConfig parent; +}; + +struct _NMIP4ConfigClass { + NMIPConfigClass parent; +}; + +G_DEFINE_TYPE(NMIP4Config, nm_ip4_config, NM_TYPE_IP_CONFIG) + +/*****************************************************************************/ + +static void +nm_ip4_config_init(NMIP4Config *config) +{} + +static void +nm_ip4_config_class_init(NMIP4ConfigClass *config_class) +{} diff --git a/src/libnm-client-impl/nm-ip4-config.h b/src/libnm-client-impl/nm-ip4-config.h new file mode 100644 index 00000000..b9297dc0 --- /dev/null +++ b/src/libnm-client-impl/nm-ip4-config.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + */ + +#ifndef __NM_IP4_CONFIG_H__ +#define __NM_IP4_CONFIG_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#include "nm-ip-config.h" + +#define NM_TYPE_IP4_CONFIG (nm_ip4_config_get_type()) +#define NM_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IP4_CONFIG, NMIP4Config)) +#define NM_IP4_CONFIG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass)) +#define NM_IS_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_IP4_CONFIG)) +#define NM_IS_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_IP4_CONFIG)) +#define NM_IP4_CONFIG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass)) + +/** + * NMIP4Config: + */ +typedef struct _NMIP4Config NMIP4Config; +typedef struct _NMIP4ConfigClass NMIP4ConfigClass; + +GType nm_ip4_config_get_type(void); + +#endif /* __NM_IP4_CONFIG_H__ */ diff --git a/src/libnm-client-impl/nm-ip6-config.c b/src/libnm-client-impl/nm-ip6-config.c new file mode 100644 index 00000000..78e0b7dc --- /dev/null +++ b/src/libnm-client-impl/nm-ip6-config.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-ip6-config.h" + +/*****************************************************************************/ + +struct _NMIP6Config { + NMIPConfig parent; +}; + +struct _NMIP6ConfigClass { + NMIPConfigClass parent; +}; + +G_DEFINE_TYPE(NMIP6Config, nm_ip6_config, NM_TYPE_IP_CONFIG) + +/*****************************************************************************/ + +static void +nm_ip6_config_init(NMIP6Config *config) +{} + +static void +nm_ip6_config_class_init(NMIP6ConfigClass *config_class) +{} diff --git a/src/libnm-client-impl/nm-ip6-config.h b/src/libnm-client-impl/nm-ip6-config.h new file mode 100644 index 00000000..0c7c2a9f --- /dev/null +++ b/src/libnm-client-impl/nm-ip6-config.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2008 - 2014 Red Hat, Inc. + */ + +#ifndef __NM_IP6_CONFIG_H__ +#define __NM_IP6_CONFIG_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#include "nm-ip-config.h" + +#define NM_TYPE_IP6_CONFIG (nm_ip6_config_get_type()) +#define NM_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IP6_CONFIG, NMIP6Config)) +#define NM_IP6_CONFIG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass)) +#define NM_IS_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_IP6_CONFIG)) +#define NM_IS_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_IP6_CONFIG)) +#define NM_IP6_CONFIG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass)) + +/** + * NMIP6Config: + */ +typedef struct _NMIP6Config NMIP6Config; +typedef struct _NMIP6ConfigClass NMIP6ConfigClass; + +GType nm_ip6_config_get_type(void); + +#endif /* __NM_IP6_CONFIG_H__ */ diff --git a/src/libnm-client-impl/nm-libnm-utils.c b/src/libnm-client-impl/nm-libnm-utils.c new file mode 100644 index 00000000..671466cf --- /dev/null +++ b/src/libnm-client-impl/nm-libnm-utils.c @@ -0,0 +1,916 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2018 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-libnm-utils.h" + +#include "libnm-glib-aux/nm-time-utils.h" +#include "libnm-core-aux-intern/nm-common-macros.h" +#include "nm-object.h" + +/*****************************************************************************/ + +volatile int _nml_dbus_log_level = 0; + +int +_nml_dbus_log_level_init(void) +{ + const GDebugKey keys[] = { + {"trace", _NML_DBUS_LOG_LEVEL_TRACE}, + {"debug", _NML_DBUS_LOG_LEVEL_DEBUG}, + {"warning", _NML_DBUS_LOG_LEVEL_WARN}, + {"error", _NML_DBUS_LOG_LEVEL_ERROR}, + {"stdout", NML_DBUS_LOG_STDOUT}, + }; + int l; + + l = _NML_DBUS_LOG_LEVEL_INITIALIZED + | nm_utils_parse_debug_string(g_getenv("LIBNM_CLIENT_DEBUG"), keys, G_N_ELEMENTS(keys)); + + if (!g_atomic_int_compare_and_exchange(&_nml_dbus_log_level, 0, l)) + l = g_atomic_int_get(&_nml_dbus_log_level); + + nm_assert(l & _NML_DBUS_LOG_LEVEL_INITIALIZED); + return l; +} + +void +_nml_dbus_log(NMLDBusLogLevel level, gboolean use_stdout, const char *fmt, ...) +{ + NMLDBusLogLevel configured_log_level; + gs_free char * msg = NULL; + va_list args; + const char * prefix = ""; + gint64 ts; + pid_t pid; + + /* we only call _nml_dbus_log() after nml_dbus_log_enabled(), which already does + * an atomic access to the variable. Since the value is only initialized once and + * never changes, we can just access it without additional locking. */ + configured_log_level = _nml_dbus_log_level; + + nm_assert(level & configured_log_level); + + va_start(args, fmt); + msg = g_strdup_vprintf(fmt, args); + va_end(args); + + switch (level) { + case NML_DBUS_LOG_LEVEL_TRACE: + prefix = "<trace> "; + break; + case NML_DBUS_LOG_LEVEL_DEBUG: + prefix = "<debug> "; + break; + case NML_DBUS_LOG_LEVEL_WARN: + prefix = "<warn > "; + if (NM_FLAGS_HAS(configured_log_level, _NML_DBUS_LOG_LEVEL_WARN)) { + g_warning("libnm-dbus: %s%s", prefix, msg); + return; + } + break; + case NML_DBUS_LOG_LEVEL_ERROR: + prefix = "<error> "; + if (NM_FLAGS_HAS(configured_log_level, _NML_DBUS_LOG_LEVEL_ERROR)) { + g_critical("libnm-dbus: %s%s", prefix, msg); + return; + } + if (NM_FLAGS_HAS(configured_log_level, _NML_DBUS_LOG_LEVEL_WARN)) { + g_warning("libnm-dbus: %s%s", prefix, msg); + return; + } + break; + default: + break; + } + + ts = nm_utils_clock_gettime_nsec(CLOCK_BOOTTIME); + + pid = getpid(); + + if (use_stdout) { + g_print("libnm-dbus[%lld]: %s[%" G_GINT64_FORMAT ".%05" G_GINT64_FORMAT "] %s\n", + (long long) pid, + prefix, + ts / NM_UTILS_NSEC_PER_SEC, + (ts / (NM_UTILS_NSEC_PER_SEC / 10000)) % 10000, + msg); + } else { + g_printerr("libnm-dbus[%lld]: %s[%" G_GINT64_FORMAT ".%05" G_GINT64_FORMAT "] %s\n", + (long long) pid, + prefix, + ts / NM_UTILS_NSEC_PER_SEC, + (ts / (NM_UTILS_NSEC_PER_SEC / 10000)) % 10000, + msg); + } +} + +/*****************************************************************************/ + +/* Stolen from dbus-glib */ +char * +nm_utils_wincaps_to_dash(const char *caps) +{ + const char *p; + GString * str; + + str = g_string_new(NULL); + p = caps; + while (*p) { + if (g_ascii_isupper(*p)) { + if (str->len > 0 && (str->len < 2 || str->str[str->len - 2] != '-')) + g_string_append_c(str, '-'); + g_string_append_c(str, g_ascii_tolower(*p)); + } else + g_string_append_c(str, *p); + ++p; + } + + return g_string_free(str, FALSE); +} + +/*****************************************************************************/ + +static char * +_fixup_string(const char * desc, + const char *const *ignored_phrases, + const char *const *ignored_words, + gboolean square_brackets_sensible) +{ + char * desc_full; + gboolean in_paren = FALSE; + char * p, *q; + int i; + + if (!desc || !desc[0]) + return NULL; + + /* restore original non-UTF-8-safe text. */ + desc_full = nm_utils_str_utf8safe_unescape_cp(desc, NM_UTILS_STR_UTF8_SAFE_FLAG_NONE); + + /* replace all invalid UTF-8 bytes with space. */ + p = desc_full; + while (!g_utf8_validate(p, -1, (const char **) &q)) { + /* the byte is invalid UTF-8. Replace it with space and proceed. */ + *q = ' '; + p = q + 1; + } + + /* replace '_', ',', ASCII control characters and everything inside parentheses, with space. */ + for (p = desc_full; p[0]; p++) { + if (*p == '(') + in_paren = TRUE; + else if (*p == ')') + in_paren = FALSE; + else if (NM_IN_SET(*p, '_', ',') || nm_ascii_is_ctrl_or_del(*p) || in_paren) { + /* pass */ + } else + continue; + *p = ' '; + } + + /* Attempt to shorten ID by ignoring certain phrases */ + for (i = 0; ignored_phrases[i]; i++) { + p = strstr(desc_full, ignored_phrases[i]); + if (p) { + const char *eow = &p[strlen(ignored_phrases[i])]; + + /* require that the phrase is delimited by space, or + * at the beginning or end of the description. */ + if ((p == desc_full || p[-1] == ' ') && NM_IN_SET(eow[0], '\0', ' ')) + memmove(p, eow, strlen(eow) + 1); /* +1 for the \0 */ + } + } + + /* Attempt to shorten ID by ignoring certain individual words. + * - word-split the description at spaces + * - coalesce multiple spaces + * - skip over ignored_words */ + p = desc_full; + q = desc_full; + for (;;) { + char *eow; + gsize l; + + /* skip leading spaces. */ + while (p[0] == ' ') + p++; + + if (!p[0]) + break; + + /* split leading word on first space */ + eow = strchr(p, ' '); + if (eow) + *eow = '\0'; + + if (nm_utils_strv_find_first((char **) ignored_words, -1, p) >= 0) + goto next; + + l = strlen(p); + if (q != p) { + if (q != desc_full) + *q++ = ' '; + memmove(q, p, l); + } + q += l; + +next: + if (!eow) + break; + p = eow + 1; + } + + *q++ = '\0'; + + p = strchr(desc_full, '['); + if (p == desc_full) { + /* All we're left with is in square brackets. + * Always prefer that to a blank string.*/ + square_brackets_sensible = TRUE; + } + if (square_brackets_sensible) { + /* If there's a [<string>] that survived the substitution, then the string + * is a short form that is generally preferable. */ + q = strchr(desc_full, ']'); + if (p && q > p) { + p++; + memmove(desc_full, p, q - p); + desc_full[q - p] = '\0'; + } + } else { + /* [<string>] sometimes contains the preferred human-readable name, but + * mostly it's utterly useless. Sigh. Drop it. */ + if (p) { + if (p > desc_full && p[-1] == ' ') + p--; + *p = '\0'; + } + } + + if (!desc_full[0]) { + g_free(desc_full); + return NULL; + } + + return desc_full; +} + +char * +nm_utils_fixup_vendor_string(const char *desc) +{ + static const char *const IGNORED_PHRASES[] = { + "Access Systems", + "Business Mobile Networks BV", + "Communications & Multimedia", + "Company of Japan", + "Computer Co.", + "Computer Corp.", + "Computer Corporation", + "Computer Inc.", + "Computer, Inc.", + "Information and Communication Products", + "Macao Commercial Offshore", + "Mobile Phones", + "(M) Son", + "Multimedia Internet Technology", + "Technology Group Ltd.", + "Wireless Networks", + "Wireless Solutions", + NULL, + }; + static const char *const IGNORED_WORDS[] = { + "AB", + "AG", + "A/S", + "ASA", + "B.V.", + "Chips", + "Co.", + "Co", + "Communications", + "Components", + "Computers", + "Computertechnik", + "corp.", + "Corp.", + "Corp", + "Corporation", + "Design", + "Electronics", + "Enterprise", + "Enterprises", + "Europe", + "GmbH", + "Hardware", + "[hex]", + "Holdings", + "Inc.", + "Inc", + "INC.", + "Incorporated", + "Instruments", + "International", + "Intl.", + "Labs", + "Limited.", + "Limited", + "Ltd.", + "Ltd", + "Microelectronics", + "Microsystems", + "MSM", + "Multimedia", + "Networks", + "Norway", + "Optical", + "PCS", + "Semiconductor", + "Systems", + "Systemtechnik", + "Techcenter", + "Technik", + "Technologies", + "Technology", + "TECHNOLOGY", + "Telephonics", + "USA", + "WCDMA", + NULL, + }; + char *desc_full; + char *p; + + desc_full = _fixup_string(desc, IGNORED_PHRASES, IGNORED_WORDS, TRUE); + if (!desc_full) + return NULL; + + /* Chop off everything after a slash. */ + for (p = desc_full; *p; p++) { + if ((p[0] == ' ' && p[1] == '/') || p[0] == '/') { + p[0] = '\0'; + break; + } + } + + nm_assert(g_utf8_validate(desc_full, -1, NULL)); + + return desc_full; +} + +char * +nm_utils_fixup_product_string(const char *desc) +{ + static const char *const IGNORED_PHRASES[] = { + "100/10 MBit", + "10/100 Mbps", + "1.0 GbE", + "10 GbE", + "10 Gigabit", + "10 Mbps", + "1/10 Gigabit", + "150 Mbps", + "2.5 GbE", + "54 Mbps", + "Attached Port", + "+ BT", + "\"CDC Subset\"", + "CE Media Processor", + "Controller Area Network", + "Converged Network", + "DEC-Tulip compatible", + "Dish Adapter", + "Double 108 Mbps", + "Dual Band", + "Dual Port", + "Embedded UTP", + "Ethernet Connection", + "Ethernet Pro 100", + "Express Module", + "Fabric Adapter", + "Fast Ethernet", + "for 10GBASE-T", + "for 10GbE backplane", + "for 10GbE QSFP+", + "for 10GbE SFP+", + "for 1GbE", + "for 20GbE backplane", + "for 25GbE backplane", + "for 25GbE SFP28", + "for 40GbE backplane", + "for 40GbE QSFP+", + "G Adapter", + "Gigabit Desktop Network", + "Gigabit Ethernet", + "Gigabit or", + "Host Interface", + "Host Virtual Interface", + "IEEE 802.11a/b/g", + "IEEE 802.11g", + "IEEE 802.11G", + "IEEE 802.11n", + "MAC + PHY", + "Mini Card", + "Mini Wireless", + "multicore SoC", + "Multi Function", + "N Draft 11n Wireless", + "Network Connection", + "Network Everywhere", + "N Wireless", + "N+ Wireless", + "OCT To Fast Ethernet Converter", + "PC Card", + "PCI Express", + "Platform Controller Hub", + "Plus Bluetooth", + "Quad Gigabit", + "rev 1", + "rev 17", + "rev 2", + "rev A", + "rev B", + "rev F", + "TO Ethernet", + "Turbo Wireless Adapter", + "Unified Wire", + "USB 1.1", + "USB 2.0", + "Virtual media for", + "WiFi Link", + "+ WiMAX", + "WiMAX/WiFi Link", + "Wireless G", + "Wireless G+", + "Wireless Lan", + "Wireless Mini adapter", + "Wireless Mini Adapter", + "Wireless N", + "with 1000-BASE-T interface", + "with CX4 copper interface", + "with Range Amplifier", + "with SR-XFP optical interface", + "w/ Upgradable Antenna", + NULL, + }; + static const char *const IGNORED_WORDS[] = { + "1000BaseSX", + "1000BASE-T", + "1000Base-ZX", + "100/10M", + "100baseFx", + "100Base-MII", + "100Base-T", + "100BaseT4", + "100Base-TX", + "100BaseTX", + "100GbE", + "100Mbps", + "100MBps", + "10/100", + "10/100/1000", + "10/100/1000Base-T", + "10/100/1000BASE-T", + "10/100BaseT", + "10/100baseTX", + "10/100BaseTX", + "10/100/BNC", + "10/100M", + "10/20-Gigabit", + "10/25/40/50GbE", + "10/40G", + "10base-FL", + "10BaseT", + "10BASE-T", + "10G", + "10Gb", + "10Gb/25Gb", + "10Gb/25Gb/40Gb/50Gb", + "10Gbase-T", + "10GBase-T", + "10GBASE-T", + "10GbE", + "10Gbps", + "10-Giga", + "10-Gigabit", + "10mbps", + "10Mbps", + "1/10GbE", + "1/10-Gigabit", + "11b/g/n", + "11g", + "150Mbps", + "16Gbps/10Gbps", + "1GbE", + "1x2:2", + "20GbE", + "25Gb", + "25GbE", + "2-Port", + "2x3:3", + "3G", + "3G/4G", + "3x3:3", + "40GbE", + "4G", + "54g", + "54M", + "54Mbps", + "56k", + "5G", + "802.11", + "802.11a/b/g", + "802.11abg", + "802.11a/b/g/n", + "802.11abgn", + "802.11ac", + "802.11ad", + "802.11a/g", + "802.11b", + "802.11b/g", + "802.11bg", + "802.11b/g/n", + "802.11bgn", + "802.11b/g/n-draft", + "802.11g", + "802.11n", + "802.11N", + "802.11n/b/g", + "802.11ng", + "802AIN", + "802UIG-1", + "adapter", + "Adapter", + "adaptor", + "ADSL", + "Basic", + "CAN-Bus", + "card", + "Card", + "Cardbus", + "CardBus", + "CDMA", + "CNA", + "Composite", + "controller", + "Controller", + "Copper", + "DB", + "Desktop", + "device", + "Device", + "dongle", + "driver", + "Dual-band", + "Dual-Protocol", + "EISA", + "Enhanced", + "ethernet.", + "ethernet", + "Ethernet", + "Ethernet/RNDIS", + "ExpressModule", + "family", + "Family", + "Fast/Gigabit", + "Fiber", + "gigabit", + "Gigabit", + "G-NIC", + "Hi-Gain", + "Hi-Speed", + "HSDPA", + "HSUPA", + "integrated", + "Integrated", + "interface", + "LAN", + "LAN+Winmodem", + "Laptop", + "LTE", + "LTE/UMTS/GSM", + "MAC", + "Micro", + "Mini-Card", + "Mini-USB", + "misprogrammed", + "modem", + "Modem", + "Modem/Networkcard", + "Module", + "Multimode", + "Multithreaded", + "Name:", + "net", + "network", + "Network", + "n/g/b", + "NIC", + "Notebook", + "OEM", + "PCI", + "PCI64", + "PCIe", + "PCI-E", + "PCI-Express", + "PCI-X", + "PCMCIA", + "PDA", + "PnP", + "RDMA", + "RJ-45", + "Series", + "Server", + "SoC", + "Switch", + "Technologies", + "TOE", + "USB", + "USB2.0", + "USB/Ethernet", + "UTP", + "UTP/Coax", + "v1", + "v1.1", + "v2", + "V2.0", + "v3", + "v4", + "wifi", + "Wi-Fi", + "WiFi", + "wireless", + "Wireless", + "Wireless-150N", + "Wireless-300N", + "Wireless-G", + "Wireless-N", + "WLAN", + NULL, + }; + char *desc_full; + char *p; + + desc_full = _fixup_string(desc, IGNORED_PHRASES, IGNORED_WORDS, FALSE); + if (!desc_full) + return NULL; + + /* Chop off everything after a '-'. */ + for (p = desc_full; *p; p++) { + if (p[0] == ' ' && p[1] == '-' && p[2] == ' ') { + p[0] = '\0'; + break; + } + } + + nm_assert(g_utf8_validate(desc_full, -1, NULL)); + + return desc_full; +} + +/*****************************************************************************/ + +const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[] = { + &_nml_dbus_meta_iface_nm, + &_nml_dbus_meta_iface_nm_accesspoint, + &_nml_dbus_meta_iface_nm_agentmanager, + &_nml_dbus_meta_iface_nm_checkpoint, + &_nml_dbus_meta_iface_nm_connection_active, + &_nml_dbus_meta_iface_nm_dhcp4config, + &_nml_dbus_meta_iface_nm_dhcp6config, + &_nml_dbus_meta_iface_nm_device, + &_nml_dbus_meta_iface_nm_device_adsl, + &_nml_dbus_meta_iface_nm_device_bluetooth, + &_nml_dbus_meta_iface_nm_device_bond, + &_nml_dbus_meta_iface_nm_device_bridge, + &_nml_dbus_meta_iface_nm_device_dummy, + &_nml_dbus_meta_iface_nm_device_generic, + &_nml_dbus_meta_iface_nm_device_iptunnel, + &_nml_dbus_meta_iface_nm_device_infiniband, + &_nml_dbus_meta_iface_nm_device_lowpan, + &_nml_dbus_meta_iface_nm_device_macsec, + &_nml_dbus_meta_iface_nm_device_macvlan, + &_nml_dbus_meta_iface_nm_device_modem, + &_nml_dbus_meta_iface_nm_device_olpcmesh, + &_nml_dbus_meta_iface_nm_device_ovsbridge, + &_nml_dbus_meta_iface_nm_device_ovsinterface, + &_nml_dbus_meta_iface_nm_device_ovsport, + &_nml_dbus_meta_iface_nm_device_ppp, + &_nml_dbus_meta_iface_nm_device_statistics, + &_nml_dbus_meta_iface_nm_device_team, + &_nml_dbus_meta_iface_nm_device_tun, + &_nml_dbus_meta_iface_nm_device_veth, + &_nml_dbus_meta_iface_nm_device_vlan, + &_nml_dbus_meta_iface_nm_device_vrf, + &_nml_dbus_meta_iface_nm_device_vxlan, + &_nml_dbus_meta_iface_nm_device_wifip2p, + &_nml_dbus_meta_iface_nm_device_wireguard, + &_nml_dbus_meta_iface_nm_device_wired, + &_nml_dbus_meta_iface_nm_device_wireless, + &_nml_dbus_meta_iface_nm_device_wpan, + &_nml_dbus_meta_iface_nm_dnsmanager, + &_nml_dbus_meta_iface_nm_ip4config, + &_nml_dbus_meta_iface_nm_ip6config, + &_nml_dbus_meta_iface_nm_settings, + &_nml_dbus_meta_iface_nm_settings_connection, + &_nml_dbus_meta_iface_nm_vpn_connection, + &_nml_dbus_meta_iface_nm_wifip2ppeer, +}; + +#define COMMON_PREFIX "org.freedesktop.NetworkManager" + +static int +_strcmp_common_prefix(gconstpointer a, gconstpointer b, gpointer user_data) +{ + const NMLDBusMetaIface *iface = a; + const char * dbus_iface_name = b; + + nm_assert(g_str_has_prefix(iface->dbus_iface_name, COMMON_PREFIX)); + + return strcmp(&iface->dbus_iface_name[NM_STRLEN(COMMON_PREFIX)], dbus_iface_name); +} + +const NMLDBusMetaIface * +nml_dbus_meta_iface_get(const char *dbus_iface_name) +{ + gssize idx; + + nm_assert(dbus_iface_name); + + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMLDBusMetaIface, dbus_iface_name) == 0); + + /* we assume that NetworkManager only uses unique interface names. E.g. one + * interface name always has one particular meaning (and offers one set of + * properties, signals and methods). This is a convenient assumption, and + * we sure would never violate it when extending NM's D-Bus API. */ + + if (NM_STR_HAS_PREFIX(dbus_iface_name, COMMON_PREFIX)) { + /* optimize, that in fact all our interfaces have the same prefix. */ + idx = nm_utils_ptrarray_find_binary_search((gconstpointer *) _nml_dbus_meta_ifaces, + G_N_ELEMENTS(_nml_dbus_meta_ifaces), + &dbus_iface_name[NM_STRLEN(COMMON_PREFIX)], + _strcmp_common_prefix, + NULL); + } else + return NULL; + + if (idx < 0) + return NULL; + return _nml_dbus_meta_ifaces[idx]; +} + +const NMLDBusMetaProperty * +nml_dbus_meta_property_get(const NMLDBusMetaIface *meta_iface, + const char * dbus_property_name, + guint * out_idx) +{ + gssize idx; + + nm_assert(meta_iface); + nm_assert(dbus_property_name); + + idx = nm_utils_array_find_binary_search(meta_iface->dbus_properties, + sizeof(meta_iface->dbus_properties[0]), + meta_iface->n_dbus_properties, + &dbus_property_name, + nm_strcmp_p_with_data, + NULL); + if (idx < 0) { + NM_SET_OUT(out_idx, meta_iface->n_dbus_properties); + return NULL; + } + NM_SET_OUT(out_idx, idx); + return &meta_iface->dbus_properties[idx]; +} + +void +_nml_dbus_meta_class_init_with_properties_impl(GObjectClass * object_class, + const NMLDBusMetaIface *const *meta_ifaces) +{ + int i_iface; + + nm_assert(G_IS_OBJECT_CLASS(object_class)); + nm_assert(meta_ifaces); + nm_assert(meta_ifaces[0]); + + for (i_iface = 0; meta_ifaces[i_iface]; i_iface++) { + const NMLDBusMetaIface *meta_iface = meta_ifaces[i_iface]; + guint8 * reverse_idx; + guint8 i; + + nm_assert(g_type_is_a(meta_iface->get_type_fcn(), G_OBJECT_CLASS_TYPE(object_class))); + nm_assert(meta_iface->n_obj_properties > 0); + nm_assert(meta_iface->obj_properties); + nm_assert(meta_iface->obj_properties_reverse_idx[0] == 0); + nm_assert(meta_iface->obj_properties == meta_ifaces[0]->obj_properties); + + if (i_iface == 0) + g_object_class_install_properties(object_class, + meta_iface->n_obj_properties, + (GParamSpec **) meta_iface->obj_properties); + + reverse_idx = (guint8 *) meta_iface->obj_properties_reverse_idx; + + for (i = 0; i < meta_iface->n_obj_properties; i++) + reverse_idx[i] = 0xFFu; + for (i = 0; i < meta_iface->n_dbus_properties; i++) { + const NMLDBusMetaProperty *mpr = &meta_iface->dbus_properties[i]; + + if (mpr->obj_properties_idx != 0 && !mpr->obj_property_no_reverse_idx) { + nm_assert(mpr->obj_properties_idx < meta_iface->n_obj_properties); + nm_assert(reverse_idx[mpr->obj_properties_idx] == 0xFFu); + + reverse_idx[mpr->obj_properties_idx] = i; + } + } + } +} + +gboolean +nm_utils_g_param_spec_is_default(const GParamSpec *pspec) +{ + g_return_val_if_fail(pspec, FALSE); + + if (pspec->value_type == G_TYPE_BOOLEAN) + return ((((GParamSpecBoolean *) pspec)->default_value) == FALSE); + if (pspec->value_type == G_TYPE_UCHAR) + return ((((GParamSpecUChar *) pspec)->default_value) == 0u); + if (pspec->value_type == G_TYPE_INT) + return ((((GParamSpecInt *) pspec)->default_value) == 0); + if (pspec->value_type == G_TYPE_UINT) + return ((((GParamSpecUInt *) pspec)->default_value) == 0u); + if (pspec->value_type == G_TYPE_INT64) + return ((((GParamSpecInt64 *) pspec)->default_value) == 0); + if (pspec->value_type == G_TYPE_UINT64) + return ((((GParamSpecUInt64 *) pspec)->default_value) == 0u); + if (g_type_is_a(pspec->value_type, G_TYPE_ENUM)) + return ((((GParamSpecEnum *) pspec)->default_value) == 0); + if (g_type_is_a(pspec->value_type, G_TYPE_FLAGS)) + return ((((GParamSpecFlags *) pspec)->default_value) == 0u); + if (pspec->value_type == G_TYPE_STRING) + return ((((GParamSpecString *) pspec)->default_value) == NULL); + if (NM_IN_SET(pspec->value_type, + G_TYPE_BYTES, + G_TYPE_PTR_ARRAY, + G_TYPE_ARRAY, + G_TYPE_HASH_TABLE, + G_TYPE_STRV)) { + /* boxed types have NULL default. */ + g_return_val_if_fail(G_IS_PARAM_SPEC_BOXED(pspec), FALSE); + g_return_val_if_fail(G_TYPE_IS_BOXED(pspec->value_type), FALSE); + return TRUE; + } + if (g_type_is_a(pspec->value_type, NM_TYPE_OBJECT)) { + /* object types have NULL default. */ + g_return_val_if_fail(G_IS_PARAM_SPEC_OBJECT(pspec), FALSE); + g_return_val_if_fail(G_TYPE_IS_OBJECT(pspec->value_type), FALSE); + return TRUE; + } + + /* This function is only used for asserting/testing. It thus + * strictly asserts and only support argument types that we expect. */ + g_return_val_if_reached(FALSE); +} + +/*****************************************************************************/ + +/** + * nm_utils_print: + * @output_mode: if 1 it uses g_print(). If 2, it uses g_printerr(). + * If 0, it uses either g_print() or g_printerr(), depending + * on LIBNM_CLIENT_DEBUG (and the "stdout" flag). + * @msg: the message to print. The function does not append + * a trailing newline. + * + * The only purpose of this function is to give access to g_print() + * or g_printerr() from pygobject. libnm can do debug logging by + * setting LIBNM_CLIENT_DEBUG and uses thereby g_printerr() or + * g_print(). A plain "print()" function in python is not in sync + * with these functions (it implements additional buffering). By + * using nm_utils_print(), the same logging mechanisms can be used. + * + * Since: 1.30 + */ +void +nm_utils_print(int output_mode, const char *msg) +{ + gboolean use_stdout; + + g_return_if_fail(msg); + + if (output_mode == 0) { + nml_dbus_log_enabled_full(NML_DBUS_LOG_LEVEL_ANY, &use_stdout); + output_mode = use_stdout ? 1 : 2; + } + + if (output_mode == 1) + g_print("%s", msg); + else if (output_mode == 2) + g_printerr("%s", msg); + else + g_return_if_reached(); +} diff --git a/src/libnm-client-impl/nm-libnm-utils.h b/src/libnm-client-impl/nm-libnm-utils.h new file mode 100644 index 00000000..3fd3b679 --- /dev/null +++ b/src/libnm-client-impl/nm-libnm-utils.h @@ -0,0 +1,1030 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017, 2018 Red Hat, Inc. + */ + +#ifndef __NM_LIBNM_UTILS_H__ +#define __NM_LIBNM_UTILS_H__ + +#include "c-list/src/c-list.h" +#include "nm-device.h" +#include "libnm-glib-aux/nm-ref-string.h" +#include "libnm-glib-aux/nm-logging-fwd.h" +#include "nm-types.h" +#include "nm-object.h" +#include "nm-client.h" + +/*****************************************************************************/ + +char *nm_utils_fixup_vendor_string(const char *desc); +char *nm_utils_fixup_product_string(const char *desc); + +char *nm_utils_wincaps_to_dash(const char *caps); + +gboolean nm_utils_g_param_spec_is_default(const GParamSpec *pspec); + +/*****************************************************************************/ + +typedef enum { + _NML_DBUS_LOG_LEVEL_NONE = 0x00, + + _NML_DBUS_LOG_LEVEL_INITIALIZED = 0x01, + + _NML_DBUS_LOG_LEVEL_TRACE = 0x02, + + _NML_DBUS_LOG_LEVEL_DEBUG = 0x04, + + /* the difference between a warning and a critical is that it results in + * g_warning() vs. g_critical() messages. Note that we want to use "warnings" + * for unknown D-Bus API that could just result because we run against a + * newer NetworkManager version (such warnings are more graceful, because + * we want that libnm can be forward compatible against newer servers). + * Critical warnings should be emitted when NetworkManager exposes something + * on D-Bus that breaks the current expectations. Usually NetworkManager + * should not break API, hence such issues are more severe. */ + _NML_DBUS_LOG_LEVEL_WARN = 0x08, + _NML_DBUS_LOG_LEVEL_ERROR = 0x10, + + /* ANY is only relevant for nml_dbus_log_enabled() to check whether any of the + * options is on. */ + NML_DBUS_LOG_LEVEL_ANY = _NML_DBUS_LOG_LEVEL_INITIALIZED, + + NML_DBUS_LOG_LEVEL_TRACE = _NML_DBUS_LOG_LEVEL_TRACE, + NML_DBUS_LOG_LEVEL_DEBUG = _NML_DBUS_LOG_LEVEL_DEBUG | NML_DBUS_LOG_LEVEL_TRACE, + NML_DBUS_LOG_LEVEL_WARN = _NML_DBUS_LOG_LEVEL_WARN | NML_DBUS_LOG_LEVEL_DEBUG, + NML_DBUS_LOG_LEVEL_ERROR = _NML_DBUS_LOG_LEVEL_ERROR | NML_DBUS_LOG_LEVEL_WARN, + + NML_DBUS_LOG_STDOUT = 0x20, +} NMLDBusLogLevel; + +#undef _LOGL_TRACE +#undef _LOGL_DEBUG +#undef _LOGL_INFO +#undef _LOGL_WARN +#undef _LOGL_ERR + +#define _LOGL_TRACE NML_DBUS_LOG_LEVEL_TRACE +#define _LOGL_DEBUG NML_DBUS_LOG_LEVEL_DEBUG +#define _LOGL_INFO NML_DBUS_LOG_LEVEL_INFO +#define _LOGL_WARN NML_DBUS_LOG_LEVEL_WARN +#define _LOGL_ERR NML_DBUS_LOG_LEVEL_ERR + +extern volatile int _nml_dbus_log_level; + +int _nml_dbus_log_level_init(void); + +static inline gboolean +nml_dbus_log_enabled_full(NMLDBusLogLevel level, gboolean *out_use_stdout) +{ + int l; + + nm_assert(NM_IN_SET(level, + NML_DBUS_LOG_LEVEL_ANY, + _NML_DBUS_LOG_LEVEL_NONE, + NML_DBUS_LOG_LEVEL_TRACE, + NML_DBUS_LOG_LEVEL_DEBUG, + NML_DBUS_LOG_LEVEL_WARN, + NML_DBUS_LOG_LEVEL_ERROR)); + + l = g_atomic_int_get(&_nml_dbus_log_level); + if (G_UNLIKELY(l == 0)) + l = _nml_dbus_log_level_init(); + + nm_assert(l & _NML_DBUS_LOG_LEVEL_INITIALIZED); + NM_SET_OUT(out_use_stdout, NM_FLAGS_HAS(l, NML_DBUS_LOG_STDOUT)); + if (level == NML_DBUS_LOG_LEVEL_ANY) + return l != _NML_DBUS_LOG_LEVEL_INITIALIZED; + return !!(((NMLDBusLogLevel) l) & level); +} + +static inline gboolean +nml_dbus_log_enabled(NMLDBusLogLevel level) +{ + return nml_dbus_log_enabled_full(level, NULL); +} + +void _nml_dbus_log(NMLDBusLogLevel level, gboolean use_stdout, const char *fmt, ...) + _nm_printf(3, 4); + +#define NML_DBUS_LOG(level, ...) \ + G_STMT_START \ + { \ + gboolean _use_stdout; \ + \ + G_STATIC_ASSERT((level) == _NML_DBUS_LOG_LEVEL_NONE || (level) == NML_DBUS_LOG_LEVEL_TRACE \ + || (level) == NML_DBUS_LOG_LEVEL_DEBUG \ + || (level) == NML_DBUS_LOG_LEVEL_WARN \ + || (level) == NML_DBUS_LOG_LEVEL_ERROR); \ + \ + if (nml_dbus_log_enabled_full(level, &_use_stdout)) { \ + _nml_dbus_log((level), _use_stdout, __VA_ARGS__); \ + } \ + } \ + G_STMT_END + +#define NML_DBUS_LOG_T(...) NML_DBUS_LOG(NML_DBUS_LOG_LEVEL_TRACE, __VA_ARGS__) +#define NML_DBUS_LOG_D(...) NML_DBUS_LOG(NML_DBUS_LOG_LEVEL_DEBUG, __VA_ARGS__) +#define NML_DBUS_LOG_W(...) NML_DBUS_LOG(NML_DBUS_LOG_LEVEL_WARN, __VA_ARGS__) +#define NML_DBUS_LOG_E(...) NML_DBUS_LOG(NML_DBUS_LOG_LEVEL_ERROR, __VA_ARGS__) + +/* _NML_NMCLIENT_LOG_LEVEL_COERCE is only for printf debugging. You can disable client logging by + * mapping the requested log level to a different one (or disable it altogether). + * That's useful for example if you are interested in *other* trace logging messages from + * libnm and don't want to get flooded by NMClient's trace messages. */ +#define _NML_NMCLIENT_LOG_LEVEL_COERCE(level) \ + /* for example, change condition below to suppress <trace> messages from NMClient. */ \ + ((TRUE || ((level) != NML_DBUS_LOG_LEVEL_TRACE)) ? (level) : _NML_DBUS_LOG_LEVEL_NONE) + +#define NML_NMCLIENT_LOG(level, self, ...) \ + NML_DBUS_LOG(_NML_NMCLIENT_LOG_LEVEL_COERCE(level), \ + "nmclient[" NM_HASH_OBFUSCATE_PTR_FMT "]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + NM_HASH_OBFUSCATE_PTR(self) _NM_UTILS_MACRO_REST(__VA_ARGS__)) + +#define NML_NMCLIENT_LOG_T(self, ...) NML_NMCLIENT_LOG(NML_DBUS_LOG_LEVEL_TRACE, self, __VA_ARGS__) +#define NML_NMCLIENT_LOG_D(self, ...) NML_NMCLIENT_LOG(NML_DBUS_LOG_LEVEL_DEBUG, self, __VA_ARGS__) +#define NML_NMCLIENT_LOG_W(self, ...) NML_NMCLIENT_LOG(NML_DBUS_LOG_LEVEL_WARN, self, __VA_ARGS__) +#define NML_NMCLIENT_LOG_E(self, ...) NML_NMCLIENT_LOG(NML_DBUS_LOG_LEVEL_ERROR, self, __VA_ARGS__) + +/*****************************************************************************/ + +static inline const char * +_nml_coerce_property_str_not_null(const char *str) +{ + return str ?: ""; +} + +static inline const char * +_nml_coerce_property_str_not_empty(const char *str) +{ + return str && str[0] ? str : NULL; +} + +static inline const char * +_nml_coerce_property_object_path(NMRefString *path) +{ + if (!path) + return NULL; + return nm_dbus_path_not_empty(path->str); +} + +static inline const char *const * +_nml_coerce_property_strv_not_null(char **strv) +{ + return ((const char *const *) strv) ?: NM_PTRARRAY_EMPTY(const char *); +} + +/*****************************************************************************/ + +GQuark nm_context_busy_watcher_quark(void); + +void nm_context_busy_watcher_integrate_source(GMainContext *outer_context, + GMainContext *inner_context, + GObject * context_busy_watcher); + +/*****************************************************************************/ + +typedef struct { + GCancellable *cancellable; + GSource * cancel_on_idle_source; + gulong cancelled_id; + union { + struct { + GTask *task; + } async; + struct { + GMainLoop *main_loop; + GError ** error_location; + } sync; + } data; + bool is_sync : 1; +} NMLInitData; + +NMLInitData * +nml_init_data_new_sync(GCancellable *cancellable, GMainLoop *main_loop, GError **error_location); + +NMLInitData *nml_init_data_new_async(GCancellable *cancellable, GTask *task_take); + +void nml_init_data_return(NMLInitData *init_data, GError *error_take); + +void nml_cleanup_context_busy_watcher_on_idle(GObject * context_busy_watcher_take, + GMainContext *context); + +/*****************************************************************************/ + +typedef struct _NMLDBusObject NMLDBusObject; +typedef struct _NMLDBusObjWatcher NMLDBusObjWatcher; +typedef struct _NMLDBusMetaIface NMLDBusMetaIface; +typedef struct _NMLDBusPropertyO NMLDBusPropertyO; +typedef struct _NMLDBusPropertyAO NMLDBusPropertyAO; + +typedef enum { + /* See comments below for NMLDBusMetaIface.interface_prio. + * + * Higher numbers means more important to detect the GObject type. */ + NML_DBUS_META_INTERFACE_PRIO_NONE = 0, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT = 1, + NML_DBUS_META_INTERFACE_PRIO_PARENT_TYPE = 2, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_10 = 3, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_20 = 4, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30 = 5, +} NMLDBusMetaInteracePrio; + +/*****************************************************************************/ + +#define NM_CLIENT_INSTANCE_FLAGS_ALL ((NMClientInstanceFlags) 0x1) + +typedef struct { + GType (*get_o_type_fcn)(void); + + /* Ignore whether the referenced NMObject is ready or not. That means, + * the property is always ready (and if the pointed object itself is + * not yet ready, the property pretends to be %NULL for the moment. */ + bool is_always_ready : 1; + +} NMLDBusPropertVTableO; + +struct _NMLDBusPropertyO { + NMLDBusObject * owner_dbobj; + NMLDBusObjWatcher * obj_watcher; + GObject * nmobj; + const NMLDBusMetaIface *meta_iface; + guint dbus_property_idx; + bool is_ready : 1; + bool is_changed : 1; + bool block_is_changed : 1; +}; + +gpointer nml_dbus_property_o_get_obj(NMLDBusPropertyO *pr_o); + +gboolean nml_dbus_property_o_is_ready(const NMLDBusPropertyO *pr_o); + +gboolean nml_dbus_property_o_is_ready_fully(const NMLDBusPropertyO *pr_o); + +void nml_dbus_property_o_clear(NMLDBusPropertyO *pr_o, NMClient *client); + +void nml_dbus_property_o_clear_many(NMLDBusPropertyO *pr_o, guint len, NMClient *self); + +void nml_dbus_property_o_notify_changed_many(NMLDBusPropertyO *ptr, guint len, NMClient *self); + +/*****************************************************************************/ + +typedef struct { + GType (*get_o_type_fcn)(void); + + void (*notify_changed_ao)(NMLDBusPropertyAO *pr_ao, + NMClient * self, + NMObject * nmobj, + gboolean is_added /* or else removed */); + + gboolean (*check_nmobj_visible_fcn)(GObject *nmobj); + + /* Ignore whether the referenced NMObject is ready or not. That means, + * the property is always ready (and if the pointed object itself is + * not yet ready, the property pretends to be %NULL for the moment. */ + bool is_always_ready : 1; + +} NMLDBusPropertVTableAO; + +struct _NMLDBusPropertyAOData; + +struct _NMLDBusPropertyAO { + CList data_lst_head; + GHashTable * hash; + NMLDBusObject * owner_dbobj; + const NMLDBusMetaIface * meta_iface; + GPtrArray * arr; + struct _NMLDBusPropertyAOData *changed_head; + guint dbus_property_idx; + guint n_not_ready; + bool is_changed : 1; +}; + +const GPtrArray *nml_dbus_property_ao_get_objs_as_ptrarray(NMLDBusPropertyAO *pr_ao); + +gboolean nml_dbus_property_ao_is_ready(const NMLDBusPropertyAO *pr_ao); + +void nml_dbus_property_ao_clear(NMLDBusPropertyAO *pr_ao, NMClient *client); + +void nml_dbus_property_ao_clear_many(NMLDBusPropertyAO *pr_ao, guint len, NMClient *self); + +void nml_dbus_property_ao_notify_changed_many(NMLDBusPropertyAO *ptr, guint len, NMClient *self); + +/*****************************************************************************/ + +typedef enum { + NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE = 0, + NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY = 0x1, +} NMLDBusNotifyUpdatePropFlags; + +NMLDBusNotifyUpdatePropFlags _nml_dbus_notify_update_prop_ignore(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant *value); + +NMLDBusNotifyUpdatePropFlags _nml_dbus_notify_update_prop_o(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant *value); + +typedef struct { + const char * dbus_property_name; + const GVariantType *dbus_type; + + guint16 prop_struct_offset; + + guint8 obj_properties_idx; + + bool use_notify_update_prop : 1; + + bool obj_property_no_reverse_idx : 1; + + union { + union { + const NMLDBusPropertVTableO * property_vtable_o; + const NMLDBusPropertVTableAO *property_vtable_ao; + } extra; + + NMLDBusNotifyUpdatePropFlags (*notify_update_prop)(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant *value); + }; + +} NMLDBusMetaProperty; + +#define NML_DBUS_META_PROPERTY_INIT(v_dbus_property_name, v_dbus_type, v_obj_properties_idx, ...) \ + { \ + .dbus_property_name = "" v_dbus_property_name "", \ + .dbus_type = NM_G_VARIANT_TYPE("" v_dbus_type ""), \ + .obj_properties_idx = v_obj_properties_idx, ##__VA_ARGS__ \ + } + +#define _NML_DBUS_META_PROPERTY_INIT_DEFAULT(v_dbus_type, \ + v_exp_type, \ + v_dbus_property_name, \ + v_obj_properties_idx, \ + v_container, \ + v_field) \ + NML_DBUS_META_PROPERTY_INIT( \ + v_dbus_property_name, \ + v_dbus_type, \ + v_obj_properties_idx, \ + .prop_struct_offset = NM_STRUCT_OFFSET_ENSURE_TYPE(v_exp_type, v_container, v_field)) + +#define NML_DBUS_META_PROPERTY_INIT_B(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("b", bool, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_Y(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("y", guint8, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_Q(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("q", guint16, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_I(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("i", gint32, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_U(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("u", guint32, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_X(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("x", gint64, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_T(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("t", guint64, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_S(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("s", char *, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_AS(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("as", char **, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_AY(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("ay", GBytes *, __VA_ARGS__) + +#define NML_DBUS_META_PROPERTY_INIT_O(v_dbus_property_name, \ + v_obj_properties_idx, \ + v_container, \ + v_field) \ + NML_DBUS_META_PROPERTY_INIT( \ + v_dbus_property_name, \ + "o", \ + v_obj_properties_idx, \ + .prop_struct_offset = NM_STRUCT_OFFSET_ENSURE_TYPE(NMRefString *, v_container, v_field), \ + .use_notify_update_prop = TRUE, \ + .notify_update_prop = _nml_dbus_notify_update_prop_o) + +#define NML_DBUS_META_PROPERTY_INIT_O_PROP(v_dbus_property_name, \ + v_obj_properties_idx, \ + v_container, \ + v_field, \ + v_get_o_type_fcn, \ + ...) \ + NML_DBUS_META_PROPERTY_INIT( \ + v_dbus_property_name, \ + "o", \ + v_obj_properties_idx, \ + .prop_struct_offset = \ + NM_STRUCT_OFFSET_ENSURE_TYPE(NMLDBusPropertyO, v_container, v_field), \ + .extra.property_vtable_o = \ + &((const NMLDBusPropertVTableO){.get_o_type_fcn = (v_get_o_type_fcn), ##__VA_ARGS__})) + +#define NML_DBUS_META_PROPERTY_INIT_AO_PROP(v_dbus_property_name, \ + v_obj_properties_idx, \ + v_container, \ + v_field, \ + v_get_o_type_fcn, \ + ...) \ + NML_DBUS_META_PROPERTY_INIT( \ + v_dbus_property_name, \ + "ao", \ + v_obj_properties_idx, \ + .prop_struct_offset = \ + NM_STRUCT_OFFSET_ENSURE_TYPE(NMLDBusPropertyAO, v_container, v_field), \ + .extra.property_vtable_ao = &( \ + (const NMLDBusPropertVTableAO){.get_o_type_fcn = (v_get_o_type_fcn), ##__VA_ARGS__})) + +#define NML_DBUS_META_PROPERTY_INIT_FCN(v_dbus_property_name, \ + v_obj_properties_idx, \ + v_dbus_type, \ + v_notify_update_prop, \ + ...) \ + NML_DBUS_META_PROPERTY_INIT(v_dbus_property_name, \ + v_dbus_type, \ + v_obj_properties_idx, \ + .use_notify_update_prop = TRUE, \ + .notify_update_prop = (v_notify_update_prop), \ + ##__VA_ARGS__) + +#define NML_DBUS_META_PROPERTY_INIT_IGNORE(v_dbus_property_name, v_dbus_type) \ + NML_DBUS_META_PROPERTY_INIT(v_dbus_property_name, \ + v_dbus_type, \ + 0, \ + .use_notify_update_prop = TRUE, \ + .notify_update_prop = _nml_dbus_notify_update_prop_ignore) + +/* "TODO" is like "IGNORE". The difference is that we don't plan to ever implement "IGNORE", but + * "TODO" is something we should add support for. */ +#define NML_DBUS_META_PROPERTY_INIT_TODO(...) NML_DBUS_META_PROPERTY_INIT_IGNORE(__VA_ARGS__) + +struct _NMLDBusMetaIface { + const char *dbus_iface_name; + GType (*get_type_fcn)(void); + + /* Usually there is a one-to-one correspondence between the properties + * on D-Bus (dbus_properties) and the GObject properties (obj_properties). + * + * With: + * meta_iface->obj_properties[o_idx] (o_idx < n_obj_properties) + * &meta_iface->dbus_properties[d_idx] (d_idx < n_dbus_properties) + * it follows that + * assert (meta_iface->obj_properties_reverse_idx[o_idx] == d_idx) + * assert (meta_iface->dbus_properties[d_idx].obj_properties_idx == o_idx) + * if (and only if) two properties correspond. + */ + const GParamSpec *const * obj_properties; + const NMLDBusMetaProperty *dbus_properties; + const guint8 * obj_properties_reverse_idx; + + guint8 n_dbus_properties; + guint8 n_obj_properties; + + /* The offsets "prop_struct_offset" in NMLDBusMetaProperty are based on some base + * struct. If "base_struct_offset" is 0, then the base struct is the GObject pointer + * itself. + * If this is non-null, then we expect at that location a pointer to the offset. + * In this case we need to first find the base pointer via + * *((gpointer *) ((char *) nmobj + meta_iface->base_struct_offset)). + * + * This covers NMDeviceBridge._priv vs. NMDevice._priv. In the second case, + * _priv is a pointer that we first need to follow. + */ + guint8 base_struct_offset; + + /* We create the appropriate NMObject GType based on the D-Bus interfaces that + * are present. For example, if we see a "org.freedesktop.NetworkManager.Device.Bridge" + * interface, we create a NMDeviceBridge. Basically, if it looks like a certain + * object (based on the D-Bus interface), we assume it is. + * + * Some interfaces are purely additional ("org.freedesktop.NetworkManager.Device.Statistics") + * and don't determine the NMObject type (%NML_DBUS_META_INTERFACE_PRIO_NONE). + * + * Some interfaces are of a parent type ("org.freedesktop.NetworkManager.Device" for + * NMDevice), and don't determine the type either (%NML_DBUS_META_INTERFACE_PRIO_PARENT_TYPE). + * + * Some interfaces ("org.freedesktop.NetworkManager.AgentManager") belong to NMClient + * itself. Those have priority %NML_DBUS_META_INTERFACE_PRIO_NMCLIENT. + * + * In most cases, each D-Bus object is expected to have only one D-Bus interface + * to determine the type. While theoretically an object + * "/org/freedesktop/NetworkManager/Devices/3" could have interfaces "org.freedesktop.NetworkManager.Device.Bridge" + * and "org.freedesktop.NetworkManager.Device.Bond" at the same time, in practice it doesn't. + * Note that we also assume that once a D-Bus object gets a NMObject, it cannot change (*). + * NetworkManager's API does not add/remove interfaces after exporting the object the + * first time, so in practice each D-Bus object is expected to have a suitable D-Bus + * interface (and only determining interface, which doesn't change). Those interfaces have + * priority %NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30. + * + * (*) note that nothing bad would happen if a faulty NetworkManager would violate that. + * Of course, something would not work correctly, but the D-Bus interface we find is unexpected + * and wrong. + * + * One exception is "org.freedesktop.NetworkManager.Connection.Active". This can either + * be a NMActiveConnection or a NMVpnConnection. Hence, this profile has priority + * %NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_10, and depending on whether there is + * a "org.freedesktop.NetworkManager.VPN.Connection" (with high priority), we create + * one or the other type. + * + * Another exception is "org.freedesktop.NetworkManager.Device.Veth". It is a NMDeviceVeth + * and the parent is NMDeviceEthernet. Therefore it contains "org.freedesktop.NetworkManager.Device.Wired" + * as it should be registered as NMDeviceVeth, the profile has priority + * %NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_20. + * + */ + NMLDBusMetaInteracePrio interface_prio : 3; +}; + +#define NML_DBUS_META_IFACE_OBJ_PROPERTIES() \ + .obj_properties = (const GParamSpec *const *) (obj_properties), \ + .n_obj_properties = _PROPERTY_ENUMS_LAST, \ + .obj_properties_reverse_idx = ((guint8[_PROPERTY_ENUMS_LAST]){}) + +#define NML_DBUS_META_IFACE_DBUS_PROPERTIES(...) \ + .dbus_properties = ((const NMLDBusMetaProperty[]){__VA_ARGS__}), \ + .n_dbus_properties = \ + (sizeof((const NMLDBusMetaProperty[]){__VA_ARGS__}) / sizeof(NMLDBusMetaProperty)) + +#define NML_DBUS_META_IFACE_INIT(v_dbus_iface_name, v_get_type_fcn, v_interface_prio, ...) \ + { \ + .dbus_iface_name = "" v_dbus_iface_name "", .get_type_fcn = v_get_type_fcn, \ + .interface_prio = v_interface_prio, ##__VA_ARGS__ \ + } + +#define NML_DBUS_META_IFACE_INIT_PROP(v_dbus_iface_name, v_get_type_fcn, v_interface_prio, ...) \ + NML_DBUS_META_IFACE_INIT(v_dbus_iface_name, \ + v_get_type_fcn, \ + v_interface_prio, \ + NML_DBUS_META_IFACE_OBJ_PROPERTIES(), \ + ##__VA_ARGS__) + +extern const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[44]; + +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_accesspoint; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_agentmanager; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_checkpoint; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_connection_active; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_adsl; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bluetooth; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bond; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bridge; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_dummy; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_generic; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_infiniband; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_iptunnel; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_lowpan; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_macsec; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_macvlan; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_modem; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_olpcmesh; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ovsbridge; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ovsinterface; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ovsport; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ppp; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_statistics; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_team; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_tun; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_veth; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_vlan; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_vrf; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_vxlan; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wifip2p; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wired; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wireguard; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wireless; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wpan; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_dhcp4config; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_dhcp6config; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_dnsmanager; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_ip4config; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_ip6config; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_settings; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_settings_connection; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_vpn_connection; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_wifip2ppeer; + +const NMLDBusMetaIface *nml_dbus_meta_iface_get(const char *dbus_iface_name); + +const NMLDBusMetaProperty *nml_dbus_meta_property_get(const NMLDBusMetaIface *meta_iface, + const char * dbus_property_name, + guint * out_idx); + +void _nml_dbus_meta_class_init_with_properties_impl(GObjectClass * object_class, + const NMLDBusMetaIface *const *meta_iface); +#define _nml_dbus_meta_class_init_with_properties(object_class, ...) \ + _nml_dbus_meta_class_init_with_properties_impl( \ + (object_class), \ + ((const NMLDBusMetaIface *const[]){__VA_ARGS__, NULL})) + +/*****************************************************************************/ + +typedef enum { + NML_DBUS_OBJ_STATE_UNLINKED = 0, + NML_DBUS_OBJ_STATE_WATCHED_ONLY, + NML_DBUS_OBJ_STATE_ON_DBUS, + NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY, + NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY, +} NMLDBusObjState; + +typedef enum { + NML_DBUS_OBJ_CHANGED_TYPE_NONE = 0, + NML_DBUS_OBJ_CHANGED_TYPE_DBUS = (1LL << 0), + NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ = (1LL << 1), +} NMLDBusObjChangedType; + +struct _NMLDBusObject { + NMRefString *dbus_path; + + /* While the object is tracked by NMClient, it is linked with this list. + * The lists are partitioned based on the NMLDBusObjState. */ + CList dbus_objects_lst; + + /* The list of D-Bus interface NMLDBusObjIfaceData. + * + * Some may be about to be removed (iface_removed) or + * unknown (!dbus_iface_is_wellknown). */ + CList iface_lst_head; + + /* The list of registered NMLDBusObjWatcher. */ + CList watcher_lst_head; + + /* When an object changes (e.g. because of new information on D-Bus), we often + * don't process the changes right away, but enqueue the object in a changed + * list. This list goes together with obj_changed_type property below, which + * tracks what changed. */ + CList obj_changed_lst; + + GObject *nmobj; + + int ref_count; + + NMLDBusObjState obj_state : 4; + + NMLDBusObjChangedType obj_changed_type : 3; +}; + +static inline gboolean +NML_IS_DBUS_OBJECT(NMLDBusObject *dbobj) +{ + nm_assert(!dbobj || (NM_IS_REF_STRING(dbobj->dbus_path) && dbobj->ref_count > 0)); + nm_assert(!dbobj || !dbobj->nmobj || NM_IS_OBJECT(dbobj->nmobj) || NM_IS_CLIENT(dbobj->nmobj)); + return !!dbobj; +} + +NMLDBusObject *nml_dbus_object_ref(NMLDBusObject *dbobj); + +void nml_dbus_object_unref(NMLDBusObject *dbobj); + +NM_AUTO_DEFINE_FCN0(NMLDBusObject *, _nm_auto_unref_nml_dbusobj, nml_dbus_object_unref); +#define nm_auto_unref_nml_dbusobj nm_auto(_nm_auto_unref_nml_dbusobj) + +gpointer nml_dbus_object_get_property_location(NMLDBusObject * dbobj, + const NMLDBusMetaIface * meta_iface, + const NMLDBusMetaProperty *meta_property); + +/*****************************************************************************/ + +/* NMClient is not an NMObject, but in some aspects we want to track it like + * an NMObject. For that, both NMClient and NMObject "implement" NMObjectBase, + * despite not actually implementing such a GObject type. */ +typedef struct { + GObject parent; + CList queue_notify_lst; + bool is_disposing : 1; +} NMObjectBase; + +typedef struct { + GObjectClass parent; +} NMObjectBaseClass; + +struct _NMObjectPrivate; + +struct _NMObject { + union { + GObject parent; + NMObjectBase obj_base; + }; + struct _NMObjectPrivate *_priv; +}; + +typedef struct _NMObjectClassFieldInfo { + const struct _NMObjectClassFieldInfo *parent; + NMObjectClass * klass; + guint16 offset; + guint16 num; +} _NMObjectClassFieldInfo; + +struct _NMObjectClass { + union { + GObjectClass parent; + NMObjectBaseClass obj_base; + }; + + void (*register_client)(NMObject *self, NMClient *client, NMLDBusObject *dbobj); + + void (*unregister_client)(NMObject *self, NMClient *client, NMLDBusObject *dbobj); + + gboolean (*is_ready)(NMObject *self); + + void (*obj_changed_notify)(NMObject *self); + + const _NMObjectClassFieldInfo *property_o_info; + const _NMObjectClassFieldInfo *property_ao_info; + + guint16 priv_ptr_offset; + + bool priv_ptr_indirect : 1; +}; + +#define _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, type_name) \ + G_STMT_START \ + { \ + (nm_object_class)->priv_ptr_offset = \ + NM_STRUCT_OFFSET_ENSURE_TYPE(type_name##Private, type_name, _priv); \ + (nm_object_class)->priv_ptr_indirect = FALSE; \ + } \ + G_STMT_END + +#define _NM_OBJECT_CLASS_INIT_PRIV_PTR_INDIRECT(nm_object_class, type_name) \ + G_STMT_START \ + { \ + (nm_object_class)->priv_ptr_offset = \ + NM_STRUCT_OFFSET_ENSURE_TYPE(type_name##Private *, type_name, _priv); \ + (nm_object_class)->priv_ptr_indirect = TRUE; \ + } \ + G_STMT_END + +#define _NM_OBJECT_CLASS_INIT_FIELD_INFO(_nm_object_class, _field_name, _offset, _num) \ + G_STMT_START \ + { \ + (_nm_object_class)->_field_name = ({ \ + static _NMObjectClassFieldInfo _f; \ + \ + _f = (_NMObjectClassFieldInfo){ \ + .parent = (_nm_object_class)->_field_name, \ + .klass = (_nm_object_class), \ + .offset = _offset, \ + .num = _num, \ + }; \ + &_f; \ + }); \ + } \ + G_STMT_END + +#define _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, type_name, field_name) \ + _NM_OBJECT_CLASS_INIT_FIELD_INFO( \ + nm_object_class, \ + property_o_info, \ + NM_STRUCT_OFFSET_ENSURE_TYPE(NMLDBusPropertyO, type_name, field_name), \ + 1) + +#define _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_N(nm_object_class, type_name, field_name) \ + _NM_OBJECT_CLASS_INIT_FIELD_INFO( \ + nm_object_class, \ + property_o_info, \ + NM_STRUCT_OFFSET_ENSURE_TYPE(NMLDBusPropertyO *, type_name, field_name), \ + G_N_ELEMENTS(((type_name *) NULL)->field_name)) + +#define _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, type_name, field_name) \ + _NM_OBJECT_CLASS_INIT_FIELD_INFO( \ + nm_object_class, \ + property_ao_info, \ + NM_STRUCT_OFFSET_ENSURE_TYPE(NMLDBusPropertyAO, type_name, field_name), \ + 1) + +#define _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_N(nm_object_class, type_name, field_name) \ + _NM_OBJECT_CLASS_INIT_FIELD_INFO( \ + nm_object_class, \ + property_ao_info, \ + NM_STRUCT_OFFSET_ENSURE_TYPE(NMLDBusPropertyAO *, type_name, field_name), \ + G_N_ELEMENTS(((type_name *) NULL)->field_name)) + +/*****************************************************************************/ + +struct _NMDevicePrivate; + +struct _NMDevice { + NMObject parent; + struct _NMDevicePrivate *_priv; +}; + +struct _NMDeviceClass { + struct _NMObjectClass parent; + + gboolean (*connection_compatible)(NMDevice *device, NMConnection *connection, GError **error); + + const char *(*get_type_description)(NMDevice *device); + + GType (*get_setting_type)(NMDevice *device); +}; + +/*****************************************************************************/ + +struct _NMDeviceEthernetPrivate; + +struct _NMDeviceEthernet { + NMDevice parent; + struct _NMDeviceEthernetPrivate *_priv; +}; + +struct _NMDeviceEthernetClass { + NMDeviceClass parent; +}; + +/*****************************************************************************/ + +struct _NMActiveConnectionPrivate; + +struct _NMActiveConnection { + NMObject parent; + struct _NMActiveConnectionPrivate *_priv; +}; + +struct _NMActiveConnectionClass { + struct _NMObjectClass parent; +}; + +/*****************************************************************************/ + +struct _NMDhcpConfigPrivate; + +struct _NMDhcpConfig { + NMObject parent; + struct _NMDhcpConfigPrivate *_priv; +}; + +struct _NMDhcpConfigClass { + struct _NMObjectClass parent; +}; + +/*****************************************************************************/ + +struct _NMIPConfigPrivate; + +struct _NMIPConfig { + NMObject parent; + struct _NMIPConfigPrivate *_priv; +}; + +struct _NMIPConfigClass { + struct _NMObjectClass parent; +}; + +/*****************************************************************************/ + +NMLDBusObject *_nm_object_get_dbobj(gpointer self); + +const char *_nm_object_get_path(gpointer self); + +NMClient *_nm_object_get_client(gpointer self); + +GDBusConnection *_nm_client_get_dbus_connection(NMClient *client); + +const char *_nm_client_get_dbus_name_owner(NMClient *client); + +GMainContext *_nm_client_get_context_main(NMClient *client); +GMainContext *_nm_client_get_context_dbus(NMClient *client); + +void _nm_client_queue_notify_object(NMClient *client, gpointer nmobj, const GParamSpec *pspec); + +void _nm_client_notify_object_changed(NMClient *self, NMLDBusObject *dbobj); + +struct udev *_nm_client_get_udev(NMClient *self); + +/*****************************************************************************/ + +#define NM_CLIENT_NOTIFY_EVENT_PRIO_BEFORE (-100) +#define NM_CLIENT_NOTIFY_EVENT_PRIO_GPROP 0 +#define NM_CLIENT_NOTIFY_EVENT_PRIO_AFTER 100 + +typedef struct _NMClientNotifyEvent NMClientNotifyEvent; + +typedef void (*NMClientNotifyEventCb)(NMClient *self, gpointer notify_event); + +struct _NMClientNotifyEvent { + CList lst; + NMClientNotifyEventCb callback; + int priority; +}; + +gpointer _nm_client_notify_event_queue(NMClient * self, + int priority, + NMClientNotifyEventCb callback, + gsize event_size); + +typedef struct _NMClientNotifyEventWithPtr NMClientNotifyEventWithPtr; + +typedef void (*NMClientNotifyEventWithPtrCb)(NMClient * self, + NMClientNotifyEventWithPtr *notify_event); + +struct _NMClientNotifyEventWithPtr { + NMClientNotifyEvent parent; + gpointer user_data; +}; + +NMClientNotifyEventWithPtr * +_nm_client_notify_event_queue_with_ptr(NMClient * self, + int priority, + NMClientNotifyEventWithPtrCb callback, + gpointer user_data); + +void _nm_client_notify_event_queue_emit_obj_signal(NMClient *self, + GObject * source, + NMObject *nmobj, + gboolean is_added /* or else removed */, + int prio_offset, + guint signal_id); + +/*****************************************************************************/ + +GError *_nm_client_new_error_nm_not_running(void); +GError *_nm_client_new_error_nm_not_cached(void); + +void _nm_client_dbus_call_simple(NMClient * self, + GCancellable * cancellable, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + GAsyncReadyCallback callback, + gpointer user_data); + +void _nm_client_dbus_call(NMClient * self, + gpointer source_obj, + gpointer source_tag, + GCancellable * cancellable, + GAsyncReadyCallback user_callback, + gpointer user_callback_data, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + GAsyncReadyCallback internal_callback); + +GVariant *_nm_client_dbus_call_sync(NMClient * self, + GCancellable * cancellable, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + gboolean strip_dbus_error, + GError ** error); + +gboolean _nm_client_dbus_call_sync_void(NMClient * self, + GCancellable * cancellable, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + GDBusCallFlags flags, + int timeout_msec, + gboolean strip_dbus_error, + GError ** error); + +void _nm_client_set_property_sync_legacy(NMClient * self, + const char *object_path, + const char *interface, + const char *prop_name, + const char *format_string, + ...); + +/*****************************************************************************/ + +void _nm_client_get_settings_call(NMClient *self, NMLDBusObject *dbobj); + +GCancellable *_nm_remote_settings_get_settings_prepare(NMRemoteConnection *self); + +void _nm_remote_settings_get_settings_commit(NMRemoteConnection *self, GVariant *settings); + +/*****************************************************************************/ + +void +_nm_active_connection_state_changed_commit(NMActiveConnection *self, guint32 state, guint32 reason); + +void _nm_vpn_connection_state_changed_commit(NMVpnConnection *self, guint32 state, guint32 reason); + +/*****************************************************************************/ + +NMLDBusNotifyUpdatePropFlags +_nm_device_notify_update_prop_hw_address(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value); + +/*****************************************************************************/ + +#endif /* __NM_LIBNM_UTILS_H__ */ diff --git a/src/libnm-client-impl/nm-object-private.h b/src/libnm-client-impl/nm-object-private.h new file mode 100644 index 00000000..9f78304d --- /dev/null +++ b/src/libnm-client-impl/nm-object-private.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2008 - 2011 Red Hat, Inc. + */ + +#ifndef __NM_OBJECT_PRIVATE_H__ +#define __NM_OBJECT_PRIVATE_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#include "nm-object.h" + +#endif /* __NM_OBJECT_PRIVATE_H__ */ diff --git a/src/libnm-client-impl/nm-object.c b/src/libnm-client-impl/nm-object.c new file mode 100644 index 00000000..2e7de8c2 --- /dev/null +++ b/src/libnm-client-impl/nm-object.c @@ -0,0 +1,311 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-object.h" + +#include <stdlib.h> +#include <stdio.h> + +#include "nm-utils.h" +#include "nm-dbus-interface.h" +#include "nm-object-private.h" +#include "nm-dbus-helpers.h" +#include "nm-client.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "c-list/src/c-list.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PATH, ); + +typedef struct _NMObjectPrivate { + NMClient * client; + NMLDBusObject *dbobj; +} NMObjectPrivate; + +G_DEFINE_ABSTRACT_TYPE(NMObject, nm_object, G_TYPE_OBJECT); + +#define NM_OBJECT_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMObject, NM_IS_OBJECT) + +static NMObjectClass *_nm_object_class = NULL; + +/*****************************************************************************/ + +static gpointer +_nm_object_get_private(NMObjectClass *klass, NMObject *self, guint16 extra_offset) +{ + char *ptr; + + nm_assert(klass->priv_ptr_offset > 0); + + ptr = (char *) self; + ptr += klass->priv_ptr_offset; + if (klass->priv_ptr_indirect) + ptr = *((gpointer *) ptr); + return ptr + extra_offset; +} + +NMLDBusObject * +_nm_object_get_dbobj(gpointer self) +{ + return NM_OBJECT_GET_PRIVATE(self)->dbobj; +} + +const char * +_nm_object_get_path(gpointer self) +{ + return NM_OBJECT_GET_PRIVATE(self)->dbobj->dbus_path->str; +} + +NMClient * +_nm_object_get_client(gpointer self) +{ + return NM_OBJECT_GET_PRIVATE(self)->client; +} + +/** + * nm_object_get_path: + * @object: a #NMObject + * + * Gets the DBus path of the #NMObject. + * + * Returns: the object's path. This is the internal string used by the + * object, and must not be modified. + * + * Note that the D-Bus path of an NMObject never changes, even + * if the instance gets removed from the cache. To find out + * whether the object is still alive/cached, check nm_object_get_client(). + **/ +const char * +nm_object_get_path(NMObject *object) +{ + g_return_val_if_fail(NM_IS_OBJECT(object), NULL); + + return _nm_object_get_path(object); +} + +/** + * nm_object_get_client: + * @object: a #NMObject + * + * Returns the #NMClient instance in which object is cached. + * Also, if the object got removed from the client cached, + * this returns %NULL. So it can be used to check whether the + * object is still alive. + * + * Returns: (transfer none): the #NMClient cache in which the + * object can be found, or %NULL if the object is no longer + * cached. + * + * Since: 1.24 + **/ +NMClient * +nm_object_get_client(NMObject *object) +{ + g_return_val_if_fail(NM_IS_OBJECT(object), NULL); + + return _nm_object_get_client(object); +} + +/*****************************************************************************/ + +static void +clear_properties(NMObject *self, NMClient *client) +{ + NMObjectClass * klass = NM_OBJECT_GET_CLASS(self); + const _NMObjectClassFieldInfo *p; + + nm_assert(NM_IS_OBJECT(self)); + nm_assert(!client || NM_IS_CLIENT(client)); + + for (p = klass->property_o_info; p; p = p->parent) { + nml_dbus_property_o_clear_many(_nm_object_get_private(p->klass, self, p->offset), + p->num, + client); + } + + for (p = klass->property_ao_info; p; p = p->parent) { + nml_dbus_property_ao_clear_many(_nm_object_get_private(p->klass, self, p->offset), + p->num, + client); + } +} + +/*****************************************************************************/ + +static gboolean +is_ready(NMObject *self) +{ + NMObjectClass * klass = NM_OBJECT_GET_CLASS(self); + NMClient * client = _nm_object_get_client(self); + const _NMObjectClassFieldInfo *p; + guint16 i; + + nm_assert(NM_IS_CLIENT(client)); + + for (p = klass->property_o_info; p; p = p->parent) { + NMLDBusPropertyO *fields = _nm_object_get_private(p->klass, self, p->offset); + + for (i = 0; i < p->num; i++) { + if (!nml_dbus_property_o_is_ready(&fields[i])) + return FALSE; + } + } + + for (p = klass->property_ao_info; p; p = p->parent) { + NMLDBusPropertyAO *fields = _nm_object_get_private(p->klass, self, p->offset); + + for (i = 0; i < p->num; i++) { + if (!nml_dbus_property_ao_is_ready(&fields[i])) + return FALSE; + } + } + + return TRUE; +} + +static void +obj_changed_notify(NMObject *self) +{ + NMObjectClass * klass = NM_OBJECT_GET_CLASS(self); + NMClient * client = _nm_object_get_client(self); + const _NMObjectClassFieldInfo *p; + + nm_assert(NM_IS_CLIENT(client)); + + for (p = klass->property_o_info; p; p = p->parent) { + nml_dbus_property_o_notify_changed_many(_nm_object_get_private(p->klass, self, p->offset), + p->num, + client); + } + + for (p = klass->property_ao_info; p; p = p->parent) { + nml_dbus_property_ao_notify_changed_many(_nm_object_get_private(p->klass, self, p->offset), + p->num, + client); + } +} + +/*****************************************************************************/ + +static void +register_client(NMObject *self, NMClient *client, NMLDBusObject *dbobj) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE(self); + + nm_assert(!priv->client); + nm_assert(NML_IS_DBUS_OBJECT(dbobj)); + nm_assert(dbobj->nmobj == G_OBJECT(self)); + + priv->client = client; + priv->dbobj = nml_dbus_object_ref(dbobj); +} + +static void +unregister_client(NMObject *self, NMClient *client, NMLDBusObject *dbobj) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE(self); + + nm_assert(NM_IS_CLIENT(client)); + nm_assert(priv->client == client); + priv->client = NULL; + + clear_properties(self, client); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMObject *self = NM_OBJECT(object); + + switch (prop_id) { + case PROP_PATH: + g_value_set_string(value, nm_object_get_path(self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_object_init(NMObject *object) +{ + NMObject * self = NM_OBJECT(object); + NMObjectPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_OBJECT, NMObjectPrivate); + + self->_priv = priv; + + c_list_init(&self->obj_base.queue_notify_lst); + + NML_DBUS_LOG(_NML_NMCLIENT_LOG_LEVEL_COERCE(NML_DBUS_LOG_LEVEL_TRACE), + "nmobj[" NM_HASH_OBFUSCATE_PTR_FMT "]: creating", + NM_HASH_OBFUSCATE_PTR(self)); +} + +static void +dispose(GObject *object) +{ + NMObject * self = NM_OBJECT(object); + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE(self); + + if (!self->obj_base.is_disposing) { + NML_DBUS_LOG(_NML_NMCLIENT_LOG_LEVEL_COERCE(NML_DBUS_LOG_LEVEL_TRACE), + "nmobj[" NM_HASH_OBFUSCATE_PTR_FMT "]: disposing", + NM_HASH_OBFUSCATE_PTR(self)); + } + + self->obj_base.is_disposing = TRUE; + + nm_assert(c_list_is_empty(&self->obj_base.queue_notify_lst)); + nm_assert(!priv->client); + nm_assert(!priv->dbobj || !priv->dbobj->nmobj); + + clear_properties(self, NULL); + + G_OBJECT_CLASS(nm_object_parent_class)->dispose(object); + + nm_clear_pointer(&priv->dbobj, nml_dbus_object_unref); +} + +static void +nm_object_class_init(NMObjectClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + _nm_object_class = klass; + + g_type_class_add_private(klass, sizeof(NMObjectPrivate)); + + object_class->get_property = get_property; + object_class->dispose = dispose; + + klass->register_client = register_client; + klass->unregister_client = unregister_client; + klass->is_ready = is_ready; + klass->obj_changed_notify = obj_changed_notify; + + /** + * NMObject:path: + * + * The D-Bus object path. + **/ + obj_properties[PROP_PATH] = g_param_spec_string(NM_OBJECT_PATH, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} diff --git a/src/libnm-client-impl/nm-property-infos-dbus.xml b/src/libnm-client-impl/nm-property-infos-dbus.xml new file mode 100644 index 00000000..216ed1e7 --- /dev/null +++ b/src/libnm-client-impl/nm-property-infos-dbus.xml @@ -0,0 +1,125 @@ +<nm-setting-docs> +<setting name="6lowpan"> +</setting> +<setting name="802-1x"> +</setting> +<setting name="adsl"> +</setting> +<setting name="bluetooth"> +</setting> +<setting name="bond"> +<property name="interface-name" variable="interface-name" format="string" values="" default="" example="" description="Deprecated in favor of connection.interface-name, but can be used for backward-compatibility with older daemons, to set the bond's interface name." /> +</setting> +<setting name="bridge-port"> +</setting> +<setting name="bridge"> +<property name="interface-name" variable="interface-name" format="string" values="" default="" example="" description="Deprecated in favor of connection.interface-name, but can be used for backward-compatibility with older daemons, to set the bridge's interface name." /> +</setting> +<setting name="cdma"> +</setting> +<setting name="connection"> +</setting> +<setting name="dcb"> +</setting> +<setting name="dummy"> +</setting> +<setting name="ethtool"> +</setting> +<setting name="generic"> +</setting> +<setting name="gsm"> +</setting> +<setting name="hostname"> +</setting> +<setting name="infiniband"> +</setting> +<setting name="ip-tunnel"> +</setting> +<setting name="ipv4"> +<property name="dns" variable="dns" format="array of uint32" values="" default="" example="" description="Array of IP addresses of DNS servers (as network-byte-order integers)" /> +<property name="addresses" variable="addresses" format="array of array of uint32" values="" default="" example="" description="Deprecated in favor of the 'address-data' and 'gateway' properties, but this can be used for backward-compatibility with older daemons. Note that if you send this property the daemon will ignore 'address-data' and 'gateway'. Array of IPv4 address structures. Each IPv4 address structure is composed of 3 32-bit values; the first being the IPv4 address (network byte order), the second the prefix (1 - 32), and last the IPv4 gateway (network byte order). The gateway may be left as 0 if no gateway exists for that subnet." /> +<property name="address-data" variable="address-data" format="array of vardict" values="" default="" example="" description="Array of IPv4 addresses. Each address dictionary contains at least 'address' and 'prefix' entries, containing the IP address as a string, and the prefix length as a uint32. Additional attributes may also exist on some addresses." /> +<property name="routes" variable="routes" format="array of array of uint32" values="" default="" example="" description="Deprecated in favor of the 'route-data' property, but this can be used for backward-compatibility with older daemons. Note that if you send this property the daemon will ignore 'route-data'. Array of IPv4 route structures. Each IPv4 route structure is composed of 4 32-bit values; the first being the destination IPv4 network or address (network byte order), the second the destination network or address prefix (1 - 32), the third being the next-hop (network byte order) if any, and the fourth being the route metric. If the metric is 0, NM will choose an appropriate default metric for the device. (There is no way to explicitly specify an actual metric of 0 with this property.)" /> +<property name="route-data" variable="route-data" format="array of vardict" values="" default="" example="" description="Array of IPv4 routes. Each route dictionary contains at least 'dest' and 'prefix' entries, containing the destination IP address as a string, and the prefix length as a uint32. Most routes will also have a 'next-hop' entry, containing the next hop IP address as a string. If the route has a 'metric' entry (containing a uint32), that will be used as the metric for the route (otherwise NM will pick a default value appropriate to the device). Additional attributes may also exist on some routes." /> +</setting> +<setting name="ipv6"> +<property name="dns" variable="dns" format="array of byte array" values="" default="" example="" description="Array of IP addresses of DNS servers (in network byte order)" /> +<property name="addresses" variable="addresses" format="array of legacy IPv6 address struct (a(ayuay))" values="" default="" example="" description="Deprecated in favor of the 'address-data' and 'gateway' properties, but this can be used for backward-compatibility with older daemons. Note that if you send this property the daemon will ignore 'address-data' and 'gateway'. Array of IPv6 address structures. Each IPv6 address structure is composed of an IPv6 address, a prefix length (1 - 128), and an IPv6 gateway address. The gateway may be zeroed out if no gateway exists for that subnet." /> +<property name="address-data" variable="address-data" format="array of vardict" values="" default="" example="" description="Array of IPv6 addresses. Each address dictionary contains at least 'address' and 'prefix' entries, containing the IP address as a string, and the prefix length as a uint32. Additional attributes may also exist on some addresses." /> +<property name="routes" variable="routes" format="array of legacy IPv6 route struct (a(ayuayu))" values="" default="" example="" description="Deprecated in favor of the 'route-data' property, but this can be used for backward-compatibility with older daemons. Note that if you send this property the daemon will ignore 'route-data'. Array of IPv6 route structures. Each IPv6 route structure is composed of an IPv6 address, a prefix length (1 - 128), an IPv6 next hop address (which may be zeroed out if there is no next hop), and a metric. If the metric is 0, NM will choose an appropriate default metric for the device." /> +<property name="route-data" variable="route-data" format="array of vardict" values="" default="" example="" description="Array of IPv6 routes. Each route dictionary contains at least 'dest' and 'prefix' entries, containing the destination IP address as a string, and the prefix length as a uint32. Most routes will also have a 'next-hop' entry, containing the next hop IP address as a string. If the route has a 'metric' entry (containing a uint32), that will be used as the metric for the route (otherwise NM will pick a default value appropriate to the device). Additional attributes may also exist on some routes." /> +</setting> +<setting name="macsec"> +</setting> +<setting name="macvlan"> +</setting> +<setting name="match"> +</setting> +<setting name="802-11-olpc-mesh"> +</setting> +<setting name="ovs-bridge"> +</setting> +<setting name="ovs-dpdk"> +</setting> +<setting name="ovs-external-ids"> +</setting> +<setting name="ovs-interface"> +</setting> +<setting name="ovs-patch"> +</setting> +<setting name="ovs-port"> +</setting> +<setting name="ppp"> +</setting> +<setting name="pppoe"> +</setting> +<setting name="proxy"> +</setting> +<setting name="serial"> +<property name="parity" variable="parity" format="byte" values="" default="" example="" description="The connection parity: 69 (ASCII 'E') for even parity, 111 (ASCII 'o') for odd, 110 (ASCII 'n') for none." /> +</setting> +<setting name="sriov"> +</setting> +<setting name="tc"> +</setting> +<setting name="team-port"> +</setting> +<setting name="team"> +<property name="interface-name" variable="interface-name" format="string" values="" default="" example="" description="Deprecated in favor of connection.interface-name, but can be used for backward-compatibility with older daemons, to set the team's interface name." /> +</setting> +<setting name="tun"> +</setting> +<setting name="user"> +</setting> +<setting name="veth"> +</setting> +<setting name="vlan"> +<property name="interface-name" variable="interface-name" format="string" values="" default="" example="" description="Deprecated in favor of connection.interface-name, but can be used for backward-compatibility with older daemons, to set the vlan's interface name." /> +</setting> +<setting name="vpn"> +</setting> +<setting name="vrf"> +</setting> +<setting name="vxlan"> +</setting> +<setting name="wifi-p2p"> +</setting> +<setting name="wimax"> +</setting> +<setting name="802-3-ethernet"> +<property name="cloned-mac-address" variable="cloned-mac-address" format="byte array" values="" default="" example="" description="This D-Bus field is deprecated in favor of "assigned-mac-address" which is more flexible and allows specifying special variants like "random". For libnm and nmcli, this field is called "cloned-mac-address"." /> +<property name="assigned-mac-address" variable="assigned-mac-address" format="string" values="" default="" example="" description="The new field for the cloned MAC address. It can be either a hardware address in ASCII representation, or one of the special values "preserve", "permanent", "random" or "stable". This field replaces the deprecated "cloned-mac-address" on D-Bus, which can only contain explicit hardware addresses. Note that this property only exists in D-Bus API. libnm and nmcli continue to call this property "cloned-mac-address"." /> +</setting> +<setting name="wireguard"> +<property name="peers" variable="peers" format="array of 'a{sv}'" values="" default="" example="" description="Array of dictionaries for the WireGuard peers." /> +</setting> +<setting name="802-11-wireless-security"> +</setting> +<setting name="802-11-wireless"> +<property name="cloned-mac-address" variable="cloned-mac-address" format="byte array" values="" default="" example="" description="This D-Bus field is deprecated in favor of "assigned-mac-address" which is more flexible and allows specifying special variants like "random". For libnm and nmcli, this field is called "cloned-mac-address"." /> +<property name="assigned-mac-address" variable="assigned-mac-address" format="string" values="" default="" example="" description="The new field for the cloned MAC address. It can be either a hardware address in ASCII representation, or one of the special values "preserve", "permanent", "random" or "stable". This field replaces the deprecated "cloned-mac-address" on D-Bus, which can only contain explicit hardware addresses. Note that this property only exists in D-Bus API. libnm and nmcli continue to call this property "cloned-mac-address"." /> +<property name="security" variable="security" format="" values="" default="" example="" description="This property is deprecated, but can be set to the value '802-11-wireless-security' when a wireless security setting is also present in the connection dictionary, for compatibility with very old NetworkManager daemons." /> +</setting> +<setting name="wpan"> +</setting> +</nm-setting-docs> \ No newline at end of file diff --git a/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml b/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml new file mode 100644 index 00000000..df38ca79 --- /dev/null +++ b/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml @@ -0,0 +1,335 @@ +<nm-setting-docs> +<setting name="6lowpan"> +</setting> +<setting name="802-1x"> +<property name="eap" variable="IEEE_8021X_EAP_METHODS(+)" format="" values=""LEAP", "PWD", "TLS", "PEAP", "TTLS", "FAST"" default="" example="IEEE_8021X_EAP_METHODS=PEAP" description="EAP method for 802.1X authentication." /> +<property name="identity" variable="IEEE_8021X_IDENTITY(+)" format="" values="" default="" example="IEEE_8021X_IDENTITY=itsme" description="Identity for EAP authentication methods." /> +<property name="anonymous-identity" variable="IEEE_8021X_ANON_IDENTITY(+)" format="" values="" default="" example="" description="Anonymous identity for EAP authentication methods." /> +<property name="pac-file" variable="IEEE_8021X_PAC_FILE(+)" format="" values="" default="" example="IEEE_8021X_PAC_FILE=/home/joe/my-fast.pac" description="File with PAC (Protected Access Credential) for EAP-FAST." /> +<property name="ca-cert" variable="IEEE_8021X_CA_CERT(+)" format="" values="" default="" example="IEEE_8021X_CA_CERT=/home/joe/cacert.crt" description="CA certificate for EAP." /> +<property name="ca-path" variable="IEEE_8021X_CA_PATH(+)" format="" values="" default="" example="" description="The search path for the certificate." /> +<property name="subject-match" variable="IEEE_8021X_SUBJECT_MATCH(+)" format="" values="" default="" example="IEEE_8021X_SUBJECT_MATCH="Red Hat"" description="Substring to match subject of server certificate against." /> +<property name="altsubject-matches" variable="IEEE_8021X_ALTSUBJECT_MATCHES(+)" format="" values="" default="" example="IEEE_8021X_ALTSUBJECT_MATCHES="s1.domain.cc"" description="List of strings to be matched against the altSubjectName." /> +<property name="domain-suffix-match" variable="IEEE_8021X_DOMAIN_SUFFIX_MATCH(+)" format="" values="" default="" example="" description="Suffix to match domain of server certificate against." /> +<property name="domain-match" variable="IEEE_8021X_DOMAIN_MATCH(+)" format="" values="" default="" example="" description="Value to match domain of server certificate against." /> +<property name="client-cert" variable="IEEE_8021X_CLIENT_CERT(+)" format="" values="" default="" example="IEEE_8021X_CLIENT_CERT=/home/joe/mycert.crt" description="Client certificate for EAP." /> +<property name="phase1-peapver" variable="IEEE_8021X_PEAP_VERSION(+)" format="" values="0, 1" default="" example="" description="Use to force a specific PEAP version." /> +<property name="phase1-peaplabel" variable="IEEE_8021X_PEAP_FORCE_NEW_LABEL(+)" format="" values="yes, no" default="no" example="" description="Use to force the new PEAP label during key derivation." /> +<property name="phase1-fast-provisioning" variable="IEEE_8021X_FAST_PROVISIONING(+)" format="" values="space-separated list of these values [allow-auth, allow-unauth]" default="" example="IEEE_8021X_FAST_PROVISIONING="allow-auth allow-unauth"" description="Enable in-line provisioning of EAP-FAST credentials." /> +<property name="phase1-auth-flags" variable="IEEE_8021X_PHASE1_AUTH_FLAGS(+)" format="" values="space-separated list of authentication flags names" default="" example="IEEE_8021X_PHASE1_AUTH_FLAGS="tls-1-0-disable tls-1-1-disable"" description="Authentication flags for the supplicant" /> +<property name="phase2-auth" variable="IEEE_8021X_INNER_AUTH_METHODS(+)" format="" values=""PAP", "CHAP", "MSCHAP", "MSCHAPV2", "GTC", "OTP", "MD5" and "TLS"" default="" example="IEEE_8021X_INNER_AUTH_METHODS=PAP" description="Inner non-EAP authentication methods for TTLS or the inner EAP authentication method for PEAP. IEEE_8021X_INNER_AUTH_METHODS can contain values both for 'phase2-auth' and 'phase2-autheap' properties." /> +<property name="phase2-autheap" variable="IEEE_8021X_INNER_AUTH_METHODS(+)" format="" values=""EAP-MD5", "EAP-MSCHAPV2", "EAP-GTC", "EAP-OTP" and "EAP-TLS"" default="" example="IEEE_8021X_INNER_AUTH_METHODS="MSCHAPV2 EAP-TLS"" description="Inner EAP-based authentication methods. Note that IEEE_8021X_INNER_AUTH_METHODS is also used for 'phase2-auth' values." /> +<property name="phase2-ca-path" variable="IEEE_8021X_PHASE2_CA_PATH(+)" format="" values="" default="" example="" description="The search path for the certificate." /> +<property name="phase2-subject-match" variable="IEEE_8021X_PHASE2_SUBJECT_MATCH(+)" format="" values="" default="" example="IEEE_8021X_PHASE2_SUBJECT_MATCH="Red Hat"" description="Substring to match subject of server certificate against." /> +<property name="phase2-altsubject-matches" variable="IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES(+)" format="" values="" default="" example="" description="" /> +<property name="phase2-domain-suffix-match" variable="IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH(+)" format="" values="" default="" example="" description="Suffix to match domain of server certificate for phase 2 against." /> +<property name="phase2-domain-match" variable="IEEE_8021X_PHASE2_DOMAIN_MATCH(+)" format="" values="" default="" example="" description="Value to match domain of server certificate for phase 2 against." /> +<property name="phase2-client-cert" variable="IEEE_8021X_INNER_CLIENT_CERT(+)" format="" values="" default="" example="IEEE_8021X_INNER_CLIENT_CERT=/home/joe/mycert.crt" description="Client certificate for inner EAP method." /> +<property name="password" variable="IEEE_8021X_PASSWORD(+)" format="" values="" default="" example="" description="UTF-8 encoded password used for EAP. It can also go to "key-" lookaside file, or it can be owned by a secret agent." /> +<property name="password-flags" variable="IEEE_8021X_PASSWORD_FLAGS(+)" format="NMSettingSecretFlags" values="" default="" example="" description="Password flags for IEEE_8021X_PASSWORD password." /> +<property name="password-raw" variable="IEEE_8021X_PASSWORD_RAW(+)" format="" values="" default="" example="IEEE_8021X_PASSWORD_RAW=041c8320083aa4bf" description="password used for EAP, encoded as a hexadecimal string. It can also go to "key-" lookaside file." /> +<property name="password-raw-flags" variable="IEEE_8021X_PASSWORD_RAW_FLAGS(+)" format="" values="" default="" example="" description="The secret flags for password-raw." /> +<property name="private-key" variable="IEEE_8021X_PRIVATE_KEY(+)" format="" values="" default="" example="IEEE_8021X_PRIVATE_KEY=/home/joe/mykey.p12" description="Private key for EAP-TLS." /> +<property name="private-key-password" variable="IEEE_8021X_PRIVATE_KEY_PASSWORD(+)" format="" values="" default="" example="" description="Password for IEEE_8021X_PRIVATE_KEY. It can also go to "key-" lookaside file, or it can be owned by a secret agent." /> +<property name="private-key-password-flags" variable="IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS(+)" format="NMSettingSecretFlags" values="" default="" example="" description="Password flags for IEEE_8021X_PRIVATE_KEY_PASSWORD password." /> +<property name="phase2-private-key" variable="IEEE_8021X_INNER_PRIVATE_KEY(+)" format="" values="" default="" example="" description="Private key for inner authentication method for EAP-TLS." /> +<property name="phase2-private-key-password" variable="IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD(+)" format="" values="" default="" example="" description="Password for IEEE_8021X_INNER_PRIVATE_KEY. It can also go to "key-" lookaside file, or it can be owned by a secret agent." /> +<property name="phase2-private-key-password-flags" variable="IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS(+)" format="NMSettingSecretFlags" values="" default="" example="" description="Password flags for IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD password." /> +<property name="pin" variable="IEEE_8021X_PIN(+)" format="" values="" default="" example="" description="The pin secret used for EAP authentication methods." /> +<property name="pin-flags" variable="IEEE_8021X_PIN_FLAGS(+)" format="" values="" default="" example="" description="The secret flags for the pin property." /> +<property name="system-ca-certs" variable="IEEE_8021X_SYSTEM_CA_CERTS(+)" format="" values="" default="" example="" description="a boolean value." /> +<property name="auth-timeout" variable="IEEE_8021X_AUTH_TIMEOUT(+)" format="" values="" default="0" example="" description="Timeout in seconds for the 802.1X authentication. Zero means the global default or 25." /> +<property name="optional" variable="IEEE_8021X_OPTIONAL(+) default=no" format="" values="" default="" example="" description="whether the 802.1X authentication is optional" /> +</setting> +<setting name="adsl"> +</setting> +<setting name="bluetooth"> +</setting> +<setting name="bond"> +<property name="options" variable="BONDING_OPTS" format="" values="" default="" example="BONDING_OPTS="miimon=100 mode=broadcast"" description="Bonding options." /> +</setting> +<setting name="bridge-port"> +<property name="priority" variable="BRIDGING_OPTS: priority=" format="" values="0 - 63" default="32" example="" description="STP priority." /> +<property name="path-cost" variable="BRIDGING_OPTS: path_cost=" format="" values="1 - 65535" default="100" example="" description="STP cost." /> +<property name="hairpin-mode" variable="BRIDGING_OPTS: hairpin_mode=" format="" values="" default="yes" example="" description="Hairpin mode of the bridge port." /> +<property name="vlans" variable="BRIDGE_PORT_VLANS" format="" values="" default="" example="BRIDGE_PORT_VLANS="1 pvid untagged,20,300-400 untagged"" description="List of VLANs on the bridge port" /> +</setting> +<setting name="bridge"> +<property name="mac-address" variable="BRIDGE_MACADDR(+)" format="" values="" default="" example="" description="MAC address of the bridge. Note that this requires a recent kernel support, originally introduced in 3.15 upstream kernel) BRIDGE_MACADDR for bridges is an NM extension." /> +<property name="stp" variable="STP" format="" values="" default="no" example="" description="Span tree protocol participation." /> +<property name="priority" variable="BRIDGING_OPTS: priority=" format="" values="0 - 32768" default="32768" example="" description="STP priority." /> +<property name="forward-delay" variable="DELAY" format="" values="2 - 30" default="15" example="" description="STP forwarding delay." /> +<property name="hello-time" variable="BRIDGING_OPTS: hello_time=" format="" values="1 - 10" default="2" example="" description="STP hello time." /> +<property name="max-age" variable="BRIDGING_OPTS: max_age=" format="" values="6 - 40" default="20" example="" description="STP maximum message age." /> +<property name="ageing-time" variable="BRIDGING_OPTS: ageing_time=" format="" values="0 - 1000000" default="300" example="" description="Ethernet MAC ageing time." /> +<property name="multicast-snooping" variable="BRIDGING_OPTS: multicast_snooping=" format="" values="0 or 1" default="1" example="" description="IGMP snooping support." /> +<property name="vlan-filtering" variable="BRIDGING_OPTS: vlan_filtering=" format="" values="0 or 1" default="0" example="" description="VLAN filtering support." /> +<property name="vlan-default-pvid" variable="BRIDGING_OPTS: default_pvid=" format="" values="0 - 4094" default="1" example="" description="default VLAN PVID." /> +<property name="vlans" variable="BRIDGE_VLANS" format="" values="" default="" example="BRIDGE_VLANS="1 pvid untagged,20,300-400 untagged"" description="List of VLANs on the bridge" /> +<property name="group-address" variable="BRIDGING_OPTS: group_address=" format="" values="" default="" example="BRIDGING_OPTS="group_address=01:80:C2:00:00:0A"" description="STP group address." /> +<property name="vlan-protocol" variable="BRIDGING_OPTS: vlan_protocol=" format="" values="" default="" example="BRIDGING_OPTS="vlan_protocol=802.1Q"" description="VLAN filtering protocol." /> +<property name="vlan-stats-enabled" variable="BRIDGING_OPTS: vlan_stats_enabled=" format="" values="" default="0" example="BRIDGING_OPTS="vlan_stats_enabled=1"" description="" /> +<property name="multicast-router" variable="BRIDGING_OPTS: multicast_router=" format="" values="auto, enabled, disabled" default="auto" example="BRIDGING_OPTS="multicast_router=enabled"" description="" /> +<property name="multicast-query-use-ifaddr" variable="BRIDGING_OPTS: multicast_query_use_ifaddr=" format="" values="" default="0" example="BRIDGING_OPTS="multicast_query-use_ifaddr=1"" description="" /> +<property name="multicast-querier" variable="BRIDGING_OPTS: multicast_querier=" format="" values="" default="0" example="BRIDGING_OPTS="multicast_querier=1"" description="" /> +<property name="multicast-hash-max" variable="BRIDGING_OPTS: multicast_hash_max=" format="" values="" default="4096" example="BRIDGING_OPTS="multicast_hash_max=8192"" description="" /> +<property name="multicast-last-member-count" variable="BRIDGING_OPTS: multicast_last_member_count=" format="" values="" default="2" example="BRIDGING_OPTS="multicast_last_member_count=4"" description="" /> +<property name="multicast-last-member-interval" variable="BRIDGING_OPTS: multicast_last_member_interval=" format="" values="" default="100" example="BRIDGING_OPTS="multicast_last_member_interval=200"" description="" /> +<property name="multicast-membership-interval" variable="BRIDGING_OPTS: multicast_membership_interval=" format="" values="" default="26000" example="BRIDGING_OPTS="multicast_membership_interval=16000"" description="" /> +<property name="multicast-querier-interval" variable="BRIDGING_OPTS: multicast_querier_interval=" format="" values="" default="25500" example="BRIDGING_OPTS="multicast_querier_interval=20000"" description="" /> +<property name="multicast-query-interval" variable="BRIDGING_OPTS: multicast_query_interval=" format="" values="" default="12500" example="BRIDGING_OPTS="multicast_query_interval=22500"" description="" /> +<property name="multicast-query-response-interval" variable="BRIDGING_OPTS: multicast_query_response_interval=" format="" values="" default="1000" example="BRIDGING_OPTS="multicast_query_response_interval=2000"" description="" /> +<property name="multicast-startup-query-count" variable="BRIDGING_OPTS: multicast_startup_query_count=" format="" values="" default="2" example="BRIDGING_OPTS="multicast_startup_query_count=4"" description="" /> +<property name="multicast-startup-query-interval" variable="BRIDGING_OPTS: multicast_startup_query_interval=" format="" values="" default="3125" example="BRIDGING_OPTS="multicast_startup_query_interval=4000"" description="" /> +</setting> +<setting name="cdma"> +</setting> +<setting name="connection"> +<property name="id" variable="NAME(+)" format="" values="" default="" example="" description="User friendly name for the connection profile." /> +<property name="uuid" variable="UUID(+)" format="" values="" default="" example="" description="UUID for the connection profile. When missing, NetworkManager creates the UUID itself (by hashing the filename)." /> +<property name="stable-id" variable="STABLE_ID(+)" format="" values="" default="" example="" description="Token to generate stable IDs." /> +<property name="interface-name" variable="DEVICE" format="" values="" default="" example="" description="Interface name of the device this profile is bound to. The variable can be left out when the profile should apply for more devices. Note that DEVICE can be required for some connection types." /> +<property name="type" variable="TYPE (DEVICETYPE, DEVICE)" format="" values="Ethernet, Wireless, InfiniBand, Bridge, Bond, Vlan, Team, TeamPort" default="" example="TYPE=Ethernet; TYPE=Bond; TYPE=Bridge; DEVICETYPE=TeamPort" description="Base type of the connection. DEVICETYPE is used for teaming connections." /> +<property name="permissions" variable="USERS(+)" format="" values="" default="" example="USERS="joe bob"" description="Restrict to certain users the access to this connection, and allow the connection to be active only when at least one of the specified users is logged into an active session." /> +<property name="autoconnect" variable="ONBOOT" format="" values="" default="yes" example="" description="Whether the connection should be autoconnected (not only while booting)." /> +<property name="autoconnect-priority" variable="AUTOCONNECT_PRIORITY(+)" format="" values="-999 to 999" default="0" example="AUTOCONNECT_PRIORITY=20" description="Connection priority for automatic activation. Connections with higher numbers are preferred when selecting profiles for automatic activation." /> +<property name="autoconnect-retries" variable="AUTOCONNECT_RETRIES(+)" format="" values="-1 (use global default), 0 (forever) or a positive value" default="" example="AUTOCONNECT_RETRIES=1" description="The number of times a connection should be autoactivated before giving up and switching to the next one." /> +<property name="multi-connect" variable="MULTI_CONNECT(+)" format="" values="" default="" example="MULTI_CONNECT=3" description="whether the profile can be active on multiple devices at a given moment. The values are numbers corresponding to #NMConnectionMultiConnect enum." /> +<property name="zone" variable="ZONE(+)" format="" values="" default="" example="ZONE=Work" description="Trust level of this connection. The string is usually used for a firewall." /> +<property name="master" variable="MASTER, MASTER_UUID, TEAM_MASTER, TEAM_MASTER_UUID, BRIDGE, BRIDGE_UUID" format="" values="" default="" example="" description="Reference to master connection. The variable used depends on the connection type and the value. In general, if the *_UUID variant is present, the variant without *_UUID is ignored. NetworkManager attempts to write both for compatibility with legacy tooling." /> +<property name="slave-type" variable="MASTER, MASTER_UUID, TEAM_MASTER, TEAM_MASTER_UUID, DEVICETYPE, BRIDGE, BRIDGE_UUID" format="" values="" default="" example="" description="Slave type doesn't map directly to a variable, but it is recognized using different variables. MASTER and MASTER_UUID for bonding, TEAM_MASTER, TEAM_MASTER_UUID and DEVICETYPE for teaming, BRIDGE and BRIDGE_UUID for bridging." /> +<property name="autoconnect-slaves" variable="AUTOCONNECT_SLAVES(+)" format="" values="" default="missing variable means global default" example="" description="Whether slaves of this connection should be auto-connected when this connection is activated." /> +<property name="secondaries" variable="SECONDARY_UUIDS(+)" format="" values="" default="" example="" description="UUID of VPN connections that should be activated together with this connection." /> +<property name="gateway-ping-timeout" variable="GATEWAY_PING_TIMEOUT(+)" format="" values="" default="0" example="GATEWAY_PING_TIMEOUT=5" description="If greater than zero, the IP connectivity will be checked by pinging the gateway and waiting for the specified timeout (in seconds)." /> +<property name="metered" variable="CONNECTION_METERED(+)" format="" values="yes,no,unknown" default="" example="CONNECTION_METERED=yes" description="Whether the device is metered" /> +<property name="lldp" variable="LLDP(+)" format="" values="boolean value or 'rx'" default="missing variable means global default" example="LLDP=no" description="whether LLDP is enabled for the connection" /> +<property name="auth-retries" variable="AUTH_RETRIES(+)" format="" values="" default="0" example="" description="Number of retries for authentication." /> +<property name="mdns" variable="MDNS(+)" format="" values="yes,no,resolve" default="missing variable means global default" example="MDNS=yes" description="Whether or not mDNS is enabled for the connection" /> +<property name="llmnr" variable="LLMNR(+)" format="" values="yes,no,resolve" default="missing variable means global default" example="LLMNR=yes" description="Whether or not LLMNR is enabled for the connection" /> +<property name="wait-device-timeout" variable="DEVTIMEOUT(+)" format="" values="timeout in seconds." default="" example="DEVTIMEOUT=5" description="for initscripts compatibility, this variable must be a whole integer. If necessary, NetworkManager stores also a fractional component for the milliseconds." /> +<property name="mud-url" variable="MUD_URL" format="" values="a valid URL that points to recommended policy for this device" default="" example="https://yourdevice.example.com/model.json" description="MUD_URL to be sent by device (See RFC 8520)." /> +</setting> +<setting name="dcb"> +<property name="app-fcoe-flags" variable="DCB_APP_FCOE_ENABLE, DCB_APP_FCOE_ADVERTISE, DCB_APP_FCOE_WILLING" format="" values="" default="no" example="DCB_APP_FCOE_ENABLE=yes DCB_APP_FCOE_ADVERTISE=yes" description="FCOE flags." /> +<property name="app-fcoe-priority" variable="DCB_APP_FCOE_PRIORITY" format="" values="0 - 7" default="" example="" description="Priority of FCoE frames." /> +<property name="app-fcoe-mode" variable="DCB_APP_FCOE_MODE" format="" values="fabric, vn2vn" default="fabric" example="" description="FCoE controller mode." /> +<property name="app-iscsi-flags" variable="DCB_APP_ISCSI_ENABLE, DCB_APP_ISCSI_ADVERTISE, DCB_APP_ISCSI_WILLING" format="" values="" default="no" example="" description="iSCSI flags." /> +<property name="app-iscsi-priority" variable="DCB_APP_ISCSI_PRIORITY" format="" values="0 - 7" default="" example="" description="Priority of iSCSI frames." /> +<property name="app-fip-flags" variable="DCB_APP_FIP_ENABLE, DCB_APP_FIP_ADVERTISE, DCB_APP_FIP_WILLING" format="" values="" default="no" example="" description="FIP flags." /> +<property name="app-fip-priority" variable="DCB_APP_FIP_PRIORITY" format="" values="0 - 7" default="" example="" description="Priority of FIP frames." /> +<property name="priority-flow-control-flags" variable="DCB_PFC_ENABLE, DCB_PFC_ADVERTISE, DCB_PFC_WILLING" format="" values="" default="no" example="" description="Priority flow control flags." /> +<property name="priority-flow-control" variable="DCB_PFC_UP" format="" values="" default="" example="DCB_PFC_UP=01101110" description="Priority flow control values. String of 8 "0" and "1", where "0". means "do not transmit priority pause", "1" means "transmit pause"." /> +<property name="priority-group-flags" variable="DCB_PG_ENABLE, DCB_PG_ADVERTISE, DCB_PG_WILLING" format="" values="" default="no" example="" description="Priority groups flags." /> +<property name="priority-group-id" variable="DCB_PG_ID" format="" values="" default="" example="DCB_PG_ID=1205f173" description="Priority groups values. String of eight priorities (0 - 7) or "f" (unrestricted)." /> +<property name="priority-group-bandwidth" variable="DCB_PG_PCT" format="" values="" default="" example="DCB_PG_PCT=10,5,10,15,10,10,10,30" description="Priority groups values. Eight bandwidths (in percent), separated with commas." /> +<property name="priority-bandwidth" variable="DCB_PG_UPPCT" format="" values="" default="" example="DCB_PG_UPPCT=7,13,10,10,15,15,10,20" description="Priority values. Eight bandwidths (in percent), separated with commas. The sum of the numbers must be 100." /> +<property name="priority-strict-bandwidth" variable="DCB_PG_STRICT" format="" values="" default="" example="DCB_PG_STRICT=01101110" description="Priority values. String of eight "0" or "1", where "0" means "may not utilize all bandwidth", "1" means "may utilize all bandwidth"." /> +<property name="priority-traffic-class" variable="DCB_PG_UP2TC" format="" values="" default="" example="DCB_PG_UP2TC=01623701" description="Priority values. String of eight traffic class values (0 - 7)." /> +</setting> +<setting name="dummy"> +</setting> +<setting name="ethtool"> +</setting> +<setting name="generic"> +</setting> +<setting name="gsm"> +</setting> +<setting name="hostname"> +<property name="priority" variable="HOSTNAME_PRIORITY(+)" format="" values="" default="missing variable means global value or 100" example="HOSTNAME_PRIORITY=50" description="hostname priority" /> +<property name="from-dhcp" variable="HOSTNAME_FROM_DHCP(+)" format="" values="" default="missing variable means global default or 1" example="HOSTNAME_FROM_DHCP=0,1" description="whether the system hostname can be determined from DHCP" /> +<property name="from-dhcp" variable="HOSTNAME_FROM_DNS_LOOKUP(+)" format="" values="" default="missing variable means global default or 1" example="HOSTNAME_FROM_DNS_LOOKUP=0,1" description="whether the system hostname can be determined from reverse DNS lookup" /> +<property name="only-best-device" variable="HOSTNAME_ONLY_FROM_DEFAULT(+)" format="" values="" default="missing variable means global default or 1" example="HOSTNAME_ONLY_FROM_DEFAULT=0,1" description="whether the hostname can be determined only from devices with the default route" /> +</setting> +<setting name="infiniband"> +<property name="mac-address" variable="HWADDR" format="" values="" default="" example="HWADDR=01:02:03:04:05:06:07:08:09:0A:01:02:03:04:05:06:07:08:09:11" description="IBoIP 20-byte hardware address of the device (in traditional hex-digits-and-colons notation). Note that for initscripts this is the current MAC address of the device as found during ifup. For NetworkManager this is the permanent MAC address. Or in case no permanent MAC address exists, the MAC address initially configured on the device." /> +<property name="mtu" variable="MTU" format="" values="" default="" example="" description="MTU of the interface." /> +<property name="transport-mode" variable="CONNECTED_MODE" format="" values="" default="CONNECTED_MODE=no" example="" description="CONNECTED_MODE=yes for "connected" mode, CONNECTED_MODE=no for "datagram" mode" /> +<property name="p-key" variable="PKEY_ID (and PKEY=yes)" format="" values="" default="PKEY=no" example="PKEY=yes PKEY_ID=2 PHYSDEV=mlx4_ib0 DEVICE=mlx4_ib0.8002" description="InfiniBand P_Key. The value can be a hex number prefixed with "0x" or a decimal number. When PKEY_ID is specified, PHYSDEV and DEVICE also must be specified." /> +<property name="parent" variable="PHYSDEV (PKEY=yes)" format="" values="" default="PKEY=no" example="PHYSDEV=ib0" description="InfiniBand parent device." /> +</setting> +<setting name="ip-tunnel"> +</setting> +<setting name="ipv4"> +<property name="method" variable="BOOTPROTO" format="string" values="none, dhcp (bootp), static, ibft, autoip, shared" default="none" example="" description="Method used for IPv4 protocol configuration." /> +<property name="dns" variable="DNS1, DNS2, ..." format="string" values="" default="" example="DNS1=1.2.3.4 DNS2=10.0.0.254 DNS3=8.8.8.8" description="List of DNS servers. Even if NetworkManager supports many DNS servers, initscripts and resolver only care about the first three, usually." /> +<property name="dns-search" variable="DOMAIN" format="string (space-separated domains)" values="" default="" example="" description="List of DNS search domains." /> +<property name="addresses" variable="IPADDR, PREFIX (NETMASK), IPADDR1, PREFIX1 (NETMASK1), ..." format="" values="" default="" example="IPADDR=10.5.5.23 PREFIX=24 IPADDR1=1.1.1.2 PREFIX1=16" description="List of static IP addresses." /> +<property name="gateway" variable="GATEWAY" format="" values="" default="" example="GATEWAY=10.5.5.1" description="Gateway IP address." /> +<property name="routes" variable="ADDRESS1, NETMASK1, GATEWAY1, METRIC1, OPTIONS1, ..." format="" values="" default="" example="" description="List of static routes. They are not stored in ifcfg-* file, but in route-* file instead." /> +<property name="ignore-auto-routes" variable="PEERROUTES(+)" format="" values="" default="yes" example="" description="PEERROUTES has the opposite meaning as 'ignore-auto-routes' property." /> +<property name="ignore-auto-dns" variable="PEERDNS" format="" values="" default="yes" example="" description="PEERDNS has the opposite meaning as 'ignore-auto-dns' property." /> +<property name="dhcp-send-hostname" variable="DHCP_SEND_HOSTNAME(+)" format="" values="" default="yes" example="" description="Whether DHCP_HOSTNAME should be sent to the DHCP server." /> +<property name="dhcp-hostname" variable="DHCP_HOSTNAME" format="" values="" default="" example="" description="Hostname to send to the DHCP server. When both DHCP_HOSTNAME and DHCP_FQDN are specified only the latter is used." /> +<property name="never-default" variable="DEFROUTE (GATEWAYDEV in /etc/sysconfig/network)" format="" values="" default="yes" example="" description="DEFROUTE=no tells NetworkManager that this connection should not be assigned the default route. DEFROUTE has the opposite meaning as 'never-default' property." /> +<property name="may-fail" variable="IPV4_FAILURE_FATAL(+)" format="" values="" default="no" example="" description="IPV4_FAILURE_FATAL has the opposite meaning as 'may-fail' property." /> +<property name="route-metric" variable="IPV4_ROUTE_METRIC(+)" format="" values="" default="-1" example="" description="IPV4_ROUTE_METRIC is the default IPv4 metric for routes on this connection. If set to -1, a default metric based on the device type is used." /> +<property name="route-table" variable="IPV4_ROUTE_TABLE(+)" format="" values="" default="0" example="" description="IPV4_ROUTE_TABLE enables policy-routing and sets the default routing table." /> +<property name="dns-options" variable="RES_OPTIONS(+)" format="" values="" default="" example="RES_OPTIONS=ndots:2 timeout:3" description="List of DNS options to be added to /etc/resolv.conf" /> +<property name="dns-priority" variable="IPV4_DNS_PRIORITY(+)" format="" values="" default="0" example="IPV4_DNS_PRIORITY=20" description="The priority for DNS servers of this connection. Lower values have higher priority. If zero, the default value will be used (50 for VPNs, 100 for other connections). A negative value prevents DNS from other connections with greater values to be used." /> +<property name="dhcp-client-id" variable="DHCP_CLIENT_ID(+)" format="" values="" default="" example="DHCP_CLIENT_ID=ax-srv-1; DHCP_CLIENT_ID=01:44:44:44:44:44:44" description="A string sent to the DHCP server to identify the local machine. A binary value can be specified using hex notation ('aa:bb:cc')." /> +<property name="dad-timeout" variable="ACD_TIMEOUT(+), ARPING_WAIT" format="" values="" default="missing variable means global default (config override or zero)" example="ACD_TIMEOUT=2000 or ARPING_WAIT=2" description="Timeout (in milliseconds for ACD_TIMEOUT or in seconds for ARPING_WAIT) for address conflict detection before configuring IPv4 addresses. 0 turns off the ACD completely, -1 means default value." /> +<property name="dhcp-timeout" variable="IPV4_DHCP_TIMEOUT(+)" format="" values="" default="" example="IPV4_DHCP_TIMEOUT=10" description="A timeout after which the DHCP transaction fails in case of no response." /> +<property name="dhcp-fqdn" variable="DHCP_FQDN" format="" values="" default="" example="DHCP_FQDN=foo.bar.com" description="FQDN to send to the DHCP server. When both DHCP_HOSTNAME and DHCP_FQDN are specified only the latter is used." /> +<property name="dhcp-vendor-class-identifier" variable="DHCP_VENDOR_CLASS_IDENTIFIER(+)" format="" values="" default="" example="DHCP_VENDOR_CLASS_IDENTIFIER=foo" description="The Vendor Class Identifier DHCP option (60)." /> +</setting> +<setting name="ipv6"> +<property name="method" variable="IPV6INIT, IPV6FORWARDING, IPV6_AUTOCONF, DHCPV6C, IPV6_DISABLED" format="" values="" default="IPV6INIT=yes; IPV6FORWARDING=no; IPV6_AUTOCONF=!IPV6FORWARDING, DHCPV6=no" example="" description="Method used for IPv6 protocol configuration. ignore ~ IPV6INIT=no; auto ~ IPV6_AUTOCONF=yes; dhcp ~ IPV6_AUTOCONF=no and DHCPV6C=yes; disabled ~ IPV6_DISABLED=yes" /> +<property name="dns" variable="DNS1, DNS2, ..." format="string" values="" default="" example="" description="List of DNS servers. NetworkManager uses the variables both for IPv4 and IPv6." /> +<property name="dns-search" variable="IPV6_DOMAIN(+)" format="string (space-separated domains)" values="" default="" example="" description="List of DNS search domains." /> +<property name="addresses" variable="IPV6ADDR, IPV6ADDR_SECONDARIES" format="" values="" default="" example="IPV6ADDR=ab12:9876::1 IPV6ADDR_SECONDARIES="ab12:9876::2 ab12:9876::3"" description="List of static IP addresses." /> +<property name="gateway" variable="IPV6_DEFAULTGW" format="" values="" default="" example="IPV6_DEFAULTGW=abbe::1" description="Gateway IP address." /> +<property name="routes" variable="(none)" format="" values="" default="" example="" description="List of static routes. They are not stored in ifcfg-* file, but in route6-* file instead in the form of command line for 'ip route add'." /> +<property name="ignore-auto-routes" variable="IPV6_PEERROUTES(+)" format="" values="" default="yes" example="" description="IPV6_PEERROUTES has the opposite meaning as 'ignore-auto-routes' property." /> +<property name="ignore-auto-dns" variable="IPV6_PEERDNS(+)" format="" values="" default="yes" example="" description="IPV6_PEERDNS has the opposite meaning as 'ignore-auto-dns' property." /> +<property name="dhcp-hostname" variable="DHCPV6_HOSTNAME" format="" values="" default="" example="" description="Hostname to send the DHCP server." /> +<property name="dhcp-timeout" variable="IPV6_DHCP_TIMEOUT(+)" format="" values="" default="" example="IPV6_DHCP_TIMEOUT=10" description="A timeout after which the DHCP transaction fails in case of no response." /> +<property name="dhcp-hostname-flags" variable="DHCPV6_HOSTNAME_FLAGS" format="" values="" default="" example="DHCPV6_HOSTNAME_FLAGS=5" description="flags for the DHCP hostname property" /> +<property name="never-default" variable="IPV6_DEFROUTE(+), (and IPV6_DEFAULTGW, IPV6_DEFAULTDEV in /etc/sysconfig/network)" format="" values="" default="IPV6_DEFROUTE=yes (when no variable specified)" example="" description="IPV6_DEFROUTE=no tells NetworkManager that this connection should not be assigned the default IPv6 route. IPV6_DEFROUTE has the opposite meaning as 'never-default' property." /> +<property name="may-fail" variable="IPV6_FAILURE_FATAL(+)" format="" values="" default="no" example="" description="IPV6_FAILURE_FATAL has the opposite meaning as 'may-fail' property." /> +<property name="route-metric" variable="IPV6_ROUTE_METRIC(+)" format="" values="" default="-1" example="" description="IPV6_ROUTE_METRIC is the default IPv6 metric for routes on this connection. If set to -1, a default metric based on the device type is used." /> +<property name="route-table" variable="IPV6_ROUTE_TABLE(+)" format="" values="" default="0" example="" description="IPV6_ROUTE_TABLE enables policy-routing and sets the default routing table." /> +<property name="dns-priority" variable="IPV6_DNS_PRIORITY(+)" format="" values="" default="0" example="IPV6_DNS_PRIORITY=20" description="The priority for DNS servers of this connection. Lower values have higher priority. If zero, the default value will be used (50 for VPNs, 100 for other connections). A negative value prevents DNS from other connections with greater values to be used." /> +<property name="dns-options" variable="IPV6_RES_OPTIONS(+)" format="" values="" default="" example="IPV6_RES_OPTIONS=ndots:2 timeout:3" description="List of DNS options to be added to /etc/resolv.conf" /> +<property name="ip6-privacy" variable="IPV6_PRIVACY, IPV6_PRIVACY_PREFER_PUBLIC_IP(+)" format="" values="IPV6_PRIVACY: no, yes (rfc3041 or rfc4941); IPV6_PRIVACY_PREFER_PUBLIC_IP: yes, no" default="no" example="IPV6_PRIVACY=rfc3041 IPV6_PRIVACY_PREFER_PUBLIC_IP=yes" description="Configure IPv6 Privacy Extensions for SLAAC (RFC4941)." /> +<property name="addr-gen-mode" variable="IPV6_ADDR_GEN_MODE" format="" values="IPV6_ADDR_GEN_MODE: eui64, stable-privacy" default="eui64" example="IPV6_ADDR_GEN_MODE=stable-privacy" description="Configure IPv6 Stable Privacy addressing for SLAAC (RFC7217)." /> +<property name="token" variable="IPV6_TOKEN" format="" values="" default="" example="IPV6_TOKEN=::53" description="The IPv6 tokenized interface identifier token" /> +<property name="dhcp-timeout" variable="IPV6_RA_TIMEOUT(+)" format="" values="" default="" example="IPV6_RA_TIMEOUT=10" description="A timeout for waiting Router Advertisements in seconds." /> +<property name="dhcp-duid" variable="DHCPV6_DUID(+)" format="" values="" default="" example="DHCPV6_DUID=LL; DHCPV6_DUID=0301deadbeef0001; DHCPV6_DUID=03:01:de:ad:be:ef:00:01" description="A string sent to the DHCPv6 server to identify the local machine. Apart from the special values "lease", "stable-llt", "stable-ll", "stable-uuid", "llt" and "ll" a binary value in hex format is expected. An hex string where each octet is separated by a colon is also accepted." /> +</setting> +<setting name="macsec"> +</setting> +<setting name="macvlan"> +</setting> +<setting name="match"> +<property name="path" variable="MATCH_PATH" format="" values="" default="" example="MATCH_PATH="pci-0000:01:00.0 pci-0000:0c:00.0"" description="space-separated list of paths to match against the udev property ID_PATHS of devices" /> +</setting> +<setting name="802-11-olpc-mesh"> +</setting> +<setting name="ovs-bridge"> +</setting> +<setting name="ovs-dpdk"> +</setting> +<setting name="ovs-external-ids"> +</setting> +<setting name="ovs-interface"> +</setting> +<setting name="ovs-patch"> +</setting> +<setting name="ovs-port"> +</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"> +</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"> +<property name="config" variable="TEAM_PORT_CONFIG" format="" values="" default="" example="" description="Team port configuration in JSON. See man teamd.conf for details." /> +</setting> +<setting name="team"> +<property name="config" variable="TEAM_CONFIG" format="" values="" default="" example="" description="Team configuration in JSON. See man teamd.conf for details." /> +</setting> +<setting name="tun"> +</setting> +<setting name="user"> +<property name="data" variable="NM_USER_*" format="" values="" default="" example="NM_USER_FOO__BAR=something" description="each key/value pair is stored as a separate variable with name composed by concatenating NM_USER_ with the encoded key. The key is encoded by substituting lowercase letters with uppercase and prepending uppercase letters with an underscore. A dot is encoded as a double underscore. Remaining characters are encoded as underscore followed by a 3 digit octal representation of the character." /> +</setting> +<setting name="veth"> +</setting> +<setting name="vlan"> +<property name="parent" variable="DEVICE or PHYSDEV" format="" values="" default="" example="" description="Parent interface of the VLAN." /> +<property name="id" variable="VLAN_ID, DEVICE." format="" values="" default="" example="" description="VLAN identifier. If VLAN_ID is not set, it is attempted to be detected from the suffix of DEVICE=. Note that older versions of NetworkManager had a bug where they would prefer the detected ID from the DEVICE over VLAN_ID." /> +<property name="flags" variable="GVRP, MVRP, VLAN_FLAGS" format="" values=""yes or "no" for GVRP and MVRP; "LOOSE_BINDING" and "NO_REORDER_HDR" for VLAN_FLAGS" default="" example="" description="VLAN flags." /> +<property name="ingress-priority-map" variable="VLAN_INGRESS_PRIORITY_MAP" format="" values="" default="" example="VLAN_INGRESS_PRIORITY_MAP=4:2,3:5" description="Ingress priority mapping." /> +<property name="egress-priority-map" variable="VLAN_EGRESS_PRIORITY_MAP" format="" values="" default="" example="VLAN_EGRESS_PRIORITY_MAP=5:4,4:1,3:7" description="Egress priority mapping." /> +<property name="interface-name" variable="PHYSDEV and VLAN_ID, or DEVICE" format="" values="" default="" example="PHYSDEV=eth0, VLAN_ID=12; or DEVICE=eth0.12" description="VLAN interface name. If all variables are set, parent device from PHYSDEV takes precedence over DEVICE, but VLAN id from DEVICE takes precedence over VLAN_ID." /> +</setting> +<setting name="vpn"> +</setting> +<setting name="vrf"> +</setting> +<setting name="vxlan"> +</setting> +<setting name="wifi-p2p"> +</setting> +<setting name="wimax"> +</setting> +<setting name="802-3-ethernet"> +<property name="port" variable="(none)" format="" values="" default="" example="" description="The property is not saved by the plugin." /> +<property name="speed" variable="ETHTOOL_OPTS" format="" values="" default="" example="" description="Fixed speed for the ethernet link. It is added as "speed" parameter in the ETHTOOL_OPTS variable." /> +<property name="duplex" variable="ETHTOOL_OPTS" format="" values="" default="" example="" description="Fixed duplex mode for the ethernet link. It is added as "duplex" parameter in the ETHOOL_OPTS variable." /> +<property name="auto-negotiate" variable="ETHTOOL_OPTS" format="" values="" default="" example="" description="Whether link speed and duplex autonegotiation is enabled. It is not saved only if disabled and no values are provided for the "speed" and "duplex" parameters (skips link configuration)." /> +<property name="mac-address" variable="HWADDR" format="" values="" default="" example="" description="Hardware address of the device in traditional hex-digits-and-colons notation (e.g. 00:22:68:14:5A:05). Note that for initscripts this is the current MAC address of the device as found during ifup. For NetworkManager this is the permanent MAC address. Or in case no permanent MAC address exists, the MAC address initially configured on the device." /> +<property name="cloned-mac-address" variable="MACADDR" format="" values="" default="" example="" description="Cloned (spoofed) MAC address in traditional hex-digits-and-colons notation (e.g. 00:22:68:14:5A:99)." /> +<property name="generate-mac-address-mask" variable="GENERATE_MAC_ADDRESS_MASK(+)" format="" values="" default="" example="" description="the MAC address mask for generating randomized and stable cloned-mac-address." /> +<property name="mac-address-blacklist" variable="HWADDR_BLACKLIST(+)" format="" values="" default="" example="HWADDR_BLACKLIST="00:22:68:11:69:08 00:11:22:11:44:55"" description="It denies usage of the connection for any device whose address is listed." /> +<property name="mtu" variable="MTU" format="" values="" default="" example="" description="MTU of the interface." /> +<property name="s390-subchannels" variable="SUBCHANNELS" format="" values="" default="" example="SUBCHANNELS=0.0.b00a,0.0.b00b,0.0.b00c" description="Subchannels for IBM S390 hosts." /> +<property name="s390-nettype" variable="NETTYPE" format="" values=""qeth", "lcs" or "ctc"" default="" example="NETTYPE=qeth" description="Network type of the S390 host." /> +<property name="s390-options" variable="OPTIONS and PORTNAME, CTCPROTO," format="" values="" default="" example="" description="S390 device options. All options go to OPTIONS, except for "portname" and "ctcprot" that have their own variables." /> +<property name="wake-on-lan" variable="ETHTOOL_OPTS, ETHTOOL_WAKE_ON_LAN" format="" values="" default="" example="" description="Wake on Lan mode for ethernet. The setting "ignore" is expressed with "ETHTOOL_WAKE_ON_LAN=ignore". Otherwise, the "ETHTOOL_OPTS" variable is set with the value "wol" and several of the characters "p|u|m|b|a|g|s|f|d" as explained in the ethtool manual page." /> +<property name="wake-on-lan-password" variable="ETHTOOL_OPTS" format="" values="" default="" example="ETHTOOL_OPTS="wol gs sopass 00:11:22:33:44:55"" description="Password for secure-on based Wake-on-Lan. It is added as "sopass" parameter in the ETHTOOL_OPTS variable." /> +<property name="accept-all-mac-addresses" variable="ACCEPT_ALL_MAC_ADDRESSES" format="" values="" default="" example="" description="Enforce the interface to accept all the packets." /> +</setting> +<setting name="wireguard"> +</setting> +<setting name="802-11-wireless-security"> +<property name="key-mgmt" variable="KEY_MGMT(+)" format="" values="none, ieee8021x, owe, wpa-psk, sae, wpa-eap, wpa-eap-suite-b-192" default="" example="" description="Key management method." /> +<property name="wep-tx-keyidx" variable="DEFAULTKEY" format="" values="1, 2, 3, 4" default="1" example="" description="Index of active WEP key." /> +<property name="auth-alg" variable="SECURITYMODE(+)" format="" values="restricted, open, leap" default="" example="" description="Authentication algorithm for WEP." /> +<property name="proto" variable="WPA_ALLOW_WPA(+), WPA_ALLOW_WPA2(+)" format="" values="yes, no" default="no" example="" description="Allowed WPA protocols, WPA and WPA2 (RSN)." /> +<property name="pairwise" variable="CIPHER_PAIRWISE(+)" format="" values="CCMP, TKIP" default="" example="" description="Restrict pairwise encryption algorithms, specified as a space separated list." /> +<property name="group" variable="CIPHER_GROUP(+)" format="" values="CCMP, TKIP, WEP40, WEP104" default="" example="" description="Restrict group/broadcast encryption algorithms, specified as a space separated list." /> +<property name="pmf" variable="PMF(+)" format="" values="default, disable, optional, required" default="" example="PMF=required" description="Enables or disables PMF (802.11w)" /> +<property name="leap-username" variable="IEEE_8021X_IDENTITY(+)" format="" values="" default="" example="" description="Login name for LEAP." /> +<property name="wep-key0" variable="KEY1, KEY_PASSPHRASE1(+)" format="" values="" default="" example="" description="The first WEP key (used in most networks). See also DEFAULTKEY for key index." /> +<property name="wep-key1" variable="KEY2, KEY_PASSPHRASE2(+)" format="" values="" default="" example="" description="WEP key with index 1. See also DEFAULTKEY for key index." /> +<property name="wep-key2" variable="KEY3, KEY_PASSPHRASE3(+)" format="" values="" default="" example="" description="WEP key with index 2. See also DEFAULTKEY for key index." /> +<property name="wep-key3" variable="KEY4, KEY_PASSPHRASE4(+)" format="" values="" default="" example="" description="WEP key with index 3. See also DEFAULTKEY for key index." /> +<property name="wep-key-flags" variable="WEP_KEY_FLAGS(+)" format="NMSettingSecretFlags" values="" default="" example="" description="Password flags for KEY<i>, KEY_PASSPHRASE<i> password." /> +<property name="psk" variable="WPA_PSK" format="" values="" default="" example="" description="Pre-Shared-Key for WPA networks." /> +<property name="psk-flags" variable="WPA_PSK_FLAGS(+)" format="NMSettingSecretFlags" values="" default="" example="WPA_PSK_FLAGS=user" description="Password flags for WPA_PSK_FLAGS." /> +<property name="leap-password" variable="IEEE_8021X_PASSWORD(+)" format="" values="" default="" example="" description="Password for LEAP. It can also go to "key-" lookaside file, or it can be owned by a secret agent." /> +<property name="leap-password-flags" variable="IEEE_8021X_PASSWORD_FLAGS(+)" format="NMSettingSecretFlags" values="" default="" example="" description="Password flags for IEEE_8021X_PASSWORD_FLAGS." /> +<property name="wep-key-type" variable="KEY<i> or KEY_PASSPHRASE<i>(+); KEY_TYPE(+)" format="" values="" default="" example="KEY1=s:ahoj, KEY1=0a1c45bc02, KEY_PASSPHRASE1=mysupersecretkey" description="KEY is used for "key" type (10 or 26 hexadecimal characters, or 5 or 13 character string prefixed with "s:"). KEY_PASSPHRASE is used for WEP passphrases. KEY_TYPE specifies the key type and can be either 'key' or 'passphrase'. KEY_TYPE is redundant and can be omitted." /> +<property name="wps-method" variable="WPS_METHOD" format="" values="" default="" example="WPS_METHOD=disabled, WPS_METHOD="pin pbc"" description="Used to control the WPS methods to be used Valid values are "default", "auto", "disabled", "pin" and "pbc". If omitted, whatver the AP announces is used." /> +<property name="fils" variable="FILS(+)" format="" values="default, disable, optional, required" default="" example="FILS=required" description="Enables or disables FILS (802.11ai)" /> +</setting> +<setting name="802-11-wireless"> +<property name="ssid" variable="ESSID" format="" values="" default="" example="ESSID="Quick Net"" description="SSID of Wi-Fi network." /> +<property name="mode" variable="MODE" format="" values="Ad-Hoc, Managed (Auto) [case insensitive]" default="" example="" description="Wi-Fi network mode." /> +<property name="band" variable="BAND(+)" format="" values="a, bg" default="" example="BAND=bg" description="BAND alone is honored, but CHANNEL overrides BAND since it implies a band." /> +<property name="channel" variable="CHANNEL" format="" values="" default="" example="CHANNEL=6" description="Channel used for the Wi-Fi communication. Channels greater than 14 mean "a" band, otherwise the band is "bg"." /> +<property name="bssid" variable="BSSID(+)" format="" values="" default="" example="BSSID=00:1E:BD:64:83:21" description="Restricts association only to a single AP." /> +<property name="rate" variable="(none)" format="" values="" default="" example="" description="This property is not handled by ifcfg-rh plugin." /> +<property name="tx-power" variable="(none)" format="" values="" default="" example="" description="This property is not handled by ifcfg-rh plugin." /> +<property name="mac-address" variable="HWADDR" format="" values="" default="" example="" description="Hardware address of the device in traditional hex-digits-and-colons notation (e.g. 00:22:68:14:5A:05). Note that for initscripts this is the current MAC address of the device as found during ifup. For NetworkManager this is the permanent MAC address. Or in case no permanent MAC address exists, the MAC address initially configured on the device." /> +<property name="cloned-mac-address" variable="MACADDR" format="" values="" default="" example="" description="Cloned (spoofed) MAC address in traditional hex-digits-and-colons notation (e.g. 00:22:68:14:5A:99)." /> +<property name="generate-mac-address-mask" variable="GENERATE_MAC_ADDRESS_MASK(+)" format="" values="" default="" example="" description="the MAC address mask for generating randomized and stable cloned-mac-address." /> +<property name="mac-address-blacklist" variable="HWADDR_BLACKLIST(+)" format="" values="" default="" example="" description="It denies usage of the connection for any device whose address is listed." /> +<property name="seen-bssids" variable="(none)" format="" values="" default="" example="" description="This property is not handled by ifcfg-rh plugin." /> +<property name="mtu" variable="MTU" format="" values="" default="" example="" description="MTU of the wireless interface." /> +<property name="hidden" variable="SSID_HIDDEN(+)" format="" values="" default="" example="" description="Whether the network hides the SSID." /> +<property name="powersave" variable="POWERSAVE(+)" format="" values="default, ignore, enable, disable" default="" example="POWERSAVE=enable" description="Enables or disables Wi-Fi power saving." /> +<property name="mac-address-randomization" variable="MAC_ADDRESS_RANDOMIZATION(+)" format="" values="default, never, always" default="" example="MAC_ADDRESS_RANDOMIZATION=always" description="Enables or disables Wi-Fi MAC address randomization." /> +<property name="security" variable="(none)" format="" values="" default="" example="" description="This property is deprecated and not handled by ifcfg-rh-plugin." /> +<property name="ap-isolation" variable="AP_ISOLATION(+)" format="" values=""yes", "no"" default="missing variable means global default" example="" description="Whether AP isolation is enabled" /> +</setting> +<setting name="wpan"> +</setting> +</nm-setting-docs> \ No newline at end of file diff --git a/src/libnm-client-impl/nm-property-infos-keyfile.xml b/src/libnm-client-impl/nm-property-infos-keyfile.xml new file mode 100644 index 00000000..486d61e9 --- /dev/null +++ b/src/libnm-client-impl/nm-property-infos-keyfile.xml @@ -0,0 +1,126 @@ +<nm-setting-docs> +<setting name="6lowpan"> +</setting> +<setting name="802-1x"> +</setting> +<setting name="adsl"> +</setting> +<setting name="bluetooth"> +</setting> +<setting name="bond"> +</setting> +<setting name="bridge-port"> +</setting> +<setting name="bridge"> +<property name="mac-address" variable="mac-address" format="usual hex-digits-and-colons notation" values="" default="" example="mac-address=00:22:68:12:79:A2 mac-address=0;34;104;18;121;162;" description="MAC address in traditional hex-digits-and-colons notation, or semicolon separated list of 6 decimal bytes (obsolete)" /> +</setting> +<setting name="cdma"> +</setting> +<setting name="connection"> +</setting> +<setting name="dcb"> +</setting> +<setting name="dummy"> +</setting> +<setting name="ethtool"> +</setting> +<setting name="generic"> +</setting> +<setting name="gsm"> +</setting> +<setting name="hostname"> +</setting> +<setting name="infiniband"> +<property name="mac-address" variable="mac-address" format="usual hex-digits-and-colons notation" values="" default="" example="mac-address= 80:00:00:6d:fe:80:00:00:00:00:00:00:00:02:55:00:70:33:cf:01" description="MAC address in traditional hex-digits-and-colons notation, or or semicolon separated list of 20 decimal bytes (obsolete)" /> +</setting> +<setting name="ip-tunnel"> +</setting> +<setting name="ipv4"> +<property name="dns" variable="dns" format="list of DNS IP addresses" values="" default="" example="dns=1.2.3.4;8.8.8.8;8.8.4.4;" description="List of DNS servers." /> +<property name="addresses" variable="address1, address2, ..." format="address/plen" values="" default="" example="address1=192.168.100.100/24 address2=10.1.1.5/24" description="List of static IP addresses." /> +<property name="gateway" variable="gateway" format="string" values="" default="" example="gateway=192.168.100.1" description="Gateway IP addresses as a string." /> +<property name="routes" variable="route1, route2, ..." format="route/plen[,gateway,metric]" values="" default="" example="route1=8.8.8.0/24,10.1.1.1,77 route2=7.7.0.0/16" description="List of IP routes." /> +</setting> +<setting name="ipv6"> +<property name="dns" variable="dns" format="list of DNS IP addresses" values="" default="" example="dns=2001:4860:4860::8888;2001:4860:4860::8844;" description="List of DNS servers." /> +<property name="addresses" variable="address1, address2, ..." format="address/plen" values="" default="" example="address1=abbe::cafe/96 address2=2001::1234" description="List of static IP addresses." /> +<property name="gateway" variable="gateway" format="string" values="" default="" example="gateway=abbe::1" description="Gateway IP addresses as a string." /> +<property name="routes" variable="route1, route2, ..." format="route/plen[,gateway,metric]" values="" default="" example="route1=2001:4860:4860::/64,2620:52:0:2219:222:68ff:fe11:5403" description="List of IP routes." /> +</setting> +<setting name="macsec"> +</setting> +<setting name="macvlan"> +</setting> +<setting name="match"> +</setting> +<setting name="802-11-olpc-mesh"> +</setting> +<setting name="ovs-bridge"> +</setting> +<setting name="ovs-dpdk"> +</setting> +<setting name="ovs-external-ids"> +</setting> +<setting name="ovs-interface"> +</setting> +<setting name="ovs-patch"> +</setting> +<setting name="ovs-port"> +</setting> +<setting name="ppp"> +</setting> +<setting name="pppoe"> +</setting> +<setting name="proxy"> +</setting> +<setting name="serial"> +<property name="parity" variable="parity" format="'e', 'o', or 'n'" values="" default="" example="parity=n" description="The connection parity; even, odd, or none. Note that older versions of NetworkManager stored this as an integer: 69 ('E') for even, 111 ('o') for odd, or 110 ('n') for none." /> +</setting> +<setting name="sriov"> +</setting> +<setting name="tc"> +</setting> +<setting name="team-port"> +</setting> +<setting name="team"> +</setting> +<setting name="tun"> +</setting> +<setting name="user"> +</setting> +<setting name="veth"> +</setting> +<setting name="vlan"> +</setting> +<setting name="vpn"> +<property name="data" variable="separate variables named after keys of the dictionary" format="" values="" default="" example="remote=ovpn.corp.com cipher=AES-256-CBC username=joe" description="The keys of the data dictionary are used as variable names directly under [vpn] section." /> +<property name="secrets" variable="separate variables named after keys of the dictionary" format="" values="" default="" example="password=Popocatepetl" description="The keys of the secrets dictionary are used as variable names directly under [vpn-secrets] section." /> +</setting> +<setting name="vrf"> +</setting> +<setting name="vxlan"> +</setting> +<setting name="wifi-p2p"> +<property name="peer" variable="peer" format="usual hex-digits-and-colons notation" values="" default="" example="" description="MAC address in traditional hex-digits-and-colons notation (e.g. 00:22:68:12:79:A2), or semicolon separated list of 6 bytes (obsolete) (e.g. 0;34;104;18;121;162)." /> +</setting> +<setting name="wimax"> +</setting> +<setting name="802-3-ethernet"> +<property name="mac-address" variable="mac-address" format="usual hex-digits-and-colons notation" values="" default="" example="" description="MAC address in traditional hex-digits-and-colons notation (e.g. 00:22:68:12:79:A2), or semicolon separated list of 6 bytes (obsolete) (e.g. 0;34;104;18;121;162)" /> +<property name="cloned-mac-address" variable="cloned-mac-address" format="usual hex-digits-and-colons notation" values="" default="" example="" description="Cloned MAC address in traditional hex-digits-and-colons notation (e.g. 00:22:68:12:79:B2), or semicolon separated list of 6 bytes (obsolete) (e.g. 0;34;104;18;121;178)." /> +<property name="mac-address-blacklist" variable="mac-address-blacklist" format="list of MACs (separated with semicolons)" values="" default="" example="mac-address-blacklist= 00:22:68:12:79:A6;00:22:68:12:79:78" description="MAC address blacklist." /> +</setting> +<setting name="wireguard"> +</setting> +<setting name="802-11-wireless-security"> +</setting> +<setting name="802-11-wireless"> +<property name="ssid" variable="ssid" format="string (or decimal-byte list - obsolete)" values="" default="" example="ssid=Quick Net" description="SSID of Wi-Fi network." /> +<property name="mac-address" variable="mac-address" format="usual hex-digits-and-colons notation" values="" default="" example="" description="MAC address in traditional hex-digits-and-colons notation (e.g. 00:22:68:12:79:A2), or semicolon separated list of 6 bytes (obsolete) (e.g. 0;34;104;18;121;162)." /> +<property name="cloned-mac-address" variable="cloned-mac-address" format="usual hex-digits-and-colons notation" values="" default="" example="" description="Cloned MAC address in traditional hex-digits-and-colons notation (e.g. 00:22:68:12:79:B2), or semicolon separated list of 6 bytes (obsolete) (e.g. 0;34;104;18;121;178)." /> +<property name="mac-address-blacklist" variable="mac-address-blacklist" format="list of MACs (separated with semicolons)" values="" default="" example="mac-address-blacklist= 00:22:68:12:79:A6;00:22:68:12:79:78" description="MAC address blacklist." /> +</setting> +<setting name="wpan"> +<property name="mac-address" variable="mac-address" format="usual hex-digits-and-colons notation" values="" default="" example="" description="MAC address in hex-digits-and-colons notation (e.g. 76:d8:9b:87:66:60:84:ee)." /> +</setting> +</nm-setting-docs> \ No newline at end of file diff --git a/src/libnm-client-impl/nm-property-infos-nmcli.xml b/src/libnm-client-impl/nm-property-infos-nmcli.xml new file mode 100644 index 00000000..d6aa8a80 --- /dev/null +++ b/src/libnm-client-impl/nm-property-infos-nmcli.xml @@ -0,0 +1,107 @@ +<nm-setting-docs> +<setting name="6lowpan"> +</setting> +<setting name="802-1x"> +</setting> +<setting name="adsl"> +</setting> +<setting name="bluetooth"> +</setting> +<setting name="bond"> +</setting> +<setting name="bridge-port"> +</setting> +<setting name="bridge"> +</setting> +<setting name="cdma"> +</setting> +<setting name="connection"> +</setting> +<setting name="dcb"> +</setting> +<setting name="dummy"> +</setting> +<setting name="ethtool"> +</setting> +<setting name="generic"> +</setting> +<setting name="gsm"> +</setting> +<setting name="hostname"> +</setting> +<setting name="infiniband"> +</setting> +<setting name="ip-tunnel"> +</setting> +<setting name="ipv4"> +<property name="addresses" variable="addresses" format="a comma separated list of addresses" values="" default="" example="" 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="routes" variable="routes" format="a comma separated list of routes" values="" default="" example="" 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"." /> +</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." /> +</setting> +<setting name="macsec"> +</setting> +<setting name="macvlan"> +</setting> +<setting name="match"> +</setting> +<setting name="802-11-olpc-mesh"> +</setting> +<setting name="ovs-bridge"> +</setting> +<setting name="ovs-dpdk"> +</setting> +<setting name="ovs-external-ids"> +</setting> +<setting name="ovs-interface"> +</setting> +<setting name="ovs-patch"> +</setting> +<setting name="ovs-port"> +</setting> +<setting name="ppp"> +</setting> +<setting name="pppoe"> +</setting> +<setting name="proxy"> +</setting> +<setting name="serial"> +</setting> +<setting name="sriov"> +</setting> +<setting name="tc"> +</setting> +<setting name="team-port"> +</setting> +<setting name="team"> +</setting> +<setting name="tun"> +</setting> +<setting name="user"> +</setting> +<setting name="veth"> +</setting> +<setting name="vlan"> +</setting> +<setting name="vpn"> +</setting> +<setting name="vrf"> +</setting> +<setting name="vxlan"> +</setting> +<setting name="wifi-p2p"> +</setting> +<setting name="wimax"> +</setting> +<setting name="802-3-ethernet"> +</setting> +<setting name="wireguard"> +</setting> +<setting name="802-11-wireless-security"> +</setting> +<setting name="802-11-wireless"> +</setting> +<setting name="wpan"> +</setting> +</nm-setting-docs> \ No newline at end of file diff --git a/src/libnm-client-impl/nm-remote-connection-private.h b/src/libnm-client-impl/nm-remote-connection-private.h new file mode 100644 index 00000000..3ca934bf --- /dev/null +++ b/src/libnm-client-impl/nm-remote-connection-private.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef __NM_REMOTE_CONNECTION_PRIVATE_H__ +#define __NM_REMOTE_CONNECTION_PRIVATE_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#define NM_REMOTE_CONNECTION_INIT_RESULT "init-result" + +typedef enum { + NM_REMOTE_CONNECTION_INIT_RESULT_UNKNOWN = 0, + NM_REMOTE_CONNECTION_INIT_RESULT_SUCCESS, + NM_REMOTE_CONNECTION_INIT_RESULT_ERROR, + NM_REMOTE_CONNECTION_INIT_RESULT_INVISIBLE, +} NMRemoteConnectionInitResult; + +#endif /* __NM_REMOTE_CONNECTION_PRIVATE__ */ diff --git a/src/libnm-client-impl/nm-remote-connection.c b/src/libnm-client-impl/nm-remote-connection.c new file mode 100644 index 00000000..8e36cf6d --- /dev/null +++ b/src/libnm-client-impl/nm-remote-connection.c @@ -0,0 +1,839 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2011 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-remote-connection.h" + +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "nm-dbus-interface.h" +#include "nm-utils.h" +#include "nm-setting-connection.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-remote-connection-private.h" +#include "nm-object-private.h" +#include "nm-dbus-helpers.h" + +/** + * SECTION:nm-remote-connection + * @short_description: A connection managed by NetworkManager server + * + * A #NMRemoteConnection represents a connection that is exported via + * NetworkManager D-Bus interface. + **/ + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMRemoteConnection, + PROP_UNSAVED, + PROP_FLAGS, + PROP_FILENAME, + PROP_VISIBLE, ); + +typedef struct { + GCancellable *get_settings_cancellable; + + char * filename; + guint32 flags; + bool unsaved; + + bool visible : 1; + bool is_initialized : 1; +} NMRemoteConnectionPrivate; + +struct _NMRemoteConnection { + NMObject parent; + NMRemoteConnectionPrivate _priv; +}; + +struct _NMRemoteConnectionClass { + NMObjectClass parent_class; +}; + +static void nm_remote_connection_connection_iface_init(NMConnectionInterface *iface); + +G_DEFINE_TYPE_WITH_CODE(NMRemoteConnection, + nm_remote_connection, + NM_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(NM_TYPE_CONNECTION, + nm_remote_connection_connection_iface_init);) + +#define NM_REMOTE_CONNECTION_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMRemoteConnection, NM_IS_REMOTE_CONNECTION, NMObject) + +/*****************************************************************************/ + +/** + * nm_remote_connection_update2: + * @connection: the #NMRemoteConnection + * @settings: (allow-none): optional connection to update the settings. + * @flags: update-flags + * @args: (allow-none): optional arguments. + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the commit operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously calls the Update2() D-Bus method. + * + * Since: 1.12 + **/ +void +nm_remote_connection_update2(NMRemoteConnection * connection, + GVariant * settings, + NMSettingsUpdate2Flags flags, + GVariant * args, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_REMOTE_CONNECTION(connection)); + g_return_if_fail(!settings || g_variant_is_of_type(settings, NM_VARIANT_TYPE_CONNECTION)); + g_return_if_fail(!args || g_variant_is_of_type(args, G_VARIANT_TYPE("a{sv}"))); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + if (!settings) + settings = nm_g_variant_singleton_aLsaLsvII(); + if (!args) + args = nm_g_variant_singleton_aLsvI(); + + _nm_client_dbus_call(_nm_object_get_client(connection), + connection, + nm_remote_connection_update2, + cancellable, + callback, + user_data, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Update2", + g_variant_new("(@a{sa{sv}}u@a{sv})", settings, (guint32) flags, args), + G_VARIANT_TYPE("(a{sv})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); +} + +/** + * nm_remote_connection_update2_finish: + * @connection: the #NMRemoteConnection + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_remote_connection_commit_changes_async(). + * + * Returns: (transfer full): on success, a #GVariant of type "a{sv}" with the result. On failure, + * %NULL. + **/ +GVariant * +nm_remote_connection_update2_finish(NMRemoteConnection *connection, + GAsyncResult * result, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + GVariant * v_result; + + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), NULL); + g_return_val_if_fail(nm_g_task_is_valid(result, connection, nm_remote_connection_update2), + NULL); + + ret = g_task_propagate_pointer(G_TASK(result), error); + if (!ret) + return NULL; + + g_variant_get(ret, "(@a{sv})", &v_result); + + return v_result; +} + +/*****************************************************************************/ + +/** + * nm_remote_connection_commit_changes: + * @connection: the #NMRemoteConnection + * @save_to_disk: whether to persist the changes to disk + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Send any local changes to the settings and properties of @connection to + * NetworkManager. If @save_to_disk is %TRUE, the updated connection will be saved to + * disk; if %FALSE, then only the in-memory representation will be changed. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + * + * Deprecated: 1.22: Use nm_remote_connection_commit_changes_async() or GDBusConnection. + **/ +gboolean +nm_remote_connection_commit_changes(NMRemoteConnection *connection, + gboolean save_to_disk, + GCancellable * cancellable, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + + ret = _nm_client_dbus_call_sync( + _nm_object_get_client(connection), + cancellable, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Update2", + g_variant_new("(@a{sa{sv}}u@a{sv})", + nm_connection_to_dbus(NM_CONNECTION(connection), NM_CONNECTION_SERIALIZE_ALL), + (guint32) (save_to_disk ? NM_SETTINGS_UPDATE2_FLAG_TO_DISK + : NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY), + nm_g_variant_singleton_aLsvI()), + G_VARIANT_TYPE("(a{sv})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) + return FALSE; + + return TRUE; +} + +/** + * nm_remote_connection_commit_changes_async: + * @connection: the #NMRemoteConnection + * @save_to_disk: whether to save the changes to persistent storage + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the commit operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously sends any local changes to the settings and properties of + * @connection to NetworkManager. If @save is %TRUE, the updated connection will + * be saved to disk; if %FALSE, then only the in-memory representation will be + * changed. + **/ +void +nm_remote_connection_commit_changes_async(NMRemoteConnection *connection, + gboolean save_to_disk, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_REMOTE_CONNECTION(connection)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + nm_remote_connection_update2( + connection, + nm_connection_to_dbus(NM_CONNECTION(connection), NM_CONNECTION_SERIALIZE_ALL), + save_to_disk ? NM_SETTINGS_UPDATE2_FLAG_TO_DISK : NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY, + NULL, + cancellable, + callback, + user_data); +} + +/** + * nm_remote_connection_commit_changes_finish: + * @connection: the #NMRemoteConnection + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_remote_connection_commit_changes_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + **/ +gboolean +nm_remote_connection_commit_changes_finish(NMRemoteConnection *connection, + GAsyncResult * result, + GError ** error) +{ + gs_unref_variant GVariant *v_result = NULL; + + v_result = nm_remote_connection_update2_finish(connection, result, error); + return !!v_result; +} + +/*****************************************************************************/ + +/** + * nm_remote_connection_save: + * @connection: the #NMRemoteConnection + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Saves the connection to disk if the connection has changes that have not yet + * been written to disk, or if the connection has never been saved. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + * + * Deprecated: 1.22: Use nm_remote_connection_save_async() or GDBusConnection. + **/ +gboolean +nm_remote_connection_save(NMRemoteConnection *connection, GCancellable *cancellable, GError **error) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + + return _nm_client_dbus_call_sync_void(_nm_object_get_client(connection), + cancellable, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Save", + g_variant_new("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_remote_connection_save_async: + * @connection: the #NMRemoteConnection + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the save operation completes + * @user_data: caller-specific data passed to @callback + * + * Saves the connection to disk if the connection has changes that have not yet + * been written to disk, or if the connection has never been saved. + **/ +void +nm_remote_connection_save_async(NMRemoteConnection *connection, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_REMOTE_CONNECTION(connection)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(_nm_object_get_client(connection), + connection, + nm_remote_connection_save_async, + cancellable, + callback, + user_data, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Save", + g_variant_new("()"), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_remote_connection_save_finish: + * @connection: the #NMRemoteConnection + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_remote_connection_save_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + **/ +gboolean +nm_remote_connection_save_finish(NMRemoteConnection *connection, + GAsyncResult * result, + GError ** error) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, connection, nm_remote_connection_save_async), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +/** + * nm_remote_connection_delete: + * @connection: the #NMRemoteConnection + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Deletes the connection. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + * + * Deprecated: 1.22: Use nm_remote_connection_delete_async() or GDBusConnection. + **/ +gboolean +nm_remote_connection_delete(NMRemoteConnection *connection, + GCancellable * cancellable, + GError ** error) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + + return _nm_client_dbus_call_sync_void(_nm_object_get_client(connection), + cancellable, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Delete", + g_variant_new("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_remote_connection_delete_async: + * @connection: the #NMRemoteConnection + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the delete operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously deletes the connection. + **/ +void +nm_remote_connection_delete_async(NMRemoteConnection *connection, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_REMOTE_CONNECTION(connection)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(_nm_object_get_client(connection), + connection, + nm_remote_connection_delete_async, + cancellable, + callback, + user_data, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Delete", + g_variant_new("()"), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_remote_connection_delete_finish: + * @connection: the #NMRemoteConnection + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_remote_connection_delete_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + **/ +gboolean +nm_remote_connection_delete_finish(NMRemoteConnection *connection, + GAsyncResult * result, + GError ** error) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, connection, nm_remote_connection_delete_async), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/** + * nm_remote_connection_get_secrets: + * @connection: the #NMRemoteConnection + * @setting_name: the #NMSetting object name to get secrets for + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Request the connection's secrets. Note that this is a blocking D-Bus call, + * not a simple property accessor. + * + * Returns: (transfer full): a #GVariant of type %NM_VARIANT_TYPE_CONNECTION containing + * @connection's secrets, or %NULL on error. + * + * Deprecated: 1.22: Use nm_remote_connection_get_secrets_async() or GDBusConnection. + **/ +GVariant * +nm_remote_connection_get_secrets(NMRemoteConnection *connection, + const char * setting_name, + GCancellable * cancellable, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + GVariant * secrets; + + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), NULL); + g_return_val_if_fail(setting_name, NULL); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), NULL); + + ret = _nm_client_dbus_call_sync(_nm_object_get_client(connection), + cancellable, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "GetSecrets", + g_variant_new("(s)", setting_name), + G_VARIANT_TYPE("(a{sa{sv}})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) + return NULL; + + g_variant_get(ret, "(@a{sa{sv}})", &secrets); + + return secrets; +} + +/** + * nm_remote_connection_get_secrets_async: + * @connection: the #NMRemoteConnection + * @setting_name: the #NMSetting object name to get secrets for + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the secret request completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously requests the connection's secrets. + **/ +void +nm_remote_connection_get_secrets_async(NMRemoteConnection *connection, + const char * setting_name, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_REMOTE_CONNECTION(connection)); + g_return_if_fail(setting_name); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(_nm_object_get_client(connection), + connection, + nm_remote_connection_get_secrets_async, + cancellable, + callback, + user_data, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "GetSecrets", + g_variant_new("(s)", setting_name), + G_VARIANT_TYPE("(a{sa{sv}})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); +} + +/** + * nm_remote_connection_get_secrets_finish: + * @connection: the #NMRemoteConnection + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_remote_connection_get_secrets_async(). + * + * Returns: (transfer full): a #GVariant of type %NM_VARIANT_TYPE_CONNECTION + * containing @connection's secrets, or %NULL on error. + **/ +GVariant * +nm_remote_connection_get_secrets_finish(NMRemoteConnection *connection, + GAsyncResult * result, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + GVariant * secrets; + + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), NULL); + g_return_val_if_fail( + nm_g_task_is_valid(result, connection, nm_remote_connection_get_secrets_async), + FALSE); + + ret = g_task_propagate_pointer(G_TASK(result), error); + if (!ret) + return NULL; + + g_variant_get(ret, "(@a{sa{sv}})", &secrets); + + return secrets; +} + +/** + * nm_remote_connection_get_unsaved: + * @connection: the #NMRemoteConnection + * + * Returns: %TRUE if the remote connection contains changes that have not + * been saved to disk, %FALSE if the connection is the same as its on-disk + * representation. + **/ +gboolean +nm_remote_connection_get_unsaved(NMRemoteConnection *connection) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + + return NM_REMOTE_CONNECTION_GET_PRIVATE(connection)->unsaved; +} + +/** + * nm_remote_connection_get_flags: + * @connection: the #NMRemoteConnection + * + * Returns: the flags of the connection of type #NMSettingsConnectionFlags. + * + * Since: 1.12 + **/ +NMSettingsConnectionFlags +nm_remote_connection_get_flags(NMRemoteConnection *connection) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + + return (NMSettingsConnectionFlags) NM_REMOTE_CONNECTION_GET_PRIVATE(connection)->flags; +} + +/** + * nm_remote_connection_get_filename: + * @connection: the #NMRemoteConnection + * + * Returns: file that stores the connection in case the connection is file-backed. + * + * Since: 1.12 + **/ +const char * +nm_remote_connection_get_filename(NMRemoteConnection *connection) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), NULL); + + return NM_REMOTE_CONNECTION_GET_PRIVATE(connection)->filename; +} + +/** + * nm_remote_connection_get_visible: + * @connection: the #NMRemoteConnection + * + * Checks if the connection is visible to the current user. If the + * connection is not visible then it is essentially useless; it will + * not contain any settings, and operations such as + * nm_remote_connection_save() and nm_remote_connection_delete() will + * always fail. (#NMRemoteSettings will not normally return + * non-visible connections to callers, but it is possible for a + * connection's visibility to change after you already have a + * reference to it.) + * + * Returns: %TRUE if the remote connection is visible to the current + * user, %FALSE if not. + **/ +gboolean +nm_remote_connection_get_visible(NMRemoteConnection *connection) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + + return NM_REMOTE_CONNECTION_GET_PRIVATE(connection)->visible; +} + +/*****************************************************************************/ + +GCancellable * +_nm_remote_settings_get_settings_prepare(NMRemoteConnection *self) +{ + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE(self); + + nm_clear_g_cancellable(&priv->get_settings_cancellable); + priv->get_settings_cancellable = g_cancellable_new(); + return priv->get_settings_cancellable; +} + +void +_nm_remote_settings_get_settings_commit(NMRemoteConnection *self, GVariant *settings) +{ + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE(self); + GError * error = NULL; + gboolean visible = FALSE; + gboolean changed = FALSE; + + g_clear_object(&priv->get_settings_cancellable); + + if (!priv->is_initialized) { + changed = TRUE; + priv->is_initialized = TRUE; + } + + if (settings) { + if (!_nm_connection_replace_settings((NMConnection *) self, + settings, + NM_SETTING_PARSE_FLAGS_BEST_EFFORT, + &error)) { + NML_NMCLIENT_LOG_E(_nm_object_get_client(self), + "[%s] failure to update settings: %s", + _nm_object_get_path(self), + error->message); + g_clear_error(&error); + } else + visible = TRUE; + } else + nm_connection_clear_settings(NM_CONNECTION(self)); + + if (priv->visible != visible) { + priv->visible = visible; + _nm_client_queue_notify_object(_nm_object_get_client(self), + self, + obj_properties[PROP_VISIBLE]); + changed = TRUE; + } + + if (changed) + _nm_client_notify_object_changed(_nm_object_get_client(self), _nm_object_get_dbobj(self)); +} + +/*****************************************************************************/ + +static gboolean +is_ready(NMObject *nmobj) +{ + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE(nmobj); + + if (!priv->is_initialized) + return FALSE; + + return NM_OBJECT_CLASS(nm_remote_connection_parent_class)->is_ready(nmobj); +} + +/*****************************************************************************/ + +static void +register_client(NMObject *nmobj, NMClient *client, NMLDBusObject *dbobj) +{ + NM_OBJECT_CLASS(nm_remote_connection_parent_class)->register_client(nmobj, client, dbobj); + nm_connection_set_path(NM_CONNECTION(nmobj), dbobj->dbus_path->str); + _nm_client_get_settings_call(client, dbobj); +} + +static void +unregister_client(NMObject *nmobj, NMClient *client, NMLDBusObject *dbobj) +{ + nm_clear_g_cancellable(&NM_REMOTE_CONNECTION_GET_PRIVATE(nmobj)->get_settings_cancellable); + NM_OBJECT_CLASS(nm_remote_connection_parent_class)->unregister_client(nmobj, client, dbobj); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_UNSAVED: + g_value_set_boolean(value, NM_REMOTE_CONNECTION_GET_PRIVATE(object)->unsaved); + break; + case PROP_FLAGS: + g_value_set_uint(value, NM_REMOTE_CONNECTION_GET_PRIVATE(object)->flags); + break; + case PROP_FILENAME: + g_value_set_string(value, NM_REMOTE_CONNECTION_GET_PRIVATE(object)->filename); + break; + case PROP_VISIBLE: + g_value_set_boolean(value, NM_REMOTE_CONNECTION_GET_PRIVATE(object)->visible); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_remote_connection_init(NMRemoteConnection *self) +{} + +static void +dispose(GObject *object) +{ + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE(object); + + nm_clear_g_free(&priv->filename); + + G_OBJECT_CLASS(nm_remote_connection_parent_class)->dispose(object); +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_settings_connection = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + nm_remote_connection_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_S("Filename", + PROP_FILENAME, + NMRemoteConnection, + _priv.filename), + NML_DBUS_META_PROPERTY_INIT_U("Flags", PROP_FLAGS, NMRemoteConnection, _priv.flags), + NML_DBUS_META_PROPERTY_INIT_B("Unsaved", + PROP_UNSAVED, + NMRemoteConnection, + _priv.unsaved), ), ); + +static void +nm_remote_connection_class_init(NMRemoteConnectionClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + object_class->get_property = get_property; + object_class->dispose = dispose; + + nm_object_class->is_ready = is_ready; + nm_object_class->register_client = register_client; + nm_object_class->unregister_client = unregister_client; + + /** + * NMRemoteConnection:unsaved: + * + * %TRUE if the remote connection contains changes that have not been saved + * to disk, %FALSE if the connection is the same as its on-disk representation. + **/ + obj_properties[PROP_UNSAVED] = g_param_spec_boolean(NM_REMOTE_CONNECTION_UNSAVED, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMRemoteConnection:flags: + * + * The flags of the connection as unsigned integer. The values + * correspond to the #NMSettingsConnectionFlags enum. + * + * Since: 1.12 + **/ + obj_properties[PROP_FLAGS] = g_param_spec_uint(NM_REMOTE_CONNECTION_FLAGS, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMRemoteConnection:filename: + * + * File that stores the connection in case the connection is + * file-backed. + * + * Since: 1.12 + **/ + obj_properties[PROP_FILENAME] = g_param_spec_string(NM_REMOTE_CONNECTION_FILENAME, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMRemoteConnection:visible: + * + * %TRUE if the remote connection is visible to the current user, %FALSE if + * not. If the connection is not visible then it is essentially useless; it + * will not contain any settings, and operations such as + * nm_remote_connection_save() and nm_remote_connection_delete() will always + * fail. (#NMRemoteSettings will not normally return non-visible connections + * to callers, but it is possible for a connection's visibility to change + * after you already have a reference to it.) + **/ + obj_properties[PROP_VISIBLE] = g_param_spec_boolean(NM_REMOTE_CONNECTION_VISIBLE, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_settings_connection); +} + +static void +nm_remote_connection_connection_iface_init(NMConnectionInterface *iface) +{} diff --git a/src/libnm-client-impl/nm-secret-agent-old.c b/src/libnm-client-impl/nm-secret-agent-old.c new file mode 100644 index 00000000..881dfd54 --- /dev/null +++ b/src/libnm-client-impl/nm-secret-agent-old.c @@ -0,0 +1,1995 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2010 - 2011 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-secret-agent-old.h" + +#include "c-list/src/c-list.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-dbus-helpers.h" +#include "nm-dbus-interface.h" +#include "nm-enum-types.h" +#include "libnm-glib-aux/nm-c-list.h" +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "libnm-glib-aux/nm-time-utils.h" +#include "nm-simple-connection.h" + +#define REGISTER_RETRY_TIMEOUT_MSEC 3000 +#define _CALL_REGISTER_TIMEOUT_MSEC 15000 + +/*****************************************************************************/ + +typedef struct { + char * connection_path; + char * setting_name; + GDBusMethodInvocation *context; + CList gsi_lst; + bool is_cancelling : 1; +} GetSecretsInfo; + +NM_GOBJECT_PROPERTIES_DEFINE(NMSecretAgentOld, + PROP_IDENTIFIER, + PROP_AUTO_REGISTER, + PROP_REGISTERED, + PROP_CAPABILITIES, + PROP_DBUS_CONNECTION, ); + +typedef struct { + GDBusConnection *dbus_connection; + GMainContext * main_context; + GMainContext * dbus_context; + GObject * context_busy_watcher; + GCancellable * name_owner_cancellable; + GCancellable * registering_cancellable; + GSource * registering_retry_source; + + NMLInitData *init_data; + + CList gsi_lst_head; + + CList pending_tasks_register_lst_head; + + char *identifier; + + NMRefString *name_owner_curr; + NMRefString *name_owner_next; + + gint64 registering_timeout_msec; + + guint name_owner_changed_id; + + guint exported_id; + + guint capabilities; + + guint8 registering_try_count; + + guint8 register_state_change_reenter : 2; + + bool session_bus : 1; + + bool auto_register : 1; + + bool is_registered : 1; + + bool is_enabled : 1; + + bool registration_force_unregister : 1; + + /* This is true, if we either are in the process of RegisterWithCapabilities() or + * are already successfully registered. + * + * This is only TRUE, if the name owner was authenticated to run as root user. + * + * It also means, we should follow up with an Unregister() call during shutdown. */ + bool registered_against_server : 1; + + bool is_initialized : 1; + bool is_destroyed : 1; +} NMSecretAgentOldPrivate; + +static void nm_secret_agent_old_initable_iface_init(GInitableIface *iface); +static void nm_secret_agent_old_async_initable_iface_init(GAsyncInitableIface *iface); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE( + NMSecretAgentOld, + nm_secret_agent_old, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, nm_secret_agent_old_initable_iface_init); + G_IMPLEMENT_INTERFACE(G_TYPE_ASYNC_INITABLE, nm_secret_agent_old_async_initable_iface_init);) + +#define NM_SECRET_AGENT_OLD_GET_PRIVATE(self) \ + (G_TYPE_INSTANCE_GET_PRIVATE((self), NM_TYPE_SECRET_AGENT_OLD, NMSecretAgentOldPrivate)) + +/*****************************************************************************/ + +#define _NMLOG(level, ...) \ + NML_DBUS_LOG((level), \ + "secret-agent[" NM_HASH_OBFUSCATE_PTR_FMT \ + "]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + NM_HASH_OBFUSCATE_PTR(self) _NM_UTILS_MACRO_REST(__VA_ARGS__)) + +/*****************************************************************************/ + +static const GDBusInterfaceInfo interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT( + NM_DBUS_INTERFACE_SECRET_AGENT, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS( + NM_DEFINE_GDBUS_METHOD_INFO("GetSecrets", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS( + NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"), + NM_DEFINE_GDBUS_ARG_INFO("setting_name", "s"), + NM_DEFINE_GDBUS_ARG_INFO("hints", "as"), + NM_DEFINE_GDBUS_ARG_INFO("flags", "u"), ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS( + NM_DEFINE_GDBUS_ARG_INFO("secrets", "a{sa{sv}}"), ), ), + NM_DEFINE_GDBUS_METHOD_INFO("CancelGetSecrets", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS( + NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"), + NM_DEFINE_GDBUS_ARG_INFO("setting_name", "s"), ), ), + NM_DEFINE_GDBUS_METHOD_INFO("SaveSecrets", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS( + NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"), ), ), + NM_DEFINE_GDBUS_METHOD_INFO( + "DeleteSecrets", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS( + NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"), ), ), ), ); + +/*****************************************************************************/ + +static void _register_state_change(NMSecretAgentOld *self); + +static void _register_dbus_call(NMSecretAgentOld *self); + +static void _init_complete(NMSecretAgentOld *self, GError *error_take); + +static void _register_state_complete(NMSecretAgentOld *self); + +/*****************************************************************************/ + +/** + * nm_secret_agent_old_get_dbus_connection: + * @self: the #NMSecretAgentOld instance + * + * Returns: (transfer none): the #GDBusConnection used by the secret agent. + * You may either set this as construct property %NM_SECRET_AGENT_OLD_DBUS_CONNECTION, + * or it will automatically set during initialization. + * + * Since: 1.24 + */ +GDBusConnection * +nm_secret_agent_old_get_dbus_connection(NMSecretAgentOld *self) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL); + + return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->dbus_connection; +} + +/** + * nm_secret_agent_old_get_main_context: + * @self: the #NMSecretAgentOld instance + * + * Returns: (transfer none): the #GMainContext instance associate with the + * instance. This is the g_main_context_get_thread_default() at the time + * when creating the instance. + * + * Since: 1.24 + */ +GMainContext * +nm_secret_agent_old_get_main_context(NMSecretAgentOld *self) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL); + + return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->main_context; +} + +/** + * nm_secret_agent_old_get_context_busy_watcher: + * @self: the #NMSecretAgentOld instance + * + * Returns a #GObject that stays alive as long as there are pending + * requests in the #GDBusConnection. Such requests keep the #GMainContext + * alive, and thus you may want to keep iterating the context as long + * until a weak reference indicates that this object is gone. This is + * useful because even when you destroy the instance right away (and all + * the internally pending requests get cancelled), any pending g_dbus_connection_call() + * requests will still invoke the result on the #GMainContext. Hence, this + * allows you to know how long you must iterate the context to know + * that all remains are cleaned up. + * + * Returns: (transfer none): a #GObject that you may register a weak pointer + * to know that the #GMainContext is still kept busy by @self. + * + * Since: 1.24 + */ +GObject * +nm_secret_agent_old_get_context_busy_watcher(NMSecretAgentOld *self) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL); + + return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->context_busy_watcher; +} + +/** + * nm_secret_agent_old_get_dbus_name_owner: + * @self: the #NMSecretAgentOld instance + * + * Returns: the current D-Bus name owner. While this property + * is set while registering, it really only makes sense when + * the nm_secret_agent_old_get_registered() indicates that + * registration is successful. + * + * Since: 1.24 + */ +const char * +nm_secret_agent_old_get_dbus_name_owner(NMSecretAgentOld *self) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL); + + return nm_ref_string_get_str(NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->name_owner_curr); +} + +/** + * nm_secret_agent_old_get_registered: + * @self: a #NMSecretAgentOld + * + * Note that the secret agent transparently registers and re-registers + * as the D-Bus name owner appears. Hence, this property is not really + * useful. Also, to be graceful against races during registration, the + * instance will already accept requests while being in the process of + * registering. + * If you need to avoid races and want to wait until @self is registered, + * call nm_secret_agent_old_register_async(). If that function completes + * with success, you know the instance is registered. + * + * Returns: a %TRUE if the agent is registered, %FALSE if it is not. + **/ +gboolean +nm_secret_agent_old_get_registered(NMSecretAgentOld *self) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); + + return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->is_registered; +} + +/*****************************************************************************/ + +static void +get_secret_info_free(GetSecretsInfo *info) +{ + nm_assert(info); + nm_assert(!info->context); + + c_list_unlink_stale(&info->gsi_lst); + g_free(info->connection_path); + g_free(info->setting_name); + nm_g_slice_free(info); +} + +static void +get_secret_info_complete_and_free(GetSecretsInfo *info, GVariant *secrets, GError *error) +{ + if (error) { + if (secrets) + nm_g_variant_unref_floating(secrets); + g_dbus_method_invocation_return_gerror(g_steal_pointer(&info->context), error); + } else { + g_dbus_method_invocation_return_value(g_steal_pointer(&info->context), + g_variant_new("(@a{sa{sv}})", secrets)); + } + get_secret_info_free(info); +} + +static void +get_secret_info_complete_and_free_error(GetSecretsInfo *info, + GQuark error_domain, + int error_code, + const char * error_message) +{ + g_dbus_method_invocation_return_error_literal(g_steal_pointer(&info->context), + error_domain, + error_code, + error_message); + get_secret_info_free(info); +} + +/*****************************************************************************/ + +static void +_dbus_connection_call_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + gs_unref_object GObject *context_busy_watcher = NULL; + GAsyncReadyCallback callback; + gpointer callback_user_data; + + nm_utils_user_data_unpack(user_data, &context_busy_watcher, &callback, &callback_user_data); + callback(source, result, callback_user_data); +} + +static void +_dbus_connection_call(NMSecretAgentOld * self, + const char * bus_name, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + nm_assert(nm_g_main_context_is_thread_default(priv->dbus_context)); + + g_dbus_connection_call( + priv->dbus_connection, + bus_name, + object_path, + interface_name, + method_name, + parameters, + reply_type, + flags, + timeout_msec, + cancellable, + callback ? _dbus_connection_call_cb : NULL, + callback + ? nm_utils_user_data_pack(g_object_ref(priv->context_busy_watcher), callback, user_data) + : NULL); +} + +/*****************************************************************************/ + +static GetSecretsInfo * +find_get_secrets_info(NMSecretAgentOldPrivate *priv, + const char * connection_path, + const char * setting_name) +{ + GetSecretsInfo *info; + + c_list_for_each_entry (info, &priv->gsi_lst_head, gsi_lst) { + if (nm_streq(connection_path, info->connection_path) + && nm_streq(setting_name, info->setting_name)) + return info; + } + return NULL; +} + +static void +_cancel_get_secret_request(NMSecretAgentOld *self, GetSecretsInfo *info, const char *message) +{ + c_list_unlink(&info->gsi_lst); + info->is_cancelling = TRUE; + + _LOGT("cancel get-secrets request \"%s\", \"%s\": %s", + info->connection_path, + info->setting_name, + message); + + NM_SECRET_AGENT_OLD_GET_CLASS(self)->cancel_get_secrets(self, + info->connection_path, + info->setting_name); + + get_secret_info_complete_and_free_error(info, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_AGENT_CANCELED, + message); +} + +static gboolean +verify_request(NMSecretAgentOld * self, + GDBusMethodInvocation *context, + GVariant * connection_dict, + const char * connection_path, + NMConnection ** out_connection, + GError ** error) +{ + gs_unref_object NMConnection *connection = NULL; + gs_free_error GError *local = NULL; + + if (!nm_dbus_path_not_empty(connection_path)) { + g_set_error_literal(error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, + "Invalid connection: no connection path given."); + return FALSE; + } + + connection = _nm_simple_connection_new_from_dbus(connection_dict, + NM_SETTING_PARSE_FLAGS_BEST_EFFORT, + &local); + if (!connection) { + g_set_error(error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, + "Invalid connection: %s", + local->message); + return FALSE; + } + + nm_connection_set_path(connection, connection_path); + NM_SET_OUT(out_connection, g_steal_pointer(&connection)); + return TRUE; +} + +static void +get_secrets_cb(NMSecretAgentOld *self, + NMConnection * connection, + GVariant * secrets, + GError * error, + gpointer user_data) +{ + GetSecretsInfo *info = user_data; + + if (info->is_cancelling) { + if (secrets) + nm_g_variant_unref_floating(secrets); + return; + } + + _LOGT("request: get-secrets request \"%s\", \"%s\" complete with %s%s%s", + info->connection_path, + info->setting_name, + NM_PRINT_FMT_QUOTED(error, "error: ", error->message, "", "success")); + + get_secret_info_complete_and_free(info, secrets, error); +} + +static void +impl_get_secrets(NMSecretAgentOld *self, GVariant *parameters, GDBusMethodInvocation *context) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + GError * error = NULL; + gs_unref_object NMConnection *connection = NULL; + GetSecretsInfo * info; + gs_unref_variant GVariant *arg_connection = NULL; + const char * arg_connection_path; + const char * arg_setting_name; + gs_free const char ** arg_hints = NULL; + guint32 arg_flags; + + g_variant_get(parameters, + "(@a{sa{sv}}&o&s^a&su)", + &arg_connection, + &arg_connection_path, + &arg_setting_name, + &arg_hints, + &arg_flags); + + if (!verify_request(self, context, arg_connection, arg_connection_path, &connection, &error)) { + g_dbus_method_invocation_take_error(context, error); + return; + } + + _LOGT("request: get-secrets(\"%s\", \"%s\")", arg_connection_path, arg_setting_name); + + info = find_get_secrets_info(priv, arg_connection_path, arg_setting_name); + if (info) + _cancel_get_secret_request(self, info, "Request aborted due to new request"); + + info = g_slice_new(GetSecretsInfo); + *info = (GetSecretsInfo){ + .context = context, + .connection_path = g_strdup(arg_connection_path), + .setting_name = g_strdup(arg_setting_name), + }; + c_list_link_tail(&priv->gsi_lst_head, &info->gsi_lst); + + NM_SECRET_AGENT_OLD_GET_CLASS(self)->get_secrets(self, + connection, + info->connection_path, + info->setting_name, + arg_hints, + arg_flags, + get_secrets_cb, + info); +} + +static void +impl_cancel_get_secrets(NMSecretAgentOld * self, + GVariant * parameters, + GDBusMethodInvocation *context) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + GetSecretsInfo * info; + const char * arg_connection_path; + const char * arg_setting_name; + + g_variant_get(parameters, "(&o&s)", &arg_connection_path, &arg_setting_name); + + info = find_get_secrets_info(priv, arg_connection_path, arg_setting_name); + if (!info) { + g_dbus_method_invocation_return_error_literal( + context, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + "No secrets request in progress for this connection."); + return; + } + + _cancel_get_secret_request(self, info, "Request cancelled by NetworkManager"); + + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +save_secrets_cb(NMSecretAgentOld *self, NMConnection *connection, GError *error, gpointer user_data) +{ + GDBusMethodInvocation *context = user_data; + + if (error) + g_dbus_method_invocation_return_gerror(context, error); + else + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_save_secrets(NMSecretAgentOld *self, GVariant *parameters, GDBusMethodInvocation *context) +{ + gs_unref_object NMConnection *connection = NULL; + gs_unref_variant GVariant *arg_connection = NULL; + const char * arg_connection_path; + GError * error = NULL; + + g_variant_get(parameters, "(@a{sa{sv}}&o)", &arg_connection, &arg_connection_path); + + if (!verify_request(self, context, arg_connection, arg_connection_path, &connection, &error)) { + g_dbus_method_invocation_take_error(context, error); + return; + } + + _LOGT("request: save-secrets(\"%s\")", arg_connection_path); + + NM_SECRET_AGENT_OLD_GET_CLASS(self)->save_secrets(self, + connection, + arg_connection_path, + save_secrets_cb, + context); +} + +static void +delete_secrets_cb(NMSecretAgentOld *self, + NMConnection * connection, + GError * error, + gpointer user_data) +{ + GDBusMethodInvocation *context = user_data; + + if (error) + g_dbus_method_invocation_return_gerror(context, error); + else + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_delete_secrets(NMSecretAgentOld *self, GVariant *parameters, GDBusMethodInvocation *context) +{ + gs_unref_object NMConnection *connection = NULL; + gs_unref_variant GVariant *arg_connection = NULL; + const char * arg_connection_path; + GError * error = NULL; + + g_variant_get(parameters, "(@a{sa{sv}}&o)", &arg_connection, &arg_connection_path); + + if (!verify_request(self, context, arg_connection, arg_connection_path, &connection, &error)) { + g_dbus_method_invocation_take_error(context, error); + return; + } + + _LOGT("request: delete-secrets(\"%s\")", arg_connection_path); + + NM_SECRET_AGENT_OLD_GET_CLASS(self)->delete_secrets(self, + connection, + arg_connection_path, + delete_secrets_cb, + context); +} + +/*****************************************************************************/ + +/** + * nm_secret_agent_old_enable: + * @self: the #NMSecretAgentOld instance + * @enable: whether to enable or disable the listener. + * + * This has the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER + * property. + * + * Unlike most other functions, you may already call this function before + * initialization completes. + * + * Since: 1.24 + */ +void +nm_secret_agent_old_enable(NMSecretAgentOld *self, gboolean enable) +{ + NMSecretAgentOldPrivate *priv; + + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); + + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + enable = (!!enable); + + if (priv->auto_register != enable) { + priv->auto_register = enable; + priv->is_enabled = enable; + _notify(self, PROP_AUTO_REGISTER); + } + _register_state_change(self); +} + +static void +_secret_agent_old_destroy(NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + priv->is_destroyed = TRUE; + + if (priv->exported_id != 0) { + g_dbus_connection_unregister_object(priv->dbus_connection, + nm_steal_int(&priv->exported_id)); + } + + _register_state_change(self); + + nm_assert(!priv->name_owner_changed_id); + nm_assert(!priv->name_owner_curr); + nm_assert(!priv->name_owner_next); + nm_assert(!priv->name_owner_cancellable); + nm_assert(!priv->registering_retry_source); + nm_assert(!priv->registering_cancellable); + nm_assert(!priv->init_data); + nm_assert(c_list_is_empty(&priv->gsi_lst_head)); + nm_assert(c_list_is_empty(&priv->pending_tasks_register_lst_head)); +} + +/** + * nm_secret_agent_old_destroy: + * @self: the #NMSecretAgentOld instance. + * + * Since 1.24, the instance will already register a D-Bus object on the + * D-Bus connection during initialization. That object will stay registered + * until @self gets unrefed (destroyed) or this function is called. This + * function performs the necessary cleanup to tear down the instance. Afterwards, + * the function can not longer be used. This is optional, but necessary to + * ensure unregistering the D-Bus object at a define point, when other users + * might still have a reference on @self. + * + * You may call this function any time and repeatedly. However, after destroying + * the instance, it is a bug to still use the instance for other purposes. The + * instance becomes defunct and cannot re-register. + * + * Since: 1.24 + */ +void +nm_secret_agent_old_destroy(NMSecretAgentOld *self) +{ + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); + + _LOGT("destroying"); + + _secret_agent_old_destroy(self); +} + +/*****************************************************************************/ + +/** + * nm_secret_agent_old_register: + * @self: a #NMSecretAgentOld + * @cancellable: a #GCancellable, or %NULL + * @error: return location for a #GError, or %NULL + * + * Registers the #NMSecretAgentOld with the NetworkManager secret manager, + * indicating to NetworkManager that the agent is able to provide and save + * secrets for connections on behalf of its user. + * + * Returns: %TRUE if registration was successful, %FALSE on error. + * + * Since 1.24, this can no longer fail unless the @cancellable gets + * cancelled. Contrary to nm_secret_agent_old_register_async(), this also + * does not wait for the registration to succeed. You cannot synchronously + * (without iterating the caller's GMainContext) wait for registration. + * + * Since 1.24, registration is idempotent. It has the same effect as setting + * %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %TRUE or nm_secret_agent_old_enable(). + * + * Deprecated: 1.24: Use nm_secret_agent_old_enable() or nm_secret_agent_old_register_async(). + **/ +gboolean +nm_secret_agent_old_register(NMSecretAgentOld *self, GCancellable *cancellable, GError **error) +{ + NMSecretAgentOldPrivate *priv; + + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + g_return_val_if_fail(priv->is_initialized && !priv->is_destroyed, FALSE); + + priv->is_enabled = TRUE; + _register_state_change(self); + + if (g_cancellable_set_error_if_cancelled(cancellable, error)) + return FALSE; + + /* This is a synchronous function, meaning: we are not allowed to iterate + * the caller's GMainContext. This is a catch 22, because we don't want + * to perform synchronous calls that bypasses the ordering of our otherwise + * asynchronous mode of operation. Hence, we always signal success. + * That's why this function is deprecated. + * + * So despite claiming success, we might still be in the process of registering + * or NetworkManager might not be available. + * + * This is a change in behavior with respect to libnm before 1.24. + */ + return TRUE; +} + +static void +_register_cancelled_cb(GObject *object, gpointer user_data) +{ + GTask * task0 = user_data; + gs_unref_object GTask * task = NULL; + NMSecretAgentOld * self = g_task_get_source_object(task0); + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + gulong * p_cancelled_id; + NMCListElem * elem; + gs_free_error GError *error = NULL; + + elem = nm_c_list_elem_find_first(&priv->pending_tasks_register_lst_head, x, x == task0); + + g_return_if_fail(elem); + + task = nm_c_list_elem_free_steal(elem); + + p_cancelled_id = g_task_get_task_data(task); + if (p_cancelled_id) { + g_signal_handler_disconnect(g_task_get_cancellable(task), *p_cancelled_id); + g_task_set_task_data(task, NULL, NULL); + } + + nm_utils_error_set_cancelled(&error, FALSE, NULL); + g_task_return_error(task, error); +} + +/** + * nm_secret_agent_old_register_async: + * @self: a #NMSecretAgentOld + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the agent is registered + * @user_data: data for @callback + * + * Asynchronously registers the #NMSecretAgentOld with the NetworkManager secret + * manager, indicating to NetworkManager that the agent is able to provide and + * save secrets for connections on behalf of its user. + * + * Since 1.24, registration cannot fail and is idempotent. It has + * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %TRUE + * or nm_secret_agent_old_enable(). + * + * Since 1.24, the asynchronous result indicates whether the instance is successfully + * registered. In any case, this call enables the agent and it will automatically + * try to register and handle secret requests. A failure of this function only indicates + * that currently the instance might not be ready (but since it will automatically + * try to recover, it might be ready in a moment afterwards). Use this function if + * you want to check and ensure that the agent is registered. + **/ +void +nm_secret_agent_old_register_async(NMSecretAgentOld * self, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgentOldPrivate *priv; + + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + g_return_if_fail(priv->is_initialized && !priv->is_destroyed); + + if (callback) { + GTask *task; + + task = nm_g_task_new(self, + cancellable, + nm_secret_agent_old_register_async, + callback, + user_data); + + c_list_link_tail(&priv->pending_tasks_register_lst_head, + &nm_c_list_elem_new_stale(task)->lst); + + if (cancellable) { + gulong cancelled_id; + + cancelled_id = + g_cancellable_connect(cancellable, G_CALLBACK(_register_cancelled_cb), task, NULL); + if (cancelled_id != 0) { + g_task_set_task_data(task, g_memdup(&cancelled_id, sizeof(cancelled_id)), g_free); + } + } + } + + priv->is_enabled = TRUE; + _register_state_change(self); +} + +/** + * nm_secret_agent_old_register_finish: + * @self: a #NMSecretAgentOld + * @result: the result passed to the #GAsyncReadyCallback + * @error: return location for a #GError, or %NULL + * + * Gets the result of a call to nm_secret_agent_old_register_async(). + * + * Returns: %TRUE if registration was successful, %FALSE on error. + * + * Since 1.24, registration cannot fail and is idempotent. It has + * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %TRUE + * or nm_secret_agent_old_enable(). + **/ +gboolean +nm_secret_agent_old_register_finish(NMSecretAgentOld *self, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, self, nm_secret_agent_old_register_async), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/** + * nm_secret_agent_old_unregister: + * @self: a #NMSecretAgentOld + * @cancellable: a #GCancellable, or %NULL + * @error: return location for a #GError, or %NULL + * + * Unregisters the #NMSecretAgentOld with the NetworkManager secret manager, + * indicating to NetworkManager that the agent will no longer provide or + * store secrets on behalf of this user. + * + * Returns: %TRUE if unregistration was successful, %FALSE on error + * + * Since 1.24, registration cannot fail and is idempotent. It has + * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %FALSE + * or nm_secret_agent_old_enable(). + * + * Deprecated: 1.24: Use nm_secret_agent_old_enable(). + **/ +gboolean +nm_secret_agent_old_unregister(NMSecretAgentOld *self, GCancellable *cancellable, GError **error) +{ + NMSecretAgentOldPrivate *priv; + + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + g_return_val_if_fail(priv->is_initialized && !priv->is_destroyed, FALSE); + + priv->is_enabled = FALSE; + _register_state_change(self); + + return !g_cancellable_set_error_if_cancelled(cancellable, error); +} + +/** + * nm_secret_agent_old_unregister_async: + * @self: a #NMSecretAgentOld + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the agent is unregistered + * @user_data: data for @callback + * + * Asynchronously unregisters the #NMSecretAgentOld with the NetworkManager secret + * manager, indicating to NetworkManager that the agent will no longer provide + * or store secrets on behalf of this user. + * + * Since 1.24, registration cannot fail and is idempotent. It has + * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %FALSE + * or nm_secret_agent_old_enable(). + * + * Deprecated: 1.24: Use nm_secret_agent_old_enable(). + **/ +void +nm_secret_agent_old_unregister_async(NMSecretAgentOld * self, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgentOldPrivate *priv; + + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + g_return_if_fail(priv->is_initialized && !priv->is_destroyed); + + if (callback) { + gs_unref_object GTask *task = NULL; + + task = nm_g_task_new(self, + cancellable, + nm_secret_agent_old_unregister_async, + callback, + user_data); + g_task_return_boolean(task, TRUE); + } + + priv->is_enabled = FALSE; + _register_state_change(self); +} + +/** + * nm_secret_agent_old_unregister_finish: + * @self: a #NMSecretAgentOld + * @result: the result passed to the #GAsyncReadyCallback + * @error: return location for a #GError, or %NULL + * + * Gets the result of a call to nm_secret_agent_old_unregister_async(). + * + * Returns: %TRUE if unregistration was successful, %FALSE on error. + * + * Since 1.24, registration cannot fail and is idempotent. It has + * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %FALSE + * or nm_secret_agent_old_enable(). + * + * Deprecated: 1.24: Use nm_secret_agent_old_enable(). + **/ +gboolean +nm_secret_agent_old_unregister_finish(NMSecretAgentOld *self, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, self, nm_secret_agent_old_unregister_async), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +/** + * nm_secret_agent_old_get_secrets: (virtual get_secrets): + * @self: a #NMSecretAgentOld + * @connection: the #NMConnection for which we're asked secrets + * @setting_name: the name of the secret setting + * @hints: (array zero-terminated=1): hints to the agent + * @flags: flags that modify the behavior of the request + * @callback: (scope async): a callback, to be invoked when the operation is done + * @user_data: (closure): caller-specific data to be passed to @callback + * + * Asynchronously retrieves secrets belonging to @connection for the + * setting @setting_name. @flags indicate specific behavior that the secret + * agent should use when performing the request, for example returning only + * existing secrets without user interaction, or requesting entirely new + * secrets from the user. + */ +void +nm_secret_agent_old_get_secrets(NMSecretAgentOld * self, + NMConnection * connection, + const char * setting_name, + const char ** hints, + NMSecretAgentGetSecretsFlags flags, + NMSecretAgentOldGetSecretsFunc callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); + g_return_if_fail(NM_IS_CONNECTION(connection)); + g_return_if_fail(nm_connection_get_path(connection)); + g_return_if_fail(setting_name && setting_name[0]); + g_return_if_fail(!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM)); + g_return_if_fail(!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS)); + g_return_if_fail(callback != NULL); + + NM_SECRET_AGENT_OLD_GET_CLASS(self)->get_secrets(self, + connection, + nm_connection_get_path(connection), + setting_name, + hints, + flags, + callback, + user_data); +} + +/** + * nm_secret_agent_old_save_secrets: (virtual save_secrets): + * @self: a #NMSecretAgentOld + * @connection: a #NMConnection + * @callback: (scope async): a callback, to be invoked when the operation is done + * @user_data: (closure): caller-specific data to be passed to @callback + * + * Asynchronously ensures that all secrets inside @connection are stored to + * disk. + */ +void +nm_secret_agent_old_save_secrets(NMSecretAgentOld * self, + NMConnection * connection, + NMSecretAgentOldSaveSecretsFunc callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); + g_return_if_fail(NM_IS_CONNECTION(connection)); + g_return_if_fail(nm_connection_get_path(connection)); + + NM_SECRET_AGENT_OLD_GET_CLASS(self)->save_secrets(self, + connection, + nm_connection_get_path(connection), + callback, + user_data); +} + +/** + * nm_secret_agent_old_delete_secrets: (virtual delete_secrets): + * @self: a #NMSecretAgentOld + * @connection: a #NMConnection + * @callback: (scope async): a callback, to be invoked when the operation is done + * @user_data: (closure): caller-specific data to be passed to @callback + * + * Asynchronously asks the agent to delete all saved secrets belonging to + * @connection. + */ +void +nm_secret_agent_old_delete_secrets(NMSecretAgentOld * self, + NMConnection * connection, + NMSecretAgentOldDeleteSecretsFunc callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); + g_return_if_fail(NM_IS_CONNECTION(connection)); + g_return_if_fail(nm_connection_get_path(connection)); + + NM_SECRET_AGENT_OLD_GET_CLASS(self)->delete_secrets(self, + connection, + nm_connection_get_path(connection), + callback, + user_data); +} + +/*****************************************************************************/ + +static gboolean +validate_identifier(const char *identifier) +{ + const char *p = identifier; + size_t id_len; + + /* Length between 3 and 255 characters inclusive */ + id_len = strlen(identifier); + if (id_len < 3 || id_len > 255) + return FALSE; + + if ((identifier[0] == '.') || (identifier[id_len - 1] == '.')) + return FALSE; + + /* FIXME: do complete validation here */ + while (p && *p) { + if (!g_ascii_isalnum(*p) && (*p != '_') && (*p != '-') && (*p != '.')) + return FALSE; + if ((*p == '.') && (*(p + 1) == '.')) + return FALSE; + p++; + } + + return TRUE; +} + +/*****************************************************************************/ + +static gboolean +_register_retry_cb(gpointer user_data) +{ + NMSecretAgentOld * self = user_data; + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); + + nm_clear_g_source_inst(&priv->registering_retry_source); + _register_dbus_call(self); + return G_SOURCE_CONTINUE; +} + +static void +_register_call_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMSecretAgentOld * self = user_data; + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + + if (nm_utils_error_is_cancelled(error)) + return; + + nm_assert(!priv->registering_retry_source); + nm_assert(!priv->is_registered); + nm_assert(priv->registering_cancellable); + + if (nm_dbus_error_is(error, NM_DBUS_ERROR_NAME_UNKNOWN_METHOD) + && nm_utils_get_monotonic_timestamp_msec() < priv->registering_timeout_msec) { + guint timeout_msec; + + timeout_msec = (2u << NM_MIN(6u, ++priv->registering_try_count)); + + _LOGT("register: registration failed with error \"%s\". Retry in %u msec...", + error->message, + timeout_msec); + + priv->registering_retry_source = + nm_g_source_attach(nm_g_timeout_source_new(timeout_msec, + G_PRIORITY_DEFAULT, + _register_retry_cb, + self, + NULL), + priv->dbus_context); + return; + } + + g_clear_object(&priv->registering_cancellable); + + if (error) { + /* registration apparently failed. However we still keep priv->registered_against_server TRUE, because + * + * - eventually we want to still make an Unregister() call. Even if it probably has no effect, + * better be sure. + * + * - we actually accept secret request (from the right name owner). We register so that + * NetworkManager knows that we are here. We don't require the registration to succeed + * for our purpose. If NetworkManager makes requests for us, despite the registration + * failing, that is fine. */ + _LOGT("register: registration failed with error \"%s\"", error->message); + goto out; + } + + _LOGT("register: registration succeeded"); + priv->is_registered = TRUE; + _notify(self, PROP_REGISTERED); + +out: + _register_state_complete(self); +} + +static void +_register_dbus_call(NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + _dbus_connection_call(self, + nm_ref_string_get_str(priv->name_owner_curr), + NM_DBUS_PATH_AGENT_MANAGER, + NM_DBUS_INTERFACE_AGENT_MANAGER, + "RegisterWithCapabilities", + g_variant_new("(su)", priv->identifier, (guint32) priv->capabilities), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + _CALL_REGISTER_TIMEOUT_MSEC, + priv->registering_cancellable, + _register_call_cb, + self); +} + +static void +_get_connection_unix_user_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMSecretAgentOld * self; + NMSecretAgentOldPrivate *priv; + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + guint32 sender_uid = 0; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (nm_utils_error_is_cancelled(error)) + return; + + self = user_data; + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + nm_assert(priv->registering_cancellable); + nm_assert(!priv->registered_against_server); + + if (ret) + g_variant_get(ret, "(u)", &sender_uid); + + if (ret && sender_uid == 0) + _LOGT("register: peer %s is owned by root. Validated to accept requests.", + priv->name_owner_curr->str); + else if (ret && priv->session_bus) { + _LOGT( + "register: peer %s is owned by user %d for session bus. Validated to accept requests.", + priv->name_owner_curr->str, + sender_uid); + } else { + /* the peer is not validated. We don't actually register. */ + if (ret) + _LOGT("register: peer %s is owned by user %u. Not validated as NetworkManager service.", + priv->name_owner_curr->str, + sender_uid); + else + _LOGT("register: failed to get user id for peer %s: %s. Not validated as " + "NetworkManager service.", + priv->name_owner_curr->str, + error->message); + + /* we actually don't do anything and keep the agent unregistered. + * + * We keep priv->registering_cancellable set to not retry this again, until we loose the + * name owner. But the state of the agent is lingering and won't accept any requests. */ + return; + } + + priv->registering_timeout_msec = + nm_utils_get_monotonic_timestamp_msec() + REGISTER_RETRY_TIMEOUT_MSEC; + priv->registering_try_count = 0; + priv->registered_against_server = TRUE; + _register_dbus_call(self); +} + +/*****************************************************************************/ + +static void +_name_owner_changed(NMSecretAgentOld *self, const char *name_owner, gboolean is_event) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + if (is_event) { + if (priv->name_owner_cancellable) { + /* we are still fetching the name-owner. Ignore this event. */ + return; + } + } else + g_clear_object(&priv->name_owner_cancellable); + + nm_ref_string_unref(priv->name_owner_next); + priv->name_owner_next = nm_ref_string_new(nm_str_not_empty(name_owner)); + + _LOGT("name-owner changed: %s%s%s -> %s%s%s", + NM_PRINT_FMT_QUOTED(priv->name_owner_curr, + "\"", + priv->name_owner_curr->str, + "\"", + "(null)"), + NM_PRINT_FMT_QUOTED(priv->name_owner_next, + "\"", + priv->name_owner_next->str, + "\"", + "(null)")); + + _register_state_change(self); +} + +static void +_name_owner_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMSecretAgentOld *self = user_data; + const char * new_owner; + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) + return; + + g_variant_get(parameters, "(&s&s&s)", NULL, NULL, &new_owner); + + _name_owner_changed(self, new_owner, TRUE); +} + +static void +_name_owner_get_cb(const char *name_owner, GError *error, gpointer user_data) +{ + if (name_owner || !nm_utils_error_is_cancelled(error)) + _name_owner_changed(user_data, name_owner, FALSE); +} + +/*****************************************************************************/ + +static void +_method_call(GDBusConnection * connection, + const char * sender, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + GDBusMethodInvocation *context, + gpointer user_data) +{ + NMSecretAgentOld * self = user_data; + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + nm_assert(nm_streq0(object_path, NM_DBUS_PATH_SECRET_AGENT)); + nm_assert(nm_streq0(interface_name, NM_DBUS_INTERFACE_SECRET_AGENT)); + nm_assert(sender); + nm_assert(nm_streq0(sender, g_dbus_method_invocation_get_sender(context))); + + if (!priv->name_owner_curr || !priv->registered_against_server) { + /* priv->registered_against_server means that we started to register, but not necessarily + * that the registration fully succeeded. However, we already authenticated the request + * and so we accept it, even if the registration is not yet complete. */ + g_dbus_method_invocation_return_error_literal(context, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_PERMISSION_DENIED, + "Request by non authenticated peer rejected"); + return; + } + + if (nm_streq(method_name, "GetSecrets")) + impl_get_secrets(self, parameters, context); + else if (nm_streq(method_name, "CancelGetSecrets")) + impl_cancel_get_secrets(self, parameters, context); + else if (nm_streq(method_name, "SaveSecrets")) + impl_save_secrets(self, parameters, context); + else if (nm_streq(method_name, "DeleteSecrets")) + impl_delete_secrets(self, parameters, context); + else + nm_assert_not_reached(); +} + +/*****************************************************************************/ + +static void +_register_state_complete(NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + NMCListElem * elem; + gboolean any_tasks_to_complete = FALSE; + + if (!c_list_is_empty(&priv->pending_tasks_register_lst_head)) { + /* add a dummy sentinel. We want to complete all the task we started + * so far, but as we invoke user callbacks, the user might register + * new tasks. Those we don't complete in this run. */ + g_object_ref(self); + any_tasks_to_complete = TRUE; + c_list_link_tail(&priv->pending_tasks_register_lst_head, + &nm_c_list_elem_new_stale(&any_tasks_to_complete)->lst); + } + + _init_complete(self, NULL); + + if (any_tasks_to_complete) { + while ( + (elem = c_list_first_entry(&priv->pending_tasks_register_lst_head, NMCListElem, lst))) { + gpointer data = nm_c_list_elem_free_steal(elem); + gs_unref_object GTask *task = NULL; + + if (data == &any_tasks_to_complete) { + any_tasks_to_complete = FALSE; + break; + } + + task = data; + + if (!priv->is_registered) { + g_task_return_error(task, + g_error_new_literal(NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + _("registration failed"))); + continue; + } + g_task_return_boolean(task, TRUE); + } + nm_assert(!any_tasks_to_complete); + g_object_unref(self); + } +} + +static void +_register_state_change_do(NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + if (priv->is_destroyed) + priv->is_enabled = FALSE; + + if (!priv->is_enabled || priv->registration_force_unregister + || priv->name_owner_curr != priv->name_owner_next) { + GetSecretsInfo *info; + + while ((info = c_list_first_entry(&priv->gsi_lst_head, GetSecretsInfo, gsi_lst))) { + _cancel_get_secret_request(self, info, "The secret agent is going away"); + _register_state_change(self); + return; + } + + priv->registration_force_unregister = FALSE; + + nm_clear_g_cancellable(&priv->registering_cancellable); + nm_clear_g_source_inst(&priv->registering_retry_source); + + if (priv->registered_against_server) { + priv->registered_against_server = FALSE; + if (priv->name_owner_curr) { + _LOGT("register: unregister from %s", priv->name_owner_curr->str); + _dbus_connection_call(self, + priv->name_owner_curr->str, + NM_DBUS_PATH_AGENT_MANAGER, + NM_DBUS_INTERFACE_AGENT_MANAGER, + "Unregister", + g_variant_new("()"), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + _CALL_REGISTER_TIMEOUT_MSEC, + NULL, + NULL, + NULL); + } + } + + if (!priv->is_enabled) { + nm_clear_g_cancellable(&priv->name_owner_cancellable); + nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); + nm_clear_pointer(&priv->name_owner_curr, nm_ref_string_unref); + nm_clear_pointer(&priv->name_owner_next, nm_ref_string_unref); + } + + if (priv->is_registered) { + priv->is_registered = FALSE; + if (!priv->is_destroyed) { + _LOGT("register: now unregistered"); + _notify(self, PROP_REGISTERED); + _register_state_change(self); + return; + } + } + + if (!priv->is_enabled) { + _register_state_complete(self); + return; + } + + if (priv->name_owner_curr != priv->name_owner_next) { + nm_ref_string_unref(priv->name_owner_curr); + priv->name_owner_curr = nm_ref_string_ref(priv->name_owner_next); + } + } + + if (priv->name_owner_changed_id == 0) { + nm_assert(!priv->name_owner_cancellable); + nm_assert(!priv->name_owner_curr); + nm_assert(!priv->name_owner_next); + priv->name_owner_cancellable = g_cancellable_new(); + priv->name_owner_changed_id = + nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection, + NM_DBUS_SERVICE, + _name_owner_changed_cb, + self, + NULL); + nm_dbus_connection_call_get_name_owner(priv->dbus_connection, + NM_DBUS_SERVICE, + -1, + priv->name_owner_cancellable, + _name_owner_get_cb, + self); + return; + } + + if (priv->name_owner_cancellable) { + /* we still wait for the name owner. Nothing to do for now. */ + return; + } + + if (!priv->name_owner_curr) { + /* we don't have a name owner. We are done and wait. */ + _register_state_complete(self); + return; + } + + if (priv->registering_cancellable) { + /* we are already registering... wait longer. */ + return; + } + + nm_assert(!priv->registering_retry_source); + + if (!priv->is_registered) { + /* start registering... */ + priv->registering_cancellable = g_cancellable_new(); + _dbus_connection_call(self, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionUnixUser", + g_variant_new("(s)", priv->name_owner_curr->str), + G_VARIANT_TYPE("(u)"), + G_DBUS_CALL_FLAGS_NONE, + _CALL_REGISTER_TIMEOUT_MSEC, + priv->registering_cancellable, + _get_connection_unix_user_cb, + self); + return; + } + + /* we are fully registered and done. */ + _register_state_complete(self); +} + +static void +_register_state_change(NMSecretAgentOld *self) +{ + _nm_unused gs_unref_object NMSecretAgentOld *self_keep_alive = g_object_ref(self); + NMSecretAgentOldPrivate * priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + + if (priv->register_state_change_reenter == 0) { + /* We are not yet initialized. Do nothing. */ + return; + } + + if (priv->register_state_change_reenter != 1) { + /* Recursive calls are prevented. Do nothing for now, but repeat + * the state change afterwards. */ + priv->register_state_change_reenter = 3; + return; + } + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); + +again: + priv->register_state_change_reenter = 2; + + _register_state_change_do(self); + + if (priv->register_state_change_reenter != 2) + goto again; + + priv->register_state_change_reenter = 1; +} + +/*****************************************************************************/ + +static void +_init_complete(NMSecretAgentOld *self, GError *error_take) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + gs_free_error GError *error = g_steal_pointer(&error_take); + GError * error_cancelled = NULL; + + if (!priv->init_data) + return; + + if (g_cancellable_set_error_if_cancelled(priv->init_data->cancellable, &error_cancelled)) { + g_clear_error(&error); + g_propagate_error(&error, error_cancelled); + } + + priv->is_initialized = (!error); + + _LOGT("%s init complete with %s%s%s", + priv->init_data->is_sync ? "sync" : "async", + NM_PRINT_FMT_QUOTED(error_take, "error: ", error_take->message, "", "success")); + + nml_init_data_return(g_steal_pointer(&priv->init_data), g_steal_pointer(&error)); +} + +static void +_init_register_object(NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + gs_free_error GError *error = NULL; + GDBusInterfaceVTable interface_vtable = { + .method_call = _method_call, + }; + + if (g_cancellable_set_error_if_cancelled(priv->init_data->cancellable, &error)) { + _init_complete(self, g_steal_pointer(&error)); + return; + } + + priv->exported_id = g_dbus_connection_register_object(priv->dbus_connection, + NM_DBUS_PATH_SECRET_AGENT, + (GDBusInterfaceInfo *) &interface_info, + &interface_vtable, + self, + NULL, + &error); + if (priv->exported_id == 0) { + _init_complete(self, g_steal_pointer(&error)); + return; + } + + priv->register_state_change_reenter = 1; + + _register_state_change(self); +} + +static void +_init_got_bus(GObject *initable, GAsyncResult *result, gpointer user_data) +{ + NMSecretAgentOld * self = user_data; + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + gs_free_error GError *error = NULL; + + priv->dbus_connection = g_bus_get_finish(result, &error); + if (!priv->dbus_connection) { + _init_complete(self, g_steal_pointer(&error)); + return; + } + + _LOGT("init: got GDBusConnection"); + + _notify(self, PROP_DBUS_CONNECTION); + + _init_register_object(self); +} + +static void +_init_start(NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + if (!priv->dbus_connection) { + GBusType bus_type; + + bus_type = _nm_dbus_bus_type(); + + priv->session_bus = (bus_type == G_BUS_TYPE_SESSION); + + g_bus_get(bus_type, priv->init_data->cancellable, _init_got_bus, self); + return; + } + + _init_register_object(self); +} + +static void +init_async(GAsyncInitable * initable, + int io_priority, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgentOld * self; + NMSecretAgentOldClass * klass; + NMSecretAgentOldPrivate *priv; + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + gs_unref_object GTask *task = NULL; + + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(initable)); + + self = NM_SECRET_AGENT_OLD(initable); + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + g_return_if_fail(!priv->dbus_context); + g_return_if_fail(!priv->is_destroyed); + + klass = NM_SECRET_AGENT_OLD_GET_CLASS(self); + g_return_if_fail(klass->get_secrets); + g_return_if_fail(klass->cancel_get_secrets); + g_return_if_fail(klass->save_secrets); + g_return_if_fail(klass->delete_secrets); + + _LOGT("init-async starting..."); + + priv->dbus_context = g_main_context_ref(priv->main_context); + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); + + task = nm_g_task_new(self, cancellable, init_async, callback, user_data); + g_task_set_priority(task, io_priority); + + priv->init_data = nml_init_data_new_async(cancellable, g_steal_pointer(&task)); + + _init_start(self); +} + +static gboolean +init_finish(GAsyncInitable *initable, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(initable), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, initable, init_async), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +static gboolean +init_sync(GInitable *initable, GCancellable *cancellable, GError **error) +{ + gs_unref_object NMSecretAgentOld *self = NULL; + NMSecretAgentOldPrivate * priv; + NMSecretAgentOldClass * klass; + GMainLoop * main_loop; + GError * local_error = NULL; + + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(initable), FALSE); + + self = g_object_ref(NM_SECRET_AGENT_OLD(initable)); + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + g_return_val_if_fail(!priv->dbus_context, FALSE); + g_return_val_if_fail(!priv->is_destroyed, FALSE); + + klass = NM_SECRET_AGENT_OLD_GET_CLASS(self); + g_return_val_if_fail(klass->get_secrets, FALSE); + g_return_val_if_fail(klass->cancel_get_secrets, FALSE); + g_return_val_if_fail(klass->save_secrets, FALSE); + g_return_val_if_fail(klass->delete_secrets, FALSE); + + _LOGT("init-sync"); + + /* See NMClient's sync-init method for explanation about why we create + * an internal GMainContext priv->dbus_context. */ + + priv->dbus_context = g_main_context_new(); + + g_main_context_push_thread_default(priv->dbus_context); + + main_loop = g_main_loop_new(priv->dbus_context, FALSE); + + priv->init_data = nml_init_data_new_sync(cancellable, main_loop, &local_error); + + _init_start(self); + + g_main_loop_run(main_loop); + + g_main_loop_unref(main_loop); + + g_main_context_pop_thread_default(priv->dbus_context); + + nm_context_busy_watcher_integrate_source(priv->main_context, + priv->dbus_context, + priv->context_busy_watcher); + + if (local_error) { + g_propagate_error(error, local_error); + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_DBUS_CONNECTION: + g_value_set_object(value, priv->dbus_connection); + break; + case PROP_IDENTIFIER: + g_value_set_string(value, priv->identifier); + break; + case PROP_AUTO_REGISTER: + g_value_set_boolean(value, priv->auto_register); + break; + case PROP_REGISTERED: + g_value_set_boolean(value, priv->is_registered); + break; + case PROP_CAPABILITIES: + g_value_set_flags(value, priv->capabilities); + 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) +{ + NMSecretAgentOld * self = NM_SECRET_AGENT_OLD(object); + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + guint u; + + switch (prop_id) { + case PROP_DBUS_CONNECTION: + /* construct-only */ + priv->dbus_connection = g_value_dup_object(value); + break; + case PROP_IDENTIFIER: + /* construct-only */ + priv->identifier = g_value_dup_string(value); + g_return_if_fail(validate_identifier(priv->identifier)); + break; + case PROP_AUTO_REGISTER: + /* construct */ + priv->auto_register = g_value_get_boolean(value); + priv->is_enabled = priv->auto_register; + _register_state_change(self); + break; + case PROP_CAPABILITIES: + /* construct */ + u = g_value_get_flags(value); + if (u != priv->capabilities) { + priv->capabilities = u; + priv->registration_force_unregister = TRUE; + _register_state_change(self); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_secret_agent_old_init(NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + _LOGT("create new instance"); + + c_list_init(&priv->gsi_lst_head); + c_list_init(&priv->pending_tasks_register_lst_head); + + priv->main_context = g_main_context_ref_thread_default(); + priv->context_busy_watcher = g_object_new(G_TYPE_OBJECT, NULL); +} + +static void +dispose(GObject *object) +{ + NMSecretAgentOld *self = NM_SECRET_AGENT_OLD(object); + + _LOGT("disposing"); + + _secret_agent_old_destroy(self); + + G_OBJECT_CLASS(nm_secret_agent_old_parent_class)->dispose(object); +} + +static void +finalize(GObject *object) +{ + NMSecretAgentOld * self = NM_SECRET_AGENT_OLD(object); + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + _LOGT("finalizing"); + + if (priv->dbus_context) { + nml_cleanup_context_busy_watcher_on_idle(g_steal_pointer(&priv->context_busy_watcher), + priv->dbus_context); + } + + g_clear_object(&priv->dbus_connection); + nm_clear_pointer(&priv->dbus_context, g_main_context_unref); + nm_clear_pointer(&priv->main_context, g_main_context_unref); + + g_clear_object(&priv->context_busy_watcher); + + g_free(priv->identifier); + + G_OBJECT_CLASS(nm_secret_agent_old_parent_class)->finalize(object); +} + +static void +nm_secret_agent_old_class_init(NMSecretAgentOldClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(class); + + g_type_class_add_private(class, sizeof(NMSecretAgentOldPrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + /** + * NMSecretAgentOld:dbus-connection: + * + * The #GDBusConnection used by the instance. You may either set this + * as construct-only property, or otherwise #NMSecretAgentOld will choose + * a connection via g_bus_get() during initialization. + * + * Since: 1.24 + **/ + obj_properties[PROP_DBUS_CONNECTION] = + g_param_spec_object(NM_SECRET_AGENT_OLD_DBUS_CONNECTION, + "", + "", + G_TYPE_DBUS_CONNECTION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + /** + * NMSecretAgentOld:identifier: + * + * Identifies this agent; only one agent in each user session may use the + * same identifier. Identifier formatting follows the same rules as + * D-Bus bus names with the exception that the ':' character is not + * allowed. The valid set of characters is "[A-Z][a-z][0-9]_-." and the + * identifier is limited in length to 255 characters with a minimum + * of 3 characters. An example valid identifier is 'org.gnome.nm-applet' + * (without quotes). + **/ + obj_properties[PROP_IDENTIFIER] = + g_param_spec_string(NM_SECRET_AGENT_OLD_IDENTIFIER, + "", + "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + /** + * NMSecretAgentOld:auto-register: + * + * If %TRUE (the default), the agent will always be registered when + * NetworkManager is running; if NetworkManager exits and restarts, the + * agent will re-register itself automatically. + * + * In particular, if this property is %TRUE at construct time, then the + * agent will register itself with NetworkManager during + * construction/initialization and initialization will only complete + * after registration is completed (either successfully or unsuccessfully). + * Since 1.24, a failure to register will no longer cause initialization + * of #NMSecretAgentOld to fail. + * + * If the property is %FALSE, the agent will not automatically register with + * NetworkManager, and nm_secret_agent_old_enable() or + * nm_secret_agent_old_register_async() must be called to register it. + * + * Calling nm_secret_agent_old_enable() has the same effect as setting this + * property. + **/ + obj_properties[PROP_AUTO_REGISTER] = + g_param_spec_boolean(NM_SECRET_AGENT_OLD_AUTO_REGISTER, + "", + "", + TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + + /** + * NMSecretAgentOld:registered: + * + * %TRUE if the agent is registered with NetworkManager, %FALSE if not. + **/ + obj_properties[PROP_REGISTERED] = + g_param_spec_boolean(NM_SECRET_AGENT_OLD_REGISTERED, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMSecretAgentOld:capabilities: + * + * A bitfield of %NMSecretAgentCapabilities. + * + * Changing this property is possible at any time. In case the secret + * agent is currently registered, this will cause a re-registration. + **/ + obj_properties[PROP_CAPABILITIES] = + g_param_spec_flags(NM_SECRET_AGENT_OLD_CAPABILITIES, + "", + "", + NM_TYPE_SECRET_AGENT_CAPABILITIES, + NM_SECRET_AGENT_CAPABILITY_NONE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} + +static void +nm_secret_agent_old_initable_iface_init(GInitableIface *iface) +{ + iface->init = init_sync; +} + +static void +nm_secret_agent_old_async_initable_iface_init(GAsyncInitableIface *iface) +{ + iface->init_async = init_async; + iface->init_finish = init_finish; +} diff --git a/src/libnm-client-impl/nm-settings-docs-gir.xml b/src/libnm-client-impl/nm-settings-docs-gir.xml new file mode 100644 index 00000000..cbbae7b6 --- /dev/null +++ b/src/libnm-client-impl/nm-settings-docs-gir.xml @@ -0,0 +1,532 @@ +<?xml version="1.0"?> +<!DOCTYPE nm-setting-docs [ +<!ENTITY quot """> +]> +<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. 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. If the connection is set to autoconnect, connections with higher priority will be preferred. Defaults to 0. The higher number means higher priority." /> + <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="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" default='"fabric"' description='The FCoE controller mode; either "fabric" (default) or "vn2vn".' /> + <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> + <setting name="ethtool" description="Ethtool Ethernet Settings" name_upper="ETHTOOL" > + </setting> + <setting name="generic" description="Generic Link Settings" name_upper="GENERIC" > + </setting> + <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="hostname" description="Hostname settings" name_upper="HOSTNAME" > + <property name="from-dhcp" name_upper="FROM_DHCP" type="NMTernary (int32)" description="Whether the system hostname can be determined from DHCP on this connection. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1)." /> + <property name="from-dns-lookup" name_upper="FROM_DNS_LOOKUP" type="NMTernary (int32)" description="Whether the system hostname can be determined from reverse DNS lookup of addresses on this device. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1)." /> + <property name="only-from-default" name_upper="ONLY_FROM_DEFAULT" type="NMTernary (int32)" description="If set to NM_TERNARY_TRUE (1), NetworkManager attempts to get the hostname via DHCPv4/DHCPv6 or reverse DNS lookup on this device only when the device has the default route for the given address family (IPv4/IPv6). If set to NM_TERNARY_FALSE (0), the hostname can be set from this device even if it doesn't have the default route. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_FALSE (0)." /> + <property name="priority" name_upper="PRIORITY" type="int32" default="0" description="The relative priority of this connection to determine the system hostname. A lower numerical value is better (higher priority). A connection with higher priority is considered before connections with lower priority. If the value is zero, it can be overridden by a global value from NetworkManager configuration. If the property doesn't have a value in the global configuration, the value is assumed to be 100. Negative values have the special effect of excluding other connections with a greater numerical priority value; so in presence of at least one negative priority, only connections with the lowest priority value will be used to determine the hostname." /> + </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="array of array of uint32" description="Array of IP addresses." /> + <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="array of array of uint32" description="Array of IP routes." /> + </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="array of legacy IPv6 address struct" description="Array of IP addresses." /> + <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="array of legacy IPv6 route struct" description="Array of IP routes." /> + <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." /> + </setting> + <setting name="ovs-external-ids" description="OVS External IDs Settings" name_upper="OVS_EXTERNAL_IDS" > + <property name="data" name_upper="DATA" type="dict of string to string" default="{}" description="A dictionary of key/value pairs with exernal-ids for OVS." /> + </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="array of vardict" 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." /> + <property name="tfilters" name_upper="TFILTERS" type="array of vardict" description="Array of TC traffic filters. When the "tc" setting is present, filters from this property are applied upon activation. If the property is empty, NetworkManager removes all the filters. If the "tc" setting is not present, NetworkManager doesn't touch the filters present on the interface." /> + </setting> + <setting name="team" description="Teaming Settings" name_upper="TEAM" > + <property name="config" name_upper="CONFIG" type="string" description="The JSON configuration for the team network interface. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details." /> + <property name="link-watchers" name_upper="LINK_WATCHERS" type="array of vardict" description="Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are: ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details." /> + <property name="mcast-rejoin-count" name_upper="MCAST_REJOIN_COUNT" type="int32" default="-1" description="Corresponds to the teamd mcast_rejoin.count." /> + <property name="mcast-rejoin-interval" name_upper="MCAST_REJOIN_INTERVAL" type="int32" default="-1" description="Corresponds to the teamd mcast_rejoin.interval." /> + <property name="notify-peers-count" name_upper="NOTIFY_PEERS_COUNT" type="int32" default="-1" description="Corresponds to the teamd notify_peers.count." /> + <property name="notify-peers-interval" name_upper="NOTIFY_PEERS_INTERVAL" type="int32" default="-1" description="Corresponds to the teamd notify_peers.interval." /> + <property name="runner" name_upper="RUNNER" type="string" description='Corresponds to the teamd runner.name. Permitted values are: "roundrobin", "broadcast", "activebackup", "loadbalance", "lacp", "random".' /> + <property name="runner-active" name_upper="RUNNER_ACTIVE" type="boolean" default="TRUE" description="Corresponds to the teamd runner.active." /> + <property name="runner-agg-select-policy" name_upper="RUNNER_AGG_SELECT_POLICY" type="string" description="Corresponds to the teamd runner.agg_select_policy." /> + <property name="runner-fast-rate" name_upper="RUNNER_FAST_RATE" type="boolean" default="FALSE" description="Corresponds to the teamd runner.fast_rate." /> + <property name="runner-hwaddr-policy" name_upper="RUNNER_HWADDR_POLICY" type="string" description="Corresponds to the teamd runner.hwaddr_policy." /> + <property name="runner-min-ports" name_upper="RUNNER_MIN_PORTS" type="int32" default="-1" description="Corresponds to the teamd runner.min_ports." /> + <property name="runner-sys-prio" name_upper="RUNNER_SYS_PRIO" type="int32" default="-1" description="Corresponds to the teamd runner.sys_prio." /> + <property name="runner-tx-balancer" name_upper="RUNNER_TX_BALANCER" type="string" description="Corresponds to the teamd runner.tx_balancer.name." /> + <property name="runner-tx-balancer-interval" name_upper="RUNNER_TX_BALANCER_INTERVAL" type="int32" default="-1" description="Corresponds to the teamd runner.tx_balancer.interval." /> + <property name="runner-tx-hash" name_upper="RUNNER_TX_HASH" type="array of string" description="Corresponds to the teamd runner.tx_hash." /> + </setting> + <setting name="team-port" description="Team Port Settings" name_upper="TEAM_PORT" > + <property name="config" name_upper="CONFIG" type="string" description="The JSON configuration for the team port. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details." /> + <property name="lacp-key" name_upper="LACP_KEY" type="int32" default="-1" description="Corresponds to the teamd ports.PORTIFNAME.lacp_key." /> + <property name="lacp-prio" name_upper="LACP_PRIO" type="int32" default="-1" description="Corresponds to the teamd ports.PORTIFNAME.lacp_prio." /> + <property name="link-watchers" name_upper="LINK_WATCHERS" type="array of vardict" description="Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are: ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details." /> + <property name="prio" name_upper="PRIO" type="int32" default="0" description="Corresponds to the teamd ports.PORTIFNAME.prio." /> + <property name="queue-id" name_upper="QUEUE_ID" type="int32" default="-1" description="Corresponds to the teamd ports.PORTIFNAME.queue_id. When set to -1 means the parameter is skipped from the json config." /> + <property name="sticky" name_upper="STICKY" type="boolean" default="FALSE" description="Corresponds to the teamd ports.PORTIFNAME.sticky." /> + </setting> + <setting name="tun" description="Tunnel Settings" name_upper="TUN" > + <property name="group" name_upper="GROUP" type="string" description="The group ID which will own the device. If set to NULL everyone will be able to use the device." /> + <property name="mode" name_upper="MODE" type="uint32" default="1" description="The operating mode of the virtual device. Allowed values are NM_SETTING_TUN_MODE_TUN (1) to create a layer 3 device and NM_SETTING_TUN_MODE_TAP (2) to create an Ethernet-like layer 2 one." /> + <property name="multi-queue" name_upper="MULTI_QUEUE" type="boolean" default="FALSE" description="If the property is set to TRUE, the interface will support multiple file descriptors (queues) to parallelize packet sending or receiving. Otherwise, the interface will only support a single queue." /> + <property name="owner" name_upper="OWNER" type="string" description="The user ID which will own the device. If set to NULL everyone will be able to use the device." /> + <property name="pi" name_upper="PI" type="boolean" default="FALSE" description="If TRUE the interface will prepend a 4 byte header describing the physical interface to the packets." /> + <property name="vnet-hdr" name_upper="VNET_HDR" type="boolean" default="FALSE" description="If TRUE the IFF_VNET_HDR the tunnel packets will include a virtio network header." /> + </setting> + <setting name="user" description="General User Profile Settings" name_upper="USER" > + <property name="data" name_upper="DATA" type="dict of string to string" default="{}" description="A dictionary of key/value pairs with user data. This data is ignored by NetworkManager and can be used at the users discretion. The keys only support a strict ascii format, but the values can be arbitrary UTF8 strings up to a certain length." /> + </setting> + <setting name="veth" description="Veth Settings" name_upper="VETH" > + <property name="peer" name_upper="PEER" type="string" description="This property specifies the peer interface name of the veth. This property is mandatory." /> + </setting> + <setting name="vlan" description="VLAN Settings" name_upper="VLAN" > + <property name="egress-priority-map" name_upper="EGRESS_PRIORITY_MAP" type="array of string" description='For outgoing packets, a list of mappings from Linux SKB priorities to 802.1p priorities. The mapping is given in the format "from:to" where both "from" and "to" are unsigned integers, ie "7:3".' /> + <property name="flags" name_upper="FLAGS" type="NMVlanFlags (uint32)" description="One or more flags which control the behavior and features of the VLAN interface. Flags include NM_VLAN_FLAG_REORDER_HEADERS (0x1) (reordering of output packet headers), NM_VLAN_FLAG_GVRP (0x2) (use of the GVRP protocol), and NM_VLAN_FLAG_LOOSE_BINDING (0x4) (loose binding of the interface to its master device's operating state). NM_VLAN_FLAG_MVRP (0x8) (use of the MVRP protocol). The default value of this property is NM_VLAN_FLAG_REORDER_HEADERS, but it used to be 0. To preserve backward compatibility, the default-value in the D-Bus API continues to be 0 and a missing property on D-Bus is still considered as 0." /> + <property name="id" name_upper="ID" type="uint32" default="0" description="The VLAN identifier that the interface created by this connection should be assigned. The valid range is from 0 to 4094, without the reserved id 4095." /> + <property name="ingress-priority-map" name_upper="INGRESS_PRIORITY_MAP" type="array of string" description='For incoming packets, a list of mappings from 802.1p priorities to Linux SKB priorities. The mapping is given in the format "from:to" where both "from" and "to" are unsigned integers, ie "7:3".' /> + <property name="parent" name_upper="PARENT" type="string" description='If given, specifies the parent interface name or parent connection UUID from which this 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.' /> + </setting> + <setting name="vpn" description="VPN Settings" name_upper="VPN" > + <property name="data" name_upper="DATA" type="dict of string to string" default="{}" description="Dictionary of key/value pairs of VPN plugin specific data. Both keys and values must be strings." /> + <property name="persistent" name_upper="PERSISTENT" type="boolean" default="FALSE" description="If the VPN service supports persistence, and this property is TRUE, the VPN will attempt to stay connected across link changes and outages, until explicitly disconnected." /> + <property name="secrets" name_upper="SECRETS" type="dict of string to string" default="{}" description="Dictionary of key/value pairs of VPN plugin specific secrets like passwords or private keys. Both keys and values must be strings." /> + <property name="service-type" name_upper="SERVICE_TYPE" type="string" description="D-Bus service name of the VPN plugin that this setting uses to connect to its network. i.e. org.freedesktop.NetworkManager.vpnc for the vpnc plugin." /> + <property name="timeout" name_upper="TIMEOUT" type="uint32" default="0" description="Timeout for the VPN service to establish the connection. Some services may take quite a long time to connect. Value of 0 means a default timeout, which is 60 seconds (unless overridden by vpn.timeout in configuration file). Values greater than zero mean timeout in seconds." /> + <property name="user-name" name_upper="USER_NAME" type="string" description="If the VPN connection requires a user name for authentication, that name should be provided here. If the connection is available to more than one user, and the VPN requires each user to supply a different name, then leave this property empty. If this property is empty, NetworkManager will automatically supply the username of the user which requested the VPN connection." /> + </setting> + <setting name="vrf" description="VRF settings" name_upper="VRF" > + <property name="table" name_upper="TABLE" type="uint32" default="0" description="The routing table for this VRF." /> + </setting> + <setting name="vxlan" description="VXLAN Settings" name_upper="VXLAN" > + <property name="ageing" name_upper="AGEING" type="uint32" default="300" description="Specifies the lifetime in seconds of FDB entries learnt by the kernel." /> + <property name="destination-port" name_upper="DESTINATION_PORT" type="uint32" default="8472" description="Specifies the UDP destination port to communicate to the remote VXLAN tunnel endpoint." /> + <property name="id" name_upper="ID" type="uint32" default="0" description="Specifies the VXLAN Network Identifier (or VXLAN Segment Identifier) to use." /> + <property name="l2-miss" name_upper="L2_MISS" type="boolean" default="FALSE" description="Specifies whether netlink LL ADDR miss notifications are generated." /> + <property name="l3-miss" name_upper="L3_MISS" type="boolean" default="FALSE" description="Specifies whether netlink IP ADDR miss notifications are generated." /> + <property name="learning" name_upper="LEARNING" type="boolean" default="TRUE" description="Specifies whether unknown source link layer addresses and IP addresses are entered into the VXLAN device forwarding database." /> + <property name="limit" name_upper="LIMIT" type="uint32" default="0" description="Specifies the maximum number of FDB entries. A value of zero means that the kernel will store unlimited entries." /> + <property name="local" name_upper="LOCAL" type="string" description="If given, specifies the source IP address to use in outgoing packets." /> + <property name="parent" name_upper="PARENT" type="string" description="If given, specifies the parent interface name or parent connection UUID." /> + <property name="proxy" name_upper="PROXY" type="boolean" default="FALSE" description="Specifies whether ARP proxy is turned on." /> + <property name="remote" name_upper="REMOTE" type="string" description="Specifies the unicast destination IP address to use in outgoing packets when the destination link layer address is not known in the VXLAN device forwarding database, or the multicast IP address to join." /> + <property name="rsc" name_upper="RSC" type="boolean" default="FALSE" description="Specifies whether route short circuit is turned on." /> + <property name="source-port-max" name_upper="SOURCE_PORT_MAX" type="uint32" default="0" description="Specifies the maximum UDP source port to communicate to the remote VXLAN tunnel endpoint." /> + <property name="source-port-min" name_upper="SOURCE_PORT_MIN" type="uint32" default="0" description="Specifies the minimum UDP source port to communicate to the remote VXLAN tunnel endpoint." /> + <property name="tos" name_upper="TOS" type="uint32" default="0" description="Specifies the TOS value to use in outgoing packets." /> + <property name="ttl" name_upper="TTL" type="uint32" default="0" description="Specifies the time-to-live value to use in outgoing packets." /> + </setting> + <setting name="wifi-p2p" description="Wi-Fi P2P Settings" name_upper="WIFI_P2P" > + <property name="peer" name_upper="PEER" type="string" description="The P2P device that should be connected to. Currently, this is the only way to create or join a group." /> + <property name="wfd-ies" name_upper="WFD_IES" type="byte array" description="The Wi-Fi Display (WFD) Information Elements (IEs) to set. Wi-Fi Display requires a protocol specific information element to be set in certain Wi-Fi frames. These can be specified here for the purpose of establishing a connection. This setting is only useful when implementing a Wi-Fi Display client." /> + <property name="wps-method" name_upper="WPS_METHOD" type="uint32" default="0" description="Flags indicating which mode of WPS is to be used. There's little point in changing the default setting as NetworkManager will automatically determine the best method to use." /> + </setting> + <setting name="wimax" description="WiMax Settings" name_upper="WIMAX" > + <property name="mac-address" name_upper="MAC_ADDRESS" type="byte array" description="If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing). Deprecated: 1" /> + <property name="network-name" name_upper="NETWORK_NAME" type="string" description="Network Service Provider (NSP) name of the WiMAX network this connection should use. Deprecated: 1" /> + </setting> + <setting name="802-3-ethernet" description="Wired Ethernet Settings" name_upper="WIRED" > + <property name="accept-all-mac-addresses" name_upper="ACCEPT_ALL_MAC_ADDRESSES" type="NMTernary (int32)" description="When TRUE, setup the interface to accept packets for all MAC addresses. This is enabling the kernel interface flag IFF_PROMISC. When FALSE, the interface will only accept the packets with the interface destination mac address or broadcast." /> + <property name="auto-negotiate" name_upper="AUTO_NEGOTIATE" type="boolean" default="FALSE" description='When TRUE, enforce auto-negotiation of speed and duplex mode. If "speed" and "duplex" properties are both specified, only that single mode will be advertised and accepted during the link auto-negotiation process: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabits modes, as in these cases link negotiation is mandatory. When FALSE, "speed" and "duplex" properties should be both set or link configuration will be skipped.' /> + <property name="cloned-mac-address" name_upper="CLONED_MAC_ADDRESS" type="byte array" description='If specified, request that the device use this MAC address instead. This is known as MAC cloning or spoofing. Beside explicitly specifying a MAC address, the special values "preserve", "permanent", "random" and "stable" are supported. "preserve" means not to touch the MAC address on activation. "permanent" means to use the permanent hardware address if the device has one (otherwise this is treated as "preserve"). "random" creates a random MAC address on each connect. "stable" creates a hashed MAC address based on connection.stable-id and a machine dependent key. If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to "preserve" (older versions of NetworkManager may use a different default value). On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated "cloned-mac-address".' /> + <property name="duplex" name_upper="DUPLEX" type="string" description='When a value is set, either "half" or "full", configures the device to use the specified duplex mode. If "auto-negotiate" is "yes" the specified duplex mode will be the only one advertised during link negotiation: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabits modes, as in these cases link negotiation is mandatory. If the value is unset (the default), the link configuration will be either skipped (if "auto-negotiate" is "no", the default) or will be auto-negotiated (if "auto-negotiate" is "yes") and the local device will advertise all the supported duplex modes. Must be set together with the "speed" property if specified. Before specifying a duplex mode be sure your device supports it.' /> + <property name="generate-mac-address-mask" name_upper="GENERATE_MAC_ADDRESS_MASK" type="string" description='With "cloned-mac-address" setting "random" or "stable", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address. If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address. If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the "random" or "stable" algorithm. If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled globally-administered, burned-in MAC address. If the value contains more than one additional MAC addresses, one of them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" will create a fully scrambled MAC address, randomly locally or globally administered.' /> + <property name="mac-address" name_upper="MAC_ADDRESS" type="byte array" description="If specified, this connection will only apply to the Ethernet device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing)." /> + <property name="mac-address-blacklist" name_upper="MAC_ADDRESS_BLACKLIST" type="array of string" description="If specified, this connection will never apply to the Ethernet device whose permanent MAC address matches an address in the list. Each MAC address is in the standard hex-digits-and-colons notation (00:11:22:33:44:55)." /> + <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 Ethernet frames." /> + <property name="port" name_upper="PORT" type="string" description='Specific port type to use if the device supports multiple attachment methods. One of "tp" (Twisted Pair), "aui" (Attachment Unit Interface), "bnc" (Thin Ethernet) or "mii" (Media Independent Interface). If the device supports only one port type, this setting is ignored.' /> + <property name="s390-nettype" name_upper="S390_NETTYPE" type="string" description='s390 network device type; one of "qeth", "lcs", or "ctc", representing the different types of virtual network devices available on s390 systems.' /> + <property name="s390-options" name_upper="S390_OPTIONS" type="dict of string to string" default="{}" description='Dictionary of key/value pairs of s390-specific device options. Both keys and values must be strings. Allowed keys include "portno", "layer2", "portname", "protocol", among others. Key names must contain only alphanumeric characters (ie, [a-zA-Z0-9]).' /> + <property name="s390-subchannels" name_upper="S390_SUBCHANNELS" type="array of string" description='Identifies specific subchannels that this network device uses for communication with z/VM or s390 host. Like the "mac-address" property for non-z/VM devices, this property can be used to ensure this connection only applies to the network device that uses these subchannels. The list should contain exactly 3 strings, and each string may only be composed of hexadecimal characters and the period (.) character.' /> + <property name="speed" name_upper="SPEED" type="uint32" default="0" description='When a value greater than 0 is set, configures the device to use the specified speed. If "auto-negotiate" is "yes" the specified speed will be the only one advertised during link negotiation: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabit speeds, as in this case link negotiation is mandatory. If the value is unset (0, the default), the link configuration will be either skipped (if "auto-negotiate" is "no", the default) or will be auto-negotiated (if "auto-negotiate" is "yes") and the local device will advertise all the supported speeds. In Mbit/s, ie 100 == 100Mbit/s. Must be set together with the "duplex" property when non-zero. Before specifying a speed value be sure your device supports it.' /> + <property name="wake-on-lan" name_upper="WAKE_ON_LAN" type="uint32" default="1" description="The NMSettingWiredWakeOnLan options to enable. Not all devices support all options. May be any combination of 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) or the special values NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT (0x1) (to use global settings) and NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE (0x8000) (to disable management of Wake-on-LAN in NetworkManager)." /> + <property name="wake-on-lan-password" name_upper="WAKE_ON_LAN_PASSWORD" type="string" description="If specified, the password used with magic-packet-based Wake-on-LAN, represented as an Ethernet MAC address. If NULL, no password will be required." /> + </setting> + <setting name="wireguard" description="WireGuard Settings" name_upper="WIREGUARD" > + <property name="fwmark" name_upper="FWMARK" type="uint32" default="0" description='The use of fwmark is optional and is by default off. Setting it to 0 disables it. Otherwise, it is a 32-bit fwmark for outgoing packets. Note that "ip4-auto-default-route" or "ip6-auto-default-route" enabled, implies to automatically choose a fwmark.' /> + <property name="ip4-auto-default-route" name_upper="IP4_AUTO_DEFAULT_ROUTE" type="NMTernary (int32)" description="Whether to enable special handling of the IPv4 default route. If enabled, the IPv4 default route from wireguard.peer-routes will be placed to a dedicated routing-table and two policy routing rules will be added. The fwmark number is also used as routing-table for the default-route, and if fwmark is zero, an unused fwmark/table is chosen automatically. This corresponds to what wg-quick does with Table=auto and what WireGuard calls "Improved Rule-based Routing". Note that for this automatism to work, you usually don't want to set ipv4.gateway, because that will result in a conflicting default route. Leaving this at the default will enable this option automatically if ipv4.never-default is not set and there are any peers that use a default-route as allowed-ips." /> + <property name="ip6-auto-default-route" name_upper="IP6_AUTO_DEFAULT_ROUTE" type="NMTernary (int32)" description="Like ip4-auto-default-route, but for the IPv6 default route." /> + <property name="listen-port" name_upper="LISTEN_PORT" type="uint32" default="0" description="The listen-port. If listen-port is not specified, the port will be chosen randomly when the interface comes up." /> + <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. If zero a default MTU is used. Note that contrary to wg-quick's MTU setting, this does not take into account the current routes at the time of activation." /> + <property name="peer-routes" name_upper="PEER_ROUTES" type="boolean" default="TRUE" description="Whether to automatically add routes for the AllowedIPs ranges of the peers. If TRUE (the default), NetworkManager will automatically add routes in the routing tables according to ipv4.route-table and ipv6.route-table. Usually you want this automatism enabled. If FALSE, no such routes are added automatically. In this case, the user may want to configure static routes in ipv4.routes and ipv6.routes, respectively. Note that if the peer's AllowedIPs is "0.0.0.0/0" or "::/0" and the profile's ipv4.never-default or ipv6.never-default setting is enabled, the peer route for this peer won't be added automatically." /> + <property name="private-key" name_upper="PRIVATE_KEY" type="string" description="The 256 bit private-key in base64 encoding." /> + <property name="private-key-flags" name_upper="PRIVATE_KEY_FLAGS" type="NMSettingSecretFlags (uint32)" description='Flags indicating how to handle the "private-key" property.' /> + </setting> + <setting name="802-11-wireless" description="Wi-Fi Settings" name_upper="WIRELESS" > + <property name="ap-isolation" name_upper="AP_ISOLATION" type="NMTernary (int32)" description="Configures AP isolation, which prevents communication between wireless devices connected to this AP. This property can be set to a value different from NM_TERNARY_DEFAULT (-1) only when the interface is configured in AP mode. If set to NM_TERNARY_TRUE (1), devices are not able to communicate with each other. This increases security because it protects devices against attacks from other clients in the network. At the same time, it prevents devices to access resources on the same wireless networks as file shares, printers, etc. If set to NM_TERNARY_FALSE (0), devices can talk to each other. 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_FALSE (0)." /> + <property name="band" name_upper="BAND" type="string" description="802.11 frequency band of the network. One of "a" for 5GHz 802.11a or "bg" for 2.4GHz 802.11. This will lock associations to the Wi-Fi network to the specific band, i.e. if "a" is specified, the device will not associate with the same network in the 2.4GHz band even if the network's settings are compatible. This setting depends on specific driver capability and may not work with all drivers." /> + <property name="bssid" name_upper="BSSID" type="byte array" description="If specified, directs the device to only associate with the given access point. This capability is highly driver dependent and not supported by all devices. Note: this property does not control the BSSID used when creating an Ad-Hoc network and is unlikely to in the future." /> + <property name="channel" name_upper="CHANNEL" type="uint32" default="0" description='Wireless channel to use for the Wi-Fi connection. The device will only join (or create for Ad-Hoc networks) a Wi-Fi network on the specified channel. Because channel numbers overlap between bands, this property also requires the "band" property to be set.' /> + <property name="cloned-mac-address" name_upper="CLONED_MAC_ADDRESS" type="byte array" description='If specified, request that the device use this MAC address instead. This is known as MAC cloning or spoofing. Beside explicitly specifying a MAC address, the special values "preserve", "permanent", "random" and "stable" are supported. "preserve" means not to touch the MAC address on activation. "permanent" means to use the permanent hardware address of the device. "random" creates a random MAC address on each connect. "stable" creates a hashed MAC address based on connection.stable-id and a machine dependent key. If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to "preserve" (older versions of NetworkManager may use a different default value). On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated "cloned-mac-address".' /> + <property name="generate-mac-address-mask" name_upper="GENERATE_MAC_ADDRESS_MASK" type="string" description='With "cloned-mac-address" setting "random" or "stable", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address. If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address. If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the "random" or "stable" algorithm. If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled globally-administered, burned-in MAC address. If the value contains more than one additional MAC addresses, one of them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" will create a fully scrambled MAC address, randomly locally or globally administered.' /> + <property name="hidden" name_upper="HIDDEN" type="boolean" default="FALSE" description="If TRUE, indicates that the network is a non-broadcasting network that hides its SSID. This works both in infrastructure and AP mode. In infrastructure mode, various workarounds are used for a more reliable discovery of hidden networks, such as probe-scanning the SSID. However, these workarounds expose inherent insecurities with hidden SSID networks, and thus hidden SSID networks should be used with caution. In AP mode, the created network does not broadcast its SSID. Note that marking the network as hidden may be a privacy issue for you (in infrastructure mode) or client stations (in AP mode), as the explicit probe-scans are distinctly recognizable on the air." /> + <property name="mac-address" name_upper="MAC_ADDRESS" type="byte array" description="If specified, this connection will only apply to the Wi-Fi device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing)." /> + <property name="mac-address-blacklist" name_upper="MAC_ADDRESS_BLACKLIST" type="array of string" description='A list of permanent MAC addresses of Wi-Fi devices to which this connection should never apply. Each MAC address should be given in the standard hex-digits-and-colons notation (eg "00:11:22:33:44:55").' /> + <property name="mac-address-randomization" name_upper="MAC_ADDRESS_RANDOMIZATION" type="uint32" default="0" description="One of NM_SETTING_MAC_RANDOMIZATION_DEFAULT (0) (never randomize unless the user has set a global default to randomize and the supplicant supports randomization), NM_SETTING_MAC_RANDOMIZATION_NEVER (1) (never randomize the MAC address), or NM_SETTING_MAC_RANDOMIZATION_ALWAYS (2) (always randomize the MAC address). This property is deprecated for 'cloned-mac-address'. Deprecated: 1" /> + <property name="mode" name_upper="MODE" type="string" description='Wi-Fi network mode; one of "infrastructure", "mesh", "adhoc" or "ap". If blank, infrastructure is assumed.' /> + <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 Ethernet frames." /> + <property name="powersave" name_upper="POWERSAVE" type="uint32" default="0" description="One of NM_SETTING_WIRELESS_POWERSAVE_DISABLE (2) (disable Wi-Fi power saving), NM_SETTING_WIRELESS_POWERSAVE_ENABLE (3) (enable Wi-Fi power saving), NM_SETTING_WIRELESS_POWERSAVE_IGNORE (1) (don't touch currently configure setting) or NM_SETTING_WIRELESS_POWERSAVE_DEFAULT (0) (use the globally configured value). All other values are reserved." /> + <property name="rate" name_upper="RATE" type="uint32" default="0" description="If non-zero, directs the device to only use the specified bitrate for communication with the access point. Units are in Kb/s, ie 5500 = 5.5 Mbit/s. This property is highly driver dependent and not all devices support setting a static bitrate." /> + <property name="seen-bssids" name_upper="SEEN_BSSIDS" type="array of string" description='A list of BSSIDs (each BSSID formatted as a MAC address like "00:11:22:33:44:55") that have been detected as part of the Wi-Fi network. NetworkManager internally tracks previously seen BSSIDs. The property is only meant for reading and reflects the BSSID list of NetworkManager. The changes you make to this property will not be preserved.' /> + <property name="ssid" name_upper="SSID" type="byte array" description="SSID of the Wi-Fi network. Must be specified." /> + <property name="tx-power" name_upper="TX_POWER" type="uint32" default="0" description="If non-zero, directs the device to use the specified transmit power. Units are dBm. This property is highly driver dependent and not all devices support setting a static transmit power." /> + <property name="wake-on-wlan" name_upper="WAKE_ON_WLAN" type="uint32" default="1" description="The NMSettingWirelessWakeOnWLan options to enable. Not all devices support all options. May be any combination of NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY (0x2), NM_SETTING_WIRELESS_WAKE_ON_WLAN_DISCONNECT (0x4), NM_SETTING_WIRELESS_WAKE_ON_WLAN_MAGIC (0x8), NM_SETTING_WIRELESS_WAKE_ON_WLAN_GTK_REKEY_FAILURE (0x10), NM_SETTING_WIRELESS_WAKE_ON_WLAN_EAP_IDENTITY_REQUEST (0x20), NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE (0x40), NM_SETTING_WIRELESS_WAKE_ON_WLAN_RFKILL_RELEASE (0x80), NM_SETTING_WIRELESS_WAKE_ON_WLAN_TCP (0x100) or the special values NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT (0x1) (to use global settings) and NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE (0x8000) (to disable management of Wake-on-LAN in NetworkManager)." /> + </setting> + <setting name="802-11-wireless-security" description="Wi-Fi Security Settings" name_upper="WIRELESS_SECURITY" > + <property name="auth-alg" name_upper="AUTH_ALG" type="string" description='When WEP is used (ie, key-mgmt = "none" or "ieee8021x") indicate the 802.11 authentication algorithm required by the AP here. One of "open" for Open System, "shared" for Shared Key, or "leap" for Cisco LEAP. When using Cisco LEAP (ie, key-mgmt = "ieee8021x" and auth-alg = "leap") the "leap-username" and "leap-password" properties must be specified.' /> + <property name="fils" name_upper="FILS" type="int32" default="0" description="Indicates whether Fast Initial Link Setup (802.11ai) must be enabled for the connection. One of NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) (use global default value), NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE (1) (disable FILS), NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL (2) (enable FILS if the supplicant and the access point support it) or NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED (3) (enable FILS and fail if not supported). When set to NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) and no global default is set, FILS will be optionally enabled." /> + <property name="group" name_upper="GROUP" type="array of string" description='A list of group/broadcast encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list. For maximum compatibility leave this property empty. Each list element may be one of "wep40", "wep104", "tkip", or "ccmp".' /> + <property name="key-mgmt" name_upper="KEY_MGMT" type="string" description='Key management used for the connection. One of "none" (WEP or no password protection), "ieee8021x" (Dynamic WEP), "owe" (Opportunistic Wireless Encryption), "wpa-psk" (WPA2 + WPA3 personal), "sae" (WPA3 personal only), "wpa-eap" (WPA2 + WPA3 enterprise) or "wpa-eap-suite-b-192" (WPA3 enterprise only). This property must be set for any Wi-Fi connection that uses security.' /> + <property name="leap-password" name_upper="LEAP_PASSWORD" type="string" description='The login password for legacy LEAP connections (ie, key-mgmt = "ieee8021x" and auth-alg = "leap").' /> + <property name="leap-password-flags" name_upper="LEAP_PASSWORD_FLAGS" type="NMSettingSecretFlags (uint32)" description='Flags indicating how to handle the "leap-password" property.' /> + <property name="leap-username" name_upper="LEAP_USERNAME" type="string" description='The login username for legacy LEAP connections (ie, key-mgmt = "ieee8021x" and auth-alg = "leap").' /> + <property name="pairwise" name_upper="PAIRWISE" type="array of string" description='A list of pairwise encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list. For maximum compatibility leave this property empty. Each list element may be one of "tkip" or "ccmp".' /> + <property name="pmf" name_upper="PMF" type="int32" default="0" description="Indicates whether Protected Management Frames (802.11w) must be enabled for the connection. One of NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) (use global default value), NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE (1) (disable PMF), NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL (2) (enable PMF if the supplicant and the access point support it) or NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED (3) (enable PMF and fail if not supported). When set to NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) and no global default is set, PMF will be optionally enabled." /> + <property name="proto" name_upper="PROTO" type="array of string" description='List of strings specifying the allowed WPA protocol versions to use. Each element may be one "wpa" (allow WPA) or "rsn" (allow WPA2/RSN). If not specified, both WPA and RSN connections are allowed.' /> + <property name="psk" name_upper="PSK" type="string" description="Pre-Shared-Key for WPA networks. For WPA-PSK, it's either an ASCII passphrase of 8 to 63 characters that is (as specified in the 802.11i standard) hashed to derive the actual key, or the key in form of 64 hexadecimal character. The WPA3-Personal networks use a passphrase of any length for SAE authentication." /> + <property name="psk-flags" name_upper="PSK_FLAGS" type="NMSettingSecretFlags (uint32)" description='Flags indicating how to handle the "psk" property.' /> + <property name="wep-key-flags" name_upper="WEP_KEY_FLAGS" type="NMSettingSecretFlags (uint32)" description='Flags indicating how to handle the "wep-key0", "wep-key1", "wep-key2", and "wep-key3" properties.' /> + <property name="wep-key-type" name_upper="WEP_KEY_TYPE" type="NMWepKeyType (uint32)" description="Controls the interpretation of WEP keys. Allowed values are NM_WEP_KEY_TYPE_KEY (1), in which case the key is either a 10- or 26-character hexadecimal string, or a 5- or 13-character ASCII password; or NM_WEP_KEY_TYPE_PASSPHRASE (2), in which case the passphrase is provided as a string and will be hashed using the de-facto MD5 method to derive the actual WEP key." /> + <property name="wep-key0" name_upper="WEP_KEY0" type="string" description='Index 0 WEP key. This is the WEP key used in most networks. See the "wep-key-type" property for a description of how this key is interpreted.' /> + <property name="wep-key1" name_upper="WEP_KEY1" type="string" description='Index 1 WEP key. This WEP index is not used by most networks. See the "wep-key-type" property for a description of how this key is interpreted.' /> + <property name="wep-key2" name_upper="WEP_KEY2" type="string" description='Index 2 WEP key. This WEP index is not used by most networks. See the "wep-key-type" property for a description of how this key is interpreted.' /> + <property name="wep-key3" name_upper="WEP_KEY3" type="string" description='Index 3 WEP key. This WEP index is not used by most networks. See the "wep-key-type" property for a description of how this key is interpreted.' /> + <property name="wep-tx-keyidx" name_upper="WEP_TX_KEYIDX" type="uint32" default="0" description='When static WEP is used (ie, key-mgmt = "none") and a non-default WEP key index is used by the AP, put that WEP key index here. Valid values are 0 (default key) through 3. Note that some consumer access points (like the Linksys WRT54G) number the keys 1 - 4.' /> + <property name="wps-method" name_upper="WPS_METHOD" type="uint32" default="0" description="Flags indicating which mode of WPS is to be used if any. There's little point in changing the default setting as NetworkManager will automatically determine whether it's feasible to start WPS enrollment from the Access Point capabilities. WPS can be disabled by setting this property to a value of 1." /> + </setting> + <setting name="wpan" description="IEEE 802.15.4 (WPAN) MAC Settings" name_upper="WPAN" > + <property name="channel" name_upper="CHANNEL" type="int32" default="-1" description='IEEE 802.15.4 channel. A positive integer or -1, meaning "do not set, use whatever the device is already set to".' /> + <property name="mac-address" name_upper="MAC_ADDRESS" type="string" description="If specified, this connection will only apply to the IEEE 802.15.4 (WPAN) MAC layer device whose permanent MAC address matches." /> + <property name="page" name_upper="PAGE" type="int32" default="-1" description='IEEE 802.15.4 channel page. A positive integer or -1, meaning "do not set, use whatever the device is already set to".' /> + <property name="pan-id" name_upper="PAN_ID" type="uint32" default="65535" description="IEEE 802.15.4 Personal Area Network (PAN) identifier." /> + <property name="short-address" name_upper="SHORT_ADDRESS" type="uint32" default="65535" description="Short IEEE 802.15.4 address to be used within a restricted environment." /> + </setting> +</nm-setting-docs> diff --git a/src/libnm-client-impl/nm-vpn-connection.c b/src/libnm-client-impl/nm-vpn-connection.c new file mode 100644 index 00000000..fb061532 --- /dev/null +++ b/src/libnm-client-impl/nm-vpn-connection.c @@ -0,0 +1,223 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-vpn-connection.h" + +#include "nm-dbus-interface.h" +#include "nm-utils.h" +#include "nm-object-private.h" +#include "nm-active-connection.h" +#include "nm-dbus-helpers.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMVpnConnection, PROP_VPN_STATE, PROP_BANNER, ); + +enum { + VPN_STATE_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +typedef struct { + char * banner; + guint32 vpn_state; + guint32 reason; +} NMVpnConnectionPrivate; + +struct _NMVpnConnection { + NMActiveConnection parent; + NMVpnConnectionPrivate _priv; +}; + +struct _NMVpnConnectionClass { + NMActiveConnectionClass parent; +}; + +G_DEFINE_TYPE(NMVpnConnection, nm_vpn_connection, NM_TYPE_ACTIVE_CONNECTION) + +#define NM_VPN_CONNECTION_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMVpnConnection, NM_IS_VPN_CONNECTION, NMObject, NMActiveConnection) + +G_STATIC_ASSERT(sizeof(NMVpnConnectionStateReason) == sizeof(NMActiveConnectionStateReason)); + +/*****************************************************************************/ + +/** + * nm_vpn_connection_get_banner: + * @vpn: a #NMVpnConnection + * + * Gets the VPN login banner of the active #NMVpnConnection. + * + * Returns: the VPN login banner of the VPN connection. This is the internal + * string used by the connection, and must not be modified. + **/ +const char * +nm_vpn_connection_get_banner(NMVpnConnection *vpn) +{ + g_return_val_if_fail(NM_IS_VPN_CONNECTION(vpn), NULL); + + return _nml_coerce_property_str_not_empty(NM_VPN_CONNECTION_GET_PRIVATE(vpn)->banner); +} + +/** + * nm_vpn_connection_get_vpn_state: + * @vpn: a #NMVpnConnection + * + * Gets the current #NMVpnConnection state. + * + * Returns: the VPN state of the active VPN connection. + **/ +NMVpnConnectionState +nm_vpn_connection_get_vpn_state(NMVpnConnection *vpn) +{ + g_return_val_if_fail(NM_IS_VPN_CONNECTION(vpn), NM_VPN_CONNECTION_STATE_UNKNOWN); + + return NM_VPN_CONNECTION_GET_PRIVATE(vpn)->vpn_state; +} + +/*****************************************************************************/ + +static void +_notify_event_state_changed(NMClient *client, NMClientNotifyEventWithPtr *notify_event) +{ + gs_unref_object NMVpnConnection *self = notify_event->user_data; + NMVpnConnectionPrivate * priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + /* we expose here the value cache in @priv. In practice, this is the same + * value as we received from the signal. In the unexpected case where they + * differ, the cached value of the current instance would still be more correct. */ + g_signal_emit(self, + signals[VPN_STATE_CHANGED], + 0, + (guint) priv->vpn_state, + (guint) priv->reason); +} + +void +_nm_vpn_connection_state_changed_commit(NMVpnConnection *self, guint32 state, guint32 reason) +{ + NMClient * client; + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + client = _nm_object_get_client(self); + + if (priv->vpn_state != state) { + priv->vpn_state = state; + _nm_client_queue_notify_object(client, self, obj_properties[PROP_VPN_STATE]); + } + + priv->reason = reason; + + _nm_client_notify_event_queue_with_ptr(client, + NM_CLIENT_NOTIFY_EVENT_PRIO_GPROP + 1, + _notify_event_state_changed, + g_object_ref(self)); +} + +/*****************************************************************************/ + +static void +nm_vpn_connection_init(NMVpnConnection *connection) +{} + +static void +finalize(GObject *object) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(object); + + g_free(priv->banner); + + G_OBJECT_CLASS(nm_vpn_connection_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMVpnConnection *self = NM_VPN_CONNECTION(object); + + switch (prop_id) { + case PROP_VPN_STATE: + g_value_set_enum(value, nm_vpn_connection_get_vpn_state(self)); + break; + case PROP_BANNER: + g_value_set_string(value, nm_vpn_connection_get_banner(self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_vpn_connection = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_VPN_CONNECTION, + nm_vpn_connection_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_S("Banner", PROP_BANNER, NMVpnConnection, _priv.banner), + NML_DBUS_META_PROPERTY_INIT_U("VpnState", + PROP_VPN_STATE, + NMVpnConnection, + _priv.vpn_state), ), ); + +static void +nm_vpn_connection_class_init(NMVpnConnectionClass *connection_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(connection_class); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + /** + * NMVpnConnection:vpn-state: + * + * The VPN state of the active VPN connection. + **/ + obj_properties[PROP_VPN_STATE] = g_param_spec_enum(NM_VPN_CONNECTION_VPN_STATE, + "", + "", + NM_TYPE_VPN_CONNECTION_STATE, + NM_VPN_CONNECTION_STATE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMVpnConnection:banner: + * + * The VPN login banner of the active VPN connection. + **/ + obj_properties[PROP_BANNER] = g_param_spec_string(NM_VPN_CONNECTION_BANNER, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_vpn_connection); + + /* TODO: the state reason should also be exposed as a property in libnm's NMVpnConnection, + * like done for NMDevice's state reason. */ + + /* TODO: the D-Bus API should also expose the state-reason as a property instead of + * a "VpnStateChanged" signal. Like done for Device's "StateReason". */ + + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + signals[VPN_STATE_CHANGED] = g_signal_new("vpn-state-changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 2, + G_TYPE_UINT, + G_TYPE_UINT); + G_GNUC_END_IGNORE_DEPRECATIONS +} diff --git a/src/libnm-client-impl/nm-vpn-editor.c b/src/libnm-client-impl/nm-vpn-editor.c new file mode 100644 index 00000000..57aa8b02 --- /dev/null +++ b/src/libnm-client-impl/nm-vpn-editor.c @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2008 - 2010 Red Hat, Inc. + * Copyright (C) 2008 Novell, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-vpn-editor.h" + +static void nm_vpn_editor_default_init(NMVpnEditorInterface *iface); + +G_DEFINE_INTERFACE(NMVpnEditor, nm_vpn_editor, G_TYPE_OBJECT) + +static void +nm_vpn_editor_default_init(NMVpnEditorInterface *iface) +{ + GType iface_type = G_TYPE_FROM_INTERFACE(iface); + + /* Signals */ + g_signal_new("changed", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnEditorInterface, changed), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + +/** + * nm_vpn_editor_get_widget: + * @editor: the #NMVpnEditor + * + * Returns: (transfer none): + */ +GObject * +nm_vpn_editor_get_widget(NMVpnEditor *editor) +{ + g_return_val_if_fail(NM_IS_VPN_EDITOR(editor), NULL); + + return NM_VPN_EDITOR_GET_INTERFACE(editor)->get_widget(editor); +} + +gboolean +nm_vpn_editor_update_connection(NMVpnEditor *editor, NMConnection *connection, GError **error) +{ + g_return_val_if_fail(NM_IS_VPN_EDITOR(editor), FALSE); + + if (error) + g_return_val_if_fail(*error == NULL, FALSE); + + return NM_VPN_EDITOR_GET_INTERFACE(editor)->update_connection(editor, connection, error); +} diff --git a/src/libnm-client-impl/nm-vpn-plugin-old.c b/src/libnm-client-impl/nm-vpn-plugin-old.c new file mode 100644 index 00000000..f60bf19c --- /dev/null +++ b/src/libnm-client-impl/nm-vpn-plugin-old.c @@ -0,0 +1,1135 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2008 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-vpn-plugin-old.h" + +#include <signal.h> +#include <stdlib.h> + +#include "nm-enum-types.h" +#include "nm-utils.h" +#include "nm-connection.h" +#include "nm-dbus-helpers.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-simple-connection.h" +#include "nm-vpn-service-plugin.h" + +#include "introspection/org.freedesktop.NetworkManager.VPN.Plugin.h" + +#define NM_VPN_PLUGIN_OLD_QUIT_TIMER 180 + +static void nm_vpn_plugin_old_initable_iface_init(GInitableIface *iface); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE(NMVpnPluginOld, + nm_vpn_plugin_old, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, + nm_vpn_plugin_old_initable_iface_init);) + +typedef struct { + NMVpnServiceState state; + + /* DBUS-y stuff */ + GDBusConnection *connection; + NMDBusVpnPlugin *dbus_vpn_plugin_old; + char * dbus_service_name; + + /* Temporary stuff */ + guint connect_timer; + guint quit_timer; + guint fail_stop_id; + gboolean interactive; + + gboolean got_config; + gboolean has_ip4, got_ip4; + gboolean has_ip6, got_ip6; + + /* Config stuff copied from config to ip4config */ + GVariant *banner, *tundev, *gateway, *mtu; +} NMVpnPluginOldPrivate; + +#define NM_VPN_PLUGIN_OLD_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NM_TYPE_VPN_PLUGIN_OLD, NMVpnPluginOldPrivate)) + +enum { + STATE_CHANGED, + CONFIG, + IP4_CONFIG, + IP6_CONFIG, + LOGIN_BANNER, + FAILURE, + QUIT, + SECRETS_REQUIRED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_DBUS_SERVICE_NAME, PROP_STATE, ); + +static GSList *active_plugins = NULL; + +static void +nm_vpn_plugin_old_set_connection(NMVpnPluginOld *plugin, GDBusConnection *connection) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + g_clear_object(&priv->connection); + + if (connection) + priv->connection = g_object_ref(connection); +} + +/** + * nm_vpn_plugin_old_get_connection: + * + * Returns: (transfer full): + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +GDBusConnection * +nm_vpn_plugin_old_get_connection(NMVpnPluginOld *plugin) +{ + GDBusConnection *connection; + + g_return_val_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin), NULL); + + connection = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin)->connection; + + if (connection) + g_object_ref(connection); + + return connection; +} + +/** + * nm_vpn_plugin_old_get_state: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +NMVpnServiceState +nm_vpn_plugin_old_get_state(NMVpnPluginOld *plugin) +{ + g_return_val_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin), NM_VPN_SERVICE_STATE_UNKNOWN); + + return NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin)->state; +} + +/** + * nm_vpn_plugin_old_set_state: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +void +nm_vpn_plugin_old_set_state(NMVpnPluginOld *plugin, NMVpnServiceState state) +{ + NMVpnPluginOldPrivate *priv; + + g_return_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin)); + + priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + if (priv->state != state) { + priv->state = state; + g_signal_emit(plugin, signals[STATE_CHANGED], 0, state); + } +} + +/** + * nm_vpn_plugin_old_set_login_banner: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +void +nm_vpn_plugin_old_set_login_banner(NMVpnPluginOld *plugin, const char *banner) +{ + g_return_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin)); + g_return_if_fail(banner != NULL); + + g_signal_emit(plugin, signals[LOGIN_BANNER], 0, banner); +} + +/** + * nm_vpn_plugin_old_failure: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +void +nm_vpn_plugin_old_failure(NMVpnPluginOld *plugin, NMVpnPluginFailure reason) +{ + g_return_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin)); + + g_signal_emit(plugin, signals[FAILURE], 0, reason); +} + +/** + * nm_vpn_plugin_old_disconnect: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +gboolean +nm_vpn_plugin_old_disconnect(NMVpnPluginOld *plugin, GError **err) +{ + gboolean ret = FALSE; + NMVpnServiceState state; + + g_return_val_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin), FALSE); + + state = nm_vpn_plugin_old_get_state(plugin); + switch (state) { + case NM_VPN_SERVICE_STATE_STOPPING: + g_set_error( + err, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS, + "%s", + "Could not process the request because the VPN connection is already being stopped."); + break; + case NM_VPN_SERVICE_STATE_STOPPED: + g_set_error(err, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_ALREADY_STOPPED, + "%s", + "Could not process the request because no VPN connection was active."); + break; + case NM_VPN_SERVICE_STATE_STARTING: + case NM_VPN_SERVICE_STATE_STARTED: + nm_vpn_plugin_old_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPING); + ret = NM_VPN_PLUGIN_OLD_GET_CLASS(plugin)->disconnect(plugin, err); + nm_vpn_plugin_old_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPED); + break; + case NM_VPN_SERVICE_STATE_INIT: + ret = TRUE; + break; + + default: + g_warning("Unhandled VPN service state %d", state); + g_assert_not_reached(); + break; + } + + return ret; +} + +static void +nm_vpn_plugin_old_emit_quit(NMVpnPluginOld *plugin) +{ + g_signal_emit(plugin, signals[QUIT], 0); +} + +static gboolean +connect_timer_expired(gpointer data) +{ + NMVpnPluginOld *plugin = NM_VPN_PLUGIN_OLD(data); + GError * err = NULL; + + NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin)->connect_timer = 0; + g_message("Connect timer expired, disconnecting."); + nm_vpn_plugin_old_disconnect(plugin, &err); + if (err) { + g_warning("Disconnect failed: %s", err->message); + g_error_free(err); + } + + return G_SOURCE_REMOVE; +} + +static gboolean +quit_timer_expired(gpointer data) +{ + NMVpnPluginOld *self = NM_VPN_PLUGIN_OLD(data); + + NM_VPN_PLUGIN_OLD_GET_PRIVATE(self)->quit_timer = 0; + nm_vpn_plugin_old_emit_quit(self); + return G_SOURCE_REMOVE; +} + +static void +schedule_quit_timer(NMVpnPluginOld *self) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(self); + + nm_clear_g_source(&priv->quit_timer); + priv->quit_timer = + g_timeout_add_seconds(NM_VPN_PLUGIN_OLD_QUIT_TIMER, quit_timer_expired, self); +} + +static gboolean +fail_stop(gpointer data) +{ + NMVpnPluginOld *self = NM_VPN_PLUGIN_OLD(data); + + NM_VPN_PLUGIN_OLD_GET_PRIVATE(self)->fail_stop_id = 0; + nm_vpn_plugin_old_set_state(self, NM_VPN_SERVICE_STATE_STOPPED); + return G_SOURCE_REMOVE; +} + +static void +schedule_fail_stop(NMVpnPluginOld *plugin, guint timeout_secs) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + nm_clear_g_source(&priv->fail_stop_id); + if (timeout_secs) + priv->fail_stop_id = g_timeout_add_seconds(timeout_secs, fail_stop, plugin); + else + priv->fail_stop_id = g_idle_add(fail_stop, plugin); +} + +/** + * nm_vpn_plugin_old_set_config: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +void +nm_vpn_plugin_old_set_config(NMVpnPluginOld *plugin, GVariant *config) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + g_return_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin)); + g_return_if_fail(config != NULL); + + priv->got_config = TRUE; + + (void) g_variant_lookup(config, NM_VPN_PLUGIN_CONFIG_HAS_IP4, "b", &priv->has_ip4); + (void) g_variant_lookup(config, NM_VPN_PLUGIN_CONFIG_HAS_IP6, "b", &priv->has_ip6); + + g_warn_if_fail(priv->has_ip4 || priv->has_ip6); + + /* Record the items that need to also be inserted into the + * ip4config, for compatibility with older daemons. + */ + if (priv->banner) + g_variant_unref(priv->banner); + priv->banner = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_BANNER, G_VARIANT_TYPE("s")); + if (priv->tundev) + g_variant_unref(priv->tundev); + priv->tundev = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_TUNDEV, G_VARIANT_TYPE("s")); + if (priv->gateway) + g_variant_unref(priv->gateway); + priv->gateway = + g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, G_VARIANT_TYPE("u")); + if (priv->mtu) + g_variant_unref(priv->mtu); + priv->mtu = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_MTU, G_VARIANT_TYPE("u")); + + g_signal_emit(plugin, signals[CONFIG], 0, config); +} + +/** + * nm_vpn_plugin_old_set_ip4_config: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +void +nm_vpn_plugin_old_set_ip4_config(NMVpnPluginOld *plugin, GVariant *ip4_config) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + GVariant * combined_config; + GVariantBuilder builder; + GVariantIter iter; + const char * key; + GVariant * value; + + g_return_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin)); + g_return_if_fail(ip4_config != NULL); + + priv->got_ip4 = TRUE; + + /* Old plugins won't send the "config" signal and thus can't send + * NM_VPN_PLUGIN_OLD_CONFIG_HAS_IP4 either. But since they don't support IPv6, + * we can safely assume that, if we don't receive a "config" signal but do + * receive an "ip4-config" signal, the old plugin supports IPv4. + */ + if (!priv->got_config) + priv->has_ip4 = TRUE; + + /* Older NetworkManager daemons expect all config info to be in + * the ip4 config, so they won't even notice the "config" signal + * being emitted. So just copy all of that data into the ip4 + * config too. + */ + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + g_variant_iter_init(&iter, ip4_config); + while (g_variant_iter_next(&iter, "{&sv}", &key, &value)) { + g_variant_builder_add(&builder, "{sv}", key, value); + g_variant_unref(value); + } + + if (priv->banner) + g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_BANNER, &priv->banner); + if (priv->tundev) + g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, &priv->tundev); + if (priv->gateway) + g_variant_builder_add(&builder, + "{sv}", + NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, + &priv->gateway); + if (priv->mtu) + g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_MTU, &priv->mtu); + + combined_config = g_variant_builder_end(&builder); + g_variant_ref_sink(combined_config); + g_signal_emit(plugin, signals[IP4_CONFIG], 0, combined_config); + g_variant_unref(combined_config); + + if (priv->has_ip4 == priv->got_ip4 && priv->has_ip6 == priv->got_ip6) + nm_vpn_plugin_old_set_state(plugin, NM_VPN_SERVICE_STATE_STARTED); +} + +/** + * nm_vpn_plugin_old_set_ip6_config: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +void +nm_vpn_plugin_old_set_ip6_config(NMVpnPluginOld *plugin, GVariant *ip6_config) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + g_return_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin)); + g_return_if_fail(ip6_config != NULL); + + g_variant_ref_sink(ip6_config); + + priv->got_ip6 = TRUE; + g_signal_emit(plugin, signals[IP6_CONFIG], 0, ip6_config); + + g_variant_unref(ip6_config); + + if (priv->has_ip4 == priv->got_ip4 && priv->has_ip6 == priv->got_ip6) + nm_vpn_plugin_old_set_state(plugin, NM_VPN_SERVICE_STATE_STARTED); +} + +static void +connect_timer_start(NMVpnPluginOld *plugin) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + priv->connect_timer = g_timeout_add_seconds(60, connect_timer_expired, plugin); +} + +static void +_connect_generic(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * properties, + GVariant * details) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + NMVpnPluginOldClass * vpn_class = NM_VPN_PLUGIN_OLD_GET_CLASS(plugin); + gs_unref_object NMConnection *connection = NULL; + gboolean success = FALSE; + gs_free_error GError *error = NULL; + guint fail_stop_timeout = 0; + + if (priv->state != NM_VPN_SERVICE_STATE_STOPPED && priv->state != NM_VPN_SERVICE_STATE_INIT) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_WRONG_STATE, + "Could not start connection: wrong plugin state %d", + priv->state); + return; + } + + connection = + _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); + if (!connection) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Invalid connection: %s", + error->message); + return; + } + + priv->interactive = FALSE; + if (details && !vpn_class->connect_interactive) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED, + "Plugin does not implement ConnectInteractive()"); + return; + } + + nm_clear_g_source(&priv->fail_stop_id); + + if (details) { + priv->interactive = TRUE; + success = vpn_class->connect_interactive(plugin, connection, details, &error); + if (g_error_matches(error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED)) { + /* Give NetworkManager a bit of time to fall back to Connect() */ + fail_stop_timeout = 5; + } + } else + success = vpn_class->connect(plugin, connection, &error); + + if (success) { + nm_vpn_plugin_old_set_state(plugin, NM_VPN_SERVICE_STATE_STARTING); + + g_dbus_method_invocation_return_value(context, NULL); + + /* Add a timer to make sure we do not wait indefinitely for the successful connect. */ + connect_timer_start(plugin); + } else { + g_dbus_method_invocation_take_error(context, g_steal_pointer(&error)); + + /* Stop the plugin from an idle handler so that the Connect + * method return gets sent before the STOP StateChanged signal. + */ + schedule_fail_stop(plugin, fail_stop_timeout); + } +} + +static void +impl_vpn_plugin_old_connect(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * connection, + gpointer user_data) +{ + _connect_generic(plugin, context, connection, NULL); +} + +static void +impl_vpn_plugin_old_connect_interactive(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * connection, + GVariant * details, + gpointer user_data) +{ + _connect_generic(plugin, context, connection, details); +} + +/*****************************************************************************/ + +static void +impl_vpn_plugin_old_need_secrets(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * properties, + gpointer user_data) +{ + NMConnection *connection; + const char * setting_name; + gboolean needed; + GError * error = NULL; + + connection = + _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); + if (!connection) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INVALID_CONNECTION, + "The connection was invalid: %s", + error->message); + g_error_free(error); + return; + } + + if (!NM_VPN_PLUGIN_OLD_GET_CLASS(plugin)->need_secrets) { + g_dbus_method_invocation_return_value(context, g_variant_new("(s)", "")); + return; + } + + needed = NM_VPN_PLUGIN_OLD_GET_CLASS(plugin)->need_secrets(plugin, + connection, + &setting_name, + &error); + if (error) { + g_dbus_method_invocation_take_error(context, error); + return; + } + + if (needed) { + /* Push back the quit timer so the VPN plugin doesn't quit in the + * middle of asking the user for secrets. + */ + schedule_quit_timer(plugin); + + g_assert(setting_name); + g_dbus_method_invocation_return_value(context, g_variant_new("(s)", setting_name)); + } else { + /* No secrets required */ + g_dbus_method_invocation_return_value(context, g_variant_new("(s)", "")); + } +} + +static void +impl_vpn_plugin_old_new_secrets(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * properties, + gpointer user_data) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + NMConnection * connection; + GError * error = NULL; + gboolean success; + + if (priv->state != NM_VPN_SERVICE_STATE_STARTING) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_WRONG_STATE, + "Could not accept new secrets: wrong plugin state %d", + priv->state); + return; + } + + connection = + _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); + if (!connection) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Invalid connection: %s", + error->message); + g_clear_error(&error); + return; + } + + if (!NM_VPN_PLUGIN_OLD_GET_CLASS(plugin)->new_secrets) { + g_dbus_method_invocation_return_error( + context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED, + "Could not accept new secrets: plugin cannot process interactive secrets"); + g_object_unref(connection); + return; + } + + success = NM_VPN_PLUGIN_OLD_GET_CLASS(plugin)->new_secrets(plugin, connection, &error); + if (success) { + g_dbus_method_invocation_return_value(context, NULL); + + /* Add a timer to make sure we do not wait indefinitely for the successful connect. */ + connect_timer_start(plugin); + } else { + g_dbus_method_invocation_take_error(context, error); + + /* Stop the plugin from and idle handler so that the NewSecrets + * method return gets sent before the STOP StateChanged signal. + */ + schedule_fail_stop(plugin, 0); + } + + g_object_unref(connection); +} + +/** + * nm_vpn_plugin_old_secrets_required: + * @plugin: the #NMVpnPluginOld + * @message: an information message about why secrets are required, if any + * @hints: VPN specific secret names for required new secrets + * + * Called by VPN plugin implementations to signal to NetworkManager that secrets + * are required during the connection process. This signal may be used to + * request new secrets when the secrets originally provided by NetworkManager + * are insufficient, or the VPN process indicates that it needs additional + * information to complete the request. + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +void +nm_vpn_plugin_old_secrets_required(NMVpnPluginOld *plugin, const char *message, const char **hints) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + /* Plugin must be able to accept the new secrets if it calls this method */ + g_return_if_fail(NM_VPN_PLUGIN_OLD_GET_CLASS(plugin)->new_secrets); + + /* Plugin cannot call this method if NetworkManager didn't originally call + * ConnectInteractive(). + */ + g_return_if_fail(priv->interactive == TRUE); + + /* Cancel the connect timer since secrets might take a while. It'll + * get restarted when the secrets come back via NewSecrets(). + */ + nm_clear_g_source(&priv->connect_timer); + + g_signal_emit(plugin, signals[SECRETS_REQUIRED], 0, message, hints); +} + +/*****************************************************************************/ + +/** + * nm_vpn_plugin_old_read_vpn_details: + * @fd: file descriptor to read from, usually stdin (0) + * @out_data: (out) (transfer full): on successful return, a hash table + * (mapping char*:char*) containing the key/value pairs of VPN data items + * @out_secrets: (out) (transfer full): on successful return, a hash table + * (mapping char*:char*) containing the key/value pairsof VPN secrets + * + * Parses key/value pairs from a file descriptor (normally stdin) passed by + * an applet when the applet calls the authentication dialog of the VPN plugin. + * + * Returns: %TRUE if reading values was successful, %FALSE if not + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + **/ +gboolean +nm_vpn_plugin_old_read_vpn_details(int fd, GHashTable **out_data, GHashTable **out_secrets) +{ + return nm_vpn_service_plugin_read_vpn_details(fd, out_data, out_secrets); +} + +/** + * nm_vpn_plugin_old_get_secret_flags: + * @data: hash table containing VPN key/value pair data items + * @secret_name: VPN secret key name for which to retrieve flags for + * @out_flags: (out): on success, the flags associated with @secret_name + * + * Given a VPN secret key name, attempts to find the corresponding flags data + * item in @data. If found, converts the flags data item to + * #NMSettingSecretFlags and returns it. + * + * Returns: %TRUE if the flag data item was found and successfully converted + * to flags, %FALSE if not + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + **/ +gboolean +nm_vpn_plugin_old_get_secret_flags(GHashTable * data, + const char * secret_name, + NMSettingSecretFlags *out_flags) +{ + return nm_vpn_service_plugin_get_secret_flags(data, secret_name, out_flags); +} + +/*****************************************************************************/ + +static void +impl_vpn_plugin_old_disconnect(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + gpointer user_data) +{ + GError *error = NULL; + + if (nm_vpn_plugin_old_disconnect(plugin, &error)) + g_dbus_method_invocation_return_value(context, NULL); + else + g_dbus_method_invocation_take_error(context, error); +} + +static void +impl_vpn_plugin_old_set_config(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * config, + gpointer user_data) +{ + nm_vpn_plugin_old_set_config(plugin, config); + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_vpn_plugin_old_set_ip4_config(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * config, + gpointer user_data) +{ + nm_vpn_plugin_old_set_ip4_config(plugin, config); + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_vpn_plugin_old_set_ip6_config(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * config, + gpointer user_data) +{ + nm_vpn_plugin_old_set_ip6_config(plugin, config); + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_vpn_plugin_old_set_failure(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + char * reason, + gpointer user_data) +{ + nm_vpn_plugin_old_failure(plugin, NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG); + g_dbus_method_invocation_return_value(context, NULL); +} + +/*****************************************************************************/ + +static void +_emit_quit(gpointer data, gpointer user_data) +{ + NMVpnPluginOld *plugin = data; + + nm_vpn_plugin_old_emit_quit(plugin); +} + +static void +sigterm_handler(int signum) +{ + g_slist_foreach(active_plugins, _emit_quit, NULL); +} + +static void +setup_unix_signal_handler(void) +{ + struct sigaction action; + sigset_t block_mask; + + action.sa_handler = sigterm_handler; + sigemptyset(&block_mask); + action.sa_mask = block_mask; + action.sa_flags = 0; + sigaction(SIGINT, &action, NULL); + sigaction(SIGTERM, &action, NULL); +} + +/*****************************************************************************/ + +static void +one_plugin_destroyed(gpointer data, GObject *object) +{ + active_plugins = g_slist_remove(active_plugins, object); +} + +static void +nm_vpn_plugin_old_init(NMVpnPluginOld *plugin) +{ + active_plugins = g_slist_append(active_plugins, plugin); + g_object_weak_ref(G_OBJECT(plugin), one_plugin_destroyed, NULL); +} + +static gboolean +init_sync(GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMVpnPluginOld * plugin = NM_VPN_PLUGIN_OLD(initable); + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + GDBusConnection * connection = NULL; + GDBusProxy * proxy; + GVariant * ret; + gboolean success = FALSE; + + if (!priv->dbus_service_name) { + g_set_error_literal(error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + _("No service name specified")); + return FALSE; + } + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); + if (!connection) + return FALSE; + + proxy = g_dbus_proxy_new_sync(connection, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES + | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + cancellable, + error); + if (!proxy) + goto out; + + ret = g_dbus_proxy_call_sync(proxy, + "RequestName", + g_variant_new("(su)", priv->dbus_service_name, 0), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + g_object_unref(proxy); + if (!ret) { + if (error && *error) + g_dbus_error_strip_remote_error(*error); + goto out; + } + g_variant_unref(ret); + + priv->dbus_vpn_plugin_old = nmdbus_vpn_plugin_skeleton_new(); + if (!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(priv->dbus_vpn_plugin_old), + connection, + NM_VPN_DBUS_PLUGIN_PATH, + error)) + goto out; + + _nm_dbus_bind_properties(plugin, priv->dbus_vpn_plugin_old); + _nm_dbus_bind_methods(plugin, + priv->dbus_vpn_plugin_old, + "Connect", + impl_vpn_plugin_old_connect, + "ConnectInteractive", + impl_vpn_plugin_old_connect_interactive, + "NeedSecrets", + impl_vpn_plugin_old_need_secrets, + "NewSecrets", + impl_vpn_plugin_old_new_secrets, + "Disconnect", + impl_vpn_plugin_old_disconnect, + "SetConfig", + impl_vpn_plugin_old_set_config, + "SetIp4Config", + impl_vpn_plugin_old_set_ip4_config, + "SetIp6Config", + impl_vpn_plugin_old_set_ip6_config, + "SetFailure", + impl_vpn_plugin_old_set_failure, + NULL); + + nm_vpn_plugin_old_set_connection(plugin, connection); + nm_vpn_plugin_old_set_state(plugin, NM_VPN_SERVICE_STATE_INIT); + + success = TRUE; + +out: + g_clear_object(&connection); + + return success; +} + +static void +set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_DBUS_SERVICE_NAME: + /* construct-only */ + priv->dbus_service_name = g_value_dup_string(value); + break; + case PROP_STATE: + nm_vpn_plugin_old_set_state(NM_VPN_PLUGIN_OLD(object), + (NMVpnServiceState) g_value_get_enum(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_DBUS_SERVICE_NAME: + g_value_set_string(value, priv->dbus_service_name); + break; + case PROP_STATE: + g_value_set_enum(value, nm_vpn_plugin_old_get_state(NM_VPN_PLUGIN_OLD(object))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +dispose(GObject *object) +{ + NMVpnPluginOld * plugin = NM_VPN_PLUGIN_OLD(object); + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + NMVpnServiceState state; + GError * err = NULL; + + nm_clear_g_source(&priv->fail_stop_id); + nm_clear_g_source(&priv->quit_timer); + nm_clear_g_source(&priv->connect_timer); + + state = nm_vpn_plugin_old_get_state(plugin); + + if (state == NM_VPN_SERVICE_STATE_STARTED || state == NM_VPN_SERVICE_STATE_STARTING) + nm_vpn_plugin_old_disconnect(plugin, &err); + + if (err) { + g_warning("Error disconnecting VPN connection: %s", err->message); + g_error_free(err); + } + + G_OBJECT_CLASS(nm_vpn_plugin_old_parent_class)->dispose(object); +} + +static void +finalize(GObject *object) +{ + NMVpnPluginOld * plugin = NM_VPN_PLUGIN_OLD(object); + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + nm_vpn_plugin_old_set_connection(plugin, NULL); + g_free(priv->dbus_service_name); + + nm_clear_pointer(&priv->banner, g_variant_unref); + nm_clear_pointer(&priv->tundev, g_variant_unref); + nm_clear_pointer(&priv->gateway, g_variant_unref); + nm_clear_pointer(&priv->mtu, g_variant_unref); + + G_OBJECT_CLASS(nm_vpn_plugin_old_parent_class)->finalize(object); +} + +static void +state_changed(NMVpnPluginOld *plugin, NMVpnServiceState state) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + switch (state) { + case NM_VPN_SERVICE_STATE_STARTING: + nm_clear_g_source(&priv->quit_timer); + nm_clear_g_source(&priv->fail_stop_id); + break; + case NM_VPN_SERVICE_STATE_STOPPED: + schedule_quit_timer(plugin); + break; + default: + /* Clean up all timers we might have set up. */ + nm_clear_g_source(&priv->connect_timer); + nm_clear_g_source(&priv->quit_timer); + nm_clear_g_source(&priv->fail_stop_id); + break; + } +} + +static void +nm_vpn_plugin_old_class_init(NMVpnPluginOldClass *plugin_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(plugin_class); + + g_type_class_add_private(object_class, sizeof(NMVpnPluginOldPrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + plugin_class->state_changed = state_changed; + + /** + * NMVpnPluginOld:service-name: + * + * The D-Bus service name of this plugin. + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ + obj_properties[PROP_DBUS_SERVICE_NAME] = + g_param_spec_string(NM_VPN_PLUGIN_OLD_DBUS_SERVICE_NAME, + "", + "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + /** + * NMVpnPluginOld:state: + * + * The state of the plugin. + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ + obj_properties[PROP_STATE] = g_param_spec_enum(NM_VPN_PLUGIN_OLD_STATE, + "", + "", + NM_TYPE_VPN_SERVICE_STATE, + NM_VPN_SERVICE_STATE_INIT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + signals[STATE_CHANGED] = g_signal_new("state-changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnPluginOldClass, state_changed), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + signals[SECRETS_REQUIRED] = g_signal_new("secrets-required", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 2, + G_TYPE_STRING, + G_TYPE_STRV); + + signals[CONFIG] = g_signal_new("config", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnPluginOldClass, config), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_VARIANT); + + signals[IP4_CONFIG] = g_signal_new("ip4-config", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnPluginOldClass, ip4_config), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_VARIANT); + + signals[IP6_CONFIG] = g_signal_new("ip6-config", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnPluginOldClass, ip6_config), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_VARIANT); + + signals[LOGIN_BANNER] = g_signal_new("login-banner", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnPluginOldClass, login_banner), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals[FAILURE] = g_signal_new("failure", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnPluginOldClass, failure), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + signals[QUIT] = g_signal_new("quit", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnPluginOldClass, quit), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0, + G_TYPE_NONE); + + setup_unix_signal_handler(); +} + +static void +nm_vpn_plugin_old_initable_iface_init(GInitableIface *iface) +{ + iface->init = init_sync; +} diff --git a/src/libnm-client-impl/nm-vpn-service-plugin.c b/src/libnm-client-impl/nm-vpn-service-plugin.c new file mode 100644 index 00000000..18a85e91 --- /dev/null +++ b/src/libnm-client-impl/nm-vpn-service-plugin.c @@ -0,0 +1,1384 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2015 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-vpn-service-plugin.h" + +#include <signal.h> +#include <stdlib.h> +#include <poll.h> + +#include "libnm-glib-aux/nm-secret-utils.h" +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "nm-enum-types.h" +#include "nm-utils.h" +#include "nm-connection.h" +#include "nm-dbus-helpers.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-simple-connection.h" + +#include "introspection/org.freedesktop.NetworkManager.VPN.Plugin.h" + +#define NM_VPN_SERVICE_PLUGIN_QUIT_TIMER 180 + +static void nm_vpn_service_plugin_initable_iface_init(GInitableIface *iface); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE(NMVpnServicePlugin, + nm_vpn_service_plugin, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, + nm_vpn_service_plugin_initable_iface_init);) + +typedef struct { + NMVpnServiceState state; + + /* DBUS-y stuff */ + GDBusConnection *connection; + NMDBusVpnPlugin *dbus_vpn_service_plugin; + char * dbus_service_name; + gboolean dbus_watch_peer; + + /* Temporary stuff */ + guint connect_timer; + guint quit_timer; + guint fail_stop_id; + guint peer_watch_id; + gboolean interactive; + + gboolean got_config; + gboolean has_ip4, got_ip4; + gboolean has_ip6, got_ip6; + + /* Config stuff copied from config to ip4config */ + GVariant *banner, *tundev, *gateway, *mtu; +} NMVpnServicePluginPrivate; + +#define NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NM_TYPE_VPN_SERVICE_PLUGIN, NMVpnServicePluginPrivate)) + +enum { + STATE_CHANGED, + CONFIG, + IP4_CONFIG, + IP6_CONFIG, + LOGIN_BANNER, + FAILURE, + QUIT, + SECRETS_REQUIRED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_DBUS_SERVICE_NAME, PROP_DBUS_WATCH_PEER, PROP_STATE, ); + +static GSList *active_plugins = NULL; + +static void +nm_vpn_service_plugin_set_connection(NMVpnServicePlugin *plugin, GDBusConnection *connection) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + g_clear_object(&priv->connection); + + if (connection) + priv->connection = g_object_ref(connection); +} + +/** + * nm_vpn_service_plugin_get_connection: + * + * Returns: (transfer full): + * + * Since: 1.2 + */ +GDBusConnection * +nm_vpn_service_plugin_get_connection(NMVpnServicePlugin *plugin) +{ + GDBusConnection *connection; + + g_return_val_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin), NULL); + + connection = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin)->connection; + + if (connection) + g_object_ref(connection); + + return connection; +} + +static NMVpnServiceState +nm_vpn_service_plugin_get_state(NMVpnServicePlugin *plugin) +{ + g_return_val_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin), NM_VPN_SERVICE_STATE_UNKNOWN); + + return NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin)->state; +} + +static void +nm_vpn_service_plugin_set_state(NMVpnServicePlugin *plugin, NMVpnServiceState state) +{ + NMVpnServicePluginPrivate *priv; + + g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); + + priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + if (priv->state != state) { + priv->state = state; + g_signal_emit(plugin, signals[STATE_CHANGED], 0, state); + if (priv->dbus_vpn_service_plugin) + nmdbus_vpn_plugin_emit_state_changed(priv->dbus_vpn_service_plugin, state); + } +} + +void +nm_vpn_service_plugin_set_login_banner(NMVpnServicePlugin *plugin, const char *banner) +{ + NMVpnServicePluginPrivate *priv; + + g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); + g_return_if_fail(banner != NULL); + + priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + g_signal_emit(plugin, signals[LOGIN_BANNER], 0, banner); + if (priv->dbus_vpn_service_plugin) + nmdbus_vpn_plugin_emit_login_banner(priv->dbus_vpn_service_plugin, banner); +} + +static void +_emit_failure(NMVpnServicePlugin *plugin, NMVpnPluginFailure reason) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + g_signal_emit(plugin, signals[FAILURE], 0, reason); + if (priv->dbus_vpn_service_plugin) + nmdbus_vpn_plugin_emit_failure(priv->dbus_vpn_service_plugin, reason); +} + +void +nm_vpn_service_plugin_failure(NMVpnServicePlugin *plugin, NMVpnPluginFailure reason) +{ + g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); + + _emit_failure(plugin, reason); + nm_vpn_service_plugin_disconnect(plugin, NULL); +} + +gboolean +nm_vpn_service_plugin_disconnect(NMVpnServicePlugin *plugin, GError **err) +{ + gboolean ret = FALSE; + NMVpnServiceState state; + + g_return_val_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin), FALSE); + + state = nm_vpn_service_plugin_get_state(plugin); + switch (state) { + case NM_VPN_SERVICE_STATE_STOPPING: + g_set_error( + err, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS, + "%s", + "Could not process the request because the VPN connection is already being stopped."); + break; + case NM_VPN_SERVICE_STATE_STOPPED: + g_set_error(err, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_ALREADY_STOPPED, + "%s", + "Could not process the request because no VPN connection was active."); + break; + case NM_VPN_SERVICE_STATE_STARTING: + _emit_failure(plugin, NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED); + /* fall-through */ + case NM_VPN_SERVICE_STATE_STARTED: + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPING); + ret = NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->disconnect(plugin, err); + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPED); + break; + case NM_VPN_SERVICE_STATE_INIT: + ret = TRUE; + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPED); + break; + + default: + g_warning("Unhandled VPN service state %d", state); + g_assert_not_reached(); + break; + } + + return ret; +} + +static void +nm_vpn_service_plugin_emit_quit(NMVpnServicePlugin *plugin) +{ + g_signal_emit(plugin, signals[QUIT], 0); +} + +/** + * nm_vpn_service_plugin_shutdown: + * @plugin: the #NMVpnServicePlugin instance + * + * Shutdown the @plugin and disconnect from D-Bus. After this, + * the plugin instance is dead and should no longer be used. + * It ensures to get no more requests from D-Bus. In principle, + * you don't need to shutdown the plugin, disposing the instance + * has the same effect. However, this gives a way to deactivate + * the plugin before giving up the last reference. + * + * Since: 1.12 + */ +void +nm_vpn_service_plugin_shutdown(NMVpnServicePlugin *plugin) +{ + NMVpnServicePluginPrivate *priv; + NMVpnServiceState state; + GError * error = NULL; + + g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); + + priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + nm_clear_g_source(&priv->fail_stop_id); + nm_clear_g_source(&priv->quit_timer); + nm_clear_g_source(&priv->connect_timer); + + state = nm_vpn_service_plugin_get_state(plugin); + if (state == NM_VPN_SERVICE_STATE_STARTED || state == NM_VPN_SERVICE_STATE_STARTING) { + nm_vpn_service_plugin_disconnect(plugin, &error); + + if (error) { + g_warning("Error disconnecting VPN connection: %s", error->message); + g_error_free(error); + } + } + + if (priv->dbus_vpn_service_plugin) { + g_dbus_interface_skeleton_unexport( + G_DBUS_INTERFACE_SKELETON(priv->dbus_vpn_service_plugin)); + g_clear_object(&priv->dbus_vpn_service_plugin); + } +} + +static gboolean +connect_timer_expired(gpointer data) +{ + NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN(data); + GError * err = NULL; + + NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin)->connect_timer = 0; + g_message("Connect timer expired, disconnecting."); + nm_vpn_service_plugin_disconnect(plugin, &err); + if (err) { + g_warning("Disconnect failed: %s", err->message); + g_error_free(err); + } + + return G_SOURCE_REMOVE; +} + +static gboolean +quit_timer_expired(gpointer data) +{ + NMVpnServicePlugin *self = NM_VPN_SERVICE_PLUGIN(data); + + NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(self)->quit_timer = 0; + nm_vpn_service_plugin_emit_quit(self); + return G_SOURCE_REMOVE; +} + +static void +schedule_quit_timer(NMVpnServicePlugin *self) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(self); + + nm_clear_g_source(&priv->quit_timer); + priv->quit_timer = + g_timeout_add_seconds(NM_VPN_SERVICE_PLUGIN_QUIT_TIMER, quit_timer_expired, self); +} + +static gboolean +fail_stop(gpointer data) +{ + NMVpnServicePlugin *self = NM_VPN_SERVICE_PLUGIN(data); + + NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(self)->fail_stop_id = 0; + nm_vpn_service_plugin_set_state(self, NM_VPN_SERVICE_STATE_STOPPED); + return G_SOURCE_REMOVE; +} + +static void +schedule_fail_stop(NMVpnServicePlugin *plugin, guint timeout_secs) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + nm_clear_g_source(&priv->fail_stop_id); + if (timeout_secs) + priv->fail_stop_id = g_timeout_add_seconds(timeout_secs, fail_stop, plugin); + else + priv->fail_stop_id = g_idle_add(fail_stop, plugin); +} + +void +nm_vpn_service_plugin_set_config(NMVpnServicePlugin *plugin, GVariant *config) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); + g_return_if_fail(config != NULL); + + priv->got_config = TRUE; + + (void) g_variant_lookup(config, NM_VPN_PLUGIN_CONFIG_HAS_IP4, "b", &priv->has_ip4); + (void) g_variant_lookup(config, NM_VPN_PLUGIN_CONFIG_HAS_IP6, "b", &priv->has_ip6); + + /* Record the items that need to also be inserted into the + * ip4config, for compatibility with older daemons. + */ + if (priv->banner) + g_variant_unref(priv->banner); + priv->banner = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_BANNER, G_VARIANT_TYPE("s")); + if (priv->tundev) + g_variant_unref(priv->tundev); + priv->tundev = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_TUNDEV, G_VARIANT_TYPE("s")); + if (priv->gateway) + g_variant_unref(priv->gateway); + priv->gateway = + g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, G_VARIANT_TYPE("u")); + if (priv->mtu) + g_variant_unref(priv->mtu); + priv->mtu = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_MTU, G_VARIANT_TYPE("u")); + + g_signal_emit(plugin, signals[CONFIG], 0, config); + if (priv->dbus_vpn_service_plugin) + nmdbus_vpn_plugin_emit_config(priv->dbus_vpn_service_plugin, config); + + if (priv->has_ip4 == priv->got_ip4 && priv->has_ip6 == priv->got_ip6) + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STARTED); +} + +void +nm_vpn_service_plugin_set_ip4_config(NMVpnServicePlugin *plugin, GVariant *ip4_config) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + GVariant * combined_config; + GVariantBuilder builder; + GVariantIter iter; + const char * key; + GVariant * value; + + g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); + g_return_if_fail(ip4_config != NULL); + + priv->got_ip4 = TRUE; + + /* Old plugins won't send the "config" signal and thus can't send + * NM_VPN_SERVICE_PLUGIN_CONFIG_HAS_IP4 either. But since they don't support IPv6, + * we can safely assume that, if we don't receive a "config" signal but do + * receive an "ip4-config" signal, the old plugin supports IPv4. + */ + if (!priv->got_config) + priv->has_ip4 = TRUE; + + /* Older NetworkManager daemons expect all config info to be in + * the ip4 config, so they won't even notice the "config" signal + * being emitted. So just copy all of that data into the ip4 + * config too. + */ + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + g_variant_iter_init(&iter, ip4_config); + while (g_variant_iter_next(&iter, "{&sv}", &key, &value)) { + g_variant_builder_add(&builder, "{sv}", key, value); + g_variant_unref(value); + } + + if (priv->banner) + g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_BANNER, priv->banner); + if (priv->tundev) + g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, priv->tundev); + if (priv->gateway) + g_variant_builder_add(&builder, + "{sv}", + NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, + priv->gateway); + if (priv->mtu) + g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_MTU, priv->mtu); + + combined_config = g_variant_builder_end(&builder); + g_variant_ref_sink(combined_config); + g_signal_emit(plugin, signals[IP4_CONFIG], 0, combined_config); + if (priv->dbus_vpn_service_plugin) + nmdbus_vpn_plugin_emit_ip4_config(priv->dbus_vpn_service_plugin, combined_config); + g_variant_unref(combined_config); + + if (priv->has_ip4 == priv->got_ip4 && priv->has_ip6 == priv->got_ip6) + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STARTED); +} + +void +nm_vpn_service_plugin_set_ip6_config(NMVpnServicePlugin *plugin, GVariant *ip6_config) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); + g_return_if_fail(ip6_config != NULL); + + g_variant_ref_sink(ip6_config); + + priv->got_ip6 = TRUE; + g_signal_emit(plugin, signals[IP6_CONFIG], 0, ip6_config); + if (priv->dbus_vpn_service_plugin) + nmdbus_vpn_plugin_emit_ip6_config(priv->dbus_vpn_service_plugin, ip6_config); + + g_variant_unref(ip6_config); + + if (priv->has_ip4 == priv->got_ip4 && priv->has_ip6 == priv->got_ip6) + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STARTED); +} + +static void +connect_timer_start(NMVpnServicePlugin *plugin) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + nm_clear_g_source(&priv->connect_timer); + priv->connect_timer = g_timeout_add_seconds(60, connect_timer_expired, plugin); +} + +static void +peer_vanished(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + nm_vpn_service_plugin_disconnect(NM_VPN_SERVICE_PLUGIN(user_data), NULL); +} + +static guint +watch_peer(NMVpnServicePlugin *plugin, GDBusMethodInvocation *context) +{ + GDBusConnection *connection = g_dbus_method_invocation_get_connection(context); + const char *peer = g_dbus_message_get_sender(g_dbus_method_invocation_get_message(context)); + + return nm_dbus_connection_signal_subscribe_name_owner_changed(connection, + peer, + peer_vanished, + plugin, + NULL); +} + +static void +_connect_generic(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * properties, + GVariant * details) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + NMVpnServicePluginClass * vpn_class = NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin); + NMConnection * connection; + gboolean success = FALSE; + GError * error = NULL; + guint fail_stop_timeout = 0; + + if (priv->state != NM_VPN_SERVICE_STATE_STOPPED && priv->state != NM_VPN_SERVICE_STATE_INIT) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_WRONG_STATE, + "Could not start connection: wrong plugin state %d", + priv->state); + return; + } + + connection = + _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); + if (!connection) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Invalid connection: %s", + error->message); + g_clear_error(&error); + return; + } + + priv->interactive = FALSE; + if (details && !vpn_class->connect_interactive) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED, + "Plugin does not implement ConnectInteractive()"); + return; + } + + nm_clear_g_source(&priv->fail_stop_id); + + if (priv->dbus_watch_peer) + priv->peer_watch_id = watch_peer(plugin, context); + + if (details) { + priv->interactive = TRUE; + success = vpn_class->connect_interactive(plugin, connection, details, &error); + if (g_error_matches(error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED)) { + /* Give NetworkManager a bit of time to fall back to Connect() */ + fail_stop_timeout = 5; + } + } else + success = vpn_class->connect(plugin, connection, &error); + + if (success) { + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STARTING); + + g_dbus_method_invocation_return_value(context, NULL); + + /* Add a timer to make sure we do not wait indefinitely for the successful connect. */ + connect_timer_start(plugin); + } else { + g_dbus_method_invocation_take_error(context, error); + + /* Stop the plugin from an idle handler so that the Connect + * method return gets sent before the STOP StateChanged signal. + */ + schedule_fail_stop(plugin, fail_stop_timeout); + } + + g_object_unref(connection); +} + +static void +impl_vpn_service_plugin_connect(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * connection, + gpointer user_data) +{ + _connect_generic(plugin, context, connection, NULL); +} + +static void +impl_vpn_service_plugin_connect_interactive(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * connection, + GVariant * details, + gpointer user_data) +{ + _connect_generic(plugin, context, connection, details); +} + +/*****************************************************************************/ + +static void +impl_vpn_service_plugin_need_secrets(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * properties, + gpointer user_data) +{ + NMConnection *connection; + const char * setting_name; + gboolean needed; + GError * error = NULL; + + connection = + _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); + if (!connection) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INVALID_CONNECTION, + "The connection was invalid: %s", + error->message); + g_error_free(error); + return; + } + + if (!NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->need_secrets) { + g_dbus_method_invocation_return_value(context, g_variant_new("(s)", "")); + return; + } + + needed = NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->need_secrets(plugin, + connection, + &setting_name, + &error); + if (error) { + g_dbus_method_invocation_take_error(context, error); + return; + } + + if (needed) { + /* Push back the quit timer so the VPN plugin doesn't quit in the + * middle of asking the user for secrets. + */ + schedule_quit_timer(plugin); + + g_assert(setting_name); + g_dbus_method_invocation_return_value(context, g_variant_new("(s)", setting_name)); + } else { + /* No secrets required */ + g_dbus_method_invocation_return_value(context, g_variant_new("(s)", "")); + } +} + +static void +impl_vpn_service_plugin_new_secrets(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * properties, + gpointer user_data) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + NMConnection * connection; + GError * error = NULL; + gboolean success; + + if (priv->state != NM_VPN_SERVICE_STATE_STARTING) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_WRONG_STATE, + "Could not accept new secrets: wrong plugin state %d", + priv->state); + return; + } + + connection = + _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); + if (!connection) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Invalid connection: %s", + error->message); + g_clear_error(&error); + return; + } + + if (!NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->new_secrets) { + g_dbus_method_invocation_return_error( + context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED, + "Could not accept new secrets: plugin cannot process interactive secrets"); + g_object_unref(connection); + return; + } + + success = NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->new_secrets(plugin, connection, &error); + if (success) { + g_dbus_method_invocation_return_value(context, NULL); + + /* Add a timer to make sure we do not wait indefinitely for the successful connect. */ + connect_timer_start(plugin); + } else { + g_dbus_method_invocation_take_error(context, error); + + /* Stop the plugin from and idle handler so that the NewSecrets + * method return gets sent before the STOP StateChanged signal. + */ + schedule_fail_stop(plugin, 0); + } + + g_object_unref(connection); +} + +/** + * nm_vpn_service_plugin_secrets_required: + * @plugin: the #NMVpnServicePlugin + * @message: an information message about why secrets are required, if any + * @hints: VPN specific secret names for required new secrets + * + * Called by VPN plugin implementations to signal to NetworkManager that secrets + * are required during the connection process. This signal may be used to + * request new secrets when the secrets originally provided by NetworkManager + * are insufficient, or the VPN process indicates that it needs additional + * information to complete the request. + * + * Since: 1.2 + */ +void +nm_vpn_service_plugin_secrets_required(NMVpnServicePlugin *plugin, + const char * message, + const char ** hints) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + /* Plugin must be able to accept the new secrets if it calls this method */ + g_return_if_fail(NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->new_secrets); + + /* Plugin cannot call this method if NetworkManager didn't originally call + * ConnectInteractive(). + */ + g_return_if_fail(priv->interactive == TRUE); + + /* Cancel the connect timer since secrets might take a while. It'll + * get restarted when the secrets come back via NewSecrets(). + */ + nm_clear_g_source(&priv->connect_timer); + + g_signal_emit(plugin, signals[SECRETS_REQUIRED], 0, message, hints); + if (priv->dbus_vpn_service_plugin) + nmdbus_vpn_plugin_emit_secrets_required(priv->dbus_vpn_service_plugin, message, hints); +} + +/*****************************************************************************/ + +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=" +#define SECRET_VAL_TAG "SECRET_VAL=" + +/** + * nm_vpn_service_plugin_read_vpn_details: + * @fd: file descriptor to read from, usually stdin (0) + * @out_data: (out) (transfer full): on successful return, a hash table + * (mapping char*:char*) containing the key/value pairs of VPN data items + * @out_secrets: (out) (transfer full): on successful return, a hash table + * (mapping char*:char*) containing the key/value pairsof VPN secrets + * + * Parses key/value pairs from a file descriptor (normally stdin) passed by + * an applet when the applet calls the authentication dialog of the VPN plugin. + * + * Returns: %TRUE if reading values was successful, %FALSE if not + * + * Since: 1.2 + **/ +gboolean +nm_vpn_service_plugin_read_vpn_details(int fd, GHashTable **out_data, GHashTable **out_secrets) +{ + gs_unref_hashtable GHashTable *data = NULL; + gs_unref_hashtable GHashTable *secrets = NULL; + gboolean success = FALSE; + GHashTable * hash = NULL; + nm_auto_free_gstring GString *key = NULL; + nm_auto_free_gstring GString *val = NULL; + nm_auto_free_gstring GString *line = NULL; + GString * str = NULL; + ReadFdBuf read_buf; + + if (out_data) + g_return_val_if_fail(*out_data == NULL, FALSE); + if (out_secrets) + g_return_val_if_fail(*out_secrets == NULL, FALSE); + + data = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); + 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'; + + eof = !_read_fd_buf_c(&read_buf, &c); + + if (!eof && c == '\0') { + /* On the first '\0' char, we also assume the data is finished. Abort. */ + read_buf.eof = TRUE; + eof = TRUE; + } + + if (!eof && 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. */ + break; + } + continue; + } + + if (str && *line->str == '=') { + /* continuation */ + g_string_append_c(str, '\n'); + g_string_append(str, line->str + 1); + goto next; + } + + if (key && val) { + /* done a line */ + g_return_val_if_fail(hash, FALSE); + g_hash_table_insert(hash, g_string_free(key, FALSE), g_string_free(val, FALSE)); + key = NULL; + val = NULL; + hash = NULL; + success = TRUE; /* Got at least one value */ + } + + if (nm_streq(line->str, "DONE")) { + /* finish marker */ + break; + } else if (NM_STR_HAS_PREFIX(line->str, DATA_KEY_TAG)) { + nm_clear_g_string(&key); + key = g_string_new(line->str + strlen(DATA_KEY_TAG)); + str = key; + hash = data; + } else if (NM_STR_HAS_PREFIX(line->str, DATA_VAL_TAG)) { + if (val || !key || hash != data) + break; + val = g_string_new(line->str + strlen(DATA_VAL_TAG)); + str = val; + } else if (NM_STR_HAS_PREFIX(line->str, SECRET_KEY_TAG)) { + nm_clear_g_string(&key); + key = g_string_new(line->str + strlen(SECRET_KEY_TAG)); + str = key; + hash = secrets; + } else if (NM_STR_HAS_PREFIX(line->str, SECRET_VAL_TAG)) { + if (val || !key || hash != secrets) + break; + val = g_string_new(line->str + strlen(SECRET_VAL_TAG)); + str = val; + } + +next: + g_string_truncate(line, 0); + + if (eof) + break; + } + + if (success) { + NM_SET_OUT(out_data, g_steal_pointer(&data)); + NM_SET_OUT(out_secrets, g_steal_pointer(&secrets)); + } + return success; +} + +/** + * nm_vpn_service_plugin_get_secret_flags: + * @data: hash table containing VPN key/value pair data items + * @secret_name: VPN secret key name for which to retrieve flags for + * @out_flags: (out): on success, the flags associated with @secret_name + * + * Given a VPN secret key name, attempts to find the corresponding flags data + * item in @data. If found, converts the flags data item to + * #NMSettingSecretFlags and returns it. + * + * Returns: %TRUE if the flag data item was found and successfully converted + * to flags, %FALSE if not + * + * Since: 1.2 + **/ +gboolean +nm_vpn_service_plugin_get_secret_flags(GHashTable * data, + const char * secret_name, + NMSettingSecretFlags *out_flags) +{ + gs_free char * flag_name_free = NULL; + const char * s; + gint64 t1; + NMSettingSecretFlags t0; + + g_return_val_if_fail(data, FALSE); + g_return_val_if_fail(out_flags && *out_flags == NM_SETTING_SECRET_FLAG_NONE, FALSE); + if (!secret_name || !*secret_name) + g_return_val_if_reached(FALSE); + + s = g_hash_table_lookup(data, nm_construct_name_a("%s-flags", secret_name, &flag_name_free)); + if (!s) + return FALSE; + t1 = _nm_utils_ascii_str_to_int64(s, 10, 0, G_MAXINT64, -1); + if (t1 == -1) + return FALSE; + t0 = (NMSettingSecretFlags) t1; + if ((gint64) t0 != t1) + return FALSE; + NM_SET_OUT(out_flags, t0); + return TRUE; +} + +/*****************************************************************************/ + +static void +impl_vpn_service_plugin_disconnect(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + gpointer user_data) +{ + GError *error = NULL; + + if (nm_vpn_service_plugin_disconnect(plugin, &error)) + g_dbus_method_invocation_return_value(context, NULL); + else + g_dbus_method_invocation_take_error(context, error); +} + +static void +impl_vpn_service_plugin_set_config(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * config, + gpointer user_data) +{ + nm_vpn_service_plugin_set_config(plugin, config); + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_vpn_service_plugin_set_ip4_config(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * config, + gpointer user_data) +{ + nm_vpn_service_plugin_set_ip4_config(plugin, config); + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_vpn_service_plugin_set_ip6_config(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * config, + gpointer user_data) +{ + nm_vpn_service_plugin_set_ip6_config(plugin, config); + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_vpn_service_plugin_set_failure(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + char * reason, + gpointer user_data) +{ + nm_vpn_service_plugin_failure(plugin, NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG); + g_dbus_method_invocation_return_value(context, NULL); +} + +/*****************************************************************************/ + +static void +_emit_quit(gpointer data, gpointer user_data) +{ + NMVpnServicePlugin *plugin = data; + + nm_vpn_service_plugin_emit_quit(plugin); +} + +static void +sigterm_handler(int signum) +{ + g_slist_foreach(active_plugins, _emit_quit, NULL); +} + +static void +setup_unix_signal_handler(void) +{ + struct sigaction action; + sigset_t block_mask; + + action.sa_handler = sigterm_handler; + sigemptyset(&block_mask); + action.sa_mask = block_mask; + action.sa_flags = 0; + sigaction(SIGINT, &action, NULL); + sigaction(SIGTERM, &action, NULL); +} + +/*****************************************************************************/ + +static void +one_plugin_destroyed(gpointer data, GObject *object) +{ + active_plugins = g_slist_remove(active_plugins, object); +} + +static void +nm_vpn_service_plugin_init(NMVpnServicePlugin *plugin) +{ + active_plugins = g_slist_append(active_plugins, plugin); + g_object_weak_ref(G_OBJECT(plugin), one_plugin_destroyed, NULL); +} + +static gboolean +init_sync(GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMVpnServicePlugin * plugin = NM_VPN_SERVICE_PLUGIN(initable); + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + gs_unref_object GDBusConnection *connection = NULL; + gs_unref_object GDBusProxy *proxy = NULL; + GVariant * ret; + + if (!priv->dbus_service_name) { + g_set_error_literal(error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + _("No service name specified")); + return FALSE; + } + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); + if (!connection) + return FALSE; + + proxy = g_dbus_proxy_new_sync(connection, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES + | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + cancellable, + error); + if (!proxy) + return FALSE; + + priv->dbus_vpn_service_plugin = nmdbus_vpn_plugin_skeleton_new(); + + _nm_dbus_bind_properties(plugin, priv->dbus_vpn_service_plugin); + _nm_dbus_bind_methods(plugin, + priv->dbus_vpn_service_plugin, + "Connect", + impl_vpn_service_plugin_connect, + "ConnectInteractive", + impl_vpn_service_plugin_connect_interactive, + "NeedSecrets", + impl_vpn_service_plugin_need_secrets, + "NewSecrets", + impl_vpn_service_plugin_new_secrets, + "Disconnect", + impl_vpn_service_plugin_disconnect, + "SetConfig", + impl_vpn_service_plugin_set_config, + "SetIp4Config", + impl_vpn_service_plugin_set_ip4_config, + "SetIp6Config", + impl_vpn_service_plugin_set_ip6_config, + "SetFailure", + impl_vpn_service_plugin_set_failure, + NULL); + + if (!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(priv->dbus_vpn_service_plugin), + connection, + NM_VPN_DBUS_PLUGIN_PATH, + error)) + return FALSE; + + nm_vpn_service_plugin_set_connection(plugin, connection); + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_INIT); + + ret = g_dbus_proxy_call_sync(proxy, + "RequestName", + g_variant_new("(su)", priv->dbus_service_name, 0), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (!ret) { + if (error && *error) + g_dbus_error_strip_remote_error(*error); + return FALSE; + } + g_variant_unref(ret); + + return TRUE; +} + +static void +set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_DBUS_SERVICE_NAME: + /* construct-only */ + priv->dbus_service_name = g_value_dup_string(value); + break; + case PROP_DBUS_WATCH_PEER: + /* construct-only */ + priv->dbus_watch_peer = g_value_get_boolean(value); + break; + case PROP_STATE: + nm_vpn_service_plugin_set_state(NM_VPN_SERVICE_PLUGIN(object), + (NMVpnServiceState) g_value_get_enum(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_DBUS_SERVICE_NAME: + g_value_set_string(value, priv->dbus_service_name); + break; + case PROP_DBUS_WATCH_PEER: + g_value_set_boolean(value, priv->dbus_watch_peer); + break; + case PROP_STATE: + g_value_set_enum(value, nm_vpn_service_plugin_get_state(NM_VPN_SERVICE_PLUGIN(object))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +dispose(GObject *object) +{ + nm_vpn_service_plugin_shutdown(NM_VPN_SERVICE_PLUGIN(object)); + G_OBJECT_CLASS(nm_vpn_service_plugin_parent_class)->dispose(object); +} + +static void +finalize(GObject *object) +{ + NMVpnServicePlugin * plugin = NM_VPN_SERVICE_PLUGIN(object); + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + nm_vpn_service_plugin_set_connection(plugin, NULL); + g_free(priv->dbus_service_name); + + nm_clear_pointer(&priv->banner, g_variant_unref); + nm_clear_pointer(&priv->tundev, g_variant_unref); + nm_clear_pointer(&priv->gateway, g_variant_unref); + nm_clear_pointer(&priv->mtu, g_variant_unref); + + G_OBJECT_CLASS(nm_vpn_service_plugin_parent_class)->finalize(object); +} + +static void +state_changed(NMVpnServicePlugin *plugin, NMVpnServiceState state) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + switch (state) { + case NM_VPN_SERVICE_STATE_STARTING: + nm_clear_g_source(&priv->quit_timer); + nm_clear_g_source(&priv->fail_stop_id); + break; + case NM_VPN_SERVICE_STATE_STOPPED: + if (priv->dbus_watch_peer) + nm_vpn_service_plugin_emit_quit(plugin); + else + schedule_quit_timer(plugin); + nm_clear_g_dbus_connection_signal(nm_vpn_service_plugin_get_connection(plugin), + &priv->peer_watch_id); + break; + default: + /* Clean up all timers we might have set up. */ + nm_clear_g_source(&priv->connect_timer); + nm_clear_g_source(&priv->quit_timer); + nm_clear_g_source(&priv->fail_stop_id); + break; + } +} + +static void +nm_vpn_service_plugin_class_init(NMVpnServicePluginClass *plugin_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(plugin_class); + + g_type_class_add_private(object_class, sizeof(NMVpnServicePluginPrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + plugin_class->state_changed = state_changed; + + /** + * NMVpnServicePlugin:service-name: + * + * The D-Bus service name of this plugin. + * + * Since: 1.2 + */ + obj_properties[PROP_DBUS_SERVICE_NAME] = + g_param_spec_string(NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME, + "", + "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + /** + * NMVpnServicePlugin:watch-peer: + * + * Whether to watch for D-Bus peer's changes. + * + * Since: 1.2 + */ + obj_properties[PROP_DBUS_WATCH_PEER] = + g_param_spec_boolean(NM_VPN_SERVICE_PLUGIN_DBUS_WATCH_PEER, + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + /** + * NMVpnServicePlugin:state: + * + * The state of the plugin. + * + * Since: 1.2 + */ + obj_properties[PROP_STATE] = g_param_spec_enum(NM_VPN_SERVICE_PLUGIN_STATE, + "", + "", + NM_TYPE_VPN_SERVICE_STATE, + NM_VPN_SERVICE_STATE_INIT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + signals[STATE_CHANGED] = g_signal_new("state-changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnServicePluginClass, state_changed), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + signals[SECRETS_REQUIRED] = g_signal_new("secrets-required", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 2, + G_TYPE_STRING, + G_TYPE_STRV); + + signals[CONFIG] = g_signal_new("config", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnServicePluginClass, config), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_VARIANT); + + signals[IP4_CONFIG] = g_signal_new("ip4-config", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnServicePluginClass, ip4_config), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_VARIANT); + + signals[IP6_CONFIG] = g_signal_new("ip6-config", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnServicePluginClass, ip6_config), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_VARIANT); + + signals[LOGIN_BANNER] = g_signal_new("login-banner", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnServicePluginClass, login_banner), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals[FAILURE] = g_signal_new("failure", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnServicePluginClass, failure), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + signals[QUIT] = g_signal_new("quit", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnServicePluginClass, quit), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0, + G_TYPE_NONE); + + setup_unix_signal_handler(); +} + +static void +nm_vpn_service_plugin_initable_iface_init(GInitableIface *iface) +{ + iface->init = init_sync; +} diff --git a/src/libnm-client-impl/nm-wifi-p2p-peer.c b/src/libnm-client-impl/nm-wifi-p2p-peer.c new file mode 100644 index 00000000..1d6b7c95 --- /dev/null +++ b/src/libnm-client-impl/nm-wifi-p2p-peer.c @@ -0,0 +1,575 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018 - 2019 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-wifi-p2p-peer.h" + +#include "nm-connection.h" +#include "nm-setting-connection.h" +#include "nm-setting-wifi-p2p.h" +#include "nm-utils.h" +#include "nm-dbus-interface.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_FLAGS, + PROP_NAME, + PROP_MANUFACTURER, + PROP_MODEL, + PROP_MODEL_NUMBER, + PROP_SERIAL, + PROP_WFD_IES, + PROP_HW_ADDRESS, + PROP_STRENGTH, + PROP_LAST_SEEN, ); + +typedef struct { + GBytes *wfd_ies; + char * name; + char * manufacturer; + char * model; + char * model_number; + char * serial; + char * hw_address; + gint32 last_seen; + guint32 flags; + guint8 strength; +} NMWifiP2PPeerPrivate; + +struct _NMWifiP2PPeer { + NMObject parent; + NMWifiP2PPeerPrivate _priv; +}; + +struct _NMWifiP2PPeerClass { + NMObjectClass parent; +}; + +G_DEFINE_TYPE(NMWifiP2PPeer, nm_wifi_p2p_peer, NM_TYPE_OBJECT) + +#define NM_WIFI_P2P_PEER_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMWifiP2PPeer, NM_IS_WIFI_P2P_PEER, NMObject) + +/*****************************************************************************/ + +/** + * nm_wifi_p2p_peer_get_flags: + * @peer: a #NMWifiP2PPeer + * + * Gets the flags of the P2P peer. + * + * Returns: the flags + * + * Since: 1.16 + **/ +NM80211ApFlags +nm_wifi_p2p_peer_get_flags(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NM_802_11_AP_FLAGS_NONE); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->flags; +} + +/** + * nm_wifi_p2p_peer_get_name: + * @peer: a #NMWifiP2PPeer + * + * Gets the name of the P2P peer. + * + * Returns: the name + * + * Since: 1.16 + **/ +const char * +nm_wifi_p2p_peer_get_name(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->name; +} + +/** + * nm_wifi_p2p_peer_get_manufacturer: + * @peer: a #NMWifiP2PPeer + * + * Gets the manufacturer of the P2P peer. + * + * Returns: the manufacturer + * + * Since: 1.16 + **/ +const char * +nm_wifi_p2p_peer_get_manufacturer(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->manufacturer; +} + +/** + * nm_wifi_p2p_peer_get_model: + * @peer: a #NMWifiP2PPeer + * + * Gets the model of the P2P peer. + * + * Returns: the model + * + * Since: 1.16 + **/ +const char * +nm_wifi_p2p_peer_get_model(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->model; +} + +/** + * nm_wifi_p2p_peer_get_model_number: + * @peer: a #NMWifiP2PPeer + * + * Gets the model number of the P2P peer. + * + * Returns: the model number + * + * Since: 1.16 + **/ +const char * +nm_wifi_p2p_peer_get_model_number(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->model_number; +} + +/** + * nm_wifi_p2p_peer_get_serial: + * @peer: a #NMWifiP2PPeer + * + * Gets the serial number of the P2P peer. + * + * Returns: the serial number + * + * Since: 1.16 + **/ +const char * +nm_wifi_p2p_peer_get_serial(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->serial; +} + +/** + * nm_wifi_p2p_peer_get_wfd_ies: + * @peer: a #NMWifiP2PPeer + * + * Gets the WFD information elements of the P2P peer. + * + * Returns: (transfer none): the #GBytes containing the WFD IEs, or %NULL. + * + * Since: 1.16 + **/ +GBytes * +nm_wifi_p2p_peer_get_wfd_ies(NMWifiP2PPeer *peer) +{ + NMWifiP2PPeerPrivate *priv; + + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NULL); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE(peer); + if (!priv->wfd_ies || g_bytes_get_size(priv->wfd_ies) == 0) + return NULL; + + return priv->wfd_ies; +} + +/** + * nm_wifi_p2p_peer_get_hw_address: + * @peer: a #NMWifiP2PPeer + * + * Gets the hardware address of the P2P peer. + * + * Returns: the hardware address + * + * Since: 1.16 + **/ +const char * +nm_wifi_p2p_peer_get_hw_address(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->hw_address; +} + +/** + * nm_wifi_p2p_peer_get_strength: + * @peer: a #NMWifiP2PPeer + * + * Gets the current signal strength of the P2P peer as a percentage. + * + * Returns: the signal strength (0 to 100) + * + * Since: 1.16 + **/ +guint8 +nm_wifi_p2p_peer_get_strength(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), 0); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->strength; +} + +/** + * nm_wifi_p2p_peer_get_last_seen: + * @peer: a #NMWifiP2PPeer + * + * Returns the timestamp (in CLOCK_BOOTTIME seconds) for the last time the + * P2P peer was seen. A value of -1 means the P2P peer has never been seen. + * + * Returns: the last seen time in seconds + * + * Since: 1.16 + **/ +int +nm_wifi_p2p_peer_get_last_seen(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), -1); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->last_seen; +} + +/** + * nm_wifi_p2p_peer_connection_valid: + * @peer: an #NMWifiP2PPeer to validate @connection against + * @connection: an #NMConnection to validate against @peer + * + * Validates a given connection against a given Wi-Fi P2P peer to ensure that + * the connection may be activated with that peer. The connection must match the + * @peer's address and in the future possibly other attributes. + * + * Returns: %TRUE if the connection may be activated with this Wi-Fi P2P Peer, + * %FALSE if it cannot be. + * + * Since: 1.16 + **/ +gboolean +nm_wifi_p2p_peer_connection_valid(NMWifiP2PPeer *peer, NMConnection *connection) +{ + NMSettingConnection *s_con; + NMSettingWifiP2P * s_wifi_p2p; + const char * ctype; + const char * hw_address; + const char * setting_peer; + + s_wifi_p2p = + (NMSettingWifiP2P *) nm_connection_get_setting(connection, NM_TYPE_SETTING_WIFI_P2P); + if (!s_wifi_p2p) + return FALSE; + + s_con = nm_connection_get_setting_connection(connection); + if (!s_con) + return FALSE; + + ctype = nm_setting_connection_get_connection_type(s_con); + if (!ctype || !nm_streq(ctype, NM_SETTING_WIFI_P2P_SETTING_NAME)) + return FALSE; + + /* HW Address check */ + hw_address = nm_wifi_p2p_peer_get_hw_address(peer); + if (!hw_address) + return FALSE; + + setting_peer = nm_setting_wifi_p2p_get_peer(s_wifi_p2p); + if (!setting_peer || !nm_streq(hw_address, setting_peer)) + return FALSE; + + return TRUE; +} + +/** + * nm_wifi_p2p_peer_filter_connections: + * @peer: an #NMWifiP2PPeer to filter connections for + * @connections: (element-type NMConnection): an array of #NMConnections to + * filter + * + * Filters a given array of connections for a given #NMWifiP2PPeer object and + * returns connections which may be activated with the P2P peer. Any + * returned connections will match the @peers's HW address and in the future + * possibly other attributes. + * + * To obtain the list of connections that are compatible with this P2P peer, + * use nm_client_get_connections() and then filter the returned list for a given + * #NMDevice using nm_device_filter_connections() and finally filter that list + * with this function. + * + * Returns: (transfer container) (element-type NMConnection): an array of + * #NMConnections that could be activated with the given @peer. The array should + * be freed with g_ptr_array_unref() when it is no longer required. + * + * Since: 1.16 + **/ +GPtrArray * +nm_wifi_p2p_peer_filter_connections(NMWifiP2PPeer *peer, const GPtrArray *connections) +{ + GPtrArray *filtered; + guint i; + + filtered = g_ptr_array_new_with_free_func(g_object_unref); + for (i = 0; i < connections->len; i++) { + NMConnection *candidate = connections->pdata[i]; + + if (nm_wifi_p2p_peer_connection_valid(peer, candidate)) + g_ptr_array_add(filtered, g_object_ref(candidate)); + } + + return filtered; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMWifiP2PPeer *peer = NM_WIFI_P2P_PEER(object); + + switch (prop_id) { + case PROP_FLAGS: + g_value_set_flags(value, nm_wifi_p2p_peer_get_flags(peer)); + break; + case PROP_NAME: + g_value_set_string(value, nm_wifi_p2p_peer_get_name(peer)); + break; + case PROP_MANUFACTURER: + g_value_set_string(value, nm_wifi_p2p_peer_get_manufacturer(peer)); + break; + case PROP_MODEL: + g_value_set_string(value, nm_wifi_p2p_peer_get_model(peer)); + break; + case PROP_MODEL_NUMBER: + g_value_set_string(value, nm_wifi_p2p_peer_get_model_number(peer)); + break; + case PROP_SERIAL: + g_value_set_string(value, nm_wifi_p2p_peer_get_serial(peer)); + break; + case PROP_WFD_IES: + g_value_set_boxed(value, nm_wifi_p2p_peer_get_wfd_ies(peer)); + break; + case PROP_HW_ADDRESS: + g_value_set_string(value, nm_wifi_p2p_peer_get_hw_address(peer)); + break; + case PROP_STRENGTH: + g_value_set_uchar(value, nm_wifi_p2p_peer_get_strength(peer)); + break; + case PROP_LAST_SEEN: + g_value_set_int(value, nm_wifi_p2p_peer_get_last_seen(peer)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_wifi_p2p_peer_init(NMWifiP2PPeer *peer) +{ + NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->last_seen = -1; +} + +static void +finalize(GObject *object) +{ + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE(object); + + g_free(priv->name); + g_free(priv->manufacturer); + g_free(priv->model); + g_free(priv->model_number); + g_free(priv->serial); + g_free(priv->hw_address); + + g_bytes_unref(priv->wfd_ies); + + G_OBJECT_CLASS(nm_wifi_p2p_peer_parent_class)->finalize(object); +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_wifip2ppeer = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_WIFI_P2P_PEER, + nm_wifi_p2p_peer_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_U("Flags", PROP_FLAGS, NMWifiP2PPeer, _priv.flags), + NML_DBUS_META_PROPERTY_INIT_IGNORE("Groups", "as"), + NML_DBUS_META_PROPERTY_INIT_S("HwAddress", + PROP_HW_ADDRESS, + NMWifiP2PPeer, + _priv.hw_address), + NML_DBUS_META_PROPERTY_INIT_I("LastSeen", PROP_LAST_SEEN, NMWifiP2PPeer, _priv.last_seen), + NML_DBUS_META_PROPERTY_INIT_S("Manufacturer", + PROP_MANUFACTURER, + NMWifiP2PPeer, + _priv.manufacturer), + NML_DBUS_META_PROPERTY_INIT_S("Model", PROP_MODEL, NMWifiP2PPeer, _priv.model), + NML_DBUS_META_PROPERTY_INIT_S("ModelNumber", + PROP_MODEL_NUMBER, + NMWifiP2PPeer, + _priv.model_number), + NML_DBUS_META_PROPERTY_INIT_S("Name", PROP_NAME, NMWifiP2PPeer, _priv.name), + NML_DBUS_META_PROPERTY_INIT_S("Serial", PROP_SERIAL, NMWifiP2PPeer, _priv.serial), + NML_DBUS_META_PROPERTY_INIT_Y("Strength", PROP_STRENGTH, NMWifiP2PPeer, _priv.strength), + NML_DBUS_META_PROPERTY_INIT_AY("WfdIEs", PROP_WFD_IES, NMWifiP2PPeer, _priv.wfd_ies), ), ); + +static void +nm_wifi_p2p_peer_class_init(NMWifiP2PPeerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + /** + * NMWifiP2PPeer:flags: + * + * The flags of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_FLAGS] = g_param_spec_flags(NM_WIFI_P2P_PEER_FLAGS, + "", + "", + NM_TYPE_802_11_AP_FLAGS, + NM_802_11_AP_FLAGS_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:name: + * + * The name of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_NAME] = g_param_spec_string(NM_WIFI_P2P_PEER_NAME, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:manufacturer: + * + * The manufacturer of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_MANUFACTURER] = + g_param_spec_string(NM_WIFI_P2P_PEER_MANUFACTURER, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:model: + * + * The model of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_MODEL] = g_param_spec_string(NM_WIFI_P2P_PEER_MODEL, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:model-number: + * + * The hardware address of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_MODEL_NUMBER] = + g_param_spec_string(NM_WIFI_P2P_PEER_MODEL_NUMBER, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:serial: + * + * The serial number of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_SERIAL] = g_param_spec_string(NM_WIFI_P2P_PEER_SERIAL, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:wfd-ies: + * + * The WFD information elements of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_WFD_IES] = g_param_spec_boxed(NM_WIFI_P2P_PEER_WFD_IES, + "", + "", + G_TYPE_BYTES, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + /** + * NMWifiP2PPeer:hw-address: + * + * The hardware address of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_HW_ADDRESS] = + g_param_spec_string(NM_WIFI_P2P_PEER_HW_ADDRESS, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:strength: + * + * The current signal strength of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_STRENGTH] = g_param_spec_uchar(NM_WIFI_P2P_PEER_STRENGTH, + "", + "", + 0, + G_MAXUINT8, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:last-seen: + * + * The timestamp (in CLOCK_BOOTTIME seconds) for the last time the + * P2P peer was found. A value of -1 means the peer has never been seen. + * + * Since: 1.16 + **/ + obj_properties[PROP_LAST_SEEN] = g_param_spec_int(NM_WIFI_P2P_PEER_LAST_SEEN, + "", + "", + -1, + G_MAXINT, + -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_wifip2ppeer); +} diff --git a/src/libnm-client-impl/nm-wimax-nsp.c b/src/libnm-client-impl/nm-wimax-nsp.c new file mode 100644 index 00000000..62181d6b --- /dev/null +++ b/src/libnm-client-impl/nm-wimax-nsp.c @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2011 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-wimax-nsp.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_NAME, PROP_SIGNAL_QUALITY, PROP_NETWORK_TYPE, ); + +struct _NMWimaxNsp { + NMObject parent; +}; + +struct _NMWimaxNspClass { + NMObjectClass parent; +}; + +G_DEFINE_TYPE(NMWimaxNsp, nm_wimax_nsp, NM_TYPE_OBJECT) + +#define NM_WIMAX_NSP_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMWimaxNsp, NM_IS_WIMAX_NSP, NMObject) + +/*****************************************************************************/ + +/** + * nm_wimax_nsp_get_name: + * @nsp: a #NMWimaxNsp + * + * Gets the name of the wimax NSP + * + * Returns: the name + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ +const char * +nm_wimax_nsp_get_name(NMWimaxNsp *nsp) +{ + g_return_val_if_reached(NULL); +} + +/** + * nm_wimax_nsp_get_signal_quality: + * @nsp: a #NMWimaxNsp + * + * Gets the WPA signal quality of the wimax NSP. + * + * Returns: the signal quality + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ +guint32 +nm_wimax_nsp_get_signal_quality(NMWimaxNsp *nsp) +{ + g_return_val_if_reached(0); +} + +/** + * nm_wimax_nsp_get_network_type: + * @nsp: a #NMWimaxNsp + * + * Gets the network type of the wimax NSP. + * + * Returns: the network type + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ +NMWimaxNspNetworkType +nm_wimax_nsp_get_network_type(NMWimaxNsp *nsp) +{ + g_return_val_if_reached(NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN); +} + +/** + * nm_wimax_nsp_connection_valid: + * @nsp: an #NMWimaxNsp to validate @connection against + * @connection: an #NMConnection to validate against @nsp + * + * Validates a given connection against a given WiMAX NSP to ensure that the + * connection may be activated with that NSP. The connection must match the + * @nsp's network name and other attributes. + * + * Returns: %TRUE if the connection may be activated with this WiMAX NSP, + * %FALSE if it cannot be. + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ +gboolean +nm_wimax_nsp_connection_valid(NMWimaxNsp *nsp, NMConnection *connection) +{ + g_return_val_if_reached(FALSE); +} + +/** + * nm_wimax_nsp_filter_connections: + * @nsp: an #NMWimaxNsp to filter connections for + * @connections: (element-type NMConnection): an array of #NMConnections to + * filter + * + * Filters a given array of connections for a given #NMWimaxNsp object and + * return connections which may be activated with the NSP. Any returned + * connections will match the @nsp's network name and other attributes. + * + * Returns: (transfer full) (element-type NMConnection): an array of + * #NMConnections that could be activated with the given @nsp. The array should + * be freed with g_ptr_array_unref() when it is no longer required. + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ +GPtrArray * +nm_wimax_nsp_filter_connections(NMWimaxNsp *nsp, const GPtrArray *connections) +{ + g_return_val_if_reached(NULL); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + g_return_if_reached(); +} + +static void +nm_wimax_nsp_init(NMWimaxNsp *nsp) +{ + g_return_if_reached(); +} + +static void +nm_wimax_nsp_class_init(NMWimaxNspClass *nsp_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(nsp_class); + + object_class->get_property = get_property; + + /** + * NMWimaxNsp:name: + * + * The name of the WiMAX NSP. + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ + obj_properties[PROP_NAME] = g_param_spec_string(NM_WIMAX_NSP_NAME, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWimaxNsp:signal-quality: + * + * The signal quality of the WiMAX NSP. + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ + obj_properties[PROP_SIGNAL_QUALITY] = + g_param_spec_uint(NM_WIMAX_NSP_SIGNAL_QUALITY, + "", + "", + 0, + 100, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWimaxNsp:network-type: + * + * The network type of the WiMAX NSP. + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ + obj_properties[PROP_NETWORK_TYPE] = + g_param_spec_enum(NM_WIMAX_NSP_NETWORK_TYPE, + "", + "", + NM_TYPE_WIMAX_NSP_NETWORK_TYPE, + NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} diff --git a/src/libnm-client-impl/tests/meson.build b/src/libnm-client-impl/tests/meson.build new file mode 100644 index 00000000..3f2e78a7 --- /dev/null +++ b/src/libnm-client-impl/tests/meson.build @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +test_units = [ + 'test-libnm', + 'test-nm-client', + 'test-remote-settings-client', + 'test-secret-agent', +] + +foreach test_unit: test_units + exe = executable( + test_unit, + [ test_unit + '.c' ] + (test_unit == 'test-libnm' ? src_contrib_nm_compat_source : []), + include_directories: [ + libnm_client_public_inc, + ], + dependencies: [ + libnm_core_public_dep, + libnm_client_public_dep, + glib_dep, + libudev_dep, + ], + link_with: [ + libnm_client_impl, + libnm_client_test, + libnm_core_aux_intern, + libnm_core_impl, + libnm_crypto, + libnm_base, + libnm_udev_aux, + libnm_systemd_shared, + libnm_log_null, + libnm_glib_aux, + libnm_std_aux, + libc_siphash, + libnmdbus, + ], + ) + + test( + 'src/libnm-client-impl/tests/' + test_unit, + test_script, + timeout: 90, + args: test_args + [exe.full_path()], + ) +endforeach diff --git a/src/libnm-client-impl/tests/test-libnm.c b/src/libnm-client-impl/tests/test-libnm.c new file mode 100644 index 00000000..e470c63e --- /dev/null +++ b/src/libnm-client-impl/tests/test-libnm.c @@ -0,0 +1,3502 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#if defined(HAVE_DECL_MEMFD_CREATE) && HAVE_DECL_MEMFD_CREATE + #include <linux/memfd.h> +#endif + +#include <sys/mman.h> + +#include "NetworkManager.h" +#include "nm-access-point.h" +#include "nm-checkpoint.h" +#include "libnm-client-impl/nm-dhcp4-config.h" +#include "libnm-client-impl/nm-dhcp6-config.h" +#include "libnm-client-impl/nm-dns-manager.h" +#include "libnm-client-impl/nm-ip4-config.h" +#include "libnm-client-impl/nm-ip6-config.h" +#include "libnm-client-impl/nm-libnm-utils.h" +#include "nm-object.h" +#include "nm-vpn-service-plugin.h" +#include "libnm-core-aux-intern/nm-libnm-core-utils.h" + +#include "libnm-glib-aux/nm-test-utils.h" + +/*****************************************************************************/ + +typedef struct { + const char *desc; + const char *expected; + int line; +} TestFixupData; + +static void +_test_fixup_string(const TestFixupData *data, guint n_data, char *(*func)(const char *) ) +{ + guint i; + + g_assert(data); + g_assert(n_data > 0); + g_assert(func); + + for (i = 0; i < n_data; i++, data++) { + gs_free char *value = func(data->desc); + + if (!nm_streq0(value, data->expected)) { + g_error("Error comparing value: %s:%i: expected %s%s%s for %s%s%s, but got %s%s%s", + __FILE__, + data->line, + NM_PRINT_FMT_QUOTE_STRING(data->expected), + NM_PRINT_FMT_QUOTE_STRING(data->desc), + NM_PRINT_FMT_QUOTE_STRING(value)); + } + } +} + +#define T_DATA(_desc, _expected) \ + { \ + .desc = _desc, .expected = _expected, .line = __LINE__, \ + } + +static void +test_fixup_vendor_string(void) +{ + static const TestFixupData data[] = { + T_DATA("3Com", "3Com"), + T_DATA("3Com Corp.", "3Com"), + T_DATA("3Com Corporation", "3Com"), + T_DATA("Abocom Systems Inc", "Abocom"), + T_DATA("AboCom Systems Inc", "AboCom"), + T_DATA("Accton Technology Corp.", "Accton"), + T_DATA("Accton Technology Corporation", "Accton"), + T_DATA("Acer Communications & Multimedia", "Acer"), + T_DATA("Actiontec Electronics, Inc. [hex]", "Actiontec"), + T_DATA("Adaptec", "Adaptec"), + T_DATA("Addtron Technology Co, Inc.", "Addtron"), + T_DATA("ADMtek", "ADMtek"), + T_DATA("ADMtek, Inc.", "ADMtek"), + T_DATA("ADS Technologies, Inc.", "ADS"), + T_DATA("Advanced Micro Devices, Inc. [AMD]", "AMD"), + T_DATA("Advance Multimedia Internet Technology Inc. (AMIT)", "Advance"), + T_DATA("AEI", "AEI"), + T_DATA("Airprime, Incorporated", "Airprime"), + T_DATA("AirTies Wireless Networks", "AirTies"), + T_DATA("AirVast", "AirVast"), + T_DATA("Alcatel Telecom", "Alcatel Telecom"), + T_DATA("ALi Corp.", "ALi"), + T_DATA("Allied Telesis", "Allied Telesis"), + T_DATA("Allied Telesyn International", "Allied Telesyn"), + T_DATA("Alteon Networks Inc.", "Alteon"), + T_DATA("Altima (nee Broadcom)", "Altima"), + T_DATA("A-Max Technology Macao Commercial Offshore Co. Ltd.", "A-Max"), + T_DATA("Amigo Technology Inc.", "Amigo"), + T_DATA("AMIT Technology, Inc.", "AMIT"), + T_DATA("Anchor Chips, Inc.", "Anchor"), + T_DATA("AnyDATA Corporation", "AnyDATA"), + T_DATA("Apple Inc.", "Apple"), + T_DATA("Apple, Inc.", "Apple"), + T_DATA("ARC International", "ARC"), + T_DATA("ASIX Electronics Corp.", "ASIX"), + T_DATA("Asix Electronics Corporation", "Asix"), + T_DATA("Askey Computer Corp. [hex]", "Askey"), + T_DATA("ASUSTek Computer, Inc.", "ASUSTek Computer"), + T_DATA("ASUSTek Computer, Inc. (wrong ID)", "ASUSTek Computer"), + T_DATA("ATEN International Co., Ltd", "ATEN"), + T_DATA("Atheros Communications", "Atheros"), + T_DATA("Atheros Communications, Inc.", "Atheros"), + T_DATA("AVM GmbH", "AVM"), + T_DATA("AzureWave", "AzureWave"), + T_DATA("Belkin", "Belkin"), + T_DATA("Belkin Components", "Belkin"), + T_DATA("Billionton Systems, Inc.", "Billionton"), + T_DATA("Broadcom Corp.", "Broadcom"), + T_DATA("Broadcom Limited", "Broadcom"), + T_DATA("Brocade Communications Systems, Inc.", "Brocade"), + T_DATA("BUFFALO INC. (formerly MelCo., Inc.)", "BUFFALO"), + T_DATA("CACE Technologies Inc.", "CACE"), + T_DATA("Cadence Design Systems, Inc.", "Cadence"), + T_DATA("Chelsio Communications Inc", "Chelsio"), + T_DATA("Chicony Electronics Co., Ltd", "Chicony"), + T_DATA("Chu Yuen Enterprise Co., Ltd", "Chu Yuen"), + T_DATA("Cisco Systems Inc", "Cisco"), + T_DATA("Cisco Systems, Inc.", "Cisco"), + T_DATA("CMOTECH Co., Ltd.", "CMOTECH"), + T_DATA("CNet Technology Inc.", "CNet"), + T_DATA("CNet Technology Inc", "CNet"), + T_DATA("Comneon", "Comneon"), + T_DATA("Compaq Computer Corp.", "Compaq"), + T_DATA("Compaq Computer Corporation", "Compaq"), + T_DATA("Compex", "Compex"), + T_DATA("Computer Access Technology Corp.", "Computer Access"), + T_DATA("Conexant Systems, Inc.", "Conexant"), + T_DATA("Conexant Systems (Rockwell), Inc.", "Conexant"), + T_DATA("Corega K.K.", "Corega K.K."), + T_DATA("Curitel Communications, Inc.", "Curitel"), + T_DATA("CyberTAN Technology", "CyberTAN"), + T_DATA("Cypress Semiconductor Corp.", "Cypress"), + T_DATA("Davicom Semiconductor, Inc.", "Davicom"), + T_DATA("Dell Computer Corp.", "Dell"), + T_DATA("DELTA Electronics, Inc", "DELTA"), + T_DATA("Digital Equipment Corporation", "Digital Equipment"), + T_DATA("D-Link Corp.", "D-Link"), + T_DATA("D-Link System", "D-Link System"), + T_DATA("D-Link System Inc", "D-Link System"), + T_DATA("DrayTek Corp.", "DrayTek"), + T_DATA("d'TV", "d'TV"), + T_DATA("DVICO", "DVICO"), + T_DATA("Edimax Computer Co.", "Edimax"), + T_DATA("Edimax Technology Co., Ltd", "Edimax"), + T_DATA("Efar Microsystems", "Efar"), + T_DATA("Efficient Networks, Inc.", "Efficient"), + T_DATA("ELCON Systemtechnik", "ELCON"), + T_DATA("Elecom Co., Ltd", "Elecom"), + T_DATA("ELSA AG", "ELSA"), + T_DATA("Emulex Corporation", "Emulex"), + T_DATA("Encore Electronics Inc.", "Encore"), + T_DATA("EndPoints, Inc.", "EndPoints"), + T_DATA("Entrega [hex]", "Entrega"), + T_DATA("Ericsson Business Mobile Networks BV", "Ericsson"), + T_DATA("eTEK Labs", "eTEK"), + T_DATA("Exar Corp.", "Exar"), + T_DATA("Fiberline", "Fiberline"), + T_DATA("Fujitsu Limited.", "Fujitsu"), + T_DATA("Fujitsu Siemens Computers", "Fujitsu Siemens"), + T_DATA("Gateway, Inc.", "Gateway"), + T_DATA("Gemtek", "Gemtek"), + T_DATA("Genesys Logic, Inc.", "Genesys Logic"), + T_DATA("Global Sun Technology", "Global Sun"), + T_DATA("Global Sun Technology, Inc.", "Global Sun"), + T_DATA("GlobeSpan, Inc.", "GlobeSpan"), + T_DATA("Gmate, Inc.", "Gmate"), + T_DATA("Good Way Technology", "Good Way"), + T_DATA("Guillemot Corp.", "Guillemot"), + T_DATA("Hangzhou Silan Microelectronics Co., Ltd.", "Hangzhou Silan"), + T_DATA("Hawking Technologies", "Hawking"), + T_DATA("Hewlett-Packard", "Hewlett-Packard"), + T_DATA("Hirose Electric", "Hirose Electric"), + T_DATA("Holtek Microelectronics Inc", "Holtek"), + T_DATA("Huawei-3Com", "Huawei-3Com"), + T_DATA("Huawei Technologies Co., Ltd.", "Huawei"), + T_DATA("ICS Advent", "ICS Advent"), + T_DATA("IMC Networks", "IMC"), + T_DATA("Intel Corp.", "Intel"), + T_DATA("Intel Corporation", "Intel"), + T_DATA("Intellon Corp.", "Intellon"), + T_DATA("InterBiometrics", "InterBiometrics"), + T_DATA("Intersil Corp.", "Intersil"), + T_DATA("Intersil Corporation", "Intersil"), + T_DATA("I-O Data Device, Inc.", "I-O Data Device"), + T_DATA("Jaton Corp.", "Jaton"), + T_DATA("JMicron Technology Corp.", "JMicron"), + T_DATA("Kawasaki LSI", "Kawasaki LSI"), + T_DATA("KC Technology, Inc.", "KC"), + T_DATA("Kingston Technology", "Kingston"), + T_DATA("KTI", "KTI"), + T_DATA("Kvaser AB", "Kvaser"), + T_DATA("LapLink, Inc.", "LapLink"), + T_DATA("Lenovo", "Lenovo"), + T_DATA("LevelOne", "LevelOne"), + T_DATA("LG Electronics, Inc.", "LG"), + T_DATA("LG Electronics USA, Inc.", "LG"), + T_DATA("Linksys, Inc.", "Linksys"), + T_DATA("Linksys (?)", "Linksys"), + T_DATA("Linksys", "Linksys"), + T_DATA("Lite-On Communications Inc", "Lite-On"), + T_DATA("Lite-On Technology Corp.", "Lite-On"), + T_DATA("Logitec Corp.", "Logitec"), + T_DATA("Logitech, Inc.", "Logitech"), + T_DATA("LSI Corporation", "LSI"), + T_DATA("LSI Logic / Symbios Logic", "LSI Logic"), + T_DATA("Macronix, Inc. [MXIC]", "MXIC"), + T_DATA("Marvell Semiconductor, Inc.", "Marvell"), + T_DATA("Marvell Technology Group Ltd.", "Marvell"), + T_DATA("MediaTek Inc.", "MediaTek"), + T_DATA("Mellanox Technologies", "Mellanox"), + T_DATA("Memorex", "Memorex"), + T_DATA("Micrel-Kendin", "Micrel-Kendin"), + T_DATA("Microchip Technology, Inc.", "Microchip"), + T_DATA("Microcomputer Systems (M) Son", "Microcomputer Son"), + T_DATA("Microsoft Corp.", "Microsoft"), + T_DATA("Microsoft Corporation", "Microsoft"), + T_DATA("Micro-Star International Co., Ltd. [MSI]", "MSI"), + T_DATA("Micro Star International", "Micro Star"), + T_DATA("Mobility", "Mobility"), + T_DATA("MosChip Semiconductor", "MosChip"), + T_DATA("Motorola PCS", "Motorola"), + T_DATA("MYRICOM Inc.", "MYRICOM"), + T_DATA("MYSON Technology Inc", "MYSON"), + T_DATA("National Instruments Corp.", "National"), + T_DATA("National Semiconductor Corporation", "National"), + T_DATA("NEC Corp.", "NEC"), + T_DATA("Netchip Technology, Inc.", "Netchip"), + T_DATA("Netgear, Inc", "Netgear"), + T_DATA("NetGear, Inc.", "NetGear"), + T_DATA("Netgear", "Netgear"), + T_DATA("Netopia, Inc.", "Netopia"), + T_DATA("Netronome Systems, Inc.", "Netronome"), + T_DATA("NetVin", "NetVin"), + T_DATA("NetXen Incorporated", "NetXen"), + T_DATA("Nordic Semiconductor ASA", "Nordic"), + T_DATA("Northern Telecom", "Northern Telecom"), + T_DATA("NovaTech", "NovaTech"), + T_DATA("Novatel Wireless", "Novatel Wireless"), + T_DATA("NVIDIA Corp.", "NVIDIA"), + T_DATA("NVIDIA Corporation", "NVIDIA"), + T_DATA("Olicom", "Olicom"), + T_DATA("Olivetti Techcenter", "Olivetti"), + T_DATA("Olympus Optical Co., Ltd", "Olympus"), + T_DATA("OMEGA TECHNOLOGY", "OMEGA"), + T_DATA("Omnidirectional Control Technology, Inc.", "Omnidirectional Control"), + T_DATA("OpenMoko, Inc.", "OpenMoko"), + T_DATA("Option", "Option"), + T_DATA("OQO", "OQO"), + T_DATA("Oracle/SUN", "Oracle"), + T_DATA("Ovislink Corp.", "Ovislink"), + T_DATA("Packet Engines Inc.", "Packet Engines"), + T_DATA("Panasonic (Matsushita)", "Panasonic"), + T_DATA("PEAK System", "PEAK System"), + T_DATA("PEAK-System Technik GmbH", "PEAK-System"), + T_DATA("PEGATRON CORPORATION", "PEGATRON CORPORATION"), + T_DATA("Peppercon AG", "Peppercon"), + T_DATA("Peracom Networks, Inc.", "Peracom"), + T_DATA("Philips (or NXP)", "Philips"), + T_DATA("Planex Communications, Inc", "Planex"), + T_DATA("Planex Communications", "Planex"), + T_DATA("Planex", "Planex"), + T_DATA("PLANEX", "PLANEX"), + T_DATA("Portsmith", "Portsmith"), + T_DATA("Prolific Technology, Inc.", "Prolific"), + T_DATA("Qcom", "Qcom"), + T_DATA("Qi Hardware", "Qi"), + T_DATA("QinHeng Electronics", "QinHeng"), + T_DATA("QLogic Corp.", "QLogic"), + T_DATA("Qualcomm Atheros Communications", "Qualcomm Atheros"), + T_DATA("Qualcomm Atheros", "Qualcomm Atheros"), + T_DATA("Qualcomm, Inc.", "Qualcomm"), + T_DATA("Qualcomm / Option", "Qualcomm"), + T_DATA("Quanta Computer, Inc.", "Quanta Computer"), + T_DATA("Quanta Microsystems, Inc.", "Quanta"), + T_DATA("Quantenna Communications, Inc.", "Quantenna"), + T_DATA("RadioShack Corp. (Tandy)", "RadioShack"), + T_DATA("Ralink corp.", "Ralink"), + T_DATA("Ralink Technology, Corp.", "Ralink"), + T_DATA("RDC Semiconductor, Inc.", "RDC"), + T_DATA("Realtek Semiconductor Co., Ltd.", "Realtek"), + T_DATA("Realtek Semiconductor Corp.", "Realtek"), + T_DATA("Red Hat, Inc.", "Red Hat"), + T_DATA("SafeNet (wrong ID)", "SafeNet"), + T_DATA("Sagem", "Sagem"), + T_DATA("Samsung Electronics Co., Ltd", "Samsung"), + T_DATA("Sega Enterprises Ltd", "Sega"), + T_DATA("Senao", "Senao"), + T_DATA("Shark Multimedia", "Shark"), + T_DATA("Sharp Corp.", "Sharp"), + T_DATA("Siemens Information and Communication Products", "Siemens"), + T_DATA("Sierra Wireless, Inc.", "Sierra Wireless"), + T_DATA("Silicom", "Silicom"), + T_DATA("Silicon Graphics Intl. Corp.", "Silicon Graphics"), + T_DATA("Silicon Integrated Systems [SiS]", "SiS"), + T_DATA("Sitecom Europe B.V.", "Sitecom"), + T_DATA("Sitecom", "Sitecom"), + T_DATA("smartBridges, Inc.", "smartBridges"), + T_DATA("SohoWare", "SohoWare"), + T_DATA("Solarflare Communications", "Solarflare"), + T_DATA("Sony Corp.", "Sony"), + T_DATA("SpeedStream", "SpeedStream"), + T_DATA("Sphairon Access Systems GmbH", "Sphairon"), + T_DATA("Standard Microsystems Corp [SMC]", "SMC"), + T_DATA("Standard Microsystems Corp.", "Standard"), + T_DATA("STMicroelectronics", "STMicroelectronics"), + T_DATA("Sundance Technology Inc / IC Plus Corp", "Sundance"), + T_DATA("Surecom Technology Corp.", "Surecom"), + T_DATA("Surecom Technology", "Surecom"), + T_DATA("Sweex", "Sweex"), + T_DATA("SysKonnect", "SysKonnect"), + T_DATA("T & A Mobile Phones", "T & A"), + T_DATA("TDK Semiconductor Corp.", "TDK"), + T_DATA("Tehuti Networks Ltd.", "Tehuti"), + T_DATA("Tekram Technology Co., Ltd", "Tekram"), + T_DATA("Telit Wireless Solutions", "Telit"), + T_DATA("Texas Instruments, Inc.", "Texas"), + T_DATA("Thales Norway A/S", "Thales"), + T_DATA("TMT Technology, Inc.", "TMT"), + T_DATA("Toshiba Corp.", "Toshiba"), + T_DATA("TRENDnet", "TRENDnet"), + T_DATA("Trident Microsystems", "Trident"), + T_DATA("Trust International B.V.", "Trust"), + T_DATA("TTTech Computertechnik AG (Wrong ID)", "TTTech"), + T_DATA("TwinMOS", "TwinMOS"), + T_DATA("U-Blox AG", "U-Blox"), + T_DATA("ULi Electronics Inc.", "ULi"), + T_DATA("U.S. Robotics", "U.S. Robotics"), + T_DATA("Vaillant", "Vaillant"), + T_DATA("VIA Technologies, Inc.", "VIA"), + T_DATA("Victor Company of Japan, Ltd", "Victor"), + T_DATA("VMware", "VMware"), + T_DATA("VTech Holdings, Ltd", "VTech"), + T_DATA("Wavecom", "Wavecom"), + T_DATA("Westell", "Westell"), + T_DATA("Western Digital Technologies, Inc.", "Western Digital"), + T_DATA("Wilocity Ltd.", "Wilocity"), + T_DATA("Winbond Electronics Corp", "Winbond"), + T_DATA("Winbond", "Winbond"), + T_DATA("Wistron NeWeb", "Wistron NeWeb"), + T_DATA("Xircom", "Xircom"), + T_DATA("Z-Com", "Z-Com"), + T_DATA("Zinwell", "Zinwell"), + T_DATA("Zoom Telephonics, Inc.", "Zoom"), + T_DATA("ZTE WCDMA Technologies MSM", "ZTE"), + T_DATA("ZyDAS", "ZyDAS"), + T_DATA("ZyXEL Communications Corp.", "ZyXEL"), + }; + + _test_fixup_string(data, G_N_ELEMENTS(data), nm_utils_fixup_vendor_string); +} + +static void +test_fixup_product_string(void) +{ + static const TestFixupData data[] = { + T_DATA("10/100BaseTX [RTL81xx]", "RTL81xx"), + T_DATA("10/100 Ethernet", NULL), + T_DATA("10/100 Ethernet [pegasus]", "pegasus"), + T_DATA("10/100 USB Ethernet", NULL), + T_DATA("10/100 USB NIC", NULL), + T_DATA("1010/1020/1007/1741 10Gbps CNA", "1010/1020/1007/1741"), + T_DATA("1012 PCMCIA 10/100 Ethernet Card [RTL81xx]", "1012"), + T_DATA("101 Ethernet [klsi]", "101"), + T_DATA("10GbE Converged Network Adapter (TCP/IP Networking)", NULL), + T_DATA("10GbE Ethernet Adapter", NULL), + T_DATA("10 Gigabit BR KX4 Dual Port Network Connection", "BR KX4"), + T_DATA("10-Giga TOE SmartNIC 2-Port", "SmartNIC"), + T_DATA("10-Giga TOE SmartNIC", "SmartNIC"), + T_DATA("10Mbps Ethernet [klsi]", "klsi"), + T_DATA("1860 16Gbps/10Gbps Fabric Adapter", "1860"), + T_DATA("190 Ethernet Adapter", "190"), + T_DATA("191 Gigabit Ethernet Adapter", "191"), + T_DATA("21145 Fast Ethernet", "21145"), + T_DATA("21x4x DEC-Tulip compatible 10/100 Ethernet", "21x4x"), + T_DATA("21x4x DEC-Tulip compatible Fast Ethernet", "21x4x"), + T_DATA("2202 Ethernet [klsi]", "2202"), + T_DATA("2202 Ethernet [pegasus]", "2202"), + T_DATA("3C19250 Ethernet [klsi]", "3C19250"), + T_DATA("3c450 HomePNA [Tornado]", "3c450 HomePNA"), + T_DATA("3C460B 10/100 Ethernet Adapter", "3C460B"), + T_DATA("3c555 Laptop Hurricane", "3c555 Hurricane"), + T_DATA("3c556B CardBus [Tornado]", "3c556B"), + T_DATA("3c556 Hurricane CardBus [Cyclone]", "3c556 Hurricane"), + T_DATA("3c575 Megahertz 10/100 LAN CardBus [Boomerang]", "3c575 Megahertz"), + T_DATA("3c590 10BaseT [Vortex]", "3c590"), + T_DATA("3c592 EISA 10mbps Demon/Vortex", "3c592 Demon/Vortex"), + T_DATA("3c595 100Base-MII [Vortex]", "3c595"), + T_DATA("3c595 100BaseT4 [Vortex]", "3c595"), + T_DATA("3c595 100BaseTX [Vortex]", "3c595"), + T_DATA("3c595 Megahertz 10/100 LAN CardBus [Boomerang]", "3c595 Megahertz"), + T_DATA("3c597 EISA Fast Demon/Vortex", "3c597 Fast Demon/Vortex"), + T_DATA("3c900 10BaseT [Boomerang]", "3c900"), + T_DATA("3c900 10Mbps Combo [Boomerang]", "3c900 Combo"), + T_DATA("3c900B-Combo Etherlink XL [Cyclone]", "3c900B-Combo Etherlink XL"), + T_DATA("3c900B-FL 10base-FL [Cyclone]", "3c900B-FL"), + T_DATA("3c900B-TPC Etherlink XL [Cyclone]", "3c900B-TPC Etherlink XL"), + T_DATA("3c900B-TPO Etherlink XL [Cyclone]", "3c900B-TPO Etherlink XL"), + T_DATA("3c905 100BaseT4 [Boomerang]", "3c905"), + T_DATA("3c905 100BaseTX [Boomerang]", "3c905"), + T_DATA("3c905B 100BaseTX [Cyclone]", "3c905B"), + T_DATA("3c905B Deluxe Etherlink 10/100/BNC [Cyclone]", "3c905B Deluxe Etherlink"), + T_DATA("3c905B-FX Fast Etherlink XL FX 100baseFx [Cyclone]", + "3c905B-FX Fast Etherlink XL FX"), + T_DATA("3c905B-T4 Fast EtherLink XL [Cyclone]", "3c905B-T4 Fast EtherLink XL"), + T_DATA("3C905B-TX Fast Etherlink XL PCI", "3C905B-TX Fast Etherlink XL"), + T_DATA("3c905C-TX/TX-M [Tornado]", "3c905C-TX/TX-M"), + T_DATA("3C920B-EMB Integrated Fast Ethernet Controller [Tornado]", "3C920B-EMB"), + T_DATA("3C920B-EMB-WNM Integrated Fast Ethernet Controller", "3C920B-EMB-WNM"), + T_DATA("3c940 10/100/1000Base-T [Marvell]", "3c940"), + T_DATA("3c940B 10/100/1000Base-T", "3c940B"), + T_DATA("3c980-C 10/100baseTX NIC [Python-T]", "3c980-C"), + T_DATA("3c980-TX Fast Etherlink XL Server Adapter [Cyclone]", "3c980-TX Fast Etherlink XL"), + T_DATA("3c982-TXM 10/100baseTX Dual Port A [Hydra]", "3c982-TXM A"), + T_DATA("3c982-TXM 10/100baseTX Dual Port B [Hydra]", "3c982-TXM B"), + T_DATA("3c985 1000BaseSX (SX/TX)", "3c985"), + T_DATA("3C990B-TX-M/3C990BSVR [Typhoon2]", "3C990B-TX-M/3C990BSVR"), + T_DATA("3C990SVR [Typhoon Server]", "3C990SVR"), + T_DATA("3C990-TX [Typhoon]", "3C990-TX"), + T_DATA("3cCFE575BT Megahertz 10/100 LAN CardBus [Cyclone]", "3cCFE575BT Megahertz"), + T_DATA("3cCFE575CT CardBus [Cyclone]", "3cCFE575CT"), + T_DATA("3cCFE656 CardBus [Cyclone]", "3cCFE656"), + T_DATA("3cCFEM656B 10/100 LAN+Winmodem CardBus [Cyclone]", "3cCFEM656B"), + T_DATA("3Com 3C920B-EMB-WNM Integrated Fast Ethernet Controller", "3Com 3C920B-EMB-WNM"), + T_DATA("3Com 3CRUSBN275 802.11abgn Wireless Adapter [Atheros AR9170]", "3Com 3CRUSBN275"), + T_DATA("3com 3CRWE154G72 [Office Connect Wireless LAN Adapter]", "3com 3CRWE154G72"), + T_DATA("3CR990-FX-95/97/95 [Typhon Fiber]", "3CR990-FX-95/97/95"), + T_DATA("3CR990SVR95 [Typhoon Server 56-bit]", "3CR990SVR95"), + T_DATA("3CR990SVR97 [Typhoon Server 168-bit]", "3CR990SVR97"), + T_DATA("3CR990-TX-95 [Typhoon 56-bit]", "3CR990-TX-95"), + T_DATA("3CR990-TX-97 [Typhoon 168-bit]", "3CR990-TX-97"), + T_DATA("3CRPAG175 Wireless PC Card", "3CRPAG175"), + T_DATA("3CRUSB10075 802.11bg [ZyDAS ZD1211]", "3CRUSB10075"), + T_DATA("3CRWE254G72 802.11g Adapter", "3CRWE254G72"), + T_DATA("3CSOHO100B-TX 910-A01 [tulip]", "3CSOHO100B-TX 910-A01"), + T_DATA("3cSOHO100-TX Hurricane", "3cSOHO100-TX Hurricane"), + T_DATA("3cXFEM656C 10/100 LAN+Winmodem CardBus [Tornado]", "3cXFEM656C"), + T_DATA("4410a Wireless-G Adapter [Intersil ISL3887]", "4410a"), + T_DATA("4DWave DX", "4DWave DX"), + T_DATA("4G LTE adapter", NULL), + T_DATA("54g USB Network Adapter", NULL), + T_DATA("570x 10/100 Integrated Controller", "570x"), + T_DATA("79c970 [PCnet32 LANCE]", "79c970"), + T_DATA("79c978 [HomePNA]", "79c978"), + T_DATA("80003ES2LAN Gigabit Ethernet Controller (Copper)", "80003ES2LAN"), + T_DATA("80003ES2LAN Gigabit Ethernet Controller (Serdes)", "80003ES2LAN"), + T_DATA("802.11a/b/g/n USB Wireless LAN Card", NULL), + T_DATA("802.11 Adapter", NULL), + T_DATA("802.11bgn 1T1R Mini Card Wireless Adapter", "1T1R"), + T_DATA("802.11bg", NULL), + T_DATA("802.11b/g/n USB Wireless LAN Card", NULL), + T_DATA("802.11b/g/n USB Wireless Network Adapter", NULL), + T_DATA("802.11b/g/n Wireless Network Adapter", NULL), + T_DATA("802.11b/g Turbo Wireless Adapter", NULL), + T_DATA("802.11b/g Wireless Network Adapter", NULL), + T_DATA("802.11g Wireless Adapter [Intersil ISL3886]", "Intersil ISL3886"), + T_DATA("802.11n adapter", NULL), + T_DATA("802.11n/b/g Mini Wireless LAN USB2.0 Adapter", NULL), + T_DATA("802.11n/b/g Wireless LAN USB2.0 Adapter", NULL), + T_DATA("802.11 n/g/b Wireless LAN Adapter", NULL), + T_DATA("802.11 n/g/b Wireless LAN USB Adapter", NULL), + T_DATA("802.11 n/g/b Wireless LAN USB Mini-Card", NULL), + T_DATA("802.11n Network Adapter", NULL), + T_DATA("802.11n Network Adapter (wrong ID - swapped vendor and device)", NULL), + T_DATA("802.11n USB Wireless Card", NULL), + T_DATA("802.11n Wireless Adapter", NULL), + T_DATA("802.11n Wireless LAN Card", NULL), + T_DATA("802.11n Wireless USB Card", NULL), + T_DATA("802AIN Wireless N Network Adapter [Atheros AR9170+AR9101]", + "Atheros AR9170+AR9101"), + T_DATA("802UIG-1 802.11g Wireless Mini Adapter [Intersil ISL3887]", "Intersil ISL3887"), + T_DATA("82540EM Gigabit Ethernet Controller", "82540EM"), + T_DATA("82540EM Gigabit Ethernet Controller (LOM)", "82540EM"), + T_DATA("82540EP Gigabit Ethernet Controller", "82540EP"), + T_DATA("82540EP Gigabit Ethernet Controller (Mobile)", "82540EP"), + T_DATA("82541EI Gigabit Ethernet Controller", "82541EI"), + T_DATA("82541ER Gigabit Ethernet Controller", "82541ER"), + T_DATA("82541GI Gigabit Ethernet Controller", "82541GI"), + T_DATA("82541PI Gigabit Ethernet Controller", "82541PI"), + T_DATA("82542 Gigabit Ethernet Controller (Fiber)", "82542"), + T_DATA("82543GC Gigabit Ethernet Controller (Copper)", "82543GC"), + T_DATA("82543GC Gigabit Ethernet Controller (Fiber)", "82543GC"), + T_DATA("82544EI Gigabit Ethernet Controller (Copper)", "82544EI"), + T_DATA("82544EI Gigabit Ethernet Controller (Fiber)", "82544EI"), + T_DATA("82544GC Gigabit Ethernet Controller (Copper)", "82544GC"), + T_DATA("82544GC Gigabit Ethernet Controller (LOM)", "82544GC"), + T_DATA("82545EM Gigabit Ethernet Controller (Copper)", "82545EM"), + T_DATA("82545EM Gigabit Ethernet Controller (Fiber)", "82545EM"), + T_DATA("82545GM Gigabit Ethernet Controller", "82545GM"), + T_DATA("82546EB Gigabit Ethernet Controller", "82546EB"), + T_DATA("82546EB Gigabit Ethernet Controller (Copper)", "82546EB"), + T_DATA("82546EB Gigabit Ethernet Controller (Fiber)", "82546EB"), + T_DATA("82546GB Gigabit Ethernet Controller", "82546GB"), + T_DATA("82546GB Gigabit Ethernet Controller (Copper)", "82546GB"), + T_DATA("82547EI Gigabit Ethernet Controller", "82547EI"), + T_DATA("82547EI Gigabit Ethernet Controller (Mobile)", "82547EI"), + T_DATA("82547GI Gigabit Ethernet Controller", "82547GI"), + T_DATA("82551QM Ethernet Controller", "82551QM"), + T_DATA("82552 10/100 Network Connection", "82552"), + T_DATA("82557/8/9/0/1 Ethernet Pro 100", "82557/8/9/0/1"), + T_DATA("82559 Ethernet Controller", "82559"), + T_DATA("82559 InBusiness 10/100", "82559 InBusiness"), + T_DATA("8255xER/82551IT Fast Ethernet Controller", "8255xER/82551IT"), + T_DATA("82562 EM/EX/GX - PRO/100 VM Ethernet Controller", "82562 EM/EX/GX"), + T_DATA("82562 EM/EX/GX - PRO/100 VM (LOM) Ethernet Controller", "82562 EM/EX/GX"), + T_DATA("82562EM/EX/GX - PRO/100 VM (LOM) Ethernet Controller Mobile", "82562EM/EX/GX"), + T_DATA("82562ET/EZ/GT/GZ - PRO/100 VE Ethernet Controller", "82562ET/EZ/GT/GZ"), + T_DATA("82562ET/EZ/GT/GZ - PRO/100 VE (LOM) Ethernet Controller", "82562ET/EZ/GT/GZ"), + T_DATA("82562ET/EZ/GT/GZ - PRO/100 VE (LOM) Ethernet Controller Mobile", + "82562ET/EZ/GT/GZ"), + T_DATA("82562EZ 10/100 Ethernet Controller", "82562EZ"), + T_DATA("82562G 10/100 Network Connection", "82562G"), + T_DATA("82562G-2 10/100 Network Connection", "82562G-2"), + T_DATA("82562G - PRO/100 VE Ethernet Controller Mobile", "82562G"), + T_DATA("82562G - PRO/100 VE (LOM) Ethernet Controller", "82562G"), + T_DATA("82562GT 10/100 Network Connection", "82562GT"), + T_DATA("82562GT-2 10/100 Network Connection", "82562GT-2"), + T_DATA("82562V 10/100 Network Connection", "82562V"), + T_DATA("82562V-2 10/100 Network Connection", "82562V-2"), + T_DATA("82566DC-2 Gigabit Network Connection", "82566DC-2"), + T_DATA("82566DC Gigabit Network Connection", "82566DC"), + T_DATA("82566DM-2 Gigabit Network Connection", "82566DM-2"), + T_DATA("82566DM Gigabit Network Connection", "82566DM"), + T_DATA("82566MC Gigabit Network Connection", "82566MC"), + T_DATA("82566MM Gigabit Network Connection", "82566MM"), + T_DATA("82567LF-2 Gigabit Network Connection", "82567LF-2"), + T_DATA("82567LF-3 Gigabit Network Connection", "82567LF-3"), + T_DATA("82567LF Gigabit Network Connection", "82567LF"), + T_DATA("82567LM-2 Gigabit Network Connection", "82567LM-2"), + T_DATA("82567LM-3 Gigabit Network Connection", "82567LM-3"), + T_DATA("82567LM-4 Gigabit Network Connection", "82567LM-4"), + T_DATA("82567LM Gigabit Network Connection", "82567LM"), + T_DATA("82567V-2 Gigabit Network Connection", "82567V-2"), + T_DATA("82567V-3 Gigabit Network Connection", "82567V-3"), + T_DATA("82567V-4 Gigabit Network Connection", "82567V-4"), + T_DATA("82567V Gigabit Network Connection", "82567V"), + T_DATA("82571EB Dual Port Gigabit Mezzanine Adapter", "82571EB Mezzanine"), + T_DATA("82571EB Gigabit Ethernet Controller", "82571EB"), + T_DATA("82571EB Gigabit Ethernet Controller (Copper)", "82571EB"), + T_DATA("82571EB Gigabit Ethernet Controller (Fiber)", "82571EB"), + T_DATA("82571EB Quad Port Gigabit Mezzanine Adapter", "82571EB Quad Port Mezzanine"), + T_DATA("82571PT Gigabit PT Quad Port Server ExpressModule", "82571PT PT Quad Port"), + T_DATA("82572EI Gigabit Ethernet Controller", "82572EI"), + T_DATA("82572EI Gigabit Ethernet Controller (Copper)", "82572EI"), + T_DATA("82572EI Gigabit Ethernet Controller (Fiber)", "82572EI"), + T_DATA("82573E Gigabit Ethernet Controller (Copper)", "82573E"), + T_DATA("82573L Gigabit Ethernet Controller", "82573L"), + T_DATA("82573V Gigabit Ethernet Controller (Copper)", "82573V"), + T_DATA("82574L Gigabit Network Connection", "82574L"), + T_DATA("82575EB Gigabit Backplane Connection", "82575EB Backplane Connection"), + T_DATA("82575EB Gigabit Network Connection", "82575EB"), + T_DATA("82575GB Gigabit Network Connection", "82575GB"), + T_DATA("82576 Gigabit Backplane Connection", "82576 Backplane Connection"), + T_DATA("82576 Gigabit Network Connection", "82576"), + T_DATA("82576NS Gigabit Network Connection", "82576NS"), + T_DATA("82576NS SerDes Gigabit Network Connection", "82576NS SerDes"), + T_DATA("82576 Virtual Function", "82576 Virtual Function"), + T_DATA("82577LC Gigabit Network Connection", "82577LC"), + T_DATA("82577LM Gigabit Network Connection", "82577LM"), + T_DATA("82578DC Gigabit Network Connection", "82578DC"), + T_DATA("82578DM Gigabit Network Connection", "82578DM"), + T_DATA("82579LM Gigabit Network Connection (Lewisville)", "82579LM"), + T_DATA("82579V Gigabit Network Connection", "82579V"), + T_DATA("82580 Gigabit Backplane Connection", "82580 Backplane Connection"), + T_DATA("82580 Gigabit Fiber Network Connection", "82580"), + T_DATA("82580 Gigabit Network Connection", "82580"), + T_DATA("82580 Gigabit SFP Connection", "82580 SFP Connection"), + T_DATA("82583V Gigabit Network Connection", "82583V"), + T_DATA("82597EX 10GbE Ethernet Controller", "82597EX"), + T_DATA("82598 10GbE PCI-Express Ethernet Controller", "82598"), + T_DATA("82598EB 10-Gigabit AF Dual Port Network Connection", "82598EB AF"), + T_DATA("82598EB 10-Gigabit AF Network Connection", "82598EB AF"), + T_DATA("82598EB 10-Gigabit AT2 Server Adapter", "82598EB AT2"), + T_DATA("82598EB 10-Gigabit AT CX4 Network Connection", "82598EB AT CX4"), + T_DATA("82598EB 10-Gigabit AT Network Connection", "82598EB AT"), + T_DATA("82598EB 10-Gigabit Dual Port Network Connection", "82598EB"), + T_DATA("82598EB Gigabit BX Network Connection", "82598EB BX"), + T_DATA("82599 10 Gigabit Dual Port Backplane Connection", "82599 Backplane Connection"), + T_DATA("82599 10 Gigabit Dual Port Backplane Connection with FCoE", + "82599 Backplane Connection with FCoE"), + T_DATA("82599 10 Gigabit Dual Port Network Connection", "82599"), + T_DATA("82599 10 Gigabit Dual Port Network Connection with FCoE", "82599 with FCoE"), + T_DATA("82599 10 Gigabit Network Connection", "82599"), + T_DATA("82599 10 Gigabit TN Network Connection", "82599 TN"), + T_DATA("82599ES 10 Gigabit Network Connection", "82599ES"), + T_DATA("82599ES 10-Gigabit SFI/SFP+ Network Connection", "82599ES SFI/SFP+"), + T_DATA("82599 Ethernet Controller Virtual Function", "82599 Virtual Function"), + T_DATA("82599 Virtual Function", "82599 Virtual Function"), + T_DATA("82801BA/BAM/CA/CAM Ethernet Controller", "82801BA/BAM/CA/CAM"), + T_DATA("82801CAM (ICH3) PRO/100 VE Ethernet Controller", "82801CAM PRO/100 VE"), + T_DATA("82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller", "82801CAM PRO/100 VE"), + T_DATA("82801CAM (ICH3) PRO/100 VM Ethernet Controller", "82801CAM PRO/100 VM"), + T_DATA("82801CAM (ICH3) PRO/100 VM (KM) Ethernet Controller", "82801CAM PRO/100 VM"), + T_DATA("82801CAM (ICH3) PRO/100 VM (LOM) Ethernet Controller", "82801CAM PRO/100 VM"), + T_DATA("82801DB PRO/100 VE (CNR) Ethernet Controller", "82801DB PRO/100 VE"), + T_DATA("82801DB PRO/100 VE (LOM) Ethernet Controller", "82801DB PRO/100 VE"), + T_DATA("82801DB PRO/100 VE (MOB) Ethernet Controller", "82801DB PRO/100 VE"), + T_DATA("82801DB PRO/100 VM (CNR) Ethernet Controller", "82801DB PRO/100 VM"), + T_DATA("82801DB PRO/100 VM (LOM) Ethernet Controller", "82801DB PRO/100 VM"), + T_DATA("82801DB PRO/100 VM (MOB) Ethernet Controller", "82801DB PRO/100 VM"), + T_DATA("82801EB/ER (ICH5/ICH5R) integrated LAN Controller", "82801EB/ER"), + T_DATA("82801E Ethernet Controller 0", "82801E 0"), + T_DATA("82801E Ethernet Controller 1", "82801E 1"), + T_DATA("83c170 EPIC/100 Fast Ethernet Adapter", "83c170 EPIC/100"), + T_DATA("83c175 EPIC/100 Fast Ethernet Adapter", "83c175 EPIC/100"), + T_DATA("83C885 NT50 DigitalScape Fast Ethernet", "83C885 NT50 DigitalScape"), + T_DATA("88E8001 Gigabit Ethernet Controller", "88E8001"), + T_DATA("88E8021 PCI-X IPMI Gigabit Ethernet Controller", "88E8021 IPMI"), + T_DATA("88E8022 PCI-X IPMI Gigabit Ethernet Controller", "88E8022 IPMI"), + T_DATA("88E8035 PCI-E Fast Ethernet Controller", "88E8035"), + T_DATA("88E8036 PCI-E Fast Ethernet Controller", "88E8036"), + T_DATA("88E8038 PCI-E Fast Ethernet Controller", "88E8038"), + T_DATA("88E8039 PCI-E Fast Ethernet Controller", "88E8039"), + T_DATA("88E8040 PCI-E Fast Ethernet Controller", "88E8040"), + T_DATA("88E8040T PCI-E Fast Ethernet Controller", "88E8040T"), + T_DATA("88E8042 PCI-E Fast Ethernet Controller", "88E8042"), + T_DATA("88E8048 PCI-E Fast Ethernet Controller", "88E8048"), + T_DATA("88E8050 PCI-E ASF Gigabit Ethernet Controller", "88E8050 ASF"), + T_DATA("88E8052 PCI-E ASF Gigabit Ethernet Controller", "88E8052 ASF"), + T_DATA("88E8053 PCI-E Gigabit Ethernet Controller", "88E8053"), + T_DATA("88E8055 PCI-E Gigabit Ethernet Controller", "88E8055"), + T_DATA("88E8056 PCI-E Gigabit Ethernet Controller", "88E8056"), + T_DATA("88E8057 PCI-E Gigabit Ethernet Controller", "88E8057"), + T_DATA("88E8058 PCI-E Gigabit Ethernet Controller", "88E8058"), + T_DATA("88E8061 PCI-E IPMI Gigabit Ethernet Controller", "88E8061 IPMI"), + T_DATA("88E8062 PCI-E IPMI Gigabit Ethernet Controller", "88E8062 IPMI"), + T_DATA("88E8070 based Ethernet Controller", "88E8070 based"), + T_DATA("88E8071 PCI-E Gigabit Ethernet Controller", "88E8071"), + T_DATA("88E8072 PCI-E Gigabit Ethernet Controller", "88E8072"), + T_DATA("88E8075 PCI-E Gigabit Ethernet Controller", "88E8075"), + T_DATA("88EC032 Ethernet Controller", "88EC032"), + T_DATA("88EC033 PCI-E Fast Ethernet Controller", "88EC033"), + T_DATA("88EC034 Ethernet Controller", "88EC034"), + T_DATA("88EC036 PCI-E Gigabit Ethernet Controller", "88EC036"), + T_DATA("88EC042 Ethernet Controller", "88EC042"), + T_DATA("88W8363 [TopDog] 802.11n Wireless", "88W8363"), + T_DATA("88W8366 [TopDog] 802.11n Wireless", "88W8366"), + T_DATA("88W8388 802.11a/b/g WLAN", "88W8388"), + T_DATA("88W8687 [TopDog] 802.11b/g Wireless", "88W8687"), + T_DATA("88W8764 [Avastar] 802.11n Wireless", "88W8764"), + T_DATA("88W8897 [AVASTAR] 802.11ac Wireless", "88W8897"), + T_DATA("A90-211WG-01 802.11g Adapter [Intersil ISL3887]", "A90-211WG-01"), + T_DATA("A9T wireless 802.11bg", "A9T"), + T_DATA("AboCom Systems Inc [WN2001 Prolink Wireless-N Nano Adapter]", "AboCom Systems Inc"), + T_DATA("AC1000 Gigabit Ethernet", "AC1000"), + T_DATA("AC1001 Gigabit Ethernet", "AC1001"), + T_DATA("AC1003 Gigabit Ethernet", "AC1003"), + T_DATA("AC9100 Gigabit Ethernet", "AC9100"), + T_DATA("AceNIC Gigabit Ethernet", "AceNIC"), + T_DATA("AceNIC Gigabit Ethernet (Copper)", "AceNIC"), + T_DATA("Acer Gobi 2000 Wireless Modem", "Acer Gobi 2000"), + T_DATA("Acer Gobi Wireless Modem", "Acer Gobi"), + T_DATA("ADM8511 Pegasus II Ethernet", "ADM8511 Pegasus II"), + T_DATA("ADMtek ADM8515 NIC", "ADMtek ADM8515"), + T_DATA("ADMtek Centaur-C rev 17 [D-Link DFE-680TX] CardBus Fast Ethernet Adapter", + "ADMtek Centaur-C"), + T_DATA("ADSL Modem", NULL), + T_DATA("AE1000 v1 802.11n [Ralink RT3572]", "AE1000"), + T_DATA("AE1200 802.11bgn Wireless Adapter [Broadcom BCM43235]", "AE1200"), + T_DATA("AE3000 802.11abgn (3x3) Wireless Adapter [Ralink RT3573]", "AE3000"), + T_DATA("AG-225H 802.11bg", "AG-225H"), + T_DATA("Air2210 54 Mbps Wireless Adapter", "Air2210"), + T_DATA("Air2310 150 Mbps Wireless Adapter", "Air2310"), + T_DATA("Airlink101 AWLL6070 802.11bgn Wireless Adapter [Ralink RT2770]", + "Airlink101 AWLL6070"), + T_DATA("Airlink101 AWLL6080 802.11bgn Wireless Adapter [Ralink RT2870]", + "Airlink101 AWLL6080"), + T_DATA("AirLive WL-1600USB 802.11g Adapter [Realtek RTL8187L]", "AirLive WL-1600USB"), + T_DATA("AirLive WN-200USB wireless 11b/g/n dongle", "AirLive WN-200USB"), + T_DATA("AirLive WN-360USB adapter", "AirLive WN-360USB"), + T_DATA("AirLive X.USB 802.11abgn [Atheros AR9170+AR9104]", "AirLive X.USB"), + T_DATA("AirPcap NX [Atheros AR9001U-(2)NG]", "AirPcap NX"), + T_DATA("AirPlus G DWL-G120 Wireless Adapter(rev.C) [Intersil ISL3887]", + "AirPlus G DWL-G120"), + T_DATA("AirPlus G DWL-G122 Wireless Adapter(rev.A1) [Intersil ISL3880]", + "AirPlus G DWL-G122"), + T_DATA("AirPlus G DWL-G122 Wireless Adapter(rev.A2) [Intersil ISL3887]", + "AirPlus G DWL-G122"), + T_DATA("AirPlus G DWL-G122 Wireless Adapter(rev.B1) [Ralink RT2571]", "AirPlus G DWL-G122"), + T_DATA("AirPlus G DWL-G122 Wireless Adapter(rev.C1) [Ralink RT2571W]", + "AirPlus G DWL-G122"), + T_DATA("AirPlus G DWL-G122 Wireless Adapter(rev.E1) [Ralink RT2070]", "AirPlus G DWL-G122"), + T_DATA("Alcatel One Touch L100V / Telekom Speedstick LTE II", + "Alcatel One Touch L100V / Telekom Speedstick II"), + T_DATA("Allnet ALL0283 [AR5523]", "Allnet ALL0283"), + T_DATA("Allnet ALL0283 [AR5523](no firmware)", "Allnet ALL0283"), + T_DATA("Allnet ALL0298 v2 802.11bg", "Allnet ALL0298"), + T_DATA("AM10 v1 802.11n [Ralink RT3072]", "AM10"), + T_DATA("AMD-8111 Ethernet", "AMD-8111"), + T_DATA("AN2720 USB-USB Bridge", "AN2720 USB-USB Bridge"), + T_DATA("AN8513 Ethernet", "AN8513"), + T_DATA("AN8515 Ethernet", "AN8515"), + T_DATA("AN986A Ethernet", "AN986A"), + T_DATA("AN986 Pegasus Ethernet", "AN986 Pegasus"), + T_DATA("ANA620xx/ANA69011A", "ANA620xx/ANA69011A"), + T_DATA("AN-WF500 802.11abgn + BT Wireless Adapter [Broadcom BCM43242]", "AN-WF500"), + T_DATA("Aolynk WUB320g", "Aolynk WUB320g"), + T_DATA("AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg]", "AR2413/AR2414"), + T_DATA("AR2417 Wireless Network Adapter [AR5007G 802.11bg]", "AR2417"), + T_DATA("AR2425 Wireless Network Adapter [AR5007EG 802.11bg]", "AR2425"), + T_DATA("AR2427 802.11bg Wireless Network Adapter (PCI-Express)", "AR2427"), + T_DATA("AR242x / AR542x Wireless Network Adapter (PCI-Express)", "AR242x / AR542x"), + T_DATA("AR5210 Wireless Network Adapter [AR5000 802.11a]", "AR5210"), + T_DATA("AR5211 Wireless Network Adapter [AR5001A 802.11a]", "AR5211"), + T_DATA("AR5211 Wireless Network Adapter [AR5001X 802.11ab]", "AR5211"), + T_DATA("AR5212/5213/2414 Wireless Network Adapter", "AR5212/5213/2414"), + T_DATA("AR5212 802.11abg NIC (3CRDAG675)", "AR5212"), + T_DATA("AR5212 802.11abg NIC", "AR5212"), + T_DATA("AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg]", "AR5413/AR5414"), + T_DATA("AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn]", "AR5416"), + T_DATA("AR5418 Wireless Network Adapter [AR5008E 802.11(a)bgn] (PCI-Express)", "AR5418"), + T_DATA("AR5523", "AR5523"), + T_DATA("AR5523 driver (no firmware)", "AR5523"), + T_DATA("AR5523 (no firmware)", "AR5523"), + T_DATA("AR7010 (no firmware)", "AR7010"), + T_DATA("AR8121/AR8113/AR8114 Gigabit or Fast Ethernet", "AR8121/AR8113/AR8114"), + T_DATA("AR8131 Gigabit Ethernet", "AR8131"), + T_DATA("AR8132 Fast Ethernet", "AR8132"), + T_DATA("AR8151 v1.0 Gigabit Ethernet", "AR8151 v1.0"), + T_DATA("AR8151 v2.0 Gigabit Ethernet", "AR8151 v2.0"), + T_DATA("AR8152 v1.1 Fast Ethernet", "AR8152"), + T_DATA("AR8152 v2.0 Fast Ethernet", "AR8152 v2.0"), + T_DATA("AR8161 Gigabit Ethernet", "AR8161"), + T_DATA("AR8162 Fast Ethernet", "AR8162"), + T_DATA("AR9160 Wireless Network Adapter [AR9001 802.11(a)bgn]", "AR9160"), + T_DATA("AR9170 802.11n", "AR9170"), + T_DATA("AR9170+AR9104 802.11abgn Wireless Adapter", "AR9170+AR9104"), + T_DATA("AR9227 Wireless Network Adapter", "AR9227"), + T_DATA("AR922X Wireless Network Adapter", "AR922X"), + T_DATA("AR922X Wireless Network Adapter (Compex WLM200NX / Wistron DNMA-92)", "AR922X"), + T_DATA("AR9271 802.11n", "AR9271"), + T_DATA("AR9285 Wireless Network Adapter (PCI-Express)", "AR9285"), + T_DATA("AR9285 Wireless Network Adapter (PCI-Express) (AW-NB037H 802.11bgn Wireless " + "Half-size Mini PCIe Card [AR9002WB-1NGCD])", + "AR9285"), + T_DATA("AR9287 Wireless Network Adapter (PCI-Express)", "AR9287"), + T_DATA("AR928X Wireless Network Adapter (PCI-Express)", "AR928X"), + T_DATA("AR928X Wireless Network Adapter (PCI-Express) (EM306 802.11bgn Wireless Half-size " + "Mini PCIe Card [AR9283])", + "AR928X"), + T_DATA("AR928X Wireless Network Adapter (PCI-Express) (T77H047.31 802.11bgn Wireless " + "Half-size Mini PCIe Card [AR9283])", + "AR928X"), + T_DATA("AR93xx Wireless Network Adapter", "AR93xx"), + T_DATA("AR93xx Wireless Network Adapter (Killer Wireless-N 1102 Half-size Mini PCIe Card " + "[AR9382])", + "AR93xx"), + T_DATA("AR93xx Wireless Network Adapter (Killer Wireless-N 1103 Half-size Mini PCIe Card " + "[AR9380])", + "AR93xx"), + T_DATA("AR9462 Wireless Network Adapter", "AR9462"), + T_DATA("AR9462 Wireless Network Adapter (Wireless 1601 802.11abgn Adapter)", "AR9462"), + T_DATA("AR9462 Wireless Network Adapter (Wireless 1802 802.11abgn Adapter)", "AR9462"), + T_DATA("AR9485 Wireless Network Adapter", "AR9485"), + T_DATA("AR9485 Wireless Network Adapter (AR9485WB-EG 802.11b/g/n mini-PCIe card on a " + "series 3 laptop)", + "AR9485"), + T_DATA("AR9485 Wireless Network Adapter (AW-NE186H)", "AR9485"), + T_DATA("AR958x 802.11abgn Wireless Network Adapter", "AR958x"), + T_DATA("Arcadyan 802.11N Wireless Adapter", "Arcadyan"), + T_DATA("Arcadyan WN4501 802.11b/g", "Arcadyan WN4501"), + T_DATA("Arcadyan WN7512 802.11n", "Arcadyan WN7512"), + T_DATA("Asus Gobi 2000 Wireless Modem", "Asus Gobi 2000"), + T_DATA("Aterm PA-WL54GU", "Aterm PA-WL54GU"), + T_DATA("Aterm WL300NU-AG", "Aterm WL300NU-AG"), + T_DATA("Aterm WL300NU-G", "Aterm WL300NU-G"), + T_DATA("Attansic L1 Gigabit Ethernet", "Attansic L1"), + T_DATA("Attansic L2c Gigabit Ethernet", "Attansic L2c"), + T_DATA("Attansic L2 Fast Ethernet", "Attansic L2"), + T_DATA("AT-USB100", "AT-USB100"), + T_DATA("AX88141 Fast Ethernet Controller", "AX88141"), + T_DATA("AX88178", "AX88178"), + T_DATA("AX88179 Gigabit Ethernet", "AX88179"), + T_DATA("AX88179 Gigabit Ethernet [Sitecom]", "AX88179"), + T_DATA("AX88179 Gigabit Ethernet [ThinkPad OneLink GigaLAN]", "AX88179"), + T_DATA("AX88772A Fast Ethernet", "AX88772A"), + T_DATA("AX88772", "AX88772"), + T_DATA("AX88772B", "AX88772B"), + T_DATA("AX88772B Fast Ethernet Controller", "AX88772B"), + T_DATA("B404-BT Unified Wire Ethernet Controller", "B404-BT"), + T_DATA("B404-BT Unified Wire Ethernet Controller [VF]", "B404-BT"), + T_DATA("B420-SR Unified Wire Ethernet Controller", "B420-SR"), + T_DATA("B420-SR Unified Wire Ethernet Controller [VF]", "B420-SR"), + T_DATA("B504-BT Unified Wire Ethernet Controller", "B504-BT"), + T_DATA("B504-BT Unified Wire Ethernet Controller [VF]", "B504-BT"), + T_DATA("B520-SR Unified Wire Ethernet Controller", "B520-SR"), + T_DATA("B520-SR Unified Wire Ethernet Controller [VF]", "B520-SR"), + T_DATA("BCM43142 802.11b/g/n", "BCM43142"), + T_DATA("BCM43143 802.11bgn (1x1) Wireless Adapter", "BCM43143"), + T_DATA("BCM43143 WLAN card", "BCM43143"), + T_DATA("BCM43236 802.11abgn Wireless Adapter", "BCM43236"), + T_DATA("BCM43241 WLAN card", "BCM43241"), + T_DATA("BCM43242 802.11abgn Wireless Adapter", "BCM43242"), + T_DATA("BCM4329 WLAN card", "BCM4329"), + T_DATA("BCM4330 WLAN card", "BCM4330"), + T_DATA("BCM43340 WLAN card", "BCM43340"), + T_DATA("BCM43341 WLAN card", "BCM43341"), + T_DATA("BCM4334 WLAN card", "BCM4334"), + T_DATA("BCM4335/BCM4339 WLAN card", "BCM4335/BCM4339"), + T_DATA("BCM43362 WLAN card", "BCM43362"), + T_DATA("BCM4350 802.11ac Wireless Network Adapter", "BCM4350"), + T_DATA("BCM4354 WLAN card", "BCM4354"), + T_DATA("BCM43567 802.11ac Wireless Network Adapter", "BCM43567"), + T_DATA("BCM4356 802.11ac Wireless Network Adapter", "BCM4356"), + T_DATA("BCM43570 802.11ac Wireless Network Adapter", "BCM43570"), + T_DATA("BCM4358 802.11ac Wireless LAN SoC", "BCM4358"), + T_DATA("BCM43602 802.11ac Wireless LAN SoC", "BCM43602"), + T_DATA("BCM4401 100Base-T", "BCM4401"), + T_DATA("BCM4401-B0 100Base-TX", "BCM4401-B0"), + T_DATA("BCM4402 Integrated 10/100BaseT", "BCM4402"), + T_DATA("BCM57301 NetXtreme-C 10Gb Ethernet Controller", "BCM57301 NetXtreme-C"), + T_DATA("BCM57302 NetXtreme-C 10Gb/25Gb Ethernet Controller", "BCM57302 NetXtreme-C"), + T_DATA("BCM57304 NetXtreme-C 10Gb/25Gb/40Gb/50Gb Ethernet Controller", + "BCM57304 NetXtreme-C"), + T_DATA("BCM57311 NetXtreme-C 10Gb RDMA Ethernet Controller", "BCM57311 NetXtreme-C"), + T_DATA("BCM57312 NetXtreme-C 10Gb/25Gb RDMA Ethernet Controller", "BCM57312 NetXtreme-C"), + T_DATA("BCM57314 NetXtreme-C 10Gb/25Gb/40Gb/50Gb RDMA Ethernet Controller", + "BCM57314 NetXtreme-C"), + T_DATA("BCM57402 NetXtreme-E 10Gb Ethernet Controller", "BCM57402 NetXtreme-E"), + T_DATA("BCM57402 NetXtreme-E Ethernet Partition", "BCM57402 NetXtreme-E Partition"), + T_DATA("BCM57404 NetXtreme-E 10Gb/25Gb Ethernet Controller", "BCM57404 NetXtreme-E"), + T_DATA("BCM57404 NetXtreme-E Ethernet Partition", "BCM57404 NetXtreme-E Partition"), + T_DATA("BCM57406 NetXtreme-E 10GBASE-T Ethernet Controller", "BCM57406 NetXtreme-E"), + T_DATA("BCM57406 NetXtreme-E Ethernet Partition", "BCM57406 NetXtreme-E Partition"), + T_DATA("BCM57407 NetXtreme-E 10GBase-T Ethernet Controller", "BCM57407 NetXtreme-E"), + T_DATA("BCM57407 NetXtreme-E 25Gb Ethernet Controller", "BCM57407 NetXtreme-E"), + T_DATA("BCM57412 NetXtreme-E 10Gb RDMA Ethernet Controller", "BCM57412 NetXtreme-E"), + T_DATA("BCM57412 NetXtreme-E Ethernet Partition", "BCM57412 NetXtreme-E Partition"), + T_DATA("BCM57414 NetXtreme-E 10Gb/25Gb RDMA Ethernet Controller", "BCM57414 NetXtreme-E"), + T_DATA("BCM57414 NetXtreme-E Ethernet Partition", "BCM57414 NetXtreme-E Partition"), + T_DATA("BCM57414 NetXtreme-E RDMA Partition", "BCM57414 NetXtreme-E Partition"), + T_DATA("BCM57416 NetXtreme-E 10GBase-T RDMA Ethernet Controller", "BCM57416 NetXtreme-E"), + T_DATA("BCM57416 NetXtreme-E 10Gb RDMA Ethernet Controller", "BCM57416 NetXtreme-E"), + T_DATA("BCM57416 NetXtreme-E Ethernet Partition", "BCM57416 NetXtreme-E Partition"), + T_DATA("BCM57416 NetXtreme-E RDMA Partition", "BCM57416 NetXtreme-E Partition"), + T_DATA("BCM57417 NetXtreme-E 10Gb/25Gb RDMA Ethernet Controller", "BCM57417 NetXtreme-E"), + T_DATA("BCM57417 NetXtreme-E 10GBASE-T RDMA Ethernet Controller", "BCM57417 NetXtreme-E"), + T_DATA("BCM57417 NetXtreme-E Ethernet Partition", "BCM57417 NetXtreme-E Partition"), + T_DATA("BCM57840 NetXtreme II 10/20-Gigabit Ethernet", "BCM57840 NetXtreme II"), + T_DATA("BCM57840 NetXtreme II 10 Gigabit Ethernet", "BCM57840 NetXtreme II"), + T_DATA("BCM57840 NetXtreme II Ethernet Multi Function", "BCM57840 NetXtreme II"), + T_DATA("Belkin F5D5005 Gigabit Desktop Network PCI Card", "Belkin F5D5005"), + T_DATA("ben-wpan, AT86RF230-based", "ben-wpan AT86RF230-based"), + T_DATA("BladeCenter-H 10-Gigabit Ethernet High Speed Daughter Card", + "BladeCenter-H High Speed Daughter"), + T_DATA("BladeEngine2 10Gb Gen2 PCIe Network Adapter", "BladeEngine2 Gen2"), + T_DATA("BladeEngine3 10Gb Gen2 PCIe Network Adapter", "BladeEngine3 Gen2"), + T_DATA("BLOB boot loader firmware", "BLOB boot loader firmware"), + T_DATA("Broadcom NetXtreme BCM5701 Gigabit Ethernet", "Broadcom NetXtreme BCM5701"), + T_DATA("BWIFI-USB54AR 802.11bg", "BWIFI-USB54AR"), + T_DATA("Cardbus Ethernet 10/100", NULL), + T_DATA("Cassini 10/100/1000", "Cassini"), + T_DATA("CE Media Processor Gigabit Ethernet Controller", NULL), + T_DATA("Centrino Advanced-N 6200", "Centrino Advanced-N 6200"), + T_DATA("Centrino Advanced-N 6205 [Taylor Peak]", "Centrino Advanced-N 6205"), + T_DATA("Centrino Advanced-N 6230 [Rainbow Peak]", "Centrino Advanced-N 6230"), + T_DATA("Centrino Advanced-N 6235", "Centrino Advanced-N 6235"), + T_DATA("Centrino Advanced-N + WiMAX 6250 [Kilmer Peak]", "Centrino Advanced-N 6250"), + T_DATA("Centrino Ultimate-N 6300", "Centrino Ultimate-N 6300"), + T_DATA("Centrino Wireless-N 1000 [Condor Peak]", "Centrino 1000"), + T_DATA("Centrino Wireless-N 100", "Centrino 100"), + T_DATA("Centrino Wireless-N 1030 [Rainbow Peak]", "Centrino 1030"), + T_DATA("Centrino Wireless-N 105", "Centrino 105"), + T_DATA("Centrino Wireless-N 130", "Centrino 130"), + T_DATA("Centrino Wireless-N 135", "Centrino 135"), + T_DATA("Centrino Wireless-N 2200", "Centrino 2200"), + T_DATA("Centrino Wireless-N 2230", "Centrino 2230"), + T_DATA("Centrino Wireless-N + WiMAX 6150", "Centrino 6150"), + T_DATA("CG-WLUSB10 Corega Wireless USB Adapter", "CG-WLUSB10 Corega"), + T_DATA("CG-WLUSB2GNL", "CG-WLUSB2GNL"), + T_DATA("CG-WLUSB2GNR Corega Wireless USB Adapter", "CG-WLUSB2GNR Corega"), + T_DATA("CG-WLUSB2GO", "CG-WLUSB2GO"), + T_DATA("CG-WLUSB2GPX [Ralink RT2571W]", "CG-WLUSB2GPX"), + T_DATA("CG-WLUSB2GT 802.11g Wireless Adapter [Intersil ISL3880]", "CG-WLUSB2GT"), + T_DATA("CG-WLUSB2GTST 802.11g Wireless Adapter [Intersil ISL3887]", "CG-WLUSB2GTST"), + T_DATA("CG-WLUSB300AGN", "CG-WLUSB300AGN"), + T_DATA("CG-WLUSB300GNM", "CG-WLUSB300GNM"), + T_DATA("CG-WLUSB300GNS", "CG-WLUSB300GNS"), + T_DATA("CK804 Ethernet Controller", "CK804"), + T_DATA("CK8S Ethernet Controller", "CK8S"), + T_DATA("cLOM8214 1/10GbE Controller", "cLOM8214"), + T_DATA("CMOTECH CDMA Technologies modem", "CMOTECH"), + T_DATA("Cohiba 802.11g Wireless Mini adapter [Intersil ISL3887]", "Cohiba"), + T_DATA("Conceptronic C300RU v1 802.11bgn Wireless Adapter [Ralink RT2870]", + "Conceptronic C300RU"), + T_DATA("Conceptronic C300RU v2 802.11bgn Wireless Adapter [Ralink RT2770]", + "Conceptronic C300RU"), + T_DATA("Conceptronic C54RU v2 802.11bg Wireless Adapter [Ralink RT2571]", + "Conceptronic C54RU"), + T_DATA("Conceptronic C54RU v3 802.11bg Wireless Adapter [Ralink RT2571W]", + "Conceptronic C54RU"), + T_DATA("Connect2Air E-5400 802.11g Wireless Adapter", "Connect2Air E-5400"), + T_DATA("Connect2Air E-5400 D1700 802.11g Wireless Adapter [Intersil ISL3887]", + "Connect2Air E-5400 D1700"), + T_DATA("CPWUE001 USB/Ethernet Adapter", "CPWUE001"), + T_DATA("CWD-854 rev F", "CWD-854"), + T_DATA("CWD-854 [RT2573]", "CWD-854"), + T_DATA("CWD-854 Wireless 802.11g 54Mbps Network Adapter [RTL8187]", "CWD-854"), + T_DATA("DECchip 21040 [Tulip]", "DECchip 21040"), + T_DATA("DECchip 21041 [Tulip Pass 3]", "DECchip 21041"), + T_DATA("DECchip 21140 [FasterNet]", "DECchip 21140"), + T_DATA("DECchip 21142/43", "DECchip 21142/43"), + T_DATA("DFE-680TXD CardBus PC Card", "DFE-680TXD"), + T_DATA("DFE-690TXD CardBus PC Card", "DFE-690TXD"), + T_DATA("DGE-528T Gigabit Ethernet Adapter", "DGE-528T"), + T_DATA("DGE-528T Gigabit Ethernet Adapter (DGE-560T PCI Express (x1) Gigabit Ethernet " + "Adapter)", + "DGE-528T"), + T_DATA("DGE-530T Gigabit Ethernet Adapter (rev 11)", "DGE-530T"), + T_DATA("DGE-530T Gigabit Ethernet Adapter (rev.C1) [Realtek RTL8169]", "DGE-530T"), + T_DATA("DGE-550SX PCI-X Gigabit Ethernet Adapter", "DGE-550SX"), + T_DATA("DGE-550T Gigabit Ethernet Adapter V.B1", "DGE-550T V.B1"), + T_DATA("DGE-560SX PCI Express Gigabit Ethernet Adapter", "DGE-560SX"), + T_DATA("DGE-560T PCI Express Gigabit Ethernet Adapter", "DGE-560T"), + T_DATA("DH8900CC Series Gigabit Backplane Network Connection", "DH8900CC Backplane"), + T_DATA("DH8900CC Series Gigabit Fiber Network Connection", "DH8900CC"), + T_DATA("DH8900CC Series Gigabit Network Connection", "DH8900CC"), + T_DATA("DH8900CC Series Gigabit SFP Network Connection", "DH8900CC SFP"), + T_DATA("Direct Connect", "Direct Connect"), + T_DATA("DL10050 Sundance Ethernet (DFE-550TX/FX)", "DL10050 Sundance"), + T_DATA("DL10050 Sundance Ethernet (DFE-580TX)", "DL10050 Sundance"), + T_DATA("DL10050 Sundance Ethernet", "DL10050 Sundance"), + T_DATA("DL2000-based Gigabit Ethernet", "DL2000-based"), + T_DATA("DM9000E Fast Ethernet Adapter", "DM9000E"), + T_DATA("DM9601 Fast Ethernet Adapter", "DM9601"), + T_DATA("DP83065 [Saturn] 10/100/1000 Ethernet Controller", "DP83065"), + T_DATA("DP83815 (MacPhyter) Ethernet Controller (Aculab E1/T1 PMXc cPCI carrier card)", + "DP83815"), + T_DATA("DP83815 (MacPhyter) Ethernet Controller", "DP83815"), + T_DATA("DP83820 10/100/1000 Ethernet Controller", "DP83820"), + T_DATA("DrayTek Vigor N61 802.11bgn Wireless Adapter [Ralink RT2870]", "DrayTek Vigor N61"), + T_DATA("DRP-32TXD Cardbus PC Card", "DRP-32TXD"), + T_DATA("DSB-650 10Mbps Ethernet [klsi]", "DSB-650"), + T_DATA("DSB-650C Ethernet [klsi]", "DSB-650C"), + T_DATA("DSB-650 Ethernet [pegasus]", "DSB-650"), + T_DATA("DSB-650TX Ethernet [pegasus]", "DSB-650TX"), + T_DATA("DSB-650TX-PNA Ethernet [pegasus]", "DSB-650TX-PNA"), + T_DATA("Dual Band Wireless-AC 3165 Plus Bluetooth", "Wireless-AC 3165"), + T_DATA("DUB-E100 Fast Ethernet Adapter(rev.A) [ASIX AX88172]", "DUB-E100"), + T_DATA("DUB-E100 Fast Ethernet Adapter(rev.B1) [ASIX AX88772]", "DUB-E100"), + T_DATA("DUB-E100 Fast Ethernet Adapter(rev.C1) [ASIX AX88772]", "DUB-E100"), + T_DATA("DU-E100 Ethernet [pegasus]", "DU-E100"), + T_DATA("DU-E10 Ethernet [klsi]", "DU-E10"), + T_DATA("DU-E10 Ethernet [pegasus]", "DU-E10"), + T_DATA("DWA-110 Wireless G Adapter(rev.A1) [Ralink RT2571W]", "DWA-110"), + T_DATA("DWA-110 Wireless G Adapter(rev.B) [Ralink RT2870]", "DWA-110"), + T_DATA("DWA-111 802.11bg Wireless Adapter [Ralink RT2571W]", "DWA-111"), + T_DATA("DWA-121 802.11n Wireless N 150 Pico Adapter [Realtek RTL8188CUS]", + "DWA-121 150 Pico"), + T_DATA("DWA-123 Wireless N 150 Adapter(rev.A1) [Ralink RT3370]", "DWA-123 150"), + T_DATA("DWA-125 Wireless N 150 Adapter(rev.A1) [Ralink RT3070]", "DWA-125 150"), + T_DATA("DWA-125 Wireless N 150 Adapter(rev.A2) [Ralink RT3070]", "DWA-125 150"), + T_DATA("DWA-125 Wireless N 150 Adapter(rev.A3) [Ralink RT5370]", "DWA-125 150"), + T_DATA("DWA-126 802.11n Wireless Adapter [Atheros AR9271]", "DWA-126"), + T_DATA("DWA-127 Wireless N 150 High-Gain Adapter(rev.A1) [Ralink RT3070]", + "DWA-127 150 High-Gain"), + T_DATA("DWA-130 802.11n Wireless N Adapter(rev.B) [Ralink RT2870]", "DWA-130"), + T_DATA("DWA-130 802.11n Wireless N Adapter(rev.D) [Atheros AR9170+AR9102]", "DWA-130"), + T_DATA("DWA-133 802.11n Wireless N Adapter [Realtek RTL8192CU]", "DWA-133"), + T_DATA("DWA-135 802.11n Wireless N Adapter(rev.A1) [Realtek RTL8192CU]", "DWA-135"), + T_DATA("DWA-140 RangeBooster N Adapter(rev.B1) [Ralink RT2870]", "DWA-140 RangeBooster N"), + T_DATA("DWA-140 RangeBooster N Adapter(rev.B2) [Ralink RT3072]", "DWA-140 RangeBooster N"), + T_DATA("DWA-140 RangeBooster N Adapter(rev.B3) [Ralink RT2870]", "DWA-140 RangeBooster N"), + T_DATA("DWA-140 RangeBooster N Adapter(rev.B3) [Ralink RT5372]", "DWA-140 RangeBooster N"), + T_DATA("DWA-160 802.11abgn Xtreme N Dual Band Adapter(rev.A1) [Atheros AR9170+AR9104]", + "DWA-160 Xtreme N"), + T_DATA("DWA-160 802.11abgn Xtreme N Dual Band Adapter(rev.A2) [Atheros AR9170+AR9104]", + "DWA-160 Xtreme N"), + T_DATA("DWA-160 802.11abgn Xtreme N Dual Band Adapter(rev.B2) [Ralink RT5572]", + "DWA-160 Xtreme N"), + T_DATA("DWA-160 Xtreme N Dual Band USB Adapter(rev.B) [Ralink RT2870]", "DWA-160 Xtreme N"), + T_DATA("DWL-510 / DWL-610 802.11b [Realtek RTL8180L]", "DWL-510 / DWL-610"), + T_DATA("DWL-AG122 [Atheros AR5523]", "DWL-AG122"), + T_DATA("DWL-AG122 (no firmware) [Atheros AR5523]", "DWL-AG122"), + T_DATA("DWL-AG132 [Atheros AR5523]", "DWL-AG132"), + T_DATA("DWL-AG132 (no firmware) [Atheros AR5523]", "DWL-AG132"), + T_DATA("DWL-G120 Spinnaker 802.11g [Intersil ISL3886]", "DWL-G120 Spinnaker"), + T_DATA("DWL-G132 [Atheros AR5523]", "DWL-G132"), + T_DATA("DWL-G132 (no firmware) [Atheros AR5523]", "DWL-G132"), + T_DATA("DY-WL10 802.11abgn Adapter [Broadcom BCM4323]", "DY-WL10"), + T_DATA("E180v", "E180v"), + T_DATA("E45 Ethernet [klsi]", "E45"), + T_DATA("E815", "E815"), + T_DATA("EA101 10 Mbps 10BASE-T Ethernet [Kawasaki LSI KL5KLUSB101B]", "EA101"), + T_DATA("EasiDock Ethernet", "EasiDock"), + T_DATA("EH103 Wireless G Adapter", "EH103"), + T_DATA("Eminent EM4045 [Broadcom 4320 USB]", "Eminent EM4045"), + T_DATA("EN-1216 Ethernet Adapter", "EN-1216"), + T_DATA("EN-1217 Ethernet Adapter", "EN-1217"), + T_DATA("Enet2 Ethernet [klsi]", "Enet2"), + T_DATA("Enet Ethernet [klsi]", "Enet"), + T_DATA("EnGenius 802.11n Wireless USB Adapter", "EnGenius"), + T_DATA("ENUWI-N3 [802.11n Wireless N150 Adapter]", "ENUWI-N3"), + T_DATA("EP-1427X-2 Ethernet Adapter [Acer]", "EP-1427X-2"), + T_DATA("EP-9001-g 802.11g 54M WLAN Adapter", "EP-9001-g"), + T_DATA("ET-131x PCI-E Ethernet Controller", "ET-131x"), + T_DATA("ET32P2", "ET32P2"), + T_DATA("ETG-US2", "ETG-US2"), + T_DATA("Ethernet 100/10 MBit", NULL), + T_DATA("Ethernet 10G 2P X520 Adapter", "2P X520"), + T_DATA("Ethernet Adapter [A1277]", "A1277"), + T_DATA("Ethernet Adapter", NULL), + T_DATA("Ethernet adapter [U2L 100P-Y1]", "U2L 100P-Y1"), + T_DATA("Ethernet Adaptive Virtual Function", "Adaptive Virtual Function"), + T_DATA("Ethernet Connection (2) I218-LM", "I218-LM"), + T_DATA("Ethernet Connection (2 I218-V", NULL), + T_DATA("Ethernet Connection (2 I219-LM", NULL), + T_DATA("Ethernet Connection (2 I219-V", NULL), + T_DATA("Ethernet Connection (3) I218-LM", "I218-LM"), + T_DATA("Ethernet Connection (3) I218-V", "I218-V"), + T_DATA("Ethernet Connection (3 I219-LM", NULL), + T_DATA("Ethernet Connection (4 I219-LM", NULL), + T_DATA("Ethernet Connection (4 I219-V", NULL), + T_DATA("Ethernet Connection (5 I219-LM", NULL), + T_DATA("Ethernet Connection (5 I219-V", NULL), + T_DATA("Ethernet Connection (6 I219-LM", NULL), + T_DATA("Ethernet Connection (6 I219-V", NULL), + T_DATA("Ethernet Connection (7 I219-LM", NULL), + T_DATA("Ethernet Connection (7 I219-V", NULL), + T_DATA("Ethernet Connection (8 I219-LM", NULL), + T_DATA("Ethernet Connection (8 I219-V", NULL), + T_DATA("Ethernet Connection (9 I219-LM", NULL), + T_DATA("Ethernet Connection (9 I219-V", NULL), + T_DATA("Ethernet Connection I217-LM", "I217-LM"), + T_DATA("Ethernet Connection I217-V", "I217-V"), + T_DATA("Ethernet Connection I218-LM", "I218-LM"), + T_DATA("Ethernet Connection I218-V", "I218-V"), + T_DATA("Ethernet Connection I219-LM", "I219-LM"), + T_DATA("Ethernet Connection I219-V", "I219-V"), + T_DATA("Ethernet Connection I354 1.0 GbE Backplane", "I354 Backplane"), + T_DATA("Ethernet Connection I354 2.5 GbE Backplane", "I354 Backplane"), + T_DATA("Ethernet Connection I354", "I354"), + T_DATA("Ethernet Connection X552 1000BASE-T", "X552"), + T_DATA("Ethernet Connection X552 10 GbE Backplane", "X552 Backplane"), + T_DATA("Ethernet Connection X552 10 GbE SFP+", "X552 SFP+"), + T_DATA("Ethernet Connection X552 Backplane", "X552 Backplane"), + T_DATA("Ethernet Connection X552 Virtual Function", "X552 Virtual Function"), + T_DATA("Ethernet Connection X552/X557-AT 10GBASE-T", "X552/X557-AT"), + T_DATA("Ethernet Connection X553 10 GbE SFP+", "X553 SFP+"), + T_DATA("Ethernet Connection X553 1GbE", "X553"), + T_DATA("Ethernet Connection X553 Backplane", "X553 Backplane"), + T_DATA("Ethernet Connection X553/X557-AT 10GBASE-T", "X553/X557-AT"), + T_DATA("Ethernet Connection X722 for 10GBASE-T", "X722"), + T_DATA("Ethernet Connection X722 for 10GbE backplane", "X722"), + T_DATA("Ethernet Connection X722 for 10GbE QSFP+", "X722"), + T_DATA("Ethernet Connection X722 for 10GbE SFP+", "X722"), + T_DATA("Ethernet Connection X722 for 1GbE", "X722"), + T_DATA("Ethernet Controller 10-Gigabit X540-AT2", "X540-AT2"), + T_DATA("Ethernet Controller 10G X550T", "X550T"), + T_DATA("Ethernet Controller X540", "X540"), + T_DATA("Ethernet Controller X710 for 10GBASE-T", "X710"), + T_DATA("Ethernet Controller X710 for 10GbE backplane", "X710"), + T_DATA("Ethernet Controller X710 for 10GbE QSFP+", "X710"), + T_DATA("Ethernet Controller X710 for 10GbE SFP+", "X710"), + T_DATA("Ethernet Controller X710/X557-AT 10GBASE-T", "X710/X557-AT"), + T_DATA("Ethernet Controller XL710 for 20GbE backplane", "XL710"), + T_DATA("Ethernet Controller XL710 for 40GbE backplane", "XL710"), + T_DATA("Ethernet Controller XL710 for 40GbE QSFP+", "XL710"), + T_DATA("Ethernet Controller XXV710 for 25GbE backplane", "XXV710"), + T_DATA("Ethernet Controller XXV710 for 25GbE SFP28", "XXV710"), + T_DATA("Ethernet Converged Network Adapter X520-Q1", "X520-Q1"), + T_DATA("Ethernet Express Module X520-P2", "X520-P2"), + T_DATA("Ethernet HN210E", "HN210E"), + T_DATA("Ethernet", NULL), + T_DATA("Ethernet Server Adapter X520-4", "X520-4"), + T_DATA("Ethernet Switch FM10000 Host Interface", "FM10000"), + T_DATA("Ethernet Switch FM10000 Host Virtual Interface", "FM10000"), + T_DATA("Ethernet X520 10GbE Dual Port KX4 Mezz", "X520 KX4 Mezz"), + T_DATA("Ether USB-T Ethernet [klsi]", "Ether USB-T"), + T_DATA("ET/TX Ethernet [pegasus]", "ET/TX"), + T_DATA("ET/TX-S Ethernet [pegasus2]", "ET/TX-S"), + T_DATA("EUB-3701 EXT 802.11g Wireless Adapter [Ralink RT2571W]", "EUB-3701 EXT"), + T_DATA("EUB600v1 802.11abgn Wireless Adapter [Ralink RT3572]", "EUB600v1"), + T_DATA("EUB9706 802.11n Wireless Adapter [Ralink RT3072]", "EUB9706"), + T_DATA("EUB9801 802.11abgn Wireless Adapter [Ralink RT3572]", "EUB9801"), + T_DATA("EW-7711UTn nLite Wireless Adapter [Ralink RT2870]", "EW-7711UTn nLite"), + T_DATA("EW-7717UN 802.11n Wireless Adapter [Ralink RT2870]", "EW-7717UN"), + T_DATA("EW-7718UN 802.11n Wireless Adapter [Ralink RT2870]", "EW-7718UN"), + T_DATA("EW-7722UTn 802.11n Wireless Adapter [Ralink RT307x]", "EW-7722UTn"), + T_DATA("EW-7811Un 802.11n Wireless Adapter [Realtek RTL8188CUS]", "EW-7811Un"), + T_DATA("Expedite E362", "Expedite E362"), + T_DATA("Express Ethernet", "Express"), + T_DATA("EZ Connect USB Ethernet", "EZ Connect"), + T_DATA("F5D5050 100Mbps Ethernet", "F5D5050"), + T_DATA("F5D5055 Gigabit Network Adapter [AX88xxx]", "F5D5055"), + T_DATA("F5D6001 Wireless PCI Card [Realtek RTL8180]", "F5D6001"), + T_DATA("F5D6020 v3000 Wireless PCMCIA Card [Realtek RTL8180]", "F5D6020 v3000"), + T_DATA("F5D7000 v7000 Wireless G Desktop Card [Realtek RTL8185]", "F5D7000 v7000"), + T_DATA("F5D7010 v7000 Wireless G Notebook Card [Realtek RTL8185]", "F5D7010 v7000"), + T_DATA("F5D7050 Wireless G Adapter v1000/v2000 [Intersil ISL3887]", "F5D7050 v1000/v2000"), + T_DATA("F5D7050 Wireless G Adapter v3000 [Ralink RT2571W]", "F5D7050 v3000"), + T_DATA("F5D7050 Wireless G Adapter v4000 [Zydas ZD1211B]", "F5D7050 v4000"), + T_DATA("F5D7050 Wireless G Adapter v5000 [Realtek RTL8187B]", "F5D7050 v5000"), + T_DATA("F5D7051 802.11g Adapter v1000 [Broadcom 4320 USB]", "F5D7051 v1000"), + T_DATA("F5D8053 N Wireless Adapter v3000 [Ralink RT2870]", "F5D8053 v3000"), + T_DATA("F5D8053 N Wireless USB Adapter v1000/v4000 [Ralink RT2870]", "F5D8053 v1000/v4000"), + T_DATA("F5D8053 N Wireless USB Adapter v3000 [Ralink RT2870]", "F5D8053 v3000"), + T_DATA("F5D8055 N+ Wireless Adapter v1000 [Ralink RT2870]", "F5D8055 v1000"), + T_DATA("F5D8055 N+ Wireless Adapter v2000 [Ralink RT3072]", "F5D8055 v2000"), + T_DATA("F5D9050 Wireless G+ MIMO Network Adapter v3000 [Ralink RT2573]", + "F5D9050 MIMO v3000"), + T_DATA("F5D9050 Wireless G+ MIMO Network Adapter v4000 [Ralink RT2573]", + "F5D9050 MIMO v4000"), + T_DATA("F5U258 Host to Host cable", "F5U258 Host to Host cable"), + T_DATA("F6D4050 N150 Enhanced Wireless Network Adapter v1000 [Ralink RT3070]", + "F6D4050 N150 v1000"), + T_DATA("F6D4050 N150 Enhanced Wireless Network Adapter v2000 [Ralink RT3070]", + "F6D4050 N150 v2000"), + T_DATA("F7D1101 v2 Basic Wireless Adapter [Ralink RT3370]", "F7D1101"), + T_DATA("F7D1102 N150/Surf Micro Wireless Adapter v1000 [Realtek RTL8188CUS]", + "F7D1102 N150/Surf v1000"), + T_DATA("F7D2102 802.11n N300 Micro Wireless Adapter v3000 [Realtek RTL8192CU]", + "F7D2102 N300 v3000"), + T_DATA("F9L1004 802.11n Surf N300 XR Wireless Adapter [Realtek RTL8192CU]", + "F9L1004 Surf N300 XR"), + T_DATA("F9L1103 N750 DB 802.11abgn 2x3:3 [Ralink RT3573]", "F9L1103 N750"), + T_DATA("FA101 Fast Ethernet USB 1.1", "FA101"), + T_DATA("FA120 Fast Ethernet USB 2.0 [Asix AX88172 / AX8817x]", "FA120"), + T_DATA("Farallon PN9000SX Gigabit Ethernet", "Farallon PN9000SX"), + T_DATA("Farallon PN9100-T Gigabit Ethernet", "Farallon PN9100-T"), + T_DATA("Fast Ethernet", NULL), + T_DATA("FastLinQ QL41000 Series 10/25/40/50GbE Controller", "FastLinQ QL41000"), + T_DATA("FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF)", + "FastLinQ QL41000"), + T_DATA("FastLinQ QL45000 Series 100GbE Controller", "FastLinQ QL45000"), + T_DATA("FastLinQ QL45000 Series 25GbE Controller", "FastLinQ QL45000"), + T_DATA("FastLinQ QL45000 Series 40GbE Controller", "FastLinQ QL45000"), + T_DATA("FastLinQ QL45000 Series Gigabit Ethernet Controller (SR-IOV VF)", + "FastLinQ QL45000"), + T_DATA("FEther USB2-TX", "FEther USB2-TX"), + T_DATA("FEther USB-TXC", "FEther USB-TXC"), + T_DATA("FEther USB-TX Ethernet [pegasus]", "FEther USB-TX"), + T_DATA("FEther USB-TXS", "FEther USB-TXS"), + T_DATA("FNW-3602-TX CardBus Fast Ethernet", "FNW-3602-TX"), + T_DATA("FNW-3603-TX CardBus Fast Ethernet", "FNW-3603-TX"), + T_DATA("FPC-0106TX misprogrammed [RTL81xx]", "FPC-0106TX"), + T_DATA("Fritz!WLAN N 2.4 [Atheros AR9001U]", "Fritz!WLAN N 2.4"), + T_DATA("Fritz!WLAN N [Atheros AR9001U]", "Fritz!WLAN N"), + T_DATA("Fritz!WLAN N v2 [Atheros AR9271]", "Fritz!WLAN N"), + T_DATA("FRITZ WLAN N v2 [RT5572/rt2870.bin]", "FRITZ N"), + T_DATA("G-200 v2 802.11bg", "G-200"), + T_DATA("G-210H 802.11g Wireless Adapter", "G-210H"), + T_DATA("G-220 v2 802.11bg", "G-220"), + T_DATA("G240 802.11bg", "G240"), + T_DATA("GA620 Gigabit Ethernet", "GA620"), + T_DATA("GA630 Gigabit Ethernet", "GA630"), + T_DATA("GEM 10/100/1000 Ethernet [ge]", "GEM"), + T_DATA("Gigabit Ethernet Adapter", NULL), + T_DATA("Gigabit Network Adapter", NULL), + T_DATA("GigaCard Network Adapter", "GigaCard"), + T_DATA("Gigaset USB Adapter 300", "Gigaset 300"), + T_DATA("GL620USB-A GeneLink USB-USB Bridge", "GL620USB-A GeneLink USB-USB Bridge"), + T_DATA("GlobeTrotter Express 7.2 v2", "GlobeTrotter Express 7.2"), + T_DATA("Globetrotter GI0505 [iCON 505]", "Globetrotter GI0505"), + T_DATA("Globetrotter HSDPA Modem", "Globetrotter"), + T_DATA("Globetrotter HSUPA Modem (aka icon 451)", "Globetrotter"), + T_DATA("Globetrotter HSUPA Modem (aka iCON HSUPA E)", "Globetrotter"), + T_DATA("Globetrotter HSUPA Modem (icon 411 aka \"Vodafone K3760\")", "Globetrotter"), + T_DATA("Globetrotter MO40x 3G Modem (GTM 382)", "Globetrotter MO40x"), + T_DATA("GN-54G", "GN-54G"), + T_DATA("GN-BR402W", "GN-BR402W"), + T_DATA("GNIC-II PCI Gigabit Ethernet [Hamachi]", "GNIC-II"), + T_DATA("GN-WB01GS", "GN-WB01GS"), + T_DATA("GN-WB30N 802.11n WLAN Card", "GN-WB30N"), + T_DATA("GN-WB31N 802.11n USB WLAN Card", "GN-WB31N"), + T_DATA("GN-WB32L 802.11n USB WLAN Card", "GN-WB32L"), + T_DATA("GN-WBKG", "GN-WBKG"), + T_DATA("GN-WI05GS", "GN-WI05GS"), + T_DATA("Gobi 2000", "Gobi 2000"), + T_DATA("Gobi 2000 Wireless Modem", "Gobi 2000"), + T_DATA("Gobi 3000 HSPA+ Modem", "Gobi 3000 HSPA+"), + T_DATA("Gobi 9x15 Multimode 3G/4G LTE Modem (IP passthrough mode)", "Gobi 9x15"), + T_DATA("Gobi 9x15 Multimode 3G/4G LTE Modem (NAT mode)", "Gobi 9x15"), + T_DATA("Gobi Wireless Modem", "Gobi"), + T_DATA("Goldpfeil P-LAN", "Goldpfeil P-LAN"), + T_DATA("GT-B3730 Composite LTE device (Commercial)", "GT-B3730"), + T_DATA("GU-1000T", "GU-1000T"), + T_DATA("GWUS300 802.11n", "GWUS300"), + T_DATA("GW-US300MiniS", "GW-US300MiniS"), + T_DATA("GW-US300MiniW 802.11bgn Wireless Adapter", "GW-US300MiniW"), + T_DATA("GW-US54GXS 802.11bg", "GW-US54GXS"), + T_DATA("GW-US54GZ", "GW-US54GZ"), + T_DATA("GW-US54HP", "GW-US54HP"), + T_DATA("GW-US54Mini2", "GW-US54Mini2"), + T_DATA("GW-US54Mini 802.11bg", "GW-US54Mini"), + T_DATA("GW-US54ZGL 802.11bg", "GW-US54ZGL"), + T_DATA("GWUSB2E", "GWUSB2E"), + T_DATA("GW-USEco300 802.11bgn Wireless Adapter [Realtek RTL8192CU]", "GW-USEco300"), + T_DATA("GW-USMicro300", "GW-USMicro300"), + T_DATA("GW-USMini2N 802.11n Wireless Adapter [Ralink RT2870]", "GW-USMini2N"), + T_DATA("GW-USNano2 802.11n Wireless Adapter [Realtek RTL8188CUS]", "GW-USNano2"), + T_DATA("GW-USValue-EZ 802.11n Wireless Adapter [Realtek RTL8188CUS]", "GW-USValue-EZ"), + T_DATA("Happy Meal 10/100 Ethernet [hme]", "Happy Meal"), + T_DATA("Harmony 900/1100 Remote", "Harmony 900/1100 Remote"), + T_DATA("HAWNU1 Hi-Gain Wireless-150N Network Adapter with Range Amplifier [Ralink RT3070]", + "HAWNU1"), + T_DATA("HCF 56k Modem", "HCF"), + T_DATA("Hercules HWNUp-150 802.11n Wireless N Pico [Realtek RTL8188CUS]", + "Hercules HWNUp-150 Pico"), + T_DATA("HNE-300 (RealTek RTL8139c) [iPaq Networking]", "HNE-300"), + T_DATA("HomeConnect 3C460", "HomeConnect 3C460"), + T_DATA("@Home Networks Ethernet [klsi]", "@Home Networks"), + T_DATA("HU200TS Wireless Adapter", "HU200TS"), + T_DATA("HWDN1 Hi-Gain Wireless-300N Dish Adapter [Ralink RT2870]", "HWDN1"), + T_DATA("HWDN2 Hi-Gain Wireless-150N Dish Adapter [Ralink RT2770]", "HWDN2"), + T_DATA("HWGUSB2-54-LB", "HWGUSB2-54-LB"), + T_DATA("HWGUSB2-54V2-AP", "HWGUSB2-54V2-AP"), + T_DATA("HWGUSB2-54 WLAN", "HWGUSB2-54"), + T_DATA("HWU54DM", "HWU54DM"), + T_DATA("HWUN1 Hi-Gain Wireless-300N Adapter w/ Upgradable Antenna [Ralink RT2870]", + "HWUN1"), + T_DATA("HWUN2 Hi-Gain Wireless-150N Adapter w/ Upgradable Antenna [Ralink RT2770]", + "HWUN2"), + T_DATA("HWUN3 Hi-Gain Wireless-N Adapter [Ralink RT3070]", "HWUN3"), + T_DATA("I210 Gigabit Backplane Connection", "I210 Backplane Connection"), + T_DATA("I210 Gigabit Fiber Network Connection", "I210"), + T_DATA("I210 Gigabit Network Connection", "I210"), + T_DATA("I211 Gigabit Network Connection", "I211"), + T_DATA("I350 Ethernet Controller Virtual Function", "I350 Virtual Function"), + T_DATA("I350 Gigabit Backplane Connection", "I350 Backplane Connection"), + T_DATA("I350 Gigabit Connection", "I350 Connection"), + T_DATA("I350 Gigabit Fiber Network Connection", "I350"), + T_DATA("I350 Gigabit Network Connection", "I350"), + T_DATA("IC Plus IP100A Integrated 10/100 Ethernet MAC + PHY", "IC Plus IP100A"), + T_DATA("IEEE 802.11g Wireless Network Adapter", NULL), + T_DATA("IFU-WLM2 USB Wireless LAN Module (Wireless Mode)", "IFU-WLM2"), + T_DATA("Integrated NetFlex-3/P", "NetFlex-3/P"), + T_DATA("Intrepid2 GMAC (Sun GEM)", "Intrepid2 GMAC"), + T_DATA("IOGear GWU513 v2 802.11bg Wireless Adapter [Intersil ISL3887]", "IOGear GWU513"), + T_DATA("IP1000 Family Gigabit Ethernet", "IP1000"), + T_DATA("iPad 2 (3G; 64GB)", "iPad 2"), + T_DATA("iPad 3 (3G, 16 GB)", "iPad 3"), + T_DATA("iPad 4/Mini1", "iPad 4/Mini1"), + T_DATA("iPad", "iPad"), + T_DATA("iPAQ Networking 10/100 Ethernet [pegasus2]", "iPAQ Networking"), + T_DATA("iPhone 3G", "iPhone"), + T_DATA("iPhone 3GS", "iPhone 3GS"), + T_DATA("iPhone 4(CDMA)", "iPhone 4"), + T_DATA("iPhone 4", "iPhone 4"), + T_DATA("iPhone 4S", "iPhone 4S"), + T_DATA("iPhone5/5C/5S/6", "iPhone5/5C/5S/6"), + T_DATA("iPhone", "iPhone"), + T_DATA("iRex Technologies Gobi 2000 Wireless Modem", "iRex Gobi 2000"), + T_DATA("ISL3877 [Prism Indigo]", "ISL3877"), + T_DATA("ISL3886IK", "ISL3886IK"), + T_DATA("ISL3886 [Prism Javelin/Prism Xbow]", "ISL3886"), + T_DATA("ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow]", "ISL3890"), + T_DATA("ISP4022-based Ethernet NIC", "ISP4022-based"), + T_DATA("ISP4032-based Ethernet IPv6 NIC", "ISP4032-based IPv6"), + T_DATA("ISP8324 1/10GbE Converged Network Controller", "ISP8324"), + T_DATA("ISP8324 1/10GbE Converged Network Controller (NIC VF)", "ISP8324"), + T_DATA("ISY Wireless Micro Adapter IWL 2000 [RTL8188CUS]", "ISY IWL 2000"), + T_DATA("JMC250 PCI Express Gigabit Ethernet Controller", "JMC250"), + T_DATA("JMC260 PCI Express Fast Ethernet Controller", "JMC260"), + T_DATA("K2 GMAC (Sun GEM)", "K2 GMAC"), + T_DATA("K3565-Z HSDPA", "K3565-Z"), + T_DATA("K3570-Z", "K3570-Z"), + T_DATA("K3571-Z", "K3571-Z"), + T_DATA("K4505-Z", "K4505-Z"), + T_DATA("K5006-Z vodafone LTE/UMTS/GSM Modem/Networkcard", "K5006-Z vodafone"), + T_DATA("KC2190 USB Host-to-Host cable", "KC2190 Host-to-Host cable"), + T_DATA("Keebox W150NU 802.11bgn Wireless Adapter [Ralink RT3070]", "Keebox W150NU"), + T_DATA("Killer E220x Gigabit Ethernet Controller", "Killer E220x"), + T_DATA("Killer E2400 Gigabit Ethernet Controller", "Killer E2400"), + T_DATA("KL5KUSB101B Ethernet [klsi]", "KL5KUSB101B"), + T_DATA("KNU101TX 100baseTX Ethernet", "KNU101TX 100baseTX"), + T_DATA("KSZ8842-PMQL 2-Port Ethernet Switch", "KSZ8842-PMQL"), + T_DATA("KwikLink Host-Host Connector", "KwikLink Host-Host Connector"), + T_DATA("LAN7500 Ethernet 10/100/1000 Adapter", "LAN7500"), + T_DATA("LAN9420/LAN9420i", "LAN9420/LAN9420i"), + T_DATA("LAN9512/LAN9514 Ethernet 10/100 Adapter (SAL10)", "LAN9512/LAN9514"), + T_DATA("Laneed 100Mbps Ethernet LD-USB/TX [pegasus]", "Laneed LD-USB/TX"), + T_DATA("LAN-GTJ/U2A", "LAN-GTJ/U2A"), + T_DATA("LAN-W150N/U2 Wireless LAN Adapter", "LAN-W150N/U2"), + T_DATA("LAN-W150/U2M Wireless LAN Adapter", "LAN-W150/U2M"), + T_DATA("LAN-W300AN/U2 Wireless LAN Adapter", "LAN-W300AN/U2"), + T_DATA("LAN-W300N/U2 Wireless LAN Adapter", "LAN-W300N/U2"), + T_DATA("LAN-WN12/U2 Wireless LAN Adapter", "LAN-WN12/U2"), + T_DATA("LAN-WN22/U2 Wireless LAN Adapter", "LAN-WN22/U2"), + T_DATA("LapLink Gold USB-USB Bridge [net1080]", "LapLink Gold USB-USB Bridge"), + T_DATA("LD-USB20", "LD-USB20"), + T_DATA("LD-USBL/TX", "LD-USBL/TX"), + T_DATA("LD-USB/TX", "LD-USB/TX"), + T_DATA("LE920", "LE920"), + T_DATA("Leaf Light HS", "Leaf Light HS"), + T_DATA("Leaf SemiPro HS", "Leaf SemiPro HS"), + T_DATA("LevelOne WUA-0605 N_Max Wireless USB Adapter", "LevelOne WUA-0605 N Max"), + T_DATA("LevelOne WUA-0615 N_Max Wireless USB Adapter", "LevelOne WUA-0615 N Max"), + T_DATA("Libertas", "Libertas"), + T_DATA("Linksys WUSB54GP v1 OEM 802.11g Adapter [Intersil ISL3886]", "Linksys WUSB54GP"), + T_DATA("Linksys WUSB54G v1 OEM 802.11g Adapter [Intersil ISL3886]", "Linksys WUSB54G"), + T_DATA("Linux-USB \"CDC Subset\" Device, or Itsy (experimental)", "Linux-USB or Itsy"), + T_DATA("Linux-USB Ethernet/RNDIS Gadget", "Linux-USB Gadget"), + T_DATA("LN-028 Network USB 2.0 Adapter", "LN-028"), + T_DATA("LN-031 10/100/1000 Ethernet Adapter", "LN-031"), + T_DATA("LNE100TX [Linksys EtherFast 10/100]", "LNE100TX"), + T_DATA("LNE100TX", "LNE100TX"), + T_DATA("lt4112 Gobi 4G Module Network Device", "lt4112 Gobi"), + T_DATA("LTE4G O2 ZTE MF821D LTE/UMTS/GSM Modem/Networkcard", "LTE4G O2 ZTE MF821D"), + T_DATA("LTE Storage Driver [CMC2xx]", "Storage Driver"), + T_DATA("LUA2-TX Ethernet", "LUA2-TX"), + T_DATA("LUA-KTX Ethernet", "LUA-KTX"), + T_DATA("LUA-TX Ethernet", "LUA-TX"), + T_DATA("LUA-TX Ethernet [pegasus]", "LUA-TX"), + T_DATA("LUA-U2-GT 10/100/1000 Ethernet Adapter", "LUA-U2-GT"), + T_DATA("LUA-U2-KTX Ethernet", "LUA-U2-KTX"), + T_DATA("LW153 802.11n Adapter [ralink rt3070]", "LW153"), + T_DATA("LW313 802.11n Adapter [ralink rt2770 + rt2720]", "LW313"), + T_DATA("M-202 802.11bg", "M-202"), + T_DATA("M5261 Ethernet Controller", "M5261"), + T_DATA("M5632 Host-to-Host Link", "M5632 Host-to-Host Link"), + T_DATA("Marvell 88W8388 802.11a/b/g WLAN", "Marvell 88W8388"), + T_DATA("MC8700 Modem", "MC8700"), + T_DATA("MCP04 Ethernet Controller", "MCP04"), + T_DATA("MCP2A Ethernet Controller", "MCP2A"), + T_DATA("MCP51 Ethernet Controller", "MCP51"), + T_DATA("MCP55 Ethernet", "MCP55"), + T_DATA("MCP61 Ethernet", "MCP61"), + T_DATA("MCP65 Ethernet", "MCP65"), + T_DATA("MCP67 Ethernet", "MCP67"), + T_DATA("MCP73 Ethernet", "MCP73"), + T_DATA("MCP77 Ethernet", "MCP77"), + T_DATA("MCP79 Ethernet", "MCP79"), + T_DATA("MCP89 Ethernet", "MCP89"), + T_DATA("MCS7730 10/100 Mbps Ethernet adapter", "MCS7730"), + T_DATA("MCS7830 10/100 Mbps Ethernet adapter", "MCS7830"), + T_DATA("MCS7832 10/100 Mbps Ethernet adapter", "MCS7832"), + T_DATA("Metronic 495257 wifi 802.11ng", "Metronic 495257"), + T_DATA("MF110/MF627/MF636", "MF110/MF627/MF636"), + T_DATA("MF632/ONDA ET502HS/MT505UP", "MF632/ONDA ET502HS/MT505UP"), + T_DATA("MF820 4G LTE", "MF820"), + T_DATA("Micolink USB2Ethernet [pegasus]", "Micolink USB2Ethernet"), + T_DATA("MicroLink dLAN", "MicroLink dLAN"), + T_DATA("MN-120 (ADMtek Centaur-C based)", "MN-120"), + T_DATA("MN-130 (ADMtek Centaur-P based)", "MN-130"), + T_DATA("MN-710 802.11g Wireless Adapter [Intersil ISL3886]", "MN-710"), + T_DATA("model 01 Ethernet interface", "model 01"), + T_DATA("model 01+ Ethernet", "model 01+"), + T_DATA("Motorola 802.11n 5G USB Wireless Adapter", "Motorola"), + T_DATA("Motorola 802.11n Dualband USB Wireless Adapter", "Motorola Dualband"), + T_DATA("MP-PRX1 Ethernet", "MP-PRX1"), + T_DATA("MS-3870 802.11bgn Wireless Module [Ralink RT3070]", "MS-3870"), + T_DATA("MS-3871 802.11bgn Wireless Module [Ralink RT8070]", "MS-3871"), + T_DATA("MSI-6861 802.11g WiFi adapter", "MSI-6861"), + T_DATA("MT25400 Family [ConnectX-2 Virtual Function]", "MT25400"), + T_DATA("MT25408 [ConnectX EN 10GigE 10GBaseT, PCIe 2.0 2.5GT/s]", "MT25408"), + T_DATA("MT25408 [ConnectX EN 10GigE 10GBaseT, PCIe Gen2 5GT/s]", "MT25408"), + T_DATA("MT25408 [ConnectX VPI - IB SDR / 10GigE]", "MT25408"), + T_DATA("MT25418 [ConnectX VPI PCIe 2.0 2.5GT/s - IB DDR / 10GigE]", "MT25418"), + T_DATA("MT25448 [ConnectX EN 10GigE, PCIe 2.0 2.5GT/s]", "MT25448"), + T_DATA("MT26418 [ConnectX VPI PCIe 2.0 5GT/s - IB DDR / 10GigE]", "MT26418"), + T_DATA("MT26428 [ConnectX VPI PCIe 2.0 5GT/s - IB QDR / 10GigE]", "MT26428"), + T_DATA("MT26438 [ConnectX VPI PCIe 2.0 5GT/s - IB QDR / 10GigE Virtualization+]", + "MT26438"), + T_DATA("MT26448 [ConnectX EN 10GigE, PCIe 2.0 5GT/s]", "MT26448"), + T_DATA("MT26468 [ConnectX EN 10GigE, PCIe 2.0 5GT/s Virtualization+]", "MT26468"), + T_DATA("MT26478 [ConnectX EN 40GigE, PCIe 2.0 5GT/s]", "MT26478"), + T_DATA("MT27500 Family [ConnectX-3]", "MT27500"), + T_DATA("MT27500/MT27520 Family [ConnectX-3/ConnectX-3 Pro Virtual Function]", + "MT27500/MT27520"), + T_DATA("MT27510 Family", "MT27510"), + T_DATA("MT27511 Family", "MT27511"), + T_DATA("MT27520 Family [ConnectX-3 Pro]", "MT27520"), + T_DATA("MT27530 Family", "MT27530"), + T_DATA("MT27531 Family", "MT27531"), + T_DATA("MT27540 Family", "MT27540"), + T_DATA("MT27541 Family", "MT27541"), + T_DATA("MT27550 Family", "MT27550"), + T_DATA("MT27551 Family", "MT27551"), + T_DATA("MT27560 Family", "MT27560"), + T_DATA("MT27561 Family", "MT27561"), + T_DATA("MT27600 [Connect-IB]", "MT27600"), + T_DATA("MT27600 Family [Connect-IB Virtual Function]", "MT27600"), + T_DATA("MT27700 Family [ConnectX-4]", "MT27700"), + T_DATA("MT27700 Family [ConnectX-4 Virtual Function]", "MT27700"), + T_DATA("MT27710 Family [ConnectX-4 Lx]", "MT27710"), + T_DATA("MT27710 Family [ConnectX-4 Lx Virtual Function]", "MT27710"), + T_DATA("MT27800 Family [ConnectX-5]", "MT27800"), + T_DATA("MT27800 Family [ConnectX-5 Virtual Function]", "MT27800"), + T_DATA("MT28800 Family [ConnectX-5 Ex]", "MT28800"), + T_DATA("MT28800 Family [ConnectX-5 Ex Virtual Function]", "MT28800"), + T_DATA("MT28908 Family [ConnectX-6]", "MT28908"), + T_DATA("MT28908 Family [ConnectX-6 Virtual Function]", "MT28908"), + T_DATA("MT416842 BlueField integrated ConnectX-5 network controller", + "MT416842 BlueField ConnectX-5"), + T_DATA("MT416842 BlueField multicore SoC family VF", "MT416842 BlueField VF"), + T_DATA("MT51136", "MT51136"), + T_DATA("MT52100", "MT52100"), + T_DATA("MT53236", "MT53236"), + T_DATA("MT7601U Wireless Adapter", "MT7601U"), + T_DATA("MTD-8xx 100/10M Ethernet PCI Adapter", "MTD-8xx"), + T_DATA("Multithreaded 10-Gigabit Ethernet Network Controller", NULL), + T_DATA("MX98713", "MX98713"), + T_DATA("MX987x5", "MX987x5"), + T_DATA("Myri-10G Dual-Protocol NIC", "Myri-10G"), + T_DATA("N10 Nano 802.11n Network Adapter [Realtek RTL8192CU]", "N10 Nano"), + T_DATA("N220 802.11bgn Wireless Adapter", "N220"), + T_DATA("N320-G2-CR 10GbE Dual Port Adapter", "N320-G2-CR"), + T_DATA("N5HBZ0000055 802.11abgn Wireless Adapter [Atheros AR7010+AR9280]", "N5HBZ0000055"), + T_DATA("Name: Voyager 1055 Laptop 802.11g Adapter [Broadcom 4320]", "Voyager 1055"), + T_DATA("NC100 Network Everywhere Fast Ethernet 10/100", "NC100"), + T_DATA("NE-34", "NE-34"), + T_DATA("NET1080 USB-USB Bridge", "NET1080 USB-USB Bridge"), + T_DATA("Netelligent 10/100 TX Embedded UTP", "Netelligent TX"), + T_DATA("Netelligent 10/100 TX PCI UTP", "Netelligent TX"), + T_DATA("Netelligent 10/100 TX UTP", "Netelligent TX"), + T_DATA("Netelligent 10 T/2 PCI UTP/Coax", "Netelligent 10 T/2"), + T_DATA("Netelligent 10 T PCI UTP", "Netelligent 10 T"), + T_DATA("Netelligent Dual 10/100 TX PCI UTP", "Netelligent Dual TX"), + T_DATA("Netelligent Integrated 10/100 TX UTP", "Netelligent TX"), + T_DATA("NetFlex-3/P ThunderLAN 1.0", "NetFlex-3/P ThunderLAN 1.0"), + T_DATA("NetFlex-3/P ThunderLAN 2.3", "NetFlex-3/P ThunderLAN 2.3"), + T_DATA("NetLink BCM57780 Gigabit Ethernet PCIe", "NetLink BCM57780"), + T_DATA("NetLink BCM57781 Gigabit Ethernet PCIe", "NetLink BCM57781"), + T_DATA("NetLink BCM57785 Gigabit Ethernet PCIe", "NetLink BCM57785"), + T_DATA("NetLink BCM57788 Gigabit Ethernet PCIe", "NetLink BCM57788"), + T_DATA("NetLink BCM57790 Gigabit Ethernet PCIe", "NetLink BCM57790"), + T_DATA("NetLink BCM57791 Gigabit Ethernet PCIe", "NetLink BCM57791"), + T_DATA("NetLink BCM57795 Gigabit Ethernet PCIe", "NetLink BCM57795"), + T_DATA("NetLink BCM5781 Gigabit Ethernet PCI Express", "NetLink BCM5781"), + T_DATA("NetLink BCM5784M Gigabit Ethernet PCIe", "NetLink BCM5784M"), + T_DATA("NetLink BCM5785 Fast Ethernet", "NetLink BCM5785"), + T_DATA("NetLink BCM5785 Gigabit Ethernet", "NetLink BCM5785"), + T_DATA("NetLink BCM5786 Gigabit Ethernet PCI Express", "NetLink BCM5786"), + T_DATA("NetLink BCM5787F Fast Ethernet PCI Express", "NetLink BCM5787F"), + T_DATA("NetLink BCM5787 Gigabit Ethernet PCI Express", "NetLink BCM5787"), + T_DATA("NetLink BCM5787M Gigabit Ethernet PCI Express", "NetLink BCM5787M"), + T_DATA("NetLink BCM5789 Gigabit Ethernet PCI Express", "NetLink BCM5789"), + T_DATA("NetLink BCM5906 Fast Ethernet PCI Express", "NetLink BCM5906"), + T_DATA("NetLink BCM5906M Fast Ethernet PCI Express", "NetLink BCM5906M"), + T_DATA("NetMate2 Ethernet", "NetMate2"), + T_DATA("NetMate Ethernet", "NetMate"), + T_DATA("NetXen Dual Port 10GbE Multifunction Adapter for c-Class", + "NetXen Multifunction for c-Class"), + T_DATA("NetXtreme 5714S Gigabit Ethernet", "NetXtreme 5714S"), + T_DATA("NetXtreme BCM5700 Gigabit Ethernet", "NetXtreme BCM5700"), + T_DATA("NetXtreme BCM5701 Gigabit Ethernet", "NetXtreme BCM5701"), + T_DATA("NetXtreme BCM5702A3 Gigabit Ethernet", "NetXtreme BCM5702A3"), + T_DATA("NetXtreme BCM5702FE Gigabit Ethernet", "NetXtreme BCM5702FE"), + T_DATA("NetXtreme BCM5702 Gigabit Ethernet", "NetXtreme BCM5702"), + T_DATA("NetXtreme BCM5702X Gigabit Ethernet", "NetXtreme BCM5702X"), + T_DATA("NetXtreme BCM5703 Gigabit Ethernet", "NetXtreme BCM5703"), + T_DATA("NetXtreme BCM5703X Gigabit Ethernet", "NetXtreme BCM5703X"), + T_DATA("NetXtreme BCM5704 Gigabit Ethernet", "NetXtreme BCM5704"), + T_DATA("NetXtreme BCM5704S_2 Gigabit Ethernet", "NetXtreme BCM5704S 2"), + T_DATA("NetXtreme BCM5704S Gigabit Ethernet", "NetXtreme BCM5704S"), + T_DATA("NetXtreme BCM5705_2 Gigabit Ethernet", "NetXtreme BCM5705 2"), + T_DATA("NetXtreme BCM5705 Gigabit Ethernet", "NetXtreme BCM5705"), + T_DATA("NetXtreme BCM5705M_2 Gigabit Ethernet", "NetXtreme BCM5705M 2"), + T_DATA("NetXtreme BCM5705M Gigabit Ethernet", "NetXtreme BCM5705M"), + T_DATA("NetXtreme BCM5714 Gigabit Ethernet", "NetXtreme BCM5714"), + T_DATA("NetXtreme BCM5715 Gigabit Ethernet", "NetXtreme BCM5715"), + T_DATA("NetXtreme BCM5715S Gigabit Ethernet", "NetXtreme BCM5715S"), + T_DATA("NetXtreme BCM5717 Gigabit Ethernet PCIe", "NetXtreme BCM5717"), + T_DATA("NetXtreme BCM5718 Gigabit Ethernet PCIe", "NetXtreme BCM5718"), + T_DATA("NetXtreme BCM5719 Gigabit Ethernet PCIe", "NetXtreme BCM5719"), + T_DATA("NetXtreme BCM5720 Gigabit Ethernet PCIe", "NetXtreme BCM5720"), + T_DATA("NetXtreme BCM5721 Gigabit Ethernet PCI Express", "NetXtreme BCM5721"), + T_DATA("NetXtreme BCM5722 Gigabit Ethernet PCI Express", "NetXtreme BCM5722"), + T_DATA("NetXtreme BCM5723 Gigabit Ethernet PCIe", "NetXtreme BCM5723"), + T_DATA("NetXtreme BCM5725 Gigabit Ethernet PCIe", "NetXtreme BCM5725"), + T_DATA("NetXtreme BCM5727 Gigabit Ethernet PCIe", "NetXtreme BCM5727"), + T_DATA("NetXtreme BCM5751F Fast Ethernet PCI Express", "NetXtreme BCM5751F"), + T_DATA("NetXtreme BCM5751 Gigabit Ethernet PCI Express", "NetXtreme BCM5751"), + T_DATA("NetXtreme BCM5751M Gigabit Ethernet PCI Express", "NetXtreme BCM5751M"), + T_DATA("NetXtreme BCM5752 Gigabit Ethernet PCI Express", "NetXtreme BCM5752"), + T_DATA("NetXtreme BCM5752M Gigabit Ethernet PCI Express", "NetXtreme BCM5752M"), + T_DATA("NetXtreme BCM5753F Fast Ethernet PCI Express", "NetXtreme BCM5753F"), + T_DATA("NetXtreme BCM5753 Gigabit Ethernet PCI Express", "NetXtreme BCM5753"), + T_DATA("NetXtreme BCM5753M Gigabit Ethernet PCI Express", "NetXtreme BCM5753M"), + T_DATA("NetXtreme BCM5754 Gigabit Ethernet PCI Express", "NetXtreme BCM5754"), + T_DATA("NetXtreme BCM5754M Gigabit Ethernet PCI Express", "NetXtreme BCM5754M"), + T_DATA("NetXtreme BCM5755 Gigabit Ethernet PCI Express", "NetXtreme BCM5755"), + T_DATA("NetXtreme BCM5755M Gigabit Ethernet PCI Express", "NetXtreme BCM5755M"), + T_DATA("NetXtreme BCM5756ME Gigabit Ethernet PCI Express", "NetXtreme BCM5756ME"), + T_DATA("NetXtreme BCM5761 10/100/1000BASE-T Ethernet", "NetXtreme BCM5761"), + T_DATA("NetXtreme BCM5761e Gigabit Ethernet PCIe", "NetXtreme BCM5761e"), + T_DATA("NetXtreme BCM5761 Gigabit Ethernet PCIe", "NetXtreme BCM5761"), + T_DATA("NetXtreme BCM5762 Gigabit Ethernet PCIe", "NetXtreme BCM5762"), + T_DATA("NetXtreme BCM5764M Gigabit Ethernet PCIe", "NetXtreme BCM5764M"), + T_DATA("NetXtreme BCM57760 Gigabit Ethernet PCIe", "NetXtreme BCM57760"), + T_DATA("NetXtreme BCM57761 Gigabit Ethernet PCIe", "NetXtreme BCM57761"), + T_DATA("NetXtreme BCM57762 Gigabit Ethernet PCIe", "NetXtreme BCM57762"), + T_DATA("NetXtreme BCM57764 Gigabit Ethernet PCIe", "NetXtreme BCM57764"), + T_DATA("NetXtreme BCM57765 Gigabit Ethernet PCIe", "NetXtreme BCM57765"), + T_DATA("NetXtreme BCM57766 Gigabit Ethernet PCIe", "NetXtreme BCM57766"), + T_DATA("NetXtreme BCM57767 Gigabit Ethernet PCIe", "NetXtreme BCM57767"), + T_DATA("NetXtreme BCM57782 Gigabit Ethernet PCIe", "NetXtreme BCM57782"), + T_DATA("NetXtreme BCM57786 Gigabit Ethernet PCIe", "NetXtreme BCM57786"), + T_DATA("NetXtreme BCM57787 Gigabit Ethernet PCIe", "NetXtreme BCM57787"), + T_DATA("NetXtreme BCM5780 Gigabit Ethernet", "NetXtreme BCM5780"), + T_DATA("NetXtreme BCM5780S Gigabit Ethernet", "NetXtreme BCM5780S"), + T_DATA("NetXtreme BCM5782 Gigabit Ethernet", "NetXtreme BCM5782"), + T_DATA("NetXtreme BCM5788 Gigabit Ethernet", "NetXtreme BCM5788"), + T_DATA("NetXtreme BCM5901 100Base-TX", "NetXtreme BCM5901"), + T_DATA("NetXtreme-C Ethernet Virtual Function", "NetXtreme-C Virtual Function"), + T_DATA("NetXtreme-C RDMA Virtual Function", "NetXtreme-C Virtual Function"), + T_DATA("NetXtreme-E Ethernet Virtual Function", "NetXtreme-E Virtual Function"), + T_DATA("NetXtreme-E RDMA Virtual Function", "NetXtreme-E Virtual Function"), + T_DATA( + "NetXtreme II BCM5706 Gigabit Ethernet (NC370i Multifunction Gigabit Server Adapter)", + "NetXtreme II BCM5706"), + T_DATA( + "NetXtreme II BCM5706 Gigabit Ethernet (NC370T MultifuNCtion Gigabit Server Adapter)", + "NetXtreme II BCM5706"), + T_DATA("NetXtreme II BCM5706 Gigabit Ethernet", "NetXtreme II BCM5706"), + T_DATA( + "NetXtreme II BCM5706S Gigabit Ethernet (NC370F MultifuNCtion Gigabit Server Adapter)", + "NetXtreme II BCM5706S"), + T_DATA("NetXtreme II BCM5706S Gigabit Ethernet", "NetXtreme II BCM5706S"), + T_DATA("NetXtreme II BCM5708 Gigabit Ethernet", "NetXtreme II BCM5708"), + T_DATA("NetXtreme II BCM5708S Gigabit Ethernet", "NetXtreme II BCM5708S"), + T_DATA("NetXtreme II BCM5709 Gigabit Ethernet", "NetXtreme II BCM5709"), + T_DATA("NetXtreme II BCM5709S Gigabit Ethernet", "NetXtreme II BCM5709S"), + T_DATA("NetXtreme II BCM5716 Gigabit Ethernet", "NetXtreme II BCM5716"), + T_DATA("NetXtreme II BCM5716S Gigabit Ethernet", "NetXtreme II BCM5716S"), + T_DATA("NetXtreme II BCM57710 10-Gigabit PCIe [Everest]", "NetXtreme II BCM57710"), + T_DATA("NetXtreme II BCM57711 10-Gigabit PCIe", "NetXtreme II BCM57711"), + T_DATA("NetXtreme II BCM57711E 10-Gigabit PCIe", "NetXtreme II BCM57711E"), + T_DATA("NetXtreme II BCM57712 10 Gigabit Ethernet Multi Function", "NetXtreme II BCM57712"), + T_DATA("NetXtreme II BCM57712 10 Gigabit Ethernet", "NetXtreme II BCM57712"), + T_DATA("NetXtreme II BCM57712 10 Gigabit Ethernet Virtual Function", + "NetXtreme II BCM57712 Virtual Function"), + T_DATA("NetXtreme II BCM57800 1/10 Gigabit Ethernet Multi Function", + "NetXtreme II BCM57800"), + T_DATA("NetXtreme II BCM57800 1/10 Gigabit Ethernet", "NetXtreme II BCM57800"), + T_DATA("NetXtreme II BCM57800 1/10 Gigabit Ethernet Virtual Function", + "NetXtreme II BCM57800 Virtual Function"), + T_DATA("NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function", "NetXtreme II BCM57810"), + T_DATA("NetXtreme II BCM57810 10 Gigabit Ethernet", "NetXtreme II BCM57810"), + T_DATA("NetXtreme II BCM57810 10 Gigabit Ethernet Virtual Function", + "NetXtreme II BCM57810 Virtual Function"), + T_DATA("NetXtreme II BCM57811 10 Gigabit Ethernet Multi Function", "NetXtreme II BCM57811"), + T_DATA("NetXtreme II BCM57811 10-Gigabit Ethernet", "NetXtreme II BCM57811"), + T_DATA("NetXtreme II BCM57811 10-Gigabit Ethernet Virtual Function", + "NetXtreme II BCM57811 Virtual Function"), + T_DATA("NetXtreme II BCM57840 10/20 Gigabit Ethernet Multi Function", + "NetXtreme II BCM57840 10/20"), + T_DATA("NetXtreme II BCM57840 10/20 Gigabit Ethernet", "NetXtreme II BCM57840 10/20"), + T_DATA("NetXtreme II BCM57840 10/20 Gigabit Ethernet Virtual Function", + "NetXtreme II BCM57840 10/20 Virtual Function"), + T_DATA("nForce2 Ethernet Controller", "nForce2"), + T_DATA("nForce3 Ethernet", "nForce3"), + T_DATA("nForce Ethernet Controller", "nForce"), + T_DATA("Nintendo Wi-Fi", "Nintendo"), + T_DATA("NM10/ICH7 Family LAN Controller", "NM10/ICH7"), + T_DATA("NovaTech NV-902W", "NovaTech NV-902W"), + T_DATA("NUB100 Ethernet [pegasus]", "NUB100"), + T_DATA("NUB-350 802.11g Wireless Adapter [Intersil ISL3887]", "NUB-350"), + T_DATA("NUB-8301 802.11bg", "NUB-8301"), + T_DATA("NV5000SC", "NV5000SC"), + T_DATA("NW-3100 802.11b/g 54Mbps Wireless Network Adapter [zd1211]", "NW-3100"), + T_DATA("NWD2105 802.11bgn Wireless Adapter [Ralink RT3070]", "NWD2105"), + T_DATA("NWD-210N 802.11b/g/n-draft wireless adapter", "NWD-210N"), + T_DATA("NWD211AN 802.11abgn Wireless Adapter [Ralink RT2870]", "NWD211AN"), + T_DATA("NWD2205 802.11n Wireless N Adapter [Realtek RTL8192CU]", "NWD2205"), + T_DATA("NWD-270N Wireless N-lite USB Adapter", "NWD-270N N-lite"), + T_DATA("NWD271N 802.11n Wireless Adapter [Atheros AR9001U-(2)NG]", "NWD271N"), + T_DATA("NX3031 Multifunction 1/10-Gigabit Server Adapter", "NX3031 Multifunction"), + T_DATA("NXB-10GCX4 10-Gigabit Ethernet PCIe Adapter with CX4 copper interface", + "NXB-10GCX4"), + T_DATA("NXB-10GXSR 10-Gigabit Ethernet PCIe Adapter with SR-XFP optical interface", + "NXB-10GXSR"), + T_DATA("NXB-4GCU Quad Gigabit Ethernet PCIe Adapter with 1000-BASE-T interface", + "NXB-4GCU Quad"), + T_DATA("OC-2183/2185", "OC-2183/2185"), + T_DATA("OC-2325", "OC-2325"), + T_DATA("OC-2326", "OC-2326"), + T_DATA("OCT To Fast Ethernet Converter", "OCT To Converter"), + T_DATA("Olicard 100", "Olicard 100"), + T_DATA("OneConnect 10Gb NIC (be3)", "OneConnect"), + T_DATA("OneConnect NIC (Lancer)", "OneConnect"), + T_DATA("OneConnect NIC (Skyhawk)", "OneConnect"), + T_DATA("OneConnect NIC (Skyhawk-VF)", "OneConnect"), + T_DATA("OneConnect OCe10100/OCe10102 Series 10 GbE", "OneConnect OCe10100/OCe10102"), + T_DATA("On Networks N300MA 802.11bgn [Realtek RTL8192CU]", "On Networks N300MA"), + T_DATA("Ovation MC551", "Ovation MC551"), + T_DATA("PCAN-PCI CAN-Bus controller", "PCAN-PCI"), + T_DATA("PCAN Pro", "PCAN Pro"), + T_DATA("PCAN-USB", "PCAN-USB"), + T_DATA("PCI NE2K Ethernet", "NE2K"), + T_DATA("PCI Rocker Ethernet switch device", "Rocker switch"), + T_DATA("PL2301 USB-USB Bridge", "PL2301 USB-USB Bridge"), + T_DATA("PL2302 USB-USB Bridge", "PL2302 USB-USB Bridge"), + T_DATA("PL25A1 Host-Host Bridge", "PL25A1 Host-Host Bridge"), + T_DATA("Platform Controller Hub EG20T Controller Area Network (CAN) Controller", "EG20T"), + T_DATA("PN672TX 10/100 Ethernet", "PN672TX"), + T_DATA("Pocket Ethernet [klsi]", "Pocket"), + T_DATA("Prism GT 802.11b/g Adapter", "Prism GT"), + T_DATA("PRO/100 VE Network Connection", "PRO/100 VE"), + T_DATA("PRO/100 VM Network Connection", "PRO/100 VM"), + T_DATA("PRO/Wireless 2200BG [Calexico2] Network Connection", "PRO/Wireless 2200BG"), + T_DATA("PRO/Wireless 2915ABG [Calexico2] Network Connection", "PRO/Wireless 2915ABG"), + T_DATA("PRO/Wireless 3945ABG [Golan] Network Connection", "PRO/Wireless 3945ABG"), + T_DATA("PRO/Wireless 4965 AG or AGN [Kedron] Network Connection", + "PRO/Wireless 4965 AG or AGN"), + T_DATA("PRO/Wireless 5100 AGN [Shiloh] Network Connection", "PRO/Wireless 5100 AGN"), + T_DATA("PRO/Wireless 5350 AGN [Echo Peak] Network Connection", "PRO/Wireless 5350 AGN"), + T_DATA("PRO/Wireless LAN 2100 3B Mini PCI Adapter (Dell Latitude D800)", + "PRO/Wireless 2100 3B Mini"), + T_DATA("PRO/Wireless LAN 2100 3B Mini PCI Adapter (MIM2000/Centrino)", + "PRO/Wireless 2100 3B Mini"), + T_DATA("PRO/Wireless LAN 2100 3B Mini PCI Adapter", "PRO/Wireless 2100 3B Mini"), + T_DATA("PRO/Wireless LAN 2100 3B Mini PCI Adapter (Samsung X10/P30 integrated WLAN)", + "PRO/Wireless 2100 3B Mini"), + T_DATA("PRO/Wireless LAN 2100 3B Mini PCI Adapter (Toshiba Satellite M10)", + "PRO/Wireless 2100 3B Mini"), + T_DATA("Psion Gold Port Ethernet", "Psion Gold Port"), + T_DATA("PTA01 Wireless Adapter", "PTA01"), + T_DATA("QCA6164 802.11ac Wireless Network Adapter", "QCA6164"), + T_DATA("QCA6174 802.11ac Wireless Network Adapter", "QCA6174"), + T_DATA("QCA8171 Gigabit Ethernet", "QCA8171"), + T_DATA("QCA8172 Fast Ethernet", "QCA8172"), + T_DATA("QCA9377 802.11ac Wireless Network Adapter", "QCA9377"), + T_DATA("QCA9565 / AR9565 Wireless Network Adapter", "QCA9565 / AR9565"), + T_DATA("QCA986x/988x 802.11ac Wireless Network Adapter", "QCA986x/988x"), + T_DATA("QCA9887 802.11ac Wireless Network Adapter", "QCA9887"), + T_DATA("QCA9980/9990 802.11ac Wireless Network Adapter", "QCA9980/9990"), + T_DATA("Qualcomm HSUSB Device", "Qualcomm HSUSB"), + T_DATA("Quectel UC20", "Quectel UC20"), + T_DATA("QuickWLAN 802.11bg", "QuickWLAN"), + T_DATA("R6040 MAC Controller", "R6040"), + T_DATA("Ralink RT2770/2720 802.11b/g/n Wireless LAN Mini-USB Device", "Ralink RT2770/2720"), + T_DATA("Ralink RT3070 802.11b/g/n Wireless Lan USB Device", "Ralink RT3070"), + T_DATA("ReadyLink 2000", "ReadyLink 2000"), + T_DATA("Realtek RTL8187 Wireless 802.11g 54Mbps Network Adapter", "Realtek RTL8187"), + T_DATA("RIO 10/100 Ethernet [eri]", "RIO"), + T_DATA("RL100-ATX 10/100", "RL100-ATX"), + T_DATA("RL100TX Fast Ethernet", "RL100TX"), + T_DATA("ROL/F-100 Fast Ethernet Adapter with ROL", "ROL/F-100 with ROL"), + T_DATA("RT2070 Wireless Adapter", "RT2070"), + T_DATA("RT2500USB Wireless Adapter", "RT2500USB"), + T_DATA("RT2500 Wireless 802.11bg", "RT2500"), + T_DATA("RT2501/RT2573 Wireless Adapter", "RT2501/RT2573"), + T_DATA("RT2501USB Wireless Adapter", "RT2501USB"), + T_DATA("RT2561/RT61 802.11g PCI", "RT2561/RT61"), + T_DATA("RT2561/RT61 rev B 802.11g", "RT2561/RT61"), + T_DATA("RT2570", "RT2570"), + T_DATA("RT2570 Wireless Adapter", "RT2570"), + T_DATA("RT2573", "RT2573"), + T_DATA("RT2600 802.11 MIMO", "RT2600 MIMO"), + T_DATA("RT2601/RT2671 Wireless Adapter", "RT2601/RT2671"), + T_DATA("RT2760 Wireless 802.11n 1T/2R", "RT2760 1T/2R"), + T_DATA("RT2770 Wireless Adapter", "RT2770"), + T_DATA("RT2790 Wireless 802.11n 1T/2R PCIe", "RT2790 1T/2R"), + T_DATA("RT2800 802.11n PCI", "RT2800"), + T_DATA("RT2870/RT3070 Wireless Adapter", "RT2870/RT3070"), + T_DATA("RT2870 Wireless Adapter", "RT2870"), + T_DATA("RT2890 Wireless 802.11n PCIe", "RT2890"), + T_DATA("RT3060 Wireless 802.11n 1T/1R", "RT3060 1T/1R"), + T_DATA("RT3062 Wireless 802.11n 2T/2R", "RT3062 2T/2R"), + T_DATA("RT3071 Wireless Adapter", "RT3071"), + T_DATA("RT3072 Wireless Adapter", "RT3072"), + T_DATA("RT3090 Wireless 802.11n 1T/1R PCIe", "RT3090 1T/1R"), + T_DATA("RT3091 Wireless 802.11n 1T/2R PCIe", "RT3091 1T/2R"), + T_DATA("RT3092 Wireless 802.11n 2T/2R PCIe", "RT3092 2T/2R"), + T_DATA("RT3290 Wireless 802.11n 1T/1R PCIe", "RT3290 1T/1R"), + T_DATA("RT3370 Wireless Adapter", "RT3370"), + T_DATA("RT3572 Wireless Adapter", "RT3572"), + T_DATA("RT3573 Wireless Adapter", "RT3573"), + T_DATA("RT3592 PCIe Wireless Network Adapter", "RT3592"), + T_DATA("RT3592 Wireless 802.11abgn 2T/2R PCIe", "RT3592 2T/2R"), + T_DATA("RT5360 Wireless 802.11n 1T/1R", "RT5360 1T/1R"), + T_DATA("RT5362 PCI 802.11n Wireless Network Adapter", "RT5362"), + T_DATA("RT5370 Wireless Adapter", "RT5370"), + T_DATA("RT5372 Wireless Adapter", "RT5372"), + T_DATA("RT5390 [802.11 b/g/n 1T1R G-band PCI Express Single Chip]", "RT5390"), + T_DATA("RT5390R 802.11bgn PCIe Wireless Network Adapter", "RT5390R"), + T_DATA("RT5390 Wireless 802.11n 1T/1R PCIe", "RT5390 1T/1R"), + T_DATA("RT5392 PCIe Wireless Network Adapter", "RT5392"), + T_DATA("RT5572 Wireless Adapter", "RT5572"), + T_DATA("RT8139 (B/C) Cardbus Fast Ethernet Adapter", "RT8139"), + T_DATA("RTL-8029(AS)", "RTL-8029"), + T_DATA("RTL-8100/8101L/8139 PCI Fast Ethernet Adapter", "RTL-8100/8101L/8139"), + T_DATA("RTL8101/2/6E PCI Express Fast/Gigabit Ethernet controller", "RTL8101/2/6E"), + T_DATA("RTL-8110SC/8169SC Gigabit Ethernet", "RTL-8110SC/8169SC"), + T_DATA("RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller", "RTL8111/8168/8411"), + T_DATA("RTL-8129", "RTL-8129"), + T_DATA("RTL8139D [Realtek] PCI 10/100BaseTX ethernet adaptor", "RTL8139D"), + T_DATA("RTL8139 Ethernet", "RTL8139"), + T_DATA("RTL8139 [FE2000VX] CardBus Fast Ethernet Attached Port Adapter", "RTL8139"), + T_DATA("RTL8150 Fast Ethernet Adapter", "RTL8150"), + T_DATA("RTL8151", "RTL8151"), + T_DATA("RTL8152 Fast Ethernet Adapter", "RTL8152"), + T_DATA("RTL8153 Gigabit Ethernet Adapter", "RTL8153"), + T_DATA("RTL8169 PCI Gigabit Ethernet Controller", "RTL8169"), + T_DATA("RTL8180L 802.11b MAC", "RTL8180L"), + T_DATA("RTL-8185 IEEE 802.11a/b/g Wireless LAN Controller", "RTL-8185"), + T_DATA("RTL8187B Wireless 802.11g 54Mbps Network Adapter", "RTL8187B"), + T_DATA("RTL8187B Wireless Adapter", "RTL8187B"), + T_DATA("RTL8187SE Wireless LAN Controller", "RTL8187SE"), + T_DATA("RTL8187 Wireless Adapter", "RTL8187"), + T_DATA("RTL8188CE 802.11b/g/n WiFi Adapter", "RTL8188CE"), + T_DATA("RTL8188CUS 802.11n WLAN Adapter", "RTL8188CUS"), + T_DATA("RTL8188EE Wireless Network Adapter", "RTL8188EE"), + T_DATA("RTL8188RU 802.11n WLAN Adapter", "RTL8188RU"), + T_DATA("RTL8191CE PCIe Wireless Network Adapter", "RTL8191CE"), + T_DATA("RTL8191SEvA Wireless LAN Controller", "RTL8191SEvA"), + T_DATA("RTL8191SEvB Wireless LAN Controller", "RTL8191SEvB"), + T_DATA("RTL8192CE PCIe Wireless Network Adapter", "RTL8192CE"), + T_DATA("RTL8192CU 802.11n WLAN Adapter", "RTL8192CU"), + T_DATA("RTL8192DE Wireless LAN Controller", "RTL8192DE"), + T_DATA("RTL8192EE PCIe Wireless Network Adapter", "RTL8192EE"), + T_DATA("RTL8192E/RTL8192SE Wireless LAN Controller", "RTL8192E/RTL8192SE"), + T_DATA("RTL8192EU 802.11b/g/n WLAN Adapter", "RTL8192EU"), + T_DATA("RTL8192SE Wireless LAN Controller", "RTL8192SE"), + T_DATA("RTL81xx Fast Ethernet", "RTL81xx"), + T_DATA("RTL81xx RealTek Ethernet", "RTL81xx RealTek"), + T_DATA("RTL8723AE PCIe Wireless Network Adapter", "RTL8723AE"), + T_DATA("RTL8723AU 802.11n WLAN Adapter", "RTL8723AU"), + T_DATA("RTL8723BE PCIe Wireless Network Adapter", "RTL8723BE"), + T_DATA("RTL8812AE 802.11ac PCIe Wireless Network Adapter", "RTL8812AE"), + T_DATA("RTL8821AE 802.11ac PCIe Wireless Network Adapter", "RTL8821AE"), + T_DATA("S310-CR 10GbE Single Port Adapter", "S310-CR Single Port"), + T_DATA("S320-LP-CR 10GbE Dual Port Adapter", "S320-LP-CR"), + T_DATA("Samsung Gobi 2000 Wireless Modem", "Samsung Gobi 2000"), + T_DATA("SC92031 PCI Fast Ethernet Adapter", "SC92031"), + T_DATA("SD8688 WLAN", "SD8688"), + T_DATA("SD8786 WLAN", "SD8786"), + T_DATA("SD8787 WLAN", "SD8787"), + T_DATA("SD8797 WLAN", "SD8797"), + T_DATA("SD8897 WLAN", "SD8897"), + T_DATA("SFC4000 rev A net [Solarstorm]", "SFC4000"), + T_DATA("SFC4000 rev B [Solarstorm]", "SFC4000"), + T_DATA("SFC9020 10G Ethernet Controller", "SFC9020"), + T_DATA("SFC9120 10G Ethernet Controller", "SFC9120"), + T_DATA("SFC9120 10G Ethernet Controller (Virtual Function)", "SFC9120"), + T_DATA("SFC9140 10/40G Ethernet Controller", "SFC9140"), + T_DATA("SFC9140 10/40G Ethernet Controller (Virtual Function)", "SFC9140"), + T_DATA("SFC9220 10/40G Ethernet Controller", "SFC9220"), + T_DATA("SFC9220 10/40G Ethernet Controller (Virtual Function)", "SFC9220"), + T_DATA("SFL9021 10GBASE-T Ethernet Controller", "SFL9021"), + T_DATA("Shasta (Sun GEM)", "Shasta"), + T_DATA("Siemens S30853-S1016-R107 802.11g Wireless Adapter [Intersil ISL3886]", + "Siemens S30853-S1016-R107"), + T_DATA("Siemens S30853-S1031-R351 802.11g Wireless Adapter [Atheros AR5523]", + "Siemens S30853-S1031-R351"), + T_DATA("Siemens S30853-S1038-R351 802.11g Wireless Adapter [Atheros AR5523]", + "Siemens S30853-S1038-R351"), + T_DATA("Siemens S30863-S1016-R107-2 802.11g Wireless Adapter [Intersil ISL3887]", + "Siemens S30863-S1016-R107-2"), + T_DATA("Siemens SpeedStream 100MBps Ethernet", "Siemens SpeedStream"), + T_DATA("Sierra Wireless Gobi 3000 Modem device (MC8355)", "Sierra Gobi 3000"), + T_DATA("SIMCom SIM5218 modem", "SIMCom SIM5218"), + T_DATA("SiS7016 PCI Fast Ethernet Adapter", "SiS7016"), + T_DATA("SiS900 PCI Fast Ethernet", "SiS900"), + T_DATA("SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC", + "SK-9871 ZX/SC"), + T_DATA("SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link)", "SK-9872"), + T_DATA("SK-9Dxx Gigabit Ethernet Adapter", "SK-9Dxx"), + T_DATA("SK-9E21D 10/100/1000Base-T Adapter, Copper RJ-45", "SK-9E21D"), + T_DATA("SK-9E21M 10/100/1000Base-T Adapter", "SK-9E21M"), + T_DATA("SK-9Mxx Gigabit Ethernet Adapter", "SK-9Mxx"), + T_DATA("SK-9S21 10/100/1000Base-T Server Adapter, PCI-X, Copper RJ-45", "SK-9S21"), + T_DATA("smartNIC 2 PnP Ethernet", "smartNIC 2"), + T_DATA("smartNIC Ethernet [catc]", "smartNIC"), + T_DATA("SMC2-1211TX", "SMC2-1211TX"), + T_DATA("SMC2862W-G v1 EZ Connect 802.11g Adapter [Intersil ISL3886]", + "SMC2862W-G EZ Connect"), + T_DATA("SMC2862W-G v2 EZ Connect 802.11g Adapter [Intersil ISL3887]", + "SMC2862W-G EZ Connect"), + T_DATA("SMC2862W-G v3 EZ Connect 802.11g Adapter [Intersil ISL3887]", + "SMC2862W-G EZ Connect"), + T_DATA("SMC SMCWUSB-N 802.11bgn 2x2:2 Wireless Adapter [Ralink RT2870]", + "SMC SMCWUSB-N 2x2:2"), + T_DATA("SMCWUSB-G 802.11bg", "SMCWUSB-G"), + T_DATA("SMCWUSBS-N2 EZ Connect N Wireless Adapter [Ralink RT2870]", + "SMCWUSBS-N2 EZ Connect"), + T_DATA("SMCWUSBS-N3 EZ Connect N Wireless Adapter [Ralink RT3070]", + "SMCWUSBS-N3 EZ Connect"), + T_DATA("SMCWUSBS-N EZ Connect N Draft 11n Wireless Adapter [Ralink RT2870]", + "SMCWUSBS-N EZ Connect"), + T_DATA("SMCWUSBT-G (no firmware)", "SMCWUSBT-G"), + T_DATA("SMCWUSBT-G", "SMCWUSBT-G"), + T_DATA("SMSC9512/9514 Fast Ethernet Adapter", "SMSC9512/9514"), + T_DATA("SNU5600 802.11bg", "SNU5600"), + T_DATA("SoftGate 802.11 Adapter", "SoftGate"), + T_DATA("Sony 10Mbps Ethernet [pegasus]", "Sony"), + T_DATA("Sony Gobi 2000 Wireless Modem", "Sony Gobi 2000"), + T_DATA("Sony UWA-BR100 802.11abgn Wireless Adapter [Atheros AR7010+AR9280]", + "Sony UWA-BR100"), + T_DATA("SparkLAN WL-682 802.11bg Wireless Adapter [Intersil ISL3887]", "SparkLAN WL-682"), + T_DATA("Speedport W 102 Stick IEEE 802.11n USB 2.0 Adapter", "Speedport W 102 Stick"), + T_DATA("SpeedStream 10/100 Ethernet [pegasus]", "SpeedStream"), + T_DATA("SpeedTouch 120g 802.11g Wireless Adapter [Intersil ISL3886]", "SpeedTouch 120g"), + T_DATA("SpeedTouch 121g Wireless Dongle", "SpeedTouch 121g Dongle"), + T_DATA("Sphairon Homelink 1202 802.11n Wireless Adapter [Atheros AR9170]", + "Sphairon Homelink 1202"), + T_DATA("ST201 Sundance Ethernet", "ST201 Sundance"), + T_DATA("ST268", "ST268"), + T_DATA("SURECOM EP-320X-S 100/10M Ethernet PCI Adapter", "SURECOM EP-320X-S"), + T_DATA("T210 Protocol Engine", "T210 Protocol Engine"), + T_DATA("T302 1GbE Dual Port Adapter", "T302"), + T_DATA("T310 10GbE Single Port Adapter", "T310 Single Port"), + T_DATA("T320 10GbE Dual Port Adapter", "T320"), + T_DATA("T404-BT Unified Wire Ethernet Controller", "T404-BT"), + T_DATA("T404-BT Unified Wire Ethernet Controller [VF]", "T404-BT"), + T_DATA("T420-4082 Unified Wire Ethernet Controller", "T420-4082"), + T_DATA("T420-4082 Unified Wire Ethernet Controller [VF]", "T420-4082"), + T_DATA("T420-4085 SFP+ Unified Wire Ethernet Controller", "T420-4085 SFP+"), + T_DATA("T420-4085 SFP+ Unified Wire Ethernet Controller [VF]", "T420-4085 SFP+"), + T_DATA("T420-BCH Unified Wire Ethernet Controller", "T420-BCH"), + T_DATA("T420-BCH Unified Wire Ethernet Controller [VF]", "T420-BCH"), + T_DATA("T420-BT Unified Wire Ethernet Controller", "T420-BT"), + T_DATA("T420-BT Unified Wire Ethernet Controller [VF]", "T420-BT"), + T_DATA("T420-CR Unified Wire Ethernet Controller", "T420-CR"), + T_DATA("T420-CR Unified Wire Ethernet Controller [VF]", "T420-CR"), + T_DATA("T420-CX Unified Wire Ethernet Controller", "T420-CX"), + T_DATA("T420-CX Unified Wire Ethernet Controller [VF]", "T420-CX"), + T_DATA("T420-SO Unified Wire Ethernet Controller", "T420-SO"), + T_DATA("T420-SO Unified Wire Ethernet Controller [VF]", "T420-SO"), + T_DATA("T420X-4083 Unified Wire Ethernet Controller", "T420X-4083"), + T_DATA("T420X-4083 Unified Wire Ethernet Controller [VF]", "T420X-4083"), + T_DATA("T422-CR Unified Wire Ethernet Controller", "T422-CR"), + T_DATA("T422-CR Unified Wire Ethernet Controller [VF]", "T422-CR"), + T_DATA("T440-4084 Unified Wire Ethernet Controller", "T440-4084"), + T_DATA("T440-4084 Unified Wire Ethernet Controller [VF]", "T440-4084"), + T_DATA("T440-4086 10Gbase-T Unified Wire Ethernet Controller", "T440-4086"), + T_DATA("T440-4086 10Gbase-T Unified Wire Ethernet Controller [VF]", "T440-4086"), + T_DATA("T440-4088 Unified Wire Ethernet Controller", "T440-4088"), + T_DATA("T440-4088 Unified Wire Ethernet Controller [VF]", "T440-4088"), + T_DATA("T440-BCH Unified Wire Ethernet Controller", "T440-BCH"), + T_DATA("T440-BCH Unified Wire Ethernet Controller [VF]", "T440-BCH"), + T_DATA("T440-CH Unified Wire Ethernet Controller", "T440-CH"), + T_DATA("T440-CH Unified Wire Ethernet Controller [VF]", "T440-CH"), + T_DATA("T440-CR Unified Wire Ethernet Controller", "T440-CR"), + T_DATA("T440-CR Unified Wire Ethernet Controller [VF]", "T440-CR"), + T_DATA("T440F-4081 T440-FCoE Unified Wire Ethernet Controller", "T440F-4081 T440-FCoE"), + T_DATA("T440F-4081 T440-FCoE Unified Wire Ethernet Controller [VF]", + "T440F-4081 T440-FCoE"), + T_DATA("T440-LP-CR Unified Wire Ethernet Controller", "T440-LP-CR"), + T_DATA("T440-LP-CR Unified Wire Ethernet Controller [VF]", "T440-LP-CR"), + T_DATA("T440T-4087 Unified Wire Ethernet Controller", "T440T-4087"), + T_DATA("T440T-4087 Unified Wire Ethernet Controller [VF]", "T440T-4087"), + T_DATA("T480-4080 T480 Unified Wire Ethernet Controller", "T480-4080 T480"), + T_DATA("T480-4080 T480 Unified Wire Ethernet Controller [VF]", "T480-4080 T480"), + T_DATA("T480 Unified Wire Ethernet Controller", "T480"), + T_DATA("T480 Unified Wire Ethernet Controller [VF]", "T480"), + T_DATA("T502-BT Unified Wire Ethernet Controller", "T502-BT"), + T_DATA("T502-BT Unified Wire Ethernet Controller [VF]", "T502-BT"), + T_DATA("T504-5082 Unified Wire Ethernet Controller", "T504-5082"), + T_DATA("T504-5082 Unified Wire Ethernet Controller [VF]", "T504-5082"), + T_DATA("T504-BT Unified Wire Ethernet Controller", "T504-BT"), + T_DATA("T504-BT Unified Wire Ethernet Controller [VF]", "T504-BT"), + T_DATA("T520-5089 Unified Wire Ethernet Controller", "T520-5089"), + T_DATA("T520-5089 Unified Wire Ethernet Controller [VF]", "T520-5089"), + T_DATA("T520-5092 Unified Wire Ethernet Controller", "T520-5092"), + T_DATA("T520-5092 Unified Wire Ethernet Controller [VF]", "T520-5092"), + T_DATA("T520-5097 Unified Wire Ethernet Controller", "T520-5097"), + T_DATA("T520-5097 Unified Wire Ethernet Controller [VF]", "T520-5097"), + T_DATA("T520-509A Unified Wire Ethernet Controller", "T520-509A"), + T_DATA("T520-509A Unified Wire Ethernet Controller [VF]", "T520-509A"), + T_DATA("T520-509C Unified Wire Ethernet Controller", "T520-509C"), + T_DATA("T520-509C Unified Wire Ethernet Controller [VF]", "T520-509C"), + T_DATA("T520-509E Unified Wire Ethernet Controller", "T520-509E"), + T_DATA("T520-509E Unified Wire Ethernet Controller [VF]", "T520-509E"), + T_DATA("T520-BCH Unified Wire Ethernet Controller", "T520-BCH"), + T_DATA("T520-BCH Unified Wire Ethernet Controller [VF]", "T520-BCH"), + T_DATA("T520-BT Unified Wire Ethernet Controller", "T520-BT"), + T_DATA("T520-BT Unified Wire Ethernet Controller [VF]", "T520-BT"), + T_DATA("T520-CR Unified Wire Ethernet Controller", "T520-CR"), + T_DATA("T520-CR Unified Wire Ethernet Controller [VF]", "T520-CR"), + T_DATA("T520-CX Unified Wire Ethernet Controller", "T520-CX"), + T_DATA("T520-CX Unified Wire Ethernet Controller [VF]", "T520-CX"), + T_DATA("T520-LL-CR Unified Wire Ethernet Controller", "T520-LL-CR"), + T_DATA("T520-LL-CR Unified Wire Ethernet Controller [VF]", "T520-LL-CR"), + T_DATA("T520-OCP-SO Unified Wire Ethernet Controller", "T520-OCP-SO"), + T_DATA("T520-OCP-SO Unified Wire Ethernet Controller [VF]", "T520-OCP-SO"), + T_DATA("T520-SO Unified Wire Ethernet Controller", "T520-SO"), + T_DATA("T520-SO Unified Wire Ethernet Controller [VF]", "T520-SO"), + T_DATA("T522-5091 Unified Wire Ethernet Controller", "T522-5091"), + T_DATA("T522-5091 Unified Wire Ethernet Controller [VF]", "T522-5091"), + T_DATA("T522-CR Unified Wire Ethernet Controller", "T522-CR"), + T_DATA("T522-CR Unified Wire Ethernet Controller [VF]", "T522-CR"), + T_DATA("T540-5080 Unified Wire Ethernet Controller", "T540-5080"), + T_DATA("T540-5080 Unified Wire Ethernet Controller [VF]", "T540-5080"), + T_DATA("T540-5081 Unified Wire Ethernet Controller", "T540-5081"), + T_DATA("T540-5081 Unified Wire Ethernet Controller [VF]", "T540-5081"), + T_DATA("T540-5083 Unified Wire Ethernet Controller", "T540-5083"), + T_DATA("T540-5083 Unified Wire Ethernet Controller [VF]", "T540-5083"), + T_DATA("T540-5084 Unified Wire Ethernet Controller", "T540-5084"), + T_DATA("T540-5084 Unified Wire Ethernet Controller [VF]", "T540-5084"), + T_DATA("T540-5090 Unified Wire Ethernet Controller", "T540-5090"), + T_DATA("T540-5090 Unified Wire Ethernet Controller [VF]", "T540-5090"), + T_DATA("T540-5094 Unified Wire Ethernet Controller", "T540-5094"), + T_DATA("T540-5094 Unified Wire Ethernet Controller [VF]", "T540-5094"), + T_DATA("T540-5095 Unified Wire Ethernet Controller", "T540-5095"), + T_DATA("T540-5095 Unified Wire Ethernet Controller [VF]", "T540-5095"), + T_DATA("T540-509B Unified Wire Ethernet Controller", "T540-509B"), + T_DATA("T540-509B Unified Wire Ethernet Controller [VF]", "T540-509B"), + T_DATA("T540-509D Unified Wire Ethernet Controller", "T540-509D"), + T_DATA("T540-509D Unified Wire Ethernet Controller [VF]", "T540-509D"), + T_DATA("T540-509F Unified Wire Ethernet Controller", "T540-509F"), + T_DATA("T540-509F Unified Wire Ethernet Controller [VF]", "T540-509F"), + T_DATA("T540-50A0 Unified Wire Ethernet Controller", "T540-50A0"), + T_DATA("T540-50A0 Unified Wire Ethernet Controller [VF]", "T540-50A0"), + T_DATA("T540-50A1 Unified Wire Ethernet Controller", "T540-50A1"), + T_DATA("T540-50A1 Unified Wire Ethernet Controller [VF]", "T540-50A1"), + T_DATA("T540-BCH Unified Wire Ethernet Controller", "T540-BCH"), + T_DATA("T540-BCH Unified Wire Ethernet Controller [VF]", "T540-BCH"), + T_DATA("T540-BT Unified Wire Ethernet Controller", "T540-BT"), + T_DATA("T540-BT Unified Wire Ethernet Controller [VF]", "T540-BT"), + T_DATA("T540-CH Unified Wire Ethernet Controller", "T540-CH"), + T_DATA("T540-CH Unified Wire Ethernet Controller [VF]", "T540-CH"), + T_DATA("T540-CR Unified Wire Ethernet Controller", "T540-CR"), + T_DATA("T540-CR Unified Wire Ethernet Controller [VF]", "T540-CR"), + T_DATA("T540-LP-CR Unified Wire Ethernet Controller", "T540-LP-CR"), + T_DATA("T540-LP-CR Unified Wire Ethernet Controller [VF]", "T540-LP-CR"), + T_DATA("T560-CR Unified Wire Ethernet Controller", "T560-CR"), + T_DATA("T560-CR Unified Wire Ethernet Controller [VF]", "T560-CR"), + T_DATA("T570-5088 Unified Wire Ethernet Controller", "T570-5088"), + T_DATA("T570-5088 Unified Wire Ethernet Controller [VF]", "T570-5088"), + T_DATA("T580-5085 Unified Wire Ethernet Controller", "T580-5085"), + T_DATA("T580-5085 Unified Wire Ethernet Controller [VF]", "T580-5085"), + T_DATA("T580-5086 Unified Wire Ethernet Controller", "T580-5086"), + T_DATA("T580-5086 Unified Wire Ethernet Controller [VF]", "T580-5086"), + T_DATA("T580-5087 Unified Wire Ethernet Controller", "T580-5087"), + T_DATA("T580-5087 Unified Wire Ethernet Controller [VF]", "T580-5087"), + T_DATA("T580-5093 Unified Wire Ethernet Controller", "T580-5093"), + T_DATA("T580-5093 Unified Wire Ethernet Controller [VF]", "T580-5093"), + T_DATA("T580-5096 Unified Wire Ethernet Controller", "T580-5096"), + T_DATA("T580-5096 Unified Wire Ethernet Controller [VF]", "T580-5096"), + T_DATA("T580-5098 Unified Wire Ethernet Controller", "T580-5098"), + T_DATA("T580-5098 Unified Wire Ethernet Controller [VF]", "T580-5098"), + T_DATA("T580-5099 Unified Wire Ethernet Controller", "T580-5099"), + T_DATA("T580-5099 Unified Wire Ethernet Controller [VF]", "T580-5099"), + T_DATA("T580-50A2 Unified Wire Ethernet Controller", "T580-50A2"), + T_DATA("T580-50A2 Unified Wire Ethernet Controller [VF]", "T580-50A2"), + T_DATA("T580-CHR Unified Wire Ethernet Controller", "T580-CHR"), + T_DATA("T580-CHR Unified Wire Ethernet Controller [VF]", "T580-CHR"), + T_DATA("T580-CR Unified Wire Ethernet Controller", "T580-CR"), + T_DATA("T580-CR Unified Wire Ethernet Controller [VF]", "T580-CR"), + T_DATA("T580-LP-CR Unified Wire Ethernet Controller", "T580-LP-CR"), + T_DATA("T580-LP-CR Unified Wire Ethernet Controller [VF]", "T580-LP-CR"), + T_DATA("T580-OCP-SO Unified Wire Ethernet Controller", "T580-OCP-SO"), + T_DATA("T580-OCP-SO Unified Wire Ethernet Controller [VF]", "T580-OCP-SO"), + T_DATA("T580-SO-CR Unified Wire Ethernet Controller", "T580-SO-CR"), + T_DATA("T580-SO-CR Unified Wire Ethernet Controller [VF]", "T580-SO-CR"), + T_DATA("T61100-OCP-SO Unified Wire Ethernet Controller", "T61100-OCP-SO"), + T_DATA("T61100-OCP-SO Unified Wire Ethernet Controller [VF]", "T61100-OCP-SO"), + T_DATA("T6201-BT Unified Wire Ethernet Controller", "T6201-BT"), + T_DATA("T6201-BT Unified Wire Ethernet Controller [VF]", "T6201-BT"), + T_DATA("T62100-6081 Unified Wire Ethernet Controller", "T62100-6081"), + T_DATA("T62100-6081 Unified Wire Ethernet Controller [VF]", "T62100-6081"), + T_DATA("T62100-6083 Unified Wire Ethernet Controller", "T62100-6083"), + T_DATA("T62100-6083 Unified Wire Ethernet Controller [VF]", "T62100-6083"), + T_DATA("T62100-CR Unified Wire Ethernet Controller", "T62100-CR"), + T_DATA("T62100-CR Unified Wire Ethernet Controller [VF]", "T62100-CR"), + T_DATA("T62100-LP-CR Unified Wire Ethernet Controller", "T62100-LP-CR"), + T_DATA("T62100-LP-CR Unified Wire Ethernet Controller [VF]", "T62100-LP-CR"), + T_DATA("T62100-OCP-SO Unified Wire Ethernet Controller", "T62100-OCP-SO"), + T_DATA("T62100-OCP-SO Unified Wire Ethernet Controller [VF]", "T62100-OCP-SO"), + T_DATA("T62100-SO-CR Unified Wire Ethernet Controller", "T62100-SO-CR"), + T_DATA("T62100-SO-CR Unified Wire Ethernet Controller [VF]", "T62100-SO-CR"), + T_DATA("T6210-BT Unified Wire Ethernet Controller", "T6210-BT"), + T_DATA("T6210-BT Unified Wire Ethernet Controller [VF]", "T6210-BT"), + T_DATA("T6225-6080 Unified Wire Ethernet Controller", "T6225-6080"), + T_DATA("T6225-6080 Unified Wire Ethernet Controller [VF]", "T6225-6080"), + T_DATA("T6225-6082 Unified Wire Ethernet Controller", "T6225-6082"), + T_DATA("T6225-6082 Unified Wire Ethernet Controller [VF]", "T6225-6082"), + T_DATA("T6225-CR Unified Wire Ethernet Controller", "T6225-CR"), + T_DATA("T6225-CR Unified Wire Ethernet Controller [VF]", "T6225-CR"), + T_DATA("T6225-LL-CR Unified Wire Ethernet Controller", "T6225-LL-CR"), + T_DATA("T6225-LL-CR Unified Wire Ethernet Controller [VF]", "T6225-LL-CR"), + T_DATA("T6225-OCP-SO Unified Wire Ethernet Controller", "T6225-OCP-SO"), + T_DATA("T6225-OCP-SO Unified Wire Ethernet Controller [VF]", "T6225-OCP-SO"), + T_DATA("T6225-SO-CR Unified Wire Ethernet Controller", "T6225-SO-CR"), + T_DATA("T6225-SO-CR Unified Wire Ethernet Controller [VF]", "T6225-SO-CR"), + T_DATA("T64100-6084 Unified Wire Ethernet Controller", "T64100-6084"), + T_DATA("T64100-6084 Unified Wire Ethernet Controller [VF]", "T64100-6084"), + T_DATA("T6425-CR Unified Wire Ethernet Controller", "T6425-CR"), + T_DATA("T6425-CR Unified Wire Ethernet Controller [VF]", "T6425-CR"), + T_DATA("T6425-SO-CR Unified Wire Ethernet Controller", "T6425-SO-CR"), + T_DATA("T6425-SO-CR Unified Wire Ethernet Controller [VF]", "T6425-SO-CR"), + T_DATA("TalkTalk SNU5630NS/05 802.11bg", "TalkTalk SNU5630NS/05"), + T_DATA("TC902x Gigabit Ethernet", "TC902x"), + T_DATA("T-Com Sinus 154 data II [Intersil ISL3887]", "T-Com Sinus 154 data II"), + T_DATA("TEW-429UB 802.11bg", "TEW-429UB"), + T_DATA("TEW-429UB C1 802.11bg", "TEW-429UB C1"), + T_DATA("TEW-444UB EU (no firmware)", "TEW-444UB EU"), + T_DATA("TEW-444UB EU [TRENDnet]", "TEW-444UB EU"), + T_DATA("TEW-509UB A1 802.11abg Wireless Adapter [ZyDAS ZD1211]", "TEW-509UB A1"), + T_DATA("TEW-645UB 802.11bgn 1x2:2 Wireless Adapter [Ralink RT2770]", "TEW-645UB"), + T_DATA("TEW-648UBM 802.11n 150Mbps Micro Wireless N Adapter [Realtek RTL8188CUS]", + "TEW-648UBM"), + T_DATA("TG54USB 802.11bg", "TG54USB"), + T_DATA("Thomson TG121N [Atheros AR9001U-(2)NG]", "Thomson TG121N"), + T_DATA("Top Global Gobi 2000 Wireless Modem", "Top Global Gobi 2000"), + T_DATA("TP-Link TL-WN322G v3 / TL-WN422G v2 802.11g [Atheros AR9271]", + "TP-Link TL-WN322G / TL-WN422G"), + T_DATA("TP-Link TL-WN821N v2 / TL-WN822N v1 802.11n [Atheros AR9170]", + "TP-Link TL-WN821N / TL-WN822N"), + T_DATA("TP-Link TL-WN821N v3 / TL-WN822N v2 802.11n [Atheros AR7010+AR9287]", + "TP-Link TL-WN821N / TL-WN822N"), + T_DATA("TrueMobile 1300 802.11g Wireless Adapter [Intersil ISL3880]", "TrueMobile 1300"), + T_DATA("T-Sinus 154data", "T-Sinus 154data"), + T_DATA("TTP-Monitoring Card V2.0", "TTP-Monitoring"), + T_DATA("Turbolink UB801RE Wireless 802.11g 54Mbps Network Adapter [RTL8187]", + "Turbolink UB801RE"), + T_DATA("Turbolink UB801R WLAN Adapter", "Turbolink UB801R"), + T_DATA("U2E", "U2E"), + T_DATA("U5 802.11g Adapter", "U5"), + T_DATA("UB81 802.11bgn", "UB81"), + T_DATA("UB82 802.11abgn", "UB82"), + T_DATA("Ubiquiti WiFiStation 802.11n [Atheros AR9271]", "Ubiquiti WiFiStation"), + T_DATA("Ubiquiti WiFiStationEXT 802.11n [Atheros AR9271]", "Ubiquiti WiFiStationEXT"), + T_DATA("UBS-10BT Ethernet [klsi]", "UBS-10BT"), + T_DATA("UBS-10BT Ethernet", "UBS-10BT"), + T_DATA("UC-110T 100Mbps Ethernet [pegasus]", "UC-110T"), + T_DATA("UC-210T Ethernet", "UC-210T"), + T_DATA("UF100 Ethernet [pegasus2]", "UF100"), + T_DATA("UF200 Ethernet", "UF200"), + T_DATA("ULi 1689,1573 integrated ethernet.", "ULi 1689 1573"), + T_DATA("Ultimate N WiFi Link 5300", "Ultimate N 5300"), + T_DATA("un2400 Gobi Wireless Modem", "un2400 Gobi"), + T_DATA("UniNorth 2 GMAC (Sun GEM)", "UniNorth 2 GMAC"), + T_DATA("UniNorth GMAC (Sun GEM)", "UniNorth GMAC"), + T_DATA("UniNorth/Pangea GMAC (Sun GEM)", "UniNorth/Pangea GMAC"), + T_DATA("UR054g 802.11g Wireless Adapter [Intersil ISL3887]", "UR054g"), + T_DATA("UR055G 802.11bg", "UR055G"), + T_DATA("USB1000 Gigabit Notebook Adapter", "USB1000"), + T_DATA("USB-100N Ethernet [pegasus]", "USB-100N"), + T_DATA("USB100TX Ethernet [pegasus]", "USB100TX"), + T_DATA("USB100TX HomePNA Ethernet [pegasus]", "USB100TX HomePNA"), + T_DATA("USB10TX Ethernet [pegasus]", "USB10TX"), + T_DATA("USB10TX", "USB10TX"), + T_DATA("USB 1.1 10/100M Fast Ethernet Adapter", NULL), + T_DATA("USB200M 100baseTX Adapter", "USB200M 100baseTX"), + T_DATA("USB200M 10/100 Ethernet Adapter", "USB200M"), + T_DATA("USB 2.0 Ethernet", NULL), + T_DATA("USB2AR Ethernet", "USB2AR"), + T_DATA("USBcan II", "USBcan II"), + T_DATA("USBE-100 Ethernet [pegasus2]", "USBE-100"), + T_DATA("USBEL-100 Ethernet [pegasus]", "USBEL-100"), + T_DATA("USB Ethernet [pegasus]", "pegasus"), + T_DATA("USB ETT", "ETT"), + T_DATA("USBLAN", "USBLAN"), + T_DATA("USBLP-100 HomePNA Ethernet [pegasus]", "USBLP-100 HomePNA"), + T_DATA("USB-N10 v2 802.11b/g/n Wireless Adapter [MediaTek MT7601U]", "USB-N10"), + T_DATA("USB-N11 802.11n Network Adapter [Ralink RT2870]", "USB-N11"), + T_DATA("USB-N13 802.11n Network Adapter (rev. A1) [Ralink RT3072]", "USB-N13"), + T_DATA("USB-N13 802.11n Network Adapter (rev. B1) [Realtek RTL8192CU]", "USB-N13"), + T_DATA("USB-N14 802.11b/g/n (2x2) Wireless Adapter [Ralink RT5372]", "USB-N14"), + T_DATA("USB-N53 802.11abgn Network Adapter [Ralink RT3572]", "USB-N53"), + T_DATA("USB TO Ethernet", NULL), + T_DATA("USR5420 802.11g Adapter [Broadcom 4320 USB]", "USR5420"), + T_DATA("USR5423 802.11bg Wireless Adapter [ZyDAS ZD1211B]", "USR5423"), + T_DATA("USR997902 10/100/1000 Mbps PCI Network Card", "USR997902 Mbps"), + T_DATA("VIC Ethernet NIC Dynamic", "VIC Dynamic"), + T_DATA("VIC Ethernet NIC", "VIC"), + T_DATA("VIC SR-IOV VF", "VIC SR-IOV VF"), + T_DATA("Vigor530 IEEE 802.11G Adapter (ISL3880+NET2280)", "Vigor530"), + T_DATA("Virtual media for 802.11bg", NULL), + T_DATA("VMXNET3 Ethernet Controller", "VMXNET3"), + T_DATA("VT6102/VT6103 [Rhine-II]", "VT6102/VT6103"), + T_DATA("VT6105M [Rhine-III]", "VT6105M"), + T_DATA("VT6105/VT6106S [Rhine-III]", "VT6105/VT6106S"), + T_DATA("VT6120/VT6121/VT6122 Gigabit Ethernet Adapter", "VT6120/VT6121/VT6122"), + T_DATA("VT82C926 [Amazon]", "VT82C926"), + T_DATA("VT86C100A [Rhine]", "VT86C100A"), + T_DATA("W89C840", "W89C840"), + T_DATA("W89C940F", "W89C940F"), + T_DATA("W89C940 misprogrammed [ne2k]", "W89C940"), + T_DATA("W89C940", "W89C940"), + T_DATA("WG111T (no firmware)", "WG111T"), + T_DATA("WG111T", "WG111T"), + T_DATA("WG111U Double 108 Mbps Wireless [Atheros AR5004X / AR5005UX]", "WG111U"), + T_DATA("WG111U (no firmware) Double 108 Mbps Wireless [Atheros AR5004X / AR5005UX]", + "WG111U"), + T_DATA("WG111(v1) 54 Mbps Wireless [Intersil ISL3886]", "WG111"), + T_DATA("WG111(v1) rev 2 54 Mbps Wireless [Intersil ISL3887]", "WG111"), + T_DATA("WG111v2 54 Mbps Wireless [RealTek RTL8187L]", "WG111v2"), + T_DATA("WG111v3 54 Mbps Wireless [realtek RTL8187B]", "WG111v3"), + T_DATA("WG121(v1) 54 Mbps Wireless [Intersil ISL3886]", "WG121"), + T_DATA("WG121(v2) 54 Mbps Wireless [Intersil ISL3886]", "WG121"), + T_DATA("WGU-210 802.11g Adapter [Intersil ISL3886]", "WGU-210"), + T_DATA("WHG-AGDN/US Wireless LAN Adapter", "WHG-AGDN/US"), + T_DATA("Wi-Fi 11g adapter", NULL), + T_DATA("WiFi Link 5100", "5100"), + T_DATA("Wil6200 802.11ad Wireless Network Adapter", "Wil6200"), + T_DATA("WiMAX/WiFi Link 5150", "5150"), + T_DATA("Wireless 11n USB Adapter", "11n"), + T_DATA("Wireless 1450 Dual-band (802.11a/b/g) Adapter [Intersil ISL3887]", "1450"), + T_DATA("Wireless 3160", "3160"), + T_DATA("Wireless 3165", "3165"), + T_DATA("Wireless 7260", "7260"), + T_DATA("Wireless 7265", "7265"), + T_DATA("Wireless 802.11g 54Mbps Network Adapter [RTL8187]", "RTL8187"), + T_DATA("Wireless 8260", "8260"), + T_DATA("Wireless 8265 / 8275", "8265 / 8275"), + T_DATA("Wireless Adapter 11g", NULL), + T_DATA("Wireless LAN USB Mini-Card", NULL), + T_DATA("Wireless MAXg Adapter [Broadcom 4320]", "MAXg"), + T_DATA("Wireless Network Adapter", NULL), + T_DATA("Wireless-N Network Adapter [Ralink RT2870]", "Ralink RT2870"), + T_DATA("Wireless PCI Adapter RT2400 / RT2460", "RT2400 / RT2460"), + T_DATA("WIS09ABGN LinkStick Wireless LAN Adapter", "WIS09ABGN LinkStick"), + T_DATA("WL-113 rev 1 Wireless Network USB Adapter", "WL-113"), + T_DATA("WL-113 rev 2 Wireless Network USB Adapter", "WL-113"), + T_DATA("WL-117 Hi-Speed USB Adapter", "WL-117"), + T_DATA("WL1271", "WL1271"), + T_DATA("WL-159g 802.11bg [ZyDAS ZD1211B+AL2230]", "WL-159g"), + T_DATA("WL-167G v1 802.11g Adapter [Ralink RT2571]", "WL-167G"), + T_DATA("WL-167G v2 802.11g Adapter [Ralink RT2571W]", "WL-167G"), + T_DATA("WL-168 Wireless Network Adapter 54g", "WL-168"), + T_DATA("WL169gE 802.11g Adapter [Broadcom 4320 USB]", "WL169gE"), + T_DATA("WL-172 Wireless Network USB Adapter 54g Turbo", "WL-172 Turbo"), + T_DATA("WL-182 Wireless-N Network USB Card", "WL-182"), + T_DATA("WL-188 Wireless Network 300N USB Adapter", "WL-188 300N"), + T_DATA("WL-301 Wireless Network 300N USB Adapter", "WL-301 300N"), + T_DATA("WL-302 Wireless Network 300N USB dongle", "WL-302 300N"), + T_DATA("WL-315 Wireless-N USB Adapter", "WL-315"), + T_DATA("WL-321 Wireless USB Gaming Adapter 300N", "WL-321 Gaming 300N"), + T_DATA("WL-323 Wireless-N USB Adapter", "WL-323"), + T_DATA("WL-324 Wireless USB Adapter 300N", "WL-324 300N"), + T_DATA("WL-329 Wireless Dualband USB adapter 300N", "WL-329 Dualband 300N"), + T_DATA("WL-343 Wireless USB Adapter 150N X1", "WL-343 150N X1"), + T_DATA("WL-344 Wireless Adapter 300N X2 [Ralink RT3071]", "WL-344 300N X2"), + T_DATA("WL-345 Wireless USB adapter 300N X3", "WL-345 300N X3"), + T_DATA("WL-349v1 Wireless Adapter 150N 002 [Ralink RT3070]", "WL-349v1 150N 002"), + T_DATA("WL-349v4 Wireless Micro Adapter 150N X1 [Ralink RT3370]", "WL-349v4 150N X1"), + T_DATA("WL-352v1 Wireless USB Adapter 300N 002", "WL-352v1 300N 002"), + T_DATA("WL-358v1 Wireless Micro USB Adapter 300N X3 002", "WL-358v1 300N X3 002"), + T_DATA("WL-430U 802.11bg", "WL-430U"), + T_DATA("WL532U 802.11g Adapter", "WL532U"), + T_DATA("WL-603 Wireless Adapter", "WL-603"), + T_DATA("WL-608 Wireless USB Adapter 54g", "WL-608"), + T_DATA("WLA3310 Wireless Adapter [Intersil ISL3887]", "WLA3310"), + T_DATA("WLA-4000 802.11bgn [Ralink RT3072]", "WLA-4000"), + T_DATA("WLA-5000 802.11abgn [Ralink RT3572]", "WLA-5000"), + T_DATA("WLA-5100", "WLA-5100"), + T_DATA("WLI2-USB2-G54 Wireless LAN Adapter", "WLI2-USB2-G54"), + T_DATA("WLI-U2-G54HP", "WLI-U2-G54HP"), + T_DATA("WLI-U2-KG125S 802.11g Adapter [Broadcom 4320 USB]", "WLI-U2-KG125S"), + T_DATA("WLI-U2-KG54-AI WLAN", "WLI-U2-KG54-AI"), + T_DATA("WLI-U2-KG54-BB", "WLI-U2-KG54-BB"), + T_DATA("WLI-U2-KG54L 802.11bg [ZyDAS ZD1211B]", "WLI-U2-KG54L"), + T_DATA("WLI-U2-KG54 WLAN", "WLI-U2-KG54"), + T_DATA("WLI-U2-KG54-YB WLAN", "WLI-U2-KG54-YB"), + T_DATA("WLI-U2-SG54HP", "WLI-U2-SG54HP"), + T_DATA("WLI-UC-AG300N Wireless LAN Adapter", "WLI-UC-AG300N"), + T_DATA("WLI-UC-G300HP Wireless LAN Adapter", "WLI-UC-G300HP"), + T_DATA("WLI-UC-G300N Wireless LAN Adapter [Ralink RT2870]", "WLI-UC-G300N"), + T_DATA("WLI-UC-G301N Wireless LAN Adapter [Ralink RT3072]", "WLI-UC-G301N"), + T_DATA("WLI-UC-G450 Wireless LAN Adapter", "WLI-UC-G450"), + T_DATA("WLI-UC-GNHP Wireless LAN Adapter", "WLI-UC-GNHP"), + T_DATA("WLI-UC-GNM2 Wireless LAN Adapter [Ralink RT3070]", "WLI-UC-GNM2"), + T_DATA("WLI-UC-GNM Wireless LAN Adapter [Ralink RT8070]", "WLI-UC-GNM"), + T_DATA("WLI-UC-GN Wireless LAN Adapter [Ralink RT3070]", "WLI-UC-GN"), + T_DATA("WLI-USB-G54 802.11g Adapter [Broadcom 4320 USB]", "WLI-USB-G54"), + T_DATA("WLM-10U1 802.11abgn Wireless Adapter [Ralink RT3572]", "WLM-10U1"), + T_DATA("WLM-20U2/GN-1080 802.11abgn Wireless Adapter [Atheros AR7010+AR9280]", + "WLM-20U2/GN-1080"), + T_DATA("WLP-UC-AG300 Wireless LAN Adapter", "WLP-UC-AG300"), + T_DATA("WM168g 802.11bg Wireless Adapter [Intersil ISL3886]", "WM168g"), + T_DATA("WN111(v2) RangeMax Next Wireless [Atheros AR9170+AR9101]", "WN111 RangeMax Next"), + T_DATA("WNA1000M 802.11bgn [Realtek RTL8188CUS]", "WNA1000M"), + T_DATA("WNA1000Mv2 802.11bgn [Realtek RTL8188CUS?]", "WNA1000Mv2"), + T_DATA("WNA1000 Wireless-N 150 [Atheros AR9170+AR9101]", "WNA1000 150"), + T_DATA("WNA1100 Wireless-N 150 [Atheros AR9271]", "WNA1100 150"), + T_DATA("WNA3100M(v1) Wireless-N 300 [Realtek RTL8192CU]", "WNA3100M 300"), + T_DATA("WNDA3100v1 802.11abgn [Atheros AR9170+AR9104]", "WNDA3100v1"), + T_DATA("WNDA3200 802.11abgn Wireless Adapter [Atheros AR7010+AR9280]", "WNDA3200"), + T_DATA("WNDA4100 802.11abgn 3x3:3 [Ralink RT3573]", "WNDA4100"), + T_DATA("WN-G150U Wireless LAN Adapter", "WN-G150U"), + T_DATA("WN-G300U Wireless LAN Adapter", "WN-G300U"), + T_DATA("WNGDNUS2 802.11n", "WNGDNUS2"), + T_DATA("WN-GDN/US3 Wireless LAN Adapter", "WN-GDN/US3"), + T_DATA("WPN111 802.11g Wireless Adapter [Atheros AR5523]", "WPN111"), + T_DATA("WPN111 (no firmware)", "WPN111"), + T_DATA("WPN111 RangeMax(TM) Wireless USB 2.0 Adapter", "WPN111 RangeMax"), + T_DATA("WUA-1340", "WUA-1340"), + T_DATA("WUA-2340 RangeBooster G Adapter(rev.A) [Atheros AR5523]", "WUA-2340 RangeBooster"), + T_DATA("WUA-2340 RangeBooster G Adapter(rev.A) (no firmware) [Atheros AR5523]", + "WUA-2340 RangeBooster"), + T_DATA("WUA-2340 RangeBooster G Adapter(rev.B) [Ralink RT2070]", "WUA-2340 RangeBooster"), + T_DATA("WUBR-177G [Ralink RT2571W]", "WUBR-177G"), + T_DATA("WUBR-208N 802.11abgn Wireless Adapter [Ralink RT2870]", "WUBR-208N"), + T_DATA("WUG2690 802.11bg Wireless Module [ZyDAS ZD1211+AL2230]", "WUG2690"), + T_DATA("WUG2700", "WUG2700"), + T_DATA("WUS-201 802.11bg", "WUS-201"), + T_DATA("WUSB100 v1 RangePlus Wireless Network Adapter [Ralink RT2870]", + "WUSB100 RangePlus"), + T_DATA("WUSB100 v2 RangePlus Wireless Network Adapter [Ralink RT3070]", + "WUSB100 RangePlus"), + T_DATA("WUSB200 802.11g Adapter [Ralink RT2671]", "WUSB200"), + T_DATA("WUSB54AG 802.11a/g Adapter [Intersil ISL3887]", "WUSB54AG"), + T_DATA("WUSB54GC v1 802.11g Adapter [Ralink RT73]", "WUSB54GC"), + T_DATA("WUSB54GC v2 802.11g Adapter [Realtek RTL8187B]", "WUSB54GC"), + T_DATA("WUSB54GC v3 802.11g Adapter [Ralink RT2070L]", "WUSB54GC"), + T_DATA("WUSB54GP v1 802.11g Adapter [Intersil ISL3886]", "WUSB54GP"), + T_DATA("WUSB54GP v4.0 802.11g Adapter [Ralink RT2500USB]", "WUSB54GP v4.0"), + T_DATA("WUSB54GR", "WUSB54GR"), + T_DATA("WUSB54GSC v1 802.11g Adapter [Broadcom 4320 USB]", "WUSB54GSC"), + T_DATA("WUSB54GS v1 802.11g Adapter [Broadcom 4320 USB]", "WUSB54GS"), + T_DATA("WUSB54GS v2 802.11g Adapter [Broadcom 4320 USB]", "WUSB54GS"), + T_DATA("WUSB54G v1 802.11g Adapter [Intersil ISL3886]", "WUSB54G"), + T_DATA("WUSB54G v2 802.11g Adapter [Intersil ISL3887]", "WUSB54G"), + T_DATA("WUSB54G v4 802.11g Adapter [Ralink RT2500USB]", "WUSB54G"), + T_DATA("WUSB600N v1 Dual-Band Wireless-N Network Adapter [Ralink RT2870]", + "WUSB600N Dual-Band"), + T_DATA("WUSB600N v2 Dual-Band Wireless-N Network Adapter [Ralink RT3572]", + "WUSB600N Dual-Band"), + T_DATA("WUSBF54G 802.11bg", "WUSBF54G"), + T_DATA("WUSBF54G v1.1 802.11bg", "WUSBF54G"), + T_DATA("X3100 Series 10 Gigabit Ethernet PCIe", "X3100"), + T_DATA("X540 Ethernet Controller Virtual Function", "X540 Virtual Function"), + T_DATA("X540 Virtual Function", "X540 Virtual Function"), + T_DATA("X550 Virtual Function", "X550 Virtual Function"), + T_DATA("X552 Virtual Function", "X552 Virtual Function"), + T_DATA("X553 Virtual Function", "X553 Virtual Function"), + T_DATA("X722 Virtual Function", "X722 Virtual Function"), + T_DATA("Xframe 10-Gigabit Ethernet PCI-X", "Xframe"), + T_DATA("Xframe II 10-Gigabit Ethernet PCI-X 2.0", "Xframe II 2.0"), + T_DATA("XG-300 802.11b Adapter", "XG-300"), + T_DATA("XG-703A 802.11g Wireless Adapter [Intersil ISL3887]", "XG-703A"), + T_DATA("XG-705A 802.11g Wireless Adapter [Intersil ISL3887]", "XG-705A"), + T_DATA("XG-760A 802.11bg", "XG-760A"), + T_DATA("XG-76NA 802.11bg", "XG-76NA"), + T_DATA("XG Mgmt", "XG Mgmt"), + T_DATA("Xircom PGUNET USB-USB Bridge", "Xircom PGUNET USB-USB Bridge"), + T_DATA("XL710/X710 Virtual Function", "XL710/X710 Virtual Function"), + T_DATA("XX1", "XX1"), + T_DATA("XX2", "XX2"), + T_DATA("XX4", "XX4"), + T_DATA("XX5", "XX5"), + T_DATA("XX6", "XX6"), + T_DATA("XX7", "XX7"), + T_DATA("XX9", "XX9"), + T_DATA("Yellowfin G-NIC gigabit ethernet", "Yellowfin"), + T_DATA("YP3X00 PDA", "YP3X00"), + T_DATA("Yukon Optima 88E8059 [PCIe Gigabit Ethernet Controller with AVB]", + "Yukon Optima 88E8059"), + T_DATA("Zaurus A-300", "Zaurus A-300"), + T_DATA("Zaurus C-700 PDA", "Zaurus C-700"), + T_DATA("Zaurus C-750/C-760/C-860/SL-C3000 PDA", "Zaurus C-750/C-760/C-860/SL-C3000"), + T_DATA("Zaurus C-860 PDA", "Zaurus C-860"), + T_DATA("Zaurus SL-5000D/SL-5500 PDA", "Zaurus SL-5000D/SL-5500"), + T_DATA("Zaurus SL-6000", "Zaurus SL-6000"), + T_DATA("Zaurus SL-B500/SL-5600 PDA", "Zaurus SL-B500/SL-5600"), + T_DATA("ZD1211 802.11b/g Wireless Adapter", "ZD1211"), + T_DATA("ZD1211 802.11g", "ZD1211"), + T_DATA("ZD1211B 802.11g", "ZD1211B"), + T_DATA("ZD1211B", "ZD1211B"), + T_DATA("ZD1221 802.11n", "ZD1221"), + T_DATA("Zoom 4410 Wireless-G [Intersil ISL3887]", "Zoom 4410"), + T_DATA("ZT6688 Fast Ethernet Adapter", "ZT6688"), + T_DATA("ZyAIR AG-225H v2 802.11bg", "ZyAIR AG-225H"), + T_DATA("ZyAIR G-202 802.11bg", "ZyAIR G-202"), + T_DATA("ZyAIR G-220 802.11bg", "ZyAIR G-220"), + T_DATA("ZyAIR G-220F 802.11bg", "ZyAIR G-220F"), + }; + + _test_fixup_string(data, G_N_ELEMENTS(data), nm_utils_fixup_product_string); +} + +/*****************************************************************************/ + +static int +_memfd_create(const char *name) +{ +#if defined(HAVE_DECL_MEMFD_CREATE) && HAVE_DECL_MEMFD_CREATE + return memfd_create(name, MFD_CLOEXEC); +#endif + return -1; +} + +typedef struct { + const char *key; + const char *val; +} ReadVpnDetailData; + +#define READ_VPN_DETAIL_DATA(...) ((ReadVpnDetailData[]){__VA_ARGS__}) + +static gboolean +_do_read_vpn_details_impl1(const char * file, + int line, + int memfd, + char * mem, + gsize len, + const ReadVpnDetailData *expected_data, + guint expected_data_len, + const ReadVpnDetailData *expected_secrets, + guint expected_secrets_len) +{ + gssize written; + off_t lseeked; + gs_unref_hashtable GHashTable *data = NULL; + gs_unref_hashtable GHashTable *secrets = NULL; + + written = write(memfd, mem, len); + g_assert_cmpint(written, ==, (gssize) len); + + lseeked = lseek(memfd, 0, SEEK_SET); + g_assert_cmpint(lseeked, ==, 0); + + if (!nm_vpn_service_plugin_read_vpn_details(memfd, &data, &secrets)) { + g_assert(!data); + g_assert(!secrets); + g_assert_cmpint(expected_data_len, ==, 0); + g_assert_cmpint(expected_secrets_len, ==, 0); + return TRUE; + } + +#define _assert_hash(hash, expected, expected_len) \ + G_STMT_START \ + { \ + GHashTable * _hash = (hash); \ + guint _expected_len = (expected_len); \ + const ReadVpnDetailData *_expected = (expected); \ + GHashTableIter _iter; \ + const char * _k, *_v; \ + guint _i; \ + \ + g_assert(_hash); \ + \ + g_hash_table_iter_init(&_iter, _hash); \ + while (g_hash_table_iter_next(&_iter, (gpointer *) &_k, (gpointer *) &_v)) { \ + for (_i = 0; _i < _expected_len; _i++) { \ + if (nm_streq(_expected[_i].key, _k)) \ + break; \ + } \ + 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++) { \ + const ReadVpnDetailData *_d = &_expected[_i]; \ + \ + g_assert(_d->key); \ + g_assert(_d->val); \ + _v = g_hash_table_lookup(_hash, _d->key); \ + 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, \ + line, \ + G_STRINGIFY(hash), \ + _d->key, \ + NM_PRINT_FMT_QUOTE_STRING(_v), \ + _d->val); \ + } \ + \ + g_assert_cmpint(g_hash_table_size(_hash), ==, _expected_len); \ + } \ + G_STMT_END + + _assert_hash(data, expected_data, expected_data_len); + _assert_hash(secrets, expected_secrets, expected_secrets_len); + +#undef _assert_hash + return TRUE; +} + +#define _do_read_vpn_details_impl0(str, \ + expected_data, \ + expected_data_len, \ + expected_secrets, \ + expected_secrets_len, \ + pre_setup_cmd) \ + G_STMT_START \ + { \ + nm_auto_close int _memfd = _memfd_create("libnm-test-read-vpn-details"); \ + \ + if (_memfd < 0) \ + g_test_skip("cannot create memfd"); \ + else { \ + { \ + pre_setup_cmd; \ + } \ + _do_read_vpn_details_impl1(__FILE__, \ + __LINE__, \ + _memfd, \ + "" str "", \ + NM_STRLEN(str), \ + expected_data, \ + expected_data_len, \ + expected_secrets, \ + expected_secrets_len); \ + } \ + } \ + 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(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), \ + pre_setup_cmd) + +static void +test_nm_vpn_service_plugin_read_vpn_details(void) +{ + _do_read_vpn_details_empty(""); + _do_read_vpn_details_empty("hallo"); + _do_read_vpn_details_empty("DONE"); + _do_read_vpn_details_empty("DONE\n"); + _do_read_vpn_details_empty("DONE\0"); + _do_read_vpn_details_empty("\0DONE\0"); + + _do_read_vpn_details("" + "DATA_KEY=some-key\n" + "DATA_VAL=string\n" + "\n" + "DATA_KEY=some-other-key\n" + "DATA_VAL=val2\n" + "\n" + "SECRET_KEY=some-secret\n" + "SECRET_VAL=val3\n" + "\n" + "DONE\n" + "\n" + "", + READ_VPN_DETAIL_DATA({"some-key", "string"}, {"some-other-key", "val2"}, ), + READ_VPN_DETAIL_DATA({"some-secret", "val3"}, ), ); + + _do_read_vpn_details("" + "DATA_KEY=some-key\n" + "DATA_VAL=string\n" + "DONE\n", + READ_VPN_DETAIL_DATA({"some-key", "string"}, ), + READ_VPN_DETAIL_DATA(), ); + + _do_read_vpn_details( + "" + "DATA_KEY=some-key\n" + "DATA_VAL=string\n" + "=continued after a line break\n" + "SECRET_KEY=key names\n" + "=can have\n" + "=continuations too\n" + "bogus1=\n" + "SECRET_VAL=value\n" + "bogus=value\n" + "bogus=\n" + "DATA_VAL=x\n" + "DATA_KEY=\n" + "DATA_VAL=\n" + "DATA_VAL=y\n" + "DATA_KEY=y\n" + "DATA_KEY=y\n" + "DATA_KEY=z\n" + "SECRET_KEY=s1\n" + "DATA_VAL=z\n" + "SECRET_VAL=S1\n" + "\n" + "DONE\n" + "", + READ_VPN_DETAIL_DATA({"some-key", "string\ncontinued after a line break"}, ), + READ_VPN_DETAIL_DATA({"key names\ncan have\ncontinuations too", "value"}, ), ); + + _do_read_vpn_details( + "" + "DATA_KEY=some-key\n" + "DATA_VAL=string\n" + "=continued after a line break\n" + "SECRET_KEY=key names\n" + "=can have\n" + "=continuations too\n" + "SECRET_VAL=value\n" + "", + READ_VPN_DETAIL_DATA({"some-key", "string\ncontinued after a line break"}, ), + READ_VPN_DETAIL_DATA({"key names\ncan have\ncontinuations too", "value"}, ), ); + + _do_read_vpn_details("" + "DATA_KEY=some-key\n" + "DATA_VAL=string\n" + "\n" + "DATA_KEY=some\n" + "=key-2\n" + "DATA_VAL=val2\n" + "\n" + "DATA_KEY=key3\n" + "=key-2\n" + "DATA_VAL=val3\n" + "\n" + "SECRET_KEY=some-secret\n" + "SECRET_VAL=val3\n" + "\n" + "SECRET_KEY=\n" + "SECRET_VAL=val3\n" + "\n" + "SECRET_KEY=keyx\n" + "SECRET_VAL=\n" + "\n" + "SECRET_KEY=ke\xc0yx\n" + "SECRET_VAL=inval\n" + "\n" + "SECRET_KEY=key-inval\n" + "SECRET_VAL=in\xc1val\n" + "\n" + "DONE\n" + "\n" + "", + READ_VPN_DETAIL_DATA({"some\nkey-2", "val2"}, + {"some-key", "string"}, + {"key3\nkey-2", "val3"}, ), + READ_VPN_DETAIL_DATA({"some-secret", "val3"}, + {"", "val3"}, + {"keyx", ""}, + {"ke\xc0yx", "inval"}, + {"key-inval", "in\xc1val"}, ), ); + + _do_read_vpn_details("" + "DATA_KEY=some-key\n" + "DATA_VAL=string\n" + "\n" + "DATA_KEY=some\n" + "=key-2\n" + "DATA_VAL=val2\n" + "\n" + "DATA_KEY=key3\0" + "=key-2\n" + "DATA_VAL=val3\n" + "\n" + "SECRET_KEY=some-secret\n" + "SECRET_VAL=val3\n" + "\n" + "SECRET_KEY=\n" + "SECRET_VAL=val3\n" + "\n" + "SECRET_KEY=keyx\n" + "SECRET_VAL=\n" + "\n" + "SECRET_KEY=ke\xc0yx\n" + "SECRET_VAL=inval\n" + "\n" + "SECRET_KEY=key-inval\n" + "SECRET_VAL=in\xc1val\n" + "\n" + "DONE\n" + "\n" + "", + READ_VPN_DETAIL_DATA({"some\nkey-2", "val2"}, {"some-key", "string"}, ), + READ_VPN_DETAIL_DATA(), ); +} + +/*****************************************************************************/ + +static void +test_types(void) +{ +#define G(get_type_fcn) \ + ({ \ + GType get_type_fcn(void); \ + \ + get_type_fcn; \ + }) + GType (*get_type_fcns[])(void) = { + G(nm_802_11_ap_flags_get_type), + G(nm_802_11_ap_security_flags_get_type), + G(nm_802_11_mode_get_type), + G(nm_access_point_get_type), + G(nm_activation_state_flags_get_type), + G(nm_active_connection_get_type), + G(nm_active_connection_state_get_type), + G(nm_active_connection_state_reason_get_type), + G(nm_agent_manager_error_get_type), + G(nm_bluetooth_capabilities_get_type), + G(nm_bridge_vlan_get_type), + G(nm_capability_get_type), + G(nm_checkpoint_create_flags_get_type), + G(nm_checkpoint_get_type), + G(nm_client_error_get_type), + G(nm_client_get_type), + G(nm_client_permission_get_type), + G(nm_client_permission_result_get_type), + G(nm_connection_error_get_type), + G(nm_connection_get_type), + G(nm_connection_multi_connect_get_type), + G(nm_connection_serialization_flags_get_type), + G(nm_connectivity_state_get_type), + G(nm_crypto_error_get_type), + G(nm_device_6lowpan_get_type), + G(nm_device_adsl_get_type), + G(nm_device_bond_get_type), + G(nm_device_bridge_get_type), + G(nm_device_bt_get_type), + G(nm_device_capabilities_get_type), + G(nm_device_dummy_get_type), + G(nm_device_error_get_type), + G(nm_device_ethernet_get_type), + G(nm_device_generic_get_type), + G(nm_device_get_type), + G(nm_device_infiniband_get_type), + G(nm_device_ip_tunnel_get_type), + G(nm_device_macsec_get_type), + G(nm_device_macvlan_get_type), + G(nm_device_modem_capabilities_get_type), + G(nm_device_modem_get_type), + G(nm_device_olpc_mesh_get_type), + G(nm_device_ovs_bridge_get_type), + G(nm_device_ovs_interface_get_type), + G(nm_device_ovs_port_get_type), + G(nm_device_ppp_get_type), + G(nm_device_state_get_type), + G(nm_device_state_reason_get_type), + G(nm_device_team_get_type), + G(nm_device_tun_get_type), + G(nm_device_type_get_type), + G(nm_device_vlan_get_type), + G(nm_device_vxlan_get_type), + G(nm_device_wifi_capabilities_get_type), + G(nm_device_wifi_get_type), + G(nm_device_wifi_p2p_get_type), + G(nm_device_wimax_get_type), + G(nm_device_wireguard_get_type), + G(nm_device_wpan_get_type), + G(nm_dhcp4_config_get_type), + G(nm_dhcp6_config_get_type), + G(nm_dhcp_config_get_type), + G(nm_dns_entry_get_type), + G(nm_ip4_config_get_type), + G(nm_ip6_config_get_type), + G(nm_ip_address_get_type), + G(nm_ip_config_get_type), + G(nm_ip_route_get_type), + G(nm_ip_routing_rule_as_string_flags_get_type), + G(nm_ip_routing_rule_get_type), + G(nm_ip_tunnel_flags_get_type), + G(nm_ip_tunnel_mode_get_type), + G(nm_lldp_neighbor_get_type), + G(nm_manager_error_get_type), + G(nm_manager_reload_flags_get_type), + G(nm_metered_get_type), + G(nm_object_get_type), + G(nm_remote_connection_get_type), + G(nm_secret_agent_capabilities_get_type), + G(nm_secret_agent_error_get_type), + G(nm_secret_agent_get_secrets_flags_get_type), + G(nm_secret_agent_old_get_type), + G(nm_setting_6lowpan_get_type), + G(nm_setting_802_1x_auth_flags_get_type), + G(nm_setting_802_1x_ck_format_get_type), + G(nm_setting_802_1x_ck_scheme_get_type), + G(nm_setting_802_1x_get_type), + G(nm_setting_adsl_get_type), + G(nm_setting_bluetooth_get_type), + G(nm_setting_bond_get_type), + G(nm_setting_bridge_get_type), + G(nm_setting_bridge_port_get_type), + G(nm_setting_cdma_get_type), + G(nm_setting_compare_flags_get_type), + G(nm_setting_connection_autoconnect_slaves_get_type), + G(nm_setting_connection_get_type), + G(nm_setting_connection_lldp_get_type), + G(nm_setting_connection_llmnr_get_type), + G(nm_setting_connection_mdns_get_type), + G(nm_setting_dcb_flags_get_type), + G(nm_setting_dcb_get_type), + G(nm_setting_diff_result_get_type), + G(nm_setting_dummy_get_type), + G(nm_setting_ethtool_get_type), + G(nm_setting_generic_get_type), + G(nm_setting_get_type), + G(nm_setting_gsm_get_type), + G(nm_setting_infiniband_get_type), + G(nm_setting_ip4_config_get_type), + G(nm_setting_ip6_config_addr_gen_mode_get_type), + G(nm_setting_ip6_config_get_type), + G(nm_setting_ip6_config_privacy_get_type), + G(nm_setting_ip_config_get_type), + G(nm_setting_ip_tunnel_get_type), + G(nm_setting_mac_randomization_get_type), + G(nm_setting_macsec_get_type), + G(nm_setting_macsec_mode_get_type), + G(nm_setting_macsec_validation_get_type), + G(nm_setting_macvlan_get_type), + G(nm_setting_macvlan_mode_get_type), + G(nm_setting_match_get_type), + G(nm_setting_olpc_mesh_get_type), + G(nm_setting_ovs_bridge_get_type), + G(nm_setting_ovs_dpdk_get_type), + G(nm_setting_ovs_interface_get_type), + G(nm_setting_ovs_patch_get_type), + G(nm_setting_ovs_port_get_type), + G(nm_setting_ppp_get_type), + G(nm_setting_pppoe_get_type), + G(nm_setting_proxy_get_type), + G(nm_setting_proxy_method_get_type), + G(nm_settings_add_connection2_flags_get_type), + G(nm_settings_connection_flags_get_type), + G(nm_setting_secret_flags_get_type), + G(nm_setting_serial_get_type), + G(nm_setting_serial_parity_get_type), + G(nm_settings_error_get_type), + G(nm_setting_sriov_get_type), + G(nm_settings_update2_flags_get_type), + G(nm_setting_tc_config_get_type), + G(nm_setting_team_get_type), + G(nm_setting_team_port_get_type), + G(nm_setting_tun_get_type), + G(nm_setting_tun_mode_get_type), + G(nm_setting_user_get_type), + G(nm_setting_vlan_get_type), + G(nm_setting_vpn_get_type), + G(nm_setting_vxlan_get_type), + G(nm_setting_wifi_p2p_get_type), + G(nm_setting_wimax_get_type), + G(nm_setting_wired_get_type), + G(nm_setting_wired_wake_on_lan_get_type), + G(nm_setting_wireguard_get_type), + G(nm_setting_wireless_get_type), + G(nm_setting_wireless_powersave_get_type), + G(nm_setting_wireless_security_fils_get_type), + G(nm_setting_wireless_security_get_type), + G(nm_setting_wireless_security_pmf_get_type), + G(nm_setting_wireless_security_wps_method_get_type), + G(nm_setting_wireless_wake_on_wlan_get_type), + G(nm_setting_wpan_get_type), + G(nm_simple_connection_get_type), + G(nm_sriov_vf_get_type), + G(nm_sriov_vf_vlan_protocol_get_type), + G(nm_state_get_type), + G(nm_tc_action_get_type), + G(nm_tc_qdisc_get_type), + G(nm_tc_tfilter_get_type), + G(nm_team_link_watcher_arp_ping_flags_get_type), + G(nm_team_link_watcher_get_type), + G(nm_ternary_get_type), + G(nm_utils_security_type_get_type), + G(nm_vlan_flags_get_type), + G(nm_vlan_priority_map_get_type), + G(nm_vpn_connection_get_type), + G(nm_vpn_connection_state_get_type), + G(nm_vpn_connection_state_reason_get_type), + G(nm_vpn_editor_get_type), + G(nm_vpn_editor_plugin_capability_get_type), + G(nm_vpn_editor_plugin_get_type), + G(nm_vpn_plugin_error_get_type), + G(nm_vpn_plugin_failure_get_type), + G(nm_vpn_plugin_info_get_type), + G(nm_vpn_plugin_old_get_type), + G(nm_vpn_service_plugin_get_type), + G(nm_vpn_service_state_get_type), + G(nm_wep_key_type_get_type), + G(nm_wifi_p2p_peer_get_type), + G(nm_wimax_nsp_get_type), + G(nm_wimax_nsp_network_type_get_type), + G(nm_wireguard_peer_get_type), + }; + guint i_type; + + for (i_type = 0; i_type < G_N_ELEMENTS(get_type_fcns); i_type++) { + nm_auto_unref_gtypeclass GObjectClass *klass_unref = NULL; + GType gtype = (get_type_fcns[i_type]) (); + GObjectClass * klass; + + g_assert(g_str_has_prefix(g_type_name(gtype), "NM")); + + if (G_TYPE_IS_INTERFACE(gtype)) { + if (!NM_IN_STRSET(g_type_name(gtype), + "NMConnection", + "NMVpnEditor", + "NMVpnEditorPlugin")) + g_error("unexpected interface type %s", g_type_name(gtype)); + continue; + } + + if (g_type_is_a(gtype, G_TYPE_BOXED)) + continue; + + /* We only test parts of the types, and avoid initializing all the types. + * That is so that other unit tests in this process randomly run with either + * the class instance already initialized or not. */ + if ((nmtst_get_rand_uint() % 5) == 0) { + klass = (klass_unref = g_type_class_ref(gtype)); + g_assert(klass); + } else { + klass = g_type_class_peek(gtype); + if (!klass) + continue; + } + + if (g_type_is_a(gtype, G_TYPE_ENUM)) + continue; + + if (g_type_is_a(gtype, G_TYPE_FLAGS)) + continue; + + g_assert(g_type_is_a(gtype, G_TYPE_OBJECT)); + g_assert(G_IS_OBJECT_CLASS(klass)); + } +} + +/*****************************************************************************/ + +static void +test_nml_dbus_meta(void) +{ + const NMLDBusMetaIface * meta_iface; + const NMLDBusMetaProperty *meta_property; + guint prop_idx; + gsize i, j; + guint l, m; + + for (i = 0; i < G_N_ELEMENTS(_nml_dbus_meta_ifaces); i++) { + const NMLDBusMetaIface * mif = _nml_dbus_meta_ifaces[i]; + nm_auto_unref_gtypeclass GObjectClass *klass_unref = NULL; + GObjectClass * klass; + GType gtype; + +#define COMMON_PREFIX "org.freedesktop.NetworkManager" + + g_assert(mif); + g_assert(mif->dbus_iface_name); + g_assert(g_str_has_prefix(mif->dbus_iface_name, COMMON_PREFIX) + && !g_str_has_suffix(mif->dbus_iface_name, ".") + && NM_IN_SET(mif->dbus_iface_name[NM_STRLEN(COMMON_PREFIX)], '\0', '.')); + for (j = i + 1; j < G_N_ELEMENTS(_nml_dbus_meta_ifaces); j++) + g_assert(mif != _nml_dbus_meta_ifaces[j]); + if (i > 0) { + if (strcmp(_nml_dbus_meta_ifaces[i - 1]->dbus_iface_name, mif->dbus_iface_name) >= 0) { + g_error("meta-ifaces are not properly sorted: [%zu] \"%s\" should be after [%zu] " + "\"%s\"", + i - 1, + _nml_dbus_meta_ifaces[i - 1]->dbus_iface_name, + i, + mif->dbus_iface_name); + } + } + + g_assert((mif->n_dbus_properties > 0) == (!!mif->dbus_properties)); + + if (mif->interface_prio == NML_DBUS_META_INTERFACE_PRIO_NONE) { + g_assert(!mif->get_type_fcn); + g_assert(!mif->obj_properties); + g_assert(mif->n_obj_properties == 0); + g_assert(!mif->obj_properties_reverse_idx); + if (!NM_IN_STRSET(mif->dbus_iface_name, + NM_DBUS_INTERFACE_AGENT_MANAGER, + NM_DBUS_INTERFACE_DEVICE_STATISTICS, + NM_DBUS_INTERFACE_DEVICE_VETH)) + g_error("D-Bus interface \"%s\" is unexpectedly empty", mif->dbus_iface_name); + if (mif->n_dbus_properties == 0) + continue; + gtype = G_TYPE_NONE; + klass = NULL; + goto check_dbus_properties; + } + + g_assert(NM_IN_SET((NMLDBusMetaInteracePrio) mif->interface_prio, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + NML_DBUS_META_INTERFACE_PRIO_PARENT_TYPE, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_10, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_20, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30)); + + g_assert(mif->get_type_fcn); + gtype = mif->get_type_fcn(); + g_assert(g_type_is_a(gtype, G_TYPE_OBJECT)); + + if (mif->interface_prio == NML_DBUS_META_INTERFACE_PRIO_NMCLIENT) + g_assert(gtype == NM_TYPE_CLIENT); + else + g_assert(g_type_is_a(gtype, NM_TYPE_OBJECT)); + + /* We only test parts of the types, and avoid initializing all the types. + * That is so that other unit tests in this process randomly run with either + * the class instance already initialized or not. */ + if ((nmtst_get_rand_uint() % 5) == 0) { + klass = (klass_unref = g_type_class_ref(gtype)); + g_assert(klass); + } else + klass = g_type_class_peek(gtype); + + if (klass) { + if (NM_IS_OBJECT_CLASS(klass)) { + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + if (nm_object_class->property_o_info || nm_object_class->property_ao_info) { + int ii; + + for (ii = 0; ii < 2; ii++) { + const _NMObjectClassFieldInfo *p_prev = NULL; + const _NMObjectClassFieldInfo *p0 = ii == 0 + ? nm_object_class->property_o_info + : nm_object_class->property_ao_info; + const _NMObjectClassFieldInfo *p; + + for (p = p0; p; p = p->parent) { + GType parent_gtype; + NMObjectClass *parent_klass; + + g_assert(p->num > 0); + g_assert(NM_IS_OBJECT_CLASS(p->klass)); + g_assert(g_type_is_a(gtype, G_TYPE_FROM_CLASS(p->klass))); + if (ii == 0) + g_assert(p->klass->property_o_info == p); + else + g_assert(p->klass->property_ao_info == p); + g_assert_cmpint(p->klass->priv_ptr_offset, >, 0); + if (p_prev) { + g_assert(g_type_is_a(G_TYPE_FROM_CLASS(p_prev->klass), + G_TYPE_FROM_CLASS(p->klass))); + g_assert(p_prev->klass != p->klass); + g_assert_cmpint(p_prev->klass->priv_ptr_offset, + >, + p->klass->priv_ptr_offset); + g_assert_cmpint(p->klass->priv_ptr_indirect, ==, TRUE); + } + + parent_gtype = g_type_parent(G_TYPE_FROM_CLASS(p->klass)); + g_assert(g_type_is_a(parent_gtype, NM_TYPE_OBJECT)); + parent_klass = g_type_class_peek(parent_gtype); + g_assert(NM_IS_OBJECT_CLASS(parent_klass)); + if (parent_gtype == NM_TYPE_OBJECT) { + g_assert_cmpint(parent_klass->priv_ptr_offset, ==, 0); + g_assert_cmpint(parent_klass->priv_ptr_indirect, ==, FALSE); + g_assert(!p->parent); + } else { + if (parent_klass->priv_ptr_offset == 0) { + g_assert(!parent_klass->property_o_info); + g_assert(!parent_klass->property_ao_info); + g_assert_cmpint(parent_klass->priv_ptr_indirect, ==, FALSE); + g_assert(!p->parent); + } else if (p->klass->priv_ptr_offset + == parent_klass->priv_ptr_offset) { + g_assert(p->klass->property_o_info + == parent_klass->property_o_info); + g_assert(p->klass->property_ao_info + == parent_klass->property_ao_info); + g_assert(p->klass->priv_ptr_indirect + == parent_klass->priv_ptr_indirect); + } else { + g_assert_cmpint(parent_klass->priv_ptr_offset, >, 0); + g_assert_cmpint(parent_klass->priv_ptr_offset, + <, + p->klass->priv_ptr_offset); + g_assert_cmpint(parent_klass->priv_ptr_indirect, ==, TRUE); + g_assert(p->klass->property_o_info + != parent_klass->property_o_info + || p->klass->property_ao_info + != parent_klass->property_ao_info); + } + } + + p_prev = p; + } + } + + g_assert_cmpint(nm_object_class->priv_ptr_offset, >, 0); + } else { + g_assert_cmpint(nm_object_class->priv_ptr_offset, ==, 0); + g_assert_cmpint(nm_object_class->priv_ptr_indirect, ==, FALSE); + } + + } else + g_assert(NM_IS_CLIENT_CLASS(klass)); + } + + if (!mif->obj_properties) { + g_assert_cmpint(mif->n_obj_properties, ==, 0); + g_assert(!mif->obj_properties_reverse_idx); + } else { + g_assert(mif->obj_properties); + g_assert(mif->obj_properties[0] == 0); + g_assert_cmpint(mif->n_obj_properties, >, 1); + if (klass) { + for (l = 1; l < mif->n_obj_properties; l++) { + const GParamSpec *sp = mif->obj_properties[l]; + + g_assert(sp); + g_assert(sp->name); + g_assert(strlen(sp->name) > 0); + } + } + + g_assert(mif->obj_properties_reverse_idx); + if (klass) { + g_assert(mif->obj_properties_reverse_idx[0] == 0xFFu); + for (l = 0; l < mif->n_obj_properties; l++) { + guint8 ridx = mif->obj_properties_reverse_idx[l]; + + if (ridx != 0xFFu) { + g_assert_cmpint(ridx, <=, mif->n_dbus_properties); + for (m = l + 1; m < mif->n_obj_properties; m++) + g_assert_cmpint(ridx, !=, mif->obj_properties_reverse_idx[m]); + } + } + } + } + +check_dbus_properties: + for (l = 0; l < mif->n_dbus_properties; l++) { + const NMLDBusMetaProperty *mpr = &mif->dbus_properties[l]; + gs_free char * obj_property_name = NULL; + const struct { + const char *dbus_type; + GType default_gtype; + } * p_expected_type, *p_expected_type_2, + expected_types[] = { + {"b", G_TYPE_BOOLEAN}, {"q", G_TYPE_UINT}, + {"y", G_TYPE_UCHAR}, {"i", G_TYPE_INT}, + {"u", G_TYPE_UINT}, {"x", G_TYPE_INT64}, + {"t", G_TYPE_UINT64}, {"s", G_TYPE_STRING}, + {"o", G_TYPE_STRING}, {"ay", G_TYPE_BYTES}, + {"as", G_TYPE_STRV}, {"ao", G_TYPE_PTR_ARRAY}, + {"a{sv}", G_TYPE_HASH_TABLE}, {"aa{sv}", G_TYPE_PTR_ARRAY}, + + {"(uu)", G_TYPE_NONE}, {"aau", G_TYPE_NONE}, + {"au", G_TYPE_NONE}, {"a(ayuay)", G_TYPE_NONE}, + {"aay", G_TYPE_NONE}, {"a(ayuayu)", G_TYPE_NONE}, + + {"u", G_TYPE_FLAGS}, {"u", G_TYPE_ENUM}, + {"o", NM_TYPE_OBJECT}, + }; + const GParamSpec *pspec = NULL; + + g_assert(mpr->dbus_property_name); + g_assert(g_variant_type_string_is_valid((const char *) mpr->dbus_type)); + if (l > 0) { + if (strcmp(mif->dbus_properties[l - 1].dbus_property_name, mpr->dbus_property_name) + >= 0) { + g_error("meta-ifaces[%s] must have property #%u \"%s\" after #%u \"%s\"", + mif->dbus_iface_name, + l - 1, + mif->dbus_properties[l - 1].dbus_property_name, + l, + mpr->dbus_property_name); + } + } + + obj_property_name = nm_utils_wincaps_to_dash(mpr->dbus_property_name); + g_assert(obj_property_name); + + for (p_expected_type = &expected_types[0]; TRUE;) { + if (nm_streq((const char *) mpr->dbus_type, p_expected_type->dbus_type)) + break; + p_expected_type++; + if (p_expected_type >= &expected_types[G_N_ELEMENTS(expected_types)]) { + 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); + } + } + + if (klass && mpr->obj_properties_idx > 0) { + g_assert_cmpint(mpr->obj_properties_idx, <, mif->n_obj_properties); + if (!mpr->obj_property_no_reverse_idx) + g_assert_cmpint(mif->obj_properties_reverse_idx[mpr->obj_properties_idx], + ==, + l); + else { + g_assert_cmpint(mif->obj_properties_reverse_idx[mpr->obj_properties_idx], + !=, + l); + g_assert_cmpint(mif->obj_properties_reverse_idx[mpr->obj_properties_idx], + !=, + 0xFFu); + } + pspec = mif->obj_properties[mpr->obj_properties_idx]; + } + + if (mpr->use_notify_update_prop) { + g_assert(mpr->notify_update_prop); + } else { + if (klass) + g_assert(pspec); + } + + if (pspec) { + const char *expected_property_name; + + if (mif == &_nml_dbus_meta_iface_nm_connection_active + && nm_streq(pspec->name, NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT_PATH)) { + g_assert_cmpstr(obj_property_name, ==, "specific-object"); + expected_property_name = NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT_PATH; + } else if (mif == &_nml_dbus_meta_iface_nm_accesspoint + && nm_streq(pspec->name, NM_ACCESS_POINT_BSSID)) { + g_assert_cmpstr(obj_property_name, ==, "hw-address"); + expected_property_name = NM_ACCESS_POINT_BSSID; + } else if (mif == &_nml_dbus_meta_iface_nm_device_wireguard + && nm_streq(pspec->name, NM_DEVICE_WIREGUARD_FWMARK)) { + g_assert_cmpstr(obj_property_name, ==, "fw-mark"); + expected_property_name = NM_DEVICE_WIREGUARD_FWMARK; + } else if (NM_IN_SET(mif, + &_nml_dbus_meta_iface_nm_ip4config, + &_nml_dbus_meta_iface_nm_ip6config) + && nm_streq(pspec->name, NM_IP_CONFIG_ADDRESSES)) { + g_assert(NM_IN_STRSET(obj_property_name, "addresses", "address-data")); + expected_property_name = NM_IP_CONFIG_ADDRESSES; + } else if (NM_IN_SET(mif, + &_nml_dbus_meta_iface_nm_ip4config, + &_nml_dbus_meta_iface_nm_ip6config) + && nm_streq(pspec->name, NM_IP_CONFIG_ROUTES)) { + g_assert(NM_IN_STRSET(obj_property_name, "routes", "route-data")); + expected_property_name = NM_IP_CONFIG_ROUTES; + } else if (NM_IN_SET(mif, + &_nml_dbus_meta_iface_nm_ip4config, + &_nml_dbus_meta_iface_nm_ip6config) + && nm_streq(pspec->name, NM_IP_CONFIG_NAMESERVERS)) { + g_assert(NM_IN_STRSET(obj_property_name, "nameservers", "nameserver-data")); + expected_property_name = NM_IP_CONFIG_NAMESERVERS; + } else if (mif == &_nml_dbus_meta_iface_nm_ip4config + && nm_streq(pspec->name, NM_IP_CONFIG_WINS_SERVERS)) { + g_assert(NM_IN_STRSET(obj_property_name, "wins-servers", "wins-server-data")); + expected_property_name = NM_IP_CONFIG_WINS_SERVERS; + } else if (mif == &_nml_dbus_meta_iface_nm_dnsmanager + && nm_streq(pspec->name, NM_CLIENT_DNS_CONFIGURATION)) { + g_assert_cmpstr(obj_property_name, ==, "configuration"); + expected_property_name = NM_CLIENT_DNS_CONFIGURATION; + } else if (mif == &_nml_dbus_meta_iface_nm_dnsmanager + && nm_streq(pspec->name, NM_CLIENT_DNS_MODE)) { + g_assert_cmpstr(obj_property_name, ==, "mode"); + expected_property_name = NM_CLIENT_DNS_MODE; + } else if (mif == &_nml_dbus_meta_iface_nm_dnsmanager + && nm_streq(pspec->name, NM_CLIENT_DNS_RC_MANAGER)) { + g_assert_cmpstr(obj_property_name, ==, "rc-manager"); + expected_property_name = NM_CLIENT_DNS_RC_MANAGER; + } else + expected_property_name = obj_property_name; + + g_assert_cmpstr(expected_property_name, ==, pspec->name); + + if (!mpr->use_notify_update_prop) { + for (p_expected_type_2 = &expected_types[0]; + p_expected_type_2 < &expected_types[G_N_ELEMENTS(expected_types)]; + p_expected_type_2++) { + if (!nm_streq((const char *) mpr->dbus_type, p_expected_type_2->dbus_type)) + continue; + if (pspec->value_type == p_expected_type_2->default_gtype + || (p_expected_type_2->default_gtype == G_TYPE_ENUM + && g_type_is_a(pspec->value_type, G_TYPE_ENUM)) + || (p_expected_type_2->default_gtype == G_TYPE_FLAGS + && g_type_is_a(pspec->value_type, G_TYPE_FLAGS)) + || (p_expected_type_2->default_gtype == NM_TYPE_OBJECT + && nm_streq((const char *) mpr->dbus_type, "o") + && g_type_is_a(pspec->value_type, NM_TYPE_OBJECT))) + break; + } + if (p_expected_type_2 >= &expected_types[G_N_ELEMENTS(expected_types)]) { + 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, + mpr->dbus_property_name, + (const char *) mpr->dbus_type, + pspec->name, + g_type_name(pspec->value_type), + g_type_name(p_expected_type->default_gtype)); + } + } + + if (!nm_utils_g_param_spec_is_default(pspec)) { + /* We expect our properties to have a default value of zero/NULL. + * Except those whitelisted here: */ + if ((mif == &_nml_dbus_meta_iface_nm_accesspoint + && nm_streq(pspec->name, NM_ACCESS_POINT_LAST_SEEN)) + || (mif == &_nml_dbus_meta_iface_nm_device_vxlan + && nm_streq(pspec->name, NM_DEVICE_VXLAN_LEARNING)) + || (mif == &_nml_dbus_meta_iface_nm_device_wireless + && nm_streq(pspec->name, NM_DEVICE_WIFI_LAST_SCAN)) + || (mif == &_nml_dbus_meta_iface_nm_wifip2ppeer + && nm_streq(pspec->name, NM_WIFI_P2P_PEER_LAST_SEEN)) + || (mif == &_nml_dbus_meta_iface_nm_device_tun + && NM_IN_STRSET(pspec->name, + NM_DEVICE_TUN_GROUP, + NM_DEVICE_TUN_OWNER))) { + /* pass */ + } else { + g_error("property %s.%s (%s.%s) does not have a default value of zero", + mif->dbus_iface_name, + mpr->dbus_property_name, + g_type_name(gtype), + pspec->name); + } + } + } + } + + if (klass) { + for (l = 0; l < mif->n_obj_properties; l++) { + guint8 ridx = mif->obj_properties_reverse_idx[l]; + + if (ridx != 0xFFu) + g_assert_cmpint(mif->dbus_properties[ridx].obj_properties_idx, ==, l); + } + } + + g_assert(mif == nml_dbus_meta_iface_get(mif->dbus_iface_name)); + } + + meta_iface = nml_dbus_meta_iface_get(NM_DBUS_INTERFACE); + g_assert(meta_iface); + g_assert(meta_iface == &_nml_dbus_meta_iface_nm); + g_assert_cmpstr(meta_iface->dbus_iface_name, ==, NM_DBUS_INTERFACE); + + meta_property = nml_dbus_meta_property_get(meta_iface, "Version", &prop_idx); + g_assert(meta_property); + g_assert_cmpstr(meta_property->dbus_property_name, ==, "Version"); + g_assert(&meta_iface->dbus_properties[prop_idx] == meta_property); +} + +/*****************************************************************************/ + +static void +test_dbus_meta_types(void) +{ + struct list_data { + const char * dbus_iface_name; + GType gtype; + NMLDBusMetaInteracePrio interface_prio; + } list[] = { + { + NM_DBUS_INTERFACE, + NM_TYPE_CLIENT, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + }, + { + NM_DBUS_INTERFACE_ACCESS_POINT, + NM_TYPE_ACCESS_POINT, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_ACTIVE_CONNECTION, + NM_TYPE_ACTIVE_CONNECTION, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_10, + }, /* otherwise, NM_TYPE_VPN_CONNECTION. */ + { + NM_DBUS_INTERFACE_DEVICE_6LOWPAN, + NM_TYPE_DEVICE_6LOWPAN, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_ADSL, + NM_TYPE_DEVICE_ADSL, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_BOND, + NM_TYPE_DEVICE_BOND, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_BRIDGE, + NM_TYPE_DEVICE_BRIDGE, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_BLUETOOTH, + NM_TYPE_DEVICE_BT, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_DUMMY, + NM_TYPE_DEVICE_DUMMY, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_WIRED, + NM_TYPE_DEVICE_ETHERNET, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_20, + }, + { + NM_DBUS_INTERFACE_DEVICE_VETH, + NM_TYPE_DEVICE_VETH, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_GENERIC, + NM_TYPE_DEVICE_GENERIC, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_INFINIBAND, + NM_TYPE_DEVICE_INFINIBAND, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL, + NM_TYPE_DEVICE_IP_TUNNEL, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_MACSEC, + NM_TYPE_DEVICE_MACSEC, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_MACVLAN, + NM_TYPE_DEVICE_MACVLAN, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_MODEM, + NM_TYPE_DEVICE_MODEM, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_OLPC_MESH, + NM_TYPE_DEVICE_OLPC_MESH, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE, + NM_TYPE_DEVICE_OVS_INTERFACE, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_OVS_PORT, + NM_TYPE_DEVICE_OVS_PORT, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE, + NM_TYPE_DEVICE_OVS_BRIDGE, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_WIFI_P2P, + NM_TYPE_DEVICE_WIFI_P2P, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_PPP, + NM_TYPE_DEVICE_PPP, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_TEAM, + NM_TYPE_DEVICE_TEAM, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_TUN, + NM_TYPE_DEVICE_TUN, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_VLAN, + NM_TYPE_DEVICE_VLAN, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_WPAN, + NM_TYPE_DEVICE_WPAN, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_VXLAN, + NM_TYPE_DEVICE_VXLAN, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_WIRELESS, + NM_TYPE_DEVICE_WIFI, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_WIREGUARD, + NM_TYPE_DEVICE_WIREGUARD, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DHCP4_CONFIG, + NM_TYPE_DHCP4_CONFIG, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DHCP6_CONFIG, + NM_TYPE_DHCP6_CONFIG, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_IP4_CONFIG, + NM_TYPE_IP4_CONFIG, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_IP6_CONFIG, + NM_TYPE_IP6_CONFIG, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_WIFI_P2P_PEER, + NM_TYPE_WIFI_P2P_PEER, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + NM_TYPE_REMOTE_CONNECTION, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_SETTINGS, + NM_TYPE_CLIENT, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + }, + { + NM_DBUS_INTERFACE_DNS_MANAGER, + NM_TYPE_CLIENT, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + }, + { + NM_DBUS_INTERFACE_VPN_CONNECTION, + NM_TYPE_VPN_CONNECTION, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_CHECKPOINT, + NM_TYPE_CHECKPOINT, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + }; + guint i; + + /* These iface<->gtype associations are copied from "nm-client.c"'s obj_nm_for_gdbus_object(). + * This is redundant to the meta-data, still check that the meta data matches. */ + for (i = 0; i < G_N_ELEMENTS(list); i++) { + const struct list_data *d = &list[i]; + const NMLDBusMetaIface *meta_iface; + + meta_iface = nml_dbus_meta_iface_get(d->dbus_iface_name); + g_assert(meta_iface); + g_assert_cmpint(meta_iface->interface_prio, ==, d->interface_prio); + g_assert(meta_iface->get_type_fcn() == d->gtype); + } +} + +/*****************************************************************************/ + +static void +test_nm_auth_permissions(void) +{ + int i, j; + + G_STATIC_ASSERT(G_N_ELEMENTS(nm_auth_permission_names_by_idx) == NM_CLIENT_PERMISSION_LAST); + G_STATIC_ASSERT(G_N_ELEMENTS(nm_auth_permission_sorted) == NM_CLIENT_PERMISSION_LAST); + + for (i = 0; i < NM_CLIENT_PERMISSION_LAST; i++) { + g_assert(nm_auth_permission_names_by_idx[i]); + g_assert(NM_STR_HAS_PREFIX(nm_auth_permission_names_by_idx[i], + "org.freedesktop.NetworkManager.")); + g_assert_cmpint(nm_auth_permission_sorted[i], >, 0); + g_assert_cmpint(nm_auth_permission_sorted[i], <=, NM_CLIENT_PERMISSION_LAST); + for (j = i + 1; j < NM_CLIENT_PERMISSION_LAST; j++) { + g_assert_cmpint(nm_auth_permission_sorted[i], !=, nm_auth_permission_sorted[j]); + g_assert_cmpstr(nm_auth_permission_names_by_idx[i], + !=, + nm_auth_permission_names_by_idx[j]); + } + } + for (i = 1; i < NM_CLIENT_PERMISSION_LAST; i++) { + NMClientPermission a = nm_auth_permission_sorted[i - 1]; + NMClientPermission b = nm_auth_permission_sorted[i]; + const char * s_a = nm_auth_permission_names_by_idx[a - 1]; + const char * s_b = nm_auth_permission_names_by_idx[b - 1]; + + g_assert_cmpstr(s_a, <, s_b); + g_assert(a != b); + g_assert(s_a != s_b); + } + for (i = 1; i <= NM_CLIENT_PERMISSION_LAST; i++) { + const char *s = nm_auth_permission_to_string(i); + + g_assert_cmpstr(s, ==, nm_auth_permission_names_by_idx[i - 1]); + g_assert(s == nm_auth_permission_names_by_idx[i - 1]); + g_assert_cmpint(nm_auth_permission_from_string(s), ==, i); + } + + for (i = 0; i < NM_CLIENT_PERMISSION_LAST; i++) + g_assert_cmpint(nm_auth_permission_from_string(nm_auth_permission_names_by_idx[i]), + ==, + i + 1); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + nmtst_init(&argc, &argv, TRUE); + + g_test_add_func("/libnm/general/fixup_product_string", test_fixup_product_string); + g_test_add_func("/libnm/general/fixup_vendor_string", test_fixup_vendor_string); + g_test_add_func("/libnm/general/nm_vpn_service_plugin_read_vpn_details", + test_nm_vpn_service_plugin_read_vpn_details); + g_test_add_func("/libnm/general/test_types", test_types); + g_test_add_func("/libnm/general/test_nml_dbus_meta", test_nml_dbus_meta); + g_test_add_func("/libnm/general/test_dbus_meta_types", test_dbus_meta_types); + g_test_add_func("/libnm/general/test_nm_auth_permissions", test_nm_auth_permissions); + + return g_test_run(); +} diff --git a/src/libnm-client-impl/tests/test-nm-client.c b/src/libnm-client-impl/tests/test-nm-client.c new file mode 100644 index 00000000..55b886a0 --- /dev/null +++ b/src/libnm-client-impl/tests/test-nm-client.c @@ -0,0 +1,1300 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2010 - 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include <sys/types.h> +#include <signal.h> + +#include "libnm-client-test/nm-test-libnm-utils.h" + +static struct { + GMainLoop *loop; +} gl = {}; + +/*****************************************************************************/ + +static gboolean +loop_quit(gpointer user_data) +{ + g_main_loop_quit((GMainLoop *) user_data); + return G_SOURCE_REMOVE; +} + +/*****************************************************************************/ + +static void +devices_notify_cb(NMClient *c, GParamSpec *pspec, gpointer user_data) +{ + gboolean * notified = user_data; + const GPtrArray *devices; + NMDevice * device; + + devices = nm_client_get_devices(c); + g_assert(devices); + g_assert_cmpint(devices->len, ==, 1); + + device = g_ptr_array_index(devices, 0); + g_assert(device); + g_assert_cmpstr(nm_device_get_iface(device), ==, "eth0"); + + *notified = TRUE; +} + +static void +test_device_added(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + const GPtrArray * devices; + NMDevice * device; + gboolean notified = FALSE; + GError * error = NULL; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client = nmtstc_client_new(TRUE); + + devices = nm_client_get_devices(client); + g_assert(devices->len == 0); + + g_signal_connect(client, "notify::devices", (GCallback) devices_notify_cb, ¬ified); + + /* Tell the test service to add a new device */ + nmtstc_service_add_device(sinfo, client, "AddWiredDevice", "eth0"); + + /* coverity[loop_condition] */ + while (!notified) + g_main_context_iteration(NULL, TRUE); + + g_signal_handlers_disconnect_by_func(client, devices_notify_cb, ¬ified); + + devices = nm_client_get_devices(client); + g_assert(devices); + g_assert_cmpint(devices->len, ==, 1); + + device = g_ptr_array_index(devices, 0); + g_assert(device); + g_assert_cmpstr(nm_device_get_iface(device), ==, "eth0"); + + /* Try deleting the device via the ordinary NM interface, which should fail */ + nm_device_delete(device, NULL, &error); + g_assert_error(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_SOFTWARE); + g_clear_error(&error); +} + +/*****************************************************************************/ + +typedef enum { + SIGNAL_FIRST = 0x01, + SIGNAL_SECOND = 0x02, + SIGNAL_MASK = 0x0F, + NOTIFY_FIRST = 0x10, + NOTIFY_SECOND = 0x20, + NOTIFY_MASK = 0xF0 +} DeviceSignaledAfterInitType; + +static void +device_sai_added_cb(NMClient *c, NMDevice *device, gpointer user_data) +{ + guint *result = user_data; + + g_assert(device); + g_assert_cmpstr(nm_device_get_iface(device), ==, "eth0"); + + g_assert((*result & SIGNAL_MASK) == 0); + *result |= *result ? SIGNAL_SECOND : SIGNAL_FIRST; +} + +static void +devices_sai_notify_cb(NMClient *c, GParamSpec *pspec, gpointer user_data) +{ + guint * result = user_data; + const GPtrArray *devices; + NMDevice * device; + + g_assert_cmpstr(pspec->name, ==, "devices"); + + devices = nm_client_get_devices(c); + g_assert(devices); + g_assert_cmpint(devices->len, ==, 1); + + device = g_ptr_array_index(devices, 0); + g_assert(device); + g_assert_cmpstr(nm_device_get_iface(device), ==, "eth0"); + + g_assert((*result & NOTIFY_MASK) == 0); + *result |= *result ? NOTIFY_SECOND : NOTIFY_FIRST; +} + +static void +test_device_added_signal_after_init(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + const GPtrArray * devices; + NMDevice * device; + guint result = 0; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client = nmtstc_client_new(TRUE); + + devices = nm_client_get_devices(client); + g_assert(devices->len == 0); + + g_signal_connect(client, NM_CLIENT_DEVICE_ADDED, (GCallback) device_sai_added_cb, &result); + + g_signal_connect(client, + "notify::" NM_CLIENT_DEVICES, + (GCallback) devices_sai_notify_cb, + &result); + + /* Tell the test service to add a new device */ + nmtstc_service_add_device(sinfo, client, "AddWiredDevice", "eth0"); + + /* Ensure the 'device-added' signal doesn't show up before + * the 'Devices' property change notification */ + /* coverity[loop_condition] */ + while (!(result & SIGNAL_MASK) && !(result & NOTIFY_MASK)) + g_main_context_iteration(NULL, TRUE); + + g_signal_handlers_disconnect_by_func(client, device_sai_added_cb, &result); + g_signal_handlers_disconnect_by_func(client, devices_sai_notify_cb, &result); + + g_assert((result & SIGNAL_MASK) == SIGNAL_SECOND); + g_assert((result & NOTIFY_MASK) == NOTIFY_FIRST); + + devices = nm_client_get_devices(client); + g_assert(devices); + g_assert_cmpint(devices->len, ==, 1); + + device = g_ptr_array_index(devices, 0); + g_assert(device); + g_assert_cmpstr(nm_device_get_iface(device), ==, "eth0"); +} + +/*****************************************************************************/ + +static const char *expected_bssid = "66:55:44:33:22:11"; + +typedef struct { + GMainLoop *loop; + gboolean found; + char * ap_path; + gboolean signaled; + gboolean notified; + guint quit_id; + guint quit_count; +} WifiApInfo; + +static void +wifi_check_quit(WifiApInfo *info) +{ + info->quit_count--; + if (info->quit_count == 0) { + g_source_remove(info->quit_id); + info->quit_id = 0; + g_main_loop_quit(info->loop); + } +} + +static void +got_ap_path(WifiApInfo *info, const char *path) +{ + if (info->ap_path) + g_assert_cmpstr(info->ap_path, ==, path); + else + info->ap_path = g_strdup(path); +} + +static void +wifi_ap_added_cb(NMDeviceWifi *w, NMAccessPoint *ap, WifiApInfo *info) +{ + g_assert(ap); + g_assert_cmpstr(nm_access_point_get_bssid(ap), ==, expected_bssid); + got_ap_path(info, nm_object_get_path(NM_OBJECT(ap))); + + info->signaled = TRUE; + wifi_check_quit(info); +} + +static void +wifi_ap_add_notify_cb(NMDeviceWifi *w, GParamSpec *pspec, WifiApInfo *info) +{ + const GPtrArray *aps; + NMAccessPoint * ap; + + aps = nm_device_wifi_get_access_points(w); + g_assert(aps); + g_assert_cmpint(aps->len, ==, 1); + + ap = g_ptr_array_index(aps, 0); + g_assert(ap); + g_assert_cmpstr(nm_access_point_get_bssid(ap), ==, "66:55:44:33:22:11"); + got_ap_path(info, nm_object_get_path(NM_OBJECT(ap))); + + info->notified = TRUE; + wifi_check_quit(info); +} + +static void +wifi_ap_removed_cb(NMDeviceWifi *w, NMAccessPoint *ap, WifiApInfo *info) +{ + g_assert(ap); + g_assert_cmpstr(info->ap_path, ==, nm_object_get_path(NM_OBJECT(ap))); + + info->signaled = TRUE; + wifi_check_quit(info); +} + +static void +wifi_ap_remove_notify_cb(NMDeviceWifi *w, GParamSpec *pspec, WifiApInfo *info) +{ + const GPtrArray *aps; + + aps = nm_device_wifi_get_access_points(w); + g_assert(aps->len == 0); + + info->notified = TRUE; + wifi_check_quit(info); +} + +static void +test_wifi_ap_added_removed(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + NMDeviceWifi * wifi; + WifiApInfo info = {gl.loop, FALSE, FALSE, 0, 0}; + GVariant * ret; + GError * error = NULL; + gs_free char * expected_path = NULL; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client = nmtstc_client_new(TRUE); + + /*************************************/ + /* Add the wifi device */ + wifi = (NMDeviceWifi *) nmtstc_service_add_device(sinfo, client, "AddWifiDevice", "wlan0"); + g_assert(NM_IS_DEVICE_WIFI(wifi)); + + /*************************************/ + /* Add the wifi AP */ + info.signaled = FALSE; + info.notified = FALSE; + info.quit_id = 0; + + ret = g_dbus_proxy_call_sync(sinfo->proxy, + "AddWifiAp", + g_variant_new("(sss)", "wlan0", "test-ap", expected_bssid), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 3000, + NULL, + &error); + g_assert_no_error(error); + g_assert(ret); + g_assert_cmpstr(g_variant_get_type_string(ret), ==, "(o)"); + g_variant_get(ret, "(o)", &expected_path); + g_variant_unref(ret); + + g_signal_connect(wifi, "access-point-added", (GCallback) wifi_ap_added_cb, &info); + info.quit_count = 1; + + g_signal_connect(wifi, "notify::access-points", (GCallback) wifi_ap_add_notify_cb, &info); + info.quit_count++; + + /* Wait for libnm to find the AP */ + info.quit_id = g_timeout_add_seconds(5, loop_quit, gl.loop); + g_main_loop_run(gl.loop); + + g_assert(info.signaled); + g_assert(info.notified); + g_assert(info.ap_path); + g_assert_cmpstr(info.ap_path, ==, expected_path); + g_signal_handlers_disconnect_by_func(wifi, wifi_ap_added_cb, &info); + g_signal_handlers_disconnect_by_func(wifi, wifi_ap_add_notify_cb, &info); + + /*************************************/ + /* Remove the wifi device */ + info.signaled = FALSE; + info.notified = FALSE; + info.quit_id = 0; + + ret = g_dbus_proxy_call_sync(sinfo->proxy, + "RemoveWifiAp", + g_variant_new("(so)", "wlan0", expected_path), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 3000, + NULL, + &error); + g_assert_no_error(error); + nm_clear_pointer(&ret, g_variant_unref); + + g_signal_connect(wifi, "access-point-removed", (GCallback) wifi_ap_removed_cb, &info); + info.quit_count = 1; + + g_signal_connect(wifi, "notify::access-points", (GCallback) wifi_ap_remove_notify_cb, &info); + info.quit_count++; + + /* Wait for libnm to find the AP */ + info.quit_id = g_timeout_add_seconds(5, loop_quit, gl.loop); + g_main_loop_run(gl.loop); + + g_assert(info.signaled); + g_assert(info.notified); + g_signal_handlers_disconnect_by_func(wifi, wifi_ap_removed_cb, &info); + g_signal_handlers_disconnect_by_func(wifi, wifi_ap_remove_notify_cb, &info); + + g_free(info.ap_path); +} + +/*****************************************************************************/ + +typedef struct { + GMainLoop *loop; + gboolean signaled; + gboolean notified; + guint quit_count; + guint quit_id; +} DaInfo; + +static void +da_check_quit(DaInfo *info) +{ + info->quit_count--; + if (info->quit_count == 0) { + g_source_remove(info->quit_id); + info->quit_id = 0; + g_main_loop_quit(info->loop); + } +} + +static void +da_device_removed_cb(NMClient *c, NMDevice *device, DaInfo *info) +{ + g_assert_cmpstr(nm_device_get_iface(device), ==, "eth0"); + info->signaled = TRUE; + da_check_quit(info); +} + +static void +da_devices_notify_cb(NMClient *c, GParamSpec *pspec, DaInfo *info) +{ + const GPtrArray *devices; + NMDevice * device; + guint i; + const char * iface; + + devices = nm_client_get_devices(c); + g_assert(devices); + g_assert_cmpint(devices->len, ==, 2); + + for (i = 0; i < devices->len; i++) { + device = g_ptr_array_index(devices, i); + iface = nm_device_get_iface(device); + + g_assert(!strcmp(iface, "wlan0") || !strcmp(iface, "eth1")); + } + + info->notified = TRUE; + da_check_quit(info); +} + +static void +test_devices_array(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + DaInfo info = {gl.loop}; + NMDevice * wlan0, *eth0, *eth1, *device; + const GPtrArray * devices; + GError * error = NULL; + GVariant * ret; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client = nmtstc_client_new(TRUE); + + /*************************************/ + /* Add some devices */ + wlan0 = nmtstc_service_add_device(sinfo, client, "AddWifiDevice", "wlan0"); + eth0 = nmtstc_service_add_device(sinfo, client, "AddWiredDevice", "eth0"); + eth1 = nmtstc_service_add_device(sinfo, client, "AddWiredDevice", "eth1"); + + /* Ensure the devices now exist */ + devices = nm_client_get_devices(client); + g_assert(devices); + g_assert_cmpint(devices->len, ==, 3); + + device = nm_client_get_device_by_iface(client, "wlan0"); + g_assert(NM_IS_DEVICE_WIFI(device)); + g_assert(device == wlan0); + + device = nm_client_get_device_by_iface(client, "eth0"); + g_assert(NM_IS_DEVICE_ETHERNET(device)); + g_assert(device == eth0); + + device = nm_client_get_device_by_iface(client, "eth1"); + g_assert(NM_IS_DEVICE_ETHERNET(device)); + g_assert(device == eth1); + + /********************************/ + /* Now remove the device in the middle */ + ret = g_dbus_proxy_call_sync(sinfo->proxy, + "RemoveDevice", + g_variant_new("(o)", nm_object_get_path(NM_OBJECT(eth0))), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 3000, + NULL, + &error); + g_assert_no_error(error); + g_assert(ret); + g_variant_unref(ret); + + g_signal_connect(client, "device-removed", (GCallback) da_device_removed_cb, &info); + + g_signal_connect(client, "notify::devices", (GCallback) da_devices_notify_cb, &info); + info.quit_count = 2; + + /* Wait for libnm to notice the changes */ + info.quit_id = g_timeout_add_seconds(5, loop_quit, gl.loop); + g_main_loop_run(gl.loop); + + g_assert_cmpint(info.quit_count, ==, 0); + g_signal_handlers_disconnect_by_func(client, da_device_removed_cb, &info); + g_signal_handlers_disconnect_by_func(client, da_devices_notify_cb, &info); + + /* Ensure only two are left */ + devices = nm_client_get_devices(client); + g_assert(devices); + g_assert_cmpint(devices->len, ==, 2); + + device = nm_client_get_device_by_iface(client, "wlan0"); + g_assert(NM_IS_DEVICE_WIFI(device)); + g_assert(device == wlan0); + + device = nm_client_get_device_by_iface(client, "eth1"); + g_assert(NM_IS_DEVICE_ETHERNET(device)); + g_assert(device == eth1); +} + +static void +nm_running_changed(GObject *client, GParamSpec *pspec, gpointer user_data) +{ + int *running_changed = user_data; + + (*running_changed)++; + g_main_loop_quit(gl.loop); +} + +static void +test_client_nm_running(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client1 = NULL; + gs_unref_object NMClient *client2 = NULL; + guint quit_id; + int running_changed = 0; + GError * error = NULL; + + client1 = nmtstc_client_new(TRUE); + + g_assert(!nm_client_get_nm_running(client1)); + g_assert_cmpstr(nm_client_get_version(client1), ==, NULL); + + g_assert(!nm_client_networking_get_enabled(client1)); + /* This will have no effect, but it shouldn't cause any warnings either. */ + nm_client_networking_set_enabled(client1, TRUE, NULL); + g_assert(!nm_client_networking_get_enabled(client1)); + + /* OTOH, this should result in an error */ + nm_client_set_logging(client1, "DEFAULT", "INFO", &error); + g_assert_error(error, NM_CLIENT_ERROR, NM_CLIENT_ERROR_MANAGER_NOT_RUNNING); + g_clear_error(&error); + + /* Now start the test service. */ + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client2 = nmtstc_client_new(FALSE); + + /* client2 should know that NM is running, but the previously-created + * client1 hasn't gotten the news yet. + */ + g_assert(!nm_client_get_nm_running(client1)); + g_assert(nm_client_get_nm_running(client2)); + + g_signal_connect(client1, + "notify::" NM_CLIENT_NM_RUNNING, + G_CALLBACK(nm_running_changed), + &running_changed); + quit_id = g_timeout_add_seconds(5, loop_quit, gl.loop); + g_main_loop_run(gl.loop); + g_assert_cmpint(running_changed, ==, 1); + g_assert(nm_client_get_nm_running(client1)); + g_source_remove(quit_id); + + /* And kill it */ + nm_clear_pointer(&sinfo, nmtstc_service_cleanup); + + g_assert(nm_client_get_nm_running(client1)); + + quit_id = g_timeout_add_seconds(5, loop_quit, gl.loop); + g_main_loop_run(gl.loop); + g_assert_cmpint(running_changed, ==, 2); + g_assert(!nm_client_get_nm_running(client1)); + g_source_remove(quit_id); +} + +typedef struct { + GMainLoop * loop; + NMActiveConnection *ac; + + int remaining; + + NMDevice *device; + gulong ac_signal_id; +} TestACInfo; + +static void +assert_ac_and_device(NMClient *client) +{ + const GPtrArray * devices, *acs, *ac_devices; + NMDevice * device, *ac_device; + NMActiveConnection *ac, *device_ac; + + acs = nm_client_get_active_connections(client); + g_assert(acs != NULL); + g_assert_cmpint(acs->len, ==, 1); + devices = nm_client_get_devices(client); + g_assert(devices != NULL); + g_assert_cmpint(devices->len, >=, 1); + + ac = acs->pdata[0]; + ac_devices = nm_active_connection_get_devices(ac); + g_assert(ac_devices != NULL); + g_assert_cmpint(ac_devices->len, ==, 1); + ac_device = ac_devices->pdata[0]; + g_assert(ac_device != NULL); + + device = devices->pdata[0]; + if (device != ac_device && devices->len > 1) + device = devices->pdata[1]; + + g_assert_cmpstr(nm_object_get_path(NM_OBJECT(device)), + ==, + nm_object_get_path(NM_OBJECT(ac_device))); + g_assert(device == ac_device); + + device_ac = nm_device_get_active_connection(device); + if (!device_ac) { + /* the stub NetworkManager service starts activating in an idle handler (delayed). That means, the + * device may not yet refer to the active connection at this point. */ + } else { + g_assert_cmpstr(nm_object_get_path(NM_OBJECT(ac)), + ==, + nm_object_get_path(NM_OBJECT(device_ac))); + g_assert(ac == device_ac); + } +} + +static void +add_and_activate_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMClient * client = NM_CLIENT(object); + TestACInfo *info = user_data; + GError * error = NULL; + + info->ac = nm_client_add_and_activate_connection_finish(client, result, &error); + g_assert_no_error(error); + g_assert(info->ac != NULL); + + assert_ac_and_device(client); + + info->remaining--; + if (!info->remaining) + g_main_loop_quit(info->loop); +} + +static void +client_acs_changed_cb(GObject *client, GParamSpec *pspec, gpointer user_data) +{ + TestACInfo * info = user_data; + const GPtrArray *acs; + + acs = nm_client_get_active_connections(NM_CLIENT(client)); + g_assert(acs != NULL); + g_assert_cmpint(acs->len, ==, 1); + + info->remaining--; + if (!info->remaining) + g_main_loop_quit(info->loop); +} + +static void +device_ac_changed_cb(GObject *device, GParamSpec *pspec, gpointer user_data) +{ + TestACInfo *info = user_data; + + g_assert(nm_device_get_active_connection(NM_DEVICE(device)) != NULL); + + info->remaining--; + if (!info->remaining) + g_main_loop_quit(info->loop); +} + +static void +test_active_connections(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + NMDevice * device; + NMConnection * conn; + TestACInfo info = {gl.loop, NULL, 0}; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client = nmtstc_client_new(TRUE); + + /* Tell the test service to add a new device */ + device = nmtstc_service_add_device(sinfo, client, "AddWiredDevice", "eth0"); + + conn = nmtst_create_minimal_connection("test-ac", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL); + nm_client_add_and_activate_connection_async(client, + conn, + device, + NULL, + NULL, + add_and_activate_cb, + &info); + g_object_unref(conn); + + g_signal_connect(client, + "notify::" NM_CLIENT_ACTIVE_CONNECTIONS, + G_CALLBACK(client_acs_changed_cb), + &info); + g_signal_connect(device, + "notify::" NM_DEVICE_ACTIVE_CONNECTION, + G_CALLBACK(device_ac_changed_cb), + &info); + + /* Two signals plus activate_cb */ + info.remaining = 3; + g_main_loop_run(gl.loop); + g_signal_handlers_disconnect_by_func(client, client_acs_changed_cb, &info); + g_signal_handlers_disconnect_by_func(device, device_ac_changed_cb, &info); + + g_assert(info.ac != NULL); + + g_object_unref(info.ac); + g_clear_object(&client); + + /* Ensure that we can correctly resolve the recursive property link between the + * AC and the Device in a newly-created client. + */ + client = nmtstc_client_new(TRUE); + assert_ac_and_device(client); + g_clear_object(&client); + + client = nmtstc_client_new(TRUE); + assert_ac_and_device(client); + g_clear_object(&client); +} + +static void +client_devices_changed_cb(GObject *client, GParamSpec *pspec, gpointer user_data) +{ + TestACInfo * info = user_data; + const GPtrArray *devices; + NMDevice * device; + + devices = nm_client_get_devices(NM_CLIENT(client)); + g_assert(devices != NULL); + if (devices->len < 2) + return; + g_assert_cmpint(devices->len, ==, 2); + + if (NM_IS_DEVICE_VLAN(devices->pdata[0])) + device = devices->pdata[0]; + else if (NM_IS_DEVICE_VLAN(devices->pdata[1])) + device = devices->pdata[1]; + else + g_assert_not_reached(); + + g_assert_cmpstr(nm_device_get_iface(device), ==, "eth0.1"); + + if (!nm_device_get_active_connection(device)) { + g_assert(info->ac_signal_id == 0); + info->remaining++; + info->device = device; + g_object_add_weak_pointer(G_OBJECT(device), (gpointer *) &info->device); + info->ac_signal_id = g_signal_connect(device, + "notify::" NM_DEVICE_ACTIVE_CONNECTION, + G_CALLBACK(device_ac_changed_cb), + info); + } + + info->remaining--; + if (!info->remaining) + g_main_loop_quit(info->loop); +} + +typedef struct { + GMainLoop * loop; + NMRemoteConnection *remote; +} TestConnectionInfo; + +static void +add_connection_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + TestConnectionInfo *info = user_data; + GError * error = NULL; + + info->remote = nm_client_add_connection_finish(NM_CLIENT(object), result, &error); + g_assert_no_error(error); + g_main_loop_quit(info->loop); +} + +static void +activate_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMClient * client = NM_CLIENT(object); + TestACInfo *info = user_data; + GError * error = NULL; + + info->ac = nm_client_activate_connection_finish(client, result, &error); + g_assert_no_error(error); + g_assert(info->ac != NULL); + + assert_ac_and_device(client); + + info->remaining--; + if (!info->remaining) + g_main_loop_quit(info->loop); +} + +static void +test_activate_virtual(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + NMConnection * conn; + NMSettingConnection * s_con; + NMSettingVlan * s_vlan; + TestACInfo info = {gl.loop, NULL, 0}; + TestConnectionInfo conn_info = {gl.loop, NULL}; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client = nmtstc_client_new(TRUE); + + nmtstc_service_add_device(sinfo, client, "AddWiredDevice", "eth0"); + + conn = nmtst_create_minimal_connection("test-ac", NULL, NM_SETTING_VLAN_SETTING_NAME, &s_con); + g_object_set(s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, "eth0.1", NULL); + s_vlan = nm_connection_get_setting_vlan(conn); + g_object_set(s_vlan, NM_SETTING_VLAN_ID, 1, NM_SETTING_VLAN_PARENT, "eth0", NULL); + + nm_client_add_connection_async(client, conn, TRUE, NULL, add_connection_cb, &conn_info); + g_main_loop_run(gl.loop); + g_object_unref(conn); + conn = NM_CONNECTION(conn_info.remote); + + nm_client_activate_connection_async(client, conn, NULL, NULL, NULL, activate_cb, &info); + g_object_unref(conn); + + g_signal_connect(client, + "notify::" NM_CLIENT_ACTIVE_CONNECTIONS, + G_CALLBACK(client_acs_changed_cb), + &info); + g_signal_connect(client, + "notify::" NM_CLIENT_DEVICES, + G_CALLBACK(client_devices_changed_cb), + &info); + + /* We're expecting a client::devices change, client::activate callback, + * and a device::active-connection change. + * The client::devices callback can hook a client::active-connections + * change and bump this if the property is not yet loaded. + */ + info.remaining = 3; + + g_main_loop_run(gl.loop); + g_signal_handlers_disconnect_by_func(client, client_acs_changed_cb, &info); + g_signal_handlers_disconnect_by_func(client, client_devices_changed_cb, &info); + + g_assert(info.ac != NULL); + g_clear_object(&info.ac); + + if (info.device) { + g_object_remove_weak_pointer(G_OBJECT(info.device), (gpointer *) &info.device); + nm_clear_g_signal_handler(info.device, &info.ac_signal_id); + } +} + +static void +test_device_connection_compatibility(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + gs_unref_object NMConnection *conn = NULL; + NMDevice * device1; + NMDevice * device2; + NMSettingWired * s_wired; + GError * error = NULL; + const char * subchannels[] = {"0.0.8000", "0.0.8001", "0.0.8002", NULL}; + const char * subchannels_2[] = {"0.0.8000", "0.0.8001", NULL}; + const char * subchannels_x[] = {"0.0.8000", "0.0.8001", "0.0.800X", NULL}; + const char * hw_addr1 = "52:54:00:ab:db:23"; + const char * hw_addr2 = "52:54:00:ab:db:24"; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client = nmtstc_client_new(TRUE); + + /* Create two devices */ + device1 = nmtstc_service_add_wired_device(sinfo, client, "eth0", hw_addr1, subchannels); + device2 = nmtstc_service_add_wired_device(sinfo, client, "eth1", hw_addr2, NULL); + + g_assert_cmpstr(nm_device_get_hw_address(device1), ==, hw_addr1); + g_assert_cmpstr(nm_device_get_hw_address(device2), ==, hw_addr2); + + conn = + nmtst_create_minimal_connection("wired-matches", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL); + s_wired = nm_connection_get_setting_wired(conn); + nm_setting_wired_add_mac_blacklist_item(s_wired, "00:11:22:33:44:55"); + + /* device1 and conn are compatible */ + g_object_set(s_wired, + NM_SETTING_WIRED_MAC_ADDRESS, + hw_addr1, + NM_SETTING_WIRED_S390_SUBCHANNELS, + subchannels, + NULL); + nm_device_connection_compatible(device1, conn, &error); + g_assert_no_error(error); + + /* device2 and conn differ in subchannels */ + g_object_set(s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, subchannels_x, NULL); + nm_device_connection_compatible(device2, conn, &error); + g_assert_error(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION); + g_clear_error(&error); + + /* device1 and conn differ in subchannels - 2 in connection, 3 in device */ + g_object_set(s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, subchannels_2, NULL); + nm_device_connection_compatible(device1, conn, &error); + g_assert_error(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION); + g_clear_error(&error); + + g_object_set(s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, NULL, NULL); + + /* device2 and conn differ in MAC address */ + g_object_set(s_wired, NM_SETTING_WIRED_MAC_ADDRESS, "aa:bb:cc:dd:ee:ee", NULL); + nm_device_connection_compatible(device2, conn, &error); + g_assert_error(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION); + g_clear_error(&error); + g_object_set(s_wired, NM_SETTING_WIRED_MAC_ADDRESS, NULL, NULL); + + /* device1 is blacklisted in conn */ + nm_setting_wired_add_mac_blacklist_item(s_wired, hw_addr1); + nm_device_connection_compatible(device1, conn, &error); + g_assert_error(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION); + g_clear_error(&error); +} + +/*****************************************************************************/ + +static gboolean +_test_connection_invalid_find_connections(gpointer element, gpointer needle, gpointer user_data) +{ + NMRemoteConnection *con = NM_REMOTE_CONNECTION(element); + const char * path = needle; + + g_assert(NM_IS_REMOTE_CONNECTION(con)); + g_assert(path && *path); + + return strcmp(path, nm_connection_get_path((NMConnection *) con)) == 0; +} + +#define ASSERT_IDX(i) \ + G_STMT_START \ + { \ + g_assert_cmpint(idx[i], >=, 0); \ + g_assert(path##i &&*path##i); \ + g_assert(NM_IS_REMOTE_CONNECTION(connections->pdata[idx[i]])); \ + g_assert_cmpstr(nm_connection_get_path(connections->pdata[idx[i]]), ==, path##i); \ + } \ + G_STMT_END + +static void +test_connection_invalid(void) +{ + NMTSTC_SERVICE_INFO_SETUP(my_sinfo) + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection * s_con; + gs_unref_object NMClient *client = NULL; + const GPtrArray * connections; + gs_free char * path0 = NULL; + gs_free char * path1 = NULL; + gs_free char * path2 = NULL; + gs_free char * path3 = NULL; + gs_free char * uuid2 = NULL; + gsize n_found; + gssize idx[4]; + gs_unref_variant GVariant *variant = NULL; + + g_assert(g_main_loop_get_context(gl.loop) + == (g_main_context_get_thread_default() ?: g_main_context_default())); + + /************************************************************************** + * Add three connections before starting libnm. One valid, two invalid. + *************************************************************************/ + + connection = nmtst_create_minimal_connection("test-connection-invalid-0", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + &s_con); + nmtst_connection_normalize(connection); + g_object_set(s_con, NM_SETTING_CONNECTION_UUID, nmtst_uuid_generate(), NULL); + nmtstc_service_add_connection(my_sinfo, connection, TRUE, &path0); + + nm_connection_remove_setting(connection, NM_TYPE_SETTING_WIRED); + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "test-connection-invalid-1", + NM_SETTING_CONNECTION_TYPE, + "invalid-type-1", + NM_SETTING_CONNECTION_UUID, + nmtst_uuid_generate(), + NULL); + nmtstc_service_add_connection(my_sinfo, connection, FALSE, &path1); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "test-connection-invalid-2", + NM_SETTING_CONNECTION_TYPE, + "invalid-type-2", + NM_SETTING_CONNECTION_UUID, + nmtst_uuid_generate(), + NULL); + variant = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL); + NMTST_VARIANT_EDITOR( + variant, + NMTST_VARIANT_ADD_SETTING("invalid-type-2", + nmtst_variant_new_vardict("some-key1", + g_variant_new_string("some-value1"), + "some-key2", + g_variant_new_uint32(4722)))); + g_variant_ref_sink(variant); + nmtstc_service_add_connection_variant(my_sinfo, variant, FALSE, &path2); + + client = nmtstc_client_new(TRUE); + + connections = nm_client_get_connections(client); + g_assert(connections); + + g_assert_cmpint(connections->len, ==, 3); + n_found = nmtst_find_all_indexes(connections->pdata, + connections->len, + (gpointer *) ((const char *[]){path0, path1, path2}), + 3, + _test_connection_invalid_find_connections, + NULL, + idx); + g_assert_cmpint(n_found, ==, 3); + ASSERT_IDX(0); + ASSERT_IDX(1); + ASSERT_IDX(2); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[0]]); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[1]], 0, 0); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[2]], 0, 0); + + /************************************************************************** + * After having the client up and running, add another invalid connection + *************************************************************************/ + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "test-connection-invalid-2", + NM_SETTING_CONNECTION_TYPE, + "invalid-type-2", + NM_SETTING_CONNECTION_UUID, + (uuid2 = g_strdup(nmtst_uuid_generate())), + NULL); + nmtstc_service_add_connection(my_sinfo, connection, FALSE, &path3); + + nmtst_main_loop_run(gl.loop, 1000); + + connections = nm_client_get_connections(client); + g_assert(connections); + + g_assert_cmpint(connections->len, ==, 4); + n_found = nmtst_find_all_indexes(connections->pdata, + connections->len, + (gpointer *) ((const char *[]){path0, path1, path2, path3}), + 4, + _test_connection_invalid_find_connections, + NULL, + idx); + g_assert_cmpint(n_found, ==, 4); + ASSERT_IDX(0); + ASSERT_IDX(1); + ASSERT_IDX(2); + ASSERT_IDX(3); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[0]]); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[1]], 0, 0); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[2]], 0, 0); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[3]], 0, 0); + + /************************************************************************** + * Modify the invalid connection (still invalid) + *************************************************************************/ + + NMTST_VARIANT_EDITOR(variant, + NMTST_VARIANT_CHANGE_PROPERTY("invalid-type-2", "some-key2", "u", 4721)); + g_variant_ref_sink(variant); + nmtstc_service_update_connection_variant(my_sinfo, path2, variant, FALSE); + + nmtst_main_loop_run(gl.loop, 100); + + connections = nm_client_get_connections(client); + g_assert(connections); + + g_assert_cmpint(connections->len, ==, 4); + n_found = nmtst_find_all_indexes(connections->pdata, + connections->len, + (gpointer *) ((const char *[]){path0, path1, path2, path3}), + 4, + _test_connection_invalid_find_connections, + NULL, + idx); + g_assert_cmpint(n_found, ==, 4); + ASSERT_IDX(0); + ASSERT_IDX(1); + ASSERT_IDX(2); + ASSERT_IDX(3); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[0]]); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[1]], 0, 0); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[2]], 0, 0); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[3]], 0, 0); + + /************************************************************************** + * Modify the invalid connection (becomes valid) + *************************************************************************/ + + NMTST_VARIANT_EDITOR(variant, NMTST_VARIANT_DROP_SETTING("invalid-type-2")); + NMTST_VARIANT_EDITOR(variant, + NMTST_VARIANT_CHANGE_PROPERTY(NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_TYPE, + "s", + NM_SETTING_WIRED_SETTING_NAME)); + g_variant_ref_sink(variant); + nmtstc_service_update_connection_variant(my_sinfo, path2, variant, FALSE); + + nmtst_main_loop_run(gl.loop, 100); + + connections = nm_client_get_connections(client); + g_assert(connections); + + g_assert_cmpint(connections->len, ==, 4); + n_found = nmtst_find_all_indexes(connections->pdata, + connections->len, + (gpointer *) ((const char *[]){path0, path1, path2, path3}), + 4, + _test_connection_invalid_find_connections, + NULL, + idx); + g_assert_cmpint(n_found, ==, 4); + ASSERT_IDX(0); + ASSERT_IDX(1); + ASSERT_IDX(2); + ASSERT_IDX(3); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[0]]); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[1]], 0, 0); + nmtst_assert_connection_verifies_after_normalization(connections->pdata[idx[2]], 0, 0); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[3]], 0, 0); + + /************************************************************************** + * Modify the invalid connection (still invalid) + *************************************************************************/ + + g_object_set(s_con, NM_SETTING_CONNECTION_ID, "test-connection-invalid-2x", NULL); + nmtstc_service_update_connection(my_sinfo, path3, connection, FALSE); + + nmtst_main_loop_run(gl.loop, 100); + + connections = nm_client_get_connections(client); + g_assert(connections); + + g_assert_cmpint(connections->len, ==, 4); + n_found = nmtst_find_all_indexes(connections->pdata, + connections->len, + (gpointer *) ((const char *[]){path0, path1, path2, path3}), + 4, + _test_connection_invalid_find_connections, + NULL, + idx); + g_assert_cmpint(n_found, ==, 4); + ASSERT_IDX(0); + ASSERT_IDX(1); + ASSERT_IDX(2); + ASSERT_IDX(3); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[0]]); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[1]], 0, 0); + nmtst_assert_connection_verifies_after_normalization(connections->pdata[idx[2]], 0, 0); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[3]], 0, 0); + g_assert_cmpstr("test-connection-invalid-2x", + ==, + nm_connection_get_id(connections->pdata[idx[3]])); + + /************************************************************************** + * Modify the invalid connection (now becomes valid) + *************************************************************************/ + + g_clear_object(&connection); + connection = nmtst_create_minimal_connection("test-connection-invalid-2", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + &s_con); + nmtst_connection_normalize(connection); + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "test-connection-invalid-2z", + NM_SETTING_CONNECTION_TYPE, + "802-3-ethernet", + NM_SETTING_CONNECTION_UUID, + uuid2, + NULL); + + nmtstc_service_update_connection(my_sinfo, path3, connection, FALSE); + + nmtst_main_loop_run(gl.loop, 100); + + connections = nm_client_get_connections(client); + g_assert(connections); + + g_assert_cmpint(connections->len, ==, 4); + n_found = nmtst_find_all_indexes(connections->pdata, + connections->len, + (gpointer *) ((const char *[]){path0, path1, path2, path3}), + 4, + _test_connection_invalid_find_connections, + NULL, + idx); + g_assert_cmpint(n_found, ==, 4); + ASSERT_IDX(0); + ASSERT_IDX(1); + ASSERT_IDX(2); + ASSERT_IDX(3); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[0]]); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[1]], 0, 0); + nmtst_assert_connection_verifies_after_normalization(connections->pdata[idx[2]], 0, 0); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[3]]); + g_assert_cmpstr("test-connection-invalid-2z", + ==, + nm_connection_get_id(connections->pdata[idx[3]])); + + /************************************************************************** + * Modify the invalid connection and make it valid + *************************************************************************/ + + g_clear_object(&connection); + connection = nmtst_create_minimal_connection("test-connection-invalid-1", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + &s_con); + nmtst_connection_normalize(connection); + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "test-connection-invalid-1x", + NM_SETTING_CONNECTION_TYPE, + "802-3-ethernet", + NM_SETTING_CONNECTION_UUID, + nm_connection_get_uuid(connections->pdata[idx[1]]), + NULL); + + nmtstc_service_update_connection(my_sinfo, path1, connection, FALSE); + + nmtst_main_loop_run(gl.loop, 100); + + connections = nm_client_get_connections(client); + g_assert(connections); + + g_assert_cmpint(connections->len, ==, 4); + n_found = nmtst_find_all_indexes(connections->pdata, + connections->len, + (gpointer *) ((const char *[]){path0, path1, path2, path3}), + 4, + _test_connection_invalid_find_connections, + NULL, + idx); + g_assert_cmpint(n_found, ==, 4); + ASSERT_IDX(0); + ASSERT_IDX(1); + ASSERT_IDX(2); + ASSERT_IDX(3); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[0]]); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[1]]); + nmtst_assert_connection_verifies_after_normalization(connections->pdata[idx[2]], 0, 0); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[3]]); + g_assert_cmpstr("test-connection-invalid-1x", + ==, + nm_connection_get_id(connections->pdata[idx[1]])); + g_assert_cmpstr("test-connection-invalid-2z", + ==, + nm_connection_get_id(connections->pdata[idx[3]])); + +#undef ASSERT_IDX +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + g_setenv("LIBNM_USE_SESSION_BUS", "1", TRUE); + + nmtst_init(&argc, &argv, TRUE); + + gl.loop = g_main_loop_new(NULL, FALSE); + + g_test_add_func("/libnm/device-added", test_device_added); + g_test_add_func("/libnm/device-added-signal-after-init", test_device_added_signal_after_init); + g_test_add_func("/libnm/wifi-ap-added-removed", test_wifi_ap_added_removed); + g_test_add_func("/libnm/devices-array", test_devices_array); + g_test_add_func("/libnm/client-nm-running", test_client_nm_running); + g_test_add_func("/libnm/active-connections", test_active_connections); + g_test_add_func("/libnm/activate-virtual", test_activate_virtual); + g_test_add_func("/libnm/device-connection-compatibility", test_device_connection_compatibility); + g_test_add_func("/libnm/connection/invalid", test_connection_invalid); + + return g_test_run(); +} + +/*****************************************************************************/ + +/* this header is intended to be copied to users of nm_vpn_editor_plugin_call(), + * to simplify invocation of generic functions. Include it here, to compile + * the code. */ +#include "contrib/nm-vpn-editor-plugin-call.h" diff --git a/src/libnm-client-impl/tests/test-remote-settings-client.c b/src/libnm-client-impl/tests/test-remote-settings-client.c new file mode 100644 index 00000000..e5415f54 --- /dev/null +++ b/src/libnm-client-impl/tests/test-remote-settings-client.c @@ -0,0 +1,498 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2010 - 2011 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include <sys/types.h> +#include <signal.h> + +#include "libnm-glib-aux/nm-time-utils.h" + +#include "libnm-client-test/nm-test-libnm-utils.h" + +static struct { + NMTstcServiceInfo * sinfo; + NMClient * client; + GDBusConnection * bus; + NMRemoteConnection *remote; +} gl = {}; + +/*****************************************************************************/ + +static void +add_cb(GObject *s, GAsyncResult *result, gpointer user_data) +{ + gboolean *done = user_data; + GError * error = NULL; + + gl.remote = nm_client_add_connection_finish(gl.client, result, &error); + g_assert_no_error(error); + + *done = TRUE; + g_object_add_weak_pointer(G_OBJECT(gl.remote), (void **) &gl.remote); + + /* nm_client_add_connection_finish() adds a ref to @remote, but we + * want the weak pointer to be cleared as soon as @client drops its own ref. + * So drop ours. + */ + g_object_unref(gl.remote); +} + +#define TEST_CON_ID "blahblahblah" + +static void +test_add_connection(void) +{ + NMConnection *connection; + gboolean done = FALSE; + + if (!nmtstc_service_available(gl.sinfo)) + return; + + connection = + nmtst_create_minimal_connection(TEST_CON_ID, NULL, NM_SETTING_WIRED_SETTING_NAME, NULL); + + nm_client_add_connection_async(gl.client, connection, TRUE, NULL, add_cb, &done); + + nmtst_main_context_iterate_until_assert(NULL, 5000, done); + + g_assert(gl.remote != NULL); + + /* Make sure the connection is the same as what we added */ + g_assert( + nm_connection_compare(connection, NM_CONNECTION(gl.remote), NM_SETTING_COMPARE_FLAG_EXACT) + == TRUE); + g_object_unref(connection); +} + +/*****************************************************************************/ + +static void +set_visible_cb(GObject *proxy, GAsyncResult *result, gpointer user_data) +{ + GError * error = NULL; + GVariant *ret; + + ret = g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), result, &error); + g_assert_no_error(error); + g_variant_unref(ret); +} + +static void +visible_changed_cb(GObject *object, GParamSpec *pspec, gboolean *done) +{ + if (!nm_remote_connection_get_visible(NM_REMOTE_CONNECTION(object))) + *done = TRUE; +} + +static void +connection_removed_cb(NMClient *s, NMRemoteConnection *connection, gboolean *done) +{ + if (connection == gl.remote) + *done = TRUE; +} + +static void +invis_has_settings_cb(NMSetting * setting, + const char * key, + const GValue *value, + GParamFlags flags, + gpointer user_data) +{ + *((gboolean *) user_data) = TRUE; +} + +static void +test_make_invisible(void) +{ + const GPtrArray *conns; + int i; + GDBusProxy * proxy; + gboolean visible_changed = FALSE, connection_removed = FALSE; + gboolean has_settings = FALSE; + char * path; + + if (!nmtstc_service_available(gl.sinfo)) + return; + + g_assert(gl.remote != NULL); + + /* Listen for the remove event when the connection becomes invisible */ + g_signal_connect(gl.remote, + "notify::" NM_REMOTE_CONNECTION_VISIBLE, + G_CALLBACK(visible_changed_cb), + &visible_changed); + g_signal_connect(gl.client, + "connection-removed", + G_CALLBACK(connection_removed_cb), + &connection_removed); + + path = g_strdup(nm_connection_get_path(NM_CONNECTION(gl.remote))); + proxy = g_dbus_proxy_new_sync(gl.bus, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + NM_DBUS_SERVICE, + path, + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + NULL, + NULL); + g_assert(proxy != NULL); + + /* Bypass the NMClient object so we can test it independently */ + g_dbus_proxy_call(proxy, + "SetVisible", + g_variant_new("(b)", FALSE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + set_visible_cb, + NULL); + + /* Wait for the connection to be removed */ + nmtst_main_context_iterate_until_assert(NULL, 5000, visible_changed && connection_removed); + + g_signal_handlers_disconnect_by_func(gl.remote, + G_CALLBACK(visible_changed_cb), + &visible_changed); + g_signal_handlers_disconnect_by_func(gl.client, + G_CALLBACK(connection_removed_cb), + &connection_removed); + + /* Ensure NMClient no longer has the connection */ + conns = nm_client_get_connections(gl.client); + for (i = 0; i < conns->len; i++) { + NMConnection *candidate = NM_CONNECTION(conns->pdata[i]); + + g_assert((gpointer) gl.remote != (gpointer) candidate); + g_assert(strcmp(path, nm_connection_get_path(candidate)) != 0); + } + + /* And ensure the invisible connection no longer has any settings */ + g_assert(gl.remote); + nm_connection_for_each_setting_value(NM_CONNECTION(gl.remote), + invis_has_settings_cb, + &has_settings); + g_assert(has_settings == FALSE); + + g_free(path); + g_object_unref(proxy); +} + +/*****************************************************************************/ + +static void +vis_new_connection_cb(NMClient *foo, NMRemoteConnection *connection, NMRemoteConnection **new) +{ + *new = connection; +} + +static void +test_make_visible(void) +{ + const GPtrArray *conns; + int i; + GDBusProxy * proxy; + gboolean found = FALSE; + char * path; + NMRemoteConnection *new = NULL; + + if (!nmtstc_service_available(gl.sinfo)) + return; + + g_assert(gl.remote != NULL); + + /* Wait for the new-connection signal when the connection is visible again */ + g_signal_connect(gl.client, + NM_CLIENT_CONNECTION_ADDED, + G_CALLBACK(vis_new_connection_cb), + &new); + + path = g_strdup(nm_connection_get_path(NM_CONNECTION(gl.remote))); + proxy = g_dbus_proxy_new_sync(gl.bus, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + NM_DBUS_SERVICE, + path, + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + NULL, + NULL); + g_assert(proxy != NULL); + + /* Bypass the NMClient object so we can test it independently */ + g_dbus_proxy_call(proxy, + "SetVisible", + g_variant_new("(b)", TRUE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + set_visible_cb, + NULL); + + /* Wait for the settings service to announce the connection again */ + nmtst_main_context_iterate_until_assert(NULL, 5000, new); + + /* Ensure the new connection is the same as the one we made visible again */ + g_assert(new == gl.remote); + + g_signal_handlers_disconnect_by_func(gl.client, G_CALLBACK(vis_new_connection_cb), &new); + + /* Ensure NMClient has the connection */ + conns = nm_client_get_connections(gl.client); + for (i = 0; i < conns->len; i++) { + NMConnection *candidate = NM_CONNECTION(conns->pdata[i]); + + if ((gpointer) gl.remote == (gpointer) candidate) { + g_assert_cmpstr(path, ==, nm_connection_get_path(candidate)); + g_assert_cmpstr(TEST_CON_ID, ==, nm_connection_get_id(candidate)); + found = TRUE; + break; + } + } + g_assert(found == TRUE); + + g_free(path); + g_object_unref(proxy); +} + +/*****************************************************************************/ + +static void +deleted_cb(GObject *proxy, GAsyncResult *result, gpointer user_data) +{ + GError * error = NULL; + GVariant *ret; + + ret = g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), result, &error); + g_assert_no_error(error); + g_variant_unref(ret); +} + +static void +removed_cb(NMClient *s, NMRemoteConnection *connection, gboolean *done) +{ + if (connection == gl.remote) + *done = TRUE; +} + +static void +test_remove_connection(void) +{ + NMRemoteConnection *connection; + const GPtrArray * conns; + int i; + GDBusProxy * proxy; + gboolean done = FALSE; + char * path; + + if (!nmtstc_service_available(gl.sinfo)) + return; + + /* Find a connection to delete */ + conns = nm_client_get_connections(gl.client); + g_assert_cmpint(conns->len, >, 0); + + connection = NM_REMOTE_CONNECTION(conns->pdata[0]); + g_assert(connection); + g_assert(gl.remote == connection); + path = g_strdup(nm_connection_get_path(NM_CONNECTION(connection))); + g_signal_connect(gl.client, "connection-removed", G_CALLBACK(removed_cb), &done); + + proxy = g_dbus_proxy_new_sync(gl.bus, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + NM_DBUS_SERVICE, + path, + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + NULL, + NULL); + g_assert(proxy != NULL); + + /* Bypass the NMClient object so we can test it independently */ + g_dbus_proxy_call(proxy, "Delete", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, deleted_cb, NULL); + + nmtst_main_context_iterate_until_assert(NULL, 5000, done && !gl.remote); + + /* Ensure NMClient no longer has the connection */ + conns = nm_client_get_connections(gl.client); + for (i = 0; i < conns->len; i++) { + NMConnection *candidate = NM_CONNECTION(conns->pdata[i]); + + g_assert((gpointer) connection != (gpointer) candidate); + g_assert_cmpstr(path, ==, nm_connection_get_path(candidate)); + } + + g_free(path); + g_object_unref(proxy); +} + +/*****************************************************************************/ + +#define TEST_ADD_REMOVE_ID "add-remove-test-connection" + +static void +add_remove_cb(GObject *s, GAsyncResult *result, gpointer user_data) +{ + NMRemoteConnection *connection; + gboolean * done = user_data; + gs_free_error GError *error = NULL; + + connection = nm_client_add_connection_finish(gl.client, result, &error); + g_assert_error(error, NM_CLIENT_ERROR, NM_CLIENT_ERROR_OBJECT_CREATION_FAILED); + g_assert(connection == NULL); + + *done = TRUE; +} + +static void +test_add_remove_connection(void) +{ + gs_unref_variant GVariant *ret = NULL; + GError * error = NULL; + gs_unref_object NMConnection *connection = NULL; + gboolean done = FALSE; + + if (!nmtstc_service_available(gl.sinfo)) + return; + + /* This will cause the test server to immediately delete the connection + * after creating it. + */ + ret = g_dbus_proxy_call_sync(gl.sinfo->proxy, + "AutoRemoveNextConnection", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + nmtst_assert_success(ret, error); + + connection = nmtst_create_minimal_connection(TEST_ADD_REMOVE_ID, + NULL, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + nm_client_add_connection_async(gl.client, connection, TRUE, NULL, add_remove_cb, &done); + + nmtst_main_context_iterate_until_assert(NULL, 5000, done); +} + +/*****************************************************************************/ + +static void +add_bad_cb(GObject *s, GAsyncResult *result, gpointer user_data) +{ + gboolean * done = user_data; + gs_free_error GError *error = NULL; + + gl.remote = nm_client_add_connection_finish(gl.client, result, &error); + g_assert_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); + + *done = TRUE; +} + +static void +test_add_bad_connection(void) +{ + gs_unref_object NMConnection *connection = NULL; + gboolean done = FALSE; + + if (!nmtstc_service_available(gl.sinfo)) + return; + + /* The test daemon doesn't support bond connections */ + connection = nmtst_create_minimal_connection("bad connection test", + NULL, + NM_SETTING_BOND_SETTING_NAME, + NULL); + + nm_client_add_connection_async(gl.client, connection, TRUE, NULL, add_bad_cb, &done); + g_clear_object(&connection); + + nmtst_main_context_iterate_until_assert(NULL, 5000, done); + g_assert(gl.remote == NULL); +} + +/*****************************************************************************/ + +static void +save_hostname_cb(GObject *s, GAsyncResult *result, gpointer user_data) +{ + gboolean * done = user_data; + gs_free_error GError *error = NULL; + + nm_client_save_hostname_finish(gl.client, result, &error); + g_assert_no_error(error); + + *done = TRUE; +} + +static void +test_save_hostname(void) +{ + gint64 until_ts; + gboolean done = FALSE; + GError * error = NULL; + + if (!nmtstc_service_available(gl.sinfo)) + return; + + /* test-networkmanager-service.py requires the hostname to contain a '.' */ + nm_client_save_hostname(gl.client, "foo", NULL, &error); + g_assert_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_HOSTNAME); + g_clear_error(&error); + + nm_client_save_hostname_async(gl.client, "example.com", NULL, save_hostname_cb, &done); + + until_ts = nm_utils_get_monotonic_timestamp_msec() + 5000; + while (TRUE) { + g_main_context_iteration(NULL, FALSE); + if (done) + break; + if (nm_utils_get_monotonic_timestamp_msec() >= until_ts) + g_assert_not_reached(); + } + + g_assert(gl.remote == NULL); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + int ret; + GError *error = NULL; + + g_setenv("LIBNM_USE_SESSION_BUS", "1", TRUE); + + nmtst_init(&argc, &argv, TRUE); + + gl.bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error); + nmtst_assert_success(gl.bus, error); + + gl.sinfo = nmtstc_service_init(); + + gl.client = nmtstc_client_new(TRUE); + + /* FIXME: these tests assume that they get run in order, but g_test_run() + * does not actually guarantee that! + */ + g_test_add_func("/client/add_connection", test_add_connection); + g_test_add_func("/client/make_invisible", test_make_invisible); + g_test_add_func("/client/make_visible", test_make_visible); + g_test_add_func("/client/remove_connection", test_remove_connection); + g_test_add_func("/client/add_remove_connection", test_add_remove_connection); + g_test_add_func("/client/add_bad_connection", test_add_bad_connection); + g_test_add_func("/client/save_hostname", test_save_hostname); + + ret = g_test_run(); + + nm_clear_pointer(&gl.sinfo, nmtstc_service_cleanup); + g_clear_object(&gl.client); + g_clear_object(&gl.bus); + + return ret; +} diff --git a/src/libnm-client-impl/tests/test-secret-agent.c b/src/libnm-client-impl/tests/test-secret-agent.c new file mode 100644 index 00000000..8f758f3d --- /dev/null +++ b/src/libnm-client-impl/tests/test-secret-agent.c @@ -0,0 +1,746 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2010 - 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include <sys/types.h> +#include <signal.h> + +#include "nm-secret-agent-old.h" + +#include "libnm-client-test/nm-test-libnm-utils.h" + +/*****************************************************************************/ + +enum { + SECRET_REQUESTED, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = {0}; + +typedef NMSecretAgentOld TestSecretAgent; +typedef NMSecretAgentOldClass TestSecretAgentClass; + +GType test_secret_agent_get_type(void); + +G_DEFINE_TYPE(TestSecretAgent, test_secret_agent, NM_TYPE_SECRET_AGENT_OLD) + +static void +test_secret_agent_get_secrets(NMSecretAgentOld * agent, + NMConnection * connection, + const char * connection_path, + const char * setting_name, + const char ** hints, + NMSecretAgentGetSecretsFlags flags, + NMSecretAgentOldGetSecretsFunc callback, + gpointer callback_data) +{ + NMSettingWirelessSecurity *s_wsec; + GVariant * secrets = NULL; + GVariantBuilder secrets_builder, setting_builder; + char * secret = NULL; + GError * error = NULL; + + g_assert_cmpstr(setting_name, ==, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "wpa-psk"); + g_assert_cmpstr(nm_setting_wireless_security_get_psk(s_wsec), ==, NULL); + + g_signal_emit(agent, + signals[SECRET_REQUESTED], + 0, + connection, + connection_path, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PSK, + &secret); + + if (!secret) { + error = g_error_new(NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS, "No secrets"); + goto done; + } + + if (!strcmp(secret, "CANCEL")) { + error = g_error_new(NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_USER_CANCELED, + "User canceled"); + goto done; + } + + g_variant_builder_init(&setting_builder, NM_VARIANT_TYPE_SETTING); + g_variant_builder_add(&setting_builder, + "{sv}", + NM_SETTING_WIRELESS_SECURITY_PSK, + g_variant_new_string(secret)); + + g_variant_builder_init(&secrets_builder, NM_VARIANT_TYPE_CONNECTION); + g_variant_builder_add(&secrets_builder, "{sa{sv}}", setting_name, &setting_builder); + secrets = g_variant_ref_sink(g_variant_builder_end(&secrets_builder)); + +done: + callback(agent, connection, secrets, error, callback_data); + g_clear_error(&error); + nm_clear_pointer(&secrets, g_variant_unref); + g_free(secret); +} + +static void +test_secret_agent_cancel_get_secrets(NMSecretAgentOld *agent, + const char * connection_path, + const char * setting_name) +{ + g_assert_not_reached(); +} + +static void +test_secret_agent_save_secrets(NMSecretAgentOld * agent, + NMConnection * connection, + const char * connection_path, + NMSecretAgentOldSaveSecretsFunc callback, + gpointer callback_data) +{ + g_assert_not_reached(); +} + +static void +test_secret_agent_delete_secrets(NMSecretAgentOld * agent, + NMConnection * connection, + const char * connection_path, + NMSecretAgentOldDeleteSecretsFunc callback, + gpointer callback_data) +{ + g_assert_not_reached(); +} + +static void +test_secret_agent_init(TestSecretAgent *agent) +{} + +static NMSecretAgentOld * +test_secret_agent_new(gboolean auto_register) +{ + return nmtstc_context_object_new(test_secret_agent_get_type(), + TRUE, + NM_SECRET_AGENT_OLD_IDENTIFIER, + "test-secret-agent", + NM_SECRET_AGENT_OLD_AUTO_REGISTER, + auto_register, + NULL); +} + +static void +test_secret_agent_class_init(TestSecretAgentClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMSecretAgentOldClass *agent_class = NM_SECRET_AGENT_OLD_CLASS(klass); + + agent_class->get_secrets = test_secret_agent_get_secrets; + agent_class->cancel_get_secrets = test_secret_agent_cancel_get_secrets; + agent_class->save_secrets = test_secret_agent_save_secrets; + agent_class->delete_secrets = test_secret_agent_delete_secrets; + + signals[SECRET_REQUESTED] = g_signal_new("secret-requested", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + NULL, + G_TYPE_STRING, + 4, + NM_TYPE_CONNECTION, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); +} + +/*****************************************************************************/ + +typedef struct { + NMTstcServiceInfo *sinfo; + NMClient * client; + + NMSecretAgentOld *agent; + NMDevice * device; + NMConnection * connection; + + GMainLoop *loop; + GSource * timeout_source; + + char *ifname; + char *con_id; + + int secrets_requested; +} TestSecretAgentData; + +static void +connection_added_cb(GObject *s, GAsyncResult *result, gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + NMRemoteConnection * connection; + GError * error = NULL; + + connection = nm_client_add_connection_finish(sadata->client, result, &error); + + g_assert_no_error(error); + g_assert_cmpstr(nm_connection_get_id(NM_CONNECTION(connection)), ==, sadata->con_id); + + sadata->connection = NM_CONNECTION(connection); + g_main_loop_quit(sadata->loop); +} + +static void +register_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + GError * error = NULL; + + nm_secret_agent_old_register_finish(sadata->agent, result, &error); + g_assert_no_error(error); + g_assert(nm_secret_agent_old_get_registered(sadata->agent)); + + g_main_loop_quit(sadata->loop); +} + +#define TEST_CON_ID_PREFIX "test-secret-agent" + +static void +test_setup(TestSecretAgentData *sadata, gconstpointer test_data) +{ + static int static_counter = 0; + const int counter = static_counter++; + const char * create_agent = test_data; + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWireless * s_wireless; + GBytes * ssid; + NMSetting * s_wsec; + gs_free_error GError *error = NULL; + + sadata->sinfo = nmtstc_service_init(); + if (!sadata->sinfo) + return; + + g_assert(nm_g_main_context_is_thread_default(NULL)); + + sadata->client = nmtstc_client_new(TRUE); + + g_assert(nm_g_main_context_is_thread_default(NULL)); + g_assert(nm_g_main_context_is_thread_default(nm_client_get_main_context(sadata->client))); + + sadata->loop = g_main_loop_new(NULL, FALSE); + + sadata->timeout_source = g_timeout_source_new_seconds(5); + g_source_set_callback(sadata->timeout_source, nmtst_g_source_assert_not_called, NULL, NULL); + g_source_attach(sadata->timeout_source, NULL); + + sadata->ifname = g_strdup_printf("wlan%d", counter); + sadata->con_id = g_strdup_printf("%s-%d", TEST_CON_ID_PREFIX, counter); + + sadata->device = + nmtstc_service_add_device(sadata->sinfo, sadata->client, "AddWifiDevice", sadata->ifname); + + /* Create the connection */ + connection = nmtst_create_minimal_connection(sadata->con_id, + NULL, + NM_SETTING_WIRELESS_SETTING_NAME, + &s_con); + g_object_set(s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, sadata->ifname, NULL); + + s_wireless = nm_connection_get_setting_wireless(connection); + ssid = g_bytes_new("foo", 3); + g_object_set(s_wireless, NM_SETTING_WIRELESS_SSID, ssid, NULL); + g_bytes_unref(ssid); + + s_wsec = g_object_new(NM_TYPE_SETTING_WIRELESS_SECURITY, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-psk", + NULL); + nm_connection_add_setting(connection, s_wsec); + + nm_client_add_connection_async(sadata->client, + connection, + TRUE, + NULL, + connection_added_cb, + sadata); + g_object_unref(connection); + + g_main_loop_run(sadata->loop); + g_assert(sadata->connection); + + if (nm_streq(create_agent, "1")) { + gboolean auto_register = nmtst_get_rand_bool(); + + sadata->agent = test_secret_agent_new(auto_register); + + if (auto_register) { + g_assert(nm_secret_agent_old_get_registered(sadata->agent)); + nm_secret_agent_old_register(sadata->agent, NULL, &error); + g_assert_no_error(error); + } else { + g_assert(!nm_secret_agent_old_get_registered(sadata->agent)); + nm_secret_agent_old_register_async(sadata->agent, NULL, register_cb, sadata); + g_main_loop_run(sadata->loop); + } + + g_assert(nm_secret_agent_old_get_registered(sadata->agent)); + } +} + +static void +test_cleanup(TestSecretAgentData *sadata, gconstpointer test_data) +{ + GVariant * ret; + GError * error = NULL; + NMTstContextBusyWatcherData watcher_data = {}; + + g_assert(nm_g_main_context_is_thread_default(NULL)); + + if (!sadata->sinfo) + return; + + g_assert(nm_g_main_context_is_thread_default(nm_client_get_main_context(sadata->client))); + + nmtst_context_busy_watcher_add(&watcher_data, + nm_client_get_context_busy_watcher(sadata->client)); + + if (sadata->agent) { + nmtst_context_busy_watcher_add(&watcher_data, + nm_secret_agent_old_get_context_busy_watcher(sadata->agent)); + + if (nm_secret_agent_old_get_registered(sadata->agent)) { + nm_secret_agent_old_unregister(sadata->agent, NULL, &error); + g_assert_no_error(error); + } + g_object_unref(sadata->agent); + } + + ret = + g_dbus_proxy_call_sync(sadata->sinfo->proxy, + "RemoveDevice", + g_variant_new("(s)", nm_object_get_path(NM_OBJECT(sadata->device))), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 3000, + NULL, + &error); + g_assert_no_error(error); + g_variant_unref(ret); + + g_object_unref(sadata->connection); + g_object_unref(sadata->client); + + nmtstc_service_cleanup(sadata->sinfo); + + nm_clear_g_source_inst(&sadata->timeout_source); + + g_main_loop_unref(sadata->loop); + + g_free(sadata->ifname); + g_free(sadata->con_id); + + *sadata = (TestSecretAgentData){}; + + nmtst_context_busy_watcher_wait(&watcher_data); + + while (g_main_context_iteration(NULL, FALSE)) {} + + nmtst_main_context_assert_no_dispatch(NULL, nmtst_get_rand_uint32() % 500); +} + +/*****************************************************************************/ + +static void +connection_activated_none_cb(GObject *c, GAsyncResult *result, gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + gs_free_error GError *error = NULL; + + nm_client_activate_connection_finish(sadata->client, result, &error); + g_assert_error(error, NM_AGENT_MANAGER_ERROR, NM_AGENT_MANAGER_ERROR_NO_SECRETS); + + g_main_loop_quit(sadata->loop); +} + +static void +test_secret_agent_none(TestSecretAgentData *sadata, gconstpointer test_data) +{ + if (!nmtstc_service_available(sadata->sinfo)) + return; + + nm_client_activate_connection_async(sadata->client, + sadata->connection, + sadata->device, + NULL, + NULL, + connection_activated_none_cb, + sadata); + g_main_loop_run(sadata->loop); +} + +/*****************************************************************************/ + +static char * +secrets_requested_no_secrets_cb(TestSecretAgent *agent, + NMConnection * connection, + const char * connection_path, + const char * setting_name, + const char * secret_name, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + g_assert_cmpstr(connection_path, ==, nm_connection_get_path(sadata->connection)); + sadata->secrets_requested++; + + return NULL; +} + +static void +connection_activated_no_secrets_cb(GObject *c, GAsyncResult *result, gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + gs_unref_object NMActiveConnection *ac = NULL; + gs_free_error GError *error = NULL; + + ac = nm_client_activate_connection_finish(sadata->client, result, &error); + g_assert_error(error, NM_AGENT_MANAGER_ERROR, NM_AGENT_MANAGER_ERROR_NO_SECRETS); + g_main_loop_quit(sadata->loop); +} + +static void +test_secret_agent_no_secrets(TestSecretAgentData *sadata, gconstpointer test_data) +{ + if (!nmtstc_service_available(sadata->sinfo)) + return; + + g_signal_connect(sadata->agent, + "secret-requested", + G_CALLBACK(secrets_requested_no_secrets_cb), + sadata); + + nm_client_activate_connection_async(sadata->client, + sadata->connection, + sadata->device, + NULL, + NULL, + connection_activated_no_secrets_cb, + sadata); + g_main_loop_run(sadata->loop); + + g_assert_cmpint(sadata->secrets_requested, ==, 1); +} + +/*****************************************************************************/ + +static void +connection_activated_cancel_cb(GObject *c, GAsyncResult *result, gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + gs_unref_object NMActiveConnection *ac = NULL; + gs_free_error GError *error = NULL; + + ac = nm_client_activate_connection_finish(sadata->client, result, &error); + g_assert_error(error, NM_AGENT_MANAGER_ERROR, NM_AGENT_MANAGER_ERROR_USER_CANCELED); + g_main_loop_quit(sadata->loop); +} + +static char * +secrets_requested_cancel_cb(TestSecretAgent *agent, + NMConnection * connection, + const char * connection_path, + const char * setting_name, + const char * secret_name, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + g_assert_cmpstr(connection_path, ==, nm_connection_get_path(sadata->connection)); + sadata->secrets_requested++; + + return g_strdup("CANCEL"); +} + +static void +test_secret_agent_cancel(TestSecretAgentData *sadata, gconstpointer test_data) +{ + if (!nmtstc_service_available(sadata->sinfo)) + return; + + g_signal_connect(sadata->agent, + "secret-requested", + G_CALLBACK(secrets_requested_cancel_cb), + sadata); + + nm_client_activate_connection_async(sadata->client, + sadata->connection, + sadata->device, + NULL, + NULL, + connection_activated_cancel_cb, + sadata); + g_main_loop_run(sadata->loop); + + g_assert_cmpint(sadata->secrets_requested, ==, 1); +} + +/*****************************************************************************/ + +static void +connection_activated_good_cb(GObject *c, GAsyncResult *result, gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + NMActiveConnection * ac; + GError * error = NULL; + + ac = nm_client_activate_connection_finish(sadata->client, result, &error); + g_assert_no_error(error); + + g_object_unref(ac); + + g_main_loop_quit(sadata->loop); +} + +static char * +secrets_requested_good_cb(TestSecretAgent *agent, + NMConnection * connection, + const char * connection_path, + const char * setting_name, + const char * secret_name, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + g_assert_cmpstr(connection_path, ==, nm_connection_get_path(sadata->connection)); + sadata->secrets_requested++; + + return g_strdup("password"); +} + +static void +test_secret_agent_good(TestSecretAgentData *sadata, gconstpointer test_data) +{ + if (!nmtstc_service_available(sadata->sinfo)) + return; + + g_signal_connect(sadata->agent, + "secret-requested", + G_CALLBACK(secrets_requested_good_cb), + sadata); + + nm_client_activate_connection_async(sadata->client, + sadata->connection, + sadata->device, + NULL, + NULL, + connection_activated_good_cb, + sadata); + g_main_loop_run(sadata->loop); + + g_assert_cmpint(sadata->secrets_requested, ==, 1); +} + +/*****************************************************************************/ + +static void +async_init_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + GMainLoop * loop = user_data; + gs_free_error GError *error = NULL; + gs_unref_object GObject *agent = NULL; + + agent = g_async_initable_new_finish(G_ASYNC_INITABLE(object), result, &error); + nmtst_assert_success(NM_IS_SECRET_AGENT_OLD(agent), error); + g_assert(!nm_secret_agent_old_get_registered(NM_SECRET_AGENT_OLD(agent))); + + g_main_loop_quit(loop); +} + +static void +test_secret_agent_nm_not_running(void) +{ + gs_unref_object NMSecretAgentOld *agent = NULL; + nm_auto_unref_gmainloop GMainLoop *loop = NULL; + GError * error = NULL; + + agent = g_initable_new(test_secret_agent_get_type(), + NULL, + &error, + NM_SECRET_AGENT_OLD_IDENTIFIER, + "test-secret-agent", + NULL); + nmtst_assert_success(NM_IS_SECRET_AGENT_OLD(agent), error); + g_assert(!nm_secret_agent_old_get_registered(agent)); + g_clear_object(&agent); + + loop = g_main_loop_new(NULL, FALSE); + g_async_initable_new_async(test_secret_agent_get_type(), + G_PRIORITY_DEFAULT, + NULL, + async_init_cb, + loop, + NM_SECRET_AGENT_OLD_IDENTIFIER, + "test-secret-agent", + NULL); + g_main_loop_run(loop); +} + +/*****************************************************************************/ + +typedef struct { + int step; + int invoke_count; +} AutoRegisterData; + +static void +registered_changed(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NMSecretAgentOld *agent = NM_SECRET_AGENT_OLD(object); + AutoRegisterData *data = user_data; + + g_assert(data); + g_assert(NM_IS_SECRET_AGENT_OLD(agent)); + + data->invoke_count++; + g_assert_cmpint(data->invoke_count, ==, data->step); + + switch (data->step) { + case 1: + case 3: + g_assert(nm_secret_agent_old_get_registered(agent)); + break; + case 2: + case 4: + g_assert(!nm_secret_agent_old_get_registered(agent)); + break; + default: + g_assert_not_reached(); + } +} + +static void +test_secret_agent_auto_register(void) +{ + NMTstcServiceInfo *sinfo; + gs_unref_object NMSecretAgentOld *agent = NULL; + GError * error = NULL; + AutoRegisterData auto_register_data = { + .step = 0, + .invoke_count = 0, + }; + gulong signal_id; + NMTstContextBusyWatcherData watcher_data = {}; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + agent = test_secret_agent_new(FALSE); + g_assert(!nm_secret_agent_old_get_registered(agent)); + + signal_id = g_signal_connect(agent, + "notify::" NM_SECRET_AGENT_OLD_REGISTERED, + G_CALLBACK(registered_changed), + &auto_register_data); + + if (nmtst_get_rand_bool()) { + g_object_set(agent, NM_SECRET_AGENT_OLD_AUTO_REGISTER, TRUE, NULL); + } else + nm_secret_agent_old_enable(agent, TRUE); + g_assert(!nm_secret_agent_old_get_registered(agent)); + + nm_secret_agent_old_register(agent, NULL, &error); + g_assert_no_error(error); + g_assert(!nm_secret_agent_old_get_registered(agent)); + + auto_register_data.step = 1; + nmtst_main_context_iterate_until_assert(NULL, 1000, nm_secret_agent_old_get_registered(agent)); + + auto_register_data.step = 2; + nm_secret_agent_old_enable(agent, FALSE); + g_assert(!nm_secret_agent_old_get_registered(agent)); + + nmtst_main_context_iterate_until(NULL, nmtst_get_rand_uint32() % 200, FALSE); + + g_assert(!nm_secret_agent_old_get_registered(agent)); + + nmtstc_service_cleanup(sinfo); + + g_assert(!nm_secret_agent_old_get_registered(agent)); + + nm_secret_agent_old_enable(agent, TRUE); + + g_assert(!nm_secret_agent_old_get_registered(agent)); + + nmtst_main_context_iterate_until(NULL, nmtst_get_rand_uint32() % 200, FALSE); + + g_assert(!nm_secret_agent_old_get_registered(agent)); + + sinfo = nmtstc_service_init(); + g_assert(nmtstc_service_available(sinfo)); + + auto_register_data.step = 3; + nmtst_main_context_iterate_until_assert(NULL, 1000, nm_secret_agent_old_get_registered(agent)); + + nmtstc_service_cleanup(sinfo); + + auto_register_data.step = 4; + nmtst_main_context_iterate_until_assert(NULL, 1000, !nm_secret_agent_old_get_registered(agent)); + + nm_clear_g_signal_handler(agent, &signal_id); + + nmtst_context_busy_watcher_add(&watcher_data, + nm_secret_agent_old_get_context_busy_watcher(agent)); + + g_clear_object(&agent); + + nmtst_context_busy_watcher_wait(&watcher_data); + + nmtst_main_context_assert_no_dispatch(NULL, nmtst_get_rand_uint32() % 500); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + g_setenv("LIBNM_USE_SESSION_BUS", "1", TRUE); + + nmtst_init(&argc, &argv, TRUE); + + g_test_add("/libnm/secret-agent/none", + TestSecretAgentData, + "0", + test_setup, + test_secret_agent_none, + test_cleanup); + g_test_add("/libnm/secret-agent/no-secrets", + TestSecretAgentData, + "1", + test_setup, + test_secret_agent_no_secrets, + test_cleanup); + g_test_add("/libnm/secret-agent/cancel", + TestSecretAgentData, + "1", + test_setup, + test_secret_agent_cancel, + test_cleanup); + g_test_add("/libnm/secret-agent/good", + TestSecretAgentData, + "1", + test_setup, + test_secret_agent_good, + test_cleanup); + g_test_add_func("/libnm/secret-agent/nm-not-running", test_secret_agent_nm_not_running); + g_test_add_func("/libnm/secret-agent/auto-register", test_secret_agent_auto_register); + + return g_test_run(); +} |