diff options
author | Dan Winship <danw@gnome.org> | 2014-07-24 08:53:33 -0400 |
---|---|---|
committer | Dan Winship <danw@gnome.org> | 2014-08-01 14:34:04 -0400 |
commit | d595f7843e31e8312c0baf25b476b6489cff59a7 (patch) | |
tree | 3c078ff077d3486af4aa4382189066f3cfba40fd /libnm-core | |
parent | c123a24dc472b867d9e8a0f83776d258d156f0c1 (diff) | |
download | NetworkManager-d595f7843e31e8312c0baf25b476b6489cff59a7.tar.gz |
libnm: add libnm/libnm-core (part 1)
This commit begins creating the new "libnm", which will replace
libnm-util and libnm-glib.
The main reason for the libnm-util/libnm-glib split is that the daemon
needs to link to libnm-util (to get NMSettings, NMConnection, etc),
but can't link to libnm-glib (because it uses many of the same type
names as the NetworkManager daemon. eg, NMDevice). So the daemon links
to only libnm-util, but basically all clients link to both.
With libnm, there will be only a single client-visible library, and
NetworkManager will internally link against a private "libnm-core"
containing the parts that used to be in libnm-util.
(The "libnm-core" parts still need to be in their own directory so
that the daemon can see those header files without also seeing the
ones in libnm/ that conflict with its own headers.)
[This commit just copies the source code from libnm-util/ to
libnm-core/, and libnm-glib/ to libnm/:
mkdir -p libnm-core/tests/
mkdir -p libnm/tests/
cp libnm-util/*.[ch] libnm-util/nm-version.h.in libnm-core/
rm -f libnm-core/nm-version.h libnm-core/nm-setting-template.[ch] libnm-core/nm-utils-enum-types.[ch]
cp libnm-util/tests/*.[ch] libnm-core/tests/
cp libnm-glib/*.[ch] libnm/
rm -f libnm/libnm_glib.[ch] libnm/libnm-glib-test.c libnm/nm-glib-enum-types.[ch]
cp libnm-glib/tests/*.[ch] libnm/tests/
]
Diffstat (limited to 'libnm-core')
76 files changed, 41783 insertions, 0 deletions
diff --git a/libnm-core/NetworkManager.h b/libnm-core/NetworkManager.h new file mode 100644 index 0000000000..08b4e6cf6b --- /dev/null +++ b/libnm-core/NetworkManager.h @@ -0,0 +1,563 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2004 - 2014 Red Hat, Inc. + */ + +/* Definitions related to NetworkManager's D-Bus interfaces. + * + * Note that although this header is installed as part of libnm-util, it is also + * used by some external code that does not link to libnm-util. + */ + +#ifndef NETWORK_MANAGER_H +#define NETWORK_MANAGER_H + +#include "nm-version.h" + +/* + * dbus services details + */ +#define NM_DBUS_SERVICE "org.freedesktop.NetworkManager" + +#define NM_DBUS_PATH "/org/freedesktop/NetworkManager" +#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager" +#define NM_DBUS_INTERFACE_DEVICE NM_DBUS_INTERFACE ".Device" +#define NM_DBUS_INTERFACE_DEVICE_WIRED NM_DBUS_INTERFACE_DEVICE ".Wired" +#define NM_DBUS_INTERFACE_DEVICE_ADSL NM_DBUS_INTERFACE_DEVICE ".Adsl" +#define NM_DBUS_INTERFACE_DEVICE_WIRELESS NM_DBUS_INTERFACE_DEVICE ".Wireless" +#define NM_DBUS_INTERFACE_DEVICE_BLUETOOTH NM_DBUS_INTERFACE_DEVICE ".Bluetooth" +#define NM_DBUS_INTERFACE_DEVICE_OLPC_MESH NM_DBUS_INTERFACE_DEVICE ".OlpcMesh" +#define NM_DBUS_PATH_ACCESS_POINT NM_DBUS_PATH "/AccessPoint" +#define NM_DBUS_INTERFACE_ACCESS_POINT NM_DBUS_INTERFACE ".AccessPoint" +#define NM_DBUS_INTERFACE_DEVICE_MODEM NM_DBUS_INTERFACE_DEVICE ".Modem" +#define NM_DBUS_INTERFACE_DEVICE_WIMAX NM_DBUS_INTERFACE_DEVICE ".WiMax" +#define NM_DBUS_INTERFACE_WIMAX_NSP NM_DBUS_INTERFACE ".WiMax.Nsp" +#define NM_DBUS_PATH_WIMAX_NSP NM_DBUS_PATH "/Nsp" +#define NM_DBUS_INTERFACE_ACTIVE_CONNECTION NM_DBUS_INTERFACE ".Connection.Active" +#define NM_DBUS_INTERFACE_IP4_CONFIG NM_DBUS_INTERFACE ".IP4Config" +#define NM_DBUS_INTERFACE_DHCP4_CONFIG NM_DBUS_INTERFACE ".DHCP4Config" +#define NM_DBUS_INTERFACE_IP6_CONFIG NM_DBUS_INTERFACE ".IP6Config" +#define NM_DBUS_INTERFACE_DHCP6_CONFIG NM_DBUS_INTERFACE ".DHCP6Config" +#define NM_DBUS_INTERFACE_DEVICE_INFINIBAND NM_DBUS_INTERFACE_DEVICE ".Infiniband" +#define NM_DBUS_INTERFACE_DEVICE_BOND NM_DBUS_INTERFACE_DEVICE ".Bond" +#define NM_DBUS_INTERFACE_DEVICE_TEAM NM_DBUS_INTERFACE_DEVICE ".Team" +#define NM_DBUS_INTERFACE_DEVICE_VLAN NM_DBUS_INTERFACE_DEVICE ".Vlan" +#define NM_DBUS_INTERFACE_DEVICE_BRIDGE NM_DBUS_INTERFACE_DEVICE ".Bridge" +#define NM_DBUS_INTERFACE_DEVICE_GENERIC NM_DBUS_INTERFACE_DEVICE ".Generic" +#define NM_DBUS_INTERFACE_DEVICE_VETH NM_DBUS_INTERFACE_DEVICE ".Veth" +#define NM_DBUS_INTERFACE_DEVICE_TUN NM_DBUS_INTERFACE_DEVICE ".Tun" +#define NM_DBUS_INTERFACE_DEVICE_MACVLAN NM_DBUS_INTERFACE_DEVICE ".Macvlan" +#define NM_DBUS_INTERFACE_DEVICE_VXLAN NM_DBUS_INTERFACE_DEVICE ".Vxlan" +#define NM_DBUS_INTERFACE_DEVICE_GRE NM_DBUS_INTERFACE_DEVICE ".Gre" + + +#define NM_DBUS_IFACE_SETTINGS "org.freedesktop.NetworkManager.Settings" +#define NM_DBUS_PATH_SETTINGS "/org/freedesktop/NetworkManager/Settings" + +#define NM_DBUS_IFACE_SETTINGS_CONNECTION "org.freedesktop.NetworkManager.Settings.Connection" +#define NM_DBUS_PATH_SETTINGS_CONNECTION "/org/freedesktop/NetworkManager/Settings/Connection" +#define NM_DBUS_IFACE_SETTINGS_CONNECTION_SECRETS "org.freedesktop.NetworkManager.Settings.Connection.Secrets" + +#define NM_DBUS_INTERFACE_AGENT_MANAGER NM_DBUS_INTERFACE ".AgentManager" +#define NM_DBUS_PATH_AGENT_MANAGER "/org/freedesktop/NetworkManager/AgentManager" + +#define NM_DBUS_INTERFACE_SECRET_AGENT NM_DBUS_INTERFACE ".SecretAgent" +#define NM_DBUS_PATH_SECRET_AGENT "/org/freedesktop/NetworkManager/SecretAgent" + +/** + * NMState: + * @NM_STATE_UNKNOWN: networking state is unknown + * @NM_STATE_ASLEEP: networking is not enabled + * @NM_STATE_DISCONNECTED: there is no active network connection + * @NM_STATE_DISCONNECTING: network connections are being cleaned up + * @NM_STATE_CONNECTING: a network connection is being started + * @NM_STATE_CONNECTED_LOCAL: there is only local IPv4 and/or IPv6 connectivity + * @NM_STATE_CONNECTED_SITE: there is only site-wide IPv4 and/or IPv6 connectivity + * @NM_STATE_CONNECTED_GLOBAL: there is global IPv4 and/or IPv6 Internet connectivity + * + * #NMState values indicate the current overall networking state. + * + * (Corresponds to the NM_STATE type in nm-manager.xml.) + **/ +typedef enum { + NM_STATE_UNKNOWN = 0, + NM_STATE_ASLEEP = 10, + NM_STATE_DISCONNECTED = 20, + NM_STATE_DISCONNECTING = 30, + NM_STATE_CONNECTING = 40, + NM_STATE_CONNECTED_LOCAL = 50, + NM_STATE_CONNECTED_SITE = 60, + NM_STATE_CONNECTED_GLOBAL = 70 +} NMState; + +/* For backwards compat */ +#define NM_STATE_CONNECTED NM_STATE_CONNECTED_GLOBAL + +/** + * NMConnectivityState: + * @NM_CONNECTIVITY_UNKNOWN: Network connectivity is unknown. + * @NM_CONNECTIVITY_NONE: The host is not connected to any network. + * @NM_CONNECTIVITY_PORTAL: The host is behind a captive portal and + * cannot reach the full Internet. + * @NM_CONNECTIVITY_LIMITED: The host is connected to a network, but + * does not appear to be able to reach the full Internet. + * @NM_CONNECTIVITY_FULL: The host is connected to a network, and + * appears to be able to reach the full Internet. + * + * (Corresponds to the NM_CONNECTIVITY type in nm-manager.xml.) + * + * Since: 0.9.8.6 + */ +typedef enum { + NM_CONNECTIVITY_UNKNOWN, + NM_CONNECTIVITY_NONE, + NM_CONNECTIVITY_PORTAL, + NM_CONNECTIVITY_LIMITED, + NM_CONNECTIVITY_FULL +} NMConnectivityState; + +/** + * NMDeviceType: + * @NM_DEVICE_TYPE_UNKNOWN: unknown device + * @NM_DEVICE_TYPE_GENERIC: generic support for unrecognized device types + * @NM_DEVICE_TYPE_ETHERNET: a wired ethernet device + * @NM_DEVICE_TYPE_WIFI: an 802.11 WiFi device + * @NM_DEVICE_TYPE_UNUSED1: not used + * @NM_DEVICE_TYPE_UNUSED2: not used + * @NM_DEVICE_TYPE_BT: a Bluetooth device supporting PAN or DUN access protocols + * @NM_DEVICE_TYPE_OLPC_MESH: an OLPC XO mesh networking device + * @NM_DEVICE_TYPE_WIMAX: an 802.16e Mobile WiMAX broadband device + * @NM_DEVICE_TYPE_MODEM: a modem supporting analog telephone, CDMA/EVDO, + * GSM/UMTS, or LTE network access protocols + * @NM_DEVICE_TYPE_INFINIBAND: an IP-over-InfiniBand device + * @NM_DEVICE_TYPE_BOND: a bond master interface + * @NM_DEVICE_TYPE_VLAN: an 802.1Q VLAN interface + * @NM_DEVICE_TYPE_ADSL: ADSL modem + * @NM_DEVICE_TYPE_BRIDGE: a bridge master interface + * @NM_DEVICE_TYPE_TEAM: a team master interface + * + * #NMDeviceType values indicate the type of hardware represented by + * an #NMDevice. + * + * (Corresponds to the NM_DEVICE_TYPE type in nm-device.xml.) + **/ +typedef enum { + NM_DEVICE_TYPE_UNKNOWN = 0, + NM_DEVICE_TYPE_ETHERNET = 1, + NM_DEVICE_TYPE_WIFI = 2, + NM_DEVICE_TYPE_UNUSED1 = 3, + NM_DEVICE_TYPE_UNUSED2 = 4, + NM_DEVICE_TYPE_BT = 5, /* Bluetooth */ + NM_DEVICE_TYPE_OLPC_MESH = 6, + NM_DEVICE_TYPE_WIMAX = 7, + NM_DEVICE_TYPE_MODEM = 8, + NM_DEVICE_TYPE_INFINIBAND = 9, + NM_DEVICE_TYPE_BOND = 10, + NM_DEVICE_TYPE_VLAN = 11, + NM_DEVICE_TYPE_ADSL = 12, + NM_DEVICE_TYPE_BRIDGE = 13, + NM_DEVICE_TYPE_GENERIC = 14, + NM_DEVICE_TYPE_TEAM = 15, +} NMDeviceType; + +/** + * NMDeviceCapabilities: + * @NM_DEVICE_CAP_NONE: device has no special capabilities + * @NM_DEVICE_CAP_NM_SUPPORTED: NetworkManager supports this device + * @NM_DEVICE_CAP_CARRIER_DETECT: this device can indicate carrier status + * @NM_DEVICE_CAP_IS_SOFTWARE: this device is a software device + * + * General device capability flags. + * + * (Corresponds to the NM_DEVICE_CAP type in nm-device-wifi.xml.) + **/ +typedef enum { + NM_DEVICE_CAP_NONE = 0x00000000, + NM_DEVICE_CAP_NM_SUPPORTED = 0x00000001, + NM_DEVICE_CAP_CARRIER_DETECT = 0x00000002, + NM_DEVICE_CAP_IS_SOFTWARE = 0x00000004, +} NMDeviceCapabilities; + + +/** + * NMDeviceWifiCapabilities: + * @NM_WIFI_DEVICE_CAP_NONE: device has no encryption/authentication capabilities + * @NM_WIFI_DEVICE_CAP_CIPHER_WEP40: device supports 40/64-bit WEP encryption + * @NM_WIFI_DEVICE_CAP_CIPHER_WEP104: device supports 104/128-bit WEP encryption + * @NM_WIFI_DEVICE_CAP_CIPHER_TKIP: device supports TKIP encryption + * @NM_WIFI_DEVICE_CAP_CIPHER_CCMP: device supports AES/CCMP encryption + * @NM_WIFI_DEVICE_CAP_WPA: device supports WPA1 authentication + * @NM_WIFI_DEVICE_CAP_RSN: device supports WPA2/RSN authentication + * @NM_WIFI_DEVICE_CAP_AP: device supports Access Point mode + * @NM_WIFI_DEVICE_CAP_ADHOC: device supports Ad-Hoc mode + * + * 802.11 specific device encryption and authentication capabilities. + * + * (Corresponds to the NM_802_11_DEVICE_CAP type in nm-device-wifi.xml.) + **/ +typedef enum { + NM_WIFI_DEVICE_CAP_NONE = 0x00000000, + NM_WIFI_DEVICE_CAP_CIPHER_WEP40 = 0x00000001, + NM_WIFI_DEVICE_CAP_CIPHER_WEP104 = 0x00000002, + NM_WIFI_DEVICE_CAP_CIPHER_TKIP = 0x00000004, + NM_WIFI_DEVICE_CAP_CIPHER_CCMP = 0x00000008, + NM_WIFI_DEVICE_CAP_WPA = 0x00000010, + NM_WIFI_DEVICE_CAP_RSN = 0x00000020, + NM_WIFI_DEVICE_CAP_AP = 0x00000040, + NM_WIFI_DEVICE_CAP_ADHOC = 0x00000080 +} NMDeviceWifiCapabilities; + + +/** + * NM80211ApFlags: + * @NM_802_11_AP_FLAGS_NONE: access point has no special capabilities + * @NM_802_11_AP_FLAGS_PRIVACY: access point requires authentication and + * encryption (usually means WEP) + * + * 802.11 access point flags. + * + * (Corresponds to the NM_802_11_AP_FLAGS type in nm-access-point.xml.) + **/ +typedef enum { + NM_802_11_AP_FLAGS_NONE = 0x00000000, + NM_802_11_AP_FLAGS_PRIVACY = 0x00000001 +} NM80211ApFlags; + +/** + * NM80211ApSecurityFlags: + * @NM_802_11_AP_SEC_NONE: the access point has no special security requirements + * @NM_802_11_AP_SEC_PAIR_WEP40: 40/64-bit WEP is supported for + * pairwise/unicast encryption + * @NM_802_11_AP_SEC_PAIR_WEP104: 104/128-bit WEP is supported for + * pairwise/unicast encryption + * @NM_802_11_AP_SEC_PAIR_TKIP: TKIP is supported for pairwise/unicast encryption + * @NM_802_11_AP_SEC_PAIR_CCMP: AES/CCMP is supported for pairwise/unicast encryption + * @NM_802_11_AP_SEC_GROUP_WEP40: 40/64-bit WEP is supported for group/broadcast + * encryption + * @NM_802_11_AP_SEC_GROUP_WEP104: 104/128-bit WEP is supported for + * group/broadcast encryption + * @NM_802_11_AP_SEC_GROUP_TKIP: TKIP is supported for group/broadcast encryption + * @NM_802_11_AP_SEC_GROUP_CCMP: AES/CCMP is supported for group/broadcast + * encryption + * @NM_802_11_AP_SEC_KEY_MGMT_PSK: WPA/RSN Pre-Shared Key encryption is + * supported + * @NM_802_11_AP_SEC_KEY_MGMT_802_1X: 802.1x authentication and key management + * is supported + * + * 802.11 access point security and authentication flags. These flags describe + * the current security requirements of an access point as determined from the + * access point's beacon. + * + * (Corresponds to the NM_802_11_AP_SEC type in nm-access-point.xml.) + **/ +typedef enum { + NM_802_11_AP_SEC_NONE = 0x00000000, + NM_802_11_AP_SEC_PAIR_WEP40 = 0x00000001, + NM_802_11_AP_SEC_PAIR_WEP104 = 0x00000002, + NM_802_11_AP_SEC_PAIR_TKIP = 0x00000004, + NM_802_11_AP_SEC_PAIR_CCMP = 0x00000008, + NM_802_11_AP_SEC_GROUP_WEP40 = 0x00000010, + NM_802_11_AP_SEC_GROUP_WEP104 = 0x00000020, + NM_802_11_AP_SEC_GROUP_TKIP = 0x00000040, + NM_802_11_AP_SEC_GROUP_CCMP = 0x00000080, + NM_802_11_AP_SEC_KEY_MGMT_PSK = 0x00000100, + NM_802_11_AP_SEC_KEY_MGMT_802_1X = 0x00000200 +} NM80211ApSecurityFlags; + +/** + * NM80211Mode: + * @NM_802_11_MODE_UNKNOWN: the device or access point mode is unknown + * @NM_802_11_MODE_ADHOC: for both devices and access point objects, indicates + * the object is part of an Ad-Hoc 802.11 network without a central + * coordinating access point. + * @NM_802_11_MODE_INFRA: the device or access point is in infrastructure mode. + * For devices, this indicates the device is an 802.11 client/station. For + * access point objects, this indicates the object is an access point that + * provides connectivity to clients. + * @NM_802_11_MODE_AP: the device is an access point/hotspot. Not valid for + * access point objects; used only for hotspot mode on the local machine. + * + * Indicates the 802.11 mode an access point or device is currently in. + * + * (Corresponds to the NM_802_11_MODE type in generic-types.xml.) + **/ +typedef enum { + NM_802_11_MODE_UNKNOWN = 0, + NM_802_11_MODE_ADHOC, + NM_802_11_MODE_INFRA, + NM_802_11_MODE_AP +} NM80211Mode; + +/** + * NMBluetoothCapabilities: + * @NM_BT_CAPABILITY_NONE: device has no usable capabilities + * @NM_BT_CAPABILITY_DUN: device provides Dial-Up Networking capability + * @NM_BT_CAPABILITY_NAP: device provides Network Access Point capability + * + * #NMBluetoothCapabilities values indicate the usable capabilities of a + * Bluetooth device. + * + * (Corresponds to the NM_BT_CAPABILITY type in nm-device-bt.xml.) + **/ +typedef enum { + NM_BT_CAPABILITY_NONE = 0x00000000, + NM_BT_CAPABILITY_DUN = 0x00000001, + NM_BT_CAPABILITY_NAP = 0x00000002, +} NMBluetoothCapabilities; + +/** + * NMDeviceModemCapabilities: + * @NM_DEVICE_MODEM_CAPABILITY_NONE: modem has no usable capabilities + * @NM_DEVICE_MODEM_CAPABILITY_POTS: modem uses the analog wired telephone + * network and is not a wireless/cellular device + * @NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO: modem supports at least one of CDMA + * 1xRTT, EVDO revision 0, EVDO revision A, or EVDO revision B + * @NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS: modem supports at least one of GSM, + * GPRS, EDGE, UMTS, HSDPA, HSUPA, or HSPA+ packet switched data capability + * @NM_DEVICE_MODEM_CAPABILITY_LTE: modem has LTE data capability + * + * #NMDeviceModemCapabilities values indicate the generic radio access + * technology families a modem device supports. For more information on the + * specific access technologies the device supports use the ModemManager D-Bus + * API. + * + * (Corresponds to the NM_DEVICE_MODEM_CAPABILITY type in nm-device-modem.xml.) + **/ +typedef enum { + NM_DEVICE_MODEM_CAPABILITY_NONE = 0x00000000, + NM_DEVICE_MODEM_CAPABILITY_POTS = 0x00000001, + NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO = 0x00000002, + NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS = 0x00000004, + NM_DEVICE_MODEM_CAPABILITY_LTE = 0x00000008, +} NMDeviceModemCapabilities; + + +/** + * NMDeviceState: + * @NM_DEVICE_STATE_UNKNOWN: the device's state is unknown + * @NM_DEVICE_STATE_UNMANAGED: the device is recognized, but not managed by + * NetworkManager + * @NM_DEVICE_STATE_UNAVAILABLE: the device is managed by NetworkManager, but + * is not available for use. Reasons may include the wireless switched off, + * missing firmware, no ethernet carrier, missing supplicant or modem manager, + * etc. + * @NM_DEVICE_STATE_DISCONNECTED: the device can be activated, but is currently + * idle and not connected to a network. + * @NM_DEVICE_STATE_PREPARE: the device is preparing the connection to the + * network. This may include operations like changing the MAC address, + * setting physical link properties, and anything else required to connect + * to the requested network. + * @NM_DEVICE_STATE_CONFIG: the device is connecting to the requested network. + * This may include operations like associating with the WiFi AP, dialing + * the modem, connecting to the remote Bluetooth device, etc. + * @NM_DEVICE_STATE_NEED_AUTH: the device requires more information to continue + * connecting to the requested network. This includes secrets like WiFi + * passphrases, login passwords, PIN codes, etc. + * @NM_DEVICE_STATE_IP_CONFIG: the device is requesting IPv4 and/or IPv6 + * addresses and routing information from the network. + * @NM_DEVICE_STATE_IP_CHECK: the device is checking whether further action is + * required for the requested network connection. This may include checking + * whether only local network access is available, whether a captive portal + * is blocking access to the Internet, etc. + * @NM_DEVICE_STATE_SECONDARIES: the device is waiting for a secondary + * connection (like a VPN) which must activated before the device can be + * activated + * @NM_DEVICE_STATE_ACTIVATED: the device has a network connection, either local + * or global. + * @NM_DEVICE_STATE_DEACTIVATING: a disconnection from the current network + * connection was requested, and the device is cleaning up resources used for + * that connection. The network connection may still be valid. + * @NM_DEVICE_STATE_FAILED: the device failed to connect to the requested + * network and is cleaning up the connection request + * + * (Corresponds to the NM_DEVICE_STATE type in nm-device.xml.) + **/ +typedef enum { + NM_DEVICE_STATE_UNKNOWN = 0, + NM_DEVICE_STATE_UNMANAGED = 10, + NM_DEVICE_STATE_UNAVAILABLE = 20, + NM_DEVICE_STATE_DISCONNECTED = 30, + NM_DEVICE_STATE_PREPARE = 40, + NM_DEVICE_STATE_CONFIG = 50, + NM_DEVICE_STATE_NEED_AUTH = 60, + NM_DEVICE_STATE_IP_CONFIG = 70, + NM_DEVICE_STATE_IP_CHECK = 80, + NM_DEVICE_STATE_SECONDARIES = 90, + NM_DEVICE_STATE_ACTIVATED = 100, + NM_DEVICE_STATE_DEACTIVATING = 110, + NM_DEVICE_STATE_FAILED = 120 +} NMDeviceState; + + +/** + * NMDeviceStateReason: + * @NM_DEVICE_STATE_REASON_NONE: No reason given + * @NM_DEVICE_STATE_REASON_UNKNOWN: Unknown error + * @NM_DEVICE_STATE_REASON_NOW_MANAGED: Device is now managed + * @NM_DEVICE_STATE_REASON_NOW_UNMANAGED: Device is now unmanaged + * @NM_DEVICE_STATE_REASON_CONFIG_FAILED: The device could not be readied for configuration + * @NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE: IP configuration could not be reserved (no available address, timeout, etc) + * @NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED: The IP config is no longer valid + * @NM_DEVICE_STATE_REASON_NO_SECRETS: Secrets were required, but not provided + * @NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT: 802.1x supplicant disconnected + * @NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED: 802.1x supplicant configuration failed + * @NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED: 802.1x supplicant failed + * @NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT: 802.1x supplicant took too long to authenticate + * @NM_DEVICE_STATE_REASON_PPP_START_FAILED: PPP service failed to start + * @NM_DEVICE_STATE_REASON_PPP_DISCONNECT: PPP service disconnected + * @NM_DEVICE_STATE_REASON_PPP_FAILED: PPP failed + * @NM_DEVICE_STATE_REASON_DHCP_START_FAILED: DHCP client failed to start + * @NM_DEVICE_STATE_REASON_DHCP_ERROR: DHCP client error + * @NM_DEVICE_STATE_REASON_DHCP_FAILED: DHCP client failed + * @NM_DEVICE_STATE_REASON_SHARED_START_FAILED: Shared connection service failed to start + * @NM_DEVICE_STATE_REASON_SHARED_FAILED: Shared connection service failed + * @NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED: AutoIP service failed to start + * @NM_DEVICE_STATE_REASON_AUTOIP_ERROR: AutoIP service error + * @NM_DEVICE_STATE_REASON_AUTOIP_FAILED: AutoIP service failed + * @NM_DEVICE_STATE_REASON_MODEM_BUSY: The line is busy + * @NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE: No dial tone + * @NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER: No carrier could be established + * @NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT: The dialing request timed out + * @NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED: The dialing attempt failed + * @NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED: Modem initialization failed + * @NM_DEVICE_STATE_REASON_GSM_APN_FAILED: Failed to select the specified APN + * @NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING: Not searching for networks + * @NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED: Network registration denied + * @NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT: Network registration timed out + * @NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED: Failed to register with the requested network + * @NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED: PIN check failed + * @NM_DEVICE_STATE_REASON_FIRMWARE_MISSING: Necessary firmware for the device may be missing + * @NM_DEVICE_STATE_REASON_REMOVED: The device was removed + * @NM_DEVICE_STATE_REASON_SLEEPING: NetworkManager went to sleep + * @NM_DEVICE_STATE_REASON_CONNECTION_REMOVED: The device's active connection disappeared + * @NM_DEVICE_STATE_REASON_USER_REQUESTED: Device disconnected by user or client + * @NM_DEVICE_STATE_REASON_CARRIER: Carrier/link changed + * @NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED: The device's existing connection was assumed + * @NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE: The supplicant is now available + * @NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND: The modem could not be found + * @NM_DEVICE_STATE_REASON_BT_FAILED: The Bluetooth connection failed or timed out + * @NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED: GSM Modem's SIM Card not inserted + * @NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: GSM Modem's SIM Pin required + * @NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED: GSM Modem's SIM Puk required + * @NM_DEVICE_STATE_REASON_GSM_SIM_WRONG: GSM Modem's SIM wrong + * @NM_DEVICE_STATE_REASON_INFINIBAND_MODE: InfiniBand device does not support connected mode + * @NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED: A dependency of the connection failed + * @NM_DEVICE_STATE_REASON_BR2684_FAILED: Problem with the RFC 2684 Ethernet over ADSL bridge + * @NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE: ModemManager not running + * @NM_DEVICE_STATE_REASON_SSID_NOT_FOUND: The WiFi network could not be found + * @NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED: A secondary connection of the base connection failed + * @NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED: DCB or FCoE setup failed + * @NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED: teamd control failed + * @NM_DEVICE_STATE_REASON_MODEM_FAILED: Modem failed or no longer available + * @NM_DEVICE_STATE_REASON_MODEM_AVAILABLE: Modem now ready and available + * @NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT: SIM PIN was incorrect + * + * Device state change reason codes + * + * (Corresponds to the NM_DEVICE_STATE_REASON type in nm-device.xml.) + */ +typedef enum { + NM_DEVICE_STATE_REASON_NONE = 0, + NM_DEVICE_STATE_REASON_UNKNOWN = 1, + NM_DEVICE_STATE_REASON_NOW_MANAGED = 2, + NM_DEVICE_STATE_REASON_NOW_UNMANAGED = 3, + NM_DEVICE_STATE_REASON_CONFIG_FAILED = 4, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE = 5, + NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED = 6, + NM_DEVICE_STATE_REASON_NO_SECRETS = 7, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT = 8, + NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED = 9, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED = 10, + NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT = 11, + NM_DEVICE_STATE_REASON_PPP_START_FAILED = 12, + NM_DEVICE_STATE_REASON_PPP_DISCONNECT = 13, + NM_DEVICE_STATE_REASON_PPP_FAILED = 14, + NM_DEVICE_STATE_REASON_DHCP_START_FAILED = 15, + NM_DEVICE_STATE_REASON_DHCP_ERROR = 16, + NM_DEVICE_STATE_REASON_DHCP_FAILED = 17, + NM_DEVICE_STATE_REASON_SHARED_START_FAILED = 18, + NM_DEVICE_STATE_REASON_SHARED_FAILED = 19, + NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED = 20, + NM_DEVICE_STATE_REASON_AUTOIP_ERROR = 21, + NM_DEVICE_STATE_REASON_AUTOIP_FAILED = 22, + NM_DEVICE_STATE_REASON_MODEM_BUSY = 23, + NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE = 24, + NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER = 25, + NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT = 26, + NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED = 27, + NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED = 28, + NM_DEVICE_STATE_REASON_GSM_APN_FAILED = 29, + NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING = 30, + NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED = 31, + NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT = 32, + NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED = 33, + NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED = 34, + NM_DEVICE_STATE_REASON_FIRMWARE_MISSING = 35, + NM_DEVICE_STATE_REASON_REMOVED = 36, + NM_DEVICE_STATE_REASON_SLEEPING = 37, + NM_DEVICE_STATE_REASON_CONNECTION_REMOVED = 38, + NM_DEVICE_STATE_REASON_USER_REQUESTED = 39, + NM_DEVICE_STATE_REASON_CARRIER = 40, + NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED = 41, + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE = 42, + NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND = 43, + NM_DEVICE_STATE_REASON_BT_FAILED = 44, + NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED = 45, + NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED = 46, + NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED = 47, + NM_DEVICE_STATE_REASON_GSM_SIM_WRONG = 48, + NM_DEVICE_STATE_REASON_INFINIBAND_MODE = 49, + NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED = 50, + NM_DEVICE_STATE_REASON_BR2684_FAILED = 51, + NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE = 52, + NM_DEVICE_STATE_REASON_SSID_NOT_FOUND = 53, + NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED = 54, + NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED = 55, + NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED = 56, + NM_DEVICE_STATE_REASON_MODEM_FAILED = 57, + NM_DEVICE_STATE_REASON_MODEM_AVAILABLE = 58, + NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT = 59, + + NM_DEVICE_STATE_REASON_LAST = 0xFFFF +} NMDeviceStateReason; + + +/** + * NMActiveConnectionState: + * @NM_ACTIVE_CONNECTION_STATE_UNKNOWN: the state of the connection is unknown + * @NM_ACTIVE_CONNECTION_STATE_ACTIVATING: a network connection is being prepared + * @NM_ACTIVE_CONNECTION_STATE_ACTIVATED: there is a connection to the network + * @NM_ACTIVE_CONNECTION_STATE_DEACTIVATING: the network connection is being + * torn down and cleaned up + * @NM_ACTIVE_CONNECTION_STATE_DEACTIVATED: the network connection is disconnected + * and will be removed + * + * #NMActiveConnectionState values indicate the state of a connection to a + * specific network while it is starting, connected, or disconnecting from that + * network. + * + * (Corresponds to the NM_ACTIVE_CONNECTION_STATE type in nm-active-connection.xml.) + **/ +typedef enum { + NM_ACTIVE_CONNECTION_STATE_UNKNOWN = 0, + NM_ACTIVE_CONNECTION_STATE_ACTIVATING, + NM_ACTIVE_CONNECTION_STATE_ACTIVATED, + NM_ACTIVE_CONNECTION_STATE_DEACTIVATING, + NM_ACTIVE_CONNECTION_STATE_DEACTIVATED +} NMActiveConnectionState; + +#endif /* NETWORK_MANAGER_H */ diff --git a/libnm-core/NetworkManagerVPN.h b/libnm-core/NetworkManagerVPN.h new file mode 100644 index 0000000000..f316572a08 --- /dev/null +++ b/libnm-core/NetworkManagerVPN.h @@ -0,0 +1,302 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2004 Red Hat, Inc. + */ + +/* D-Bus-related definitions for NetworkManager VPN plugins. + * + * Note that although this header is installed as part of libnm-util, it is also + * used by some external code that does not link to libnm-util. + */ + +#ifndef NETWORK_MANAGER_VPN_H +#define NETWORK_MANAGER_VPN_H + +/* + * dbus services details + */ +#define NM_DBUS_PATH_VPN "/org/freedesktop/NetworkManager/VPN/Manager" +#define NM_DBUS_INTERFACE_VPN "org.freedesktop.NetworkManager.VPN.Manager" + +#define NM_DBUS_PATH_VPN_CONNECTION "/org/freedesktop/NetworkManager/VPN/Connection" +#define NM_DBUS_INTERFACE_VPN_CONNECTION "org.freedesktop.NetworkManager.VPN.Connection" + +#define NM_VPN_DBUS_PLUGIN_PATH "/org/freedesktop/NetworkManager/VPN/Plugin" +#define NM_VPN_DBUS_PLUGIN_INTERFACE "org.freedesktop.NetworkManager.VPN.Plugin" + +/* + * VPN Errors + */ +#define NM_DBUS_NO_ACTIVE_VPN_CONNECTION "org.freedesktop.NetworkManager.VPNConnections.NoActiveVPNConnection" +#define NM_DBUS_NO_VPN_CONNECTIONS "org.freedesktop.NetworkManager.VPNConnections.NoVPNConnections" +#define NM_DBUS_INVALID_VPN_CONNECTION "org.freedesktop.NetworkManager.VPNConnections.InvalidVPNConnection" + +#define NM_DBUS_VPN_ERROR_PREFIX "org.freedesktop.NetworkManager.VPN.Error" +#define NM_DBUS_VPN_STARTING_IN_PROGRESS "StartingInProgress" +#define NM_DBUS_VPN_ALREADY_STARTED "AlreadyStarted" +#define NM_DBUS_VPN_STOPPING_IN_PROGRESS "StoppingInProgress" +#define NM_DBUS_VPN_ALREADY_STOPPED "AlreadyStopped" +#define NM_DBUS_VPN_WRONG_STATE "WrongState" +#define NM_DBUS_VPN_BAD_ARGUMENTS "BadArguments" +#define NM_DBUS_VPN_INTERACTIVE_NOT_SUPPORTED "InteractiveNotSupported" + + +/* + * VPN daemon signals + */ +#define NM_DBUS_VPN_SIGNAL_LOGIN_BANNER "LoginBanner" +#define NM_DBUS_VPN_SIGNAL_LOGIN_FAILED "LoginFailed" +#define NM_DBUS_VPN_SIGNAL_LAUNCH_FAILED "LaunchFailed" +#define NM_DBUS_VPN_SIGNAL_CONNECT_FAILED "ConnectFailed" +#define NM_DBUS_VPN_SIGNAL_VPN_CONFIG_BAD "VPNConfigBad" +#define NM_DBUS_VPN_SIGNAL_IP_CONFIG_BAD "IPConfigBad" +#define NM_DBUS_VPN_SIGNAL_STATE_CHANGE "StateChange" +#define NM_DBUS_VPN_SIGNAL_IP4_CONFIG "IP4Config" + +/** + * NMVPNServiceState: + * @NM_VPN_SERVICE_STATE_UNKNOWN: The state of the VPN plugin is unknown. + * @NM_VPN_SERVICE_STATE_INIT: The VPN plugin is initialized. + * @NM_VPN_SERVICE_STATE_SHUTDOWN: Not used. + * @NM_VPN_SERVICE_STATE_STARTING: The plugin is attempting to connect to a VPN server. + * @NM_VPN_SERVICE_STATE_STARTED: The plugin has connected to a VPN server. + * @NM_VPN_SERVICE_STATE_STOPPING: The plugin is disconnecting from the VPN server. + * @NM_VPN_SERVICE_STATE_STOPPED: The plugin has disconnected from the VPN server. + * + * VPN daemon states + * + * (Corresponds to the NM_VPN_SERVICE_STATE type in nm-vpn-connection.xml.) + */ +typedef enum NMVPNServiceState { + NM_VPN_SERVICE_STATE_UNKNOWN = 0, + NM_VPN_SERVICE_STATE_INIT, + NM_VPN_SERVICE_STATE_SHUTDOWN, + NM_VPN_SERVICE_STATE_STARTING, + NM_VPN_SERVICE_STATE_STARTED, + NM_VPN_SERVICE_STATE_STOPPING, + NM_VPN_SERVICE_STATE_STOPPED +} NMVPNServiceState; + + +/** + * NMVPNConnectionState: + * @NM_VPN_CONNECTION_STATE_UNKNOWN: The state of the VPN connection is + * unknown. + * @NM_VPN_CONNECTION_STATE_PREPARE: The VPN connection is preparing to + * connect. + * @NM_VPN_CONNECTION_STATE_NEED_AUTH: The VPN connection needs authorization + * credentials. + * @NM_VPN_CONNECTION_STATE_CONNECT: The VPN connection is being established. + * @NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: The VPN connection is getting an IP + * address. + * @NM_VPN_CONNECTION_STATE_ACTIVATED: The VPN connection is active. + * @NM_VPN_CONNECTION_STATE_FAILED: The VPN connection failed. + * @NM_VPN_CONNECTION_STATE_DISCONNECTED: The VPN connection is disconnected. + * + * VPN connection states + * + * (Corresponds to the NM_VPN_CONNECTION_STATE type in nm-vpn-connection.xml.) + */ +typedef enum NMVPNConnectionState { + NM_VPN_CONNECTION_STATE_UNKNOWN = 0, + NM_VPN_CONNECTION_STATE_PREPARE, + NM_VPN_CONNECTION_STATE_NEED_AUTH, + NM_VPN_CONNECTION_STATE_CONNECT, + NM_VPN_CONNECTION_STATE_IP_CONFIG_GET, + NM_VPN_CONNECTION_STATE_ACTIVATED, + NM_VPN_CONNECTION_STATE_FAILED, + NM_VPN_CONNECTION_STATE_DISCONNECTED +} NMVPNConnectionState; + +/** + * NMVPNConnectionStateReason: + * @NM_VPN_CONNECTION_STATE_REASON_UNKNOWN: The reason for the VPN connection + * state change is unknown. + * @NM_VPN_CONNECTION_STATE_REASON_NONE: No reason was given for the VPN + * connection state change. + * @NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED: The VPN connection changed + * state because the user disconnected it. + * @NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED: The VPN connection + * changed state because the device it was using was disconnected. + * @NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED: The service providing the + * VPN connection was stopped. + * @NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID: The IP config of the VPN + * connection was invalid. + * @NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT: The connection attempt to + * the VPN service timed out. + * @NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT: A timeout occurred + * while starting the service providing the VPN connection. + * @NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED: Starting the service + * starting the service providing the VPN connection failed. + * @NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS: Necessary secrets for the VPN + * connection were not provided. + * @NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED: Authentication to the VPN + * server failed. + * @NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED: The connection was + * deleted from settings. + * + * VPN connection state reasons + * + * (Corresponds to the NM_VPN_CONNECTION_STATE_REASON type in nm-vpn-connection.xml.) + */ +typedef enum NMVPNConnectionStateReason { + NM_VPN_CONNECTION_STATE_REASON_UNKNOWN = 0, + NM_VPN_CONNECTION_STATE_REASON_NONE, + NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED, + NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED, + NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED, + NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID, + NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, + NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT, + NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, + NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, + NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED, + NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED +} NMVPNConnectionStateReason; + +/** + * NMVPNPluginFailure: + * @NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED: Login failed. + * @NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED: Connect failed. + * @NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG: Invalid IP configuration returned from + * the VPN plugin. + * + * VPN plugin failure reasons + * + * (Corresponds to the NM_VPN_PLUGIN_FAILURE type in nm-vpn-plugin.xml.) + */ +typedef enum { + NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED, + NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED, + NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG +} NMVPNPluginFailure; + + +/*** Generic config ***/ + +/* string: VPN interface name (tun0, tap0, etc) */ +#define NM_VPN_PLUGIN_CONFIG_TUNDEV "tundev" + +/* string: Login message */ +#define NM_VPN_PLUGIN_CONFIG_BANNER "banner" + +/* uint32 / array of uint8: IP address of the public external VPN gateway (network byte order) */ +#define NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY "gateway" + +/* uint32: Maximum Transfer Unit that the VPN interface should use */ +#define NM_VPN_PLUGIN_CONFIG_MTU "mtu" + +/* boolean: Has IP4 configuration? */ +#define NM_VPN_PLUGIN_CONFIG_HAS_IP4 "has-ip4" + +/* boolean: Has IP6 configuration? */ +#define NM_VPN_PLUGIN_CONFIG_HAS_IP6 "has-ip6" + + +/*** Ip4Config ***/ + +/* uint32: IP address of the internal gateway of the subnet the VPN interface is + * on, if the VPN uses subnet configuration (network byte order) + */ +#define NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY "internal-gateway" + +/* uint32: internal IP address of the local VPN interface (network byte order) */ +#define NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS "address" + +/* uint32: IP address of the other side of Point-to-Point connection if the VPN + * uses Point-to-Point configuration. (network byte order) + */ +#define NM_VPN_PLUGIN_IP4_CONFIG_PTP "ptp" + +/* uint32: IP prefix of the VPN interface; 1 - 32 inclusive */ +#define NM_VPN_PLUGIN_IP4_CONFIG_PREFIX "prefix" + +/* array of uint32: IP addresses of DNS servers for the VPN (network byte order) */ +#define NM_VPN_PLUGIN_IP4_CONFIG_DNS "dns" + +/* array of uint32: IP addresses of NBNS/WINS servers for the VPN (network byte order) */ +#define NM_VPN_PLUGIN_IP4_CONFIG_NBNS "nbns" + +/* uint32: Message Segment Size that the VPN interface should use */ +#define NM_VPN_PLUGIN_IP4_CONFIG_MSS "mss" + +/* string: DNS domain name */ +#define NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN "domain" + +/* array of strings: DNS domain names */ +#define NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS "domains" + +/* [ip4 routes]: custom routes the client should apply, in the format used + * by nm_utils_ip4_routes_to/from_gvalue + */ +#define NM_VPN_PLUGIN_IP4_CONFIG_ROUTES "routes" + +/* boolean: prevent this VPN connection from ever getting the default route */ +#define NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT "never-default" + +/* Deprecated */ +#define NM_VPN_PLUGIN_IP4_CONFIG_GATEWAY NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY + +/* Legacy IP4 items; these are included in the IP4 config by older plugins, + * but in the generic config by newer plugins. + */ + +#define NM_VPN_PLUGIN_IP4_CONFIG_BANNER NM_VPN_PLUGIN_CONFIG_BANNER +#define NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY +#define NM_VPN_PLUGIN_IP4_CONFIG_MTU NM_VPN_PLUGIN_CONFIG_MTU +#define NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV NM_VPN_PLUGIN_CONFIG_TUNDEV + + +/*** Ip6Config ***/ + +/* array of uint8: IP address of the internal gateway of the subnet the VPN interface is + * on, if the VPN uses subnet configuration (network byte order) + */ +#define NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY "internal-gateway" + +/* array of uint8: internal IP address of the local VPN interface (network byte order) */ +#define NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS "address" + +/* array of uint8: IP address of the other side of Point-to-Point connection if the VPN + * uses Point-to-Point configuration. (network byte order) + */ +#define NM_VPN_PLUGIN_IP6_CONFIG_PTP "ptp" + +/* uint32: prefix length of the VPN interface; 1 - 128 inclusive */ +#define NM_VPN_PLUGIN_IP6_CONFIG_PREFIX "prefix" + +/* array of array of uint8: IP addresses of DNS servers for the VPN (network byte order) */ +#define NM_VPN_PLUGIN_IP6_CONFIG_DNS "dns" + +/* uint32: Message Segment Size that the VPN interface should use */ +#define NM_VPN_PLUGIN_IP6_CONFIG_MSS "mss" + +/* string: DNS domain name */ +#define NM_VPN_PLUGIN_IP6_CONFIG_DOMAIN "domain" + +/* array of strings: DNS domain names */ +#define NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS "domains" + +/* [ip6 routes]: custom routes the client should apply, in the format used + * by nm_utils_ip6_routes_to/from_gvalue + */ +#define NM_VPN_PLUGIN_IP6_CONFIG_ROUTES "routes" + +/* boolean: prevent this VPN connection from ever getting the default route */ +#define NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT "never-default" + +#endif /* NETWORK_MANAGER_VPN_H */ diff --git a/libnm-core/crypto.c b/libnm-core/crypto.c new file mode 100644 index 0000000000..d5f7608d6e --- /dev/null +++ b/libnm-core/crypto.c @@ -0,0 +1,749 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * Dan Williams <dcbw@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2011 Red Hat, Inc. + */ + +#include "config.h" + +#include <glib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <stdlib.h> +#include <glib/gi18n.h> + +#include "crypto.h" + +GQuark +_nm_crypto_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-crypto-error-quark"); + return quark; +} + + +#define PEM_RSA_KEY_BEGIN "-----BEGIN RSA PRIVATE KEY-----" +#define PEM_RSA_KEY_END "-----END RSA PRIVATE KEY-----" + +#define PEM_DSA_KEY_BEGIN "-----BEGIN DSA PRIVATE KEY-----" +#define PEM_DSA_KEY_END "-----END DSA PRIVATE KEY-----" + +#define PEM_CERT_BEGIN "-----BEGIN CERTIFICATE-----" +#define PEM_CERT_END "-----END CERTIFICATE-----" + +#define PEM_PKCS8_ENC_KEY_BEGIN "-----BEGIN ENCRYPTED PRIVATE KEY-----" +#define PEM_PKCS8_ENC_KEY_END "-----END ENCRYPTED PRIVATE KEY-----" + +#define PEM_PKCS8_DEC_KEY_BEGIN "-----BEGIN PRIVATE KEY-----" +#define PEM_PKCS8_DEC_KEY_END "-----END PRIVATE KEY-----" + +static gboolean +find_tag (const char *tag, + const GByteArray *array, + gsize start_at, + gsize *out_pos) +{ + gsize i, taglen; + gsize len = array->len - start_at; + + g_return_val_if_fail (out_pos != NULL, FALSE); + + taglen = strlen (tag); + if (len >= taglen) { + for (i = 0; i < len - taglen + 1; i++) { + if (memcmp (array->data + start_at + i, tag, taglen) == 0) { + *out_pos = start_at + i; + return TRUE; + } + } + } + return FALSE; +} + +#define DEK_INFO_TAG "DEK-Info: " +#define PROC_TYPE_TAG "Proc-Type: " + +static GByteArray * +parse_old_openssl_key_file (const GByteArray *contents, + int key_type, + char **out_cipher, + char **out_iv, + GError **error) +{ + GByteArray *bindata = NULL; + char **lines = NULL; + char **ln = NULL; + gsize start = 0, end = 0; + GString *str = NULL; + int enc_tags = 0; + char *iv = NULL; + char *cipher = NULL; + unsigned char *tmp = NULL; + gsize tmp_len = 0; + const char *start_tag; + const char *end_tag; + guint8 save_end = 0; + + switch (key_type) { + case NM_CRYPTO_KEY_TYPE_RSA: + start_tag = PEM_RSA_KEY_BEGIN; + end_tag = PEM_RSA_KEY_END; + break; + case NM_CRYPTO_KEY_TYPE_DSA: + start_tag = PEM_DSA_KEY_BEGIN; + end_tag = PEM_DSA_KEY_END; + break; + default: + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_UNKNOWN_KEY_TYPE, + "Unknown key type %d", + key_type); + g_assert_not_reached (); + return NULL; + } + + if (!find_tag (start_tag, contents, 0, &start)) + goto parse_error; + + start += strlen (start_tag); + if (!find_tag (end_tag, contents, start, &end)) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("PEM key file had no end tag '%s'."), + end_tag); + goto parse_error; + } + + save_end = contents->data[end]; + contents->data[end] = '\0'; + lines = g_strsplit ((const char *) (contents->data + start), "\n", 0); + contents->data[end] = save_end; + + if (!lines || g_strv_length (lines) <= 1) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("Doesn't look like a PEM private key file.")); + goto parse_error; + } + + str = g_string_new_len (NULL, end - start); + for (ln = lines; *ln; ln++) { + char *p = *ln; + + /* Chug leading spaces */ + p = g_strstrip (p); + if (!*p) + continue; + + if (!strncmp (p, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG))) { + if (enc_tags++ != 0) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("Malformed PEM file: Proc-Type was not first tag.")); + goto parse_error; + } + + p += strlen (PROC_TYPE_TAG); + if (strcmp (p, "4,ENCRYPTED")) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("Malformed PEM file: unknown Proc-Type tag '%s'."), + p); + goto parse_error; + } + } else if (!strncmp (p, DEK_INFO_TAG, strlen (DEK_INFO_TAG))) { + char *comma; + + if (enc_tags++ != 1) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("Malformed PEM file: DEK-Info was not the second tag.")); + goto parse_error; + } + + p += strlen (DEK_INFO_TAG); + + /* Grab the IV first */ + comma = strchr (p, ','); + if (!comma || (*(comma + 1) == '\0')) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("Malformed PEM file: no IV found in DEK-Info tag.")); + goto parse_error; + } + *comma++ = '\0'; + if (!g_ascii_isxdigit (*comma)) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("Malformed PEM file: invalid format of IV in DEK-Info tag.")); + goto parse_error; + } + iv = g_strdup (comma); + + /* Get the private key cipher */ + if (!strcasecmp (p, "DES-EDE3-CBC")) { + cipher = g_strdup (p); + } else if (!strcasecmp (p, "DES-CBC")) { + cipher = g_strdup (p); + } else if (!strcasecmp (p, "AES-128-CBC")) { + cipher = g_strdup (p); + } else { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_UNKNOWN_KEY_TYPE, + _("Malformed PEM file: unknown private key cipher '%s'."), + p); + goto parse_error; + } + } else { + if ((enc_tags != 0) && (enc_tags != 2)) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + "Malformed PEM file: both Proc-Type and DEK-Info tags are required."); + goto parse_error; + } + g_string_append (str, p); + } + } + + tmp = g_base64_decode (str->str, &tmp_len); + if (tmp == NULL || !tmp_len) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_DECODE_FAILED, + _("Could not decode private key.")); + goto parse_error; + } + g_string_free (str, TRUE); + + if (lines) + g_strfreev (lines); + + bindata = g_byte_array_sized_new (tmp_len); + g_byte_array_append (bindata, tmp, tmp_len); + g_free (tmp); + + *out_iv = iv; + *out_cipher = cipher; + return bindata; + +parse_error: + g_free (tmp); + g_free (cipher); + g_free (iv); + if (str) + g_string_free (str, TRUE); + if (lines) + g_strfreev (lines); + return NULL; +} + +static GByteArray * +parse_pkcs8_key_file (const GByteArray *contents, + gboolean *out_encrypted, + GError **error) +{ + GByteArray *key = NULL; + gsize start = 0, end = 0; + unsigned char *der = NULL; + guint8 save_end; + gsize length = 0; + const char *start_tag = NULL, *end_tag = NULL; + gboolean encrypted = FALSE; + + /* Try encrypted first, decrypted next */ + if (find_tag (PEM_PKCS8_ENC_KEY_BEGIN, contents, 0, &start)) { + start_tag = PEM_PKCS8_ENC_KEY_BEGIN; + end_tag = PEM_PKCS8_ENC_KEY_END; + encrypted = TRUE; + } else if (find_tag (PEM_PKCS8_DEC_KEY_BEGIN, contents, 0, &start)) { + start_tag = PEM_PKCS8_DEC_KEY_BEGIN; + end_tag = PEM_PKCS8_DEC_KEY_END; + encrypted = FALSE; + } else { + g_set_error_literal (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("Failed to find expected PKCS#8 start tag.")); + return NULL; + } + + start += strlen (start_tag); + if (!find_tag (end_tag, contents, start, &end)) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("Failed to find expected PKCS#8 end tag '%s'."), + end_tag); + return NULL; + } + + /* g_base64_decode() wants a NULL-terminated string */ + save_end = contents->data[end]; + contents->data[end] = '\0'; + der = g_base64_decode ((const char *) (contents->data + start), &length); + contents->data[end] = save_end; + + if (der && length) { + key = g_byte_array_sized_new (length); + g_byte_array_append (key, der, length); + g_assert (key->len == length); + *out_encrypted = encrypted; + } else { + g_set_error_literal (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_DECODE_FAILED, + _("Failed to decode PKCS#8 private key.")); + } + + g_free (der); + return key; +} + +static GByteArray * +file_to_g_byte_array (const char *filename, GError **error) +{ + char *contents; + GByteArray *array = NULL; + gsize length = 0; + + if (g_file_get_contents (filename, &contents, &length, error)) { + array = g_byte_array_sized_new (length); + g_byte_array_append (array, (guint8 *) contents, length); + g_assert (array->len == length); + g_free (contents); + } + return array; +} + +/* + * Convert a hex string into bytes. + */ +static char * +convert_iv (const char *src, + gsize *out_len, + GError **error) +{ + int num; + int i; + char conv[3]; + char *c; + + g_return_val_if_fail (src != NULL, NULL); + + num = strlen (src); + if (num % 2) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_RAW_IV_INVALID, + _("IV must be an even number of bytes in length.")); + return NULL; + } + + num /= 2; + c = g_malloc0 (num + 1); + + conv[2] = '\0'; + for (i = 0; i < num; i++) { + conv[0] = src[(i * 2)]; + conv[1] = src[(i * 2) + 1]; + if (!g_ascii_isxdigit (conv[0]) || !g_ascii_isxdigit (conv[1])) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_RAW_IV_INVALID, + _("IV contains non-hexadecimal digits.")); + goto error; + } + + c[i] = strtol(conv, NULL, 16); + } + *out_len = num; + return c; + +error: + g_free (c); + return NULL; +} + +static char * +make_des_aes_key (const char *cipher, + const char *salt, + const gsize salt_len, + const char *password, + gsize *out_len, + GError **error) +{ + char *key; + guint32 digest_len; + + g_return_val_if_fail (cipher != NULL, NULL); + g_return_val_if_fail (salt != NULL, NULL); + g_return_val_if_fail (salt_len >= 8, NULL); + g_return_val_if_fail (password != NULL, NULL); + g_return_val_if_fail (out_len != NULL, NULL); + + if (!strcmp (cipher, "DES-EDE3-CBC")) + digest_len = 24; + else if (!strcmp (cipher, "DES-CBC")) + digest_len = 8; + else if (!strcmp (cipher, "AES-128-CBC")) + digest_len = 16; + else { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_UNKNOWN_CIPHER, + _("Private key cipher '%s' was unknown."), + cipher); + return NULL; + } + + if (password[0] == '\0') + return NULL; + + key = g_malloc0 (digest_len + 1); + + if (!crypto_md5_hash (salt, + salt_len, + password, + strlen (password), + key, + digest_len, + error)) + goto error; + + *out_len = digest_len; + return key; + +error: + if (key) { + /* Don't leak stale key material */ + memset (key, 0, digest_len); + g_free (key); + } + return NULL; +} + +static GByteArray * +decrypt_key (const char *cipher, + int key_type, + GByteArray *data, + const char *iv, + const char *password, + GError **error) +{ + char *bin_iv = NULL; + gsize bin_iv_len = 0; + char *key = NULL; + gsize key_len = 0; + char *output = NULL; + gsize decrypted_len = 0; + GByteArray *decrypted = NULL; + + g_return_val_if_fail (password != NULL, NULL); + + bin_iv = convert_iv (iv, &bin_iv_len, error); + if (!bin_iv) + return NULL; + + /* Convert the password and IV into a DES or AES key */ + key = make_des_aes_key (cipher, bin_iv, bin_iv_len, password, &key_len, error); + if (!key || !key_len) + goto out; + + output = crypto_decrypt (cipher, key_type, + data, + bin_iv, bin_iv_len, + key, key_len, + &decrypted_len, + error); + if (output && decrypted_len) { + decrypted = g_byte_array_sized_new (decrypted_len); + g_byte_array_append (decrypted, (guint8 *) output, decrypted_len); + } + +out: + /* Don't leak stale key material */ + if (key) + memset (key, 0, key_len); + g_free (output); + g_free (key); + g_free (bin_iv); + + return decrypted; +} + +GByteArray * +crypto_decrypt_private_key_data (const GByteArray *contents, + const char *password, + NMCryptoKeyType *out_key_type, + GError **error) +{ + GByteArray *decrypted = NULL; + NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_RSA; + GByteArray *data; + char *iv = NULL; + char *cipher = NULL; + + g_return_val_if_fail (contents != NULL, NULL); + if (out_key_type) + g_return_val_if_fail (*out_key_type == NM_CRYPTO_KEY_TYPE_UNKNOWN, NULL); + + /* OpenSSL non-standard legacy PEM files */ + + /* Try RSA keys first */ + data = parse_old_openssl_key_file (contents, key_type, &cipher, &iv, error); + if (!data) { + g_clear_error (error); + + /* DSA next */ + key_type = NM_CRYPTO_KEY_TYPE_DSA; + data = parse_old_openssl_key_file (contents, key_type, &cipher, &iv, error); + if (!data) { + g_clear_error (error); + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("Unable to determine private key type.")); + } + } + + if (data) { + /* return the key type even if decryption failed */ + if (out_key_type) + *out_key_type = key_type; + + if (password) { + decrypted = decrypt_key (cipher, + key_type, + data, + iv, + password, + error); + } + g_byte_array_free (data, TRUE); + } + + g_free (cipher); + g_free (iv); + + return decrypted; +} + +GByteArray * +crypto_decrypt_private_key (const char *file, + const char *password, + NMCryptoKeyType *out_key_type, + GError **error) +{ + GByteArray *contents; + GByteArray *key = NULL; + + contents = file_to_g_byte_array (file, error); + if (contents) { + key = crypto_decrypt_private_key_data (contents, password, out_key_type, error); + g_byte_array_free (contents, TRUE); + } + return key; +} + +static GByteArray * +extract_pem_cert_data (GByteArray *contents, GError **error) +{ + GByteArray *cert = NULL; + gsize start = 0, end = 0; + unsigned char *der = NULL; + guint8 save_end; + gsize length = 0; + + if (!find_tag (PEM_CERT_BEGIN, contents, 0, &start)) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("PEM certificate had no start tag '%s'."), + PEM_CERT_BEGIN); + goto done; + } + + start += strlen (PEM_CERT_BEGIN); + if (!find_tag (PEM_CERT_END, contents, start, &end)) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("PEM certificate had no end tag '%s'."), + PEM_CERT_END); + goto done; + } + + /* g_base64_decode() wants a NULL-terminated string */ + save_end = contents->data[end]; + contents->data[end] = '\0'; + der = g_base64_decode ((const char *) (contents->data + start), &length); + contents->data[end] = save_end; + + if (der && length) { + cert = g_byte_array_sized_new (length); + g_byte_array_append (cert, der, length); + g_assert (cert->len == length); + } else { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_DECODE_FAILED, + _("Failed to decode certificate.")); + } + +done: + g_free (der); + return cert; +} + +GByteArray * +crypto_load_and_verify_certificate (const char *file, + NMCryptoFileFormat *out_file_format, + GError **error) +{ + GByteArray *array, *contents; + + g_return_val_if_fail (file != NULL, NULL); + g_return_val_if_fail (out_file_format != NULL, NULL); + g_return_val_if_fail (*out_file_format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, NULL); + + contents = file_to_g_byte_array (file, error); + if (!contents) + return NULL; + + /* Check for PKCS#12 */ + if (crypto_is_pkcs12_data (contents)) { + *out_file_format = NM_CRYPTO_FILE_FORMAT_PKCS12; + return contents; + } + + /* Check for plain DER format */ + if (contents->len > 2 && contents->data[0] == 0x30 && contents->data[1] == 0x82) { + *out_file_format = crypto_verify_cert (contents->data, contents->len, error); + } else { + array = extract_pem_cert_data (contents, error); + if (!array) { + g_byte_array_free (contents, TRUE); + return NULL; + } + + *out_file_format = crypto_verify_cert (array->data, array->len, error); + g_byte_array_free (array, TRUE); + } + + if (*out_file_format != NM_CRYPTO_FILE_FORMAT_X509) { + g_byte_array_free (contents, TRUE); + contents = NULL; + } + + return contents; +} + +gboolean +crypto_is_pkcs12_data (const GByteArray *data) +{ + GError *error = NULL; + gboolean success; + + g_return_val_if_fail (data != NULL, FALSE); + + success = crypto_verify_pkcs12 (data, NULL, &error); + if (success == FALSE) { + /* If the error was just a decryption error, then it's pkcs#12 */ + if (error) { + if (g_error_matches (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED)) + success = TRUE; + g_error_free (error); + } + } + return success; +} + +gboolean +crypto_is_pkcs12_file (const char *file, GError **error) +{ + GByteArray *contents; + gboolean success = FALSE; + + g_return_val_if_fail (file != NULL, FALSE); + + contents = file_to_g_byte_array (file, error); + if (contents) { + success = crypto_is_pkcs12_data (contents); + g_byte_array_free (contents, TRUE); + } + return success; +} + +/* Verifies that a private key can be read, and if a password is given, that + * the private key can be decrypted with that password. + */ +NMCryptoFileFormat +crypto_verify_private_key_data (const GByteArray *contents, + const char *password, + GError **error) +{ + GByteArray *tmp; + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + NMCryptoKeyType ktype = NM_CRYPTO_KEY_TYPE_UNKNOWN; + gboolean is_encrypted = FALSE; + + g_return_val_if_fail (contents != NULL, FALSE); + + /* Check for PKCS#12 first */ + if (crypto_is_pkcs12_data (contents)) { + if (!password || crypto_verify_pkcs12 (contents, password, error)) + format = NM_CRYPTO_FILE_FORMAT_PKCS12; + } else { + /* Maybe it's PKCS#8 */ + tmp = parse_pkcs8_key_file (contents, &is_encrypted, error); + if (tmp) { + if (crypto_verify_pkcs8 (tmp, is_encrypted, password, error)) + format = NM_CRYPTO_FILE_FORMAT_RAW_KEY; + } else { + g_clear_error (error); + + /* Or it's old-style OpenSSL */ + tmp = crypto_decrypt_private_key_data (contents, password, &ktype, error); + if (tmp) + format = NM_CRYPTO_FILE_FORMAT_RAW_KEY; + else if (!password && (ktype != NM_CRYPTO_KEY_TYPE_UNKNOWN)) + format = NM_CRYPTO_FILE_FORMAT_RAW_KEY; + } + + if (tmp) { + /* Don't leave decrypted key data around */ + memset (tmp->data, 0, tmp->len); + g_byte_array_free (tmp, TRUE); + } + } + + return format; +} + +NMCryptoFileFormat +crypto_verify_private_key (const char *filename, + const char *password, + GError **error) +{ + GByteArray *contents; + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + + g_return_val_if_fail (filename != NULL, FALSE); + + contents = file_to_g_byte_array (filename, error); + if (contents) { + format = crypto_verify_private_key_data (contents, password, error); + g_byte_array_free (contents, TRUE); + } + return format; +} diff --git a/libnm-core/crypto.h b/libnm-core/crypto.h new file mode 100644 index 0000000000..edb79007f1 --- /dev/null +++ b/libnm-core/crypto.h @@ -0,0 +1,145 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * Dan Williams <dcbw@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + */ + +#ifndef __CRYPTO_H__ +#define __CRYPTO_H__ + +#include <glib.h> + +#define MD5_HASH_LEN 20 +#define CIPHER_DES_EDE3_CBC "DES-EDE3-CBC" +#define CIPHER_DES_CBC "DES-CBC" +#define CIPHER_AES_CBC "AES-128-CBC" + +enum { + NM_CRYPTO_ERR_NONE = 0, + NM_CRYPTO_ERR_INIT_FAILED, + NM_CRYPTO_ERR_CANT_READ_FILE, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + NM_CRYPTO_ERR_CERT_FORMAT_INVALID, + NM_CRYPTO_ERR_DECODE_FAILED, + NM_CRYPTO_ERR_OUT_OF_MEMORY, + NM_CRYPTO_ERR_UNKNOWN_KEY_TYPE, + NM_CRYPTO_ERR_UNKNOWN_CIPHER, + NM_CRYPTO_ERR_RAW_IV_INVALID, + NM_CRYPTO_ERR_MD5_INIT_FAILED, + NM_CRYPTO_ERR_CIPHER_INIT_FAILED, + NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED, + NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED, + NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, + NM_CRYPTO_ERR_INVALID_PASSWORD, + NM_CRYPTO_ERR_CIPHER_ENCRYPT_FAILED, + NM_CRYPTO_ERR_RANDOMIZE_FAILED +}; + +typedef enum { + NM_CRYPTO_KEY_TYPE_UNKNOWN = 0, + NM_CRYPTO_KEY_TYPE_RSA, + NM_CRYPTO_KEY_TYPE_DSA +} NMCryptoKeyType; + +typedef enum { + NM_CRYPTO_FILE_FORMAT_UNKNOWN = 0, + NM_CRYPTO_FILE_FORMAT_X509, + NM_CRYPTO_FILE_FORMAT_RAW_KEY, + NM_CRYPTO_FILE_FORMAT_PKCS12 +} NMCryptoFileFormat; + +#define NM_CRYPTO_ERROR _nm_crypto_error_quark () +GQuark _nm_crypto_error_quark (void); + +gboolean crypto_init (GError **error); + +void crypto_deinit (void); + +GByteArray *crypto_decrypt_private_key_data (const GByteArray *contents, + const char *password, + NMCryptoKeyType *out_key_type, + GError **error); + +GByteArray *crypto_decrypt_private_key (const char *file, + const char *password, + NMCryptoKeyType *out_key_type, + GError **error); + +GByteArray *crypto_load_and_verify_certificate (const char *file, + NMCryptoFileFormat *out_file_format, + GError **error); + +gboolean crypto_is_pkcs12_file (const char *file, GError **error); + +gboolean crypto_is_pkcs12_data (const GByteArray *data); + +NMCryptoFileFormat crypto_verify_private_key_data (const GByteArray *contents, + const char *password, + GError **error); + +NMCryptoFileFormat crypto_verify_private_key (const char *file, + const char *password, + GError **error); + +/* Internal utils API bits for crypto providers */ + +gboolean crypto_md5_hash (const char *salt, + const gsize salt_len, + const char *password, + gsize password_len, + char *buffer, + gsize buflen, + GError **error); + +char * crypto_decrypt (const char *cipher, + int key_type, + GByteArray *data, + const char *iv, + const gsize iv_len, + const char *key, + const gsize key_len, + gsize *out_len, + GError **error); + +char * crypto_encrypt (const char *cipher, + const GByteArray *data, + const char *iv, + gsize iv_len, + const char *key, + gsize key_len, + gsize *out_len, + GError **error); + +gboolean crypto_randomize (void *buffer, gsize buffer_len, GError **error); + +NMCryptoFileFormat crypto_verify_cert (const unsigned char *data, + gsize len, + GError **error); + +gboolean crypto_verify_pkcs12 (const GByteArray *data, + const char *password, + GError **error); + +gboolean crypto_verify_pkcs8 (const GByteArray *data, + gboolean is_encrypted, + const char *password, + GError **error); + +#endif /* __CRYPTO_H__ */ diff --git a/libnm-core/crypto_gnutls.c b/libnm-core/crypto_gnutls.c new file mode 100644 index 0000000000..be16d135af --- /dev/null +++ b/libnm-core/crypto_gnutls.c @@ -0,0 +1,493 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager Wireless Applet -- Display wireless access points and allow user control + * + * Dan Williams <dcbw@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2009 Red Hat, Inc. + */ + +#include "config.h" +#include <glib.h> +#include <glib/gi18n.h> + +#include <gcrypt.h> +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> +#include <gnutls/pkcs12.h> + +#include "crypto.h" + +#define SALT_LEN 8 + +static gboolean initialized = FALSE; + +gboolean +crypto_init (GError **error) +{ + if (initialized) + return TRUE; + + if (gnutls_global_init() != 0) { + gnutls_global_deinit(); + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_INIT_FAILED, + "%s", + _("Failed to initialize the crypto engine.")); + return FALSE; + } + + initialized = TRUE; + return TRUE; +} + +void +crypto_deinit (void) +{ +} + +gboolean +crypto_md5_hash (const char *salt, + const gsize salt_len, + const char *password, + gsize password_len, + char *buffer, + gsize buflen, + GError **error) +{ + gcry_md_hd_t ctx; + gcry_error_t err; + int nkey = buflen; + const gsize digest_len = 16; + int count = 0; + char digest[MD5_HASH_LEN]; + char *p = buffer; + + if (salt) + g_return_val_if_fail (salt_len >= SALT_LEN, FALSE); + + g_return_val_if_fail (password != NULL, FALSE); + g_return_val_if_fail (password_len > 0, FALSE); + g_return_val_if_fail (buffer != NULL, FALSE); + g_return_val_if_fail (buflen > 0, FALSE); + + err = gcry_md_open (&ctx, GCRY_MD_MD5, 0); + if (err) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_MD5_INIT_FAILED, + _("Failed to initialize the MD5 engine: %s / %s."), + gcry_strsource (err), gcry_strerror (err)); + return FALSE; + } + + while (nkey > 0) { + int i = 0; + + if (count++) + gcry_md_write (ctx, digest, digest_len); + gcry_md_write (ctx, password, password_len); + if (salt) + gcry_md_write (ctx, salt, SALT_LEN); /* Only use 8 bytes of salt */ + gcry_md_final (ctx); + memcpy (digest, gcry_md_read (ctx, 0), digest_len); + gcry_md_reset (ctx); + + while (nkey && (i < digest_len)) { + *(p++) = digest[i++]; + nkey--; + } + } + + memset (digest, 0, sizeof (digest)); + gcry_md_close (ctx); + return TRUE; +} + +char * +crypto_decrypt (const char *cipher, + int key_type, + GByteArray *data, + const char *iv, + const gsize iv_len, + const char *key, + const gsize key_len, + gsize *out_len, + GError **error) +{ + gcry_cipher_hd_t ctx; + gcry_error_t err; + int cipher_mech, i; + char *output = NULL; + gboolean success = FALSE; + gsize pad_len, real_iv_len; + + if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) { + cipher_mech = GCRY_CIPHER_3DES; + real_iv_len = SALT_LEN; + } else if (!strcmp (cipher, CIPHER_DES_CBC)) { + cipher_mech = GCRY_CIPHER_DES; + real_iv_len = SALT_LEN; + } else if (!strcmp (cipher, CIPHER_AES_CBC)) { + cipher_mech = GCRY_CIPHER_AES; + real_iv_len = 16; + } else { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_UNKNOWN_CIPHER, + _("Private key cipher '%s' was unknown."), + cipher); + return NULL; + } + + if (iv_len < real_iv_len) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_RAW_IV_INVALID, + _("Invalid IV length (must be at least %zd)."), + real_iv_len); + return NULL; + } + + output = g_malloc0 (data->len); + + err = gcry_cipher_open (&ctx, cipher_mech, GCRY_CIPHER_MODE_CBC, 0); + if (err) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_INIT_FAILED, + _("Failed to initialize the decryption cipher context: %s / %s."), + gcry_strsource (err), gcry_strerror (err)); + goto out; + } + + err = gcry_cipher_setkey (ctx, key, key_len); + if (err) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED, + _("Failed to set symmetric key for decryption: %s / %s."), + gcry_strsource (err), gcry_strerror (err)); + goto out; + } + + err = gcry_cipher_setiv (ctx, iv, iv_len); + if (err) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED, + _("Failed to set IV for decryption: %s / %s."), + gcry_strsource (err), gcry_strerror (err)); + goto out; + } + + err = gcry_cipher_decrypt (ctx, output, data->len, data->data, data->len); + if (err) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, + _("Failed to decrypt the private key: %s / %s."), + gcry_strsource (err), gcry_strerror (err)); + goto out; + } + pad_len = output[data->len - 1]; + + /* Check if the padding at the end of the decrypted data is valid */ + if (pad_len == 0 || pad_len > real_iv_len) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, + _("Failed to decrypt the private key: unexpected padding length.")); + goto out; + } + + /* Validate tail padding; last byte is the padding size, and all pad bytes + * should contain the padding size. + */ + for (i = 1; i <= pad_len; ++i) { + if (output[data->len - i] != pad_len) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, + _("Failed to decrypt the private key.")); + goto out; + } + } + + *out_len = data->len - pad_len; + success = TRUE; + +out: + if (!success) { + if (output) { + /* Don't expose key material */ + memset (output, 0, data->len); + g_free (output); + output = NULL; + } + } + gcry_cipher_close (ctx); + return output; +} + +char * +crypto_encrypt (const char *cipher, + const GByteArray *data, + const char *iv, + const gsize iv_len, + const char *key, + gsize key_len, + gsize *out_len, + GError **error) +{ + gcry_cipher_hd_t ctx; + gcry_error_t err; + int cipher_mech; + char *output = NULL; + gboolean success = FALSE; + gsize padded_buf_len, pad_len, output_len; + char *padded_buf = NULL; + guint32 i; + gsize salt_len; + + if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) { + cipher_mech = GCRY_CIPHER_3DES; + salt_len = SALT_LEN; + } else if (!strcmp (cipher, CIPHER_AES_CBC)) { + cipher_mech = GCRY_CIPHER_AES; + salt_len = iv_len; + } else { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_UNKNOWN_CIPHER, + _("Private key cipher '%s' was unknown."), + cipher); + return NULL; + } + + /* If data->len % ivlen == 0, then we add another complete block + * onto the end so that the decrypter knows there's padding. + */ + pad_len = iv_len - (data->len % iv_len); + output_len = padded_buf_len = data->len + pad_len; + padded_buf = g_malloc0 (padded_buf_len); + + memcpy (padded_buf, data->data, data->len); + for (i = 0; i < pad_len; i++) + padded_buf[data->len + i] = (guint8) (pad_len & 0xFF); + + output = g_malloc0 (output_len); + + err = gcry_cipher_open (&ctx, cipher_mech, GCRY_CIPHER_MODE_CBC, 0); + if (err) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_INIT_FAILED, + _("Failed to initialize the encryption cipher context: %s / %s."), + gcry_strsource (err), gcry_strerror (err)); + goto out; + } + + err = gcry_cipher_setkey (ctx, key, key_len); + if (err) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED, + _("Failed to set symmetric key for encryption: %s / %s."), + gcry_strsource (err), gcry_strerror (err)); + goto out; + } + + /* gcrypt only wants 8 bytes of the IV (same as the DES block length) */ + err = gcry_cipher_setiv (ctx, iv, salt_len); + if (err) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED, + _("Failed to set IV for encryption: %s / %s."), + gcry_strsource (err), gcry_strerror (err)); + goto out; + } + + err = gcry_cipher_encrypt (ctx, output, output_len, padded_buf, padded_buf_len); + if (err) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, + _("Failed to encrypt the data: %s / %s."), + gcry_strsource (err), gcry_strerror (err)); + goto out; + } + + *out_len = output_len; + success = TRUE; + +out: + if (padded_buf) { + memset (padded_buf, 0, padded_buf_len); + g_free (padded_buf); + padded_buf = NULL; + } + + if (!success) { + if (output) { + /* Don't expose key material */ + memset (output, 0, output_len); + g_free (output); + output = NULL; + } + } + gcry_cipher_close (ctx); + return output; +} + +NMCryptoFileFormat +crypto_verify_cert (const unsigned char *data, + gsize len, + GError **error) +{ + gnutls_x509_crt_t der; + gnutls_datum_t dt; + int err; + + err = gnutls_x509_crt_init (&der); + if (err < 0) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CERT_FORMAT_INVALID, + _("Error initializing certificate data: %s"), + gnutls_strerror (err)); + return NM_CRYPTO_FILE_FORMAT_UNKNOWN; + } + + /* Try DER first */ + dt.data = (unsigned char *) data; + dt.size = len; + err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_DER); + if (err == GNUTLS_E_SUCCESS) { + gnutls_x509_crt_deinit (der); + return NM_CRYPTO_FILE_FORMAT_X509; + } + + /* And PEM next */ + err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_PEM); + gnutls_x509_crt_deinit (der); + if (err == GNUTLS_E_SUCCESS) + return NM_CRYPTO_FILE_FORMAT_X509; + + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CERT_FORMAT_INVALID, + _("Couldn't decode certificate: %s"), + gnutls_strerror (err)); + return NM_CRYPTO_FILE_FORMAT_UNKNOWN; +} + +gboolean +crypto_verify_pkcs12 (const GByteArray *data, + const char *password, + GError **error) +{ + gnutls_pkcs12_t p12; + gnutls_datum_t dt; + gboolean success = FALSE; + int err; + + g_return_val_if_fail (data != NULL, FALSE); + + dt.data = (unsigned char *) data->data; + dt.size = data->len; + + err = gnutls_pkcs12_init (&p12); + if (err < 0) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_DECODE_FAILED, + _("Couldn't initialize PKCS#12 decoder: %s"), + gnutls_strerror (err)); + return FALSE; + } + + /* DER first */ + err = gnutls_pkcs12_import (p12, &dt, GNUTLS_X509_FMT_DER, 0); + if (err < 0) { + /* PEM next */ + err = gnutls_pkcs12_import (p12, &dt, GNUTLS_X509_FMT_PEM, 0); + if (err < 0) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("Couldn't decode PKCS#12 file: %s"), + gnutls_strerror (err)); + goto out; + } + } + + err = gnutls_pkcs12_verify_mac (p12, password); + if (err == GNUTLS_E_SUCCESS) + success = TRUE; + else { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, + _("Couldn't verify PKCS#12 file: %s"), + gnutls_strerror (err)); + } + +out: + gnutls_pkcs12_deinit (p12); + return success; +} + +gboolean +crypto_verify_pkcs8 (const GByteArray *data, + gboolean is_encrypted, + const char *password, + GError **error) +{ + gnutls_x509_privkey_t p8; + gnutls_datum_t dt; + int err; + + g_return_val_if_fail (data != NULL, FALSE); + + dt.data = (unsigned char *) data->data; + dt.size = data->len; + + err = gnutls_x509_privkey_init (&p8); + if (err < 0) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_DECODE_FAILED, + _("Couldn't initialize PKCS#8 decoder: %s"), + gnutls_strerror (err)); + return FALSE; + } + + err = gnutls_x509_privkey_import_pkcs8 (p8, + &dt, + GNUTLS_X509_FMT_DER, + is_encrypted ? password : NULL, + is_encrypted ? 0 : GNUTLS_PKCS_PLAIN); + gnutls_x509_privkey_deinit (p8); + + if (err < 0) { + if (err == GNUTLS_E_UNKNOWN_CIPHER_TYPE) { + /* HACK: gnutls doesn't support all the cipher types that openssl + * can use with PKCS#8, so if we encounter one, we have to assume + * the given password works. gnutls needs to unsuckify, apparently. + * Specifically, by default openssl uses pbeWithMD5AndDES-CBC + * which gnutls does not support. + */ + } else { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("Couldn't decode PKCS#8 file: %s"), + gnutls_strerror (err)); + return FALSE; + } + } + + return TRUE; +} + +gboolean +crypto_randomize (void *buffer, gsize buffer_len, GError **error) +{ + gcry_randomize (buffer, buffer_len, GCRY_STRONG_RANDOM); + return TRUE; +} diff --git a/libnm-core/crypto_nss.c b/libnm-core/crypto_nss.c new file mode 100644 index 0000000000..1e589ea709 --- /dev/null +++ b/libnm-core/crypto_nss.c @@ -0,0 +1,562 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * Dan Williams <dcbw@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2009 Red Hat, Inc. + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n.h> + +#include <prinit.h> +#include <nss.h> +#include <pk11pub.h> +#include <pkcs11t.h> +#include <cert.h> +#include <prerror.h> +#include <p12.h> +#include <ciferfam.h> +#include <p12plcy.h> + +#include "crypto.h" + +static gboolean initialized = FALSE; + +gboolean +crypto_init (GError **error) +{ + SECStatus ret; + + if (initialized) + return TRUE; + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 1); + ret = NSS_NoDB_Init (NULL); + if (ret != SECSuccess) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_INIT_FAILED, + _("Failed to initialize the crypto engine: %d."), + PR_GetError ()); + PR_Cleanup (); + return FALSE; + } + + SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1); + SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1); + SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1); + SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1); + SEC_PKCS12EnableCipher(PKCS12_DES_56, 1); + SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1); + SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1); + + initialized = TRUE; + return TRUE; +} + +void +crypto_deinit (void) +{ +} + +gboolean +crypto_md5_hash (const char *salt, + const gsize salt_len, + const char *password, + gsize password_len, + char *buffer, + gsize buflen, + GError **error) +{ + PK11Context *ctx; + int nkey = buflen; + unsigned int digest_len; + int count = 0; + char digest[MD5_HASH_LEN]; + char *p = buffer; + + if (salt) + g_return_val_if_fail (salt_len >= 8, FALSE); + + g_return_val_if_fail (password != NULL, FALSE); + g_return_val_if_fail (password_len > 0, FALSE); + g_return_val_if_fail (buffer != NULL, FALSE); + g_return_val_if_fail (buflen > 0, FALSE); + + ctx = PK11_CreateDigestContext (SEC_OID_MD5); + if (!ctx) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_MD5_INIT_FAILED, + _("Failed to initialize the MD5 context: %d."), + PORT_GetError ()); + return FALSE; + } + + while (nkey > 0) { + int i = 0; + + PK11_DigestBegin (ctx); + if (count++) + PK11_DigestOp (ctx, (const unsigned char *) digest, digest_len); + PK11_DigestOp (ctx, (const unsigned char *) password, password_len); + if (salt) + PK11_DigestOp (ctx, (const unsigned char *) salt, 8); /* Only use 8 bytes of salt */ + PK11_DigestFinal (ctx, (unsigned char *) digest, &digest_len, sizeof (digest)); + + while (nkey && (i < digest_len)) { + *(p++) = digest[i++]; + nkey--; + } + } + + memset (digest, 0, sizeof (digest)); + PK11_DestroyContext (ctx, PR_TRUE); + return TRUE; +} + +char * +crypto_decrypt (const char *cipher, + int key_type, + GByteArray *data, + const char *iv, + const gsize iv_len, + const char *key, + const gsize key_len, + gsize *out_len, + GError **error) +{ + char *output = NULL; + int decrypted_len = 0; + CK_MECHANISM_TYPE cipher_mech; + PK11SlotInfo *slot = NULL; + SECItem key_item; + PK11SymKey *sym_key = NULL; + SECItem *sec_param = NULL; + PK11Context *ctx = NULL; + SECStatus s; + gboolean success = FALSE; + unsigned int pad_len = 0, extra = 0; + guint32 i, real_iv_len = 0; + + if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) { + cipher_mech = CKM_DES3_CBC_PAD; + real_iv_len = 8; + } else if (!strcmp (cipher, CIPHER_DES_CBC)) { + cipher_mech = CKM_DES_CBC_PAD; + real_iv_len = 8; + } else if (!strcmp (cipher, CIPHER_AES_CBC)) { + cipher_mech = CKM_AES_CBC_PAD; + real_iv_len = 16; + } else { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_UNKNOWN_CIPHER, + _("Private key cipher '%s' was unknown."), + cipher); + return NULL; + } + + if (iv_len < real_iv_len) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_RAW_IV_INVALID, + _("Invalid IV length (must be at least %d)."), + real_iv_len); + return NULL; + } + + output = g_malloc0 (data->len); + + slot = PK11_GetBestSlot (cipher_mech, NULL); + if (!slot) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_INIT_FAILED, + _("Failed to initialize the decryption cipher slot.")); + goto out; + } + + key_item.data = (unsigned char *) key; + key_item.len = key_len; + sym_key = PK11_ImportSymKey (slot, cipher_mech, PK11_OriginUnwrap, CKA_DECRYPT, &key_item, NULL); + if (!sym_key) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED, + _("Failed to set symmetric key for decryption.")); + goto out; + } + + key_item.data = (unsigned char *) iv; + key_item.len = real_iv_len; + sec_param = PK11_ParamFromIV (cipher_mech, &key_item); + if (!sec_param) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED, + _("Failed to set IV for decryption.")); + goto out; + } + + ctx = PK11_CreateContextBySymKey (cipher_mech, CKA_DECRYPT, sym_key, sec_param); + if (!ctx) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_INIT_FAILED, + _("Failed to initialize the decryption context.")); + goto out; + } + + s = PK11_CipherOp (ctx, + (unsigned char *) output, + &decrypted_len, + data->len, + data->data, + data->len); + if (s != SECSuccess) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, + _("Failed to decrypt the private key: %d."), + PORT_GetError ()); + goto out; + } + + if (decrypted_len > data->len) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, + _("Failed to decrypt the private key: decrypted data too large.")); + goto out; + } + + s = PK11_DigestFinal (ctx, + (unsigned char *) (output + decrypted_len), + &extra, + data->len - decrypted_len); + if (s != SECSuccess) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, + _("Failed to finalize decryption of the private key: %d."), + PORT_GetError ()); + goto out; + } + decrypted_len += extra; + pad_len = data->len - decrypted_len; + + /* Check if the padding at the end of the decrypted data is valid */ + if (pad_len == 0 || pad_len > real_iv_len) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, + _("Failed to decrypt the private key: unexpected padding length.")); + goto out; + } + + /* Validate tail padding; last byte is the padding size, and all pad bytes + * should contain the padding size. + */ + for (i = pad_len; i > 0; i--) { + if (output[data->len - i] != pad_len) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, + _("Failed to decrypt the private key.")); + goto out; + } + } + + *out_len = decrypted_len; + success = TRUE; + +out: + if (ctx) + PK11_DestroyContext (ctx, PR_TRUE); + if (sym_key) + PK11_FreeSymKey (sym_key); + if (sec_param) + SECITEM_FreeItem (sec_param, PR_TRUE); + if (slot) + PK11_FreeSlot (slot); + + if (!success) { + if (output) { + /* Don't expose key material */ + memset (output, 0, data->len); + g_free (output); + output = NULL; + } + } + return output; +} + +char * +crypto_encrypt (const char *cipher, + const GByteArray *data, + const char *iv, + gsize iv_len, + const char *key, + gsize key_len, + gsize *out_len, + GError **error) +{ + SECStatus ret; + CK_MECHANISM_TYPE cipher_mech = CKM_DES3_CBC_PAD; + PK11SlotInfo *slot = NULL; + SECItem key_item = { .data = (unsigned char *) key, .len = key_len }; + SECItem iv_item = { .data = (unsigned char *) iv, .len = iv_len }; + PK11SymKey *sym_key = NULL; + SECItem *sec_param = NULL; + PK11Context *ctx = NULL; + unsigned char *output, *padded_buf; + gsize output_len; + int encrypted_len = 0, i; + gboolean success = FALSE; + gsize padded_buf_len, pad_len; + + if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) + cipher_mech = CKM_DES3_CBC_PAD; + else if (!strcmp (cipher, CIPHER_AES_CBC)) + cipher_mech = CKM_AES_CBC_PAD; + else { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_UNKNOWN_CIPHER, + _("Private key cipher '%s' was unknown."), + cipher); + return NULL; + } + + /* If data->len % ivlen == 0, then we add another complete block + * onto the end so that the decrypter knows there's padding. + */ + pad_len = iv_len - (data->len % iv_len); + output_len = padded_buf_len = data->len + pad_len; + padded_buf = g_malloc0 (padded_buf_len); + + memcpy (padded_buf, data->data, data->len); + for (i = 0; i < pad_len; i++) + padded_buf[data->len + i] = (guint8) (pad_len & 0xFF); + + output = g_malloc0 (output_len); + + slot = PK11_GetBestSlot (cipher_mech, NULL); + if (!slot) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_INIT_FAILED, + _("Failed to initialize the encryption cipher slot.")); + goto out; + } + + sym_key = PK11_ImportSymKey (slot, cipher_mech, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL); + if (!sym_key) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED, + _("Failed to set symmetric key for encryption.")); + goto out; + } + + sec_param = PK11_ParamFromIV (cipher_mech, &iv_item); + if (!sec_param) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED, + _("Failed to set IV for encryption.")); + goto out; + } + + ctx = PK11_CreateContextBySymKey (cipher_mech, CKA_ENCRYPT, sym_key, sec_param); + if (!ctx) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_INIT_FAILED, + _("Failed to initialize the encryption context.")); + goto out; + } + + ret = PK11_CipherOp (ctx, output, &encrypted_len, output_len, padded_buf, padded_buf_len); + if (ret != SECSuccess) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_ENCRYPT_FAILED, + _("Failed to encrypt: %d."), + PORT_GetError ()); + goto out; + } + + if (encrypted_len != output_len) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_ENCRYPT_FAILED, + _("Unexpected amount of data after encrypting.")); + goto out; + } + + *out_len = encrypted_len; + success = TRUE; + +out: + if (ctx) + PK11_DestroyContext (ctx, PR_TRUE); + if (sym_key) + PK11_FreeSymKey (sym_key); + if (sec_param) + SECITEM_FreeItem (sec_param, PR_TRUE); + if (slot) + PK11_FreeSlot (slot); + + memset (padded_buf, 0, padded_buf_len); + g_free (padded_buf); + + if (!success) { + memset (output, 0, output_len); + g_free (output); + output = NULL; + } + return (char *) output; +} + +NMCryptoFileFormat +crypto_verify_cert (const unsigned char *data, + gsize len, + GError **error) +{ + CERTCertificate *cert; + + /* Try DER/PEM first */ + cert = CERT_DecodeCertFromPackage ((char *) data, len); + if (!cert) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CERT_FORMAT_INVALID, + _("Couldn't decode certificate: %d"), + PORT_GetError()); + return NM_CRYPTO_FILE_FORMAT_UNKNOWN; + } + + CERT_DestroyCertificate (cert); + return NM_CRYPTO_FILE_FORMAT_X509; +} + +gboolean +crypto_verify_pkcs12 (const GByteArray *data, + const char *password, + GError **error) +{ + SEC_PKCS12DecoderContext *p12ctx = NULL; + SECItem pw = { 0 }; + PK11SlotInfo *slot = NULL; + SECStatus s; + char *ucs2_password; + glong ucs2_chars = 0; +#ifndef WORDS_BIGENDIAN + guint16 *p; +#endif /* WORDS_BIGENDIAN */ + + if (error) + g_return_val_if_fail (*error == NULL, FALSE); + + /* PKCS#12 passwords are apparently UCS2 BIG ENDIAN, and NSS doesn't do + * any conversions for us. + */ + if (password && strlen (password)) { + ucs2_password = (char *) g_utf8_to_utf16 (password, strlen (password), NULL, &ucs2_chars, NULL); + if (!ucs2_password || !ucs2_chars) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_INVALID_PASSWORD, + _("Couldn't convert password to UCS2: %d"), + PORT_GetError()); + return FALSE; + } + + ucs2_chars *= 2; /* convert # UCS2 characters -> bytes */ + pw.data = PORT_ZAlloc(ucs2_chars + 2); + memcpy (pw.data, ucs2_password, ucs2_chars); + pw.len = ucs2_chars + 2; /* include terminating NULL */ + + memset (ucs2_password, 0, ucs2_chars); + g_free (ucs2_password); + +#ifndef WORDS_BIGENDIAN + for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++) + *p = GUINT16_SWAP_LE_BE (*p); +#endif /* WORDS_BIGENDIAN */ + } else { + /* NULL password */ + pw.data = NULL; + pw.len = 0; + } + + slot = PK11_GetInternalKeySlot(); + p12ctx = SEC_PKCS12DecoderStart (&pw, slot, NULL, NULL, NULL, NULL, NULL, NULL); + if (!p12ctx) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_DECODE_FAILED, + _("Couldn't initialize PKCS#12 decoder: %d"), + PORT_GetError()); + goto error; + } + + s = SEC_PKCS12DecoderUpdate (p12ctx, data->data, data->len); + if (s != SECSuccess) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_FILE_FORMAT_INVALID, + _("Couldn't decode PKCS#12 file: %d"), + PORT_GetError()); + goto error; + } + + s = SEC_PKCS12DecoderVerify (p12ctx); + if (s != SECSuccess) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, + _("Couldn't verify PKCS#12 file: %d"), + PORT_GetError()); + goto error; + } + + SEC_PKCS12DecoderFinish (p12ctx); + SECITEM_ZfreeItem (&pw, PR_FALSE); + return TRUE; + +error: + if (p12ctx) + SEC_PKCS12DecoderFinish (p12ctx); + + if (slot) + PK11_FreeSlot(slot); + + SECITEM_ZfreeItem (&pw, PR_FALSE); + return FALSE; +} + +gboolean +crypto_verify_pkcs8 (const GByteArray *data, + gboolean is_encrypted, + const char *password, + GError **error) +{ + g_return_val_if_fail (data != NULL, FALSE); + + /* NSS apparently doesn't do PKCS#8 natively, but you have to put the + * PKCS#8 key into a PKCS#12 file and import that?? So until we figure + * all that out, we can only assume the password is valid. + */ + return TRUE; +} + +gboolean +crypto_randomize (void *buffer, gsize buffer_len, GError **error) +{ + SECStatus s; + + s = PK11_GenerateRandom (buffer, buffer_len); + if (s != SECSuccess) { + g_set_error_literal (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERR_RANDOMIZE_FAILED, + _("Could not generate random data.")); + return FALSE; + } + return TRUE; +} diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c new file mode 100644 index 0000000000..7e26309c57 --- /dev/null +++ b/libnm-core/nm-connection.c @@ -0,0 +1,2189 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2013 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include <glib-object.h> +#include <glib/gi18n.h> +#include <dbus/dbus-glib.h> +#include <string.h> +#include "nm-connection.h" +#include "nm-utils.h" +#include "nm-dbus-glib-types.h" +#include "nm-setting-private.h" + +#include "nm-setting-8021x.h" +#include "nm-setting-bluetooth.h" +#include "nm-setting-connection.h" +#include "nm-setting-infiniband.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-ip6-config.h" +#include "nm-setting-ppp.h" +#include "nm-setting-pppoe.h" +#include "nm-setting-wimax.h" +#include "nm-setting-wired.h" +#include "nm-setting-adsl.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" +#include "nm-setting-serial.h" +#include "nm-setting-vpn.h" +#include "nm-setting-olpc-mesh.h" +#include "nm-setting-bond.h" +#include "nm-setting-team.h" +#include "nm-setting-team-port.h" +#include "nm-setting-bridge.h" +#include "nm-setting-bridge-port.h" +#include "nm-setting-vlan.h" +#include "nm-setting-serial.h" +#include "nm-setting-gsm.h" +#include "nm-setting-cdma.h" + +/** + * SECTION:nm-connection + * @short_description: Describes a connection to specific network or provider + * @include: nm-connection.h + * + * An #NMConnection describes all the settings and configuration values that + * are necessary to configure network devices for operation on a specific + * network. Connections are the fundamental operating object for + * NetworkManager; no device is connected without a #NMConnection, or + * disconnected without having been connected with a #NMConnection. + * + * Each #NMConnection contains a list of #NMSetting objects usually referenced + * by name (using nm_connection_get_setting_by_name()) or by type (with + * nm_connection_get_setting()). The settings describe the actual parameters + * with which the network devices are configured, including device-specific + * parameters (MTU, SSID, APN, channel, rate, etc) and IP-level parameters + * (addresses, routes, addressing methods, etc). + * + */ + +/** + * nm_connection_error_quark: + * + * Registers an error quark for #NMConnection if necessary. + * + * Returns: the error quark used for #NMConnection errors. + **/ +GQuark +nm_connection_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-connection-error-quark"); + return quark; +} + +typedef struct { + GHashTable *settings; + + /* D-Bus path of the connection, if any */ + char *path; +} NMConnectionPrivate; + +#define NM_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONNECTION, NMConnectionPrivate)) + +G_DEFINE_TYPE (NMConnection, nm_connection, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_PATH, + + LAST_PROP +}; + +enum { + SECRETS_UPDATED, + SECRETS_CLEARED, + CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +static NMSettingVerifyResult _nm_connection_verify (NMConnection *connection, GError **error); + + +/*************************************************************/ + +/** + * nm_connection_lookup_setting_type: + * @name: a setting name + * + * Returns the #GType of the setting's class for a given setting name. + * + * Returns: the #GType of the setting's class + **/ +GType +nm_connection_lookup_setting_type (const char *name) +{ + return _nm_setting_lookup_setting_type (name); +} + +/** + * nm_connection_lookup_setting_type_by_quark: + * @error_quark: a setting error quark + * + * Returns the #GType of the setting's class for a given setting error quark. + * Useful for figuring out which setting a returned error is for. + * + * Returns: the #GType of the setting's class + **/ +GType +nm_connection_lookup_setting_type_by_quark (GQuark error_quark) +{ + return _nm_setting_lookup_setting_type_by_quark (error_quark); +} + +/** + * nm_connection_create_setting: + * @name: a setting name + * + * Create a new #NMSetting object of the desired type, given a setting name. + * + * Returns: (transfer full): the new setting object, or %NULL if the setting name was unknown + **/ +NMSetting * +nm_connection_create_setting (const char *name) +{ + GType type; + NMSetting *setting = NULL; + + g_return_val_if_fail (name != NULL, NULL); + + type = nm_connection_lookup_setting_type (name); + if (type) + setting = (NMSetting *) g_object_new (type, NULL); + + return setting; +} + +static void +setting_changed_cb (NMSetting *setting, + GParamSpec *pspec, + NMConnection *self) +{ + g_signal_emit (self, signals[CHANGED], 0); +} + +static void +_nm_connection_add_setting (NMConnection *connection, NMSetting *setting) +{ + g_hash_table_insert (NM_CONNECTION_GET_PRIVATE (connection)->settings, + (gpointer) G_OBJECT_TYPE_NAME (setting), + setting); + /* Listen for property changes so we can emit the 'changed' signal */ + g_signal_connect (setting, "notify", (GCallback) setting_changed_cb, connection); +} + +/** + * nm_connection_add_setting: + * @connection: a #NMConnection + * @setting: (transfer full): the #NMSetting to add to the connection object + * + * Adds a #NMSetting to the connection, replacing any previous #NMSetting of the + * same name which has previously been added to the #NMConnection. The + * connection takes ownership of the #NMSetting object and does not increase + * the setting object's reference count. + **/ +void +nm_connection_add_setting (NMConnection *connection, NMSetting *setting) +{ + g_return_if_fail (NM_IS_CONNECTION (connection)); + g_return_if_fail (NM_IS_SETTING (setting)); + + _nm_connection_add_setting (connection, setting); + g_signal_emit (connection, signals[CHANGED], 0); +} + +/** + * nm_connection_remove_setting: + * @connection: a #NMConnection + * @setting_type: the #GType of the setting object to remove + * + * Removes the #NMSetting with the given #GType from the #NMConnection. This + * operation dereferences the #NMSetting object. + **/ +void +nm_connection_remove_setting (NMConnection *connection, GType setting_type) +{ + NMConnectionPrivate *priv; + NMSetting *setting; + const char *setting_name; + + g_return_if_fail (NM_IS_CONNECTION (connection)); + g_return_if_fail (g_type_is_a (setting_type, NM_TYPE_SETTING)); + + priv = NM_CONNECTION_GET_PRIVATE (connection); + setting_name = g_type_name (setting_type); + setting = g_hash_table_lookup (priv->settings, setting_name); + if (setting) { + g_signal_handlers_disconnect_by_func (setting, setting_changed_cb, connection); + g_hash_table_remove (priv->settings, setting_name); + g_signal_emit (connection, signals[CHANGED], 0); + } +} + +/** + * nm_connection_get_setting: + * @connection: a #NMConnection + * @setting_type: the #GType of the setting object to return + * + * Gets the #NMSetting with the given #GType, if one has been previously added + * to the #NMConnection. + * + * Returns: (transfer none): the #NMSetting, or %NULL if no setting of that type was previously + * added to the #NMConnection + **/ +NMSetting * +nm_connection_get_setting (NMConnection *connection, GType setting_type) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (g_type_is_a (setting_type, NM_TYPE_SETTING), NULL); + + return (NMSetting *) g_hash_table_lookup (NM_CONNECTION_GET_PRIVATE (connection)->settings, + g_type_name (setting_type)); +} + +/** + * nm_connection_get_setting_by_name: + * @connection: a #NMConnection + * @name: a setting name + * + * Gets the #NMSetting with the given name, if one has been previously added + * the #NMConnection. + * + * Returns: (transfer none): the #NMSetting, or %NULL if no setting with that name was previously + * added to the #NMConnection + **/ +NMSetting * +nm_connection_get_setting_by_name (NMConnection *connection, const char *name) +{ + GType type; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (name != NULL, NULL); + + type = nm_connection_lookup_setting_type (name); + + return type ? nm_connection_get_setting (connection, type) : NULL; +} + +/* not exposed until we actually need it */ +static NMSetting * +_get_type_setting (NMConnection *connection) +{ + NMSettingConnection *s_con; + const char *type; + NMSetting *base; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + type = nm_setting_connection_get_connection_type (s_con); + g_assert (type); + + base = nm_connection_get_setting_by_name (connection, type); + g_assert (base); + + return base; +} + +static gboolean +validate_permissions_type (GHashTable *hash, GError **error) +{ + GHashTable *s_con; + GValue *permissions; + + /* Ensure the connection::permissions item (if present) is the correct + * type, otherwise the g_object_set() will throw a warning and ignore the + * error, leaving us with no permissions. + */ + s_con = g_hash_table_lookup (hash, NM_SETTING_CONNECTION_SETTING_NAME); + if (s_con) { + permissions = g_hash_table_lookup (s_con, NM_SETTING_CONNECTION_PERMISSIONS); + if (permissions) { + if ( !G_VALUE_HOLDS (permissions, G_TYPE_STRV) + && !G_VALUE_HOLDS (permissions, DBUS_TYPE_G_LIST_OF_STRING)) { + g_set_error_literal (error, + NM_SETTING_ERROR, + NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH, + "Wrong permissions property type; should be a list of strings."); + return FALSE; + } + } + } + return TRUE; +} + +static gboolean +hash_to_connection (NMConnection *connection, GHashTable *new, GError **error) +{ + GHashTableIter iter; + const char *setting_name; + GHashTable *setting_hash; + gboolean changed, valid; + NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (connection); + + if ((changed = g_hash_table_size (priv->settings) > 0)) + g_hash_table_remove_all (priv->settings); + + g_hash_table_iter_init (&iter, new); + while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, (gpointer) &setting_hash)) { + GType type = nm_connection_lookup_setting_type (setting_name); + + if (type) { + NMSetting *setting = nm_setting_new_from_hash (type, setting_hash); + + if (setting) { + _nm_connection_add_setting (connection, setting); + changed = TRUE; + } + } + } + + valid = nm_connection_verify (connection, error); + if (changed) + g_signal_emit (connection, signals[CHANGED], 0); + return valid; +} + +/** + * nm_connection_replace_settings: + * @connection: a #NMConnection + * @new_settings: (element-type utf8 GLib.HashTable): a #GHashTable of settings + * @error: location to store error, or %NULL + * + * Returns: %TRUE if the settings were valid and added to the connection, %FALSE + * if they were not + **/ +gboolean +nm_connection_replace_settings (NMConnection *connection, + GHashTable *new_settings, + GError **error) +{ + gboolean valid = FALSE; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (new_settings != NULL, FALSE); + if (error) + g_return_val_if_fail (*error == NULL, FALSE); + + if (validate_permissions_type (new_settings, error)) + valid = hash_to_connection (connection, new_settings, error); + return valid; +} + +/** + * nm_connection_replace_settings_from_connection: + * @connection: a #NMConnection + * @new_connection: a #NMConnection to replace the settings of @connection with + * @error: location to store error, or %NULL + * + * Deep-copies the settings of @new_conenction and replaces the settings of @connection + * with the copied settings. + * + * Returns: %TRUE if the settings were valid and added to the connection, %FALSE + * if they were not + * + * Since: 0.9.10 + **/ +gboolean +nm_connection_replace_settings_from_connection (NMConnection *connection, + NMConnection *new_connection, + GError **error) +{ + NMConnectionPrivate *priv; + GHashTableIter iter; + NMSetting *setting; + gboolean changed, valid; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE); + if (error) + g_return_val_if_fail (*error == NULL, FALSE); + + /* When 'connection' and 'new_connection' are the same object simply return + * in order not to destroy 'connection' */ + if (connection == new_connection) + return TRUE; + + /* No need to validate permissions like nm_connection_replace_settings() + * since we're dealing with an NMConnection which has already done that. + */ + + priv = NM_CONNECTION_GET_PRIVATE (connection); + if ((changed = g_hash_table_size (priv->settings) > 0)) + g_hash_table_remove_all (priv->settings); + + if (g_hash_table_size (NM_CONNECTION_GET_PRIVATE (new_connection)->settings)) { + g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (new_connection)->settings); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) + _nm_connection_add_setting (connection, nm_setting_duplicate (setting)); + changed = TRUE; + } + + valid = nm_connection_verify (connection, error); + if (changed) + g_signal_emit (connection, signals[CHANGED], 0); + return valid; +} + +/** + * nm_connection_compare: + * @a: a #NMConnection + * @b: a second #NMConnection to compare with the first + * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT + * + * Compares two #NMConnection objects for similarity, with comparison behavior + * modified by a set of flags. See nm_setting_compare() for a description of + * each flag's behavior. + * + * Returns: %TRUE if the comparison succeeds, %FALSE if it does not + **/ +gboolean +nm_connection_compare (NMConnection *a, + NMConnection *b, + NMSettingCompareFlags flags) +{ + GHashTableIter iter; + NMSetting *src; + + if (a == b) + return TRUE; + if (!a || !b) + return FALSE; + + /* B / A: ensure settings in B that are not in A make the comparison fail */ + if (g_hash_table_size (NM_CONNECTION_GET_PRIVATE (a)->settings) != + g_hash_table_size (NM_CONNECTION_GET_PRIVATE (b)->settings)) + return FALSE; + + /* A / B: ensure all settings in A match corresponding ones in B */ + g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (a)->settings); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &src)) { + NMSetting *cmp = nm_connection_get_setting (b, G_OBJECT_TYPE (src)); + + if (!cmp || !nm_setting_compare (src, cmp, flags)) + return FALSE; + } + + return TRUE; +} + + +static void +diff_one_connection (NMConnection *a, + NMConnection *b, + NMSettingCompareFlags flags, + gboolean invert_results, + GHashTable *diffs) +{ + NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (a); + GHashTableIter iter; + NMSetting *a_setting = NULL; + + g_hash_table_iter_init (&iter, priv->settings); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &a_setting)) { + NMSetting *b_setting = NULL; + const char *setting_name = nm_setting_get_name (a_setting); + GHashTable *results; + gboolean new_results = TRUE; + + if (b) + b_setting = nm_connection_get_setting (b, G_OBJECT_TYPE (a_setting)); + + results = g_hash_table_lookup (diffs, setting_name); + if (results) + new_results = FALSE; + + if (!nm_setting_diff (a_setting, b_setting, flags, invert_results, &results)) { + if (new_results) + g_hash_table_insert (diffs, g_strdup (setting_name), results); + } + } +} + +/** + * nm_connection_diff: + * @a: a #NMConnection + * @b: a second #NMConnection to compare with the first + * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT + * @out_settings: (element-type utf8 GLib.HashTable): if the + * connections differ, on return a hash table mapping setting names to + * second-level GHashTable (utf8 to guint32), which contains the key names that + * differ mapped to one or more of %NMSettingDiffResult as a bitfield + * + * Compares two #NMConnection objects for similarity, with comparison behavior + * modified by a set of flags. See nm_setting_compare() for a description of + * each flag's behavior. If the connections differ, settings and keys within + * each setting that differ are added to the returned @out_settings hash table. + * No values are returned, only key names. + * + * Returns: %TRUE if the connections contain the same values, %FALSE if they do + * not + **/ +gboolean +nm_connection_diff (NMConnection *a, + NMConnection *b, + NMSettingCompareFlags flags, + GHashTable **out_settings) +{ + GHashTable *diffs; + + g_return_val_if_fail (NM_IS_CONNECTION (a), FALSE); + g_return_val_if_fail (out_settings != NULL, FALSE); + g_return_val_if_fail (*out_settings == NULL, FALSE); + if (b) + g_return_val_if_fail (NM_IS_CONNECTION (b), FALSE); + + if (a == b) + return TRUE; + + diffs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy); + + /* Diff A to B, then B to A to capture keys in B that aren't in A */ + diff_one_connection (a, b, flags, FALSE, diffs); + if (b) + diff_one_connection (b, a, flags, TRUE, diffs); + + if (g_hash_table_size (diffs) == 0) + g_hash_table_destroy (diffs); + else + *out_settings = diffs; + + return *out_settings ? FALSE : TRUE; +} + +static gboolean +_normalize_virtual_iface_name (NMConnection *self) +{ + NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (self); + GHashTableIter h_iter; + NMSetting *setting; + NMSettingConnection *s_con; + const char *interface_name; + char *virtual_iface_name = NULL; + gboolean was_modified = FALSE; + const char *prop_name = NULL; + + /* search for settings that might need normalization of the interface name. */ + g_hash_table_iter_init (&h_iter, priv->settings); + while ( !prop_name + && g_hash_table_iter_next (&h_iter, NULL, (void **) &setting)) { + if (NM_IS_SETTING_BOND (setting)) + prop_name = NM_SETTING_BOND_INTERFACE_NAME; + else if (NM_IS_SETTING_BRIDGE (setting)) + prop_name = NM_SETTING_BRIDGE_INTERFACE_NAME; + else if (NM_IS_SETTING_TEAM (setting)) + prop_name = NM_SETTING_TEAM_INTERFACE_NAME; + else if (NM_IS_SETTING_VLAN (setting)) + prop_name = NM_SETTING_VLAN_INTERFACE_NAME; + } + if (!prop_name) + return FALSE; + + s_con = nm_connection_get_setting_connection (self); + g_return_val_if_fail (s_con, FALSE); + + interface_name = nm_setting_connection_get_interface_name (s_con); + + /* read the potential virtual_iface_name from the setting. */ + g_object_get (setting, prop_name, &virtual_iface_name, NULL); + + if (g_strcmp0 (interface_name, virtual_iface_name) != 0) { + if (interface_name) { + /* interface_name is set and overwrites the virtual_iface_name. */ + g_object_set (setting, prop_name, interface_name, NULL); + } else { + /* interface in NMSettingConnection must be set. */ + g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, virtual_iface_name, NULL); + } + was_modified = TRUE; + } + + g_free (virtual_iface_name); + + return was_modified; +} + +static gboolean +_normalize_ip_config (NMConnection *self, GHashTable *parameters) +{ + NMSettingConnection *s_con = nm_connection_get_setting_connection (self); + const char *default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; + const char *default_ip6_method = NULL; + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + NMSetting *setting; + + if (parameters) + default_ip6_method = g_hash_table_lookup (parameters, NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD); + if (!default_ip6_method) + default_ip6_method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; + + s_ip4 = nm_connection_get_setting_ip4_config (self); + s_ip6 = nm_connection_get_setting_ip6_config (self); + + if (nm_setting_connection_get_master (s_con)) { + /* Slave connections don't have IP configuration. */ + + if (s_ip4) + nm_connection_remove_setting (self, NM_TYPE_SETTING_IP4_CONFIG); + + if (s_ip6) + nm_connection_remove_setting (self, NM_TYPE_SETTING_IP6_CONFIG); + + return s_ip4 || s_ip6; + } else { + /* Ensure all non-slave connections have IP4 and IP6 settings objects. If no + * IP6 setting was specified, then assume that means IP6 config is allowed + * to fail. But if no IP4 setting was specified, assume the caller was just + * being lazy. + */ + if (!s_ip4) { + setting = nm_setting_ip4_config_new (); + + g_object_set (setting, + NM_SETTING_IP4_CONFIG_METHOD, default_ip4_method, + NULL); + nm_connection_add_setting (self, setting); + } + if (!s_ip6) { + setting = nm_setting_ip6_config_new (); + + g_object_set (setting, + NM_SETTING_IP6_CONFIG_METHOD, default_ip6_method, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); + nm_connection_add_setting (self, setting); + } + return !s_ip4 || !s_ip6; + } +} + +/** + * nm_connection_verify: + * @connection: the #NMConnection to verify + * @error: location to store error, or %NULL + * + * Validates the connection and all its settings. Each setting's properties + * have allowed values, and some values are dependent on other values. For + * example, if a Wi-Fi connection is security enabled, the #NMSettingWireless + * setting object's 'security' property must contain the setting name of the + * #NMSettingWirelessSecurity object, which must also be present in the + * connection for the connection to be valid. As another example, the + * #NMSettingWired object's 'mac-address' property must be a validly formatted + * MAC address. The returned #GError contains information about which + * setting and which property failed validation, and how it failed validation. + * + * Returns: %TRUE if the connection is valid, %FALSE if it is not + **/ +gboolean +nm_connection_verify (NMConnection *connection, GError **error) +{ + NMSettingVerifyResult result; + + result = _nm_connection_verify (connection, error); + + /* we treat normalizable connections as valid. */ + if (result == NM_SETTING_VERIFY_NORMALIZABLE) + g_clear_error (error); + + return result == NM_SETTING_VERIFY_SUCCESS || result == NM_SETTING_VERIFY_NORMALIZABLE; +} + +static NMSettingVerifyResult +_nm_connection_verify (NMConnection *connection, GError **error) +{ + NMConnectionPrivate *priv; + NMSettingConnection *s_con; + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + GHashTableIter iter; + gpointer value; + GSList *all_settings = NULL, *setting_i; + NMSettingVerifyResult success = NM_SETTING_VERIFY_ERROR; + NMSetting *base; + const char *ctype; + GError *normalizable_error = NULL; + NMSettingVerifyResult normalizable_error_type = NM_SETTING_VERIFY_SUCCESS; + + if (error) + g_return_val_if_fail (*error == NULL, NM_SETTING_VERIFY_ERROR); + + if (!NM_IS_CONNECTION (connection)) { + g_set_error_literal (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_UNKNOWN, + "invalid connection; failed verification"); + g_return_val_if_fail (NM_IS_CONNECTION (connection), NM_SETTING_VERIFY_ERROR); + } + + priv = NM_CONNECTION_GET_PRIVATE (connection); + + /* First, make sure there's at least 'connection' setting */ + s_con = nm_connection_get_setting_connection (connection); + if (!s_con) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_CONNECTION_SETTING_NOT_FOUND, + "connection setting not found"); + goto EXIT; + } + + /* Build up the list of settings */ + g_hash_table_iter_init (&iter, priv->settings); + while (g_hash_table_iter_next (&iter, NULL, &value)) { + /* Order NMSettingConnection so that it will be verified first. + * The reason is, that NMSettingConnection:verify() modifies the connection + * by setting NMSettingConnection:interface_name. So we want to call that + * verify() first, because the order can affect the outcome. + * Another reason is, that errors in this setting might be more fundamental + * and should be checked and reported with higher priority. + * Another reason is, that some settings look especially at the + * NMSettingConnection, so they find it first in the all_settings list. */ + if (value == s_con) + all_settings = g_slist_append (all_settings, value); + else + all_settings = g_slist_prepend (all_settings, value); + } + all_settings = g_slist_reverse (all_settings); + + /* Now, run the verify function of each setting */ + for (setting_i = all_settings; setting_i; setting_i = setting_i->next) { + GError *verify_error = NULL; + NMSettingVerifyResult verify_result; + + /* verify all settings. We stop if we find the first non-normalizable + * @NM_SETTING_VERIFY_ERROR. If we find normalizable errors we continue + * but remember the error to return it to the user. + * @NM_SETTING_VERIFY_NORMALIZABLE_ERROR has a higher priority then + * @NM_SETTING_VERIFY_NORMALIZABLE, so, if we encounter such an error type, + * we remember it instead (to return it as output). + **/ + verify_result = _nm_setting_verify (NM_SETTING (setting_i->data), all_settings, &verify_error); + if (verify_result == NM_SETTING_VERIFY_NORMALIZABLE || + verify_result == NM_SETTING_VERIFY_NORMALIZABLE_ERROR) { + if ( verify_result == NM_SETTING_VERIFY_NORMALIZABLE_ERROR + && normalizable_error_type == NM_SETTING_VERIFY_NORMALIZABLE) { + /* NORMALIZABLE_ERROR has higher priority. */ + g_clear_error (&normalizable_error); + } + if (!normalizable_error) { + g_propagate_error (&normalizable_error, verify_error); + verify_error = NULL; + normalizable_error_type = verify_result; + } + } else if (verify_result != NM_SETTING_VERIFY_SUCCESS) { + g_propagate_error (error, verify_error); + g_slist_free (all_settings); + g_return_val_if_fail (verify_result == NM_SETTING_VERIFY_ERROR, success); + goto EXIT; + } + g_clear_error (&verify_error); + } + g_slist_free (all_settings); + + /* Now make sure the given 'type' setting can actually be the base setting + * of the connection. Can't have type=ppp for example. + */ + ctype = nm_setting_connection_get_connection_type (s_con); + if (!ctype) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID, + "connection type missing"); + goto EXIT; + } + + base = nm_connection_get_setting_by_name (connection, ctype); + if (!base) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID, + "base setting GType not found"); + goto EXIT; + } + + if (!_nm_setting_is_base_type (base)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID, + "connection type '%s' is not a base type", + ctype); + goto EXIT; + } + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + s_ip6 = nm_connection_get_setting_ip6_config (connection); + + if (nm_setting_connection_get_master (s_con)) { + if ((normalizable_error_type == NM_SETTING_VERIFY_SUCCESS || + (normalizable_error_type == NM_SETTING_VERIFY_NORMALIZABLE)) && (s_ip4 || s_ip6)) { + g_clear_error (&normalizable_error); + g_set_error (&normalizable_error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_SETTING, + "slave connection cannot have an IP%c setting", + s_ip4 ? '4' : '6'); + /* having a slave with IP config *was* and is a verify() error. */ + normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE_ERROR; + } + } else { + if (normalizable_error_type == NM_SETTING_VERIFY_SUCCESS && (!s_ip4 || !s_ip6)) { + g_set_error (&normalizable_error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_SETTING_NOT_FOUND, + "connection needs an IP%c setting", + !s_ip4 ? '4' : '6'); + /* having a master without IP config was not a verify() error, accept + * it for backward compatibility. */ + normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE; + } + } + + if (normalizable_error_type != NM_SETTING_VERIFY_SUCCESS) { + g_propagate_error (error, normalizable_error); + normalizable_error = NULL; + success = normalizable_error_type; + } else + success = NM_SETTING_VERIFY_SUCCESS; + +EXIT: + g_clear_error (&normalizable_error); + return success; +} + +/** + * nm_connection_normalize: + * @connection: the #NMConnection to normalize + * @parameters: (allow-none) (element-type utf8 gpointer): a #GHashTable with + * normalization parameters to allow customization of the normalization by providing + * specific arguments. Unknown arguments will be ignored and the default will be + * used. The keys must be strings, hashed by g_str_hash() and g_str_equal() functions. + * The values are opaque and depend on the parameter name. + * @modified: (out) (allow-none): outputs whether any settings were modified. + * @error: location to store error, or %NULL. Contains the reason, + * why the connection is invalid, if the function returns an error. + * + * Does some basic normalization and fixup of well known inconsistencies + * and deprecated fields. If the connection was modified in any way, + * the output parameter @modified is set %TRUE. + * + * Finally the connection will be verified and %TRUE returns if the connection + * is valid. As this function only performs some specific normalization steps + * it cannot repair all connections. If the connection has errors that + * cannot be normalized, the connection will not be modified. + * + * Returns: %TRUE if the connection is valid, %FALSE if it is not + * + * Since: 1.0 + **/ +gboolean +nm_connection_normalize (NMConnection *connection, + GHashTable *parameters, + gboolean *modified, + GError **error) +{ + NMSettingVerifyResult success; + gboolean was_modified = FALSE; + GError *normalizable_error = NULL; + + success = _nm_connection_verify (connection, &normalizable_error); + + if (success == NM_SETTING_VERIFY_ERROR || + success == NM_SETTING_VERIFY_SUCCESS) { + if (normalizable_error) + g_propagate_error (error, normalizable_error); + goto EXIT; + } + g_assert (success == NM_SETTING_VERIFY_NORMALIZABLE || success == NM_SETTING_VERIFY_NORMALIZABLE_ERROR); + g_clear_error (&normalizable_error); + + /* Try to perform all kind of normalizations on the settings to fix it. + * We only do this, after verifying that the connection contains no un-normalizable + * errors, because in that case we rather fail without touching the settings. */ + + was_modified |= _normalize_virtual_iface_name (connection); + was_modified |= _normalize_ip_config (connection, parameters); + + /* Verify anew. */ + success = _nm_connection_verify (connection, error); + + /* we would expect, that after normalization, the connection can be verified. */ + g_return_val_if_fail (success == NM_SETTING_VERIFY_SUCCESS, success); + + /* we would expect, that the connection was modified during normalization. */ + g_return_val_if_fail (was_modified, success); + +EXIT: + if (modified) + *modified = was_modified; + + return success == NM_SETTING_VERIFY_SUCCESS; +} + +/** + * nm_connection_update_secrets: + * @connection: the #NMConnection + * @setting_name: the setting object name to which the secrets apply + * @secrets: (element-type utf8 GObject.Value): a #GHashTable mapping + * string:#GValue of setting property names and secrets of the given @setting_name + * @error: location to store error, or %NULL + * + * Update the specified setting's secrets, given a hash table of secrets + * intended for that setting (deserialized from D-Bus for example). Will also + * extract the given setting's secrets hash if given a hash of hashes, as would + * be returned from nm_connection_to_hash(). If @setting_name is %NULL, expects + * a fully serialized #NMConnection as returned by nm_connection_to_hash() and + * will update all secrets from all settings contained in @secrets. + * + * Returns: %TRUE if the secrets were successfully updated, %FALSE if the update + * failed (tried to update secrets for a setting that doesn't exist, etc) + **/ +gboolean +nm_connection_update_secrets (NMConnection *connection, + const char *setting_name, + GHashTable *secrets, + GError **error) +{ + NMSetting *setting; + gboolean success = TRUE, updated = FALSE; + GHashTable *setting_hash = NULL; + GHashTableIter iter; + const char *key; + gboolean hashed_connection = FALSE; + int success_detail; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (secrets != NULL, FALSE); + if (error) + g_return_val_if_fail (*error == NULL, FALSE); + + /* Empty @secrets means success */ + if (g_hash_table_size (secrets) == 0) + return TRUE; + + /* For backwards compatibility, this function accepts either a hashed + * connection (GHashTable of GHashTables of GValues) or a single hashed + * setting (GHashTable of GValues). + */ + g_hash_table_iter_init (&iter, secrets); + while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL)) { + if (_nm_setting_lookup_setting_type (key) != G_TYPE_INVALID) { + /* @secrets looks like a hashed connection */ + hashed_connection = TRUE; + break; + } + } + + if (setting_name) { + /* Update just one setting's secrets */ + setting = nm_connection_get_setting_by_name (connection, setting_name); + if (!setting) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_SETTING_NOT_FOUND, + setting_name); + return FALSE; + } + + if (hashed_connection) { + setting_hash = g_hash_table_lookup (secrets, setting_name); + if (!setting_hash) { + /* The hashed connection that didn't contain any secrets for + * @setting_name; just return success. + */ + return TRUE; + } + } + + g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection); + success_detail = _nm_setting_update_secrets (setting, + setting_hash ? setting_hash : secrets, + error); + g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection); + + if (success_detail == NM_SETTING_UPDATE_SECRET_ERROR) + return FALSE; + if (success_detail == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED) + updated = TRUE; + } else { + if (!hashed_connection) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_SETTING_NOT_FOUND, + key); + return FALSE; + } + + /* check first, whether all the settings exist... */ + g_hash_table_iter_init (&iter, secrets); + while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL)) { + setting = nm_connection_get_setting_by_name (connection, key); + if (!setting) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_SETTING_NOT_FOUND, + key); + return FALSE; + } + } + + /* Update each setting with any secrets from the hashed connection */ + g_hash_table_iter_init (&iter, secrets); + while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &setting_hash)) { + /* Update the secrets for this setting */ + setting = nm_connection_get_setting_by_name (connection, key); + + g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection); + success_detail = _nm_setting_update_secrets (setting, setting_hash, error); + g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection); + + if (success_detail == NM_SETTING_UPDATE_SECRET_ERROR) { + success = FALSE; + break; + } + if (success_detail == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED) + updated = TRUE; + } + } + + if (updated) { + g_signal_emit (connection, signals[SECRETS_UPDATED], 0, setting_name); + g_signal_emit (connection, signals[CHANGED], 0); + } + + return success; +} + +/** + * nm_connection_need_secrets: + * @connection: the #NMConnection + * @hints: (out) (element-type utf8) (allow-none) (transfer container): + * the address of a pointer to a #GPtrArray, initialized to %NULL, which on + * return points to an allocated #GPtrArray containing the property names of + * secrets of the #NMSetting which may be required; the caller owns the array + * and must free the array itself with g_ptr_array_free(), but not free its + * elements + * + * Returns the name of the first setting object in the connection which would + * need secrets to make a successful connection. The returned hints are only + * intended as a guide to what secrets may be required, because in some + * circumstances, there is no way to conclusively determine exactly which + * secrets are needed. + * + * Returns: the setting name of the #NMSetting object which has invalid or + * missing secrets + **/ +const char * +nm_connection_need_secrets (NMConnection *connection, + GPtrArray **hints) +{ + NMConnectionPrivate *priv; + GHashTableIter hiter; + GSList *settings = NULL; + GSList *iter; + const char *name = NULL; + NMSetting *setting; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + if (hints) + g_return_val_if_fail (*hints == NULL, NULL); + + priv = NM_CONNECTION_GET_PRIVATE (connection); + + /* Get list of settings in priority order */ + g_hash_table_iter_init (&hiter, priv->settings); + while (g_hash_table_iter_next (&hiter, NULL, (gpointer) &setting)) + settings = g_slist_insert_sorted (settings, setting, _nm_setting_compare_priority); + + for (iter = settings; iter; iter = g_slist_next (iter)) { + GPtrArray *secrets; + + setting = NM_SETTING (iter->data); + secrets = nm_setting_need_secrets (setting); + if (secrets) { + if (hints) + *hints = secrets; + else + g_ptr_array_free (secrets, TRUE); + + name = nm_setting_get_name (setting); + break; + } + } + + g_slist_free (settings); + return name; +} + +/** + * nm_connection_clear_secrets: + * @connection: the #NMConnection + * + * Clears and frees any secrets that may be stored in the connection, to avoid + * keeping secret data in memory when not needed. + **/ +void +nm_connection_clear_secrets (NMConnection *connection) +{ + GHashTableIter iter; + NMSetting *setting; + gboolean changed = FALSE; + + g_return_if_fail (NM_IS_CONNECTION (connection)); + + g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) { + g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection); + changed |= _nm_setting_clear_secrets (setting); + g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection); + } + + g_signal_emit (connection, signals[SECRETS_CLEARED], 0); + if (changed) + g_signal_emit (connection, signals[CHANGED], 0); +} + +/** + * nm_connection_clear_secrets_with_flags: + * @connection: the #NMConnection + * @func: (scope call): function to be called to determine whether a + * specific secret should be cleared or not + * @user_data: caller-supplied data passed to @func + * + * Clears and frees secrets determined by @func. + **/ +void +nm_connection_clear_secrets_with_flags (NMConnection *connection, + NMSettingClearSecretsWithFlagsFn func, + gpointer user_data) +{ + GHashTableIter iter; + NMSetting *setting; + gboolean changed = FALSE; + + g_return_if_fail (NM_IS_CONNECTION (connection)); + + g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) { + g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection); + changed |= _nm_setting_clear_secrets_with_flags (setting, func, user_data); + g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection); + } + + g_signal_emit (connection, signals[SECRETS_CLEARED], 0); + if (changed) + g_signal_emit (connection, signals[CHANGED], 0); +} + +/** + * nm_connection_to_hash: + * @connection: the #NMConnection + * @flags: hash flags, e.g. %NM_SETTING_HASH_FLAG_ALL + * + * Converts the #NMConnection into a #GHashTable describing the connection, + * suitable for marshalling over D-Bus or serializing. The hash table mapping + * is string:#GHashTable with each element in the returned hash representing + * a #NMSetting object. The keys are setting object names, and the values + * are #GHashTables mapping string:GValue, each of which represents the + * properties of the #NMSetting object. + * + * Returns: (transfer full) (element-type utf8 GLib.HashTable): a new + * #GHashTable describing the connection, its settings, and each setting's + * properties. The caller owns the hash table and must unref the hash table + * with g_hash_table_unref() when it is no longer needed. + **/ +GHashTable * +nm_connection_to_hash (NMConnection *connection, NMSettingHashFlags flags) +{ + NMConnectionPrivate *priv; + GHashTableIter iter; + gpointer key, data; + GHashTable *ret, *setting_hash; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + ret = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) g_hash_table_destroy); + + priv = NM_CONNECTION_GET_PRIVATE (connection); + + /* Add each setting's hash to the main hash */ + g_hash_table_iter_init (&iter, priv->settings); + while (g_hash_table_iter_next (&iter, &key, &data)) { + NMSetting *setting = NM_SETTING (data); + + setting_hash = nm_setting_to_hash (setting, flags); + if (setting_hash) + g_hash_table_insert (ret, g_strdup (nm_setting_get_name (setting)), setting_hash); + } + + /* Don't send empty hashes */ + if (g_hash_table_size (ret) < 1) { + g_hash_table_destroy (ret); + ret = NULL; + } + + return ret; +} + +/** + * nm_connection_is_type: + * @connection: the #NMConnection + * @type: a setting name to check the connection's type against (like + * %NM_SETTING_WIRELESS_SETTING_NAME or %NM_SETTING_WIRED_SETTING_NAME) + * + * A convenience function to check if the given @connection is a particular + * type (ie wired, Wi-Fi, ppp, etc). Checks the #NMSettingConnection:type + * property of the connection and matches that against @type. + * + * Returns: %TRUE if the connection is of the given @type, %FALSE if not + **/ +gboolean +nm_connection_is_type (NMConnection *connection, const char *type) +{ + NMSettingConnection *s_con; + const char *type2; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (type != NULL, FALSE); + + s_con = nm_connection_get_setting_connection (connection); + if (!s_con) + return FALSE; + + type2 = nm_setting_connection_get_connection_type (s_con); + + return (g_strcmp0 (type2, type) == 0); +} + +/** + * nm_connection_for_each_setting_value: + * @connection: the #NMConnection + * @func: (scope call): user-supplied function called for each setting's property + * @user_data: user data passed to @func at each invocation + * + * Iterates over the properties of each #NMSetting object in the #NMConnection, + * calling the supplied user function for each property. + **/ +void +nm_connection_for_each_setting_value (NMConnection *connection, + NMSettingValueIterFn func, + gpointer user_data) +{ + GHashTableIter iter; + gpointer value; + + g_return_if_fail (NM_IS_CONNECTION (connection)); + g_return_if_fail (func != NULL); + + g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings); + while (g_hash_table_iter_next (&iter, NULL, &value)) + nm_setting_enumerate_values (NM_SETTING (value), func, user_data); +} + +/** + * nm_connection_dump: + * @connection: the #NMConnection + * + * Print the connection to stdout. For debugging purposes ONLY, should NOT + * be used for serialization of the connection or machine-parsed in any way. The + * output format is not guaranteed to be stable and may change at any time. + **/ +void +nm_connection_dump (NMConnection *connection) +{ + GHashTableIter iter; + NMSetting *setting; + const char *setting_name; + char *str; + + if (!connection) + return; + + g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings); + while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, (gpointer) &setting)) { + str = nm_setting_to_string (setting); + g_print ("%s\n", str); + g_free (str); + } +} + +/** + * nm_connection_set_path: + * @connection: the #NMConnection + * @path: the D-Bus path of the connection as given by the settings service + * which provides the connection + * + * Sets the D-Bus path of the connection. This property is not serialized, and + * is only for the reference of the caller. Sets the #NMConnection:path + * property. + **/ +void +nm_connection_set_path (NMConnection *connection, const char *path) +{ + NMConnectionPrivate *priv; + + g_return_if_fail (NM_IS_CONNECTION (connection)); + + priv = NM_CONNECTION_GET_PRIVATE (connection); + + g_free (priv->path); + priv->path = NULL; + + if (path) + priv->path = g_strdup (path); +} + +/** + * nm_connection_get_path: + * @connection: the #NMConnection + * + * Returns the connection's D-Bus path. + * + * Returns: the D-Bus path of the connection, previously set by a call to + * nm_connection_set_path(). + **/ +const char * +nm_connection_get_path (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return NM_CONNECTION_GET_PRIVATE (connection)->path; +} + +/** + * nm_connection_get_interface_name: + * @connection: The #NMConnection + * + * Returns the interface name as stored in NMSettingConnection:interface_name. + * If the connection contains no NMSettingConnection, it will return %NULL. + * + * For hardware devices and software devices created outside of NetworkManager, + * this name is used to match the device. for software devices created by + * NetworkManager, this is the name of the created interface. + * + * Returns: Name of the kernel interface or %NULL + * + * Since: 1.0 + */ +const char * +nm_connection_get_interface_name (NMConnection *connection) +{ + NMSettingConnection *s_con; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + s_con = nm_connection_get_setting_connection (connection); + + return s_con ? nm_setting_connection_get_interface_name (s_con) : NULL; +} + +/** + * nm_connection_get_virtual_iface_name: + * @connection: The #NMConnection + * + * Returns the name of the virtual kernel interface which the connection + * needs to use if specified in the settings. This function abstracts all + * connection types which require this functionality. For all other + * connection types, this function will return %NULL. + * + * Returns: Name of the kernel interface or %NULL + */ +const char * +nm_connection_get_virtual_iface_name (NMConnection *connection) +{ + NMSetting *base; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + base = _get_type_setting (connection); + g_assert (base); + + return nm_setting_get_virtual_iface_name (base); +} + +/** + * nm_connection_new: + * + * Creates a new #NMConnection object with no #NMSetting objects. + * + * Returns: the new empty #NMConnection object + **/ +NMConnection * +nm_connection_new (void) +{ + return (NMConnection *) g_object_new (NM_TYPE_CONNECTION, NULL); +} + +/** + * nm_connection_new_from_hash: + * @hash: (element-type utf8 GLib.HashTable): the #GHashTable describing + * the connection + * @error: on unsuccessful return, an error + * + * Creates a new #NMConnection from a hash table describing the connection. See + * nm_connection_to_hash() for a description of the expected hash table. + * + * Returns: the new #NMConnection object, populated with settings created + * from the values in the hash table, or %NULL if the connection failed to + * validate + **/ +NMConnection * +nm_connection_new_from_hash (GHashTable *hash, GError **error) +{ + NMConnection *connection; + + g_return_val_if_fail (hash != NULL, NULL); + + if (!validate_permissions_type (hash, error)) + return NULL; + + connection = nm_connection_new (); + if (!hash_to_connection (connection, hash, error)) { + g_object_unref (connection); + return NULL; + } + return connection; +} + +/** + * nm_connection_duplicate: + * @connection: the #NMConnection to duplicate + * + * Duplicates a #NMConnection. + * + * Returns: (transfer full): a new #NMConnection containing the same settings and properties + * as the source #NMConnection + **/ +NMConnection * +nm_connection_duplicate (NMConnection *connection) +{ + NMConnection *dup; + GHashTableIter iter; + NMSetting *setting; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + dup = nm_connection_new (); + nm_connection_set_path (dup, nm_connection_get_path (connection)); + + g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) + nm_connection_add_setting (dup, nm_setting_duplicate (setting)); + + return dup; +} + +/** + * nm_connection_get_uuid: + * @connection: the #NMConnection + * + * A shortcut to return the UUID from the connection's #NMSettingConnection. + * + * Returns: the UUID from the connection's 'connection' setting + **/ +const char * +nm_connection_get_uuid (NMConnection *connection) +{ + NMSettingConnection *s_con; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (s_con != NULL, NULL); + + return nm_setting_connection_get_uuid (s_con); +} + +/** + * nm_connection_get_id: + * @connection: the #NMConnection + * + * A shortcut to return the ID from the connection's #NMSettingConnection. + * + * Returns: the ID from the connection's 'connection' setting + **/ +const char * +nm_connection_get_id (NMConnection *connection) +{ + NMSettingConnection *s_con; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (s_con != NULL, NULL); + + return nm_setting_connection_get_id (s_con); +} + +/** + * nm_connection_get_connection_type: + * @connection: the #NMConnection + * + * A shortcut to return the type from the connection's #NMSettingConnection. + * + * Returns: the type from the connection's 'connection' setting + * + * Since: 0.9.10 + **/ +const char * +nm_connection_get_connection_type (NMConnection *connection) +{ + NMSettingConnection *s_con; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (s_con != NULL, NULL); + + return nm_setting_connection_get_connection_type (s_con); +} + +/** + * nm_connection_get_virtual_device_description: + * @connection: an #NMConnection for a virtual device type + * + * Returns the name that nm_device_disambiguate_names() would + * return for the virtual device that would be created for @connection. + * Eg, "VLAN (eth1.1)". + * + * Returns: (transfer full): the name of @connection's device, + * or %NULL if @connection is not a virtual connection type + * + * Since: 0.9.10 + */ +char * +nm_connection_get_virtual_device_description (NMConnection *connection) +{ + const char *iface, *type, *display_type; + NMSettingConnection *s_con; + + iface = nm_connection_get_virtual_iface_name (connection); + if (!iface) + return NULL; + + s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (s_con != NULL, NULL); + type = nm_setting_connection_get_connection_type (s_con); + + if (!strcmp (type, NM_SETTING_BOND_SETTING_NAME)) + display_type = _("Bond"); + else if (!strcmp (type, NM_SETTING_TEAM_SETTING_NAME)) + display_type = _("Team"); + else if (!strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME)) + display_type = _("Bridge"); + else if (!strcmp (type, NM_SETTING_VLAN_SETTING_NAME)) + display_type = _("VLAN"); + else { + g_warning ("Unrecognized virtual device type '%s'", type); + display_type = type; + } + + return g_strdup_printf ("%s (%s)", display_type, iface); +} + +/*************************************************************/ + +/** + * nm_connection_get_setting_802_1x: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSetting8021x the connection might contain. + * + * Returns: (transfer none): an #NMSetting8021x if the connection contains one, otherwise %NULL + **/ +NMSetting8021x * +nm_connection_get_setting_802_1x (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X); +} + +/** + * nm_connection_get_setting_bluetooth: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingBluetooth the connection might contain. + * + * Returns: (transfer none): an #NMSettingBluetooth if the connection contains one, otherwise %NULL + **/ +NMSettingBluetooth * +nm_connection_get_setting_bluetooth (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingBluetooth *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH); +} + +/** + * nm_connection_get_setting_bond: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingBond the connection might contain. + * + * Returns: (transfer none): an #NMSettingBond if the connection contains one, otherwise %NULL + **/ +NMSettingBond * +nm_connection_get_setting_bond (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingBond *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BOND); +} + +/** + * nm_connection_get_setting_team: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingTeam the connection might contain. + * + * Returns: (transfer none): an #NMSettingTeam if the connection contains one, otherwise %NULL + * + * Since: 0.9.10 + **/ +NMSettingTeam * +nm_connection_get_setting_team (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingTeam *) nm_connection_get_setting (connection, NM_TYPE_SETTING_TEAM); +} + +/** + * nm_connection_get_setting_team_port: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingTeamPort the connection might contain. + * + * Returns: (transfer none): an #NMSettingTeamPort if the connection contains one, otherwise %NULL + * + * Since: 0.9.10 + **/ +NMSettingTeamPort * +nm_connection_get_setting_team_port (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingTeamPort *) nm_connection_get_setting (connection, NM_TYPE_SETTING_TEAM_PORT); +} + +/** + * nm_connection_get_setting_bridge: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingBridge the connection might contain. + * + * Returns: (transfer none): an #NMSettingBridge if the connection contains one, otherwise %NULL + **/ +NMSettingBridge * +nm_connection_get_setting_bridge (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingBridge *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BRIDGE); +} + +/** + * nm_connection_get_setting_cdma: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingCdma the connection might contain. + * + * Returns: (transfer none): an #NMSettingCdma if the connection contains one, otherwise %NULL + **/ +NMSettingCdma * +nm_connection_get_setting_cdma (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingCdma *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA); +} + +/** + * nm_connection_get_setting_connection: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingConnection the connection might contain. + * + * Returns: (transfer none): an #NMSettingConnection if the connection contains one, otherwise %NULL + **/ +NMSettingConnection * +nm_connection_get_setting_connection (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); +} + +/** + * nm_connection_get_setting_dcb: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingDcb the connection might contain. + * + * Returns: (transfer none): an #NMSettingDcb if the connection contains one, otherwise NULL + * + * Since: 0.9.10 + **/ +NMSettingDcb * +nm_connection_get_setting_dcb (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingDcb *) nm_connection_get_setting (connection, NM_TYPE_SETTING_DCB); +} + +/** + * nm_connection_get_setting_generic: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingGeneric the connection might contain. + * + * Returns: (transfer none): an #NMSettingGeneric if the connection contains one, otherwise NULL + * + * Since: 0.9.10 + **/ +NMSettingGeneric * +nm_connection_get_setting_generic (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingGeneric *) nm_connection_get_setting (connection, NM_TYPE_SETTING_GENERIC); +} + +/** + * nm_connection_get_setting_gsm: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingGsm the connection might contain. + * + * Returns: (transfer none): an #NMSettingGsm if the connection contains one, otherwise %NULL + **/ +NMSettingGsm * +nm_connection_get_setting_gsm (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingGsm *) nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM); +} + +/** + * nm_connection_get_setting_infiniband: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingInfiniband the connection might contain. + * + * Returns: (transfer none): an #NMSettingInfiniband if the connection contains one, otherwise %NULL + **/ +NMSettingInfiniband * +nm_connection_get_setting_infiniband (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingInfiniband *) nm_connection_get_setting (connection, NM_TYPE_SETTING_INFINIBAND); +} + +/** + * nm_connection_get_setting_ip4_config: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingIP4Config the connection might contain. + * + * Returns: (transfer none): an #NMSettingIP4Config if the connection contains one, otherwise %NULL + **/ +NMSettingIP4Config * +nm_connection_get_setting_ip4_config (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); +} + +/** + * nm_connection_get_setting_ip6_config: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingIP6Config the connection might contain. + * + * Returns: (transfer none): an #NMSettingIP6Config if the connection contains one, otherwise %NULL + **/ +NMSettingIP6Config * +nm_connection_get_setting_ip6_config (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); +} + +/** + * nm_connection_get_setting_olpc_mesh: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingOlpcMesh the connection might contain. + * + * Returns: (transfer none): an #NMSettingOlpcMesh if the connection contains one, otherwise %NULL + **/ +NMSettingOlpcMesh * +nm_connection_get_setting_olpc_mesh (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingOlpcMesh *) nm_connection_get_setting (connection, NM_TYPE_SETTING_OLPC_MESH); +} + +/** + * nm_connection_get_setting_ppp: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingPPP the connection might contain. + * + * Returns: (transfer none): an #NMSettingPPP if the connection contains one, otherwise %NULL + **/ +NMSettingPPP * +nm_connection_get_setting_ppp (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingPPP *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP); +} + +/** + * nm_connection_get_setting_pppoe: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingPPPOE the connection might contain. + * + * Returns: (transfer none): an #NMSettingPPPOE if the connection contains one, otherwise %NULL + **/ +NMSettingPPPOE * +nm_connection_get_setting_pppoe (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingPPPOE *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE); +} + +/** + * nm_connection_get_setting_serial: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingSerial the connection might contain. + * + * Returns: (transfer none): an #NMSettingSerial if the connection contains one, otherwise %NULL + **/ +NMSettingSerial * +nm_connection_get_setting_serial (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingSerial *) nm_connection_get_setting (connection, NM_TYPE_SETTING_SERIAL); +} + +/** + * nm_connection_get_setting_vpn: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingVPN the connection might contain. + * + * Returns: (transfer none): an #NMSettingVPN if the connection contains one, otherwise %NULL + **/ +NMSettingVPN * +nm_connection_get_setting_vpn (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingVPN *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN); +} + +/** + * nm_connection_get_setting_wimax: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingWimax the connection might contain. + * + * Returns: (transfer none): an #NMSettingWimax if the connection contains one, otherwise %NULL + **/ +NMSettingWimax * +nm_connection_get_setting_wimax (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingWimax *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIMAX); +} + +/** + * nm_connection_get_setting_wired: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingWired the connection might contain. + * + * Returns: (transfer none): an #NMSettingWired if the connection contains one, otherwise %NULL + **/ +NMSettingWired * +nm_connection_get_setting_wired (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); +} + +/** + * nm_connection_get_setting_adsl: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingAdsl the connection might contain. + * + * Returns: (transfer none): an #NMSettingAdsl if the connection contains one, otherwise %NULL + **/ +NMSettingAdsl * +nm_connection_get_setting_adsl (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingAdsl *) nm_connection_get_setting (connection, NM_TYPE_SETTING_ADSL); +} + +/** + * nm_connection_get_setting_wireless: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingWireless the connection might contain. + * + * Returns: (transfer none): an #NMSettingWireless if the connection contains one, otherwise %NULL + **/ +NMSettingWireless * +nm_connection_get_setting_wireless (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); +} + +/** + * nm_connection_get_setting_wireless_security: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingWirelessSecurity the connection might contain. + * + * Returns: (transfer none): an #NMSettingWirelessSecurity if the connection contains one, otherwise %NULL + **/ +NMSettingWirelessSecurity * +nm_connection_get_setting_wireless_security (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); +} + +/** + * nm_connection_get_setting_bridge_port: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingBridgePort the connection might contain. + * + * Returns: (transfer none): an #NMSettingBridgePort if the connection contains one, otherwise %NULL + **/ +NMSettingBridgePort * +nm_connection_get_setting_bridge_port (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingBridgePort *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BRIDGE_PORT); +} + +/** + * nm_connection_get_setting_vlan: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingVlan the connection might contain. + * + * Returns: (transfer none): an #NMSettingVlan if the connection contains one, otherwise %NULL + **/ +NMSettingVlan * +nm_connection_get_setting_vlan (NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingVlan *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VLAN); +} + +/*************************************************************/ + +static void +nm_connection_init (NMConnection *connection) +{ + NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (connection); + + priv->settings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); +} + +static void +dispose (GObject *object) +{ + NMConnection *self = NM_CONNECTION (object); + NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (self); + GHashTableIter iter; + NMSetting *setting; + + g_hash_table_iter_init (&iter, priv->settings); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) { + g_signal_handlers_disconnect_by_func (setting, setting_changed_cb, self); + g_hash_table_iter_remove (&iter); + } + + G_OBJECT_CLASS (nm_connection_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMConnection *connection = NM_CONNECTION (object); + NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (connection); + + g_hash_table_destroy (priv->settings); + g_free (priv->path); + + G_OBJECT_CLASS (nm_connection_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMConnection *connection = NM_CONNECTION (object); + + switch (prop_id) { + case PROP_PATH: + nm_connection_set_path (connection, g_value_get_string (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) +{ + NMConnection *connection = NM_CONNECTION (object); + + switch (prop_id) { + case PROP_PATH: + g_value_set_string (value, nm_connection_get_path (connection)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_connection_class_init (NMConnectionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMConnectionPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + /* Properties */ + + /** + * NMConnection:path: + * + * The connection's D-Bus path, used only by the calling process as a record + * of the D-Bus path of the connection as provided by a settings service. + **/ + g_object_class_install_property + (object_class, PROP_PATH, + g_param_spec_string (NM_CONNECTION_PATH, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /* Signals */ + + /** + * NMConnection::secrets-updated: + * @connection: the object on which the signal is emitted + * @setting_name: the setting name of the #NMSetting for which secrets were + * updated + * + * The ::secrets-updated signal is emitted when the secrets of a setting + * have been changed. + */ + signals[SECRETS_UPDATED] = + g_signal_new (NM_CONNECTION_SECRETS_UPDATED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMConnectionClass, secrets_updated), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + + /** + * NMConnection::secrets-cleared: + * @connection: the object on which the signal is emitted + * + * The ::secrets-cleared signal is emitted when the secrets of a connection + * are cleared. + */ + signals[SECRETS_CLEARED] = + g_signal_new (NM_CONNECTION_SECRETS_CLEARED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /** + * NMConnection::changed: + * @connection: the object on which the signal is emitted + * + * The ::changed signal is emitted when any property of any property + * (including secrets) of any setting of the connection is modified, + * or when settings are added or removed. + * + * Since: 0.9.10 + */ + signals[CHANGED] = + g_signal_new (NM_CONNECTION_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} diff --git a/libnm-core/nm-connection.h b/libnm-core/nm-connection.h new file mode 100644 index 0000000000..2cee2be560 --- /dev/null +++ b/libnm-core/nm-connection.h @@ -0,0 +1,256 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2013 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_CONNECTION_H +#define NM_CONNECTION_H + +#include <glib.h> +#include <glib-object.h> +#include <nm-setting.h> + +#include <nm-setting-8021x.h> +#include <nm-setting-bluetooth.h> +#include <nm-setting-bond.h> +#include <nm-setting-team.h> +#include <nm-setting-team-port.h> +#include <nm-setting-bridge.h> +#include <nm-setting-bridge-port.h> +#include <nm-setting-cdma.h> +#include <nm-setting-connection.h> +#include <nm-setting-dcb.h> +#include <nm-setting-generic.h> +#include <nm-setting-gsm.h> +#include <nm-setting-infiniband.h> +#include <nm-setting-ip4-config.h> +#include <nm-setting-ip6-config.h> +#include <nm-setting-olpc-mesh.h> +#include <nm-setting-ppp.h> +#include <nm-setting-pppoe.h> +#include <nm-setting-serial.h> +#include <nm-setting-vpn.h> +#include <nm-setting-wimax.h> +#include <nm-setting-wired.h> +#include <nm-setting-adsl.h> +#include <nm-setting-wireless.h> +#include <nm-setting-wireless-security.h> +#include <nm-setting-vlan.h> + +G_BEGIN_DECLS + +#define NM_TYPE_CONNECTION (nm_connection_get_type ()) +#define NM_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CONNECTION, NMConnection)) +#define NM_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_CONNECTION, NMConnectionClass)) +#define NM_IS_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_CONNECTION)) +#define NM_IS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CONNECTION)) +#define NM_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CONNECTION, NMConnectionClass)) + +/* Signals */ +#define NM_CONNECTION_SECRETS_UPDATED "secrets-updated" +#define NM_CONNECTION_SECRETS_CLEARED "secrets-cleared" +#define NM_CONNECTION_CHANGED "changed" + +/* Properties */ +#define NM_CONNECTION_PATH "path" + +/** + * NMConnectionError: + * @NM_CONNECTION_ERROR_UNKNOWN: unknown or unclassified error + * @NM_CONNECTION_ERROR_CONNECTION_SETTING_NOT_FOUND: the #NMConnection object + * did not contain the required #NMSettingConnection object, which must be + * present for all connections + * @NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID: the 'type' property of the + * 'connection' setting did not point to a valid connection base type; ie + * it was not a hardware-related setting like #NMSettingWired or + * #NMSettingWireless. + * @NM_CONNECTION_ERROR_SETTING_NOT_FOUND: the #NMConnection object + * did not contain the specified #NMSetting object + *@NM_CONNECTION_ERROR_INVALID_SETTING: the #NMConnection object contains + * a conflicting setting object + * + * Describes errors that may result from operations involving a #NMConnection. + * + **/ +typedef enum +{ + NM_CONNECTION_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_CONNECTION_ERROR_CONNECTION_SETTING_NOT_FOUND, /*< nick=ConnectionSettingNotFound >*/ + NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID, /*< nick=ConnectionTypeInvalid >*/ + NM_CONNECTION_ERROR_SETTING_NOT_FOUND, /*< nick=SettingNotFound >*/ + NM_CONNECTION_ERROR_INVALID_SETTING, /*< nick=InvalidSetting >*/ +} NMConnectionError; + +/* + * NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD: overwrite the ip6 method + * when normalizing ip6 configuration. If omited, this defaults to + * @NM_SETTING_IP6_CONFIG_METHOD_AUTO. + */ +#define NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD "ip6-config-method" + +#define NM_CONNECTION_ERROR nm_connection_error_quark () +GQuark nm_connection_error_quark (void); + +/** + * NMConnection: + * + * The NMConnection struct contains only private data. + * It should only be accessed through the functions described below. + */ +typedef struct { + GObject parent; +} NMConnection; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*secrets_updated) (NMConnection *connection, const char * setting); +} NMConnectionClass; + +GType nm_connection_get_type (void); + +NMConnection *nm_connection_new (void); + +NMConnection *nm_connection_new_from_hash (GHashTable *hash, GError **error); + +NMConnection *nm_connection_duplicate (NMConnection *connection); + +NMSetting *nm_connection_create_setting (const char *name); + +void nm_connection_add_setting (NMConnection *connection, + NMSetting *setting); + +void nm_connection_remove_setting (NMConnection *connection, + GType setting_type); + +NMSetting *nm_connection_get_setting (NMConnection *connection, + GType setting_type); + +NMSetting *nm_connection_get_setting_by_name (NMConnection *connection, + const char *name); + +gboolean nm_connection_replace_settings (NMConnection *connection, + GHashTable *new_settings, + GError **error); + +NM_AVAILABLE_IN_0_9_10 +gboolean nm_connection_replace_settings_from_connection (NMConnection *connection, + NMConnection *new_connection, + GError **error); + +gboolean nm_connection_compare (NMConnection *a, + NMConnection *b, + NMSettingCompareFlags flags); + +gboolean nm_connection_diff (NMConnection *a, + NMConnection *b, + NMSettingCompareFlags flags, + GHashTable **out_settings); + +gboolean nm_connection_verify (NMConnection *connection, GError **error); +NM_AVAILABLE_IN_1_0 +gboolean nm_connection_normalize (NMConnection *connection, + GHashTable *parameters, + gboolean *modified, + GError **error); + +const char * nm_connection_need_secrets (NMConnection *connection, + GPtrArray **hints); + +void nm_connection_clear_secrets (NMConnection *connection); + +void nm_connection_clear_secrets_with_flags (NMConnection *connection, + NMSettingClearSecretsWithFlagsFn func, + gpointer user_data); + +gboolean nm_connection_update_secrets (NMConnection *connection, + const char *setting_name, + GHashTable *secrets, + GError **error); + +void nm_connection_set_path (NMConnection *connection, + const char *path); + +const char * nm_connection_get_path (NMConnection *connection); + +const char * nm_connection_get_virtual_iface_name (NMConnection *connection); + +NM_AVAILABLE_IN_1_0 +const char * nm_connection_get_interface_name (NMConnection *connection); + +gboolean nm_connection_is_type (NMConnection *connection, const char *type); + +void nm_connection_for_each_setting_value (NMConnection *connection, + NMSettingValueIterFn func, + gpointer user_data); + +GHashTable *nm_connection_to_hash (NMConnection *connection, + NMSettingHashFlags flags); + +void nm_connection_dump (NMConnection *connection); + +GType nm_connection_lookup_setting_type (const char *name); + +GType nm_connection_lookup_setting_type_by_quark (GQuark error_quark); + +/* Helpers */ +const char * nm_connection_get_uuid (NMConnection *connection); +const char * nm_connection_get_id (NMConnection *connection); +NM_AVAILABLE_IN_0_9_10 +const char * nm_connection_get_connection_type (NMConnection *connection); + +NM_AVAILABLE_IN_0_9_10 +char * nm_connection_get_virtual_device_description (NMConnection *connection); + +NMSetting8021x * nm_connection_get_setting_802_1x (NMConnection *connection); +NMSettingBluetooth * nm_connection_get_setting_bluetooth (NMConnection *connection); +NMSettingBond * nm_connection_get_setting_bond (NMConnection *connection); +NM_AVAILABLE_IN_0_9_10 +NMSettingTeam * nm_connection_get_setting_team (NMConnection *connection); +NM_AVAILABLE_IN_0_9_10 +NMSettingTeamPort * nm_connection_get_setting_team_port (NMConnection *connection); +NMSettingBridge * nm_connection_get_setting_bridge (NMConnection *connection); +NMSettingBridgePort * nm_connection_get_setting_bridge_port (NMConnection *connection); +NMSettingCdma * nm_connection_get_setting_cdma (NMConnection *connection); +NMSettingConnection * nm_connection_get_setting_connection (NMConnection *connection); +NM_AVAILABLE_IN_0_9_10 +NMSettingDcb * nm_connection_get_setting_dcb (NMConnection *connection); +NM_AVAILABLE_IN_0_9_10 +NMSettingGeneric * nm_connection_get_setting_generic (NMConnection *connection); +NMSettingGsm * nm_connection_get_setting_gsm (NMConnection *connection); +NMSettingInfiniband * nm_connection_get_setting_infiniband (NMConnection *connection); +NMSettingIP4Config * nm_connection_get_setting_ip4_config (NMConnection *connection); +NMSettingIP6Config * nm_connection_get_setting_ip6_config (NMConnection *connection); +NMSettingOlpcMesh * nm_connection_get_setting_olpc_mesh (NMConnection *connection); +NMSettingPPP * nm_connection_get_setting_ppp (NMConnection *connection); +NMSettingPPPOE * nm_connection_get_setting_pppoe (NMConnection *connection); +NMSettingSerial * nm_connection_get_setting_serial (NMConnection *connection); +NMSettingVPN * nm_connection_get_setting_vpn (NMConnection *connection); +NMSettingWimax * nm_connection_get_setting_wimax (NMConnection *connection); +NMSettingAdsl * nm_connection_get_setting_adsl (NMConnection *connection); +NMSettingWired * nm_connection_get_setting_wired (NMConnection *connection); +NMSettingWireless * nm_connection_get_setting_wireless (NMConnection *connection); +NMSettingWirelessSecurity *nm_connection_get_setting_wireless_security (NMConnection *connection); +NMSettingVlan * nm_connection_get_setting_vlan (NMConnection *connection); + +G_END_DECLS + +#endif /* NM_CONNECTION_H */ diff --git a/libnm-core/nm-param-spec-specialized.c b/libnm-core/nm-param-spec-specialized.c new file mode 100644 index 0000000000..2b4ca230a0 --- /dev/null +++ b/libnm-core/nm-param-spec-specialized.c @@ -0,0 +1,972 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2011 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include "nm-param-spec-specialized.h" +#include "nm-glib-compat.h" + +struct _NMParamSpecSpecialized { + GParamSpec parent; +}; + +#include <string.h> +#include <math.h> +#include <netinet/in.h> +#include <dbus/dbus-glib.h> + +#include "nm-dbus-glib-types.h" + +/***********************************************************/ +/* _gvalues_compare */ + +static gint _gvalues_compare (const GValue *value1, const GValue *value2); + +static gboolean +type_is_fixed_size (GType type, gsize *tsize) +{ + switch (type) { + case G_TYPE_CHAR: + if (tsize) *tsize = sizeof (char); + return TRUE; + case G_TYPE_UCHAR: + if (tsize) *tsize = sizeof (guchar); + return TRUE; + case G_TYPE_BOOLEAN: + if (tsize) *tsize = sizeof (gboolean); + return TRUE; + case G_TYPE_LONG: + if (tsize) *tsize = sizeof (glong); + return TRUE; + case G_TYPE_ULONG: + if (tsize) *tsize = sizeof (gulong); + return TRUE; + case G_TYPE_INT: + if (tsize) *tsize = sizeof (gint); + return TRUE; + case G_TYPE_UINT: + if (tsize) *tsize = sizeof (guint); + return TRUE; + case G_TYPE_INT64: + if (tsize) *tsize = sizeof (gint64); + return TRUE; + case G_TYPE_UINT64: + if (tsize) *tsize = sizeof (guint64); + return TRUE; + case G_TYPE_FLOAT: + if (tsize) *tsize = sizeof (gfloat); + return TRUE; + case G_TYPE_DOUBLE: + if (tsize) *tsize = sizeof (gdouble); + return TRUE; + default: + return FALSE; + } +} + +#define FLOAT_FACTOR 0.00000001 + +static gint +_gvalues_compare_fixed (const GValue *value1, const GValue *value2) +{ + int ret = 0; + + switch (G_VALUE_TYPE (value1)) { + case G_TYPE_CHAR: { + gchar val1 = g_value_get_schar (value1); + gchar val2 = g_value_get_schar (value2); + if (val1 != val2) + ret = val1 < val2 ? -1 : val1 > val2; + break; + } + case G_TYPE_UCHAR: { + guchar val1 = g_value_get_uchar (value1); + guchar val2 = g_value_get_uchar (value2); + if (val1 != val2) + ret = val1 < val2 ? -1 : val1 > val2; + break; + } + case G_TYPE_BOOLEAN: { + gboolean val1 = g_value_get_boolean (value1); + gboolean val2 = g_value_get_boolean (value2); + if (val1 != val2) + ret = val1 < val2 ? -1 : val1 > val2; + break; + } + case G_TYPE_LONG: { + glong val1 = g_value_get_long (value1); + glong val2 = g_value_get_long (value2); + if (val1 != val2) + ret = val1 < val2 ? -1 : val1 > val2; + break; + } + case G_TYPE_ULONG: { + gulong val1 = g_value_get_ulong (value1); + gulong val2 = g_value_get_ulong (value2); + if (val1 != val2) + ret = val1 < val2 ? -1 : val1 > val2; + break; + } + case G_TYPE_INT: { + gint val1 = g_value_get_int (value1); + gint val2 = g_value_get_int (value2); + if (val1 != val2) + ret = val1 < val2 ? -1 : val1 > val2; + break; + } + case G_TYPE_UINT: { + guint val1 = g_value_get_uint (value1); + guint val2 = g_value_get_uint (value2); + if (val1 != val2) + ret = val1 < val2 ? -1 : val1 > val2; + break; + } + case G_TYPE_INT64: { + gint64 val1 = g_value_get_int64 (value1); + gint64 val2 = g_value_get_int64 (value2); + if (val1 != val2) + ret = val1 < val2 ? -1 : val1 > val2; + break; + } + case G_TYPE_UINT64: { + guint64 val1 = g_value_get_uint64 (value1); + guint64 val2 = g_value_get_uint64 (value2); + if (val1 != val2) + ret = val1 < val2 ? -1 : val1 > val2; + break; + } + case G_TYPE_FLOAT: { + gfloat val1 = g_value_get_float (value1); + gfloat val2 = g_value_get_float (value2); + /* Can't use == or != here due to inexactness of FP */ + if (fabsf (val1 - val2) > FLOAT_FACTOR) + ret = val1 < val2 ? -1 : val1 > val2; + break; + } + case G_TYPE_DOUBLE: { + gdouble val1 = g_value_get_double (value1); + gdouble val2 = g_value_get_double (value2); + if (fabs (val1 - val2) > FLOAT_FACTOR) + ret = val1 < val2 ? -1 : val1 > val2; + break; + } + default: + g_warning ("Unhandled fixed size type '%s'", G_VALUE_TYPE_NAME (value1)); + } + + return ret; +} + +static gint +_gvalues_compare_string (const GValue *value1, const GValue *value2) +{ + const char *str1 = g_value_get_string (value1); + const char *str2 = g_value_get_string (value2); + + if (str1 == str2) + return 0; + + if (!str1) + return 1; + if (!str2) + return -1; + + return strcmp (str1, str2); +} + +static gint +_gvalues_compare_strv (const GValue *value1, const GValue *value2) +{ + char **strv1; + char **strv2; + gint ret; + guint i = 0; + + strv1 = (char **) g_value_get_boxed (value1); + strv2 = (char **) g_value_get_boxed (value2); + + while (strv1[i] && strv2[i]) { + ret = strcmp (strv1[i], strv2[i]); + if (ret) + return ret; + i++; + } + + if (strv1[i] == NULL && strv2[i] == NULL) + return 0; + + if (strv1[i]) + return 1; + + return -1; +} + +static void +_gvalue_destroy (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +static GValue * +_gvalue_dup (const GValue *value) +{ + GValue *dup; + + dup = g_slice_new0 (GValue); + g_value_init (dup, G_VALUE_TYPE (value)); + g_value_copy (value, dup); + + return dup; +} + +static void +iterate_collection (const GValue *value, gpointer user_data) +{ + GSList **list = (GSList **) user_data; + + *list = g_slist_prepend (*list, _gvalue_dup (value)); +} + +static gint +_gvalues_compare_collection (const GValue *value1, const GValue *value2) +{ + gint ret; + guint len1; + guint len2; + GType value_type = dbus_g_type_get_collection_specialization (G_VALUE_TYPE (value1)); + gsize element_size = 0; + + if (type_is_fixed_size (value_type, &element_size)) { + gpointer data1 = NULL; + gpointer data2 = NULL; + + dbus_g_type_collection_get_fixed ((GValue *) value1, &data1, &len1); + dbus_g_type_collection_get_fixed ((GValue *) value2, &data2, &len2); + + if (len1 != len2) + ret = len1 < len2 ? -1 : len1 > len2; + else + ret = memcmp (data1, data2, len1 * element_size); + } else { + GSList *list1 = NULL; + GSList *list2 = NULL; + + dbus_g_type_collection_value_iterate (value1, iterate_collection, &list1); + len1 = g_slist_length (list1); + dbus_g_type_collection_value_iterate (value2, iterate_collection, &list2); + len2 = g_slist_length (list2); + + if (len1 != len2) + ret = len1 < len2 ? -1 : len1 > len2; + else { + GSList *iter1; + GSList *iter2; + + for (iter1 = list1, iter2 = list2, ret = 0; + ret == 0 && iter1 && iter2; + iter1 = iter1->next, iter2 = iter2->next) + ret = _gvalues_compare ((GValue *) iter1->data, (GValue *) iter2->data); + } + + g_slist_free_full (list1, _gvalue_destroy); + g_slist_free_full (list2, _gvalue_destroy); + } + + return ret; +} + +static void +iterate_map (const GValue *key_val, + const GValue *value_val, + gpointer user_data) +{ + GHashTable **hash = (GHashTable **) user_data; + + g_hash_table_insert (*hash, g_value_dup_string (key_val), _gvalue_dup (value_val)); +} + +typedef struct { + GHashTable *hash2; + gint ret; +} CompareMapInfo; + +static void +compare_one_map_item (gpointer key, gpointer val, gpointer user_data) +{ + CompareMapInfo *info = (CompareMapInfo *) user_data; + GValue *value2; + + if (info->ret) + return; + + value2 = (GValue *) g_hash_table_lookup (info->hash2, key); + if (value2) + info->ret = _gvalues_compare ((GValue *) val, value2); + else + info->ret = 1; +} + +static gint +_gvalues_compare_map (const GValue *value1, const GValue *value2) +{ + GHashTable *hash1 = NULL; + GHashTable *hash2 = NULL; + guint len1; + guint len2; + gint ret = 0; + + if (dbus_g_type_get_map_key_specialization (G_VALUE_TYPE (value1)) != G_TYPE_STRING) { + g_warning ("Can not compare maps with '%s' for keys", + g_type_name (dbus_g_type_get_map_key_specialization (G_VALUE_TYPE (value1)))); + return 0; + } + + hash1 = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _gvalue_destroy); + dbus_g_type_map_value_iterate (value1, iterate_map, &hash1); + len1 = g_hash_table_size (hash1); + + hash2 = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _gvalue_destroy); + dbus_g_type_map_value_iterate (value2, iterate_map, &hash2); + len2 = g_hash_table_size (hash2); + + if (len1 != len2) + ret = len1 < len2 ? -1 : len1 > len2; + else { + CompareMapInfo info; + + info.ret = 0; + info.hash2 = hash2; + g_hash_table_foreach (hash1, compare_one_map_item, &info); + ret = info.ret; + } + + g_hash_table_destroy (hash1); + g_hash_table_destroy (hash2); + + return ret; +} + +static gint +_gvalue_ip6_address_compare (const GValue *value1, const GValue *value2) +{ + GValueArray *values1, *values2; + GValue *tmp_val; + GByteArray *addr1, *addr2; + guint32 prefix1, prefix2; + GByteArray *gw1, *gw2; + gint ret = 0; + int i; + + /* IP6 addresses are GValueArrays (see nm-dbus-glib-types.h) */ + values1 = g_value_get_boxed (value1); + values2 = g_value_get_boxed (value2); + + /* Since they are NM IPv6 address structures, we expect both + * to contain two elements as specified in nm-dbus-glib-types.h. + */ + g_return_val_if_fail (values1->n_values == 3, 0); + g_return_val_if_fail (values2->n_values == 3, 0); + + /* First struct IPv6 address */ + tmp_val = g_value_array_get_nth (values1, 0); + addr1 = g_value_get_boxed (tmp_val); + /* First struct IPv6 prefix */ + tmp_val = g_value_array_get_nth (values1, 1); + prefix1 = g_value_get_uint (tmp_val); + /* First struct IPv6 gateway */ + tmp_val = g_value_array_get_nth (values1, 2); + gw1 = g_value_get_boxed (tmp_val); + + /* Second struct IPv6 address */ + tmp_val = g_value_array_get_nth (values2, 0); + addr2 = g_value_get_boxed (tmp_val); + /* Second struct IPv6 prefix */ + tmp_val = g_value_array_get_nth (values2, 1); + prefix2 = g_value_get_uint (tmp_val); + /* Second struct IPv6 gateway */ + tmp_val = g_value_array_get_nth (values2, 2); + gw2 = g_value_get_boxed (tmp_val); + + /* Compare IPv6 addresses */ + if (prefix1 != prefix2) + return prefix1 < prefix2 ? -1 : prefix1 > prefix2; + + if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *)addr1->data, (struct in6_addr *)addr2->data)) { + for (i = 0; ret == 0 && i < addr1->len; i++) + ret = addr1->data[i] < addr2->data[i] ? -1 : addr1->data[i] > addr2->data[i]; + } + + if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *) gw1->data, (struct in6_addr *) gw2->data)) { + for (i = 0; ret == 0 && i < gw1->len; i++) + ret = gw1->data[i] < gw2->data[i] ? -1 : gw1->data[i] > gw2->data[i]; + } + + return ret; +} + +static gint +_gvalue_ip6_route_compare (const GValue *value1, const GValue *value2) +{ + GValueArray *values1, *values2; + GValue *tmp_val; + GByteArray *dest1, *dest2; + GByteArray *next_hop1, *next_hop2; + guint32 prefix1, prefix2; + guint32 metric1, metric2; + gint ret = 0; + int i; + + /* IP6 routes are GValueArrays (see nm-dbus-glib-types.h) */ + values1 = g_value_get_boxed (value1); + values2 = g_value_get_boxed (value2); + + /* Since they are NM IPv6 route structures, we expect both + * to contain 4 elements as specified in nm-dbus-glib-types.h. + */ + g_return_val_if_fail (values1->n_values == 4, 0); + g_return_val_if_fail (values2->n_values == 4, 0); + + /* First struct IPv6 route */ + tmp_val = g_value_array_get_nth (values1, 0); + dest1 = g_value_get_boxed (tmp_val); + tmp_val = g_value_array_get_nth (values1, 1); + prefix1 = g_value_get_uint (tmp_val); + tmp_val = g_value_array_get_nth (values1, 2); + next_hop1 = g_value_get_boxed (tmp_val); + tmp_val = g_value_array_get_nth (values1, 3); + metric1 = g_value_get_uint (tmp_val); + + /* Second struct IPv6 route */ + tmp_val = g_value_array_get_nth (values2, 0); + dest2 = g_value_get_boxed (tmp_val); + tmp_val = g_value_array_get_nth (values2, 1); + prefix2 = g_value_get_uint (tmp_val); + tmp_val = g_value_array_get_nth (values2, 2); + next_hop2 = g_value_get_boxed (tmp_val); + tmp_val = g_value_array_get_nth (values2, 3); + metric2 = g_value_get_uint (tmp_val); + + /* Compare the routes */ + if (prefix1 != prefix2) + return prefix1 < prefix2 ? -1 : prefix1 > prefix2; + + if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *)dest1->data, (struct in6_addr *)dest2->data)) { + for (i = 0; ret == 0 && i < dest1->len; i++) + ret = dest1->data[i] < dest2->data[i] ? -1 : dest1->data[i] > dest2->data[i]; + } + + if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *)next_hop1->data, (struct in6_addr *)next_hop2->data)) { + for (i = 0; ret == 0 && i < next_hop1->len; i++) + ret = next_hop1->data[i] < next_hop2->data[i] ? -1 : next_hop1->data[i] > next_hop2->data[i]; + } + + if (metric1 != metric2) + ret = metric1 < metric2 ? -1 : metric1 > metric2; + + return ret; +} + +static gint +_gvalues_compare_struct (const GValue *value1, const GValue *value2) +{ + /* value1 and value2 must contain the same type since + * _gvalues_compare() enforced that already. + */ + + if (G_VALUE_HOLDS (value1, DBUS_TYPE_G_IP6_ADDRESS)) { + return _gvalue_ip6_address_compare (value1, value2); + } else if (G_VALUE_HOLDS (value1, DBUS_TYPE_G_IP6_ROUTE)) { + return _gvalue_ip6_route_compare (value1, value2); + } else { + g_warning ("Don't know how to compare structures"); + return (value1 == value2); + } +} + +gint +_gvalues_compare (const GValue *value1, const GValue *value2) +{ + GType type1; + GType type2; + gint ret; + + if (value1 == value2) + return 0; + if (!value1) + return 1; + if (!value2) + return -1; + + type1 = G_VALUE_TYPE (value1); + type2 = G_VALUE_TYPE (value2); + + if (type1 != type2) + return type1 < type2 ? -1 : type1 > type2; + + if (type_is_fixed_size (type1, NULL)) + ret = _gvalues_compare_fixed (value1, value2); + else if (type1 == G_TYPE_STRING) + ret = _gvalues_compare_string (value1, value2); + else if (G_VALUE_HOLDS_BOXED (value1)) { + gpointer p1 = g_value_get_boxed (value1); + gpointer p2 = g_value_get_boxed (value2); + + if (p1 == p2) + ret = 0; /* Exactly the same values */ + else if (!p1) + ret = 1; /* The comparision functions below don't handle NULLs */ + else if (!p2) + ret = -1; /* The comparision functions below don't handle NULLs */ + else if (type1 == G_TYPE_STRV) + ret = _gvalues_compare_strv (value1, value2); + else if (dbus_g_type_is_collection (type1)) + ret = _gvalues_compare_collection (value1, value2); + else if (dbus_g_type_is_map (type1)) + ret = _gvalues_compare_map (value1, value2); + else if (dbus_g_type_is_struct (type1)) + ret = _gvalues_compare_struct (value1, value2); + else if (type1 == G_TYPE_VALUE) + ret = _gvalues_compare ((GValue *) g_value_get_boxed (value1), (GValue *) g_value_get_boxed (value2)); + else { + g_warning ("Don't know how to compare boxed types '%s'", g_type_name (type1)); + ret = value1 == value2; + } + } else { + g_warning ("Don't know how to compare types '%s'", g_type_name (type1)); + ret = value1 == value2; + } + + return ret; +} + +/***********************************************************/ + +static void +param_specialized_init (GParamSpec *pspec) +{ +} + +static void +param_specialized_set_default (GParamSpec *pspec, GValue *value) +{ + value->data[0].v_pointer = NULL; +} + +static gboolean +param_specialized_validate (GParamSpec *pspec, GValue *value) +{ + NMParamSpecSpecialized *sspec = NM_PARAM_SPEC_SPECIALIZED (pspec); + GType value_type = G_VALUE_TYPE (value); + gboolean changed = FALSE; + + if (!g_value_type_compatible (value_type, G_PARAM_SPEC_VALUE_TYPE (sspec))) { + g_value_reset (value); + changed = TRUE; + } + + return changed; +} + +static gint +param_specialized_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2) +{ + return _gvalues_compare (value1, value2); +} + +GType +_nm_param_spec_specialized_get_type (void) +{ + static GType type; + + if (G_UNLIKELY (type) == 0) { + static const GParamSpecTypeInfo pspec_info = { + sizeof (NMParamSpecSpecialized), + 0, + param_specialized_init, + G_TYPE_OBJECT, /* value_type */ + NULL, /* finalize */ + param_specialized_set_default, + param_specialized_validate, + param_specialized_values_cmp, + }; + type = g_param_type_register_static ("NMParamSpecSpecialized", &pspec_info); + } + + return type; +} + +GParamSpec * +_nm_param_spec_specialized (const char *name, + const char *nick, + const char *blurb, + GType specialized_type, + GParamFlags flags) +{ + NMParamSpecSpecialized *pspec; + + g_return_val_if_fail (g_type_is_a (specialized_type, G_TYPE_BOXED), NULL); + + pspec = g_param_spec_internal (NM_TYPE_PARAM_SPEC_SPECIALIZED, + name, nick, blurb, flags); + + G_PARAM_SPEC (pspec)->value_type = specialized_type; + + return G_PARAM_SPEC (pspec); +} + +/***********************************************************/ +/* Tests */ + +#if 0 + +static void +compare_ints (void) +{ + GValue value1 = G_VALUE_INIT; + GValue value2 = G_VALUE_INIT; + + g_value_init (&value1, G_TYPE_INT); + g_value_init (&value2, G_TYPE_INT); + + g_value_set_int (&value1, 5); + g_value_set_int (&value2, 5); + g_print ("Comparing ints 5 and 5: %d\n", _gvalues_compare (&value1, &value2)); + + g_value_set_int (&value2, 10); + g_print ("Comparing ints 5 and 10: %d\n", _gvalues_compare (&value1, &value2)); + + g_value_set_int (&value2, 1); + g_print ("Comparing ints 5 and 1: %d\n", _gvalues_compare (&value1, &value2)); +} + +static void +compare_strings (void) +{ + GValue value1 = G_VALUE_INIT; + GValue value2 = G_VALUE_INIT; + const char *str1 = "hello"; + const char *str2 = "world"; + + g_value_init (&value1, G_TYPE_STRING); + g_value_init (&value2, G_TYPE_STRING); + + g_value_set_string (&value1, str1); + g_value_set_string (&value2, str1); + g_print ("Comparing identical strings: %d\n", _gvalues_compare (&value1, &value2)); + + g_value_set_string (&value2, str2); + g_print ("Comparing different strings: %d\n", _gvalues_compare (&value1, &value2)); +} + +static void +compare_strv (void) +{ + GValue value1 = G_VALUE_INIT; + GValue value2 = G_VALUE_INIT; + char *strv1[] = { "foo", "bar", "baz", NULL }; + char *strv2[] = { "foo", "bar", "bar", NULL }; + char *strv3[] = { "foo", "bar", NULL }; + char *strv4[] = { "foo", "bar", "baz", "bam", NULL }; + + g_value_init (&value1, G_TYPE_STRV); + g_value_init (&value2, G_TYPE_STRV); + + g_value_set_boxed (&value1, strv1); + g_value_set_boxed (&value2, strv1); + g_print ("Comparing identical strv's: %d\n", _gvalues_compare (&value1, &value2)); + + g_value_set_boxed (&value2, strv2); + g_print ("Comparing different strv's: %d\n", _gvalues_compare (&value1, &value2)); + + g_value_set_boxed (&value2, strv3); + g_print ("Comparing different len (smaller) strv's: %d\n", _gvalues_compare (&value1, &value2)); + + g_value_set_boxed (&value2, strv4); + g_print ("Comparing different len (longer) strv's: %d\n", _gvalues_compare (&value1, &value2)); +} + +static void +compare_garrays (void) +{ + GArray *array1; + GArray *array2; + GValue value1 = G_VALUE_INIT; + GValue value2 = G_VALUE_INIT; + int i; + + g_value_init (&value1, DBUS_TYPE_G_UINT_ARRAY); + array1 = g_array_new (FALSE, FALSE, sizeof (guint32)); + + g_value_init (&value2, DBUS_TYPE_G_UINT_ARRAY); + array2 = g_array_new (FALSE, FALSE, sizeof (guint32)); + + for (i = 0; i < 5; i++) { + g_array_append_val (array1, i); + g_array_append_val (array2, i); + } + + g_value_set_boxed (&value1, array1); + g_value_set_boxed (&value2, array2); + + g_print ("Comparing identical arrays's: %d\n", _gvalues_compare (&value1, &value2)); + + g_array_remove_index (array2, 0); + g_value_set_boxed (&value2, array2); + g_print ("Comparing different length arrays's: %d\n", _gvalues_compare (&value1, &value2)); + + i = 7; + g_array_prepend_val (array2, i); + g_value_set_boxed (&value2, array2); + g_print ("Comparing different arrays's: %d\n", _gvalues_compare (&value1, &value2)); +} + +static void +compare_ptrarrays (void) +{ + GPtrArray *array1; + GPtrArray *array2; + GValue value1 = G_VALUE_INIT; + GValue value2 = G_VALUE_INIT; + + g_value_init (&value1, dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING)); + array1 = g_ptr_array_new (); + + g_value_init (&value2, dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING)); + array2 = g_ptr_array_new (); + + g_ptr_array_add (array1, "hello"); + g_ptr_array_add (array1, "world"); + g_value_set_boxed (&value1, array1); + + g_ptr_array_add (array2, "hello"); + g_ptr_array_add (array2, "world"); + g_value_set_boxed (&value2, array2); + + g_print ("Comparing identical ptr arrays's: %d\n", _gvalues_compare (&value1, &value2)); + + g_ptr_array_add (array2, "boo"); + g_value_set_boxed (&value2, array2); + g_print ("Comparing different len ptr arrays's: %d\n", _gvalues_compare (&value1, &value2)); + + g_ptr_array_add (array1, "booz"); + g_value_set_boxed (&value1, array1); + g_print ("Comparing different ptr arrays's: %d\n", _gvalues_compare (&value1, &value2)); +} + +static void +compare_str_hash (void) +{ + GHashTable *hash1; + GHashTable *hash2; + GValue value1 = G_VALUE_INIT; + GValue value2 = G_VALUE_INIT; + + g_value_init (&value1, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING)); + g_value_init (&value2, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING)); + + hash1 = g_hash_table_new (g_str_hash, g_str_equal); + hash2 = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_insert (hash1, "key1", "hello"); + g_hash_table_insert (hash1, "key2", "world"); + + g_hash_table_insert (hash2, "key1", "hello"); + g_hash_table_insert (hash2, "key2", "world"); + + g_value_set_boxed (&value1, hash1); + g_value_set_boxed (&value2, hash2); + g_print ("Comparing identical str hashes: %d\n", _gvalues_compare (&value1, &value2)); + + g_hash_table_remove (hash2, "key2"); + g_value_set_boxed (&value2, hash2); + g_print ("Comparing different length str hashes: %d\n", _gvalues_compare (&value1, &value2)); + + g_hash_table_insert (hash2, "key2", "moon"); + g_value_set_boxed (&value2, hash2); + g_print ("Comparing different str hashes: %d\n", _gvalues_compare (&value1, &value2)); +} + +static GValue * +str_to_gvalue (const char *str) +{ + GValue *value; + + value = g_slice_new0 (GValue); + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, str); + + return value; +} + +static GValue * +int_to_gvalue (int i) +{ + GValue *value; + + value = g_slice_new0 (GValue); + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, i); + + return value; +} + +static void +compare_gvalue_hash (void) +{ + GHashTable *hash1; + GHashTable *hash2; + GValue value1 = G_VALUE_INIT; + GValue value2 = G_VALUE_INIT; + + g_value_init (&value1, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)); + g_value_init (&value2, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)); + + hash1 = g_hash_table_new (g_str_hash, g_str_equal); + hash2 = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_insert (hash1, "key1", str_to_gvalue ("hello")); + g_hash_table_insert (hash1, "key2", int_to_gvalue (5)); + + g_hash_table_insert (hash2, "key1", str_to_gvalue ("hello")); + g_hash_table_insert (hash2, "key2", int_to_gvalue (5)); + + g_value_set_boxed (&value1, hash1); + g_value_set_boxed (&value2, hash2); + g_print ("Comparing identical gvalue hashes: %d\n", _gvalues_compare (&value1, &value2)); + + g_hash_table_remove (hash2, "key2"); + g_value_set_boxed (&value2, hash2); + g_print ("Comparing different length str hashes: %d\n", _gvalues_compare (&value1, &value2)); + + g_hash_table_insert (hash2, "key2", str_to_gvalue ("moon")); + g_value_set_boxed (&value2, hash2); + g_print ("Comparing different str hashes: %d\n", _gvalues_compare (&value1, &value2)); +} + +static void +compare_ip6_addresses (void) +{ + GValueArray *array1; + GValueArray *array2; + GValueArray *array3; + GByteArray *ba1; + GByteArray *ba2; + GByteArray *ba3; + GValue element = G_VALUE_INIT; + GValue value1 = G_VALUE_INIT; + GValue value2 = G_VALUE_INIT; + struct in6_addr addr1; + struct in6_addr addr2; + struct in6_addr addr3; + guint32 prefix1 = 64; + guint32 prefix2 = 64; + guint32 prefix3 = 0; + + inet_pton (AF_INET6, "1:2:3:4:5:6:7:8", &addr1, sizeof (struct in6_addr)); + inet_pton (AF_INET6, "ffff:2:3:4:5:6:7:8", &addr2, sizeof (struct in6_addr)); + inet_pton (AF_INET6, "::", &addr3, sizeof (struct in6_addr)); + + /* address 1 */ + ba1 = g_byte_array_new (); + array1 = g_value_array_new (2); + g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY); + g_byte_array_append (ba1, (guint8 *) addr1.s6_addr, 16); + g_value_take_boxed (&element, ba1); + g_value_array_append (array1, &element); + g_value_unset (&element); + + g_value_init (&element, G_TYPE_UINT); + g_value_set_uint (&element, prefix1); + g_value_array_append (array1, &element); + g_value_unset (&element); + + /* address 2 */ + ba2 = g_byte_array_new (); + array2 = g_value_array_new (2); + g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY); + g_byte_array_append (ba2, (guint8 *) addr2.s6_addr, 16); + g_value_take_boxed (&element, ba2); + g_value_array_append (array2, &element); + g_value_unset (&element); + + g_value_init (&element, G_TYPE_UINT); + g_value_set_uint (&element, prefix2); + g_value_array_append (array2, &element); + g_value_unset (&element); + + /* address 3 */ + ba3 = g_byte_array_new (); + array3 = g_value_array_new (2); + g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY); + g_byte_array_append (ba3, (guint8 *) addr3.s6_addr, 16); + g_value_take_boxed (&element, ba3); + g_value_array_append (array3, &element); + g_value_unset (&element); + + g_value_init (&element, G_TYPE_UINT); + g_value_set_uint (&element, prefix3); + g_value_array_append (array3, &element); + g_value_unset (&element); + + g_value_init (&value1, DBUS_TYPE_G_IP6_ADDRESS); + g_value_init (&value2, DBUS_TYPE_G_IP6_ADDRESS); + + g_value_set_boxed (&value1, array1); + g_value_set_boxed (&value2, array1); + g_print ("Comparing identical IPv6 address structures: %d\n", _gvalues_compare (&value1, &value2)); + + g_value_set_boxed (&value1, array1); + g_value_set_boxed (&value2, array2); + g_print ("Comparing different IPv6 address structures: %d\n", _gvalues_compare (&value1, &value2)); + + g_value_set_boxed (&value1, array1); + g_value_set_boxed (&value2, array3); + g_print ("Comparing different IPv6 address structures: %d\n", _gvalues_compare (&value1, &value2)); +} + +int +main (int argc, char *argv[]) +{ + DBusGConnection *bus; + +#if !GLIB_CHECK_VERSION (2, 35, 0) + g_type_init (); +#endif + + bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); + + compare_ints (); + compare_strings (); + compare_strv (); + compare_garrays (); + compare_ptrarrays (); + compare_str_hash (); + compare_gvalue_hash (); + compare_ip6_addresses (); + + return 0; +} + +#endif diff --git a/libnm-core/nm-param-spec-specialized.h b/libnm-core/nm-param-spec-specialized.h new file mode 100644 index 0000000000..7803e919ae --- /dev/null +++ b/libnm-core/nm-param-spec-specialized.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_PARAM_SPEC_SPECIALIZED_H +#define NM_PARAM_SPEC_SPECIALIZED_H + +#include <glib-object.h> + +typedef struct _NMParamSpecSpecialized NMParamSpecSpecialized; + +#define NM_TYPE_PARAM_SPEC_SPECIALIZED (_nm_param_spec_specialized_get_type ()) + +#define NM_IS_PARAM_SPEC_SPECIALIZED(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), NM_TYPE_PARAM_SPEC_SPECIALIZED)) +#define NM_PARAM_SPEC_SPECIALIZED(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), NM_TYPE_PARAM_SPEC_SPECIALIZED, NMParamSpecSpecialized)) + +GType _nm_param_spec_specialized_get_type (void); + +GParamSpec *_nm_param_spec_specialized (const char *name, + const char *nick, + const char *blurb, + GType specialized_type, + GParamFlags flags); + +#endif /* NM_PARAM_SPEC_SPECIALIZED_H */ diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c new file mode 100644 index 0000000000..daa5d6b0c7 --- /dev/null +++ b/libnm-core/nm-setting-8021x.c @@ -0,0 +1,3706 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2013 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include <string.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "nm-setting-8021x.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-dbus-glib-types.h" +#include "crypto.h" +#include "nm-utils-private.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-8021x + * @short_description: Describes 802.1x-authenticated connection properties + * @include: nm-setting-8021x.h + * + * The #NMSetting8021x object is a #NMSetting subclass that describes + * properties necessary for connection to 802.1x-authenticated networks, such as + * WPA and WPA2 Enterprise Wi-Fi networks and wired 802.1x networks. 802.1x + * connections typically use certificates and/or EAP authentication methods to + * securely verify, identify, and authenticate the client to the network itself, + * instead of simply relying on a widely shared static key. + * + * It's a good idea to read up on wpa_supplicant configuration before using this + * setting extensively, since most of the options here correspond closely with + * the relevant wpa_supplicant configuration options. + * + * Furthermore, to get a good idea of 802.1x, EAP, TLS, TTLS, etc and their + * applications to Wi-Fi and wired networks, you'll want to get copies of the + * following books. + * + * 802.11 Wireless Networks: The Definitive Guide, Second Edition + * Author: Matthew Gast + * ISBN: 978-0596100520 + * + * Cisco Wireless LAN Security + * Authors: Krishna Sankar, Sri Sundaralingam, Darrin Miller, and Andrew Balinsky + * ISBN: 978-1587051548 + **/ + +#define SCHEME_PATH "file://" + +/** + * nm_setting_802_1x_error_quark: + * + * Registers an error quark for #NMSetting8021x if necessary. + * + * Returns: the error quark used for #NMSetting8021x errors. + **/ +GQuark +nm_setting_802_1x_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-802-1x-error-quark"); + return quark; +} + +G_DEFINE_TYPE_WITH_CODE (NMSetting8021x, nm_setting_802_1x, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_802_1X_SETTING_NAME, + g_define_type_id, + 2, + NM_SETTING_802_1X_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_802_1X) + +#define NM_SETTING_802_1X_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_802_1X, NMSetting8021xPrivate)) + +G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_UNKNOWN == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_UNKNOWN) ); +G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_X509 == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_X509) ); +G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_RAW_KEY == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_RAW_KEY) ); +G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_PKCS12 == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_PKCS12) ); + +typedef struct { + GSList *eap; /* GSList of strings */ + char *identity; + char *anonymous_identity; + char *pac_file; + GByteArray *ca_cert; + char *ca_path; + char *subject_match; + GSList *altsubject_matches; + GByteArray *client_cert; + char *phase1_peapver; + char *phase1_peaplabel; + char *phase1_fast_provisioning; + char *phase2_auth; + char *phase2_autheap; + GByteArray *phase2_ca_cert; + char *phase2_ca_path; + char *phase2_subject_match; + GSList *phase2_altsubject_matches; + GByteArray *phase2_client_cert; + char *password; + NMSettingSecretFlags password_flags; + GByteArray *password_raw; + NMSettingSecretFlags password_raw_flags; + char *pin; + NMSettingSecretFlags pin_flags; + GByteArray *private_key; + char *private_key_password; + NMSettingSecretFlags private_key_password_flags; + GByteArray *phase2_private_key; + char *phase2_private_key_password; + NMSettingSecretFlags phase2_private_key_password_flags; + gboolean system_ca_certs; +} NMSetting8021xPrivate; + +enum { + PROP_0, + PROP_EAP, + PROP_IDENTITY, + PROP_ANONYMOUS_IDENTITY, + PROP_PAC_FILE, + PROP_CA_CERT, + PROP_CA_PATH, + PROP_SUBJECT_MATCH, + PROP_ALTSUBJECT_MATCHES, + PROP_CLIENT_CERT, + PROP_PHASE1_PEAPVER, + PROP_PHASE1_PEAPLABEL, + PROP_PHASE1_FAST_PROVISIONING, + PROP_PHASE2_AUTH, + PROP_PHASE2_AUTHEAP, + PROP_PHASE2_CA_CERT, + PROP_PHASE2_CA_PATH, + PROP_PHASE2_SUBJECT_MATCH, + PROP_PHASE2_ALTSUBJECT_MATCHES, + PROP_PHASE2_CLIENT_CERT, + PROP_PASSWORD, + PROP_PASSWORD_FLAGS, + PROP_PASSWORD_RAW, + PROP_PASSWORD_RAW_FLAGS, + PROP_PRIVATE_KEY, + PROP_PRIVATE_KEY_PASSWORD, + PROP_PRIVATE_KEY_PASSWORD_FLAGS, + PROP_PHASE2_PRIVATE_KEY, + PROP_PHASE2_PRIVATE_KEY_PASSWORD, + PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS, + PROP_PIN, + PROP_PIN_FLAGS, + PROP_SYSTEM_CA_CERTS, + + LAST_PROP +}; + +/** + * nm_setting_802_1x_new: + * + * Creates a new #NMSetting8021x object with default values. + * + * Returns: the new empty #NMSetting8021x object + **/ +NMSetting * +nm_setting_802_1x_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_802_1X, NULL); +} + +/** + * nm_setting_802_1x_get_num_eap_methods: + * @setting: the #NMSetting8021x + * + * Returns the number of eap methods allowed for use when connecting to the + * network. Generally only one EAP method is used. Use the functions + * nm_setting_802_1x_get_eap_method(), nm_setting_802_1x_add_eap_method(), + * and nm_setting_802_1x_remove_eap_method() for adding, removing, and retrieving + * allowed EAP methods. + * + * Returns: the number of allowed EAP methods + **/ +guint32 +nm_setting_802_1x_get_num_eap_methods (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), 0); + + return g_slist_length (NM_SETTING_802_1X_GET_PRIVATE (setting)->eap); +} + +/** + * nm_setting_802_1x_get_eap_method: + * @setting: the #NMSetting8021x + * @i: the index of the EAP method name to return + * + * Returns the name of the allowed EAP method at index @i. + * + * Returns: the name of the allowed EAP method at index @i + **/ +const char * +nm_setting_802_1x_get_eap_method (NMSetting8021x *setting, guint32 i) +{ + NMSetting8021xPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->eap), NULL); + + return (const char *) g_slist_nth_data (priv->eap, i); +} + +/** + * nm_setting_802_1x_add_eap_method: + * @setting: the #NMSetting8021x + * @eap: the name of the EAP method to allow for this connection + * + * Adds an allowed EAP method. The setting is not valid until at least one + * EAP method has been added. See #NMSetting8021x:eap property for a list of + * allowed EAP methods. + * + * Returns: %TRUE if the EAP method was successfully added, %FALSE if it was + * not a valid method or if it was already allowed. + **/ +gboolean +nm_setting_802_1x_add_eap_method (NMSetting8021x *setting, const char *eap) +{ + NMSetting8021xPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + g_return_val_if_fail (eap != NULL, FALSE); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + for (iter = priv->eap; iter; iter = g_slist_next (iter)) { + if (!strcmp (eap, (char *) iter->data)) + return FALSE; + } + + priv->eap = g_slist_append (priv->eap, g_ascii_strdown (eap, -1)); + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP); + return TRUE; +} + +/** + * nm_setting_802_1x_remove_eap_method: + * @setting: the #NMSetting8021x + * @i: the index of the EAP method to remove + * + * Removes the allowed EAP method at the specified index. + **/ +void +nm_setting_802_1x_remove_eap_method (NMSetting8021x *setting, guint32 i) +{ + NMSetting8021xPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_802_1X (setting)); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + elt = g_slist_nth (priv->eap, i); + g_return_if_fail (elt != NULL); + + g_free (elt->data); + priv->eap = g_slist_delete_link (priv->eap, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP); +} + +/** + * nm_setting_802_1x_remove_eap_method_by_value: + * @setting: the #NMSetting8021x + * @eap: the name of the EAP method to remove + * + * Removes the allowed EAP method @method. + * + * Returns: %TRUE if the EAP method was founs and removed, %FALSE if it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_802_1x_remove_eap_method_by_value (NMSetting8021x *setting, + const char *eap) +{ + NMSetting8021xPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + g_return_val_if_fail (eap != NULL, FALSE); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + for (iter = priv->eap; iter; iter = g_slist_next (iter)) { + if (!strcmp (eap, (char *) iter->data)) { + priv->eap = g_slist_delete_link (priv->eap, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_802_1x_clear_eap_methods: + * @setting: the #NMSetting8021x + * + * Clears all allowed EAP methods. + **/ +void +nm_setting_802_1x_clear_eap_methods (NMSetting8021x *setting) +{ + NMSetting8021xPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_802_1X (setting)); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + g_slist_free_full (priv->eap, g_free); + priv->eap = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP); +} + +/** + * nm_setting_802_1x_get_identity: + * @setting: the #NMSetting8021x + * + * Returns the identifier used by some EAP methods (like TLS) to + * authenticate the user. Often this is a username or login name. + * + * Returns: the user identifier + **/ +const char * +nm_setting_802_1x_get_identity (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->identity; +} + +/** + * nm_setting_802_1x_get_anonymous_identity: + * @setting: the #NMSetting8021x + * + * Returns the anonymous identifier used by some EAP methods (like TTLS) to + * authenticate the user in the outer unencrypted "phase 1" authentication. The + * inner "phase 2" authentication will use the #NMSetting8021x:identity in + * a secure form, if applicable for that EAP method. + * + * Returns: the anonymous identifier + **/ +const char * +nm_setting_802_1x_get_anonymous_identity (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->anonymous_identity; +} + +/** + * nm_setting_802_1x_get_pac_file: + * @setting: the #NMSetting8021x + * + * Returns the file containing PAC credentials used by EAP-FAST method. + * + * Returns: the PAC file + **/ +const char * +nm_setting_802_1x_get_pac_file (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->pac_file; +} + +/** + * nm_setting_802_1x_get_ca_path: + * @setting: the #NMSetting8021x + * + * Returns the path of the CA certificate directory if previously set. Systems + * will often have a directory that contains multiple individual CA certificates + * which the supplicant can then add to the verification chain. This may be + * used in addition to the #NMSetting8021x:ca-cert property to add more CA + * certificates for verifying the network to client. + * + * Returns: the CA certificate directory path + **/ +const char * +nm_setting_802_1x_get_ca_path (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_path; +} + +/** + * nm_setting_802_1x_get_system_ca_certs: + * @setting: the #NMSetting8021x + * + * Sets the #NMSetting8021x:system-ca-certs property. The + * #NMSetting8021x:ca-path and #NMSetting8021x:phase2-ca-path + * properties are ignored if the #NMSetting8021x:system-ca-certs property is + * %TRUE, in which case a system-wide CA certificate directory specified at + * compile time (using the --system-ca-path configure option) is used in place + * of these properties. + * + * Returns: %TRUE if a system CA certificate path should be used, %FALSE if not + **/ +gboolean +nm_setting_802_1x_get_system_ca_certs (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->system_ca_certs; +} + +static NMSetting8021xCKScheme +get_cert_scheme (GByteArray *array) +{ + if (!array || !array->len) + return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; + + if ( (array->len > strlen (SCHEME_PATH)) + && !memcmp (array->data, SCHEME_PATH, strlen (SCHEME_PATH))) + return NM_SETTING_802_1X_CK_SCHEME_PATH; + + return NM_SETTING_802_1X_CK_SCHEME_BLOB; +} + +/** + * nm_setting_802_1x_get_ca_cert_scheme: + * @setting: the #NMSetting8021x + * + * Returns the scheme used to store the CA certificate. If the returned scheme + * is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use nm_setting_802_1x_get_ca_cert_blob(); + * if %NM_SETTING_802_1X_CK_SCHEME_PATH, use nm_setting_802_1x_get_ca_cert_path(). + * + * Returns: scheme used to store the CA certificate (blob or path) + **/ +NMSetting8021xCKScheme +nm_setting_802_1x_get_ca_cert_scheme (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); + + return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert); +} + +/** + * nm_setting_802_1x_get_ca_cert_blob: + * @setting: the #NMSetting8021x + * + * Returns the CA certificate blob if the CA certificate is stored using the + * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme. Not all EAP methods use a + * CA certificate (LEAP for example), and those that can take advantage of the + * CA certificate allow it to be unset. Note that lack of a CA certificate + * reduces security by allowing man-in-the-middle attacks, because the identity + * of the network cannot be confirmed by the client. + * + * Returns: the CA certificate data + **/ +const GByteArray * +nm_setting_802_1x_get_ca_cert_blob (NMSetting8021x *setting) +{ + NMSetting8021xCKScheme scheme; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + scheme = nm_setting_802_1x_get_ca_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert; +} + +/** + * nm_setting_802_1x_get_ca_cert_path: + * @setting: the #NMSetting8021x + * + * Returns the CA certificate path if the CA certificate is stored using the + * %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. Not all EAP methods use a + * CA certificate (LEAP for example), and those that can take advantage of the + * CA certificate allow it to be unset. Note that lack of a CA certificate + * reduces security by allowing man-in-the-middle attacks, because the identity + * of the network cannot be confirmed by the client. + * + * Returns: path to the CA certificate file + **/ +const char * +nm_setting_802_1x_get_ca_cert_path (NMSetting8021x *setting) +{ + NMSetting8021xCKScheme scheme; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + scheme = nm_setting_802_1x_get_ca_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); + + return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert->data + strlen (SCHEME_PATH)); +} + +static GByteArray * +path_to_scheme_value (const char *path) +{ + GByteArray *array; + + g_return_val_if_fail (path != NULL, NULL); + + /* Add the path scheme tag to the front, then the fielname */ + array = g_byte_array_sized_new (strlen (path) + strlen (SCHEME_PATH) + 1); + g_assert (array); + g_byte_array_append (array, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH)); + g_byte_array_append (array, (const guint8 *) path, strlen (path)); + g_byte_array_append (array, (const guint8 *) "\0", 1); + return array; +} + +/** + * nm_setting_802_1x_set_ca_cert: + * @setting: the #NMSetting8021x + * @cert_path: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH + * or %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the CA certificate + * file (PEM or DER format). The path must be UTF-8 encoded; use + * g_filename_to_utf8() to convert if needed. Passing %NULL with any @scheme + * clears the CA certificate. + * @scheme: desired storage scheme for the certificate + * @out_format: on successful return, the type of the certificate added + * @error: on unsuccessful return, an error + * + * Reads a certificate from disk and sets the #NMSetting8021x:ca-cert property + * with the raw certificate data if using the %NM_SETTING_802_1X_CK_SCHEME_BLOB + * scheme, or with the path to the certificate file if using the + * %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. + * + * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful + **/ +gboolean +nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting, + const char *cert_path, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error) +{ + NMSetting8021xPrivate *priv; + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + GByteArray *data; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + + if (cert_path) { + g_return_val_if_fail (g_utf8_validate (cert_path, -1, NULL), FALSE); + g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB + || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, + FALSE); + } + + if (out_format) + g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + + /* Clear out any previous ca_cert blob */ + if (priv->ca_cert) { + g_byte_array_free (priv->ca_cert, TRUE); + priv->ca_cert = NULL; + } + + if (!cert_path) { + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CA_CERT); + return TRUE; + } + + data = crypto_load_and_verify_certificate (cert_path, &format, error); + if (data) { + /* wpa_supplicant can only use raw x509 CA certs */ + if (format == NM_CRYPTO_FILE_FORMAT_X509) { + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + priv->ca_cert = g_byte_array_ref (data); + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + priv->ca_cert = path_to_scheme_value (cert_path); + else + g_assert_not_reached (); + } else { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("CA certificate must be in X.509 format")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT); + } + g_byte_array_unref (data); + } + + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CA_CERT); + return priv->ca_cert != NULL; +} + +/** + * nm_setting_802_1x_get_subject_match: + * @setting: the #NMSetting8021x + * + * Returns: the #NMSetting8021x:subject-match property. This is the + * substring to be matched against the subject of the authentication + * server certificate, or %NULL no subject verification is to be + * performed. + **/ +const char * +nm_setting_802_1x_get_subject_match (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->subject_match; +} + +/** + * nm_setting_802_1x_get_num_altsubject_matches: + * @setting: the #NMSetting8021x + * + * Returns the number of entries in the + * #NMSetting8021x:altsubject-matches property of this setting. + * + * Returns: the number of altsubject-matches entries. + **/ +guint32 +nm_setting_802_1x_get_num_altsubject_matches (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), 0); + + return g_slist_length (NM_SETTING_802_1X_GET_PRIVATE (setting)->altsubject_matches); +} + +/** + * nm_setting_802_1x_get_altsubject_match: + * @setting: the #NMSettingConnection + * @i: the zero-based index of the array of altSubjectName matches + * + * Returns the altSubjectName match at index @i. + * + * Returns: the altSubjectName match at index @i + **/ +const char * +nm_setting_802_1x_get_altsubject_match (NMSetting8021x *setting, guint32 i) +{ + NMSetting8021xPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->altsubject_matches), NULL); + + return (const char *) g_slist_nth_data (priv->altsubject_matches, i); +} + +/** + * nm_setting_802_1x_add_altsubject_match: + * @setting: the #NMSetting8021x + * @altsubject_match: the altSubjectName to allow for this connection + * + * Adds an allowed alternate subject name match. Until at least one + * match is added, the altSubjectName of the remote authentication + * server is not verified. + * + * Returns: %TRUE if the alternative subject name match was + * successfully added, %FALSE if it was already allowed. + **/ +gboolean +nm_setting_802_1x_add_altsubject_match (NMSetting8021x *setting, + const char *altsubject_match) +{ + NMSetting8021xPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + g_return_val_if_fail (altsubject_match != NULL, FALSE); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + for (iter = priv->altsubject_matches; iter; iter = g_slist_next (iter)) { + if (!strcmp (altsubject_match, (char *) iter->data)) + return FALSE; + } + + priv->altsubject_matches = g_slist_append (priv->altsubject_matches, + g_strdup (altsubject_match)); + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES); + return TRUE; +} + +/** + * nm_setting_802_1x_remove_altsubject_match: + * @setting: the #NMSetting8021x + * @i: the index of the altSubjectName match to remove + * + * Removes the allowed altSubjectName at the specified index. + **/ +void +nm_setting_802_1x_remove_altsubject_match (NMSetting8021x *setting, guint32 i) +{ + NMSetting8021xPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_802_1X (setting)); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + elt = g_slist_nth (priv->altsubject_matches, i); + g_return_if_fail (elt != NULL); + + g_free (elt->data); + priv->altsubject_matches = g_slist_delete_link (priv->altsubject_matches, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES); +} + +/** + * nm_setting_802_1x_remove_altsubject_match_by_value: + * @setting: the #NMSetting8021x + * @altsubject_match: the altSubjectName to remove + * + * Removes the allowed altSubjectName @altsubject_match. + * + * Returns: %TRUE if the alternative subject name match was found and removed, + * %FALSE if it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_802_1x_remove_altsubject_match_by_value (NMSetting8021x *setting, + const char *altsubject_match) +{ + NMSetting8021xPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + g_return_val_if_fail (altsubject_match != NULL, FALSE); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + for (iter = priv->altsubject_matches; iter; iter = g_slist_next (iter)) { + if (!strcmp (altsubject_match, (char *) iter->data)) { + priv->altsubject_matches = g_slist_delete_link (priv->altsubject_matches, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_802_1x_clear_altsubject_matches: + * @setting: the #NMSetting8021x + * + * Clears all altSubjectName matches. + **/ +void +nm_setting_802_1x_clear_altsubject_matches (NMSetting8021x *setting) +{ + NMSetting8021xPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_802_1X (setting)); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + g_slist_free_full (priv->altsubject_matches, g_free); + priv->altsubject_matches = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES); +} + +/** + * nm_setting_802_1x_get_client_cert_scheme: + * @setting: the #NMSetting8021x + * + * Returns the scheme used to store the client certificate. If the returned scheme + * is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use nm_setting_802_1x_get_client_cert_blob(); + * if %NM_SETTING_802_1X_CK_SCHEME_PATH, use nm_setting_802_1x_get_client_cert_path(). + * + * Returns: scheme used to store the client certificate (blob or path) + **/ +NMSetting8021xCKScheme +nm_setting_802_1x_get_client_cert_scheme (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); + + return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert); +} + +/** + * nm_setting_802_1x_get_client_cert_blob: + * @setting: the #NMSetting8021x + * + * Client certificates are used to identify the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: the client certificate data + **/ +const GByteArray * +nm_setting_802_1x_get_client_cert_blob (NMSetting8021x *setting) +{ + NMSetting8021xCKScheme scheme; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + scheme = nm_setting_802_1x_get_client_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert; +} + +/** + * nm_setting_802_1x_get_client_cert_path: + * @setting: the #NMSetting8021x + * + * Client certificates are used to identify the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: path to the client certificate file + **/ +const char * +nm_setting_802_1x_get_client_cert_path (NMSetting8021x *setting) +{ + NMSetting8021xCKScheme scheme; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + scheme = nm_setting_802_1x_get_client_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); + + return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert->data + strlen (SCHEME_PATH)); +} + +/** + * nm_setting_802_1x_set_client_cert: + * @setting: the #NMSetting8021x + * @cert_path: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH + * or %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the client + * certificate file (PEM, DER, or PKCS#12 format). The path must be UTF-8 + * encoded; use g_filename_to_utf8() to convert if needed. Passing %NULL with + * any @scheme clears the client certificate. + * @scheme: desired storage scheme for the certificate + * @out_format: on successful return, the type of the certificate added + * @error: on unsuccessful return, an error + * + * Reads a certificate from disk and sets the #NMSetting8021x:client-cert + * property with the raw certificate data if using the + * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate + * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. + * + * Client certificates are used to identify the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful + **/ +gboolean +nm_setting_802_1x_set_client_cert (NMSetting8021x *setting, + const char *cert_path, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error) +{ + NMSetting8021xPrivate *priv; + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + GByteArray *data; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + + if (cert_path) { + g_return_val_if_fail (g_utf8_validate (cert_path, -1, NULL), FALSE); + g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB + || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, + FALSE); + } + + if (out_format) + g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + + /* Clear out any previous ca_cert blob */ + if (priv->client_cert) { + g_byte_array_free (priv->client_cert, TRUE); + priv->client_cert = NULL; + } + + if (!cert_path) { + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT); + return TRUE; + } + + data = crypto_load_and_verify_certificate (cert_path, &format, error); + if (data) { + gboolean valid = FALSE; + + switch (format) { + case NM_CRYPTO_FILE_FORMAT_X509: + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; + valid = TRUE; + break; + case NM_CRYPTO_FILE_FORMAT_PKCS12: + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12; + valid = TRUE; + break; + default: + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("invalid certificate format")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT); + break; + } + + if (valid) { + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + priv->client_cert = g_byte_array_ref (data); + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + priv->client_cert = path_to_scheme_value (cert_path); + else + g_assert_not_reached (); + } + g_byte_array_unref (data); + } + + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT); + return priv->client_cert != NULL; +} + +/** + * nm_setting_802_1x_get_phase1_peapver: + * @setting: the #NMSetting8021x + * + * Returns: the "phase 1" PEAP version to be used when authenticating with + * EAP-PEAP as contained in the #NMSetting8021x:phase1-peapver property. Valid + * values are %NULL (unset), "0" (PEAP version 0), and "1" (PEAP version 1). + **/ +const char * +nm_setting_802_1x_get_phase1_peapver (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase1_peapver; +} + +/** + * nm_setting_802_1x_get_phase1_peaplabel: + * @setting: the #NMSetting8021x + * + * Returns: whether the "phase 1" PEAP label is new-style or old-style, to be + * used when authenticating with EAP-PEAP, as contained in the + * #NMSetting8021x:phase1-peaplabel property. Valid values are %NULL (unset), + * "0" (use old-style label), and "1" (use new-style label). See the + * wpa_supplicant documentation for more details. + **/ +const char * +nm_setting_802_1x_get_phase1_peaplabel (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase1_peaplabel; +} + +/** + * nm_setting_802_1x_get_phase1_fast_provisioning: + * @setting: the #NMSetting8021x + * + * Returns: whether "phase 1" PEAP fast provisioning should be used, as specified + * by the #NMSetting8021x:phase1-fast-provisioning property. See the + * wpa_supplicant documentation for more details. + **/ +const char * +nm_setting_802_1x_get_phase1_fast_provisioning (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase1_fast_provisioning; +} + +/** + * nm_setting_802_1x_get_phase2_auth: + * @setting: the #NMSetting8021x + * + * Returns: the "phase 2" non-EAP (ex MD5) allowed authentication method as + * specified by the #NMSetting8021x:phase2-auth property. + **/ +const char * +nm_setting_802_1x_get_phase2_auth (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_auth; +} + +/** + * nm_setting_802_1x_get_phase2_autheap: + * @setting: the #NMSetting8021x + * + * Returns: the "phase 2" EAP-based (ex TLS) allowed authentication method as + * specified by the #NMSetting8021x:phase2-autheap property. + **/ +const char * +nm_setting_802_1x_get_phase2_autheap (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_autheap; +} + +/** + * nm_setting_802_1x_get_phase2_ca_path: + * @setting: the #NMSetting8021x + * + * Returns the path of the "phase 2" CA certificate directory if previously set. + * Systems will often have a directory that contains multiple individual CA + * certificates which the supplicant can then add to the verification chain. + * This may be used in addition to the #NMSetting8021x:phase2-ca-cert property + * to add more CA certificates for verifying the network to client. + * + * Returns: the "phase 2" CA certificate directory path + **/ +const char * +nm_setting_802_1x_get_phase2_ca_path (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_path; +} + +/** + * nm_setting_802_1x_get_phase2_ca_cert_scheme: + * @setting: the #NMSetting8021x + * + * Returns the scheme used to store the "phase 2" CA certificate. If the + * returned scheme is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use + * nm_setting_802_1x_get_ca_cert_blob(); if %NM_SETTING_802_1X_CK_SCHEME_PATH, + * use nm_setting_802_1x_get_ca_cert_path(). + * + * Returns: scheme used to store the "phase 2" CA certificate (blob or path) + **/ +NMSetting8021xCKScheme +nm_setting_802_1x_get_phase2_ca_cert_scheme (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); + + return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert); +} + +/** + * nm_setting_802_1x_get_phase2_ca_cert_blob: + * @setting: the #NMSetting8021x + * + * Returns the "phase 2" CA certificate blob if the CA certificate is stored + * using the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme. Not all EAP methods use + * a CA certificate (LEAP for example), and those that can take advantage of the + * CA certificate allow it to be unset. Note that lack of a CA certificate + * reduces security by allowing man-in-the-middle attacks, because the identity + * of the network cannot be confirmed by the client. + * + * Returns: the "phase 2" CA certificate data + **/ +const GByteArray * +nm_setting_802_1x_get_phase2_ca_cert_blob (NMSetting8021x *setting) +{ + NMSetting8021xCKScheme scheme; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert; +} + +/** + * nm_setting_802_1x_get_phase2_ca_cert_path: + * @setting: the #NMSetting8021x + * + * Returns the "phase 2" CA certificate path if the CA certificate is stored + * using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. Not all EAP methods use + * a CA certificate (LEAP for example), and those that can take advantage of the + * CA certificate allow it to be unset. Note that lack of a CA certificate + * reduces security by allowing man-in-the-middle attacks, because the identity + * of the network cannot be confirmed by the client. + * + * Returns: path to the "phase 2" CA certificate file + **/ +const char * +nm_setting_802_1x_get_phase2_ca_cert_path (NMSetting8021x *setting) +{ + NMSetting8021xCKScheme scheme; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); + + return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert->data + strlen (SCHEME_PATH)); +} + +/** + * nm_setting_802_1x_set_phase2_ca_cert: + * @setting: the #NMSetting8021x + * @cert_path: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH + * or %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the "phase2" CA + * certificate file (PEM or DER format). The path must be UTF-8 encoded; use + * g_filename_to_utf8() to convert if needed. Passing %NULL with any @scheme + * clears the "phase2" CA certificate. + * @scheme: desired storage scheme for the certificate + * @out_format: on successful return, the type of the certificate added + * @error: on unsuccessful return, an error + * + * Reads a certificate from disk and sets the #NMSetting8021x:phase2-ca-cert + * property with the raw certificate data if using the + * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate + * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. + * + * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful + **/ +gboolean +nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *setting, + const char *cert_path, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error) +{ + NMSetting8021xPrivate *priv; + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + GByteArray *data; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + + if (cert_path) { + g_return_val_if_fail (g_utf8_validate (cert_path, -1, NULL), FALSE); + g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB + || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, + FALSE); + } + + if (out_format) + g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + + /* Clear out any previous ca_cert blob */ + if (priv->phase2_ca_cert) { + g_byte_array_free (priv->phase2_ca_cert, TRUE); + priv->phase2_ca_cert = NULL; + } + + if (!cert_path) { + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CA_CERT); + return TRUE; + } + + data = crypto_load_and_verify_certificate (cert_path, &format, error); + if (data) { + /* wpa_supplicant can only use raw x509 CA certs */ + if (format == NM_CRYPTO_FILE_FORMAT_X509) { + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + priv->phase2_ca_cert = g_byte_array_ref (data); + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + priv->phase2_ca_cert = path_to_scheme_value (cert_path); + else + g_assert_not_reached (); + } else { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("invalid certificate format")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CA_CERT); + } + g_byte_array_unref (data); + } + + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CA_CERT); + return priv->phase2_ca_cert != NULL; +} + +/** + * nm_setting_802_1x_get_phase2_subject_match: + * @setting: the #NMSetting8021x + * + * Returns: the #NMSetting8021x:phase2-subject-match property. This is + * the substring to be matched against the subject of the "phase 2" + * authentication server certificate, or %NULL no subject verification + * is to be performed. + **/ +const char * +nm_setting_802_1x_get_phase2_subject_match (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_subject_match; +} + +/** + * nm_setting_802_1x_get_num_phase2_altsubject_matches: + * @setting: the #NMSetting8021x + * + * Returns the number of entries in the + * #NMSetting8021x:phase2-altsubject-matches property of this setting. + * + * Returns: the number of phase2-altsubject-matches entries. + **/ +guint32 +nm_setting_802_1x_get_num_phase2_altsubject_matches (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), 0); + + return g_slist_length (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_altsubject_matches); +} + +/** + * nm_setting_802_1x_get_phase2_altsubject_match: + * @setting: the #NMSettingConnection + * @i: the zero-based index of the array of "phase 2" altSubjectName matches + * + * Returns the "phase 2" altSubjectName match at index @i. + * + * Returns: the "phase 2" altSubjectName match at index @i + **/ +const char * +nm_setting_802_1x_get_phase2_altsubject_match (NMSetting8021x *setting, guint32 i) +{ + NMSetting8021xPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->phase2_altsubject_matches), NULL); + + return (const char *) g_slist_nth_data (priv->phase2_altsubject_matches, i); +} + +/** + * nm_setting_802_1x_add_phase2_altsubject_match: + * @setting: the #NMSetting8021x + * @phase2_altsubject_match: the "phase 2" altSubjectName to allow for this + * connection + * + * Adds an allowed alternate subject name match for "phase 2". Until + * at least one match is added, the altSubjectName of the "phase 2" + * remote authentication server is not verified. + * + * Returns: %TRUE if the "phase 2" alternative subject name match was + * successfully added, %FALSE if it was already allowed. + **/ +gboolean +nm_setting_802_1x_add_phase2_altsubject_match (NMSetting8021x *setting, + const char *phase2_altsubject_match) +{ + NMSetting8021xPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + g_return_val_if_fail (phase2_altsubject_match != NULL, FALSE); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + for (iter = priv->phase2_altsubject_matches; iter; iter = g_slist_next (iter)) { + if (!strcmp (phase2_altsubject_match, (char *) iter->data)) + return FALSE; + } + + priv->phase2_altsubject_matches = g_slist_append (priv->phase2_altsubject_matches, + g_strdup (phase2_altsubject_match)); + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES); + return TRUE; +} + +/** + * nm_setting_802_1x_remove_phase2_altsubject_match: + * @setting: the #NMSetting8021x + * @i: the index of the "phase 2" altSubjectName match to remove + * + * Removes the allowed "phase 2" altSubjectName at the specified index. + **/ +void +nm_setting_802_1x_remove_phase2_altsubject_match (NMSetting8021x *setting, guint32 i) +{ + NMSetting8021xPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_802_1X (setting)); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + elt = g_slist_nth (priv->phase2_altsubject_matches, i); + g_return_if_fail (elt != NULL); + + g_free (elt->data); + priv->phase2_altsubject_matches = g_slist_delete_link (priv->phase2_altsubject_matches, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES); +} + + +/** + * nm_setting_802_1x_remove_phase2_altsubject_match_by_value: + * @setting: the #NMSetting8021x + * @phase2_altsubject_match: the "phase 2" altSubjectName to remove + * + * Removes the allowed "phase 2" altSubjectName @phase2_altsubject_match. + * + * Returns: %TRUE if the alternative subject name match for "phase 2" was found and removed, + * %FALSE if it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_802_1x_remove_phase2_altsubject_match_by_value (NMSetting8021x *setting, + const char *phase2_altsubject_match) +{ + NMSetting8021xPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + g_return_val_if_fail (phase2_altsubject_match != NULL, FALSE); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + for (iter = priv->phase2_altsubject_matches; iter; iter = g_slist_next (iter)) { + if (!strcmp (phase2_altsubject_match, (char *) iter->data)) { + priv->phase2_altsubject_matches = g_slist_delete_link (priv->phase2_altsubject_matches, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_802_1x_clear_phase2_altsubject_matches: + * @setting: the #NMSetting8021x + * + * Clears all "phase 2" altSubjectName matches. + **/ +void +nm_setting_802_1x_clear_phase2_altsubject_matches (NMSetting8021x *setting) +{ + NMSetting8021xPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_802_1X (setting)); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + g_slist_free_full (priv->phase2_altsubject_matches, g_free); + priv->phase2_altsubject_matches = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES); +} + +/** + * nm_setting_802_1x_get_phase2_client_cert_scheme: + * @setting: the #NMSetting8021x + * + * Returns the scheme used to store the "phase 2" client certificate. If the + * returned scheme is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use + * nm_setting_802_1x_get_client_cert_blob(); if + * %NM_SETTING_802_1X_CK_SCHEME_PATH, use + * nm_setting_802_1x_get_client_cert_path(). + * + * Returns: scheme used to store the "phase 2" client certificate (blob or path) + **/ +NMSetting8021xCKScheme +nm_setting_802_1x_get_phase2_client_cert_scheme (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); + + return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert); +} + +/** + * nm_setting_802_1x_get_phase2_client_cert_blob: + * @setting: the #NMSetting8021x + * + * Client certificates are used to identify the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: the "phase 2" client certificate data + **/ +const GByteArray * +nm_setting_802_1x_get_phase2_client_cert_blob (NMSetting8021x *setting) +{ + NMSetting8021xCKScheme scheme; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert; +} + +/** + * nm_setting_802_1x_get_phase2_client_cert_path: + * @setting: the #NMSetting8021x + * + * Client certificates are used to identify the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: path to the "phase 2" client certificate file + **/ +const char * +nm_setting_802_1x_get_phase2_client_cert_path (NMSetting8021x *setting) +{ + NMSetting8021xCKScheme scheme; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); + + return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert->data + strlen (SCHEME_PATH)); +} + +/** + * nm_setting_802_1x_set_phase2_client_cert: + * @setting: the #NMSetting8021x + * @cert_path: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH + * or %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the "phase2" client + * certificate file (PEM, DER, or PKCS#12 format). The path must be UTF-8 + * encoded; use g_filename_to_utf8() to convert if needed. Passing %NULL with + * any @scheme clears the "phase2" client certificate. + * @scheme: desired storage scheme for the certificate + * @out_format: on successful return, the type of the certificate added + * @error: on unsuccessful return, an error + * + * Reads a certificate from disk and sets the #NMSetting8021x:phase2-client-cert + * property with the raw certificate data if using the + * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate + * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. + * + * Client certificates are used to identify the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful + **/ +gboolean +nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *setting, + const char *cert_path, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error) +{ + NMSetting8021xPrivate *priv; + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + GByteArray *data; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + + if (cert_path) { + g_return_val_if_fail (g_utf8_validate (cert_path, -1, NULL), FALSE); + g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB + || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, + FALSE); + } + + if (out_format) + g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + + /* Clear out any previous ca_cert blob */ + if (priv->phase2_client_cert) { + g_byte_array_free (priv->phase2_client_cert, TRUE); + priv->phase2_client_cert = NULL; + } + + if (!cert_path) { + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT); + return TRUE; + } + + data = crypto_load_and_verify_certificate (cert_path, &format, error); + if (data) { + gboolean valid = FALSE; + + /* wpa_supplicant can only use raw x509 CA certs */ + switch (format) { + case NM_CRYPTO_FILE_FORMAT_X509: + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; + valid = TRUE; + break; + case NM_CRYPTO_FILE_FORMAT_PKCS12: + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12; + valid = TRUE; + break; + default: + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("invalid certificate format")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CLIENT_CERT); + break; + } + + if (valid) { + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + priv->phase2_client_cert = g_byte_array_ref (data); + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + priv->phase2_client_cert = path_to_scheme_value (cert_path); + else + g_assert_not_reached (); + } + g_byte_array_unref (data); + } + + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT); + return priv->phase2_client_cert != NULL; +} + +/** + * nm_setting_802_1x_get_password: + * @setting: the #NMSetting8021x + * + * Returns: the password used by the authentication method, if any, as specified + * by the #NMSetting8021x:password property + **/ +const char * +nm_setting_802_1x_get_password (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->password; +} + +/** + * nm_setting_802_1x_get_password_flags: + * @setting: the #NMSetting8021x + * + * Returns: the #NMSettingSecretFlags pertaining to the #NMSetting8021x:password + **/ +NMSettingSecretFlags +nm_setting_802_1x_get_password_flags (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_SECRET_FLAG_NONE); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->password_flags; +} + +/** + * nm_setting_802_1x_get_password_raw: + * @setting: the #NMSetting8021x + * + * Returns: the password used by the authentication method as a + * UTF-8-encoded array of bytes, as specified by the + * #NMSetting8021x:password-raw property + **/ +const GByteArray * +nm_setting_802_1x_get_password_raw (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->password_raw; +} + +/** + * nm_setting_802_1x_get_password_raw_flags: + * @setting: the #NMSetting8021x + * + * Returns: the #NMSettingSecretFlags pertaining to the + * #NMSetting8021x:password-raw + **/ +NMSettingSecretFlags +nm_setting_802_1x_get_password_raw_flags (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_SECRET_FLAG_NONE); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->password_raw_flags; +} + +/** + * nm_setting_802_1x_get_pin: + * @setting: the #NMSetting8021x + * + * Returns: the PIN used by the authentication method, if any, as specified + * by the #NMSetting8021x:pin property + **/ +const char * +nm_setting_802_1x_get_pin (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->pin; +} + +/** + * nm_setting_802_1x_get_pin_flags: + * @setting: the #NMSetting8021x + * + * Returns: the #NMSettingSecretFlags pertaining to the + * #NMSetting8021x:pin + **/ +NMSettingSecretFlags +nm_setting_802_1x_get_pin_flags (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_SECRET_FLAG_NONE); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->pin_flags; +} + +/** + * nm_setting_802_1x_get_private_key_scheme: + * @setting: the #NMSetting8021x + * + * Returns the scheme used to store the private key. If the returned scheme is + * %NM_SETTING_802_1X_CK_SCHEME_BLOB, use + * nm_setting_802_1x_get_client_cert_blob(); if + * %NM_SETTING_802_1X_CK_SCHEME_PATH, use + * nm_setting_802_1x_get_client_cert_path(). + * + * Returns: scheme used to store the private key (blob or path) + **/ +NMSetting8021xCKScheme +nm_setting_802_1x_get_private_key_scheme (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); + + return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key); +} + +/** + * nm_setting_802_1x_get_private_key_blob: + * @setting: the #NMSetting8021x + * + * Private keys are used to authenticate the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * WARNING: the private key property is not a "secret" property, and thus + * unencrypted private key data may be readable by unprivileged users. Private + * keys should always be encrypted with a private key password. + * + * Returns: the private key data + **/ +const GByteArray * +nm_setting_802_1x_get_private_key_blob (NMSetting8021x *setting) +{ + NMSetting8021xCKScheme scheme; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + scheme = nm_setting_802_1x_get_private_key_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key; +} + +/** + * nm_setting_802_1x_get_private_key_path: + * @setting: the #NMSetting8021x + * + * Private keys are used to authenticate the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: path to the private key file + **/ +const char * +nm_setting_802_1x_get_private_key_path (NMSetting8021x *setting) +{ + NMSetting8021xCKScheme scheme; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + scheme = nm_setting_802_1x_get_private_key_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); + + return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key->data + strlen (SCHEME_PATH)); +} + +static GByteArray * +file_to_byte_array (const char *filename) +{ + char *contents; + GByteArray *array = NULL; + gsize length = 0; + + if (g_file_get_contents (filename, &contents, &length, NULL)) { + array = g_byte_array_sized_new (length); + g_byte_array_append (array, (guint8 *) contents, length); + g_assert (array->len == length); + g_free (contents); + } + return array; +} + +/** + * nm_setting_802_1x_set_private_key: + * @setting: the #NMSetting8021x + * @key_path: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH or + * %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the private key file + * (PEM, DER, or PKCS#12 format). The path must be UTF-8 encoded; use + * g_filename_to_utf8() to convert if needed. Passing %NULL with any @scheme + * clears the private key. + * @password: password used to decrypt the private key, or %NULL if the password + * is unknown. If the password is given but fails to decrypt the private key, + * an error is returned. + * @scheme: desired storage scheme for the private key + * @out_format: on successful return, the type of the private key added + * @error: on unsuccessful return, an error + * + * Private keys are used to authenticate the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * This function reads a private key from disk and sets the + * #NMSetting8021x:private-key property with the private key file data if using + * the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the private + * key file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. + * + * If @password is given, this function attempts to decrypt the private key to + * verify that @password is correct, and if it is, updates the + * #NMSetting8021x:private-key-password property with the given @password. If + * the decryption is unsuccessful, %FALSE is returned, @error is set, and no + * internal data is changed. If no @password is given, the private key is + * assumed to be valid, no decryption is performed, and the password may be set + * at a later time. + * + * WARNING: the private key property 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. + * + * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful + **/ +gboolean +nm_setting_802_1x_set_private_key (NMSetting8021x *setting, + const char *key_path, + const char *password, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error) +{ + NMSetting8021xPrivate *priv; + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + gboolean key_cleared = FALSE, password_cleared = FALSE; + GError *local_err = NULL; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + + if (key_path) { + g_return_val_if_fail (g_utf8_validate (key_path, -1, NULL), FALSE); + g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB + || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, + FALSE); + } + + if (out_format) + g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE); + + /* Ensure the private key is a recognized format and if the password was + * given, that it decrypts the private key. + */ + if (key_path) { + format = crypto_verify_private_key (key_path, password, &local_err); + if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + local_err ? local_err->message : _("invalid private key")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY); + g_clear_error (&local_err); + return FALSE; + } + } + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + + /* Clear out any previous private key data */ + if (priv->private_key) { + /* Try not to leave the private key around in memory */ + memset (priv->private_key->data, 0, priv->private_key->len); + g_byte_array_free (priv->private_key, TRUE); + priv->private_key = NULL; + key_cleared = TRUE; + } + + if (priv->private_key_password) { + g_free (priv->private_key_password); + priv->private_key_password = NULL; + password_cleared = TRUE; + } + + if (key_path == NULL) { + if (key_cleared) + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY); + if (password_cleared) + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); + return TRUE; + } + + priv->private_key_password = g_strdup (password); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + /* Shouldn't fail this since we just verified the private key above */ + priv->private_key = file_to_byte_array (key_path); + g_assert (priv->private_key); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + priv->private_key = path_to_scheme_value (key_path); + else + g_assert_not_reached (); + + /* As required by NM and wpa_supplicant, set the client-cert + * property to the same PKCS#12 data. + */ + g_assert (format != NM_CRYPTO_FILE_FORMAT_UNKNOWN); + if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) { + if (priv->client_cert) + g_byte_array_free (priv->client_cert, TRUE); + + priv->client_cert = g_byte_array_sized_new (priv->private_key->len); + g_byte_array_append (priv->client_cert, priv->private_key->data, priv->private_key->len); + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT); + } + + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY); + if (password_cleared || password) + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); + + if (out_format) + *out_format = (NMSetting8021xCKFormat) format; + return priv->private_key != NULL; +} + +/** + * nm_setting_802_1x_get_private_key_password: + * @setting: the #NMSetting8021x + * + * Returns: the private key password used to decrypt the private key if + * previously set with nm_setting_802_1x_set_private_key(), or the + * #NMSetting8021x:private-key-password property. + **/ +const char * +nm_setting_802_1x_get_private_key_password (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key_password; +} + +/** + * nm_setting_802_1x_get_private_key_password_flags: + * @setting: the #NMSetting8021x + * + * Returns: the #NMSettingSecretFlags pertaining to the + * #NMSetting8021x:private-key-password + **/ +NMSettingSecretFlags +nm_setting_802_1x_get_private_key_password_flags (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_SECRET_FLAG_NONE); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key_password_flags; +} + +/** + * nm_setting_802_1x_get_private_key_format: + * @setting: the #NMSetting8021x + * + * Returns: the data format of the private key data stored in the + * #NMSetting8021x:private-key property + **/ +NMSetting8021xCKFormat +nm_setting_802_1x_get_private_key_format (NMSetting8021x *setting) +{ + NMSetting8021xPrivate *priv; + const char *path; + GError *error = NULL; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + + if (!priv->private_key) + return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + + switch (nm_setting_802_1x_get_private_key_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + if (crypto_is_pkcs12_data (priv->private_key)) + return NM_SETTING_802_1X_CK_FORMAT_PKCS12; + return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_private_key_path (setting); + if (crypto_is_pkcs12_file (path, &error)) + return NM_SETTING_802_1X_CK_FORMAT_PKCS12; + if (error) { + /* Couldn't read the file or something */ + g_error_free (error); + return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + } + return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY; + default: + break; + } + + return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; +} + +/** + * nm_setting_802_1x_get_phase2_private_key_password: + * @setting: the #NMSetting8021x + * + * Returns: the private key password used to decrypt the private key if + * previously set with nm_setting_802_1x_set_phase2_private_key() or the + * #NMSetting8021x:phase2-private-key-password property. + **/ +const char * +nm_setting_802_1x_get_phase2_private_key_password (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key_password; +} + +/** + * nm_setting_802_1x_get_phase2_private_key_password_flags: + * @setting: the #NMSetting8021x + * + * Returns: the #NMSettingSecretFlags pertaining to the + * #NMSetting8021x:phase2-private-key-password + **/ +NMSettingSecretFlags +nm_setting_802_1x_get_phase2_private_key_password_flags (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_SECRET_FLAG_NONE); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key_password_flags; +} + +/** + * nm_setting_802_1x_get_phase2_private_key_scheme: + * @setting: the #NMSetting8021x + * + * Returns the scheme used to store the "phase 2" private key. If the returned + * scheme is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use + * nm_setting_802_1x_get_client_cert_blob(); if + * %NM_SETTING_802_1X_CK_SCHEME_PATH, use + * nm_setting_802_1x_get_client_cert_path(). + * + * Returns: scheme used to store the "phase 2" private key (blob or path) + **/ +NMSetting8021xCKScheme +nm_setting_802_1x_get_phase2_private_key_scheme (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); + + return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key); +} + +/** + * nm_setting_802_1x_get_phase2_private_key_blob: + * @setting: the #NMSetting8021x + * + * Private keys are used to authenticate the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * WARNING: the phase2 private key property is not a "secret" property, and thus + * unencrypted private key data may be readable by unprivileged users. Private + * keys should always be encrypted with a private key password. + * + * Returns: the "phase 2" private key data + **/ +const GByteArray * +nm_setting_802_1x_get_phase2_private_key_blob (NMSetting8021x *setting) +{ + NMSetting8021xCKScheme scheme; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key; +} + +/** + * nm_setting_802_1x_get_phase2_private_key_path: + * @setting: the #NMSetting8021x + * + * Private keys are used to authenticate the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: path to the "phase 2" private key file + **/ +const char * +nm_setting_802_1x_get_phase2_private_key_path (NMSetting8021x *setting) +{ + NMSetting8021xCKScheme scheme; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); + + return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key->data + strlen (SCHEME_PATH)); +} + +/** + * nm_setting_802_1x_set_phase2_private_key: + * @setting: the #NMSetting8021x + * @key_path: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH or + * %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the "phase2" private + * key file (PEM, DER, or PKCS#12 format). The path must be UTF-8 encoded; + * use g_filename_to_utf8() to convert if needed. Passing %NULL with any + * @scheme clears the private key. + * @password: password used to decrypt the private key, or %NULL if the password + * is unknown. If the password is given but fails to decrypt the private key, + * an error is returned. + * @scheme: desired storage scheme for the private key + * @out_format: on successful return, the type of the private key added + * @error: on unsuccessful return, an error + * + * Private keys are used to authenticate the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * This function reads a private key from disk and sets the + * #NMSetting8021x:phase2-private-key property with the private key file data if + * using the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the + * private key file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. + * + * If @password is given, this function attempts to decrypt the private key to + * verify that @password is correct, and if it is, updates the + * #NMSetting8021x:phase2-private-key-password property with the given + * @password. If the decryption is unsuccessful, %FALSE is returned, @error is + * set, and no internal data is changed. If no @password is given, the private + * key is assumed to be valid, no decryption is performed, and the password may + * be set at a later time. + * + * WARNING: the "phase2" private key property 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. + * + * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful + **/ +gboolean +nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, + const char *key_path, + const char *password, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error) +{ + NMSetting8021xPrivate *priv; + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + gboolean key_cleared = FALSE, password_cleared = FALSE; + GError *local_err = NULL; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + + if (key_path) { + g_return_val_if_fail (g_utf8_validate (key_path, -1, NULL), FALSE); + g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB + || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, + FALSE); + } + + if (out_format) + g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE); + + /* Ensure the private key is a recognized format and if the password was + * given, that it decrypts the private key. + */ + if (key_path) { + format = crypto_verify_private_key (key_path, password, &local_err); + if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + local_err ? local_err->message : _("invalid phase2 private key")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + g_clear_error (&local_err); + return FALSE; + } + } + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + + /* Clear out any previous private key data */ + if (priv->phase2_private_key) { + /* Try not to leave the private key around in memory */ + memset (priv->phase2_private_key->data, 0, priv->phase2_private_key->len); + g_byte_array_free (priv->phase2_private_key, TRUE); + priv->phase2_private_key = NULL; + key_cleared = TRUE; + } + + if (priv->phase2_private_key_password) { + g_free (priv->phase2_private_key_password); + priv->phase2_private_key_password = NULL; + password_cleared = TRUE; + } + + if (key_path == NULL) { + if (key_cleared) + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + if (password_cleared) + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD); + return TRUE; + } + + priv->phase2_private_key_password = g_strdup (password); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + /* Shouldn't fail this since we just verified the private key above */ + priv->phase2_private_key = file_to_byte_array (key_path); + g_assert (priv->phase2_private_key); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + priv->phase2_private_key = path_to_scheme_value (key_path); + else + g_assert_not_reached (); + + /* As required by NM and wpa_supplicant, set the client-cert + * property to the same PKCS#12 data. + */ + g_assert (format != NM_CRYPTO_FILE_FORMAT_UNKNOWN); + if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) { + if (priv->phase2_client_cert) + g_byte_array_free (priv->phase2_client_cert, TRUE); + + priv->phase2_client_cert = g_byte_array_sized_new (priv->phase2_private_key->len); + g_byte_array_append (priv->phase2_client_cert, priv->phase2_private_key->data, priv->phase2_private_key->len); + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT); + } + + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + if (password_cleared || password) + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD); + + if (out_format) + *out_format = (NMSetting8021xCKFormat) format; + return priv->phase2_private_key != NULL; +} + +/** + * nm_setting_802_1x_get_phase2_private_key_format: + * @setting: the #NMSetting8021x + * + * Returns: the data format of the "phase 2" private key data stored in the + * #NMSetting8021x:phase2-private-key property + **/ +NMSetting8021xCKFormat +nm_setting_802_1x_get_phase2_private_key_format (NMSetting8021x *setting) +{ + NMSetting8021xPrivate *priv; + const char *path; + GError *error = NULL; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + + if (!priv->phase2_private_key) + return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + + switch (nm_setting_802_1x_get_phase2_private_key_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + if (crypto_is_pkcs12_data (priv->phase2_private_key)) + return NM_SETTING_802_1X_CK_FORMAT_PKCS12; + return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_phase2_private_key_path (setting); + if (crypto_is_pkcs12_file (path, &error)) + return NM_SETTING_802_1X_CK_FORMAT_PKCS12; + if (error) { + /* Couldn't read the file or something */ + g_error_free (error); + return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + } + return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY; + default: + break; + } + + return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; +} + +static void +need_secrets_password (NMSetting8021x *self, + GPtrArray *secrets, + gboolean phase2) +{ + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); + + if ( (!priv->password || !strlen (priv->password)) + && (!priv->password_raw || !priv->password_raw->len)) { + g_ptr_array_add (secrets, NM_SETTING_802_1X_PASSWORD); + g_ptr_array_add (secrets, NM_SETTING_802_1X_PASSWORD_RAW); + } +} + +static void +need_secrets_sim (NMSetting8021x *self, + GPtrArray *secrets, + gboolean phase2) +{ + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); + + if (!priv->pin || !strlen (priv->pin)) + g_ptr_array_add (secrets, NM_SETTING_802_1X_PIN); +} + +static gboolean +need_private_key_password (const GByteArray *blob, + const char *path, + const char *password) +{ + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + + /* Private key password is required */ + if (password) { + if (path) + format = crypto_verify_private_key (path, password, NULL); + else if (blob) + format = crypto_verify_private_key_data (blob, password, NULL); + else + g_warning ("%s: unknown private key password scheme", __func__); + } + + return (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN); +} + +static void +need_secrets_tls (NMSetting8021x *self, + GPtrArray *secrets, + gboolean phase2) +{ + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); + NMSetting8021xCKScheme scheme; + const GByteArray *blob = NULL; + const char *path = NULL; + + if (phase2) { + scheme = nm_setting_802_1x_get_phase2_private_key_scheme (self); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + path = nm_setting_802_1x_get_phase2_private_key_path (self); + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + blob = nm_setting_802_1x_get_phase2_private_key_blob (self); + else { + g_warning ("%s: unknown phase2 private key scheme %d", __func__, scheme); + g_ptr_array_add (secrets, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + return; + } + + if (need_private_key_password (blob, path, priv->phase2_private_key_password)) + g_ptr_array_add (secrets, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD); + } else { + scheme = nm_setting_802_1x_get_private_key_scheme (self); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + path = nm_setting_802_1x_get_private_key_path (self); + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + blob = nm_setting_802_1x_get_private_key_blob (self); + else { + g_warning ("%s: unknown private key scheme %d", __func__, scheme); + g_ptr_array_add (secrets, NM_SETTING_802_1X_PRIVATE_KEY); + return; + } + + if (need_private_key_password (blob, path, priv->private_key_password)) + g_ptr_array_add (secrets, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); + } +} + +static gboolean +verify_tls (NMSetting8021x *self, gboolean phase2, GError **error) +{ + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); + + if (phase2) { + if (!priv->phase2_client_cert) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CLIENT_CERT); + return FALSE; + } else if (!priv->phase2_client_cert->len) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CLIENT_CERT); + return FALSE; + } + + /* Private key is required for TLS */ + if (!priv->phase2_private_key) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + return FALSE; + } else if (!priv->phase2_private_key->len) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + return FALSE; + } + + /* If the private key is PKCS#12, check that it matches the client cert */ + if (crypto_is_pkcs12_data (priv->phase2_private_key)) { + if (priv->phase2_private_key->len != priv->phase2_client_cert->len) { + g_set_error (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("has to match '%s' property for PKCS#12"), + NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CLIENT_CERT); + return FALSE; + } + + if (memcmp (priv->phase2_private_key->data, + priv->phase2_client_cert->data, + priv->phase2_private_key->len)) { + g_set_error (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("has to match '%s' property for PKCS#12"), + NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CLIENT_CERT); + return FALSE; + } + } + } else { + if (!priv->client_cert) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT); + return FALSE; + } else if (!priv->client_cert->len) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT); + return FALSE; + } + + /* Private key is required for TLS */ + if (!priv->private_key) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY); + return FALSE; + } else if (!priv->private_key->len) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY); + return FALSE; + } + + /* If the private key is PKCS#12, check that it matches the client cert */ + if (crypto_is_pkcs12_data (priv->private_key)) { + if (priv->private_key->len != priv->client_cert->len) { + g_set_error (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("has to match '%s' property for PKCS#12"), + NM_SETTING_802_1X_PRIVATE_KEY); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT); + return FALSE; + } + + if (memcmp (priv->private_key->data, + priv->client_cert->data, + priv->private_key->len)) { + g_set_error (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("has to match '%s' property for PKCS#12"), + NM_SETTING_802_1X_PRIVATE_KEY); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT); + return FALSE; + } + } + } + + return TRUE; +} + +static gboolean +verify_ttls (NMSetting8021x *self, gboolean phase2, GError **error) +{ + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); + + if ( (!priv->identity || !strlen (priv->identity)) + && (!priv->anonymous_identity || !strlen (priv->anonymous_identity))) { + if (!priv->identity) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_IDENTITY); + } else if (!strlen (priv->identity)) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_IDENTITY); + } else if (!priv->anonymous_identity) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_ANONYMOUS_IDENTITY); + } else { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_ANONYMOUS_IDENTITY); + } + return FALSE; + } + + if ( (!priv->phase2_auth || !strlen (priv->phase2_auth)) + && (!priv->phase2_autheap || !strlen (priv->phase2_autheap))) { + if (!priv->phase2_auth) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_AUTH); + } else if (!strlen (priv->phase2_auth)) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_AUTH); + } else if (!priv->phase2_autheap) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_AUTHEAP); + } else { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_AUTHEAP); + } + return FALSE; + } + + return TRUE; +} + +static gboolean +verify_identity (NMSetting8021x *self, gboolean phase2, GError **error) +{ + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); + + if (!priv->identity) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_IDENTITY); + return FALSE; + } else if (!strlen (priv->identity)) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_IDENTITY); + return FALSE; + } + + return TRUE; +} + +/* Implemented below... */ +static void need_secrets_phase2 (NMSetting8021x *self, + GPtrArray *secrets, + gboolean phase2); + + +typedef void (*EAPMethodNeedSecretsFunc) (NMSetting8021x *self, + GPtrArray *secrets, + gboolean phase2); + +typedef gboolean (*EAPMethodValidateFunc)(NMSetting8021x *self, + gboolean phase2, + GError **error); + +typedef struct { + const char *method; + EAPMethodNeedSecretsFunc ns_func; + EAPMethodValidateFunc v_func; +} EAPMethodsTable; + +static EAPMethodsTable eap_methods_table[] = { + { "leap", need_secrets_password, verify_identity }, + { "pwd", need_secrets_password, verify_identity }, + { "md5", need_secrets_password, verify_identity }, + { "pap", need_secrets_password, verify_identity }, + { "chap", need_secrets_password, verify_identity }, + { "mschap", need_secrets_password, verify_identity }, + { "mschapv2", need_secrets_password, verify_identity }, + { "fast", need_secrets_password, verify_identity }, + { "tls", need_secrets_tls, verify_tls }, + { "peap", need_secrets_phase2, verify_ttls }, + { "ttls", need_secrets_phase2, verify_ttls }, + { "sim", need_secrets_sim, NULL }, + { "gtc", need_secrets_password, verify_identity }, + { "otp", NULL, NULL }, // FIXME: implement + { NULL, NULL, NULL } +}; + +static void +need_secrets_phase2 (NMSetting8021x *self, + GPtrArray *secrets, + gboolean phase2) +{ + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); + char *method = NULL; + int i; + + g_return_if_fail (phase2 == FALSE); + + /* Check phase2_auth and phase2_autheap */ + method = priv->phase2_auth; + if (!method && priv->phase2_autheap) + method = priv->phase2_autheap; + + if (!method) { + g_warning ("Couldn't find EAP method."); + g_assert_not_reached(); + return; + } + + /* Ask the configured phase2 method if it needs secrets */ + for (i = 0; eap_methods_table[i].method; i++) { + if (eap_methods_table[i].ns_func == NULL) + continue; + if (!strcmp (eap_methods_table[i].method, method)) { + (*eap_methods_table[i].ns_func) (self, secrets, TRUE); + break; + } + } +} + + +static GPtrArray * +need_secrets (NMSetting *setting) +{ + NMSetting8021x *self = NM_SETTING_802_1X (setting); + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); + GSList *iter; + GPtrArray *secrets; + gboolean eap_method_found = FALSE; + + secrets = g_ptr_array_sized_new (4); + + /* Ask each configured EAP method if it needs secrets */ + for (iter = priv->eap; iter && !eap_method_found; iter = g_slist_next (iter)) { + const char *method = (const char *) iter->data; + int i; + + for (i = 0; eap_methods_table[i].method; i++) { + if (eap_methods_table[i].ns_func == NULL) + continue; + if (!strcmp (eap_methods_table[i].method, method)) { + (*eap_methods_table[i].ns_func) (self, secrets, FALSE); + + /* Only break out of the outer loop if this EAP method + * needed secrets. + */ + if (secrets->len > 0) + eap_method_found = TRUE; + break; + } + } + } + + if (secrets->len == 0) { + g_ptr_array_free (secrets, TRUE); + secrets = NULL; + } + + return secrets; +} + +static gboolean +verify_cert (GByteArray *array, const char *prop_name, GError **error) +{ + if (!array) + return TRUE; + + switch (get_cert_scheme (array)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + return TRUE; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + /* For path-based schemes, verify that the path is zero-terminated */ + if (array->data[array->len - 1] == '\0') { + /* And ensure it's UTF-8 valid too so we can pass it through + * D-Bus and stuff like that. + */ + if (g_utf8_validate ((const char *) (array->data + strlen (SCHEME_PATH)), -1, NULL)) + return TRUE; + } + break; + default: + break; + } + + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_name); + return FALSE; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSetting8021x *self = NM_SETTING_802_1X (setting); + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); + const char *valid_eap[] = { "leap", "md5", "tls", "peap", "ttls", "sim", "fast", "pwd", NULL }; + const char *valid_phase1_peapver[] = { "0", "1", NULL }; + const char *valid_phase1_peaplabel[] = { "0", "1", NULL }; + const char *valid_phase1_fast_pac[] = { "0", "1", "2", "3", NULL }; + const char *valid_phase2_auth[] = { "pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", "tls", NULL }; + const char *valid_phase2_autheap[] = { "md5", "mschapv2", "otp", "gtc", "tls", NULL }; + GSList *iter; + + if (error) + g_return_val_if_fail (*error == NULL, FALSE); + + if (!priv->eap) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_EAP); + return FALSE; + } + + if (!_nm_utils_string_slist_validate (priv->eap, valid_eap)) { + g_set_error_literal (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_EAP); + return FALSE; + } + + /* Ask each configured EAP method if its valid */ + for (iter = priv->eap; iter; iter = g_slist_next (iter)) { + const char *method = (const char *) iter->data; + int i; + + for (i = 0; eap_methods_table[i].method; i++) { + if (eap_methods_table[i].v_func == NULL) + continue; + if (!strcmp (eap_methods_table[i].method, method)) { + if (!(*eap_methods_table[i].v_func) (self, FALSE, error)) + return FALSE; + break; + } + } + } + + if (priv->phase1_peapver && !_nm_utils_string_in_list (priv->phase1_peapver, valid_phase1_peapver)) { + g_set_error (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase1_peapver); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE1_PEAPVER); + return FALSE; + } + + if (priv->phase1_peaplabel && !_nm_utils_string_in_list (priv->phase1_peaplabel, valid_phase1_peaplabel)) { + g_set_error (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase1_peaplabel); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE1_PEAPLABEL); + return FALSE; + } + + if (priv->phase1_fast_provisioning && !_nm_utils_string_in_list (priv->phase1_fast_provisioning, valid_phase1_fast_pac)) { + g_set_error (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase1_fast_provisioning); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING); + return FALSE; + } + + if (priv->phase2_auth && !_nm_utils_string_in_list (priv->phase2_auth, valid_phase2_auth)) { + g_set_error (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase2_auth); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_AUTH); + return FALSE; + } + + if (priv->phase2_autheap && !_nm_utils_string_in_list (priv->phase2_autheap, valid_phase2_autheap)) { + g_set_error (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase2_autheap); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_AUTHEAP); + return FALSE; + } + + if (!verify_cert (priv->ca_cert, NM_SETTING_802_1X_CA_CERT, error)) + return FALSE; + if (!verify_cert (priv->phase2_ca_cert, NM_SETTING_802_1X_PHASE2_CA_CERT, error)) + return FALSE; + + if (!verify_cert (priv->client_cert, NM_SETTING_802_1X_CLIENT_CERT, error)) + return FALSE; + if (!verify_cert (priv->phase2_client_cert, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, error)) + return FALSE; + + if (!verify_cert (priv->private_key, NM_SETTING_802_1X_PRIVATE_KEY, error)) + return FALSE; + if (!verify_cert (priv->phase2_private_key, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, error)) + return FALSE; + + /* FIXME: finish */ + + return TRUE; +} + +static void +nm_setting_802_1x_init (NMSetting8021x *setting) +{ +} + +static void +finalize (GObject *object) +{ + NMSetting8021x *self = NM_SETTING_802_1X (object); + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); + + /* Strings first. g_free() already checks for NULLs so we don't have to */ + + g_free (priv->identity); + g_free (priv->anonymous_identity); + g_free (priv->ca_path); + g_free (priv->subject_match); + g_free (priv->phase1_peapver); + g_free (priv->phase1_peaplabel); + g_free (priv->phase1_fast_provisioning); + g_free (priv->phase2_auth); + g_free (priv->phase2_autheap); + g_free (priv->phase2_ca_path); + g_free (priv->phase2_subject_match); + g_free (priv->password); + if (priv->password_raw) + g_byte_array_free (priv->password_raw, TRUE); + g_free (priv->pin); + + g_slist_free_full (priv->eap, g_free); + g_slist_free_full (priv->altsubject_matches, g_free); + g_slist_free_full (priv->phase2_altsubject_matches, g_free); + + if (priv->ca_cert) + g_byte_array_free (priv->ca_cert, TRUE); + if (priv->client_cert) + g_byte_array_free (priv->client_cert, TRUE); + if (priv->private_key) + g_byte_array_free (priv->private_key, TRUE); + g_free (priv->private_key_password); + if (priv->phase2_ca_cert) + g_byte_array_free (priv->phase2_ca_cert, TRUE); + if (priv->phase2_client_cert) + g_byte_array_free (priv->phase2_client_cert, TRUE); + if (priv->phase2_private_key) + g_byte_array_free (priv->phase2_private_key, TRUE); + g_free (priv->phase2_private_key_password); + + G_OBJECT_CLASS (nm_setting_802_1x_parent_class)->finalize (object); +} + +static GByteArray * +set_cert_prop_helper (const GValue *value, const char *prop_name, GError **error) +{ + gboolean valid; + GByteArray *data = NULL; + + data = g_value_dup_boxed (value); + /* Verify the new data */ + if (data) { + valid = verify_cert (data, prop_name, error); + if (!valid) { + g_byte_array_free (data, TRUE); + data = NULL; + } + } + return data; +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSetting8021x *setting = NM_SETTING_802_1X (object); + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + GError *error = NULL; + + switch (prop_id) { + case PROP_EAP: + g_slist_free_full (priv->eap, g_free); + priv->eap = g_value_dup_boxed (value); + break; + case PROP_IDENTITY: + g_free (priv->identity); + priv->identity = g_value_dup_string (value); + break; + case PROP_ANONYMOUS_IDENTITY: + g_free (priv->anonymous_identity); + priv->anonymous_identity = g_value_dup_string (value); + break; + case PROP_PAC_FILE: + g_free (priv->pac_file); + priv->pac_file = g_value_dup_string (value); + break; + case PROP_CA_CERT: + if (priv->ca_cert) { + g_byte_array_free (priv->ca_cert, TRUE); + priv->ca_cert = NULL; + } + priv->ca_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_CA_CERT, &error); + if (error) { + g_warning ("Error setting certificate (invalid data): (%d) %s", + error->code, error->message); + g_error_free (error); + } + break; + case PROP_CA_PATH: + g_free (priv->ca_path); + priv->ca_path = g_value_dup_string (value); + break; + case PROP_SUBJECT_MATCH: + g_free (priv->subject_match); + priv->subject_match = g_value_dup_string (value); + break; + case PROP_ALTSUBJECT_MATCHES: + g_slist_free_full (priv->altsubject_matches, g_free); + priv->altsubject_matches = g_value_dup_boxed (value); + break; + case PROP_CLIENT_CERT: + if (priv->client_cert) { + g_byte_array_free (priv->client_cert, TRUE); + priv->client_cert = NULL; + } + priv->client_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_CLIENT_CERT, &error); + if (error) { + g_warning ("Error setting certificate (invalid data): (%d) %s", + error->code, error->message); + g_error_free (error); + } + break; + case PROP_PHASE1_PEAPVER: + g_free (priv->phase1_peapver); + priv->phase1_peapver = g_value_dup_string (value); + break; + case PROP_PHASE1_PEAPLABEL: + g_free (priv->phase1_peaplabel); + priv->phase1_peaplabel = g_value_dup_string (value); + break; + case PROP_PHASE1_FAST_PROVISIONING: + g_free (priv->phase1_fast_provisioning); + priv->phase1_fast_provisioning = g_value_dup_string (value); + break; + case PROP_PHASE2_AUTH: + g_free (priv->phase2_auth); + priv->phase2_auth = g_value_dup_string (value); + break; + case PROP_PHASE2_AUTHEAP: + g_free (priv->phase2_autheap); + priv->phase2_autheap = g_value_dup_string (value); + break; + case PROP_PHASE2_CA_CERT: + if (priv->phase2_ca_cert) { + g_byte_array_free (priv->phase2_ca_cert, TRUE); + priv->phase2_ca_cert = NULL; + } + priv->phase2_ca_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_CA_CERT, &error); + if (error) { + g_warning ("Error setting certificate (invalid data): (%d) %s", + error->code, error->message); + g_error_free (error); + } + break; + case PROP_PHASE2_CA_PATH: + g_free (priv->phase2_ca_path); + priv->phase2_ca_path = g_value_dup_string (value); + break; + case PROP_PHASE2_SUBJECT_MATCH: + g_free (priv->phase2_subject_match); + priv->phase2_subject_match = g_value_dup_string (value); + break; + case PROP_PHASE2_ALTSUBJECT_MATCHES: + g_slist_free_full (priv->phase2_altsubject_matches, g_free); + priv->phase2_altsubject_matches = g_value_dup_boxed (value); + break; + case PROP_PHASE2_CLIENT_CERT: + if (priv->phase2_client_cert) { + g_byte_array_free (priv->phase2_client_cert, TRUE); + priv->phase2_client_cert = NULL; + } + priv->phase2_client_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, &error); + if (error) { + g_warning ("Error setting certificate (invalid data): (%d) %s", + error->code, error->message); + g_error_free (error); + } + break; + case PROP_PASSWORD: + g_free (priv->password); + priv->password = g_value_dup_string (value); + break; + case PROP_PASSWORD_FLAGS: + priv->password_flags = g_value_get_uint (value); + break; + case PROP_PASSWORD_RAW: + if (priv->password_raw) + g_byte_array_free (priv->password_raw, TRUE); + priv->password_raw = g_value_dup_boxed (value); + break; + case PROP_PASSWORD_RAW_FLAGS: + priv->password_raw_flags = g_value_get_uint (value); + break; + case PROP_PRIVATE_KEY: + if (priv->private_key) { + g_byte_array_free (priv->private_key, TRUE); + priv->private_key = NULL; + } + priv->private_key = set_cert_prop_helper (value, NM_SETTING_802_1X_PRIVATE_KEY, &error); + if (error) { + g_warning ("Error setting private key (invalid data): (%d) %s", + error->code, error->message); + g_error_free (error); + } + break; + case PROP_PRIVATE_KEY_PASSWORD: + g_free (priv->private_key_password); + priv->private_key_password = g_value_dup_string (value); + break; + case PROP_PRIVATE_KEY_PASSWORD_FLAGS: + priv->private_key_password_flags = g_value_get_uint (value); + break; + case PROP_PHASE2_PRIVATE_KEY: + if (priv->phase2_private_key) { + g_byte_array_free (priv->phase2_private_key, TRUE); + priv->phase2_private_key = NULL; + } + priv->phase2_private_key = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &error); + if (error) { + g_warning ("Error setting private key (invalid data): (%d) %s", + error->code, error->message); + g_error_free (error); + } + break; + case PROP_PHASE2_PRIVATE_KEY_PASSWORD: + g_free (priv->phase2_private_key_password); + priv->phase2_private_key_password = g_value_dup_string (value); + break; + case PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS: + priv->phase2_private_key_password_flags = g_value_get_uint (value); + break; + case PROP_PIN: + g_free (priv->pin); + priv->pin = g_value_dup_string (value); + break; + case PROP_PIN_FLAGS: + priv->pin_flags = g_value_get_uint (value); + break; + case PROP_SYSTEM_CA_CERTS: + priv->system_ca_certs = g_value_get_boolean (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) +{ + NMSetting8021x *setting = NM_SETTING_802_1X (object); + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + + switch (prop_id) { + case PROP_EAP: + g_value_set_boxed (value, priv->eap); + break; + case PROP_IDENTITY: + g_value_set_string (value, priv->identity); + break; + case PROP_ANONYMOUS_IDENTITY: + g_value_set_string (value, priv->anonymous_identity); + break; + case PROP_PAC_FILE: + g_value_set_string (value, priv->pac_file); + break; + case PROP_CA_CERT: + g_value_set_boxed (value, priv->ca_cert); + break; + case PROP_CA_PATH: + g_value_set_string (value, priv->ca_path); + break; + case PROP_SUBJECT_MATCH: + g_value_set_string (value, priv->subject_match); + break; + case PROP_ALTSUBJECT_MATCHES: + g_value_set_boxed (value, priv->altsubject_matches); + break; + case PROP_CLIENT_CERT: + g_value_set_boxed (value, priv->client_cert); + break; + case PROP_PHASE1_PEAPVER: + g_value_set_string (value, priv->phase1_peapver); + break; + case PROP_PHASE1_PEAPLABEL: + g_value_set_string (value, priv->phase1_peaplabel); + break; + case PROP_PHASE1_FAST_PROVISIONING: + g_value_set_string (value, priv->phase1_fast_provisioning); + break; + case PROP_PHASE2_AUTH: + g_value_set_string (value, priv->phase2_auth); + break; + case PROP_PHASE2_AUTHEAP: + g_value_set_string (value, priv->phase2_autheap); + break; + case PROP_PHASE2_CA_CERT: + g_value_set_boxed (value, priv->phase2_ca_cert); + break; + case PROP_PHASE2_CA_PATH: + g_value_set_string (value, priv->phase2_ca_path); + break; + case PROP_PHASE2_SUBJECT_MATCH: + g_value_set_string (value, priv->phase2_subject_match); + break; + case PROP_PHASE2_ALTSUBJECT_MATCHES: + g_value_set_boxed (value, priv->phase2_altsubject_matches); + break; + case PROP_PHASE2_CLIENT_CERT: + g_value_set_boxed (value, priv->phase2_client_cert); + break; + case PROP_PASSWORD: + g_value_set_string (value, priv->password); + break; + case PROP_PASSWORD_FLAGS: + g_value_set_uint (value, priv->password_flags); + break; + case PROP_PASSWORD_RAW: + g_value_set_boxed (value, priv->password_raw); + break; + case PROP_PASSWORD_RAW_FLAGS: + g_value_set_uint (value, priv->password_raw_flags); + break; + case PROP_PRIVATE_KEY: + g_value_set_boxed (value, priv->private_key); + break; + case PROP_PRIVATE_KEY_PASSWORD: + g_value_set_string (value, priv->private_key_password); + break; + case PROP_PRIVATE_KEY_PASSWORD_FLAGS: + g_value_set_uint (value, priv->private_key_password_flags); + break; + case PROP_PHASE2_PRIVATE_KEY: + g_value_set_boxed (value, priv->phase2_private_key); + break; + case PROP_PHASE2_PRIVATE_KEY_PASSWORD: + g_value_set_string (value, priv->phase2_private_key_password); + break; + case PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS: + g_value_set_uint (value, priv->phase2_private_key_password_flags); + break; + case PROP_PIN: + g_value_set_string (value, priv->pin); + break; + case PROP_PIN_FLAGS: + g_value_set_uint (value, priv->pin_flags); + break; + case PROP_SYSTEM_CA_CERTS: + g_value_set_boolean (value, priv->system_ca_certs); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + GError *error = NULL; + + g_type_class_add_private (setting_class, sizeof (NMSetting8021xPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + parent_class->verify = verify; + parent_class->need_secrets = need_secrets; + + /* Properties */ + + /** + * NMSetting8021x:eap: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_EAP, + _nm_param_spec_specialized (NM_SETTING_802_1X_EAP, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:identity: + * + * Identity string for EAP authentication methods. Often the user's user or + * login name. + **/ + g_object_class_install_property + (object_class, PROP_IDENTITY, + g_param_spec_string (NM_SETTING_802_1X_IDENTITY, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:anonymous-identity: + * + * Anonymous identity string for EAP authentication methods. Used as the + * unencrypted identity with EAP types that support different tunneled + * identity like EAP-TTLS. + **/ + g_object_class_install_property + (object_class, PROP_ANONYMOUS_IDENTITY, + g_param_spec_string (NM_SETTING_802_1X_ANONYMOUS_IDENTITY, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:pac-file: + * + * UTF-8 encoded file path containing PAC for EAP-FAST. + **/ + g_object_class_install_property + (object_class, PROP_PAC_FILE, + g_param_spec_string (NM_SETTING_802_1X_PAC_FILE, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:ca-cert: + * + * Contains the CA certificate if used by the EAP method specified in the + * #NMSetting8021x: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. 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. + * + * Setting this property directly is discouraged; use the + * nm_setting_802_1x_set_ca_cert() function instead. + **/ + g_object_class_install_property + (object_class, PROP_CA_CERT, + _nm_param_spec_specialized (NM_SETTING_802_1X_CA_CERT, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:ca-path: + * + * 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 #NMSetting8021x:ca-cert property. + **/ + g_object_class_install_property + (object_class, PROP_CA_PATH, + g_param_spec_string (NM_SETTING_802_1X_CA_PATH, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:subject-match: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_SUBJECT_MATCH, + g_param_spec_string (NM_SETTING_802_1X_SUBJECT_MATCH, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:altsubject-matches: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_ALTSUBJECT_MATCHES, + _nm_param_spec_specialized (NM_SETTING_802_1X_ALTSUBJECT_MATCHES, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:client-cert: + * + * Contains the client certificate if used by the EAP method specified in + * the #NMSetting8021x: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. + * + * Setting this property directly is discouraged; use the + * nm_setting_802_1x_set_client_cert() function instead. + **/ + g_object_class_install_property + (object_class, PROP_CLIENT_CERT, + _nm_param_spec_specialized (NM_SETTING_802_1X_CLIENT_CERT, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:phase1-peapver: + * + * Forces which PEAP version is used when PEAP is set as the EAP method in + * the #NMSetting8021x: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. + **/ + g_object_class_install_property + (object_class, PROP_PHASE1_PEAPVER, + g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPVER, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:phase1-peaplabel: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_PHASE1_PEAPLABEL, + g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPLABEL, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:phase1-fast-provisioning: + * + * Enables or disables in-line provisioning of EAP-FAST credentials when + * FAST is specified as the EAP method in the #NMSetting8021x: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. + **/ + g_object_class_install_property + (object_class, PROP_PHASE1_FAST_PROVISIONING, + g_param_spec_string (NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:phase2-auth: + * + * Specifies the allowed "phase 2" inner non-EAP authentication methods when + * an EAP method that uses an inner TLS tunnel is specified in the + * #NMSetting8021x:eap property. Recognized non-EAP "phase 2" methods are + * "pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", and "tls". + * Each "phase 2" inner method requires specific parameters for successful + * authentication; see the wpa_supplicant documentation for more details. + **/ + g_object_class_install_property + (object_class, PROP_PHASE2_AUTH, + g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTH, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:phase2-autheap: + * + * Specifies the allowed "phase 2" inner EAP-based authentication methods + * when an EAP method that uses an inner TLS tunnel is specified in the + * #NMSetting8021x: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. + **/ + g_object_class_install_property + (object_class, PROP_PHASE2_AUTHEAP, + g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTHEAP, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:phase2-ca-cert: + * + * Contains the "phase 2" CA certificate if used by the EAP method specified + * in the #NMSetting8021x:phase2-auth or #NMSetting8021x: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. + * + * Setting this property directly is discouraged; use the + * nm_setting_802_1x_set_phase2_ca_cert() function instead. + **/ + g_object_class_install_property + (object_class, PROP_PHASE2_CA_CERT, + _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_CA_CERT, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:phase2-ca-path: + * + * 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 #NMSetting8021x:phase2-ca-cert property. + **/ + g_object_class_install_property + (object_class, PROP_PHASE2_CA_PATH, + g_param_spec_string (NM_SETTING_802_1X_PHASE2_CA_PATH, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:phase2-subject-match: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_PHASE2_SUBJECT_MATCH, + g_param_spec_string (NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:phase2-altsubject-matches: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_PHASE2_ALTSUBJECT_MATCHES, + _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:phase2-client-cert: + * + * Contains the "phase 2" client certificate if used by the EAP method + * specified in the #NMSetting8021x:phase2-auth or + * #NMSetting8021x: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. + * + * Setting this property directly is discouraged; use the + * nm_setting_802_1x_set_phase2_client_cert() function instead. + **/ + g_object_class_install_property + (object_class, PROP_PHASE2_CLIENT_CERT, + _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_CLIENT_CERT, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:password: + * + * UTF-8 encoded password used for EAP authentication methods. If both the + * #NMSetting8021x:password property and the #NMSetting8021x:password-raw + * property are specified, #NMSetting8021x:password is preferred. + **/ + g_object_class_install_property + (object_class, PROP_PASSWORD, + g_param_spec_string (NM_SETTING_802_1X_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:password-flags: + * + * Flags indicating how to handle the #NMSetting8021x:password property. + **/ + g_object_class_install_property + (object_class, PROP_PASSWORD_FLAGS, + g_param_spec_uint (NM_SETTING_802_1X_PASSWORD_FLAGS, "", "", + NM_SETTING_SECRET_FLAG_NONE, + NM_SETTING_SECRET_FLAGS_ALL, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:password-raw: + * + * 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 + * #NMSetting8021x:password property and the #NMSetting8021x:password-raw + * property are specified, #NMSetting8021x:password is preferred. + **/ + g_object_class_install_property + (object_class, PROP_PASSWORD_RAW, + _nm_param_spec_specialized (NM_SETTING_802_1X_PASSWORD_RAW, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:password-raw-flags: + * + * Flags indicating how to handle the #NMSetting8021x:password-raw property. + **/ + g_object_class_install_property + (object_class, PROP_PASSWORD_RAW_FLAGS, + g_param_spec_uint (NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, "", "", + NM_SETTING_SECRET_FLAG_NONE, + NM_SETTING_SECRET_FLAGS_ALL, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:private-key: + * + * Contains the private key when the #NMSetting8021x: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 + * #NMSetting8021x: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 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. + * + * Setting this property directly is discouraged; use the + * nm_setting_802_1x_set_private_key() function instead. + * + * WARNING: #NMSetting8021x: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. + **/ + g_object_class_install_property + (object_class, PROP_PRIVATE_KEY, + _nm_param_spec_specialized (NM_SETTING_802_1X_PRIVATE_KEY, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:private-key-password: + * + * The password used to decrypt the private key specified in the + * #NMSetting8021x:private-key property when the private key either uses the + * path scheme, or if the private key is a PKCS#12 format key. Setting this + * property directly is not generally necessary except when returning + * secrets to NetworkManager; it is generally set automatically when setting + * the private key by the nm_setting_802_1x_set_private_key() function. + **/ + g_object_class_install_property + (object_class, PROP_PRIVATE_KEY_PASSWORD, + g_param_spec_string (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:private-key-password-flags: + * + * Flags indicating how to handle the #NMSetting8021x:private-key-password + * property. + **/ + g_object_class_install_property + (object_class, PROP_PRIVATE_KEY_PASSWORD_FLAGS, + g_param_spec_uint (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS, "", "", + NM_SETTING_SECRET_FLAG_NONE, + NM_SETTING_SECRET_FLAGS_ALL, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:phase2-private-key: + * + * Contains the "phase 2" inner private key when the + * #NMSetting8021x:phase2-auth or #NMSetting8021x: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 + * #NMSetting8021x: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 and ending with a terminating NUL byte, and as with the blob scheme + * the #NMSetting8021x:phase2-private-key-password property must be set to + * the password used to decode the PKCS#12 private key and certificate. + * + * Setting this property directly is discouraged; use the + * nm_setting_802_1x_set_phase2_private_key() function instead. + **/ + g_object_class_install_property + (object_class, PROP_PHASE2_PRIVATE_KEY, + _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:phase2-private-key-password: + * + * The password used to decrypt the "phase 2" private key specified in the + * #NMSetting8021x:phase2-private-key property when the private key either + * uses the path scheme, or is a PKCS#12 format key. Setting this property + * directly is not generally necessary except when returning secrets to + * NetworkManager; it is generally set automatically when setting the + * private key by the nm_setting_802_1x_set_phase2_private_key() function. + **/ + g_object_class_install_property + (object_class, PROP_PHASE2_PRIVATE_KEY_PASSWORD, + g_param_spec_string (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:phase2-private-key-password-flags: + * + * Flags indicating how to handle the + * #NMSetting8021x:phase2-private-key-password property. + **/ + g_object_class_install_property + (object_class, PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS, + g_param_spec_uint (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS, "", "", + NM_SETTING_SECRET_FLAG_NONE, + NM_SETTING_SECRET_FLAGS_ALL, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:pin: + * + * PIN used for EAP authentication methods. + **/ + g_object_class_install_property + (object_class, PROP_PIN, + g_param_spec_string (NM_SETTING_802_1X_PIN, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:pin-flags: + * + * Flags indicating how to handle the #NMSetting8021x:pin property. + **/ + g_object_class_install_property + (object_class, PROP_PIN_FLAGS, + g_param_spec_uint (NM_SETTING_802_1X_PIN_FLAGS, "", "", + NM_SETTING_SECRET_FLAG_NONE, + NM_SETTING_SECRET_FLAGS_ALL, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSetting8021x:system-ca-certs: + * + * When %TRUE, overrides the #NMSetting8021x:ca-path and + * #NMSetting8021x: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 #NMSetting8021x:ca-cert and + * #NMSetting8021x:phase2-ca-cert properties. + **/ + g_object_class_install_property + (object_class, PROP_SYSTEM_CA_CERTS, + g_param_spec_boolean (NM_SETTING_802_1X_SYSTEM_CA_CERTS, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /* Initialize crypto lbrary. */ + if (!nm_utils_init (&error)) { + g_warning ("Couldn't initilize nm-utils/crypto system: %d %s", + error->code, error->message); + g_error_free (error); + } +} diff --git a/libnm-core/nm-setting-8021x.h b/libnm-core/nm-setting-8021x.h new file mode 100644 index 0000000000..2121f63b62 --- /dev/null +++ b/libnm-core/nm-setting-8021x.h @@ -0,0 +1,297 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SETTING_8021X_H +#define NM_SETTING_8021X_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +/** + * NMSetting8021xCKFormat: + * @NM_SETTING_802_1X_CK_FORMAT_UNKNOWN: unknown file format + * @NM_SETTING_802_1X_CK_FORMAT_X509: file contains an X.509 format certificate + * @NM_SETTING_802_1X_CK_FORMAT_RAW_KEY: file contains an old-style OpenSSL PEM + * or DER private key + * @NM_SETTING_802_1X_CK_FORMAT_PKCS12: file contains a PKCS#12 certificate + * and private key + * + * #NMSetting8021xCKFormat values indicate the general type of a certificate + * or private key + */ +typedef enum { /*< underscore_name=nm_setting_802_1x_ck_format >*/ + NM_SETTING_802_1X_CK_FORMAT_UNKNOWN = 0, + NM_SETTING_802_1X_CK_FORMAT_X509, + NM_SETTING_802_1X_CK_FORMAT_RAW_KEY, + NM_SETTING_802_1X_CK_FORMAT_PKCS12 +} NMSetting8021xCKFormat; + +/** + * NMSetting8021xCKScheme: + * @NM_SETTING_802_1X_CK_SCHEME_UNKNOWN: unknown certificate or private key + * scheme + * @NM_SETTING_802_1X_CK_SCHEME_BLOB: certificate or key is stored as the raw + * item data + * @NM_SETTING_802_1X_CK_SCHEME_PATH: certificate or key is stored as a path + * to a file containing the certificate or key data + * + * #NMSetting8021xCKScheme values indicate how a certificate or private key is + * stored in the setting properties, either as a blob of the item's data, or as + * a path to a certificate or private key file on the filesystem + */ +typedef enum { /*< underscore_name=nm_setting_802_1x_ck_scheme >*/ + NM_SETTING_802_1X_CK_SCHEME_UNKNOWN = 0, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + NM_SETTING_802_1X_CK_SCHEME_PATH +} NMSetting8021xCKScheme; + + +#define NM_TYPE_SETTING_802_1X (nm_setting_802_1x_get_type ()) +#define NM_SETTING_802_1X(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_802_1X, NMSetting8021x)) +#define NM_SETTING_802_1X_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_802_1X, NMSetting8021xClass)) +#define NM_IS_SETTING_802_1X(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_802_1X)) +#define NM_IS_SETTING_802_1X_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_802_1X)) +#define NM_SETTING_802_1X_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_802_1X, NMSetting8021xClass)) + +#define NM_SETTING_802_1X_SETTING_NAME "802-1x" + +/** + * NMSetting8021xError: + * @NM_SETTING_802_1X_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_802_1X_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_802_1X_ERROR_MISSING_PROPERTY: the property was missing and is + * required + */ +typedef enum { /*< underscore_name=nm_setting_802_1x_error >*/ + NM_SETTING_802_1X_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_802_1X_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/ +} NMSetting8021xError; + +#define NM_SETTING_802_1X_ERROR nm_setting_802_1x_error_quark () +GQuark nm_setting_802_1x_error_quark (void); + + +#define NM_SETTING_802_1X_EAP "eap" +#define NM_SETTING_802_1X_IDENTITY "identity" +#define NM_SETTING_802_1X_ANONYMOUS_IDENTITY "anonymous-identity" +#define NM_SETTING_802_1X_PAC_FILE "pac-file" +#define NM_SETTING_802_1X_CA_CERT "ca-cert" +#define NM_SETTING_802_1X_CA_PATH "ca-path" +#define NM_SETTING_802_1X_SUBJECT_MATCH "subject-match" +#define NM_SETTING_802_1X_ALTSUBJECT_MATCHES "altsubject-matches" +#define NM_SETTING_802_1X_CLIENT_CERT "client-cert" +#define NM_SETTING_802_1X_PHASE1_PEAPVER "phase1-peapver" +#define NM_SETTING_802_1X_PHASE1_PEAPLABEL "phase1-peaplabel" +#define NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING "phase1-fast-provisioning" +#define NM_SETTING_802_1X_PHASE2_AUTH "phase2-auth" +#define NM_SETTING_802_1X_PHASE2_AUTHEAP "phase2-autheap" +#define NM_SETTING_802_1X_PHASE2_CA_CERT "phase2-ca-cert" +#define NM_SETTING_802_1X_PHASE2_CA_PATH "phase2-ca-path" +#define NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH "phase2-subject-match" +#define NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES "phase2-altsubject-matches" +#define NM_SETTING_802_1X_PHASE2_CLIENT_CERT "phase2-client-cert" +#define NM_SETTING_802_1X_PASSWORD "password" +#define NM_SETTING_802_1X_PASSWORD_FLAGS "password-flags" +#define NM_SETTING_802_1X_PASSWORD_RAW "password-raw" +#define NM_SETTING_802_1X_PASSWORD_RAW_FLAGS "password-raw-flags" +#define NM_SETTING_802_1X_PRIVATE_KEY "private-key" +#define NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD "private-key-password" +#define NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS "private-key-password-flags" +#define NM_SETTING_802_1X_PHASE2_PRIVATE_KEY "phase2-private-key" +#define NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD "phase2-private-key-password" +#define NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS "phase2-private-key-password-flags" +#define NM_SETTING_802_1X_PIN "pin" +#define NM_SETTING_802_1X_PIN_FLAGS "pin-flags" +#define NM_SETTING_802_1X_SYSTEM_CA_CERTS "system-ca-certs" + +/* PRIVATE KEY NOTE: when setting PKCS#12 private keys directly via properties + * using the "blob" scheme, the data must be passed in PKCS#12 binary format. + * In this case, the appropriate "client-cert" (or "phase2-client-cert") + * property of the NMSetting8021x object must also contain the exact same + * PKCS#12 binary data that the private key does. This is because the + * PKCS#12 file contains both the private key and client certificate, so both + * properties need to be set to the same thing. When using the "path" scheme, + * just set both the private-key and client-cert properties to the same path. + * + * When setting OpenSSL-derived "traditional" format (ie S/MIME style, not + * PKCS#8) RSA and DSA keys directly via properties with the "blob" scheme, they + * should be passed to NetworkManager in PEM format with the "DEK-Info" and + * "Proc-Type" tags intact. Decrypted private keys should not be used as this + * is insecure and could allow unprivileged users to access the decrypted + * private key data. + * + * When using the "path" scheme, just set the private-key and client-cert + * properties to the paths to their respective objects. + */ + +typedef struct { + NMSetting parent; +} NMSetting8021x; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSetting8021xClass; + +GType nm_setting_802_1x_get_type (void); + +NMSetting *nm_setting_802_1x_new (void); + +guint32 nm_setting_802_1x_get_num_eap_methods (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_eap_method (NMSetting8021x *setting, guint32 i); +gboolean nm_setting_802_1x_add_eap_method (NMSetting8021x *setting, const char *eap); +void nm_setting_802_1x_remove_eap_method (NMSetting8021x *setting, guint32 i); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_802_1x_remove_eap_method_by_value (NMSetting8021x *setting, const char *eap); +void nm_setting_802_1x_clear_eap_methods (NMSetting8021x *setting); + +const char * nm_setting_802_1x_get_identity (NMSetting8021x *setting); + +const char * nm_setting_802_1x_get_anonymous_identity (NMSetting8021x *setting); + +const char * nm_setting_802_1x_get_pac_file (NMSetting8021x *setting); + +gboolean nm_setting_802_1x_get_system_ca_certs (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_ca_path (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_phase2_ca_path (NMSetting8021x *setting); + +NMSetting8021xCKScheme nm_setting_802_1x_get_ca_cert_scheme (NMSetting8021x *setting); +const GByteArray * nm_setting_802_1x_get_ca_cert_blob (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_ca_cert_path (NMSetting8021x *setting); +gboolean nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting, + const char *cert_path, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error); + +const char * nm_setting_802_1x_get_subject_match (NMSetting8021x *setting); + +guint32 nm_setting_802_1x_get_num_altsubject_matches (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_altsubject_match (NMSetting8021x *setting, + guint32 i); +gboolean nm_setting_802_1x_add_altsubject_match (NMSetting8021x *setting, + const char *altsubject_match); +void nm_setting_802_1x_remove_altsubject_match (NMSetting8021x *setting, + guint32 i); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_802_1x_remove_altsubject_match_by_value (NMSetting8021x *setting, + const char *altsubject_match); +void nm_setting_802_1x_clear_altsubject_matches (NMSetting8021x *setting); + +NMSetting8021xCKScheme nm_setting_802_1x_get_client_cert_scheme (NMSetting8021x *setting); +const GByteArray * nm_setting_802_1x_get_client_cert_blob (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_client_cert_path (NMSetting8021x *setting); +gboolean nm_setting_802_1x_set_client_cert (NMSetting8021x *setting, + const char *cert_path, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error); + +const char * nm_setting_802_1x_get_phase1_peapver (NMSetting8021x *setting); + +const char * nm_setting_802_1x_get_phase1_peaplabel (NMSetting8021x *setting); + +const char * nm_setting_802_1x_get_phase1_fast_provisioning (NMSetting8021x *setting); + +const char * nm_setting_802_1x_get_phase2_auth (NMSetting8021x *setting); + +const char * nm_setting_802_1x_get_phase2_autheap (NMSetting8021x *setting); + +NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_ca_cert_scheme (NMSetting8021x *setting); +const GByteArray * nm_setting_802_1x_get_phase2_ca_cert_blob (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_phase2_ca_cert_path (NMSetting8021x *setting); +gboolean nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *setting, + const char *cert_path, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error); + +const char * nm_setting_802_1x_get_phase2_subject_match (NMSetting8021x *setting); + +guint32 nm_setting_802_1x_get_num_phase2_altsubject_matches (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_phase2_altsubject_match (NMSetting8021x *setting, + guint32 i); +gboolean nm_setting_802_1x_add_phase2_altsubject_match (NMSetting8021x *setting, + const char *phase2_altsubject_match); +void nm_setting_802_1x_remove_phase2_altsubject_match (NMSetting8021x *setting, + guint32 i); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_802_1x_remove_phase2_altsubject_match_by_value (NMSetting8021x *setting, + const char *phase2_altsubject_match); +void nm_setting_802_1x_clear_phase2_altsubject_matches (NMSetting8021x *setting); + +NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_client_cert_scheme (NMSetting8021x *setting); +const GByteArray * nm_setting_802_1x_get_phase2_client_cert_blob (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_phase2_client_cert_path (NMSetting8021x *setting); +gboolean nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *setting, + const char *cert_path, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error); + +const char * nm_setting_802_1x_get_password (NMSetting8021x *setting); +NMSettingSecretFlags nm_setting_802_1x_get_password_flags (NMSetting8021x *setting); +const GByteArray * nm_setting_802_1x_get_password_raw (NMSetting8021x *setting); +NMSettingSecretFlags nm_setting_802_1x_get_password_raw_flags (NMSetting8021x *setting); + +const char * nm_setting_802_1x_get_pin (NMSetting8021x *setting); +NMSettingSecretFlags nm_setting_802_1x_get_pin_flags (NMSetting8021x *setting); + +NMSetting8021xCKScheme nm_setting_802_1x_get_private_key_scheme (NMSetting8021x *setting); +const GByteArray * nm_setting_802_1x_get_private_key_blob (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_private_key_path (NMSetting8021x *setting); +gboolean nm_setting_802_1x_set_private_key (NMSetting8021x *setting, + const char *key_path, + const char *password, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error); +const char * nm_setting_802_1x_get_private_key_password (NMSetting8021x *setting); +NMSettingSecretFlags nm_setting_802_1x_get_private_key_password_flags (NMSetting8021x *setting); + +NMSetting8021xCKFormat nm_setting_802_1x_get_private_key_format (NMSetting8021x *setting); + +NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_private_key_scheme (NMSetting8021x *setting); +const GByteArray * nm_setting_802_1x_get_phase2_private_key_blob (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_phase2_private_key_path (NMSetting8021x *setting); +gboolean nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, + const char *key_path, + const char *password, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error); +const char * nm_setting_802_1x_get_phase2_private_key_password (NMSetting8021x *setting); +NMSettingSecretFlags nm_setting_802_1x_get_phase2_private_key_password_flags (NMSetting8021x *setting); + +NMSetting8021xCKFormat nm_setting_802_1x_get_phase2_private_key_format (NMSetting8021x *setting); + + +G_END_DECLS + +#endif /* NM_SETTING_8021X_H */ diff --git a/libnm-core/nm-setting-adsl.c b/libnm-core/nm-setting-adsl.c new file mode 100644 index 0000000000..0a8eba7c0d --- /dev/null +++ b/libnm-core/nm-setting-adsl.c @@ -0,0 +1,465 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2013 Red Hat, Inc. + */ + +#include <string.h> +#include <glib/gi18n.h> + +#include "nm-setting-adsl.h" +#include "nm-setting-ppp.h" +#include "nm-setting-private.h" +#include "nm-utils.h" + +/** + * SECTION:nm-setting-adsl + * @short_description: Describes ADSL-based properties + * @include: nm-setting-adsl.h + * + * The #NMSettingAdsl object is a #NMSetting subclass that describes + * properties of ADSL connections. + */ + +/** + * nm_setting_adsl_error_quark: + * + * Registers an error quark for #NMSettingAdsl if necessary. + * + * Returns: the error quark used for #NMSettingAdsl errors. + **/ +GQuark +nm_setting_adsl_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-adsl-error-quark"); + return quark; +} + +G_DEFINE_TYPE_WITH_CODE (NMSettingAdsl, nm_setting_adsl, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_ADSL_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_ADSL_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_ADSL) + +#define NM_SETTING_ADSL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_ADSL, NMSettingAdslPrivate)) + +typedef struct { + char * username; + char * password; + NMSettingSecretFlags password_flags; + char * protocol; + char * encapsulation; + guint32 vpi; + guint32 vci; +} NMSettingAdslPrivate; + +enum { + PROP_0, + PROP_USERNAME, + PROP_PASSWORD, + PROP_PASSWORD_FLAGS, + PROP_PROTOCOL, + PROP_ENCAPSULATION, + PROP_VPI, + PROP_VCI, + + LAST_PROP +}; + +/** + * nm_setting_adsl_new: + * + * Creates a new #NMSettingAdsl object with default values. + * + * Returns: the new empty #NMSettingAdsl object + **/ +NMSetting * +nm_setting_adsl_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_ADSL, NULL); +} + +/** + * nm_setting_adsl_get_username: + * @setting: the #NMSettingAdsl + * + * Returns: the #NMSettingAdsl:username property of the setting + **/ +const char * +nm_setting_adsl_get_username (NMSettingAdsl *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_ADSL (setting), NULL); + + return NM_SETTING_ADSL_GET_PRIVATE (setting)->username; +} + +/** + * nm_setting_adsl_get_password: + * @setting: the #NMSettingAdsl + * + * Returns: the #NMSettingAdsl:password property of the setting + **/ +const char * +nm_setting_adsl_get_password (NMSettingAdsl *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_ADSL (setting), NULL); + + return NM_SETTING_ADSL_GET_PRIVATE (setting)->password; +} + +/** + * nm_setting_adsl_get_password_flags: + * @setting: the #NMSettingAdsl + * + * Returns: the #NMSettingSecretFlags pertaining to the #NMSettingAdsl:password + **/ +NMSettingSecretFlags +nm_setting_adsl_get_password_flags (NMSettingAdsl *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_ADSL (setting), NM_SETTING_SECRET_FLAG_NONE); + + return NM_SETTING_ADSL_GET_PRIVATE (setting)->password_flags; +} + +/** + * nm_setting_adsl_get_protocol: + * @setting: the #NMSettingAdsl + * + * Returns: the #NMSettingAdsl:protocol property of the setting + **/ +const char * +nm_setting_adsl_get_protocol (NMSettingAdsl *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_ADSL (setting), NULL); + + return NM_SETTING_ADSL_GET_PRIVATE (setting)->protocol; +} + +/** + * nm_setting_adsl_get_encapsulation: + * @setting: the #NMSettingAdsl + * + * Returns: the #NMSettingAdsl:encapsulation property of the setting + **/ +const char * +nm_setting_adsl_get_encapsulation (NMSettingAdsl *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_ADSL (setting), NULL); + + return NM_SETTING_ADSL_GET_PRIVATE (setting)->encapsulation; +} + +/** + * nm_setting_adsl_get_vpi: + * @setting: the #NMSettingAdsl + * + * Returns: the #NMSettingAdsl:vpi property of the setting + **/ +guint32 +nm_setting_adsl_get_vpi (NMSettingAdsl *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_ADSL (setting), 0); + + return NM_SETTING_ADSL_GET_PRIVATE (setting)->vpi; +} + +/** + * nm_setting_adsl_get_vci: + * @setting: the #NMSettingAdsl + * + * Returns: the #NMSettingAdsl:vci property of the setting + **/ +guint32 +nm_setting_adsl_get_vci (NMSettingAdsl *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_ADSL (setting), 0); + + return NM_SETTING_ADSL_GET_PRIVATE (setting)->vci; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingAdslPrivate *priv = NM_SETTING_ADSL_GET_PRIVATE (setting); + + if (!priv->username) { + g_set_error_literal (error, + NM_SETTING_ADSL_ERROR, + NM_SETTING_ADSL_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_ADSL_SETTING_NAME, NM_SETTING_ADSL_USERNAME); + return FALSE; + } else if (!strlen (priv->username)) { + g_set_error_literal (error, + NM_SETTING_ADSL_ERROR, + NM_SETTING_ADSL_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_ADSL_SETTING_NAME, NM_SETTING_ADSL_USERNAME); + return FALSE; + } + + if (priv->password && !strlen (priv->password)) { + g_set_error_literal (error, + NM_SETTING_ADSL_ERROR, + NM_SETTING_ADSL_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_ADSL_SETTING_NAME, NM_SETTING_ADSL_PASSWORD); + return FALSE; + } + + if (strcmp (priv->protocol, NM_SETTING_ADSL_PROTOCOL_PPPOA) && + strcmp (priv->protocol, NM_SETTING_ADSL_PROTOCOL_PPPOE) && + strcmp (priv->protocol, NM_SETTING_ADSL_PROTOCOL_IPOATM)) { + g_set_error (error, + NM_SETTING_ADSL_ERROR, + NM_SETTING_ADSL_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->protocol); + g_prefix_error (error, "%s.%s: ", NM_SETTING_ADSL_SETTING_NAME, NM_SETTING_ADSL_PROTOCOL); + return FALSE; + } + + if (strcmp (priv->encapsulation, NM_SETTING_ADSL_ENCAPSULATION_VCMUX) && + strcmp (priv->encapsulation, NM_SETTING_ADSL_ENCAPSULATION_LLC) ) { + g_set_error (error, + NM_SETTING_ADSL_ERROR, + NM_SETTING_ADSL_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->encapsulation); + g_prefix_error (error, "%s.%s: ", NM_SETTING_ADSL_SETTING_NAME, NM_SETTING_ADSL_ENCAPSULATION); + return FALSE; + } + + return TRUE; +} + +static GPtrArray * +need_secrets (NMSetting *setting) +{ + NMSettingAdslPrivate *priv = NM_SETTING_ADSL_GET_PRIVATE (setting); + GPtrArray *secrets = NULL; + + if (priv->password) + return NULL; + + if (!(priv->password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { + secrets = g_ptr_array_sized_new (1); + g_ptr_array_add (secrets, NM_SETTING_ADSL_PASSWORD); + } + + return secrets; +} + +static void +nm_setting_adsl_init (NMSettingAdsl *setting) +{ +} + +static void +finalize (GObject *object) +{ + NMSettingAdslPrivate *priv = NM_SETTING_ADSL_GET_PRIVATE (object); + + g_free (priv->username); + g_free (priv->password); + g_free (priv->protocol); + g_free (priv->encapsulation); + + G_OBJECT_CLASS (nm_setting_adsl_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingAdslPrivate *priv = NM_SETTING_ADSL_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_USERNAME: + g_free (priv->username); + priv->username = g_value_dup_string (value); + break; + case PROP_PASSWORD: + g_free (priv->password); + priv->password = g_value_dup_string (value); + break; + case PROP_PASSWORD_FLAGS: + priv->password_flags = g_value_get_uint (value); + break; + case PROP_PROTOCOL: + g_free (priv->protocol); + priv->protocol = g_ascii_strdown (g_value_get_string (value), -1); + break; + case PROP_ENCAPSULATION: + g_free (priv->encapsulation); + priv->encapsulation = g_ascii_strdown (g_value_get_string (value), -1); + break; + case PROP_VPI: + priv->vpi = g_value_get_uint (value); + break; + case PROP_VCI: + priv->vci = g_value_get_uint (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) +{ + NMSettingAdsl *setting = NM_SETTING_ADSL (object); + + switch (prop_id) { + case PROP_USERNAME: + g_value_set_string (value, nm_setting_adsl_get_username (setting)); + break; + case PROP_PASSWORD: + g_value_set_string (value, nm_setting_adsl_get_password (setting)); + break; + case PROP_PASSWORD_FLAGS: + g_value_set_uint (value, nm_setting_adsl_get_password_flags (setting)); + break; + case PROP_PROTOCOL: + g_value_set_string (value, nm_setting_adsl_get_protocol (setting)); + break; + case PROP_ENCAPSULATION: + g_value_set_string (value, nm_setting_adsl_get_encapsulation (setting)); + break; + case PROP_VPI: + g_value_set_uint (value, nm_setting_adsl_get_vpi (setting)); + break; + case PROP_VCI: + g_value_set_uint (value, nm_setting_adsl_get_vci (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_adsl_class_init (NMSettingAdslClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingAdslPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + parent_class->need_secrets = need_secrets; + + /* Properties */ + + /** + * NMSettingAdsl:username: + * + * Username used to authenticate with the ADSL service. + **/ + g_object_class_install_property + (object_class, PROP_USERNAME, + g_param_spec_string (NM_SETTING_ADSL_USERNAME, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingAdsl:password: + * + * Password used to authenticate with the ADSL service. + **/ + g_object_class_install_property + (object_class, PROP_PASSWORD, + g_param_spec_string (NM_SETTING_ADSL_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingAdsl:password-flags: + * + * Flags indicating how to handle the #NMSettingAdsl:password property. + **/ + g_object_class_install_property + (object_class, PROP_PASSWORD_FLAGS, + g_param_spec_uint (NM_SETTING_ADSL_PASSWORD_FLAGS, "", "", + NM_SETTING_SECRET_FLAG_NONE, + NM_SETTING_SECRET_FLAGS_ALL, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingAdsl:protocol: + * + * ADSL connection protocol. Can be "pppoa", "pppoe" or "ipoatm". + **/ + g_object_class_install_property + (object_class, PROP_PROTOCOL, + g_param_spec_string (NM_SETTING_ADSL_PROTOCOL, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingAdsl:encapsulation: + * + * Encapsulation of ADSL connection. Can be "vcmux" or "llc". + **/ + g_object_class_install_property + (object_class, PROP_ENCAPSULATION, + g_param_spec_string (NM_SETTING_ADSL_ENCAPSULATION, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingAdsl:vpi: + * + * VPI of ADSL connection + **/ + g_object_class_install_property + (object_class, PROP_VPI, + g_param_spec_uint (NM_SETTING_ADSL_VPI, "", "", + 0, 65536, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingAdsl:vci: + * + * VCI of ADSL connection + **/ + g_object_class_install_property + (object_class, PROP_VCI, + g_param_spec_uint (NM_SETTING_ADSL_VCI, "", "", + 0, 65536, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-adsl.h b/libnm-core/nm-setting-adsl.h new file mode 100644 index 0000000000..82af6ebdd0 --- /dev/null +++ b/libnm-core/nm-setting-adsl.h @@ -0,0 +1,96 @@ +/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Red Hat, Inc. + */ + +#ifndef NM_SETTING_ADSL_H +#define NM_SETTING_ADSL_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_ADSL (nm_setting_adsl_get_type ()) +#define NM_SETTING_ADSL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_ADSL, NMSettingAdsl)) +#define NM_SETTING_ADSL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_ADSL, NMSettingAdslClass)) +#define NM_IS_SETTING_ADSL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_ADSL)) +#define NM_IS_SETTING_ADSL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_ADSL)) +#define NM_SETTING_ADSL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_ADSL, NMSettingAdslClass)) + +#define NM_SETTING_ADSL_SETTING_NAME "adsl" + +/** + * NMSettingAdslError: + * @NM_SETTING_ADSL_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_ADSL_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_ADSL_ERROR_MISSING_PROPERTY: the property was missing and is + * required + */ +typedef enum { + NM_SETTING_ADSL_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_ADSL_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_ADSL_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/ +} NMSettingAdslError; + +#define NM_SETTING_ADSL_ERROR nm_setting_adsl_error_quark () +GQuark nm_setting_adsl_error_quark (void); + +#define NM_SETTING_ADSL_USERNAME "username" +#define NM_SETTING_ADSL_PASSWORD "password" +#define NM_SETTING_ADSL_PASSWORD_FLAGS "password-flags" +#define NM_SETTING_ADSL_PROTOCOL "protocol" +#define NM_SETTING_ADSL_ENCAPSULATION "encapsulation" +#define NM_SETTING_ADSL_VPI "vpi" +#define NM_SETTING_ADSL_VCI "vci" + +#define NM_SETTING_ADSL_PROTOCOL_PPPOA "pppoa" +#define NM_SETTING_ADSL_PROTOCOL_PPPOE "pppoe" +#define NM_SETTING_ADSL_PROTOCOL_IPOATM "ipoatm" + +#define NM_SETTING_ADSL_ENCAPSULATION_VCMUX "vcmux" +#define NM_SETTING_ADSL_ENCAPSULATION_LLC "llc" + +typedef struct { + NMSetting parent; +} NMSettingAdsl; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingAdslClass; + +GType nm_setting_adsl_get_type (void); + +NMSetting *nm_setting_adsl_new (void); +const char *nm_setting_adsl_get_username (NMSettingAdsl *setting); +const char *nm_setting_adsl_get_password (NMSettingAdsl *setting); +const char *nm_setting_adsl_get_protocol (NMSettingAdsl *setting); +const char *nm_setting_adsl_get_encapsulation (NMSettingAdsl *setting); +guint32 nm_setting_adsl_get_vpi (NMSettingAdsl *setting); +guint32 nm_setting_adsl_get_vci (NMSettingAdsl *setting); +NMSettingSecretFlags nm_setting_adsl_get_password_flags (NMSettingAdsl *setting); + +G_END_DECLS + +#endif /* NM_SETTING_ADSL_H */ diff --git a/libnm-core/nm-setting-bluetooth.c b/libnm-core/nm-setting-bluetooth.c new file mode 100644 index 0000000000..506ecbddee --- /dev/null +++ b/libnm-core/nm-setting-bluetooth.c @@ -0,0 +1,297 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2013 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include <string.h> +#include <net/ethernet.h> +#include <glib/gi18n.h> + +#include "nm-param-spec-specialized.h" +#include "nm-dbus-glib-types.h" +#include "nm-setting-bluetooth.h" +#include "nm-setting-cdma.h" +#include "nm-setting-gsm.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-bluetooth + * @short_description: Describes Bluetooth connection properties + * @include: nm-setting-bluetooth.h + * + * The #NMSettingBluetooth object is a #NMSetting subclass that describes + * properties necessary for connection to devices that provide network + * connections via the Bluetooth Dial-Up Networking (DUN) and Network Access + * Point (NAP) profiles. + **/ + +/** + * nm_setting_bluetooth_error_quark: + * + * Registers an error quark for #NMSettingBluetooth if necessary. + * + * Returns: the error quark used for #NMSettingBluetooth errors. + **/ +GQuark +nm_setting_bluetooth_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-bluetooth-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingBluetooth, nm_setting_bluetooth, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_BLUETOOTH_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_BLUETOOTH_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BLUETOOTH) + +#define NM_SETTING_BLUETOOTH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetoothPrivate)) + +typedef struct { + GByteArray *bdaddr; + char *type; +} NMSettingBluetoothPrivate; + +enum { + PROP_0, + PROP_BDADDR, + PROP_TYPE, + + LAST_PROP +}; + +/** + * nm_setting_bluetooth_new: + * + * Creates a new #NMSettingBluetooth object with default values. + * + * Returns: (transfer full): the new empty #NMSettingBluetooth object + **/ +NMSetting *nm_setting_bluetooth_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_BLUETOOTH, NULL); +} + +/** + * nm_setting_bluetooth_get_connection_type: + * @setting: the #NMSettingBluetooth + * + * Returns the connection method for communicating with the remote device (i.e. + * either DUN to a DUN-capable device or PANU to a NAP-capable device). + * + * Returns: the type, either %NM_SETTING_BLUETOOTH_PANU or %NM_SETTING_BLUETOOTH_DUN + **/ +const char * +nm_setting_bluetooth_get_connection_type (NMSettingBluetooth *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BLUETOOTH (setting), NULL); + + return NM_SETTING_BLUETOOTH_GET_PRIVATE (setting)->type; +} + +/** + * nm_setting_bluetooth_get_bdaddr: + * @setting: the #NMSettingBluetooth + * + * Gets the Bluetooth address of the remote device which this setting + * describes a connection to. + * + * Returns: the Bluetooth address + **/ +const GByteArray * +nm_setting_bluetooth_get_bdaddr (NMSettingBluetooth *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BLUETOOTH (setting), NULL); + + return NM_SETTING_BLUETOOTH_GET_PRIVATE (setting)->bdaddr; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingBluetoothPrivate *priv = NM_SETTING_BLUETOOTH_GET_PRIVATE (setting); + + if (!priv->bdaddr) { + g_set_error_literal (error, + NM_SETTING_BLUETOOTH_ERROR, + NM_SETTING_BLUETOOTH_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_BDADDR); + return FALSE; + } + + if (priv->bdaddr && priv->bdaddr->len != ETH_ALEN) { + g_set_error_literal (error, + NM_SETTING_BLUETOOTH_ERROR, + NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_BDADDR); + return FALSE; + } + + if (!priv->type) { + g_set_error_literal (error, + NM_SETTING_BLUETOOTH_ERROR, + NM_SETTING_BLUETOOTH_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_TYPE); + return FALSE; + } else if (!g_str_equal (priv->type, NM_SETTING_BLUETOOTH_TYPE_DUN) && + !g_str_equal (priv->type, NM_SETTING_BLUETOOTH_TYPE_PANU)) { + g_set_error (error, + NM_SETTING_BLUETOOTH_ERROR, + NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->type); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_TYPE); + return FALSE; + } + + /* Make sure the corresponding 'type' setting is present */ + if ( all_settings + && !strcmp (priv->type, NM_SETTING_BLUETOOTH_TYPE_DUN)) { + gboolean gsm = FALSE, cdma = FALSE; + + gsm = !!nm_setting_find_in_list (all_settings, NM_SETTING_GSM_SETTING_NAME); + cdma = !!nm_setting_find_in_list (all_settings, NM_SETTING_CDMA_SETTING_NAME); + + if (!gsm && !cdma) { + g_set_error (error, + NM_SETTING_BLUETOOTH_ERROR, + NM_SETTING_BLUETOOTH_ERROR_TYPE_SETTING_NOT_FOUND, + _("requires '%s' or '%s' setting"), + NM_SETTING_GSM_SETTING_NAME, NM_SETTING_CDMA_SETTING_NAME); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_TYPE); + return FALSE; + } + } + /* PANU doesn't need a 'type' setting since no further configuration + * is required at the interface level. + */ + + return TRUE; +} + +static void +nm_setting_bluetooth_init (NMSettingBluetooth *setting) +{ +} + +static void +finalize (GObject *object) +{ + NMSettingBluetoothPrivate *priv = NM_SETTING_BLUETOOTH_GET_PRIVATE (object); + + if (priv->bdaddr) + g_byte_array_free (priv->bdaddr, TRUE); + g_free (priv->type); + + G_OBJECT_CLASS (nm_setting_bluetooth_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingBluetoothPrivate *priv = NM_SETTING_BLUETOOTH_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_BDADDR: + if (priv->bdaddr) + g_byte_array_free (priv->bdaddr, TRUE); + priv->bdaddr = g_value_dup_boxed (value); + break; + case PROP_TYPE: + g_free (priv->type); + priv->type = g_value_dup_string (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) +{ + NMSettingBluetooth *setting = NM_SETTING_BLUETOOTH (object); + + switch (prop_id) { + case PROP_BDADDR: + g_value_set_boxed (value, nm_setting_bluetooth_get_bdaddr (setting)); + break; + case PROP_TYPE: + g_value_set_string (value, nm_setting_bluetooth_get_connection_type (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_bluetooth_class_init (NMSettingBluetoothClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingBluetoothPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + + /* Properties */ + + /** + * NMSettingBluetooth:bdaddr: + * + * The Bluetooth address of the device. + **/ + g_object_class_install_property + (object_class, PROP_BDADDR, + _nm_param_spec_specialized (NM_SETTING_BLUETOOTH_BDADDR, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingBluetooth:type: + * + * Either "dun" for Dial-Up Networking connections or "panu" for Personal + * Area Networking connections to devices supporting the NAP profile. + **/ + g_object_class_install_property + (object_class, PROP_TYPE, + g_param_spec_string (NM_SETTING_BLUETOOTH_TYPE, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-bluetooth.h b/libnm-core/nm-setting-bluetooth.h new file mode 100644 index 0000000000..caf12158c0 --- /dev/null +++ b/libnm-core/nm-setting-bluetooth.h @@ -0,0 +1,100 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2009 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SETTING_BLUETOOTH_H +#define NM_SETTING_BLUETOOTH_H + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_BLUETOOTH (nm_setting_bluetooth_get_type ()) +#define NM_SETTING_BLUETOOTH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetooth)) +#define NM_SETTING_BLUETOOTH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetoothClass)) +#define NM_IS_SETTING_BLUETOOTH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_BLUETOOTH)) +#define NM_IS_SETTING_BLUETOOTH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_BLUETOOTH)) +#define NM_SETTING_BLUETOOTH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetoothClass)) + +#define NM_SETTING_BLUETOOTH_SETTING_NAME "bluetooth" + +/** + * NMSettingBluetoothError: + * @NM_SETTING_BLUETOOTH_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_BLUETOOTH_ERROR_MISSING_PROPERTY: the property was missing and is + * required + * @NM_SETTING_BLUETOOTH_ERROR_TYPE_SETTING_NOT_FOUND: the connection + * did not contain a required type setting, ie for DUN connections the connection + * must also contain an #NMSettingGsm or #NMSettingCdma as appropriate + */ +typedef enum { + NM_SETTING_BLUETOOTH_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_BLUETOOTH_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ + NM_SETTING_BLUETOOTH_ERROR_TYPE_SETTING_NOT_FOUND, /*< nick=TypeSettingNotFound >*/ +} NMSettingBluetoothError; + +#define NM_SETTING_BLUETOOTH_ERROR nm_setting_bluetooth_error_quark () +GQuark nm_setting_bluetooth_error_quark (void); + +#define NM_SETTING_BLUETOOTH_BDADDR "bdaddr" +#define NM_SETTING_BLUETOOTH_TYPE "type" + +/** + * NM_SETTING_BLUETOOTH_TYPE_DUN: + * + * Connection type describing a connection to devices that support the Bluetooth + * DUN profile. + */ +#define NM_SETTING_BLUETOOTH_TYPE_DUN "dun" + +/** + * NM_SETTING_BLUETOOTH_TYPE_PANU: + * + * Connection type describing a connection to devices that support the Bluetooth + * NAP (Network Access Point) protocol, which accepts connections via PANU. + */ +#define NM_SETTING_BLUETOOTH_TYPE_PANU "panu" + +typedef struct { + NMSetting parent; +} NMSettingBluetooth; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingBluetoothClass; + +GType nm_setting_bluetooth_get_type (void); + +NMSetting * nm_setting_bluetooth_new (void); +const GByteArray *nm_setting_bluetooth_get_bdaddr (NMSettingBluetooth *setting); +const char * nm_setting_bluetooth_get_connection_type (NMSettingBluetooth *setting); + +G_END_DECLS + +#endif /* NM_SETTING_BLUETOOTH_H */ diff --git a/libnm-core/nm-setting-bond.c b/libnm-core/nm-setting-bond.c new file mode 100644 index 0000000000..10766f564d --- /dev/null +++ b/libnm-core/nm-setting-bond.c @@ -0,0 +1,804 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2013 Red Hat, Inc. + */ + +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "nm-setting-bond.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-utils-private.h" +#include "nm-dbus-glib-types.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-bond + * @short_description: Describes connection properties for bonds + * @include: nm-setting-bond.h + * + * The #NMSettingBond object is a #NMSetting subclass that describes properties + * necessary for bond connections. + **/ + +/** + * nm_setting_bond_error_quark: + * + * Registers an error quark for #NMSettingBond if necessary. + * + * Returns: the error quark used for #NMSettingBond errors. + **/ +GQuark +nm_setting_bond_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-bond-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingBond, nm_setting_bond, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_BOND_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_BOND_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BOND) + +#define NM_SETTING_BOND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BOND, NMSettingBondPrivate)) + +typedef struct { + char *interface_name; + GHashTable *options; +} NMSettingBondPrivate; + +enum { + PROP_0, + PROP_INTERFACE_NAME, + PROP_OPTIONS, + LAST_PROP +}; + +enum { + TYPE_INT, + TYPE_STR, + TYPE_BOTH, + TYPE_IP, + TYPE_IFNAME, +}; + +typedef struct { + const char *opt; + const char *val; + guint opt_type; + guint min; + guint max; + char *list[10]; +} BondDefault; + +static const BondDefault defaults[] = { + { NM_SETTING_BOND_OPTION_MODE, "balance-rr", TYPE_BOTH, 0, 6, + { "balance-rr", "active-backup", "balance-xor", "broadcast", "802.3ad", "balance-tlb", "balance-alb", NULL } }, + { NM_SETTING_BOND_OPTION_MIIMON, "100", TYPE_INT, 0, G_MAXINT }, + { NM_SETTING_BOND_OPTION_DOWNDELAY, "0", TYPE_INT, 0, G_MAXINT }, + { NM_SETTING_BOND_OPTION_UPDELAY, "0", TYPE_INT, 0, G_MAXINT }, + { NM_SETTING_BOND_OPTION_ARP_INTERVAL, "0", TYPE_INT, 0, G_MAXINT }, + { NM_SETTING_BOND_OPTION_ARP_IP_TARGET, "", TYPE_IP }, + { NM_SETTING_BOND_OPTION_ARP_VALIDATE, "0", TYPE_BOTH, 0, 3, + { "none", "active", "backup", "all", NULL } }, + { NM_SETTING_BOND_OPTION_PRIMARY, "", TYPE_IFNAME }, + { NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, "0", TYPE_BOTH, 0, 2, + { "always", "better", "failure", NULL } }, + { NM_SETTING_BOND_OPTION_FAIL_OVER_MAC, "0", TYPE_BOTH, 0, 2, + { "none", "active", "follow", NULL } }, + { NM_SETTING_BOND_OPTION_USE_CARRIER, "1", TYPE_INT, 0, 1 }, + { NM_SETTING_BOND_OPTION_AD_SELECT, "0", TYPE_BOTH, 0, 2, + { "stable", "bandwidth", "count", NULL } }, + { NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, "0", TYPE_BOTH, 0, 2, + { "layer2", "layer3+4", "layer2+3", NULL } }, + { NM_SETTING_BOND_OPTION_RESEND_IGMP, "1", TYPE_INT, 0, 255 }, +}; + +/** + * nm_setting_bond_new: + * + * Creates a new #NMSettingBond object with default values. + * + * Returns: (transfer full): the new empty #NMSettingBond object + **/ +NMSetting * +nm_setting_bond_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_BOND, NULL); +} + +/** + * nm_setting_bond_get_interface_name: + * @setting: the #NMSettingBond + * + * Returns: the #NMSettingBond:interface-name property of the setting + **/ +const char * +nm_setting_bond_get_interface_name (NMSettingBond *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); + + return NM_SETTING_BOND_GET_PRIVATE (setting)->interface_name; +} + +/** + * nm_setting_bond_get_num_options: + * @setting: the #NMSettingBond + * + * Returns the number of options that should be set for this bond when it + * is activated. This can be used to retrieve each option individually + * using nm_setting_bond_get_option(). + * + * Returns: the number of bonding options + **/ +guint32 +nm_setting_bond_get_num_options (NMSettingBond *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), 0); + + return g_hash_table_size (NM_SETTING_BOND_GET_PRIVATE (setting)->options); +} + +/** + * nm_setting_bond_get_option: + * @setting: the #NMSettingBond + * @idx: index of the desired option, from 0 to + * nm_setting_bond_get_num_options() - 1 + * @out_name: (out): on return, the name of the bonding option; this + * value is owned by the setting and should not be modified + * @out_value: (out): on return, the value of the name of the bonding + * option; this value is owned by the setting and should not be modified + * + * Given an index, return the value of the bonding option at that index. Indexes + * are *not* guaranteed to be static across modifications to options done by + * nm_setting_bond_add_option() and nm_setting_bond_remove_option(), + * and should not be used to refer to options except for short periods of time + * such as during option iteration. + * + * Returns: %TRUE on success if the index was valid and an option was found, + * %FALSE if the index was invalid (ie, greater than the number of options + * currently held by the setting) + **/ +gboolean +nm_setting_bond_get_option (NMSettingBond *setting, + guint32 idx, + const char **out_name, + const char **out_value) +{ + NMSettingBondPrivate *priv; + GList *keys; + const char *_key = NULL, *_value = NULL; + + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE); + + priv = NM_SETTING_BOND_GET_PRIVATE (setting); + + if (idx >= nm_setting_bond_get_num_options (setting)) + return FALSE; + + keys = g_hash_table_get_keys (priv->options); + _key = g_list_nth_data (keys, idx); + _value = g_hash_table_lookup (priv->options, _key); + + if (out_name) + *out_name = _key; + if (out_value) + *out_value = _value; + + g_list_free (keys); + return TRUE; +} + +static gboolean +validate_int (const char *name, const char *value, const BondDefault *def) +{ + glong num; + guint i; + + for (i = 0; i < strlen (value); i++) { + if (!g_ascii_isdigit (value[i]) && value[i] != '-') + return FALSE; + } + + errno = 0; + num = strtol (value, NULL, 10); + if (errno) + return FALSE; + if (num < def->min || num > def->max) + return FALSE; + + return TRUE; +} + +static gboolean +validate_list (const char *name, const char *value, const BondDefault *def) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (def->list) && def->list[i]; i++) { + if (g_strcmp0 (def->list[i], value) == 0) + return TRUE; + } + + /* empty validation list means all values pass */ + return def->list[0] == NULL ? TRUE : FALSE; +} + +static gboolean +validate_ip (const char *name, const char *value) +{ + char **ips, **iter; + gboolean success = TRUE; + struct in_addr addr; + + if (!value || !value[0]) + return FALSE; + + ips = g_strsplit_set (value, ",", 0); + for (iter = ips; iter && *iter && success; iter++) + success = !!inet_aton (*iter, &addr); + g_strfreev (ips); + + return success; +} + +static gboolean +validate_ifname (const char *name, const char *value) +{ + if (!value || !value[0]) + return FALSE; + + return nm_utils_iface_valid_name (value); +} + +/** + * nm_setting_bond_validate_option: + * @name: the name of the option to validate + * @value: the value of the option to validate + * + * Checks whether @name is a valid bond option and @value is a valid value for + * the @name. If @value is %NULL, the function only validates the option name. + * + * Returns: %TRUE, if the @value is valid for the given name. + * If the @name is not a valid option, %FALSE will be returned. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_bond_validate_option (const char *name, + const char *value) +{ + guint i; + + if (!name || !name[0]) + return FALSE; + + for (i = 0; i < G_N_ELEMENTS (defaults); i++) { + if (g_strcmp0 (defaults[i].opt, name) == 0) { + if (value == NULL) + return TRUE; + switch (defaults[i].opt_type) { + case TYPE_INT: + return validate_int (name, value, &defaults[i]); + case TYPE_STR: + return validate_list (name, value, &defaults[i]); + case TYPE_BOTH: + return ( validate_int (name, value, &defaults[i]) + || validate_list (name, value, &defaults[i])); + case TYPE_IP: + return validate_ip (name, value); + case TYPE_IFNAME: + return validate_ifname (name, value); + } + return FALSE; + } + } + return FALSE; +} + +/** + * nm_setting_bond_get_option_by_name: + * @setting: the #NMSettingBond + * @name: the option name for which to retrieve the value + * + * Returns the value associated with the bonding option specified by + * @name, if it exists. + * + * Returns: the value, or %NULL if the key/value pair was never added to the + * setting; the value is owned by the setting and must not be modified + **/ +const char * +nm_setting_bond_get_option_by_name (NMSettingBond *setting, + const char *name) +{ + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); + + if (!nm_setting_bond_validate_option (name, NULL)) + return NULL; + + return g_hash_table_lookup (NM_SETTING_BOND_GET_PRIVATE (setting)->options, name); +} + +/** + * nm_setting_bond_add_option: + * @setting: the #NMSettingBond + * @name: name for the option + * @value: value for the option + * + * Add an option to the table. The option is compared to an internal list + * of allowed options. Option names may contain only alphanumeric characters + * (ie [a-zA-Z0-9]). Adding a new name replaces any existing name/value pair + * that may already exist. + * + * The order of how to set several options is relevant because there are options + * that conflict with each other. + * + * Returns: %TRUE if the option was valid and was added to the internal option + * list, %FALSE if it was not. + **/ +gboolean +nm_setting_bond_add_option (NMSettingBond *setting, + const char *name, + const char *value) +{ + NMSettingBondPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE); + + if (!value || !nm_setting_bond_validate_option (name, value)) + return FALSE; + + priv = NM_SETTING_BOND_GET_PRIVATE (setting); + + g_hash_table_insert (priv->options, g_strdup (name), g_strdup (value)); + + if ( !strcmp (name, NM_SETTING_BOND_OPTION_MIIMON) + && strcmp (value, "0") != 0) { + g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_ARP_INTERVAL); + g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_ARP_IP_TARGET); + } else if ( !strcmp (name, NM_SETTING_BOND_OPTION_ARP_INTERVAL) + && strcmp (value, "0") != 0) { + g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_MIIMON); + g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_DOWNDELAY); + g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_UPDELAY); + } + + g_object_notify (G_OBJECT (setting), NM_SETTING_BOND_OPTIONS); + + return TRUE; +} + +/** + * nm_setting_bond_remove_option: + * @setting: the #NMSettingBond + * @name: name of the option to remove + * + * Remove the bonding option referenced by @name from the internal option + * list. + * + * Returns: %TRUE if the option was found and removed from the internal option + * list, %FALSE if it was not. + **/ +gboolean +nm_setting_bond_remove_option (NMSettingBond *setting, + const char *name) +{ + gboolean found; + + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE); + + if (!nm_setting_bond_validate_option (name, NULL)) + return FALSE; + + found = g_hash_table_remove (NM_SETTING_BOND_GET_PRIVATE (setting)->options, name); + if (found) + g_object_notify (G_OBJECT (setting), NM_SETTING_BOND_OPTIONS); + return found; +} + +/** + * nm_setting_bond_get_valid_options: + * @setting: the #NMSettingBond + * + * Returns a list of valid bond options. + * + * Returns: (transfer none): a %NULL-terminated array of strings of valid bond options. + **/ +const char ** +nm_setting_bond_get_valid_options (NMSettingBond *setting) +{ + static const char *array[G_N_ELEMENTS (defaults) + 1] = { NULL }; + int i; + + /* initialize the array once */ + if (G_UNLIKELY (array[0] == NULL)) { + for (i = 0; i < G_N_ELEMENTS (defaults); i++) + array[i] = defaults[i].opt; + array[i] = NULL; + } + return array; +} + +/** + * nm_setting_bond_get_option_default: + * @setting: the #NMSettingBond + * @name: the name of the option + * + * Returns: the value of the bond option if not overridden by an entry in + * the #NMSettingBond:options property. + **/ +const char * +nm_setting_bond_get_option_default (NMSettingBond *setting, const char *name) +{ + guint i; + + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); + g_return_val_if_fail (nm_setting_bond_validate_option (name, NULL), NULL); + + for (i = 0; i < G_N_ELEMENTS (defaults); i++) { + if (g_strcmp0 (defaults[i].opt, name) == 0) + return defaults[i].val; + } + /* Any option that passes nm_setting_bond_validate_option() should also be found in defaults */ + g_assert_not_reached (); +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (setting); + GHashTableIter iter; + const char *key, *value; + const char *valid_modes[] = { "balance-rr", + "active-backup", + "balance-xor", + "broadcast", + "802.3ad", + "balance-tlb", + "balance-alb", + NULL }; + int miimon = 0, arp_interval = 0; + const char *arp_ip_target = NULL; + const char *primary; + + g_hash_table_iter_init (&iter, priv->options); + while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) { + if (!value[0] || !nm_setting_bond_validate_option (key, value)) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_OPTION, + _("invalid option '%s' or its value '%s'"), + key, value); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + } + + value = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_MIIMON); + if (value) + miimon = atoi (value); + value = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_ARP_INTERVAL); + if (value) + arp_interval = atoi (value); + + /* Can only set one of miimon and arp_interval */ + if (miimon > 0 && arp_interval > 0) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_OPTION, + _("only one of '%s' and '%s' can be set"), + NM_SETTING_BOND_OPTION_MIIMON, + NM_SETTING_BOND_OPTION_ARP_INTERVAL); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + } + + value = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_MODE); + if (!value) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_MISSING_OPTION, + _("mandatory option '%s' is missing"), + NM_SETTING_BOND_OPTION_MODE); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + if (!_nm_utils_string_in_list (value, valid_modes)) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_OPTION, + _("'%s' is not a valid value for '%s'"), + value, NM_SETTING_BOND_OPTION_MODE); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + + /* Make sure mode is compatible with other settings */ + if ( strcmp (value, "balance-alb") == 0 + || strcmp (value, "balance-tlb") == 0) { + if (arp_interval > 0) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_OPTION, + _("'%s=%s' is incompatible with '%s > 0'"), + NM_SETTING_BOND_OPTION_MODE, value, NM_SETTING_BOND_OPTION_ARP_INTERVAL); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + } + + primary = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_PRIMARY); + if (strcmp (value, "active-backup") == 0) { + if (primary && !nm_utils_iface_valid_name (primary)) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_OPTION, + _("'%s' is not a valid interface name for '%s' option"), + primary, NM_SETTING_BOND_OPTION_PRIMARY); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + } else { + if (primary) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_OPTION, + _("'%s' option is only valid for '%s=%s'"), + NM_SETTING_BOND_OPTION_PRIMARY, + NM_SETTING_BOND_OPTION_MODE, "active-backup"); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + } + + if (nm_setting_find_in_list (all_settings, NM_SETTING_INFINIBAND_SETTING_NAME)) { + if (strcmp (value, "active-backup") != 0) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_OPTION, + _("'%s=%s' is not a valid configuration for '%s'"), + NM_SETTING_BOND_OPTION_MODE, value, NM_SETTING_INFINIBAND_SETTING_NAME); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + } + + if (miimon == 0) { + /* updelay and downdelay can only be used with miimon */ + if (g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_UPDELAY)) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_OPTION, + _("'%s' option requires '%s' option to be set"), + NM_SETTING_BOND_OPTION_UPDELAY, NM_SETTING_BOND_OPTION_MIIMON); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + if (g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_DOWNDELAY)) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_OPTION, + _("'%s' option requires '%s' option to be set"), + NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_MIIMON); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + } + + /* arp_ip_target can only be used with arp_interval, and must + * contain a comma-separated list of IPv4 addresses. + */ + arp_ip_target = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_ARP_IP_TARGET); + if (arp_interval > 0) { + char **addrs; + guint32 addr; + int i; + + if (!arp_ip_target) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_MISSING_OPTION, + _("'%s' option requires '%s' option to be set"), + NM_SETTING_BOND_OPTION_ARP_INTERVAL, NM_SETTING_BOND_OPTION_ARP_IP_TARGET); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + + addrs = g_strsplit (arp_ip_target, ",", -1); + if (!addrs[0]) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_OPTION, + _("'%s' option is empty"), + NM_SETTING_BOND_OPTION_ARP_IP_TARGET); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + g_strfreev (addrs); + return FALSE; + } + + for (i = 0; addrs[i]; i++) { + if (!inet_pton (AF_INET, addrs[i], &addr)) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_OPTION, + _("'%s' is not a valid IPv4 address for '%s' option"), + NM_SETTING_BOND_OPTION_ARP_IP_TARGET, addrs[i]); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + g_strfreev (addrs); + return FALSE; + } + } + g_strfreev (addrs); + } else { + if (arp_ip_target) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_OPTION, + _("'%s' option requires '%s' option to be set"), + NM_SETTING_BOND_OPTION_ARP_IP_TARGET, NM_SETTING_BOND_OPTION_ARP_INTERVAL); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + } + + return _nm_setting_verify_deprecated_virtual_iface_name ( + priv->interface_name, FALSE, + NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_INTERFACE_NAME, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + NM_SETTING_BOND_ERROR_MISSING_PROPERTY, + all_settings, error); +} + +static const char * +get_virtual_iface_name (NMSetting *setting) +{ + NMSettingBond *self = NM_SETTING_BOND (setting); + + return nm_setting_bond_get_interface_name (self); +} + +static void +nm_setting_bond_init (NMSettingBond *setting) +{ + NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (setting); + + priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + /* Default values: */ + nm_setting_bond_add_option (setting, NM_SETTING_BOND_OPTION_MODE, "balance-rr"); +} + +static void +finalize (GObject *object) +{ + NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object); + + g_free (priv->interface_name); + g_hash_table_destroy (priv->options); + + G_OBJECT_CLASS (nm_setting_bond_parent_class)->finalize (object); +} + +static void +copy_hash (gpointer key, gpointer value, gpointer user_data) +{ + g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), g_strdup (value)); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object); + GHashTable *new_hash; + + switch (prop_id) { + case PROP_INTERFACE_NAME: + g_free (priv->interface_name); + priv->interface_name = g_value_dup_string (value); + break; + case PROP_OPTIONS: + /* Must make a deep copy of the hash table here... */ + g_hash_table_remove_all (priv->options); + new_hash = g_value_get_boxed (value); + if (new_hash) + g_hash_table_foreach (new_hash, copy_hash, priv->options); + 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) +{ + NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object); + NMSettingBond *setting = NM_SETTING_BOND (object); + + switch (prop_id) { + case PROP_INTERFACE_NAME: + g_value_set_string (value, nm_setting_bond_get_interface_name (setting)); + break; + case PROP_OPTIONS: + g_value_set_boxed (value, priv->options); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_bond_class_init (NMSettingBondClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingBondPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + parent_class->get_virtual_iface_name = get_virtual_iface_name; + + /* Properties */ + /** + * NMSettingBond:interface-name: + * + * The name of the virtual in-kernel bonding network interface + **/ + g_object_class_install_property + (object_class, PROP_INTERFACE_NAME, + g_param_spec_string (NM_SETTING_BOND_INTERFACE_NAME, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingBond:options: + * + * 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]). + **/ + g_object_class_install_property + (object_class, PROP_OPTIONS, + _nm_param_spec_specialized (NM_SETTING_BOND_OPTIONS, "", "", + DBUS_TYPE_G_MAP_OF_STRING, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-bond.h b/libnm-core/nm-setting-bond.h new file mode 100644 index 0000000000..e7807380bf --- /dev/null +++ b/libnm-core/nm-setting-bond.h @@ -0,0 +1,117 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2013 Red Hat, Inc. + */ + +#ifndef NM_SETTING_BOND_H +#define NM_SETTING_BOND_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_BOND (nm_setting_bond_get_type ()) +#define NM_SETTING_BOND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_BOND, NMSettingBond)) +#define NM_SETTING_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_BOND, NMSettingBondClass)) +#define NM_IS_SETTING_BOND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_BOND)) +#define NM_IS_SETTING_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_BOND)) +#define NM_SETTING_BOND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_BOND, NMSettingBondClass)) + +#define NM_SETTING_BOND_SETTING_NAME "bond" + +/** + * NMSettingBondError: + * @NM_SETTING_BOND_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_BOND_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_BOND_ERROR_MISSING_PROPERTY: the property was missing and is + * required + */ +typedef enum { + NM_SETTING_BOND_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_BOND_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ + NM_SETTING_BOND_ERROR_INVALID_OPTION, /*< nick=InvalidOption >*/ + NM_SETTING_BOND_ERROR_MISSING_OPTION, /*< nick=MissingOption >*/ +} NMSettingBondError; + +#define NM_SETTING_BOND_ERROR nm_setting_bond_error_quark () +GQuark nm_setting_bond_error_quark (void); + +#define NM_SETTING_BOND_INTERFACE_NAME "interface-name" +#define NM_SETTING_BOND_OPTIONS "options" + +/* Valid options for the 'options' property */ +#define NM_SETTING_BOND_OPTION_MODE "mode" +#define NM_SETTING_BOND_OPTION_MIIMON "miimon" +#define NM_SETTING_BOND_OPTION_DOWNDELAY "downdelay" +#define NM_SETTING_BOND_OPTION_UPDELAY "updelay" +#define NM_SETTING_BOND_OPTION_ARP_INTERVAL "arp_interval" +#define NM_SETTING_BOND_OPTION_ARP_IP_TARGET "arp_ip_target" +#define NM_SETTING_BOND_OPTION_ARP_VALIDATE "arp_validate" +#define NM_SETTING_BOND_OPTION_PRIMARY "primary" +#define NM_SETTING_BOND_OPTION_PRIMARY_RESELECT "primary_reselect" +#define NM_SETTING_BOND_OPTION_FAIL_OVER_MAC "fail_over_mac" +#define NM_SETTING_BOND_OPTION_USE_CARRIER "use_carrier" +#define NM_SETTING_BOND_OPTION_AD_SELECT "ad_select" +#define NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY "xmit_hash_policy" +#define NM_SETTING_BOND_OPTION_RESEND_IGMP "resend_igmp" + +typedef struct { + NMSetting parent; +} NMSettingBond; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingBondClass; + +GType nm_setting_bond_get_type (void); + +NMSetting * nm_setting_bond_new (void); +const char * nm_setting_bond_get_interface_name (NMSettingBond *setting); +guint32 nm_setting_bond_get_num_options (NMSettingBond *setting); +gboolean nm_setting_bond_get_option (NMSettingBond *setting, + guint32 idx, + const char **out_name, + const char **out_value); +const char * nm_setting_bond_get_option_by_name (NMSettingBond *setting, + const char *name); +gboolean nm_setting_bond_add_option (NMSettingBond *setting, + const char *name, + const char *value); +gboolean nm_setting_bond_remove_option (NMSettingBond *setting, + const char *name); + +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_bond_validate_option (const char *name, + const char *value); + +const char **nm_setting_bond_get_valid_options (NMSettingBond *setting); + +const char * nm_setting_bond_get_option_default (NMSettingBond *setting, + const char *name); + +G_END_DECLS + +#endif /* NM_SETTING_BOND_H */ diff --git a/libnm-core/nm-setting-bridge-port.c b/libnm-core/nm-setting-bridge-port.c new file mode 100644 index 0000000000..496de581ab --- /dev/null +++ b/libnm-core/nm-setting-bridge-port.c @@ -0,0 +1,304 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2012 - 2013 Red Hat, Inc. + */ + +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "nm-setting-bridge-port.h" +#include "nm-utils.h" +#include "nm-utils-private.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-bridge-port + * @short_description: Describes connection properties for bridge ports + * @include: nm-setting-bridge-port.h + * + * The #NMSettingBridgePort object is a #NMSetting subclass that describes + * optional properties that apply to bridge ports. + * + * Since: 0.9.8 + **/ + +/** + * nm_setting_bridge_port_error_quark: + * + * Registers an error quark for #NMSettingBridgePort if necessary. + * + * Returns: the error quark used for #NMSettingBridgePort errors. + * + * Since: 0.9.8 + **/ +GQuark +nm_setting_bridge_port_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-bridge-port-error-quark"); + return quark; +} + +G_DEFINE_TYPE_WITH_CODE (NMSettingBridgePort, nm_setting_bridge_port, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_BRIDGE_PORT_SETTING_NAME, + g_define_type_id, + 3, + NM_SETTING_BRIDGE_PORT_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BRIDGE_PORT) + +#define NM_SETTING_BRIDGE_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BRIDGE_PORT, NMSettingBridgePortPrivate)) + +typedef struct { + guint16 priority; + guint16 path_cost; + gboolean hairpin_mode; +} NMSettingBridgePortPrivate; + +enum { + PROP_0, + PROP_PRIORITY, + PROP_PATH_COST, + PROP_HAIRPIN_MODE, + LAST_PROP +}; + +/**************************************************************************/ + +/** + * nm_setting_bridge_port_get_priority: + * @setting: the #NMSettingBridgePort + * + * Returns: the #NMSettingBridgePort:priority property of the setting + * + * Since: 0.9.8 + **/ +guint16 +nm_setting_bridge_port_get_priority (NMSettingBridgePort *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting), 0); + + return NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting)->priority; +} + +/** + * nm_setting_bridge_port_get_path_cost: + * @setting: the #NMSettingBridgePort + * + * Returns: the #NMSettingBridgePort:path-cost property of the setting + * + * Since: 0.9.8 + **/ +guint16 +nm_setting_bridge_port_get_path_cost (NMSettingBridgePort *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting), 0); + + return NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting)->path_cost; +} + +/** + * nm_setting_bridge_port_get_hairpin_mode: + * @setting: the #NMSettingBridgePort + * + * Returns: the #NMSettingBridgePort:hairpin-mode property of the setting + * + * Since: 0.9.8 + **/ +gboolean +nm_setting_bridge_port_get_hairpin_mode (NMSettingBridgePort *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting), FALSE); + + return NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting)->hairpin_mode; +} + +/**************************************************************************/ + +#define BR_MAX_PORT_PRIORITY 63 +#define BR_DEF_PRIORITY 32 + +#define BR_MIN_PATH_COST 1 +#define BR_MAX_PATH_COST 65535 + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingBridgePortPrivate *priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting); + + if (priv->priority > BR_MAX_PORT_PRIORITY) { + g_set_error (error, + NM_SETTING_BRIDGE_PORT_ERROR, + NM_SETTING_BRIDGE_PORT_ERROR_INVALID_PROPERTY, + _("'%d' is not a valid value for the property (should be <= %d)"), + priv->priority, BR_MAX_PORT_PRIORITY); + g_prefix_error (error, "%s.%s: ", + NM_SETTING_BRIDGE_PORT_SETTING_NAME, + NM_SETTING_BRIDGE_PORT_PRIORITY); + return FALSE; + } + + if (priv->path_cost > BR_MAX_PATH_COST) { + g_set_error (error, + NM_SETTING_BRIDGE_PORT_ERROR, + NM_SETTING_BRIDGE_PORT_ERROR_INVALID_PROPERTY, + _("'%d' is not a valid value for the property (should be <= %d)"), + priv->path_cost, BR_MAX_PATH_COST); + g_prefix_error (error, "%s.%s: ", + NM_SETTING_BRIDGE_PORT_SETTING_NAME, + NM_SETTING_BRIDGE_PORT_PATH_COST); + return FALSE; + } + + return TRUE; +} + +/**************************************************************************/ + +/** + * nm_setting_bridge_port_new: + * + * Creates a new #NMSettingBridgePort object with default values. + * + * Returns: (transfer full): the new empty #NMSettingBridgePort object + * + * Since: 0.9.8 + **/ +NMSetting * +nm_setting_bridge_port_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_BRIDGE_PORT, NULL); +} + +static void +nm_setting_bridge_port_init (NMSettingBridgePort *setting) +{ +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingBridgePortPrivate *priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PRIORITY: + priv->priority = (guint16) (g_value_get_uint (value) & 0xFFFF); + break; + case PROP_PATH_COST: + priv->path_cost = (guint16) (g_value_get_uint (value) & 0xFFFF); + break; + case PROP_HAIRPIN_MODE: + priv->hairpin_mode = g_value_get_boolean (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) +{ + NMSettingBridgePortPrivate *priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PRIORITY: + g_value_set_uint (value, priv->priority); + break; + case PROP_PATH_COST: + g_value_set_uint (value, priv->path_cost); + break; + case PROP_HAIRPIN_MODE: + g_value_set_boolean (value, priv->hairpin_mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_bridge_port_class_init (NMSettingBridgePortClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingBridgePortPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + parent_class->verify = verify; + + /* Properties */ + /** + * NMSettingBridgePort:priority: + * + * The Spanning Tree Protocol (STP) priority of this bridge port. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_PRIORITY, + g_param_spec_uint (NM_SETTING_BRIDGE_PORT_PRIORITY, "", "", + 0, BR_MAX_PORT_PRIORITY, BR_DEF_PRIORITY, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingBridgePort:path-cost: + * + * The Spanning Tree Protocol (STP) port cost for destinations via this + * port. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_PATH_COST, + g_param_spec_uint (NM_SETTING_BRIDGE_PORT_PATH_COST, "", "", + 0, BR_MAX_PATH_COST, 100, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingBridgePort:hairpin-mode: + * + * Enables or disabled "hairpin mode" for the port, which allows frames to + * be sent back out through the port the frame was received on. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_HAIRPIN_MODE, + g_param_spec_boolean (NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, "", "", + FALSE, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-bridge-port.h b/libnm-core/nm-setting-bridge-port.h new file mode 100644 index 0000000000..fcaf22ed09 --- /dev/null +++ b/libnm-core/nm-setting-bridge-port.h @@ -0,0 +1,86 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2012 Red Hat, Inc. + */ + +#ifndef NM_SETTING_BRIDGE_PORT_H +#define NM_SETTING_BRIDGE_PORT_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_BRIDGE_PORT (nm_setting_bridge_port_get_type ()) +#define NM_SETTING_BRIDGE_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_BRIDGE_PORT, NMSettingBridgePort)) +#define NM_SETTING_BRIDGE_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_BRIDGE_PORT, NMSettingBridgePortClass)) +#define NM_IS_SETTING_BRIDGE_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_BRIDGE_PORT)) +#define NM_IS_SETTING_BRIDGE_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_BRIDGE_PORT)) +#define NM_SETTING_BRIDGE_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_BRIDGE_PORT, NMSettingBridgePortClass)) + +#define NM_SETTING_BRIDGE_PORT_SETTING_NAME "bridge-port" + +/** + * NMSettingBridgePortError: + * @NM_SETTING_BRIDGE_PORT_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_BRIDGE_PORT_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_BRIDGE_PORT_ERROR_MISSING_PROPERTY: the property was missing and + * is required + * + * Since: 0.9.8 + */ +typedef enum { + NM_SETTING_BRIDGE_PORT_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_BRIDGE_PORT_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_BRIDGE_PORT_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ +} NMSettingBridgePortError; + +#define NM_SETTING_BRIDGE_PORT_ERROR nm_setting_bridge_port_error_quark () +GQuark nm_setting_bridge_port_error_quark (void); + +#define NM_SETTING_BRIDGE_PORT_PRIORITY "priority" +#define NM_SETTING_BRIDGE_PORT_PATH_COST "path-cost" +#define NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE "hairpin-mode" + +typedef struct { + NMSetting parent; +} NMSettingBridgePort; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingBridgePortClass; + +GType nm_setting_bridge_port_get_type (void); + +NMSetting * nm_setting_bridge_port_new (void); + +guint16 nm_setting_bridge_port_get_priority (NMSettingBridgePort *setting); + +guint16 nm_setting_bridge_port_get_path_cost (NMSettingBridgePort *setting); + +gboolean nm_setting_bridge_port_get_hairpin_mode (NMSettingBridgePort *setting); + +G_END_DECLS + +#endif /* NM_SETTING_BRIDGE_PORT_H */ diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c new file mode 100644 index 0000000000..789660036f --- /dev/null +++ b/libnm-core/nm-setting-bridge.c @@ -0,0 +1,576 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2013 Red Hat, Inc. + */ + +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> +#include <linux/if_ether.h> + +#include "nm-setting-bridge.h" +#include "nm-param-spec-specialized.h" +#include "nm-setting-private.h" +#include "nm-utils.h" +#include "nm-utils-private.h" +#include "nm-dbus-glib-types.h" + +/** + * SECTION:nm-setting-bridge + * @short_description: Describes connection properties for bridges + * @include: nm-setting-bridge.h + * + * The #NMSettingBridge object is a #NMSetting subclass that describes properties + * necessary for bridging connections. + * + * Since: 0.9.8 + **/ + +/** + * nm_setting_bridge_error_quark: + * + * Registers an error quark for #NMSettingBridge if necessary. + * + * Returns: the error quark used for #NMSettingBridge errors. + * + * Since: 0.9.8 + **/ +GQuark +nm_setting_bridge_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-bridge-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_BRIDGE_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_BRIDGE_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BRIDGE) + +#define NM_SETTING_BRIDGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BRIDGE, NMSettingBridgePrivate)) + +typedef struct { + char * interface_name; + GByteArray *mac_address; + gboolean stp; + guint16 priority; + guint16 forward_delay; + guint16 hello_time; + guint16 max_age; + guint32 ageing_time; +} NMSettingBridgePrivate; + +enum { + PROP_0, + PROP_INTERFACE_NAME, + PROP_MAC_ADDRESS, + PROP_STP, + PROP_PRIORITY, + PROP_FORWARD_DELAY, + PROP_HELLO_TIME, + PROP_MAX_AGE, + PROP_AGEING_TIME, + LAST_PROP +}; + +/** + * nm_setting_bridge_new: + * + * Creates a new #NMSettingBridge object with default values. + * + * Returns: (transfer full): the new empty #NMSettingBridge object + * + * Since: 0.9.8 + **/ +NMSetting * +nm_setting_bridge_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_BRIDGE, NULL); +} + +/** + * nm_setting_bridge_get_interface_name: + * @setting: the #NMSettingBridge + * + * Returns: the #NMSettingBridge:interface-name property of the setting + * + * Since: 0.9.8 + **/ +const char * +nm_setting_bridge_get_interface_name (NMSettingBridge *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0); + + return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->interface_name; +} + +/** + * nm_setting_bridge_get_mac_address: + * @setting: the #NMSettingBridge + * + * Returns: the #NMSettingBridge:mac-address property of the setting + * + * Since: 0.9.10 + **/ +const GByteArray * +nm_setting_bridge_get_mac_address (NMSettingBridge *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), NULL); + + return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->mac_address; +} + +/** + * nm_setting_bridge_get_stp: + * @setting: the #NMSettingBridge + * + * Returns: the #NMSettingBridge:stp property of the setting + * + * Since: 0.9.8 + **/ +gboolean +nm_setting_bridge_get_stp (NMSettingBridge *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), FALSE); + + return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->stp; +} + +/** + * nm_setting_bridge_get_priority: + * @setting: the #NMSettingBridge + * + * Returns: the #NMSettingBridge:priority property of the setting + * + * Since: 0.9.8 + **/ +guint16 +nm_setting_bridge_get_priority (NMSettingBridge *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0); + + return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->priority; +} + +/** + * nm_setting_bridge_get_forward_delay: + * @setting: the #NMSettingBridge + * + * Returns: the #NMSettingBridge:forward-delay property of the setting + * + * Since: 0.9.8 + **/ +guint16 +nm_setting_bridge_get_forward_delay (NMSettingBridge *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0); + + return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->forward_delay; +} + +/** + * nm_setting_bridge_get_hello_time: + * @setting: the #NMSettingBridge + * + * Returns: the #NMSettingBridge:hello-time property of the setting + * + * Since: 0.9.8 + **/ +guint16 +nm_setting_bridge_get_hello_time (NMSettingBridge *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0); + + return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->hello_time; +} + +/** + * nm_setting_bridge_get_max_age: + * @setting: the #NMSettingBridge + * + * Returns: the #NMSettingBridge:max-age property of the setting + * + * Since: 0.9.8 + **/ +guint16 +nm_setting_bridge_get_max_age (NMSettingBridge *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0); + + return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->max_age; +} + +/** + * nm_setting_bridge_get_ageing_time: + * @setting: the #NMSettingBridge + * + * Returns: the #NMSettingBridge:ageing-time property of the setting + * + * Since: 0.9.8 + **/ +guint +nm_setting_bridge_get_ageing_time (NMSettingBridge *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0); + + return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->ageing_time; +} + +/* IEEE 802.1D-1998 timer values */ +#define BR_MIN_HELLO_TIME 1 +#define BR_MAX_HELLO_TIME 10 + +#define BR_MIN_FORWARD_DELAY 2 +#define BR_MAX_FORWARD_DELAY 30 + +#define BR_MIN_MAX_AGE 6 +#define BR_MAX_MAX_AGE 40 + +/* IEEE 802.1D-1998 Table 7.4 */ +#define BR_MIN_AGEING_TIME 0 +#define BR_MAX_AGEING_TIME 1000000 + +static inline gboolean +check_range (guint32 val, + guint32 min, + guint32 max, + const char *prop, + GError **error) +{ + if ((val != 0) && (val < min || val > max)) { + g_set_error (error, + NM_SETTING_BRIDGE_ERROR, + NM_SETTING_BRIDGE_ERROR_INVALID_PROPERTY, + _("value '%d' is out of range <%d-%d>"), + val, min, max); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BRIDGE_SETTING_NAME, prop); + return FALSE; + } + return TRUE; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting); + + if (priv->mac_address && priv->mac_address->len != ETH_ALEN) { + g_set_error_literal (error, + NM_SETTING_BRIDGE_ERROR, + NM_SETTING_BRIDGE_ERROR_INVALID_PROPERTY, + _("is not a valid MAC address")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_MAC_ADDRESS); + return FALSE; + } + + if (!check_range (priv->forward_delay, + BR_MIN_FORWARD_DELAY, + BR_MAX_FORWARD_DELAY, + NM_SETTING_BRIDGE_FORWARD_DELAY, + error)) + return FALSE; + + if (!check_range (priv->hello_time, + BR_MIN_HELLO_TIME, + BR_MAX_HELLO_TIME, + NM_SETTING_BRIDGE_HELLO_TIME, + error)) + return FALSE; + + if (!check_range (priv->max_age, + BR_MIN_MAX_AGE, + BR_MAX_MAX_AGE, + NM_SETTING_BRIDGE_MAX_AGE, + error)) + return FALSE; + + if (!check_range (priv->ageing_time, + BR_MIN_AGEING_TIME, + BR_MAX_AGEING_TIME, + NM_SETTING_BRIDGE_AGEING_TIME, + error)) + return FALSE; + + return _nm_setting_verify_deprecated_virtual_iface_name ( + priv->interface_name, FALSE, + NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_INTERFACE_NAME, + NM_SETTING_BRIDGE_ERROR, + NM_SETTING_BRIDGE_ERROR_INVALID_PROPERTY, + NM_SETTING_BRIDGE_ERROR_MISSING_PROPERTY, + all_settings, error); +} + +static const char * +get_virtual_iface_name (NMSetting *setting) +{ + NMSettingBridge *self = NM_SETTING_BRIDGE (setting); + + return nm_setting_bridge_get_interface_name (self); +} + +static void +nm_setting_bridge_init (NMSettingBridge *setting) +{ +} + +static void +finalize (GObject *object) +{ + NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (object); + + g_free (priv->interface_name); + + if (priv->mac_address) + g_byte_array_free (priv->mac_address, TRUE); + + G_OBJECT_CLASS (nm_setting_bridge_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_INTERFACE_NAME: + g_free (priv->interface_name); + priv->interface_name = g_value_dup_string (value); + break; + case PROP_MAC_ADDRESS: + if (priv->mac_address) + g_byte_array_free (priv->mac_address, TRUE); + priv->mac_address = g_value_dup_boxed (value); + break; + case PROP_STP: + priv->stp = g_value_get_boolean (value); + break; + case PROP_PRIORITY: + priv->priority = (guint16) g_value_get_uint (value); + break; + case PROP_FORWARD_DELAY: + priv->forward_delay = (guint16) g_value_get_uint (value); + break; + case PROP_HELLO_TIME: + priv->hello_time = (guint16) g_value_get_uint (value); + break; + case PROP_MAX_AGE: + priv->max_age = (guint16) g_value_get_uint (value); + break; + case PROP_AGEING_TIME: + priv->ageing_time = g_value_get_uint (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) +{ + NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (object); + NMSettingBridge *setting = NM_SETTING_BRIDGE (object); + + switch (prop_id) { + case PROP_INTERFACE_NAME: + g_value_set_string (value, nm_setting_bridge_get_interface_name (setting)); + break; + case PROP_MAC_ADDRESS: + g_value_set_boxed (value, nm_setting_bridge_get_mac_address (setting)); + break; + case PROP_STP: + g_value_set_boolean (value, priv->stp); + break; + case PROP_PRIORITY: + g_value_set_uint (value, priv->priority); + break; + case PROP_FORWARD_DELAY: + g_value_set_uint (value, priv->forward_delay); + break; + case PROP_HELLO_TIME: + g_value_set_uint (value, priv->hello_time); + break; + case PROP_MAX_AGE: + g_value_set_uint (value, priv->max_age); + break; + case PROP_AGEING_TIME: + g_value_set_uint (value, priv->ageing_time); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingBridgePrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + parent_class->get_virtual_iface_name = get_virtual_iface_name; + + /* Properties */ + /** + * NMSettingBridge:interface-name: + * + * The name of the virtual in-kernel bridging network interface + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_INTERFACE_NAME, + g_param_spec_string (NM_SETTING_BRIDGE_INTERFACE_NAME, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingBridge:mac-address: + * + * If specified, the MAC address of bridge. When creating a new bridge, this + * MAC address will be set. When matching an existing (outside + * NetworkManager created) bridge, this MAC address must match. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_MAC_ADDRESS, + _nm_param_spec_specialized (NM_SETTING_BRIDGE_MAC_ADDRESS, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingBridge:stp: + * + * Controls whether Spanning Tree Protocol (STP) is enabled for this bridge. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_STP, + g_param_spec_boolean (NM_SETTING_BRIDGE_STP, "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingBridge:priority: + * + * Sets the Spanning Tree Protocol (STP) priority for this bridge. Lower + * values are "better"; the lowest priority bridge will be elected the root + * bridge. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_PRIORITY, + g_param_spec_uint (NM_SETTING_BRIDGE_PRIORITY, "", "", + 0, G_MAXUINT16, 0x8000, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingBridge:forward-delay: + * + * The Spanning Tree Protocol (STP) forwarding delay, in seconds. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_FORWARD_DELAY, + g_param_spec_uint (NM_SETTING_BRIDGE_FORWARD_DELAY, "", "", + 0, BR_MAX_FORWARD_DELAY, 15, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingBridge:hello-time: + * + * The Spanning Tree Protocol (STP) hello time, in seconds. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_HELLO_TIME, + g_param_spec_uint (NM_SETTING_BRIDGE_HELLO_TIME, "", "", + 0, BR_MAX_HELLO_TIME, 2, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingBridge:max-age: + * + * The Spanning Tree Protocol (STP) maximum message age, in seconds. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_MAX_AGE, + g_param_spec_uint (NM_SETTING_BRIDGE_MAX_AGE, "", "", + 0, BR_MAX_MAX_AGE, 20, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingBridge:ageing-time: + * + * The Ethernet MAC address aging time, in seconds. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_AGEING_TIME, + g_param_spec_uint (NM_SETTING_BRIDGE_AGEING_TIME, "", "", + 0, BR_MAX_AGEING_TIME, 300, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-bridge.h b/libnm-core/nm-setting-bridge.h new file mode 100644 index 0000000000..f6b59262b8 --- /dev/null +++ b/libnm-core/nm-setting-bridge.h @@ -0,0 +1,102 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2012 Red Hat, Inc. + */ + +#ifndef NM_SETTING_BRIDGE_H +#define NM_SETTING_BRIDGE_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_BRIDGE (nm_setting_bridge_get_type ()) +#define NM_SETTING_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_BRIDGE, NMSettingBridge)) +#define NM_SETTING_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_BRIDGE, NMSettingBridgeClass)) +#define NM_IS_SETTING_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_BRIDGE)) +#define NM_IS_SETTING_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_BRIDGE)) +#define NM_SETTING_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_BRIDGE, NMSettingBridgeClass)) + +#define NM_SETTING_BRIDGE_SETTING_NAME "bridge" + +/** + * NMSettingBridgeError: + * @NM_SETTING_BRIDGE_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_BRIDGE_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_BRIDGE_ERROR_MISSING_PROPERTY: the property was missing and is + * required + * + * Since: 0.9.8 + */ +typedef enum { + NM_SETTING_BRIDGE_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_BRIDGE_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_BRIDGE_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ +} NMSettingBridgeError; + +#define NM_SETTING_BRIDGE_ERROR nm_setting_bridge_error_quark () +GQuark nm_setting_bridge_error_quark (void); + +#define NM_SETTING_BRIDGE_INTERFACE_NAME "interface-name" +#define NM_SETTING_BRIDGE_MAC_ADDRESS "mac-address" +#define NM_SETTING_BRIDGE_STP "stp" +#define NM_SETTING_BRIDGE_PRIORITY "priority" +#define NM_SETTING_BRIDGE_FORWARD_DELAY "forward-delay" +#define NM_SETTING_BRIDGE_HELLO_TIME "hello-time" +#define NM_SETTING_BRIDGE_MAX_AGE "max-age" +#define NM_SETTING_BRIDGE_AGEING_TIME "ageing-time" + +typedef struct { + NMSetting parent; +} NMSettingBridge; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingBridgeClass; + +GType nm_setting_bridge_get_type (void); + +NMSetting * nm_setting_bridge_new (void); + +const char * nm_setting_bridge_get_interface_name (NMSettingBridge *setting); + +NM_AVAILABLE_IN_0_9_10 +const GByteArray *nm_setting_bridge_get_mac_address (NMSettingBridge *setting); + +gboolean nm_setting_bridge_get_stp (NMSettingBridge *setting); + +guint16 nm_setting_bridge_get_priority (NMSettingBridge *setting); + +guint16 nm_setting_bridge_get_forward_delay (NMSettingBridge *setting); + +guint16 nm_setting_bridge_get_hello_time (NMSettingBridge *setting); + +guint16 nm_setting_bridge_get_max_age (NMSettingBridge *setting); + +guint32 nm_setting_bridge_get_ageing_time (NMSettingBridge *setting); + +G_END_DECLS + +#endif /* NM_SETTING_BRIDGE_H */ diff --git a/libnm-core/nm-setting-cdma.c b/libnm-core/nm-setting-cdma.c new file mode 100644 index 0000000000..976a59680a --- /dev/null +++ b/libnm-core/nm-setting-cdma.c @@ -0,0 +1,356 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2013 Red Hat, Inc. + */ + +#include <string.h> +#include <glib/gi18n.h> + +#include "nm-setting-cdma.h" +#include "nm-utils.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-cdma + * @short_description: Describes CDMA-based mobile broadband properties + * @include: nm-setting-cdma.h + * + * The #NMSettingCdma object is a #NMSetting subclass that describes + * properties that allow connections to IS-95-based mobile broadband + * networks, including those using CDMA2000/EVDO technology. + */ + +/** + * nm_setting_cdma_error_quark: + * + * Registers an error quark for #NMSettingCdma if necessary. + * + * Returns: the error quark used for #NMSettingCdma errors. + **/ +GQuark +nm_setting_cdma_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-cdma-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingCdma, nm_setting_cdma, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_CDMA_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_CDMA_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_CDMA) + +#define NM_SETTING_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_CDMA, NMSettingCdmaPrivate)) + +typedef struct { + char *number; /* For dialing, duh */ + char *username; + char *password; + NMSettingSecretFlags password_flags; +} NMSettingCdmaPrivate; + +enum { + PROP_0, + PROP_NUMBER, + PROP_USERNAME, + PROP_PASSWORD, + PROP_PASSWORD_FLAGS, + + LAST_PROP +}; + +/** + * nm_setting_cdma_new: + * + * Creates a new #NMSettingCdma object with default values. + * + * Returns: the new empty #NMSettingCdma object + **/ +NMSetting * +nm_setting_cdma_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_CDMA, NULL); +} + +/** + * nm_setting_cdma_get_number: + * @setting: the #NMSettingCdma + * + * Returns: the #NMSettingCdma:number property of the setting + **/ +const char * +nm_setting_cdma_get_number (NMSettingCdma *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CDMA (setting), NULL); + + return NM_SETTING_CDMA_GET_PRIVATE (setting)->number; +} + +/** + * nm_setting_cdma_get_username: + * @setting: the #NMSettingCdma + * + * Returns: the #NMSettingCdma:username property of the setting + **/ +const char * +nm_setting_cdma_get_username (NMSettingCdma *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CDMA (setting), NULL); + + return NM_SETTING_CDMA_GET_PRIVATE (setting)->username; +} + +/** + * nm_setting_cdma_get_password: + * @setting: the #NMSettingCdma + * + * Returns: the #NMSettingCdma:password property of the setting + **/ +const char * +nm_setting_cdma_get_password (NMSettingCdma *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CDMA (setting), NULL); + + return NM_SETTING_CDMA_GET_PRIVATE (setting)->password; +} + +/** + * nm_setting_cdma_get_password_flags: + * @setting: the #NMSettingCdma + * + * Returns: the #NMSettingSecretFlags pertaining to the #NMSettingCdma:password + **/ +NMSettingSecretFlags +nm_setting_cdma_get_password_flags (NMSettingCdma *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CDMA (setting), NM_SETTING_SECRET_FLAG_NONE); + + return NM_SETTING_CDMA_GET_PRIVATE (setting)->password_flags; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE (setting); + + if (!priv->number) { + g_set_error_literal (error, + NM_SETTING_CDMA_ERROR, + NM_SETTING_CDMA_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CDMA_SETTING_NAME, NM_SETTING_CDMA_NUMBER); + return FALSE; + } else if (!strlen (priv->number)) { + g_set_error_literal (error, + NM_SETTING_CDMA_ERROR, + NM_SETTING_CDMA_ERROR_INVALID_PROPERTY, + _("property is empty'")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CDMA_SETTING_NAME, NM_SETTING_CDMA_NUMBER); + return FALSE; + } + + if (priv->username && !strlen (priv->username)) { + g_set_error_literal (error, + NM_SETTING_CDMA_ERROR, + NM_SETTING_CDMA_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CDMA_SETTING_NAME, NM_SETTING_CDMA_USERNAME); + return FALSE; + } + + if (priv->password && !strlen (priv->password)) { + g_set_error_literal (error, + NM_SETTING_CDMA_ERROR, + NM_SETTING_CDMA_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CDMA_SETTING_NAME, NM_SETTING_CDMA_PASSWORD); + return FALSE; + } + + return TRUE; +} + +static GPtrArray * +need_secrets (NMSetting *setting) +{ + NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE (setting); + GPtrArray *secrets = NULL; + + if (priv->password) + return NULL; + + if (priv->username) { + if (!(priv->password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { + secrets = g_ptr_array_sized_new (1); + g_ptr_array_add (secrets, NM_SETTING_CDMA_PASSWORD); + } + } + + return secrets; +} + +static void +nm_setting_cdma_init (NMSettingCdma *setting) +{ +} + +static void +finalize (GObject *object) +{ + NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE (object); + + g_free (priv->number); + g_free (priv->username); + g_free (priv->password); + + G_OBJECT_CLASS (nm_setting_cdma_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_NUMBER: + g_free (priv->number); + priv->number = g_value_dup_string (value); + break; + case PROP_USERNAME: + g_free (priv->username); + priv->username = g_value_dup_string (value); + break; + case PROP_PASSWORD: + g_free (priv->password); + priv->password = g_value_dup_string (value); + break; + case PROP_PASSWORD_FLAGS: + priv->password_flags = g_value_get_uint (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) +{ + NMSettingCdma *setting = NM_SETTING_CDMA (object); + + switch (prop_id) { + case PROP_NUMBER: + g_value_set_string (value, nm_setting_cdma_get_number (setting)); + break; + case PROP_USERNAME: + g_value_set_string (value, nm_setting_cdma_get_username (setting)); + break; + case PROP_PASSWORD: + g_value_set_string (value, nm_setting_cdma_get_password (setting)); + break; + case PROP_PASSWORD_FLAGS: + g_value_set_uint (value, nm_setting_cdma_get_password_flags (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_cdma_class_init (NMSettingCdmaClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingCdmaPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + parent_class->need_secrets = need_secrets; + + /* Properties */ + + /** + * NMSettingCdma:number: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_NUMBER, + g_param_spec_string (NM_SETTING_CDMA_NUMBER, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingCdma:username: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_USERNAME, + g_param_spec_string (NM_SETTING_CDMA_USERNAME, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingCdma:password: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_PASSWORD, + g_param_spec_string (NM_SETTING_CDMA_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingCdma:password-flags: + * + * Flags indicating how to handle the #NMSettingCdma:password property. + **/ + g_object_class_install_property + (object_class, PROP_PASSWORD_FLAGS, + g_param_spec_uint (NM_SETTING_CDMA_PASSWORD_FLAGS, "", "", + NM_SETTING_SECRET_FLAG_NONE, + NM_SETTING_SECRET_FLAGS_ALL, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-cdma.h b/libnm-core/nm-setting-cdma.h new file mode 100644 index 0000000000..eee4420592 --- /dev/null +++ b/libnm-core/nm-setting-cdma.h @@ -0,0 +1,87 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2011 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SETTING_CDMA_H +#define NM_SETTING_CDMA_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_CDMA (nm_setting_cdma_get_type ()) +#define NM_SETTING_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_CDMA, NMSettingCdma)) +#define NM_SETTING_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_CDMA, NMSettingCdmaClass)) +#define NM_IS_SETTING_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_CDMA)) +#define NM_IS_SETTING_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_CDMA)) +#define NM_SETTING_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_CDMA, NMSettingCdmaClass)) + +#define NM_SETTING_CDMA_SETTING_NAME "cdma" + +/** + * NMSettingCdmaError: + * @NM_SETTING_CDMA_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_CDMA_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_CDMA_ERROR_MISSING_PROPERTY: the property was missing and is + * required + * @NM_SETTING_CDMA_ERROR_MISSING_SERIAL_SETTING: the required #NMSettingSerial + * is missing in the connection + */ +typedef enum { + NM_SETTING_CDMA_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_CDMA_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_CDMA_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ + NM_SETTING_CDMA_ERROR_MISSING_SERIAL_SETTING /*< nick=MissingSerialSetting >*/ +} NMSettingCdmaError; + +#define NM_SETTING_CDMA_ERROR nm_setting_cdma_error_quark () +GQuark nm_setting_cdma_error_quark (void); + +#define NM_SETTING_CDMA_NUMBER "number" +#define NM_SETTING_CDMA_USERNAME "username" +#define NM_SETTING_CDMA_PASSWORD "password" +#define NM_SETTING_CDMA_PASSWORD_FLAGS "password-flags" + +typedef struct { + NMSetting parent; +} NMSettingCdma; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingCdmaClass; + +GType nm_setting_cdma_get_type (void); + +NMSetting *nm_setting_cdma_new (void); +const char *nm_setting_cdma_get_number (NMSettingCdma *setting); +const char *nm_setting_cdma_get_username (NMSettingCdma *setting); +const char *nm_setting_cdma_get_password (NMSettingCdma *setting); +NMSettingSecretFlags nm_setting_cdma_get_password_flags (NMSettingCdma *setting); + +G_END_DECLS + +#endif /* NM_SETTING_CDMA_H */ diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c new file mode 100644 index 0000000000..f206bbb032 --- /dev/null +++ b/libnm-core/nm-setting-connection.c @@ -0,0 +1,1333 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2013 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include <string.h> +#include <glib/gi18n.h> + +#include "nm-utils.h" +#include "nm-dbus-glib-types.h" +#include "nm-param-spec-specialized.h" +#include "nm-setting-connection.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-connection + * @short_description: Describes general connection properties + * @include: nm-setting-connection.h + * + * The #NMSettingConnection object is a #NMSetting subclass that describes + * properties that apply to all #NMConnection objects, regardless of what type + * of network connection they describe. Each #NMConnection object must contain + * a #NMSettingConnection setting. + **/ + +/** + * nm_setting_connection_error_quark: + * + * Registers an error quark for #NMSettingConnection if necessary. + * + * Returns: the error quark used for #NMSettingConnection errors. + **/ +GQuark +nm_setting_connection_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-connection-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingConnection, nm_setting_connection, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_CONNECTION_SETTING_NAME, + g_define_type_id, + 0, + NM_SETTING_CONNECTION_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_CONNECTION) + +#define NM_SETTING_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_CONNECTION, NMSettingConnectionPrivate)) + +typedef enum { + PERM_TYPE_USER = 0, +} PermType; + +typedef struct { + guint8 ptype; + char *item; +} Permission; + +typedef struct { + char *id; + char *uuid; + char *interface_name; + char *type; + char *master; + char *slave_type; + GSList *permissions; /* list of Permission structs */ + gboolean autoconnect; + guint64 timestamp; + gboolean read_only; + char *zone; + GSList *secondaries; /* secondary connections to activate with the base connection */ + guint gateway_ping_timeout; +} NMSettingConnectionPrivate; + +enum { + PROP_0, + PROP_ID, + PROP_UUID, + PROP_INTERFACE_NAME, + PROP_TYPE, + PROP_PERMISSIONS, + PROP_AUTOCONNECT, + PROP_TIMESTAMP, + PROP_READ_ONLY, + PROP_ZONE, + PROP_MASTER, + PROP_SLAVE_TYPE, + PROP_SECONDARIES, + PROP_GATEWAY_PING_TIMEOUT, + + LAST_PROP +}; + +/***********************************************************************/ + +#define PERM_USER_PREFIX "user:" + +static Permission * +permission_new_from_str (const char *str) +{ + Permission *p; + const char *last_colon; + size_t ulen = 0, i; + + g_return_val_if_fail (strncmp (str, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0, NULL); + str += strlen (PERM_USER_PREFIX); + + last_colon = strrchr (str, ':'); + if (last_colon) { + /* Ensure that somebody didn't pass "user::" */ + g_return_val_if_fail (last_colon > str, NULL); + + /* Reject :[detail] for now */ + g_return_val_if_fail (*(last_colon + 1) == '\0', NULL); + + /* Make sure we don't include detail in the username */ + ulen = last_colon - str; + } else + ulen = strlen (str); + + /* Sanity check the length of the username */ + g_return_val_if_fail (ulen < 100, NULL); + + /* Make sure there's no ':' in the username */ + for (i = 0; i < ulen; i++) + g_return_val_if_fail (str[i] != ':', NULL); + + /* And the username must be valid UTF-8 */ + g_return_val_if_fail (g_utf8_validate (str, -1, NULL) == TRUE, NULL); + + /* Yay, valid... create the new permission */ + p = g_slice_new0 (Permission); + p->ptype = PERM_TYPE_USER; + if (last_colon) { + p->item = g_malloc (ulen + 1); + memcpy (p->item, str, ulen); + p->item[ulen] = '\0'; + } else + p->item = g_strdup (str); + + return p; +} + +static Permission * +permission_new (const char *uname) +{ + Permission *p; + + g_return_val_if_fail (uname, NULL); + g_return_val_if_fail (uname[0] != '\0', NULL); + g_return_val_if_fail (strchr (uname, ':') == NULL, NULL); + g_return_val_if_fail (g_utf8_validate (uname, -1, NULL) == TRUE, NULL); + + /* Yay, valid... create the new permission */ + p = g_slice_new0 (Permission); + p->ptype = PERM_TYPE_USER; + p->item = g_strdup (uname); + return p; +} + +static char * +permission_to_string (Permission *p) +{ + return g_strdup_printf (PERM_USER_PREFIX "%s:", p->item); +} + +static void +permission_free (Permission *p) +{ + g_free (p->item); + memset (p, 0, sizeof (*p)); + g_slice_free (Permission, p); +} + +/***********************************************************************/ + +/** + * nm_setting_connection_new: + * + * Creates a new #NMSettingConnection object with default values. + * + * Returns: the new empty #NMSettingConnection object + **/ +NMSetting *nm_setting_connection_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_CONNECTION, NULL); +} + +/** + * nm_setting_connection_get_id: + * @setting: the #NMSettingConnection + * + * Returns the #NMSettingConnection:id property of the connection. + * + * Returns: the connection ID + **/ +const char * +nm_setting_connection_get_id (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->id; +} + +/** + * nm_setting_connection_get_uuid: + * @setting: the #NMSettingConnection + * + * Returns the #NMSettingConnection:uuid property of the connection. + * + * Returns: the connection UUID + **/ +const char * +nm_setting_connection_get_uuid (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->uuid; +} + +/** + * nm_setting_connection_get_interface_name: + * @setting: the #NMSettingConnection + * + * Returns the #NMSettingConnection:interface-name property of the connection. + * + * Returns: the connection's interface name + * + * Since: 0.9.10 + **/ +const char * +nm_setting_connection_get_interface_name (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->interface_name; +} + +/** + * nm_setting_connection_get_connection_type: + * @setting: the #NMSettingConnection + * + * Returns the #NMSettingConnection:type property of the connection. + * + * Returns: the connection type + **/ +const char * +nm_setting_connection_get_connection_type (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->type; +} + + +/** + * nm_setting_connection_get_num_permissions: + * @setting: the #NMSettingConnection + * + * Returns the number of entires in the #NMSettingConnection:permissions + * property of this setting. + * + * Returns: the number of permissions entires + */ +guint32 +nm_setting_connection_get_num_permissions (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), 0); + + return g_slist_length (NM_SETTING_CONNECTION_GET_PRIVATE (setting)->permissions); +} + +/** + * nm_setting_connection_get_permission: + * @setting: the #NMSettingConnection + * @idx: the zero-based index of the permissions entry + * @out_ptype: on return, the permission type (at this time, always "user") + * @out_pitem: on return, the permission item (formatted accoring to @ptype, see + * #NMSettingConnection:permissions for more detail + * @out_detail: on return, the permission detail (at this time, always %NULL) + * + * Retrieve one of the entries of the #NMSettingConnection:permissions property + * of this setting. + * + * Returns: %TRUE if a permission was returned, %FALSE if @idx was invalid + */ +gboolean +nm_setting_connection_get_permission (NMSettingConnection *setting, + guint32 idx, + const char **out_ptype, + const char **out_pitem, + const char **out_detail) +{ + NMSettingConnectionPrivate *priv; + Permission *p; + + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE); + + priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting); + + g_return_val_if_fail (idx < g_slist_length (priv->permissions), FALSE); + + p = g_slist_nth_data (priv->permissions, idx); + if (out_ptype) + *out_ptype = "user"; + if (out_pitem) + *out_pitem = p->item; + if (out_detail) + *out_detail = NULL; + + return TRUE; +} + +/** + * nm_setting_connection_permissions_user_allowed: + * @setting: the #NMSettingConnection + * @uname: the user name to check permissions for + * + * Checks whether the given username is allowed to view/access this connection. + * + * Returns: %TRUE if the requested user is allowed to view this connection, + * %FALSE if the given user is not allowed to view this connection + */ +gboolean +nm_setting_connection_permissions_user_allowed (NMSettingConnection *setting, + const char *uname) +{ + NMSettingConnectionPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE); + g_return_val_if_fail (uname != NULL, FALSE); + g_return_val_if_fail (*uname != '\0', FALSE); + + priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting); + + /* If no permissions, visible to all */ + if (priv->permissions == NULL) + return TRUE; + + /* Find the username in the permissions list */ + for (iter = priv->permissions; iter; iter = g_slist_next (iter)) { + Permission *p = iter->data; + + if (strcmp (uname, p->item) == 0) + return TRUE; + } + + return FALSE; +} + +/** + * nm_setting_connection_add_permission: + * @setting: the #NMSettingConnection + * @ptype: the permission type; at this time only "user" is supported + * @pitem: the permission item formatted as required for @ptype + * @detail: (allow-none): unused at this time; must be %NULL + * + * Adds a permission to the connection's permission list. At this time, only + * the "user" permission type is supported, and @pitem must be a username. See + * #NMSettingConnection:permissions: for more details. + * + * Returns: %TRUE if the permission was unique and was successfully added to the + * list, %FALSE if @ptype or @pitem was invalid or it the permission was already + * present in the list + */ +gboolean +nm_setting_connection_add_permission (NMSettingConnection *setting, + const char *ptype, + const char *pitem, + const char *detail) +{ + NMSettingConnectionPrivate *priv; + Permission *p; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE); + g_return_val_if_fail (ptype, FALSE); + g_return_val_if_fail (strlen (ptype) > 0, FALSE); + g_return_val_if_fail (detail == NULL, FALSE); + + /* Only "user" for now... */ + g_return_val_if_fail (strcmp (ptype, "user") == 0, FALSE); + + priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting); + + /* No dupes */ + for (iter = priv->permissions; iter; iter = g_slist_next (iter)) { + p = iter->data; + if (strcmp (pitem, p->item) == 0) + return FALSE; + } + + p = permission_new (pitem); + g_return_val_if_fail (p != NULL, FALSE); + priv->permissions = g_slist_append (priv->permissions, p); + g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_PERMISSIONS); + + return TRUE; +} + +/** + * nm_setting_connection_remove_permission: + * @setting: the #NMSettingConnection + * @idx: the zero-based index of the permission to remove + * + * Removes the permission at index @idx from the connection. + */ +void +nm_setting_connection_remove_permission (NMSettingConnection *setting, + guint32 idx) +{ + NMSettingConnectionPrivate *priv; + GSList *iter; + + g_return_if_fail (NM_IS_SETTING_CONNECTION (setting)); + + priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting); + iter = g_slist_nth (priv->permissions, idx); + g_return_if_fail (iter != NULL); + + permission_free ((Permission *) iter->data); + priv->permissions = g_slist_delete_link (priv->permissions, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_PERMISSIONS); +} + +/** + * nm_setting_connection_remove_permission_by_value: + * @setting: the #NMSettingConnection + * @ptype: the permission type; at this time only "user" is supported + * @pitem: the permission item formatted as required for @ptype + * @detail: (allow-none): unused at this time; must be %NULL + * + * Removes the permission from the connection. + * At this time, only the "user" permission type is supported, and @pitem must + * be a username. See #NMSettingConnection:permissions: for more details. + * + * Returns: %TRUE if the permission was found and removed; %FALSE if it was not. + * + * Since: 0.9.10 + */ +gboolean +nm_setting_connection_remove_permission_by_value (NMSettingConnection *setting, + const char *ptype, + const char *pitem, + const char *detail) +{ + NMSettingConnectionPrivate *priv; + Permission *p; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE); + g_return_val_if_fail (ptype, FALSE); + g_return_val_if_fail (strlen (ptype) > 0, FALSE); + g_return_val_if_fail (detail == NULL, FALSE); + + /* Only "user" for now... */ + g_return_val_if_fail (strcmp (ptype, "user") == 0, FALSE); + + priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting); + for (iter = priv->permissions; iter; iter = g_slist_next (iter)) { + p = iter->data; + if (strcmp (pitem, p->item) == 0) { + permission_free ((Permission *) iter->data); + priv->permissions = g_slist_delete_link (priv->permissions, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_PERMISSIONS); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_connection_get_autoconnect: + * @setting: the #NMSettingConnection + * + * Returns the #NMSettingConnection:autoconnect property of the connection. + * + * Returns: the connection's autoconnect behavior + **/ +gboolean +nm_setting_connection_get_autoconnect (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->autoconnect; +} + +/** + * nm_setting_connection_get_timestamp: + * @setting: the #NMSettingConnection + * + * Returns the #NMSettingConnection:timestamp property of the connection. + * + * Returns: the connection's timestamp + **/ +guint64 +nm_setting_connection_get_timestamp (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), 0); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->timestamp; +} + +/** + * nm_setting_connection_get_read_only: + * @setting: the #NMSettingConnection + * + * Returns the #NMSettingConnection:read-only property of the connection. + * + * Returns: %TRUE if the connection is read-only, %FALSE if it is not + **/ +gboolean +nm_setting_connection_get_read_only (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), TRUE); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->read_only; +} + +/** + * nm_setting_connection_get_zone: + * @setting: the #NMSettingConnection + * + * Returns the #NMSettingConnection:zone property of the connection. + * + * Returns: the trust level of a connection + **/ +const char * +nm_setting_connection_get_zone (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->zone; +} + +/** + * nm_setting_connection_get_master: + * @setting: the #NMSettingConnection + * + * Returns the #NMSettingConnection:master property of the connection. + * + * Returns: interface name of the master device or UUID of the master + * connection. + */ +const char * +nm_setting_connection_get_master (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->master; +} + +/** + * nm_setting_connection_get_slave_type: + * @setting: the #NMSettingConnection + * + * Returns the #NMSettingConnection:slave-type property of the connection. + * + * Returns: the type of slave this connection is, if any + */ +const char * +nm_setting_connection_get_slave_type (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->slave_type; +} + +/** + * nm_setting_connection_is_slave_type: + * @setting: the #NMSettingConnection + * @type: the setting name (ie #NM_SETTING_BOND_SETTING_NAME) to be matched + * against @setting's slave type + * + * Returns: %TRUE if connection is of the given slave @type + */ +gboolean +nm_setting_connection_is_slave_type (NMSettingConnection *setting, + const char *type) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE); + + return !g_strcmp0 (NM_SETTING_CONNECTION_GET_PRIVATE (setting)->slave_type, type); +} + +/** + * nm_setting_connection_get_num_secondaries: + * @setting: the #NMSettingConnection + * + * Returns: the number of configured secondary connection UUIDs + * + * Since: 0.9.8 + **/ +guint32 +nm_setting_connection_get_num_secondaries (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), 0); + + return g_slist_length (NM_SETTING_CONNECTION_GET_PRIVATE (setting)->secondaries); +} + +/** + * nm_setting_connection_get_secondary: + * @setting: the #NMSettingConnection + * @idx: the zero-based index of the secondary connection UUID entry + * + * Returns: the secondary connection UUID at index @idx + * + * Since: 0.9.8 + **/ +const char * +nm_setting_connection_get_secondary (NMSettingConnection *setting, guint32 idx) +{ + NMSettingConnectionPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL); + + priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting); + g_return_val_if_fail (idx <= g_slist_length (priv->secondaries), NULL); + + return (const char *) g_slist_nth_data (priv->secondaries, idx); +} + +/** + * nm_setting_connection_add_secondary: + * @setting: the #NMSettingConnection + * @sec_uuid: the secondary connection UUID to add + * + * Adds a new secondary connetion UUID to the setting. + * + * Returns: %TRUE if the secondary connection UUID was added; %FALSE if the UUID + * was already present + * + * Since: 0.9.8 + **/ +gboolean +nm_setting_connection_add_secondary (NMSettingConnection *setting, + const char *sec_uuid) +{ + NMSettingConnectionPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE); + g_return_val_if_fail (sec_uuid != NULL, FALSE); + g_return_val_if_fail (sec_uuid[0] != '\0', FALSE); + + priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting); + for (iter = priv->secondaries; iter; iter = g_slist_next (iter)) { + if (!strcmp (sec_uuid, (char *) iter->data)) + return FALSE; + } + + priv->secondaries = g_slist_append (priv->secondaries, g_strdup (sec_uuid)); + g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_SECONDARIES); + return TRUE; +} + +/** + * nm_setting_connection_remove_secondary: + * @setting: the #NMSettingConnection + * @idx: index number of the secondary connection UUID + * + * Removes the secondary coonnection UUID at index @idx. + * + * Since: 0.9.8 + **/ +void +nm_setting_connection_remove_secondary (NMSettingConnection *setting, guint32 idx) +{ + NMSettingConnectionPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_CONNECTION (setting)); + + priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting); + elt = g_slist_nth (priv->secondaries, idx); + g_return_if_fail (elt != NULL); + + g_free (elt->data); + priv->secondaries = g_slist_delete_link (priv->secondaries, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_SECONDARIES); +} + +/** + * nm_setting_connection_remove_secondary_by_value: + * @setting: the #NMSettingConnection + * @sec_uuid: the secondary connection UUID to remove + * + * Removes the secondary coonnection UUID @sec_uuid. + * + * Returns: %TRUE if the secondary connection UUID was found and removed; %FALSE if it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_connection_remove_secondary_by_value (NMSettingConnection *setting, + const char *sec_uuid) +{ + NMSettingConnectionPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE); + g_return_val_if_fail (sec_uuid != NULL, FALSE); + g_return_val_if_fail (sec_uuid[0] != '\0', FALSE); + + priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting); + for (iter = priv->secondaries; iter; iter = g_slist_next (iter)) { + if (!strcmp (sec_uuid, (char *) iter->data)) { + priv->secondaries = g_slist_delete_link (priv->secondaries, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_SECONDARIES); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_connection_get_gateway_ping_timeout: + * @setting: the #NMSettingConnection + * + * Returns: the value contained in the #NMSettingConnection:gateway-ping-timeout + * property. + * + * Since: 0.9.10 + **/ +guint32 +nm_setting_connection_get_gateway_ping_timeout (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), 0); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->gateway_ping_timeout; +} + + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting); + gboolean is_slave; + GSList *iter; + + if (!priv->id) { + g_set_error_literal (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_ID); + return FALSE; + } else if (!strlen (priv->id)) { + g_set_error_literal (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_ID); + return FALSE; + } + + if (!priv->uuid) { + g_set_error_literal (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_UUID); + return FALSE; + } else if (!nm_utils_is_uuid (priv->uuid)) { + g_set_error (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid UUID"), + priv->uuid); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_UUID); + return FALSE; + } + + /* FIXME: previously, verify() set the NMSettingConnection:interface_name property, + * thus modifying the setting. verify() should not do this, but keep this not to change + * behaviour. + */ + if (!priv->interface_name) { + for (iter = all_settings; iter; iter = iter->next) { + NMSetting *s_current = iter->data; + char *virtual_iface_name = NULL; + + if (NM_IS_SETTING_BOND (s_current)) + g_object_get (s_current, NM_SETTING_BOND_INTERFACE_NAME, &virtual_iface_name, NULL); + else if (NM_IS_SETTING_BRIDGE (s_current)) + g_object_get (s_current, NM_SETTING_BRIDGE_INTERFACE_NAME, &virtual_iface_name, NULL); + else if (NM_IS_SETTING_TEAM (s_current)) + g_object_get (s_current, NM_SETTING_TEAM_INTERFACE_NAME, &virtual_iface_name, NULL); + else if (NM_IS_SETTING_VLAN (s_current)) + g_object_get (s_current, NM_SETTING_VLAN_INTERFACE_NAME, &virtual_iface_name, NULL); + /* For NMSettingInfiniband, virtual_iface_name has no backing field. + * No need to set the (unset) interface_name to the default value. + **/ + + if (virtual_iface_name) { + if (nm_utils_iface_valid_name (virtual_iface_name)) { + /* found a new interface name. */ + priv->interface_name = virtual_iface_name; + break; + } + g_free (virtual_iface_name); + } + } + } + + if (priv->interface_name) { + if (!nm_utils_iface_valid_name (priv->interface_name)) { + g_set_error (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid interface name"), + priv->interface_name); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME); + return FALSE; + } + } + + if (!priv->type) { + g_set_error_literal (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE); + return FALSE; + } else if (!strlen (priv->type)) { + g_set_error_literal (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE); + return FALSE; + } + + /* Make sure the corresponding 'type' item is present */ + if (all_settings && !nm_setting_find_in_list (all_settings, priv->type)) { + g_set_error (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_TYPE_SETTING_NOT_FOUND, + _("requires presence of '%s' setting in the connection"), + priv->type); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE); + return FALSE; + } + + is_slave = ( priv->slave_type + && ( !strcmp (priv->slave_type, NM_SETTING_BOND_SETTING_NAME) + || !strcmp (priv->slave_type, NM_SETTING_BRIDGE_SETTING_NAME) + || !strcmp (priv->slave_type, NM_SETTING_TEAM_SETTING_NAME))); + + if (priv->slave_type && !is_slave) { + g_set_error (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY, + _("Unknown slave type '%s'"), priv->slave_type); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_SLAVE_TYPE); + return NM_SETTING_VERIFY_ERROR; + } + + if (is_slave) { + if (!priv->master) { + g_set_error_literal (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY, + _("Slave connections need a valid '" NM_SETTING_CONNECTION_MASTER "' property")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER); + return NM_SETTING_VERIFY_ERROR; + } + } else { + if (priv->master) { + g_set_error_literal (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY, + _("Cannot set '" NM_SETTING_CONNECTION_MASTER "' without '" NM_SETTING_CONNECTION_SLAVE_TYPE "'")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_SLAVE_TYPE); + return NM_SETTING_VERIFY_ERROR; + } + } + + return TRUE; +} + +static gboolean +compare_property (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags) +{ + /* Handle ignore ID */ + if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_ID) + && g_strcmp0 (prop_spec->name, NM_SETTING_CONNECTION_ID) == 0) + return TRUE; + + /* Otherwise chain up to parent to handle generic compare */ + return NM_SETTING_CLASS (nm_setting_connection_parent_class)->compare_property (setting, other, prop_spec, flags); +} + +static void +nm_setting_connection_init (NMSettingConnection *setting) +{ +} + +static void +finalize (GObject *object) +{ + NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (object); + + g_free (priv->id); + g_free (priv->uuid); + g_free (priv->interface_name); + g_free (priv->type); + g_free (priv->zone); + g_free (priv->master); + g_free (priv->slave_type); + g_slist_free_full (priv->permissions, (GDestroyNotify) permission_free); + g_slist_free_full (priv->secondaries, g_free); + + G_OBJECT_CLASS (nm_setting_connection_parent_class)->finalize (object); +} + +static GSList * +perm_stringlist_to_permlist (GSList *strlist) +{ + GSList *list = NULL, *iter; + + for (iter = strlist; iter; iter = g_slist_next (iter)) { + Permission *p; + + p = permission_new_from_str ((const char *) iter->data); + if (p) + list = g_slist_append (list, p); + } + + return list; +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_ID: + g_free (priv->id); + priv->id = g_value_dup_string (value); + break; + case PROP_UUID: + g_free (priv->uuid); + priv->uuid = g_value_dup_string (value); + break; + case PROP_INTERFACE_NAME: + g_free (priv->interface_name); + priv->interface_name = g_value_dup_string (value); + break; + case PROP_TYPE: + g_free (priv->type); + priv->type = g_value_dup_string (value); + break; + case PROP_PERMISSIONS: + g_slist_free_full (priv->permissions, (GDestroyNotify) permission_free); + priv->permissions = perm_stringlist_to_permlist (g_value_get_boxed (value)); + break; + case PROP_AUTOCONNECT: + priv->autoconnect = g_value_get_boolean (value); + break; + case PROP_TIMESTAMP: + priv->timestamp = g_value_get_uint64 (value); + break; + case PROP_READ_ONLY: + priv->read_only = g_value_get_boolean (value); + break; + case PROP_ZONE: + g_free (priv->zone); + priv->zone = g_value_dup_string (value); + break; + case PROP_MASTER: + g_free (priv->master); + priv->master = g_value_dup_string (value); + break; + case PROP_SLAVE_TYPE: + g_free (priv->slave_type); + priv->slave_type = g_value_dup_string (value); + break; + case PROP_SECONDARIES: + g_slist_free_full (priv->secondaries, g_free); + priv->secondaries = g_value_dup_boxed (value); + break; + case PROP_GATEWAY_PING_TIMEOUT: + priv->gateway_ping_timeout = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GSList * +perm_permlist_to_stringlist (GSList *permlist) +{ + GSList *list = NULL, *iter; + + for (iter = permlist; iter; iter = g_slist_next (iter)) + list = g_slist_append (list, permission_to_string ((Permission *) iter->data)); + return list; +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSettingConnection *setting = NM_SETTING_CONNECTION (object); + NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting); + + switch (prop_id) { + case PROP_ID: + g_value_set_string (value, nm_setting_connection_get_id (setting)); + break; + case PROP_UUID: + g_value_set_string (value, nm_setting_connection_get_uuid (setting)); + break; + case PROP_INTERFACE_NAME: + g_value_set_string (value, nm_setting_connection_get_interface_name (setting)); + break; + case PROP_TYPE: + g_value_set_string (value, nm_setting_connection_get_connection_type (setting)); + break; + case PROP_PERMISSIONS: + g_value_take_boxed (value, perm_permlist_to_stringlist (priv->permissions)); + break; + case PROP_AUTOCONNECT: + g_value_set_boolean (value, nm_setting_connection_get_autoconnect (setting)); + break; + case PROP_TIMESTAMP: + g_value_set_uint64 (value, nm_setting_connection_get_timestamp (setting)); + break; + case PROP_READ_ONLY: + g_value_set_boolean (value, nm_setting_connection_get_read_only (setting)); + break; + case PROP_ZONE: + g_value_set_string (value, nm_setting_connection_get_zone (setting)); + break; + case PROP_MASTER: + g_value_set_string (value, nm_setting_connection_get_master (setting)); + break; + case PROP_SLAVE_TYPE: + g_value_set_string (value, nm_setting_connection_get_slave_type (setting)); + break; + case PROP_SECONDARIES: + g_value_set_boxed (value, priv->secondaries); + break; + case PROP_GATEWAY_PING_TIMEOUT: + g_value_set_uint (value, priv->gateway_ping_timeout); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_connection_class_init (NMSettingConnectionClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingConnectionPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + parent_class->compare_property = compare_property; + + /* Properties */ + + /** + * NMSettingConnection:id: + * + * A human readable unique identifier for the connection, like "Work Wi-Fi" + * or "T-Mobile 3G". + **/ + g_object_class_install_property + (object_class, PROP_ID, + g_param_spec_string (NM_SETTING_CONNECTION_ID, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:uuid: + * + * 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 + * #NMSettingConnection:id property or #NMSettingIP4Config changes, but + * might need to be re-created when the Wi-Fi SSID, mobile broadband network + * provider, or #NMSettingConnection:type property changes. + * + * The UUID must be in the format "2815492f-7e56-435e-b2e9-246bd7cdc664" + * (ie, contains only hexadecimal characters and "-"). A suitable UUID may + * be generated by nm_utils_uuid_generate() or + * nm_utils_uuid_generate_from_string(). + **/ + g_object_class_install_property + (object_class, PROP_UUID, + g_param_spec_string (NM_SETTING_CONNECTION_UUID, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:interface-name: + * + * 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. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_INTERFACE_NAME, + g_param_spec_string (NM_SETTING_CONNECTION_INTERFACE_NAME, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:type: + * + * 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). + **/ + g_object_class_install_property + (object_class, PROP_TYPE, + g_param_spec_string (NM_SETTING_CONNECTION_TYPE, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:permissions: + * + * 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 a user is allowed to access this connection + * if and only if they are in this list. 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. + */ + g_object_class_install_property + (object_class, PROP_PERMISSIONS, + _nm_param_spec_specialized (NM_SETTING_CONNECTION_PERMISSIONS, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:autoconnect: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_AUTOCONNECT, + g_param_spec_boolean (NM_SETTING_CONNECTION_AUTOCONNECT, "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:timestamp: + * + * 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). + **/ + g_object_class_install_property + (object_class, PROP_TIMESTAMP, + g_param_spec_uint64 (NM_SETTING_CONNECTION_TIMESTAMP, "", "", + 0, G_MAXUINT64, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:read-only: + * + * %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. + **/ + g_object_class_install_property + (object_class, PROP_READ_ONLY, + g_param_spec_boolean (NM_SETTING_CONNECTION_READ_ONLY, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:zone: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_ZONE, + g_param_spec_string (NM_SETTING_CONNECTION_ZONE, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:master: + * + * Interface name of the master device or UUID of the master connection. + **/ + g_object_class_install_property + (object_class, PROP_MASTER, + g_param_spec_string (NM_SETTING_CONNECTION_MASTER, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:slave-type: + * + * Setting name of the device type of this slave's master connection (eg, + * %NM_SETTING_BOND_SETTING_NAME), or %NULL if this connection is not a + * slave. + **/ + g_object_class_install_property + (object_class, PROP_SLAVE_TYPE, + g_param_spec_string (NM_SETTING_CONNECTION_SLAVE_TYPE, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:secondaries: + * + * List of connection UUIDs that should be activated when the base + * connection itself is activated. Currently only VPN connections are + * supported. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_SECONDARIES, + _nm_param_spec_specialized (NM_SETTING_CONNECTION_SECONDARIES, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:gateway-ping-timeout: + * + * If greater than zero, delay success of IP addressing until either the + * timeout is reached, or an IP gateway replies to a ping. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_GATEWAY_PING_TIMEOUT, + g_param_spec_uint (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, "", "", + 0, 30, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-connection.h b/libnm-core/nm-setting-connection.h new file mode 100644 index 0000000000..143fa11d58 --- /dev/null +++ b/libnm-core/nm-setting-connection.h @@ -0,0 +1,151 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SETTING_CONNECTION_H +#define NM_SETTING_CONNECTION_H + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_CONNECTION (nm_setting_connection_get_type ()) +#define NM_SETTING_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_CONNECTION, NMSettingConnection)) +#define NM_SETTING_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_CONNECTION, NMSettingConnectionClass)) +#define NM_IS_SETTING_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_CONNECTION)) +#define NM_IS_SETTING_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_CONNECTION)) +#define NM_SETTING_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_CONNECTION, NMSettingConnectionClass)) + +#define NM_SETTING_CONNECTION_SETTING_NAME "connection" + +/** + * NMSettingConnectionError: + * @NM_SETTING_CONNECTION_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY: the property's value is + * invalid + * @NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY: a required property is not + * present + * @NM_SETTING_CONNECTION_ERROR_TYPE_SETTING_NOT_FOUND: the #NMSetting object + * referenced by the setting name contained in the + * #NMSettingConnection:type property was not present in the #NMConnection + * @NM_SETTING_CONNECTION_ERROR_IP_CONFIG_NOT_ALLOWED: ip configuration is not + * allowed to be present. + * + * Describes errors that may result from operations involving a + * #NMSettingConnection. + * + **/ +typedef enum +{ + NM_SETTING_CONNECTION_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ + NM_SETTING_CONNECTION_ERROR_TYPE_SETTING_NOT_FOUND, /*< nick=TypeSettingNotFound >*/ + NM_SETTING_CONNECTION_ERROR_IP_CONFIG_NOT_ALLOWED, /*< nick=IpConfigNotAllowed >*/ +} NMSettingConnectionError; + +#define NM_SETTING_CONNECTION_ERROR nm_setting_connection_error_quark () +GQuark nm_setting_connection_error_quark (void); + +#define NM_SETTING_CONNECTION_ID "id" +#define NM_SETTING_CONNECTION_UUID "uuid" +#define NM_SETTING_CONNECTION_INTERFACE_NAME "interface-name" +#define NM_SETTING_CONNECTION_TYPE "type" +#define NM_SETTING_CONNECTION_AUTOCONNECT "autoconnect" +#define NM_SETTING_CONNECTION_TIMESTAMP "timestamp" +#define NM_SETTING_CONNECTION_READ_ONLY "read-only" +#define NM_SETTING_CONNECTION_PERMISSIONS "permissions" +#define NM_SETTING_CONNECTION_ZONE "zone" +#define NM_SETTING_CONNECTION_MASTER "master" +#define NM_SETTING_CONNECTION_SLAVE_TYPE "slave-type" +#define NM_SETTING_CONNECTION_SECONDARIES "secondaries" +#define NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT "gateway-ping-timeout" + +/** + * NMSettingConnection: + * + * The NMSettingConnection struct contains only private data. + * It should only be accessed through the functions described below. + */ +typedef struct { + NMSetting parent; +} NMSettingConnection; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingConnectionClass; + +GType nm_setting_connection_get_type (void); + +NMSetting * nm_setting_connection_new (void); +const char *nm_setting_connection_get_id (NMSettingConnection *setting); +const char *nm_setting_connection_get_uuid (NMSettingConnection *setting); +NM_AVAILABLE_IN_0_9_10 +const char *nm_setting_connection_get_interface_name (NMSettingConnection *setting); +const char *nm_setting_connection_get_connection_type (NMSettingConnection *setting); +gboolean nm_setting_connection_get_autoconnect (NMSettingConnection *setting); +guint64 nm_setting_connection_get_timestamp (NMSettingConnection *setting); +gboolean nm_setting_connection_get_read_only (NMSettingConnection *setting); + +guint32 nm_setting_connection_get_num_permissions (NMSettingConnection *setting); +gboolean nm_setting_connection_get_permission (NMSettingConnection *setting, + guint32 idx, + const char **out_ptype, + const char **out_pitem, + const char **out_detail); +const char *nm_setting_connection_get_zone (NMSettingConnection *setting); +gboolean nm_setting_connection_permissions_user_allowed (NMSettingConnection *setting, const char *uname); +gboolean nm_setting_connection_add_permission (NMSettingConnection *setting, + const char *ptype, + const char *pitem, + const char *detail); +void nm_setting_connection_remove_permission (NMSettingConnection *setting, + guint32 idx); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_connection_remove_permission_by_value (NMSettingConnection *setting, + const char *ptype, + const char *pitem, + const char *detail); + +const char *nm_setting_connection_get_master (NMSettingConnection *setting); +gboolean nm_setting_connection_is_slave_type (NMSettingConnection *setting, + const char *type); +const char *nm_setting_connection_get_slave_type (NMSettingConnection *setting); + +guint32 nm_setting_connection_get_num_secondaries (NMSettingConnection *setting); +const char *nm_setting_connection_get_secondary (NMSettingConnection *setting, guint32 idx); +gboolean nm_setting_connection_add_secondary (NMSettingConnection *setting, const char *sec_uuid); +void nm_setting_connection_remove_secondary (NMSettingConnection *setting, guint32 idx); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_connection_remove_secondary_by_value (NMSettingConnection *setting, const char *sec_uuid); + +NM_AVAILABLE_IN_0_9_10 +guint32 nm_setting_connection_get_gateway_ping_timeout (NMSettingConnection *setting); + +G_END_DECLS + +#endif /* NM_SETTING_CONNECTION_H */ diff --git a/libnm-core/nm-setting-dcb.c b/libnm-core/nm-setting-dcb.c new file mode 100644 index 0000000000..4839f7fe75 --- /dev/null +++ b/libnm-core/nm-setting-dcb.c @@ -0,0 +1,1207 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2013 Red Hat, Inc. + */ + +#include <string.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "nm-setting-dcb.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-utils-private.h" +#include "nm-dbus-glib-types.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-dcb + * @short_description: Connection properties for Data Center Bridging + * @include: nm-setting-dcb.h + * + * The #NMSettingDcb object is a #NMSetting subclass that describes properties + * for enabling and using Data Center Bridging (DCB) on Ethernet networks. + * DCB is a set of protocols (including 802.1Qbb, 802.1Qaz, 802.1Qau, and + * 802.1AB) to eliminate packet loss in Ethernet networks and support the use + * of storage technologies like Fibre Channel over Ethernet (FCoE) and iSCSI. + * + * Since: 0.9.10 + **/ + +/** + * nm_setting_dcb_error_quark: + * + * Registers an error quark for #NMSettingDcb if necessary. + * + * Returns: the error quark used for #NMSettingDcb errors. + * + * Since: 0.9.10 + **/ +GQuark +nm_setting_dcb_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-dcb-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingDcb, nm_setting_dcb, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_DCB_SETTING_NAME, + g_define_type_id, + 2, + NM_SETTING_DCB_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_DCB) + +#define NM_SETTING_DCB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_DCB, NMSettingDcbPrivate)) + +typedef struct { + NMSettingDcbFlags app_fcoe_flags; + gint app_fcoe_priority; + const char * app_fcoe_mode; + + NMSettingDcbFlags app_iscsi_flags; + gint app_iscsi_priority; + + NMSettingDcbFlags app_fip_flags; + gint app_fip_priority; + + /* Priority Flow Control */ + NMSettingDcbFlags pfc_flags; + guint pfc[8]; + + /* Priority Groups */ + NMSettingDcbFlags priority_group_flags; + guint priority_group_id[8]; + guint priority_group_bandwidth[8]; + guint priority_bandwidth[8]; + guint priority_strict[8]; + guint priority_traffic_class[8]; +} NMSettingDcbPrivate; + +enum { + PROP_0, + PROP_APP_FCOE_FLAGS, + PROP_APP_FCOE_PRIORITY, + PROP_APP_FCOE_MODE, + + PROP_APP_ISCSI_FLAGS, + PROP_APP_ISCSI_PRIORITY, + + PROP_APP_FIP_FLAGS, + PROP_APP_FIP_PRIORITY, + + PROP_PFC_FLAGS, + PROP_PFC, + + PROP_PRIORITY_GROUP_FLAGS, + PROP_PRIORITY_GROUP_ID, + PROP_PRIORITY_GROUP_BANDWIDTH, + PROP_PRIORITY_BANDWIDTH, + PROP_PRIORITY_STRICT, + PROP_PRIORITY_TRAFFIC_CLASS, + + LAST_PROP +}; + +/** + * nm_setting_dcb_new: + * + * Creates a new #NMSettingDcb object with default values. + * + * Returns: (transfer full): the new empty #NMSettingDcb object + * + * Since: 0.9.10 + **/ +NMSetting * +nm_setting_dcb_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_DCB, NULL); +} + +/** + * nm_setting_dcb_get_app_fcoe_flags: + * @setting: the #NMSettingDcb + * + * Returns: the #NMSettingDcb:app-fcoe-flags property of the setting + * + * Since: 0.9.10 + **/ +NMSettingDcbFlags +nm_setting_dcb_get_app_fcoe_flags (NMSettingDcb *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + + return NM_SETTING_DCB_GET_PRIVATE (setting)->app_fcoe_flags; +} + +/** + * nm_setting_dcb_get_app_fcoe_priority: + * @setting: the #NMSettingDcb + * + * Returns: the #NMSettingDcb:app-fcoe-priority property of the setting + * + * Since: 0.9.10 + **/ +gint +nm_setting_dcb_get_app_fcoe_priority (NMSettingDcb *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + + return NM_SETTING_DCB_GET_PRIVATE (setting)->app_fcoe_priority; +} + +/** + * nm_setting_dcb_get_app_fcoe_mode: + * @setting: the #NMSettingDcb + * + * Returns: the #NMSettingDcb:app-fcoe-mode property of the setting + * + * Since: 0.9.10 + **/ +const char * +nm_setting_dcb_get_app_fcoe_mode (NMSettingDcb *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), NULL); + + return NM_SETTING_DCB_GET_PRIVATE (setting)->app_fcoe_mode; +} + +/** + * nm_setting_dcb_get_app_iscsi_flags: + * @setting: the #NMSettingDcb + * + * Returns: the #NMSettingDcb:app-iscsi-flags property of the setting + * + * Since: 0.9.10 + **/ +NMSettingDcbFlags +nm_setting_dcb_get_app_iscsi_flags (NMSettingDcb *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + + return NM_SETTING_DCB_GET_PRIVATE (setting)->app_iscsi_flags; +} + +/** + * nm_setting_dcb_get_app_iscsi_priority: + * @setting: the #NMSettingDcb + * + * Returns: the #NMSettingDcb:app-iscsi-priority property of the setting + * + * Since: 0.9.10 + **/ +gint +nm_setting_dcb_get_app_iscsi_priority (NMSettingDcb *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + + return NM_SETTING_DCB_GET_PRIVATE (setting)->app_iscsi_priority; +} + +/** + * nm_setting_dcb_get_app_fip_flags: + * @setting: the #NMSettingDcb + * + * Returns: the #NMSettingDcb:app-fip-flags property of the setting + * + * Since: 0.9.10 + **/ +NMSettingDcbFlags +nm_setting_dcb_get_app_fip_flags (NMSettingDcb *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + + return NM_SETTING_DCB_GET_PRIVATE (setting)->app_fip_flags; +} + +/** + * nm_setting_dcb_get_app_fip_priority: + * @setting: the #NMSettingDcb + * + * Returns: the #NMSettingDcb:app-fip-priority property of the setting + * + * Since: 0.9.10 + **/ +gint +nm_setting_dcb_get_app_fip_priority (NMSettingDcb *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + + return NM_SETTING_DCB_GET_PRIVATE (setting)->app_fip_priority; +} + +/** + * nm_setting_dcb_get_priority_flow_control_flags: + * @setting: the #NMSettingDcb + * + * Returns: the #NMSettingDcb:priority-flow-control-flags property of the setting + * + * Since: 0.9.10 + **/ +NMSettingDcbFlags +nm_setting_dcb_get_priority_flow_control_flags (NMSettingDcb *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + + return NM_SETTING_DCB_GET_PRIVATE (setting)->pfc_flags; +} + +/** + * nm_setting_dcb_get_priority_flow_control: + * @setting: the #NMSettingDcb + * @user_priority: the User Priority (0 - 7) to retrieve flow control for + * + * Returns: %TRUE if flow control is enabled for the given @user_priority, + * %FALSE if not enabled + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_dcb_get_priority_flow_control (NMSettingDcb *setting, guint user_priority) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), FALSE); + g_return_val_if_fail (user_priority <= 7, FALSE); + + return !!NM_SETTING_DCB_GET_PRIVATE (setting)->pfc[user_priority]; +} + +/** + * nm_setting_dcb_set_priority_flow_control: + * @setting: the #NMSettingDcb + * @user_priority: the User Priority (0 - 7) to set flow control for + * @enabled: %TRUE to enable flow control for this priority, %FALSE to disable it + * + * These values are only valid when #NMSettingDcb:priority-flow-control includes + * the %NM_SETTING_DCB_FLAG_ENABLE flag. + * + * Since: 0.9.10 + **/ +void +nm_setting_dcb_set_priority_flow_control (NMSettingDcb *setting, + guint user_priority, + gboolean enabled) +{ + NMSettingDcbPrivate *priv; + guint uint_enabled = enabled ? 1 : 0; + + g_return_if_fail (NM_IS_SETTING_DCB (setting)); + g_return_if_fail (user_priority <= 7); + + priv = NM_SETTING_DCB_GET_PRIVATE (setting); + if (priv->pfc[user_priority] != uint_enabled) { + priv->pfc[user_priority] = uint_enabled; + g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_FLOW_CONTROL); + } +} + +/** + * nm_setting_dcb_get_priority_group_flags: + * @setting: the #NMSettingDcb + * + * Returns: the #NMSettingDcb:priority-group-flags property of the setting + * + * Since: 0.9.10 + **/ +NMSettingDcbFlags +nm_setting_dcb_get_priority_group_flags (NMSettingDcb *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + + return NM_SETTING_DCB_GET_PRIVATE (setting)->priority_group_flags; +} + +/** + * nm_setting_dcb_get_priority_group_id: + * @setting: the #NMSettingDcb + * @user_priority: the User Priority (0 - 7) to retrieve the group ID for + * + * Returns: the group number @user_priority is assigned to. These values are + * only valid when #NMSettingDcb:priority-group-flags includes the + * %NM_SETTING_DCB_FLAG_ENABLE flag. + * + * Since: 0.9.10 + **/ +guint +nm_setting_dcb_get_priority_group_id (NMSettingDcb *setting, guint user_priority) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + g_return_val_if_fail (user_priority <= 7, 0); + + return NM_SETTING_DCB_GET_PRIVATE (setting)->priority_group_id[user_priority]; +} + +/** + * nm_setting_dcb_set_priority_group_id: + * @setting: the #NMSettingDcb + * @user_priority: the User Priority (0 - 7) to set flow control for + * @group_id: the group (0 - 7) to assign @user_priority to, or 15 for the + * unrestricted group. + * + * These values are only valid when #NMSettingDcb:priority-group-flags includes + * the %NM_SETTING_DCB_FLAG_ENABLE flag. + * + * Since: 0.9.10 + **/ +void +nm_setting_dcb_set_priority_group_id (NMSettingDcb *setting, + guint user_priority, + guint group_id) +{ + NMSettingDcbPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_DCB (setting)); + g_return_if_fail (user_priority <= 7); + g_return_if_fail (group_id <= 7 || group_id == 15); + + priv = NM_SETTING_DCB_GET_PRIVATE (setting); + if (priv->priority_group_id[user_priority] != group_id) { + priv->priority_group_id[user_priority] = group_id; + g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_GROUP_ID); + } +} + +/** + * nm_setting_dcb_get_priority_group_bandwidth: + * @setting: the #NMSettingDcb + * @group_id: the priority group (0 - 7) to retrieve the bandwidth percentage for + * + * Returns: the bandwidth percentage assigned to @group_id. These values are + * only valid when #NMSettingDcb:priority-group-flags includes the + * %NM_SETTING_DCB_FLAG_ENABLE flag. + * + * Since: 0.9.10 + **/ +guint +nm_setting_dcb_get_priority_group_bandwidth (NMSettingDcb *setting, guint group_id) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + g_return_val_if_fail (group_id <= 7, FALSE); + + return NM_SETTING_DCB_GET_PRIVATE (setting)->priority_group_bandwidth[group_id]; +} + +/** + * nm_setting_dcb_set_priority_group_bandwidth: + * @setting: the #NMSettingDcb + * @group_id: the priority group (0 - 7) to set the bandwidth percentage for + * @bandwidth_percent: the bandwidth percentage (0 - 100) to assign to @group_id to + * + * These values are only valid when #NMSettingDcb:priority-group-flags includes + * the %NM_SETTING_DCB_FLAG_ENABLE flag. + * + * Since: 0.9.10 + **/ +void +nm_setting_dcb_set_priority_group_bandwidth (NMSettingDcb *setting, + guint group_id, + guint bandwidth_percent) +{ + NMSettingDcbPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_DCB (setting)); + g_return_if_fail (group_id <= 7); + g_return_if_fail (bandwidth_percent <= 100); + + priv = NM_SETTING_DCB_GET_PRIVATE (setting); + if (priv->priority_group_bandwidth[group_id] != bandwidth_percent) { + priv->priority_group_bandwidth[group_id] = bandwidth_percent; + g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH); + } +} + +/** + * nm_setting_dcb_get_priority_bandwidth: + * @setting: the #NMSettingDcb + * @user_priority: the User Priority (0 - 7) to retrieve the group bandwidth percentage for + * + * Returns: the allowed bandwidth percentage of @user_priority in its priority group. + * These values are only valid when #NMSettingDcb:priority-group-flags includes the + * %NM_SETTING_DCB_FLAG_ENABLE flag. + * + * Since: 0.9.10 + **/ +guint +nm_setting_dcb_get_priority_bandwidth (NMSettingDcb *setting, guint user_priority) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + g_return_val_if_fail (user_priority <= 7, FALSE); + + return NM_SETTING_DCB_GET_PRIVATE (setting)->priority_bandwidth[user_priority]; +} + +/** + * nm_setting_dcb_set_priority_bandwidth: + * @setting: the #NMSettingDcb + * @user_priority: the User Priority (0 - 7) to set the bandwidth percentage for + * @bandwidth_percent: the bandwidth percentage (0 - 100) that @user_priority is + * allowed to use within its priority group + * + * These values are only valid when #NMSettingDcb:priority-group-flags includes + * the %NM_SETTING_DCB_FLAG_ENABLE flag. + * + * Since: 0.9.10 + **/ +void +nm_setting_dcb_set_priority_bandwidth (NMSettingDcb *setting, + guint user_priority, + guint bandwidth_percent) +{ + NMSettingDcbPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_DCB (setting)); + g_return_if_fail (user_priority <= 7); + g_return_if_fail (bandwidth_percent <= 100); + + priv = NM_SETTING_DCB_GET_PRIVATE (setting); + if (priv->priority_bandwidth[user_priority] != bandwidth_percent) { + priv->priority_bandwidth[user_priority] = bandwidth_percent; + g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_BANDWIDTH); + } +} + +/** + * nm_setting_dcb_get_priority_strict_bandwidth: + * @setting: the #NMSettingDcb + * @user_priority: the User Priority (0 - 7) to retrieve strict bandwidth for + * + * Returns: %TRUE if @user_priority may use all of the bandwidth allocated to its + * assigned group, or %FALSE if not. These values are only valid when + * #NMSettingDcb:priority-group-flags includes the %NM_SETTING_DCB_FLAG_ENABLE flag. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_dcb_get_priority_strict_bandwidth (NMSettingDcb *setting, guint user_priority) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + g_return_val_if_fail (user_priority <= 7, FALSE); + + return !!NM_SETTING_DCB_GET_PRIVATE (setting)->priority_strict[user_priority]; +} + +/** + * nm_setting_dcb_set_priority_strict_bandwidth: + * @setting: the #NMSettingDcb + * @user_priority: the User Priority (0 - 7) to set strict bandwidth for + * @strict: %TRUE to allow @user_priority to use all the bandwidth allocated to + * its priority group, or %FALSE if not + * + * These values are only valid when #NMSettingDcb:priority-group-flags includes + * the %NM_SETTING_DCB_FLAG_ENABLE flag. + * + * Since: 0.9.10 + **/ +void +nm_setting_dcb_set_priority_strict_bandwidth (NMSettingDcb *setting, + guint user_priority, + gboolean strict) +{ + NMSettingDcbPrivate *priv; + guint uint_strict = strict ? 1 : 0; + + g_return_if_fail (NM_IS_SETTING_DCB (setting)); + g_return_if_fail (user_priority <= 7); + + priv = NM_SETTING_DCB_GET_PRIVATE (setting); + if (priv->priority_strict[user_priority] != uint_strict) { + priv->priority_strict[user_priority] = uint_strict; + g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH); + } +} + +/** + * nm_setting_dcb_get_priority_traffic_class: + * @setting: the #NMSettingDcb + * @user_priority: the User Priority (0 - 7) to retrieve the traffic class for + * + * Returns: the traffic class assigned to @user_priority. These values are only + * valid when #NMSettingDcb:priority-group-flags includes the + * %NM_SETTING_DCB_FLAG_ENABLE flag. + * + * Since: 0.9.10 + **/ +guint +nm_setting_dcb_get_priority_traffic_class (NMSettingDcb *setting, guint user_priority) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + g_return_val_if_fail (user_priority <= 7, FALSE); + + return NM_SETTING_DCB_GET_PRIVATE (setting)->priority_traffic_class[user_priority]; +} + +/** + * nm_setting_dcb_set_priority_traffic_clas: + * @setting: the #NMSettingDcb + * @user_priority: the User Priority (0 - 7) to set the bandwidth percentage for + * @traffic_class: the traffic_class (0 - 7) that @user_priority should map to + * + * These values are only valid when #NMSettingDcb:priority-group-flags includes + * the %NM_SETTING_DCB_FLAG_ENABLE flag. + * + * Since: 0.9.10 + **/ +void +nm_setting_dcb_set_priority_traffic_class (NMSettingDcb *setting, + guint user_priority, + guint traffic_class) +{ + NMSettingDcbPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_DCB (setting)); + g_return_if_fail (user_priority <= 7); + g_return_if_fail (traffic_class <= 7); + + priv = NM_SETTING_DCB_GET_PRIVATE (setting); + if (priv->priority_traffic_class[user_priority] != traffic_class) { + priv->priority_traffic_class[user_priority] = traffic_class; + g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS); + } +} + +/******************************************************************/ + +#define DCB_FLAGS_ALL (NM_SETTING_DCB_FLAG_ENABLE | \ + NM_SETTING_DCB_FLAG_ADVERTISE | \ + NM_SETTING_DCB_FLAG_WILLING) + +static gboolean +check_dcb_flags (NMSettingDcbFlags flags, const char *prop_name, GError **error) +{ + if (flags & ~DCB_FLAGS_ALL) { + g_set_error_literal (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, + _("flags invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, prop_name); + return FALSE; + } + + if (!(flags & NM_SETTING_DCB_FLAG_ENABLE) && (flags & ~NM_SETTING_DCB_FLAG_ENABLE)) { + g_set_error_literal (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, + _("flags invalid - disabled")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, prop_name); + return FALSE; + } + + return TRUE; +} + +static gboolean +check_uint_array (const guint *array, + guint len, + NMSettingDcbFlags flags, + guint max, + guint extra, + gboolean sum_pct, + const char *prop_name, + GError **error) +{ + guint i, sum = 0; + + /* Ensure each element is <= to max or equals extra */ + for (i = 0; i < len; i++) { + if (!(flags & NM_SETTING_DCB_FLAG_ENABLE) && array[i]) { + g_set_error_literal (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, + _("property invalid (not enabled)")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, prop_name); + return FALSE; + } + + if ((array[i] > max) && (array[i] != extra)) { + g_set_error_literal (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, + _("element invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, prop_name); + return FALSE; + } + sum += array[i]; + } + + /* Verify sum of percentages */ + if (sum_pct) { + if (flags & NM_SETTING_DCB_FLAG_ENABLE) { + /* If the feature is enabled, sum must equal 100% */ + if (sum != 100) { + g_set_error_literal (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, + _("sum not 100%")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, prop_name); + return FALSE; + } + } else { + /* If the feature is disabled, sum must equal 0%, which was checked + * by the for() loop above. + */ + g_assert_cmpint (sum, ==, 0); + } + } + + return TRUE; +} + +static gboolean +check_priority (gint val, + NMSettingDcbFlags flags, + const char *prop_name, + GError **error) +{ + if (!(flags & NM_SETTING_DCB_FLAG_ENABLE) && (val >= 0)) { + g_set_error_literal (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, + _("property invalid (not enabled)")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, prop_name); + return FALSE; + } + + if (val < -1 || val > 7) { + g_set_error_literal (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, + _("property invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, prop_name); + return FALSE; + } + return TRUE; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting); + + if (!check_dcb_flags (priv->app_fcoe_flags, NM_SETTING_DCB_APP_FCOE_FLAGS, error)) + return FALSE; + + if (!check_priority (priv->app_fcoe_priority, priv->app_fcoe_flags, NM_SETTING_DCB_APP_FCOE_PRIORITY, error)) + return FALSE; + + if (!priv->app_fcoe_mode) { + g_set_error_literal (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_MISSING_PROPERTY, + _("property missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, NM_SETTING_DCB_APP_FCOE_MODE); + return FALSE; + } + + if (strcmp (priv->app_fcoe_mode, NM_SETTING_DCB_FCOE_MODE_FABRIC) && + strcmp (priv->app_fcoe_mode, NM_SETTING_DCB_FCOE_MODE_VN2VN)) { + g_set_error_literal (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, + _("property invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, NM_SETTING_DCB_APP_FCOE_MODE); + return FALSE; + } + + if (!check_dcb_flags (priv->app_iscsi_flags, NM_SETTING_DCB_APP_ISCSI_FLAGS, error)) + return FALSE; + + if (!check_priority (priv->app_iscsi_priority, priv->app_iscsi_flags, NM_SETTING_DCB_APP_ISCSI_PRIORITY, error)) + return FALSE; + + if (!check_dcb_flags (priv->app_fip_flags, NM_SETTING_DCB_APP_FIP_FLAGS, error)) + return FALSE; + + if (!check_priority (priv->app_fip_priority, priv->app_fip_flags, NM_SETTING_DCB_APP_FIP_PRIORITY, error)) + return FALSE; + + if (!check_dcb_flags (priv->pfc_flags, NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, error)) + return FALSE; + + if (!check_uint_array (priv->pfc, G_N_ELEMENTS (priv->pfc), priv->pfc_flags, 1, 0, FALSE, NM_SETTING_DCB_PRIORITY_FLOW_CONTROL, error)) + return FALSE; + + if (!check_dcb_flags (priv->priority_group_flags, NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, error)) + return FALSE; + + if (!check_uint_array (priv->priority_group_id, + G_N_ELEMENTS (priv->priority_group_id), + priv->priority_group_flags, + 7, + 15, + FALSE, + NM_SETTING_DCB_PRIORITY_GROUP_ID, + error)) + return FALSE; + + if (!check_uint_array (priv->priority_group_bandwidth, + G_N_ELEMENTS (priv->priority_group_bandwidth), + priv->priority_group_flags, + 100, + 0, + TRUE, + NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH, + error)) + return FALSE; + + /* FIXME: sum bandwidths in each group */ + if (!check_uint_array (priv->priority_bandwidth, + G_N_ELEMENTS (priv->priority_bandwidth), + priv->priority_group_flags, + 100, + 0, + FALSE, + NM_SETTING_DCB_PRIORITY_BANDWIDTH, + error)) + return FALSE; + + if (!check_uint_array (priv->priority_strict, + G_N_ELEMENTS (priv->priority_strict), + priv->priority_group_flags, + 1, + 0, + FALSE, + NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH, + error)) + return FALSE; + + if (!check_uint_array (priv->priority_traffic_class, + G_N_ELEMENTS (priv->priority_traffic_class), + priv->priority_group_flags, + 7, + 0, + FALSE, + NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS, + error)) + return FALSE; + + return TRUE; +} + +/******************************************************************/ + +static void +nm_setting_dcb_init (NMSettingDcb *setting) +{ +} + +static inline void +set_uint_array (const GValue *v, uint *a, size_t len) +{ + GArray *src = g_value_get_boxed (v); + const guint total_len = len * sizeof (a[0]); + + memset (a, 0, total_len); + if (src) { + g_return_if_fail (g_array_get_element_size (src) == sizeof (a[0])); + g_return_if_fail (src->len == len); + memcpy (a, src->data, total_len); + } +} +#define SET_UINT_ARRAY(v, a) set_uint_array (v, a, G_N_ELEMENTS (a)) + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_APP_FCOE_FLAGS: + priv->app_fcoe_flags = g_value_get_uint (value); + break; + case PROP_APP_FCOE_PRIORITY: + priv->app_fcoe_priority = g_value_get_int (value); + break; + case PROP_APP_FCOE_MODE: + priv->app_fcoe_mode = g_value_dup_string (value); + break; + case PROP_APP_ISCSI_FLAGS: + priv->app_iscsi_flags = g_value_get_uint (value); + break; + case PROP_APP_ISCSI_PRIORITY: + priv->app_iscsi_priority = g_value_get_int (value); + break; + case PROP_APP_FIP_FLAGS: + priv->app_fip_flags = g_value_get_uint (value); + break; + case PROP_APP_FIP_PRIORITY: + priv->app_fip_priority = g_value_get_int (value); + break; + case PROP_PFC_FLAGS: + priv->pfc_flags = g_value_get_uint (value); + break; + case PROP_PFC: + SET_UINT_ARRAY (value, priv->pfc); + break; + case PROP_PRIORITY_GROUP_FLAGS: + priv->priority_group_flags = g_value_get_uint (value); + break; + case PROP_PRIORITY_GROUP_ID: + SET_UINT_ARRAY (value, priv->priority_group_id); + break; + case PROP_PRIORITY_GROUP_BANDWIDTH: + SET_UINT_ARRAY (value, priv->priority_group_bandwidth); + break; + case PROP_PRIORITY_BANDWIDTH: + SET_UINT_ARRAY (value, priv->priority_bandwidth); + break; + case PROP_PRIORITY_STRICT: + SET_UINT_ARRAY (value, priv->priority_strict); + break; + case PROP_PRIORITY_TRAFFIC_CLASS: + SET_UINT_ARRAY (value, priv->priority_traffic_class); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +#define TAKE_UINT_ARRAY(v, a) \ +{ \ + guint len = G_N_ELEMENTS (a); \ + GArray *dst = g_array_sized_new (FALSE, TRUE, sizeof (guint), len); \ + g_array_append_vals (dst, (a), len); \ + g_value_take_boxed (v, dst); \ +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSettingDcb *setting = NM_SETTING_DCB (object); + NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting); + + switch (prop_id) { + case PROP_APP_FCOE_FLAGS: + g_value_set_uint (value, priv->app_fcoe_flags); + break; + case PROP_APP_FCOE_PRIORITY: + g_value_set_int (value, priv->app_fcoe_priority); + break; + case PROP_APP_FCOE_MODE: + g_value_set_string (value, priv->app_fcoe_mode); + break; + case PROP_APP_ISCSI_FLAGS: + g_value_set_uint (value, priv->app_iscsi_flags); + break; + case PROP_APP_ISCSI_PRIORITY: + g_value_set_int (value, priv->app_iscsi_priority); + break; + case PROP_APP_FIP_FLAGS: + g_value_set_uint (value, priv->app_fip_flags); + break; + case PROP_APP_FIP_PRIORITY: + g_value_set_int (value, priv->app_fip_priority); + break; + case PROP_PFC_FLAGS: + g_value_set_uint (value, priv->pfc_flags); + break; + case PROP_PFC: + TAKE_UINT_ARRAY (value, priv->pfc); + break; + case PROP_PRIORITY_GROUP_FLAGS: + g_value_set_uint (value, priv->priority_group_flags); + break; + case PROP_PRIORITY_GROUP_ID: + TAKE_UINT_ARRAY (value, priv->priority_group_id); + break; + case PROP_PRIORITY_GROUP_BANDWIDTH: + TAKE_UINT_ARRAY (value, priv->priority_group_bandwidth); + break; + case PROP_PRIORITY_BANDWIDTH: + TAKE_UINT_ARRAY (value, priv->priority_bandwidth); + break; + case PROP_PRIORITY_STRICT: + TAKE_UINT_ARRAY (value, priv->priority_strict); + break; + case PROP_PRIORITY_TRAFFIC_CLASS: + TAKE_UINT_ARRAY (value, priv->priority_traffic_class); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_dcb_class_init (NMSettingDcbClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingDcbPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + parent_class->verify = verify; + + /* Properties */ + /** + * NMSettingDcb:app-fcoe-flags: + * + * Specifies the #NMSettingDcbFlags for the DCB FCoE application. Flags may + * be any combination of %NM_SETTING_DCB_FLAG_ENABLE, + * %NM_SETTING_DCB_FLAG_ADVERTISE, and %NM_SETTING_DCB_FLAG_WILLING. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_APP_FCOE_FLAGS, + g_param_spec_uint (NM_SETTING_DCB_APP_FCOE_FLAGS, "", "", + 0, DCB_FLAGS_ALL, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingDcb:app-fcoe-priority: + * + * The highest User Priority (0 - 7) which FCoE frames should use, or -1 for + * default priority. Only used when the #NMSettingDcb:app-fcoe-flags + * property includes the %NM_SETTING_DCB_FLAG_ENABLE flag. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_APP_FCOE_PRIORITY, + g_param_spec_int (NM_SETTING_DCB_APP_FCOE_PRIORITY, "", "", + -1, 7, -1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingDcb:app-fcoe-mode: + * + * The FCoE controller mode; either %NM_SETTING_DCB_FCOE_MODE_FABRIC + * (default) or %NM_SETTING_DCB_FCOE_MODE_VN2VN. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_APP_FCOE_MODE, + g_param_spec_string (NM_SETTING_DCB_APP_FCOE_MODE, "", "", + NM_SETTING_DCB_FCOE_MODE_FABRIC, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingDcb:app-iscsi-flags: + * + * Specifies the #NMSettingDcbFlags for the DCB iSCSI application. Flags + * may be any combination of %NM_SETTING_DCB_FLAG_ENABLE, + * %NM_SETTING_DCB_FLAG_ADVERTISE, and %NM_SETTING_DCB_FLAG_WILLING. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_APP_ISCSI_FLAGS, + g_param_spec_uint (NM_SETTING_DCB_APP_ISCSI_FLAGS, "", "", + 0, DCB_FLAGS_ALL, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingDcb:app-iscsi-priority: + * + * The highest User Priority (0 - 7) which iSCSI frames should use, or -1 + * for default priority. Only used when the #NMSettingDcb:app-iscsi-flags + * property includes the %NM_SETTING_DCB_FLAG_ENABLE flag. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_APP_ISCSI_PRIORITY, + g_param_spec_int (NM_SETTING_DCB_APP_ISCSI_PRIORITY, "", "", + -1, 7, -1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingDcb:app-fip-flags: + * + * Specifies the #NMSettingDcbFlags for the DCB FIP application. Flags may + * be any combination of %NM_SETTING_DCB_FLAG_ENABLE, + * %NM_SETTING_DCB_FLAG_ADVERTISE, and %NM_SETTING_DCB_FLAG_WILLING. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_APP_FIP_FLAGS, + g_param_spec_uint (NM_SETTING_DCB_APP_FIP_FLAGS, "", "", + 0, DCB_FLAGS_ALL, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingDcb:app-fip-priority: + * + * The highest User Priority (0 - 7) which FIP frames should use, or -1 for + * default priority. Only used when the #NMSettingDcb:app-fip-flags + * property includes the %NM_SETTING_DCB_FLAG_ENABLE flag. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_APP_FIP_PRIORITY, + g_param_spec_int (NM_SETTING_DCB_APP_FIP_PRIORITY, "", "", + -1, 7, -1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingDcb:priority-flow-control-flags: + * + * Specifies the #NMSettingDcbFlags for DCB Priority Flow Control (PFC). + * Flags may be any combination of %NM_SETTING_DCB_FLAG_ENABLE, + * %NM_SETTING_DCB_FLAG_ADVERTISE, and %NM_SETTING_DCB_FLAG_WILLING. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_PFC_FLAGS, + g_param_spec_uint (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, "", "", + 0, DCB_FLAGS_ALL, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingDcb:priority-flow-control: + * + * An array of 8 uint 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. Allowed values are 0 (do not + * transmit pause) and 1 (transmit pause). + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_PFC, + _nm_param_spec_specialized (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL, "", "", + DBUS_TYPE_G_UINT_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingDcb:priority-group-flags: + * + * Specifies the #NMSettingDcbFlags for DCB Priority Groups. Flags may be + * any combination of %NM_SETTING_DCB_FLAG_ENABLE, + * %NM_SETTING_DCB_FLAG_ADVERTISE, and %NM_SETTING_DCB_FLAG_WILLING. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_PRIORITY_GROUP_FLAGS, + g_param_spec_uint (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, "", "", + 0, DCB_FLAGS_ALL, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingDcb:priority-group-id: + * + * 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. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_PRIORITY_GROUP_ID, + _nm_param_spec_specialized (NM_SETTING_DCB_PRIORITY_GROUP_ID, "", "", + DBUS_TYPE_G_UINT_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingDcb:priority-group-bandwidth: + * + * 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 percent. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_PRIORITY_GROUP_BANDWIDTH, + _nm_param_spec_specialized (NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH, "", "", + DBUS_TYPE_G_UINT_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingDcb:priority-bandwidth: + * + * 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 + * percent. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_PRIORITY_BANDWIDTH, + _nm_param_spec_specialized (NM_SETTING_DCB_PRIORITY_BANDWIDTH, "", "", + DBUS_TYPE_G_UINT_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingDcb:priority-strict-bandwidth: + * + * An array of 8 uint 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. Allowed values + * are 0 (the priority may not utilize all bandwidth) or 1 (the priority may + * utilize all bandwidth). + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_PRIORITY_STRICT, + _nm_param_spec_specialized (NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH, "", "", + DBUS_TYPE_G_UINT_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingDcb:priority-traffic-class: + * + * 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. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_PRIORITY_TRAFFIC_CLASS, + _nm_param_spec_specialized (NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS, "", "", + DBUS_TYPE_G_UINT_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-dcb.h b/libnm-core/nm-setting-dcb.h new file mode 100644 index 0000000000..ac34521e0d --- /dev/null +++ b/libnm-core/nm-setting-dcb.h @@ -0,0 +1,187 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NM_SETTING_DCB_H +#define NM_SETTING_DCB_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_DCB (nm_setting_dcb_get_type ()) +#define NM_SETTING_DCB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_DCB, NMSettingDcb)) +#define NM_SETTING_DCB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_DCB, NMSettingDcbClass)) +#define NM_IS_SETTING_DCB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_DCB)) +#define NM_IS_SETTING_DCB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_DCB)) +#define NM_SETTING_DCB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_DCB, NMSettingDcbClass)) + +#define NM_SETTING_DCB_SETTING_NAME "dcb" + +/** + * NMSettingDcbError: + * @NM_SETTING_DCB_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_DCB_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_DCB_ERROR_MISSING_PROPERTY: the property was missing and is + * required + */ +typedef enum { + NM_SETTING_DCB_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_DCB_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/ +} NMSettingDcbError; + +#define NM_SETTING_DCB_ERROR nm_setting_dcb_error_quark () +GQuark nm_setting_dcb_error_quark (void); + +/** + * NMSettingDcbFlags: + * @NM_SETTING_DCB_FLAG_NONE: no flag + * @NM_SETTING_DCB_FLAG_ENABLE: the feature is enabled + * @NM_SETTING_DCB_FLAG_ADVERTISE: the feature is advertised + * @NM_SETTING_DCB_FLAG_WILLING: the feature is willing to change based on + * peer configuration advertisements + * + * DCB feature flags. + * + * Since: 0.9.10 + **/ +typedef enum { + NM_SETTING_DCB_FLAG_NONE = 0x00000000, + NM_SETTING_DCB_FLAG_ENABLE = 0x00000001, + NM_SETTING_DCB_FLAG_ADVERTISE = 0x00000002, + NM_SETTING_DCB_FLAG_WILLING = 0x00000004 +} NMSettingDcbFlags; + +/** + * NM_SETTING_DCB_FCOE_MODE_FABRIC: + * + * Indicates that the FCoE controller should use "fabric" mode (default) + * + * Since: 0.9.10 + */ +#define NM_SETTING_DCB_FCOE_MODE_FABRIC "fabric" + +/** + * NM_SETTING_DCB_FCOE_MODE_VN2VN: + * + * Indicates that the FCoE controller should use "VN2VN" mode. + * + * Since: 0.9.10 + */ +#define NM_SETTING_DCB_FCOE_MODE_VN2VN "vn2vn" + + +/* Properties */ +#define NM_SETTING_DCB_APP_FCOE_FLAGS "app-fcoe-flags" +#define NM_SETTING_DCB_APP_FCOE_PRIORITY "app-fcoe-priority" +#define NM_SETTING_DCB_APP_FCOE_MODE "app-fcoe-mode" + +#define NM_SETTING_DCB_APP_ISCSI_FLAGS "app-iscsi-flags" +#define NM_SETTING_DCB_APP_ISCSI_PRIORITY "app-iscsi-priority" + +#define NM_SETTING_DCB_APP_FIP_FLAGS "app-fip-flags" +#define NM_SETTING_DCB_APP_FIP_PRIORITY "app-fip-priority" + +#define NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS "priority-flow-control-flags" +#define NM_SETTING_DCB_PRIORITY_FLOW_CONTROL "priority-flow-control" + +#define NM_SETTING_DCB_PRIORITY_GROUP_FLAGS "priority-group-flags" +#define NM_SETTING_DCB_PRIORITY_GROUP_ID "priority-group-id" +#define NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH "priority-group-bandwidth" +#define NM_SETTING_DCB_PRIORITY_BANDWIDTH "priority-bandwidth" +#define NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH "priority-strict-bandwidth" +#define NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS "priority-traffic-class" + + +typedef struct { + NMSetting parent; +} NMSettingDcb; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingDcbClass; + +NM_AVAILABLE_IN_0_9_10 +GType nm_setting_dcb_get_type (void); + +NM_AVAILABLE_IN_0_9_10 +NMSetting * nm_setting_dcb_new (void); + +NMSettingDcbFlags nm_setting_dcb_get_app_fcoe_flags (NMSettingDcb *setting); +gint nm_setting_dcb_get_app_fcoe_priority (NMSettingDcb *setting); +const char * nm_setting_dcb_get_app_fcoe_mode (NMSettingDcb *setting); + +NMSettingDcbFlags nm_setting_dcb_get_app_iscsi_flags (NMSettingDcb *setting); +gint nm_setting_dcb_get_app_iscsi_priority (NMSettingDcb *setting); + +NMSettingDcbFlags nm_setting_dcb_get_app_fip_flags (NMSettingDcb *setting); +gint nm_setting_dcb_get_app_fip_priority (NMSettingDcb *setting); + +/* Priority Flow Control */ +NMSettingDcbFlags nm_setting_dcb_get_priority_flow_control_flags (NMSettingDcb *setting); +gboolean nm_setting_dcb_get_priority_flow_control (NMSettingDcb *setting, + guint user_priority); +void nm_setting_dcb_set_priority_flow_control (NMSettingDcb *setting, + guint user_priority, + gboolean enabled); + +/* Priority Groups */ +NMSettingDcbFlags nm_setting_dcb_get_priority_group_flags (NMSettingDcb *setting); + +guint nm_setting_dcb_get_priority_group_id (NMSettingDcb *setting, + guint user_priority); +void nm_setting_dcb_set_priority_group_id (NMSettingDcb *setting, + guint user_priority, + guint group_id); + +guint nm_setting_dcb_get_priority_group_bandwidth (NMSettingDcb *setting, + guint group_id); +void nm_setting_dcb_set_priority_group_bandwidth (NMSettingDcb *setting, + guint group_id, + guint bandwidth_percent); + +guint nm_setting_dcb_get_priority_bandwidth (NMSettingDcb *setting, + guint user_priority); +void nm_setting_dcb_set_priority_bandwidth (NMSettingDcb *setting, + guint user_priority, + guint bandwidth_percent); + +gboolean nm_setting_dcb_get_priority_strict_bandwidth (NMSettingDcb *setting, + guint user_priority); +void nm_setting_dcb_set_priority_strict_bandwidth (NMSettingDcb *setting, + guint user_priority, + gboolean strict); + +guint nm_setting_dcb_get_priority_traffic_class (NMSettingDcb *setting, + guint user_priority); +void nm_setting_dcb_set_priority_traffic_class (NMSettingDcb *setting, + guint user_priority, + guint traffic_class); + +G_END_DECLS + +#endif /* NM_SETTING_DCB_H */ diff --git a/libnm-core/nm-setting-generic.c b/libnm-core/nm-setting-generic.c new file mode 100644 index 0000000000..e32ae4971f --- /dev/null +++ b/libnm-core/nm-setting-generic.c @@ -0,0 +1,100 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2013 Red Hat, Inc. + */ + +#include "config.h" + +#include "nm-setting-generic.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-generic + * @short_description: Describes connection properties for generic devices + * @include: nm-setting-generic.h + * + * The #NMSettingGeneric object is a #NMSetting subclass that describes + * optional properties that apply to "generic" devices (ie, devices that + * NetworkManager does not specifically recognize). + * + * There are currently no properties on this object; it exists only to be + * the "connection type" setting on #NMConnections for generic devices. + * + * Since: 0.9.10 + **/ + +/** + * nm_setting_generic_error_quark: + * + * Registers an error quark for #NMSettingGeneric if necessary. + * + * Returns: the error quark used for #NMSettingGeneric errors. + * + * Since: 0.9.10 + **/ +GQuark +nm_setting_generic_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-generic-error-quark"); + return quark; +} + +G_DEFINE_TYPE_WITH_CODE (NMSettingGeneric, nm_setting_generic, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_GENERIC_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_GENERIC_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_GENERIC) + +#define NM_SETTING_GENERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_GENERIC, NMSettingGenericPrivate)) + +typedef struct { + int dummy; +} NMSettingGenericPrivate; + +/**************************************************************************/ + +/** + * nm_setting_generic_new: + * + * Creates a new #NMSettingGeneric object with default values. + * + * Returns: (transfer full): the new empty #NMSettingGeneric object + * + * Since: 0.9.10 + **/ +NMSetting * +nm_setting_generic_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_GENERIC, NULL); +} + +static void +nm_setting_generic_init (NMSettingGeneric *setting) +{ +} + +static void +nm_setting_generic_class_init (NMSettingGenericClass *setting_class) +{ + g_type_class_add_private (setting_class, sizeof (NMSettingGenericPrivate)); +} diff --git a/libnm-core/nm-setting-generic.h b/libnm-core/nm-setting-generic.h new file mode 100644 index 0000000000..1cabae088c --- /dev/null +++ b/libnm-core/nm-setting-generic.h @@ -0,0 +1,78 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NM_SETTING_GENERIC_H +#define NM_SETTING_GENERIC_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_GENERIC (nm_setting_generic_get_type ()) +#define NM_SETTING_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_GENERIC, NMSettingGeneric)) +#define NM_SETTING_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_GENERIC, NMSettingGenericClass)) +#define NM_IS_SETTING_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_GENERIC)) +#define NM_IS_SETTING_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_GENERIC)) +#define NM_SETTING_GENERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_GENERIC, NMSettingGenericClass)) + +#define NM_SETTING_GENERIC_SETTING_NAME "generic" + +/** + * NMSettingGenericError: + * @NM_SETTING_GENERIC_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_GENERIC_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_GENERIC_ERROR_MISSING_PROPERTY: the property was missing and + * is required + * + * Since: 0.9.10 + */ +typedef enum { + NM_SETTING_GENERIC_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_GENERIC_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_GENERIC_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ +} NMSettingGenericError; + +#define NM_SETTING_GENERIC_ERROR nm_setting_generic_error_quark () +GQuark nm_setting_generic_error_quark (void); + +typedef struct { + NMSetting parent; +} NMSettingGeneric; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingGenericClass; + +NM_AVAILABLE_IN_0_9_10 +GType nm_setting_generic_get_type (void); + +NM_AVAILABLE_IN_0_9_10 +NMSetting * nm_setting_generic_new (void); + +G_END_DECLS + +#endif /* NM_SETTING_GENERIC_H */ diff --git a/libnm-core/nm-setting-gsm.c b/libnm-core/nm-setting-gsm.c new file mode 100644 index 0000000000..51ad390249 --- /dev/null +++ b/libnm-core/nm-setting-gsm.c @@ -0,0 +1,723 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2013 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include <string.h> +#include <glib/gi18n.h> + +#include "nm-setting-gsm.h" +#include "nm-utils.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-gsm + * @short_description: Describes GSM/3GPP-based mobile broadband properties + * @include: nm-setting-gsm.h + * + * The #NMSettingGsm object is a #NMSetting subclass that describes + * properties that allow connections to 3GPP-based mobile broadband + * networks, including those using GPRS/EDGE and UMTS/HSPA technology. + */ + +/** + * nm_setting_gsm_error_quark: + * + * Registers an error quark for #NMSettingGsm if necessary. + * + * Returns: the error quark used for #NMSettingGsm errors. + **/ +GQuark +nm_setting_gsm_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-gsm-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingGsm, nm_setting_gsm, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_GSM_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_GSM_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_GSM) + +#define NM_SETTING_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_GSM, NMSettingGsmPrivate)) + +typedef struct { + char *number; /* For dialing, duh */ + char *username; + char *password; + NMSettingSecretFlags password_flags; + + char *apn; /* NULL for dynamic */ + char *network_id; /* for manual registration or NULL for automatic */ + int network_type; /* One of the NM_SETTING_GSM_NETWORK_TYPE_* */ + guint32 allowed_bands; /* A bitfield of NM_SETTING_GSM_BAND_* */ + + char *pin; + NMSettingSecretFlags pin_flags; + + gboolean home_only; +} NMSettingGsmPrivate; + +enum { + PROP_0, + PROP_NUMBER, + PROP_USERNAME, + PROP_PASSWORD, + PROP_PASSWORD_FLAGS, + PROP_APN, + PROP_NETWORK_ID, + PROP_NETWORK_TYPE, + PROP_PIN, + PROP_PIN_FLAGS, + PROP_ALLOWED_BANDS, + PROP_HOME_ONLY, + + LAST_PROP +}; + +/** + * nm_setting_gsm_new: + * + * Creates a new #NMSettingGsm object with default values. + * + * Returns: the new empty #NMSettingGsm object + **/ +NMSetting * +nm_setting_gsm_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_GSM, NULL); +} + +/** + * nm_setting_gsm_get_number: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingGsm:number property of the setting + **/ +const char * +nm_setting_gsm_get_number (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->number; +} + +/** + * nm_setting_gsm_get_username: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingGsm:username property of the setting + **/ +const char * +nm_setting_gsm_get_username (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->username; +} + +/** + * nm_setting_gsm_get_password: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingGsm:password property of the setting + **/ +const char * +nm_setting_gsm_get_password (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->password; +} + +/** + * nm_setting_gsm_get_password_flags: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingSecretFlags pertaining to the #NMSettingGsm:password + **/ +NMSettingSecretFlags +nm_setting_gsm_get_password_flags (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NM_SETTING_SECRET_FLAG_NONE); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->password_flags; +} + +/** + * nm_setting_gsm_get_apn: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingGsm:apn property of the setting + **/ +const char * +nm_setting_gsm_get_apn (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->apn; +} + +/** + * nm_setting_gsm_get_network_id: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingGsm:network-id property of the setting + **/ +const char * +nm_setting_gsm_get_network_id (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->network_id; +} + +/** + * nm_setting_gsm_get_network_type: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingGsm:network-type property of the setting + * + * Deprecated: 0.9.10: No longer used. Network type setting should be done talking to ModemManager directly. + **/ +int +nm_setting_gsm_get_network_type (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), -1); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->network_type; +} + +/** + * nm_setting_gsm_get_allowed_bands: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingGsm:allowed-bands property of the setting + * + * Deprecated: 0.9.10: No longer used. Bands setting should be done talking to ModemManager directly. + **/ +guint32 +nm_setting_gsm_get_allowed_bands (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NM_SETTING_GSM_BAND_UNKNOWN); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->allowed_bands; +} + +/** + * nm_setting_gsm_get_pin: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingGsm:pin property of the setting + **/ +const char * +nm_setting_gsm_get_pin (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->pin; +} + +/** + * nm_setting_gsm_get_pin_flags: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingSecretFlags pertaining to the #NMSettingGsm:pin + **/ +NMSettingSecretFlags +nm_setting_gsm_get_pin_flags (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NM_SETTING_SECRET_FLAG_NONE); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->pin_flags; +} + +/** + * nm_setting_gsm_get_home_only: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingGsm:home-only property of the setting + **/ +gboolean +nm_setting_gsm_get_home_only (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), FALSE); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->home_only; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (setting); + + if (priv->number && !priv->number[0]) { + g_set_error_literal (error, + NM_SETTING_GSM_ERROR, + NM_SETTING_GSM_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_NUMBER); + return FALSE; + } + + if (priv->apn) { + guint32 apn_len = strlen (priv->apn); + guint32 i; + + if (apn_len < 1 || apn_len > 64) { + g_set_error (error, + NM_SETTING_GSM_ERROR, + NM_SETTING_GSM_ERROR_INVALID_PROPERTY, + _("property value '%s' is empty or too long (>64)"), + priv->apn); + g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_APN); + return FALSE; + } + + /* APNs roughly follow the same rules as DNS domain names. Allowed + * characters are a-z, 0-9, . and -. GSM 03.03 Section 9.1 states: + * + * The syntax of the APN shall follow the Name Syntax defined in + * RFC 2181 [14] and RFC 1035 [15]. The APN consists of one or + * more labels. Each label is coded as one octet length field + * followed by that number of octets coded as 8 bit ASCII characters. + * Following RFC 1035 [15] the labels should consist only of the + * alphabetic characters (A-Z and a-z), digits (0-9) and the + * dash (-). The case of alphabetic characters is not significant. + * + * A dot (.) is commonly used to separate parts of the APN, and + * apparently the underscore (_) is used as well. RFC 2181 indicates + * that no restrictions of any kind are placed on DNS labels, and thus + * it would appear that none are placed on APNs either, but many modems + * and networks will fail to accept APNs that include odd characters + * like space ( ) and such. + */ + for (i = 0; i < apn_len; i++) { + if ( !g_ascii_isalnum (priv->apn[i]) + && (priv->apn[i] != '.') + && (priv->apn[i] != '_') + && (priv->apn[i] != '-')) { + g_set_error (error, + NM_SETTING_GSM_ERROR, + NM_SETTING_GSM_ERROR_INVALID_PROPERTY, + _("'%s' contains invalid char(s) (use [A-Za-z._-])"), + priv->apn); + g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_APN); + return FALSE; + } + } + } + + if (priv->username && !strlen (priv->username)) { + g_set_error_literal (error, + NM_SETTING_GSM_ERROR, + NM_SETTING_GSM_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_USERNAME); + return FALSE; + } + + if (priv->password && !strlen (priv->password)) { + g_set_error_literal (error, + NM_SETTING_GSM_ERROR, + NM_SETTING_GSM_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_USERNAME); + return FALSE; + } + + if (priv->network_id) { + guint32 nid_len = strlen (priv->network_id); + guint32 i; + + /* Accept both 5 and 6 digit MCC/MNC codes */ + if ((nid_len < 5) || (nid_len > 6)) { + g_set_error (error, + NM_SETTING_GSM_ERROR, + NM_SETTING_GSM_ERROR_INVALID_PROPERTY, + _("'%s' length is invalid (should be 5 or 6 digits)"), + priv->network_id); + g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_NETWORK_ID); + return FALSE; + } + + for (i = 0; i < nid_len; i++) { + if (!g_ascii_isdigit (priv->network_id[i])) { + g_set_error (error, + NM_SETTING_GSM_ERROR, + NM_SETTING_GSM_ERROR_INVALID_PROPERTY, + _("'%s' is not a number"), + priv->network_id); + g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_NETWORK_ID); + return FALSE; + } + } + } + + return TRUE; +} + +static GPtrArray * +need_secrets (NMSetting *setting) +{ + NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (setting); + GPtrArray *secrets = NULL; + + if (priv->password) + return NULL; + + if (priv->username) { + if (!(priv->password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { + secrets = g_ptr_array_sized_new (1); + g_ptr_array_add (secrets, NM_SETTING_GSM_PASSWORD); + } + } + + return secrets; +} + +static void +nm_setting_gsm_init (NMSettingGsm *setting) +{ +} + +static void +finalize (GObject *object) +{ + NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (object); + + g_free (priv->number); + g_free (priv->username); + g_free (priv->password); + g_free (priv->apn); + g_free (priv->network_id); + g_free (priv->pin); + + G_OBJECT_CLASS (nm_setting_gsm_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (object); + char *tmp; + + switch (prop_id) { + case PROP_NUMBER: + g_free (priv->number); + priv->number = g_value_dup_string (value); + break; + case PROP_USERNAME: + g_free (priv->username); + priv->username = g_value_dup_string (value); + break; + case PROP_PASSWORD: + g_free (priv->password); + priv->password = g_value_dup_string (value); + break; + case PROP_PASSWORD_FLAGS: + priv->password_flags = g_value_get_uint (value); + break; + case PROP_APN: + g_free (priv->apn); + priv->apn = NULL; + tmp = g_value_dup_string (value); + if (tmp) + priv->apn = g_strstrip (tmp); + break; + case PROP_NETWORK_ID: + g_free (priv->network_id); + priv->network_id = NULL; + tmp = g_value_dup_string (value); + if (tmp) + priv->network_id = g_strstrip (tmp); + break; + case PROP_NETWORK_TYPE: + priv->network_type = g_value_get_int (value); + break; + case PROP_ALLOWED_BANDS: + priv->allowed_bands = g_value_get_uint (value); + break; + case PROP_PIN: + g_free (priv->pin); + priv->pin = g_value_dup_string (value); + break; + case PROP_PIN_FLAGS: + priv->pin_flags = g_value_get_uint (value); + break; + case PROP_HOME_ONLY: + priv->home_only = g_value_get_boolean (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) +{ + NMSettingGsm *setting = NM_SETTING_GSM (object); + + switch (prop_id) { + case PROP_NUMBER: + g_value_set_string (value, nm_setting_gsm_get_number (setting)); + break; + case PROP_USERNAME: + g_value_set_string (value, nm_setting_gsm_get_username (setting)); + break; + case PROP_PASSWORD: + g_value_set_string (value, nm_setting_gsm_get_password (setting)); + break; + case PROP_PASSWORD_FLAGS: + g_value_set_uint (value, nm_setting_gsm_get_password_flags (setting)); + break; + case PROP_APN: + g_value_set_string (value, nm_setting_gsm_get_apn (setting)); + break; + case PROP_NETWORK_ID: + g_value_set_string (value, nm_setting_gsm_get_network_id (setting)); + break; + case PROP_NETWORK_TYPE: + g_value_set_int (value, NM_SETTING_GSM_GET_PRIVATE (setting)->network_type); + break; + case PROP_ALLOWED_BANDS: + g_value_set_uint (value, NM_SETTING_GSM_GET_PRIVATE (setting)->allowed_bands); + break; + case PROP_PIN: + g_value_set_string (value, nm_setting_gsm_get_pin (setting)); + break; + case PROP_PIN_FLAGS: + g_value_set_uint (value, nm_setting_gsm_get_pin_flags (setting)); + break; + case PROP_HOME_ONLY: + g_value_set_boolean (value, nm_setting_gsm_get_home_only (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_gsm_class_init (NMSettingGsmClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingGsmPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + parent_class->need_secrets = need_secrets; + + /* Properties */ + + /** + * NMSettingGsm:number: + * + * Number to dial when establishing a PPP data session with the GSM-based + * mobile broadband network. Many modems do not require PPP for connections + * to the mobile network and thus this property should be left blank, which + * allows NetworkManager to select the appropriate settings automatically. + **/ + g_object_class_install_property + (object_class, PROP_NUMBER, + g_param_spec_string (NM_SETTING_GSM_NUMBER, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingGsm:username: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_USERNAME, + g_param_spec_string (NM_SETTING_GSM_USERNAME, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingGsm:password: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_PASSWORD, + g_param_spec_string (NM_SETTING_GSM_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingGsm:password-flags: + * + * Flags indicating how to handle the #NMSettingGsm:password property. + **/ + g_object_class_install_property + (object_class, PROP_PASSWORD_FLAGS, + g_param_spec_uint (NM_SETTING_GSM_PASSWORD_FLAGS, "", "", + NM_SETTING_SECRET_FLAG_NONE, + NM_SETTING_SECRET_FLAGS_ALL, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingGsm:apn: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_APN, + g_param_spec_string (NM_SETTING_GSM_APN, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingGsm:network-id: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_NETWORK_ID, + g_param_spec_string (NM_SETTING_GSM_NETWORK_ID, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingGsm:network-type: + * + * Network preference to force the device to only use specific network + * technologies. The permitted values are %NM_SETTING_GSM_NETWORK_TYPE_ANY, + * %NM_SETTING_GSM_NETWORK_TYPE_UMTS_HSPA, + * %NM_SETTING_GSM_NETWORK_TYPE_GPRS_EDGE, + * %NM_SETTING_GSM_NETWORK_TYPE_PREFER_UMTS_HSPA, + * %NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE, + * %NM_SETTING_GSM_NETWORK_TYPE_PREFER_4G, and + * %NM_SETTING_GSM_NETWORK_TYPE_4G. Note that not all devices allow network + * preference control. + * + * Deprecated: 0.9.10: No longer used. Network type setting should be done + * by talking to ModemManager directly. + **/ + g_object_class_install_property + (object_class, PROP_NETWORK_TYPE, + g_param_spec_int (NM_SETTING_GSM_NETWORK_TYPE, "", "", + NM_SETTING_GSM_NETWORK_TYPE_ANY, + NM_SETTING_GSM_NETWORK_TYPE_4G, + NM_SETTING_GSM_NETWORK_TYPE_ANY, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingGsm:allowed-bands: + * + * Bitfield of allowed frequency bands. Note that not all devices allow + * frequency band control. Permitted values are those specified by + * #NMSettingGsmNetworkBand. + * + * Deprecated: 0.9.10: No longer used. Band setting should be done by + * talking to ModemManager directly. + **/ + g_object_class_install_property + (object_class, PROP_ALLOWED_BANDS, + g_param_spec_uint (NM_SETTING_GSM_ALLOWED_BANDS, "", "", + NM_SETTING_GSM_BAND_UNKNOWN, + NM_SETTING_GSM_BANDS_MAX, + NM_SETTING_GSM_BAND_ANY, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingGsm:pin: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_PIN, + g_param_spec_string (NM_SETTING_GSM_PIN, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingGsm:pin-flags: + * + * Flags indicating how to handle the #NMSettingGsm:pin property. + **/ + g_object_class_install_property + (object_class, PROP_PIN_FLAGS, + g_param_spec_uint (NM_SETTING_GSM_PIN_FLAGS, "", "", + NM_SETTING_SECRET_FLAG_NONE, + NM_SETTING_SECRET_FLAGS_ALL, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingGsm:home-only: + * + * When %TRUE, only connections to the home network will be allowed. + * Connections to roaming networks will not be made. + **/ + g_object_class_install_property + (object_class, PROP_HOME_ONLY, + g_param_spec_boolean (NM_SETTING_GSM_HOME_ONLY, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-gsm.h b/libnm-core/nm-setting-gsm.h new file mode 100644 index 0000000000..564a8b4b9d --- /dev/null +++ b/libnm-core/nm-setting-gsm.h @@ -0,0 +1,204 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2011 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SETTING_GSM_H +#define NM_SETTING_GSM_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_GSM (nm_setting_gsm_get_type ()) +#define NM_SETTING_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_GSM, NMSettingGsm)) +#define NM_SETTING_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_GSM, NMSettingGsmClass)) +#define NM_IS_SETTING_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_GSM)) +#define NM_IS_SETTING_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_GSM)) +#define NM_SETTING_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_GSM, NMSettingGsmClass)) + +#define NM_SETTING_GSM_SETTING_NAME "gsm" + +/** + * NMSettingGsmError: + * @NM_SETTING_GSM_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_GSM_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_GSM_ERROR_MISSING_PROPERTY: the property was missing and is + * required + * @NM_SETTING_GSM_ERROR_MISSING_SERIAL_SETTING: the required #NMSettingSerial + * is missing in the connection + */ +typedef enum { + NM_SETTING_GSM_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_GSM_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_GSM_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ + NM_SETTING_GSM_ERROR_MISSING_SERIAL_SETTING /*< nick=MissingSerialSetting >*/ +} NMSettingGsmError; + +#define NM_SETTING_GSM_ERROR nm_setting_gsm_error_quark () +GQuark nm_setting_gsm_error_quark (void); + +#define NM_SETTING_GSM_NUMBER "number" +#define NM_SETTING_GSM_USERNAME "username" +#define NM_SETTING_GSM_PASSWORD "password" +#define NM_SETTING_GSM_PASSWORD_FLAGS "password-flags" +#define NM_SETTING_GSM_APN "apn" +#define NM_SETTING_GSM_NETWORK_ID "network-id" +#define NM_SETTING_GSM_PIN "pin" +#define NM_SETTING_GSM_PIN_FLAGS "pin-flags" +#define NM_SETTING_GSM_HOME_ONLY "home-only" + +/* Deprecated */ +#define NM_SETTING_GSM_ALLOWED_BANDS "allowed-bands" +#define NM_SETTING_GSM_NETWORK_TYPE "network-type" + +/** + * NMSettingGsmNetworkType: + * @NM_SETTING_GSM_NETWORK_TYPE_ANY: any access technology may be used + * @NM_SETTING_GSM_NETWORK_TYPE_UMTS_HSPA: only 3G-type (UMTS and HSPA) + * technologies may be used + * @NM_SETTING_GSM_NETWORK_TYPE_GPRS_EDGE: only 2G-type (GPRS and EDGE) + * technologies may be used + * @NM_SETTING_GSM_NETWORK_TYPE_PREFER_UMTS_HSPA: 3G-type technologies are + * preferred but 2G-type technologies may be used as a fallback + * @NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE: 2G-type technologies are + * preferred but 3G-type technologies may be used as a fallback + * @NM_SETTING_GSM_NETWORK_TYPE_PREFER_4G: 4G/LTE-type technologies are + * preferred but 3G/2/-type technologies may be used as a fallback + * @NM_SETTING_GSM_NETWORK_TYPE_4G: only 4G/LTE type + * technologies may be used + * + * #NMSettingGsmNetworkType values indicate the allowed access technologies + * the device may use when connecting to this network. + * + * Deprecated: 0.9.10: No longer used. + */ +typedef enum { + NM_SETTING_GSM_NETWORK_TYPE_ANY = -1, + NM_SETTING_GSM_NETWORK_TYPE_UMTS_HSPA = 0, + NM_SETTING_GSM_NETWORK_TYPE_GPRS_EDGE = 1, + NM_SETTING_GSM_NETWORK_TYPE_PREFER_UMTS_HSPA = 2, + NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE = 3, + NM_SETTING_GSM_NETWORK_TYPE_PREFER_4G = 4, + NM_SETTING_GSM_NETWORK_TYPE_4G = 5 +} NMSettingGsmNetworkType; + +/** + * NMSettingGsmNetworkBand: + * @NM_SETTING_GSM_BAND_UNKNOWN: unknown or no band specified + * @NM_SETTING_GSM_BAND_ANY: any band is allowed + * @NM_SETTING_GSM_BAND_EGSM: 900 MHz original GSM band + * @NM_SETTING_GSM_BAND_DCS: 1800 MHz DCS band + * @NM_SETTING_GSM_BAND_PCS: US 1900 MHz PCS band + * @NM_SETTING_GSM_BAND_G850: US 850 MHz Cellular band + * @NM_SETTING_GSM_BAND_U2100: WCDMA 3GPP UMTS 2100 MHz (Class I) + * @NM_SETTING_GSM_BAND_U1800: WCDMA 3GPP UMTS 1800 MHz (Class III) + * @NM_SETTING_GSM_BAND_U17IV: WCDMA 3GPP AWS 1700/2100 MHz (Class IV) + * @NM_SETTING_GSM_BAND_U800: WCDMA 3GPP UMTS 800 MHz (Class VI) + * @NM_SETTING_GSM_BAND_U850: WCDMA 3GPP UMTS 850 MHz (Class V) + * @NM_SETTING_GSM_BAND_U900: WCDMA 3GPP UMTS 900 MHz (Class VIII) + * @NM_SETTING_GSM_BAND_U17IX: WCDMA 3GPP UMTS 1700 MHz (Class IX) + * @NM_SETTING_GSM_BAND_U1900: WCDMA 3GPP UMTS 1900 MHz (Class II) + * @NM_SETTING_GSM_BAND_U2600: WCDMA 3GPP UMTS 2600 MHz (Class VII, internal) + * + * #NMSettingGsmNetworkBand values indicate the allowed frequency bands + * the device may use when connecting to this network. + * + * Deprecated: 0.9.10: No longer used. + */ +typedef enum { + NM_SETTING_GSM_BAND_UNKNOWN = 0x00000000, + NM_SETTING_GSM_BAND_ANY = 0x00000001, + NM_SETTING_GSM_BAND_EGSM = 0x00000002, /* 900 MHz */ + NM_SETTING_GSM_BAND_DCS = 0x00000004, /* 1800 MHz */ + NM_SETTING_GSM_BAND_PCS = 0x00000008, /* 1900 MHz */ + NM_SETTING_GSM_BAND_G850 = 0x00000010, /* 850 MHz */ + NM_SETTING_GSM_BAND_U2100 = 0x00000020, /* WCDMA 3GPP UMTS 2100 MHz (Class I) */ + NM_SETTING_GSM_BAND_U1800 = 0x00000040, /* WCDMA 3GPP UMTS 1800 MHz (Class III) */ + NM_SETTING_GSM_BAND_U17IV = 0x00000080, /* WCDMA 3GPP AWS 1700/2100 MHz (Class IV) */ + NM_SETTING_GSM_BAND_U800 = 0x00000100, /* WCDMA 3GPP UMTS 800 MHz (Class VI) */ + NM_SETTING_GSM_BAND_U850 = 0x00000200, /* WCDMA 3GPP UMTS 850 MHz (Class V) */ + NM_SETTING_GSM_BAND_U900 = 0x00000400, /* WCDMA 3GPP UMTS 900 MHz (Class VIII) */ + NM_SETTING_GSM_BAND_U17IX = 0x00000800, /* WCDMA 3GPP UMTS 1700 MHz (Class IX) */ + NM_SETTING_GSM_BAND_U1900 = 0x00001000, /* WCDMA 3GPP UMTS 1900 MHz (Class II) */ + NM_SETTING_GSM_BAND_U2600 = 0x00002000, /* WCDMA 3GPP UMTS 2600 MHz (Class VII, internal) */ +} NMSettingGsmNetworkBand; + +/** + * NM_SETTING_GSM_BANDS_MAX: + * + * #NM_SETTING_GSM_BANDS_MAX macro indicate the maximal value that can be used + * as the allowed frequency bands (#NMSettingGsm:allowed-bands property). + * + * Deprecated: 0.9.10: No longer used. + */ +#define NM_SETTING_GSM_BANDS_MAX ( NM_SETTING_GSM_BAND_UNKNOWN \ + | NM_SETTING_GSM_BAND_ANY \ + | NM_SETTING_GSM_BAND_EGSM \ + | NM_SETTING_GSM_BAND_DCS \ + | NM_SETTING_GSM_BAND_PCS \ + | NM_SETTING_GSM_BAND_G850 \ + | NM_SETTING_GSM_BAND_U2100 \ + | NM_SETTING_GSM_BAND_U1800 \ + | NM_SETTING_GSM_BAND_U17IV \ + | NM_SETTING_GSM_BAND_U800 \ + | NM_SETTING_GSM_BAND_U850 \ + | NM_SETTING_GSM_BAND_U900 \ + | NM_SETTING_GSM_BAND_U17IX \ + | NM_SETTING_GSM_BAND_U1900 \ + | NM_SETTING_GSM_BAND_U2600) + +typedef struct { + NMSetting parent; +} NMSettingGsm; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingGsmClass; + +GType nm_setting_gsm_get_type (void); + +NMSetting *nm_setting_gsm_new (void); +const char *nm_setting_gsm_get_number (NMSettingGsm *setting); +const char *nm_setting_gsm_get_username (NMSettingGsm *setting); +const char *nm_setting_gsm_get_password (NMSettingGsm *setting); +const char *nm_setting_gsm_get_apn (NMSettingGsm *setting); +const char *nm_setting_gsm_get_network_id (NMSettingGsm *setting); +const char *nm_setting_gsm_get_pin (NMSettingGsm *setting); +gboolean nm_setting_gsm_get_home_only (NMSettingGsm *setting); + +NMSettingSecretFlags nm_setting_gsm_get_pin_flags (NMSettingGsm *setting); +NMSettingSecretFlags nm_setting_gsm_get_password_flags (NMSettingGsm *setting); + +/* Deprecated */ +NM_DEPRECATED_IN_0_9_10 +int nm_setting_gsm_get_network_type (NMSettingGsm *setting); +NM_DEPRECATED_IN_0_9_10 +guint32 nm_setting_gsm_get_allowed_bands (NMSettingGsm *setting); + +G_END_DECLS + +#endif /* NM_SETTING_GSM_H */ diff --git a/libnm-core/nm-setting-infiniband.c b/libnm-core/nm-setting-infiniband.c new file mode 100644 index 0000000000..4e470e561b --- /dev/null +++ b/libnm-core/nm-setting-infiniband.c @@ -0,0 +1,472 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2013 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <dbus/dbus-glib.h> +#include <linux/if_infiniband.h> +#include <glib/gi18n.h> + +#include "nm-setting-infiniband.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-utils-private.h" +#include "nm-setting-private.h" +#include "nm-setting-connection.h" + +/** + * SECTION:nm-setting-infiniband + * @short_description: Describes connection properties for IP-over-InfiniBand networks + * @include: nm-setting-infiniband.h + * + * The #NMSettingInfiniband object is a #NMSetting subclass that describes properties + * necessary for connection to IP-over-InfiniBand networks. + **/ + +/** + * nm_setting_infiniband_error_quark: + * + * Registers an error quark for #NMSettingInfiniband if necessary. + * + * Returns: the error quark used for #NMSettingInfiniband errors. + **/ +GQuark +nm_setting_infiniband_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-infiniband-error-quark"); + return quark; +} + +G_DEFINE_TYPE_WITH_CODE (NMSettingInfiniband, nm_setting_infiniband, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_INFINIBAND_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_INFINIBAND_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_INFINIBAND) + +#define NM_SETTING_INFINIBAND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_INFINIBAND, NMSettingInfinibandPrivate)) + +typedef struct { + GByteArray *mac_address; + char *transport_mode; + guint32 mtu; + int p_key; + char *parent, *virtual_iface_name; +} NMSettingInfinibandPrivate; + +enum { + PROP_0, + PROP_MAC_ADDRESS, + PROP_MTU, + PROP_TRANSPORT_MODE, + PROP_P_KEY, + PROP_PARENT, + + LAST_PROP +}; + +/** + * nm_setting_infiniband_new: + * + * Creates a new #NMSettingInfiniband object with default values. + * + * Returns: (transfer full): the new empty #NMSettingInfiniband object + **/ +NMSetting * +nm_setting_infiniband_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_INFINIBAND, NULL); +} + +/** + * nm_setting_infiniband_get_mac_address: + * @setting: the #NMSettingInfiniband + * + * Returns: the #NMSettingInfiniband:mac-address property of the setting + **/ +const GByteArray * +nm_setting_infiniband_get_mac_address (NMSettingInfiniband *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_INFINIBAND (setting), NULL); + + return NM_SETTING_INFINIBAND_GET_PRIVATE (setting)->mac_address; +} + +/** + * nm_setting_infiniband_get_mtu: + * @setting: the #NMSettingInfiniband + * + * Returns: the #NMSettingInfiniband:mtu property of the setting + **/ +guint32 +nm_setting_infiniband_get_mtu (NMSettingInfiniband *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_INFINIBAND (setting), 0); + + return NM_SETTING_INFINIBAND_GET_PRIVATE (setting)->mtu; +} + +/** + * nm_setting_infiniband_get_transport_mode: + * @setting: the #NMSettingInfiniband + * + * Returns the transport mode for this device. Either 'datagram' or + * 'connected'. + * + * Returns: the IPoIB transport mode + **/ +const char * +nm_setting_infiniband_get_transport_mode (NMSettingInfiniband *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_INFINIBAND (setting), NULL); + + return NM_SETTING_INFINIBAND_GET_PRIVATE (setting)->transport_mode; +} + +/** + * nm_setting_infiniband_get_p_key: + * @setting: the #NMSettingInfiniband + * + * Returns the 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. + * + * Returns: the IPoIB P_Key + **/ +int +nm_setting_infiniband_get_p_key (NMSettingInfiniband *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_INFINIBAND (setting), -1); + + return NM_SETTING_INFINIBAND_GET_PRIVATE (setting)->p_key; +} + +/** + * nm_setting_infiniband_get_parent: + * @setting: the #NMSettingInfiniband + * + * Returns the parent interface name for this device, if set. + * + * Returns: the parent interface name + **/ +const char * +nm_setting_infiniband_get_parent (NMSettingInfiniband *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_INFINIBAND (setting), NULL); + + return NM_SETTING_INFINIBAND_GET_PRIVATE (setting)->parent; +} + +static const char * +get_virtual_iface_name (NMSetting *setting) +{ + NMSettingInfinibandPrivate *priv = NM_SETTING_INFINIBAND_GET_PRIVATE (setting); + + if (priv->p_key == -1 || !priv->parent) + return NULL; + + if (!priv->virtual_iface_name) + priv->virtual_iface_name = g_strdup_printf ("%s.%04x", priv->parent, priv->p_key); + + return NM_SETTING_INFINIBAND_GET_PRIVATE (setting)->virtual_iface_name; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingConnection *s_con; + NMSettingInfinibandPrivate *priv = NM_SETTING_INFINIBAND_GET_PRIVATE (setting); + + if (priv->mac_address && priv->mac_address->len != INFINIBAND_ALEN) { + g_set_error_literal (error, + NM_SETTING_INFINIBAND_ERROR, + NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_INFINIBAND_SETTING_NAME, NM_SETTING_INFINIBAND_MAC_ADDRESS); + return FALSE; + } + + if (!g_strcmp0 (priv->transport_mode, "datagram")) { + if (priv->mtu > 2044) + priv->mtu = 2044; + } else if (!g_strcmp0 (priv->transport_mode, "connected")) { + if (priv->mtu > 65520) + priv->mtu = 65520; + } else { + g_set_error_literal (error, + NM_SETTING_INFINIBAND_ERROR, + NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_INFINIBAND_SETTING_NAME, NM_SETTING_INFINIBAND_TRANSPORT_MODE); + return FALSE; + } + + if (priv->parent) { + if (!nm_utils_iface_valid_name (priv->parent)) { + g_set_error_literal (error, + NM_SETTING_INFINIBAND_ERROR, + NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY, + _("not a valid interface name")); + g_prefix_error (error, "%s: ", NM_SETTING_INFINIBAND_PARENT); + return FALSE; + } + if (priv->p_key == -1) { + g_set_error_literal (error, + NM_SETTING_INFINIBAND_ERROR, + NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY, + _("Must specify a P_Key if specifying parent")); + g_prefix_error (error, "%s: ", NM_SETTING_INFINIBAND_PARENT); + } + } + + if (priv->p_key != -1) { + if (!priv->mac_address && !priv->parent) { + g_set_error_literal (error, + NM_SETTING_INFINIBAND_ERROR, + NM_SETTING_INFINIBAND_ERROR_MISSING_PROPERTY, + _("InfiniBand P_Key connection did not specify parent interface name")); + g_prefix_error (error, "%s: ", NM_SETTING_INFINIBAND_PARENT); + return FALSE; + } + } + + s_con = NM_SETTING_CONNECTION (nm_setting_find_in_list (all_settings, NM_SETTING_CONNECTION_SETTING_NAME)); + if (s_con) { + const char *interface_name = nm_setting_connection_get_interface_name (s_con); + + if (!interface_name) + ; + else if (!nm_utils_iface_valid_name (interface_name)) { + /* report the error for NMSettingConnection:interface-name, because + * it's that property that is invalid -- although we currently verify() + * NMSettingInfiniband. + **/ + g_set_error (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid interface name"), + interface_name); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME); + return FALSE; + } else { + if (priv->p_key != -1) { + if (!priv->virtual_iface_name) + priv->virtual_iface_name = g_strdup_printf ("%s.%04x", priv->parent, priv->p_key); + + if (strcmp (interface_name, priv->virtual_iface_name) != 0) { + /* We don't support renaming software infiniband devices. Later we might, but + * for now just reject such connections. + **/ + g_set_error (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY, + _("interface name of software infiniband device must be '%s' or unset (instead it is '%s')"), + priv->virtual_iface_name, interface_name); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME); + return FALSE; + } + } + } + } + + return TRUE; +} + +static void +nm_setting_infiniband_init (NMSettingInfiniband *setting) +{ +} + +static void +finalize (GObject *object) +{ + NMSettingInfinibandPrivate *priv = NM_SETTING_INFINIBAND_GET_PRIVATE (object); + + g_free (priv->transport_mode); + if (priv->mac_address) + g_byte_array_free (priv->mac_address, TRUE); + g_free (priv->parent); + g_free (priv->virtual_iface_name); + + G_OBJECT_CLASS (nm_setting_infiniband_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingInfinibandPrivate *priv = NM_SETTING_INFINIBAND_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MAC_ADDRESS: + if (priv->mac_address) + g_byte_array_free (priv->mac_address, TRUE); + priv->mac_address = g_value_dup_boxed (value); + break; + case PROP_MTU: + priv->mtu = g_value_get_uint (value); + break; + case PROP_TRANSPORT_MODE: + g_free (priv->transport_mode); + priv->transport_mode = g_value_dup_string (value); + break; + case PROP_P_KEY: + priv->p_key = g_value_get_int (value); + g_clear_pointer (&priv->virtual_iface_name, g_free); + break; + case PROP_PARENT: + g_free (priv->parent); + priv->parent = g_value_dup_string (value); + g_clear_pointer (&priv->virtual_iface_name, g_free); + 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) +{ + NMSettingInfiniband *setting = NM_SETTING_INFINIBAND (object); + + switch (prop_id) { + case PROP_MAC_ADDRESS: + g_value_set_boxed (value, nm_setting_infiniband_get_mac_address (setting)); + break; + case PROP_MTU: + g_value_set_uint (value, nm_setting_infiniband_get_mtu (setting)); + break; + case PROP_TRANSPORT_MODE: + g_value_set_string (value, nm_setting_infiniband_get_transport_mode (setting)); + break; + case PROP_P_KEY: + g_value_set_int (value, nm_setting_infiniband_get_p_key (setting)); + break; + case PROP_PARENT: + g_value_set_string (value, nm_setting_infiniband_get_parent (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_infiniband_class_init (NMSettingInfinibandClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingInfinibandPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + parent_class->verify = verify; + parent_class->get_virtual_iface_name = get_virtual_iface_name; + + /* Properties */ + /** + * NMSettingInfiniband:mac-address: + * + * 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). + **/ + g_object_class_install_property + (object_class, PROP_MAC_ADDRESS, + _nm_param_spec_specialized (NM_SETTING_INFINIBAND_MAC_ADDRESS, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingInfiniband:mtu: + * + * If non-zero, only transmit packets of the specified size or smaller, + * breaking larger packets up into multiple frames. + **/ + g_object_class_install_property + (object_class, PROP_MTU, + g_param_spec_uint (NM_SETTING_INFINIBAND_MTU, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingInfiniband:transport-mode: + * + * The IP-over-InfiniBand transport mode. Either "datagram" or + * "connected". + **/ + g_object_class_install_property + (object_class, PROP_TRANSPORT_MODE, + g_param_spec_string (NM_SETTING_INFINIBAND_TRANSPORT_MODE, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingInfiniband:p-key: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_P_KEY, + g_param_spec_int (NM_SETTING_INFINIBAND_P_KEY, "", "", + -1, 0xFFFF, -1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingInfiniband:parent: + * + * The interface name of the parent device of this device. Normally %NULL, + * but if the #NMSettingInfiniband:p_key property is set, then you must + * specify the base device by setting either this property or + * #NMSettingInfiniband:mac-address. + **/ + g_object_class_install_property + (object_class, PROP_PARENT, + g_param_spec_string (NM_SETTING_INFINIBAND_PARENT, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + +} diff --git a/libnm-core/nm-setting-infiniband.h b/libnm-core/nm-setting-infiniband.h new file mode 100644 index 0000000000..dff3845036 --- /dev/null +++ b/libnm-core/nm-setting-infiniband.h @@ -0,0 +1,85 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 Red Hat, Inc. + */ + +#ifndef NM_SETTING_INFINIBAND_H +#define NM_SETTING_INFINIBAND_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_INFINIBAND (nm_setting_infiniband_get_type ()) +#define NM_SETTING_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_INFINIBAND, NMSettingInfiniband)) +#define NM_SETTING_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_INFINIBAND, NMSettingInfinibandClass)) +#define NM_IS_SETTING_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_INFINIBAND)) +#define NM_IS_SETTING_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_INFINIBAND)) +#define NM_SETTING_INFINIBAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_INFINIBAND, NMSettingInfinibandClass)) + +#define NM_SETTING_INFINIBAND_SETTING_NAME "infiniband" + +/** + * NMSettingInfinibandError: + * @NM_SETTING_INFINIBAND_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_INFINIBAND_ERROR_MISSING_PROPERTY: the property was missing and is + * required + */ +typedef enum { + NM_SETTING_INFINIBAND_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_INFINIBAND_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/ +} NMSettingInfinibandError; + +#define NM_SETTING_INFINIBAND_ERROR nm_setting_infiniband_error_quark () +GQuark nm_setting_infiniband_error_quark (void); + +#define NM_SETTING_INFINIBAND_MAC_ADDRESS "mac-address" +#define NM_SETTING_INFINIBAND_MTU "mtu" +#define NM_SETTING_INFINIBAND_TRANSPORT_MODE "transport-mode" +#define NM_SETTING_INFINIBAND_P_KEY "p-key" +#define NM_SETTING_INFINIBAND_PARENT "parent" + +typedef struct { + NMSetting parent; +} NMSettingInfiniband; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingInfinibandClass; + +GType nm_setting_infiniband_get_type (void); + +NMSetting * nm_setting_infiniband_new (void); +const GByteArray *nm_setting_infiniband_get_mac_address (NMSettingInfiniband *setting); +guint32 nm_setting_infiniband_get_mtu (NMSettingInfiniband *setting); +const char * nm_setting_infiniband_get_transport_mode (NMSettingInfiniband *setting); +int nm_setting_infiniband_get_p_key (NMSettingInfiniband *setting); +const char * nm_setting_infiniband_get_parent (NMSettingInfiniband *setting); + +G_END_DECLS + +#endif /* NM_SETTING_INFINIBAND_H */ diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c new file mode 100644 index 0000000000..bc2ef8731a --- /dev/null +++ b/libnm-core/nm-setting-ip4-config.c @@ -0,0 +1,1895 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include <string.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "nm-setting-ip4-config.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-dbus-glib-types.h" +#include "nm-glib-compat.h" +#include "nm-setting-private.h" + + +/** + * SECTION:nm-setting-ip4-config + * @short_description: Describes IPv4 addressing, routing, and name service properties + * @include: nm-setting-ip4-config.h + * + * The #NMSettingIP4Config object is a #NMSetting subclass that describes + * properties related to IPv4 addressing, routing, and Domain Name Service + **/ + +/** + * nm_setting_ip4_config_error_quark: + * + * Registers an error quark for #NMSettingIP4Config if necessary. + * + * Returns: the error quark used for #NMSettingIP4Config errors. + **/ +GQuark +nm_setting_ip4_config_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-ip4-config-error-quark"); + return quark; +} + +G_DEFINE_BOXED_TYPE (NMIP4Address, nm_ip4_address, nm_ip4_address_dup, nm_ip4_address_unref) +G_DEFINE_BOXED_TYPE (NMIP4Route, nm_ip4_route, nm_ip4_route_dup, nm_ip4_route_unref) + +G_DEFINE_TYPE_WITH_CODE (NMSettingIP4Config, nm_setting_ip4_config, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_IP4_CONFIG_SETTING_NAME, + g_define_type_id, + 4, + NM_SETTING_IP4_CONFIG_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_IP4_CONFIG) + +#define NM_SETTING_IP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP4_CONFIG, NMSettingIP4ConfigPrivate)) + +typedef struct { + char *method; + GArray *dns; /* array of guint32; elements in network byte order */ + GSList *dns_search; /* list of strings */ + GSList *addresses; /* array of NMIP4Address */ + GSList *address_labels; /* list of strings */ + GSList *routes; /* array of NMIP4Route */ + gboolean ignore_auto_routes; + gboolean ignore_auto_dns; + char *dhcp_client_id; + gboolean dhcp_send_hostname; + char *dhcp_hostname; + gboolean never_default; + gboolean may_fail; +} NMSettingIP4ConfigPrivate; + +enum { + PROP_0, + PROP_METHOD, + PROP_DNS, + PROP_DNS_SEARCH, + PROP_ADDRESSES, + PROP_ADDRESS_LABELS, + PROP_ROUTES, + PROP_IGNORE_AUTO_ROUTES, + PROP_IGNORE_AUTO_DNS, + PROP_DHCP_CLIENT_ID, + PROP_DHCP_SEND_HOSTNAME, + PROP_DHCP_HOSTNAME, + PROP_NEVER_DEFAULT, + PROP_MAY_FAIL, + + LAST_PROP +}; + +/** + * nm_setting_ip4_config_new: + * + * Creates a new #NMSettingIP4Config object with default values. + * + * Returns: (transfer full): the new empty #NMSettingIP4Config object + **/ +NMSetting * +nm_setting_ip4_config_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_IP4_CONFIG, NULL); +} + +/** + * nm_setting_ip4_config_get_method: + * @setting: the #NMSettingIP4Config + * + * Returns: the #NMSettingIP4Config:method property of the setting + **/ +const char * +nm_setting_ip4_config_get_method (NMSettingIP4Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL); + + return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->method; +} + +/** + * nm_setting_ip4_config_get_num_dns: + * @setting: the #NMSettingIP4Config + * + * Returns: the number of configured DNS servers + **/ +guint32 +nm_setting_ip4_config_get_num_dns (NMSettingIP4Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0); + + return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dns->len; +} + +/** + * nm_setting_ip4_config_get_dns: + * @setting: the #NMSettingIP4Config + * @i: index number of the DNS server to return + * + * Returns: the IPv4 address (network byte order) of the DNS server at index + * @i + **/ +guint32 +nm_setting_ip4_config_get_dns (NMSettingIP4Config *setting, guint32 i) +{ + NMSettingIP4ConfigPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + g_return_val_if_fail (i <= priv->dns->len, 0); + + return g_array_index (priv->dns, guint32, i); +} + +/** + * nm_setting_ip4_config_add_dns: + * @setting: the #NMSettingIP4Config + * @dns: the IPv4 address (network byte order) of the DNS server to add + * + * Adds a new DNS server to the setting. + * + * Returns: %TRUE if the DNS server was added; %FALSE if the server was already + * known + **/ +gboolean +nm_setting_ip4_config_add_dns (NMSettingIP4Config *setting, guint32 dns) +{ + NMSettingIP4ConfigPrivate *priv; + int i; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + for (i = 0; i < priv->dns->len; i++) { + if (dns == g_array_index (priv->dns, guint32, i)) + return FALSE; + } + + g_array_append_val (priv->dns, dns); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS); + return TRUE; +} + +/** + * nm_setting_ip4_config_remove_dns: + * @setting: the #NMSettingIP4Config + * @i: index number of the DNS server to remove + * + * Removes the DNS server at index @i. + **/ +void +nm_setting_ip4_config_remove_dns (NMSettingIP4Config *setting, guint32 i) +{ + NMSettingIP4ConfigPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + g_return_if_fail (i <= priv->dns->len); + + g_array_remove_index (priv->dns, i); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS); +} + +/** + * nm_setting_ip4_config_remove_dns_by_value: + * @setting: the #NMSettingIP4Config + * @dns: the DNS server to remove + * + * Removes the DNS server @dns. + * + * Returns: %TRUE if the DNS server was found and removed; %FALSE if it was not. + * domain was already known + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_ip4_config_remove_dns_by_value (NMSettingIP4Config *setting, guint32 dns) +{ + NMSettingIP4ConfigPrivate *priv; + int i; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + for (i = 0; i < priv->dns->len; i++) { + if (dns == g_array_index (priv->dns, guint32, i)) { + g_array_remove_index (priv->dns, i); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_ip4_config_clear_dns: + * @setting: the #NMSettingIP4Config + * + * Removes all configured DNS servers. + **/ +void +nm_setting_ip4_config_clear_dns (NMSettingIP4Config *setting) +{ + NMSettingIP4ConfigPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + g_array_remove_range (priv->dns, 0, priv->dns->len); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS); +} + +/** + * nm_setting_ip4_config_get_num_dns_searches: + * @setting: the #NMSettingIP4Config + * + * Returns: the number of configured DNS search domains + **/ +guint32 +nm_setting_ip4_config_get_num_dns_searches (NMSettingIP4Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0); + + return g_slist_length (NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dns_search); +} + +/** + * nm_setting_ip4_config_get_dns_search: + * @setting: the #NMSettingIP4Config + * @i: index number of the DNS search domain to return + * + * Returns: the DNS search domain at index @i + **/ +const char * +nm_setting_ip4_config_get_dns_search (NMSettingIP4Config *setting, guint32 i) +{ + NMSettingIP4ConfigPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->dns_search), NULL); + + return (const char *) g_slist_nth_data (priv->dns_search, i); +} + +/** + * nm_setting_ip4_config_add_dns_search: + * @setting: the #NMSettingIP4Config + * @dns_search: the search domain to add + * + * Adds a new DNS search domain to the setting. + * + * Returns: %TRUE if the DNS search domain was added; %FALSE if the search + * domain was already known + **/ +gboolean +nm_setting_ip4_config_add_dns_search (NMSettingIP4Config *setting, + const char *dns_search) +{ + NMSettingIP4ConfigPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE); + g_return_val_if_fail (dns_search != NULL, FALSE); + g_return_val_if_fail (dns_search[0] != '\0', FALSE); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + for (iter = priv->dns_search; iter; iter = g_slist_next (iter)) { + if (!strcmp (dns_search, (char *) iter->data)) + return FALSE; + } + + priv->dns_search = g_slist_append (priv->dns_search, g_strdup (dns_search)); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS_SEARCH); + return TRUE; +} + +/** + * nm_setting_ip4_config_remove_dns_search: + * @setting: the #NMSettingIP4Config + * @i: index number of the DNS search domain + * + * Removes the DNS search domain at index @i. + **/ +void +nm_setting_ip4_config_remove_dns_search (NMSettingIP4Config *setting, guint32 i) +{ + NMSettingIP4ConfigPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + elt = g_slist_nth (priv->dns_search, i); + g_return_if_fail (elt != NULL); + + g_free (elt->data); + priv->dns_search = g_slist_delete_link (priv->dns_search, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS_SEARCH); +} + +/** + * nm_setting_ip4_config_remove_dns_search_by_value: + * @setting: the #NMSettingIP4Config + * @dns_search: the search domain to remove + * + * Removes the DNS search domain @dns_search. + * + * Returns: %TRUE if the DNS search domain was found and removed; %FALSE if it was not. + * + * Since 0.9.10 + **/ +gboolean +nm_setting_ip4_config_remove_dns_search_by_value (NMSettingIP4Config *setting, + const char *dns_search) +{ + NMSettingIP4ConfigPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE); + g_return_val_if_fail (dns_search != NULL, FALSE); + g_return_val_if_fail (dns_search[0] != '\0', FALSE); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + for (iter = priv->dns_search; iter; iter = g_slist_next (iter)) { + if (!strcmp (dns_search, (char *) iter->data)) { + priv->dns_search = g_slist_delete_link (priv->dns_search, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS_SEARCH); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_ip4_config_clear_dns_searches: + * @setting: the #NMSettingIP4Config + * + * Removes all configured DNS search domains. + **/ +void +nm_setting_ip4_config_clear_dns_searches (NMSettingIP4Config *setting) +{ + g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); + + g_slist_free_full (NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dns_search, g_free); + NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dns_search = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS_SEARCH); +} + +/** + * nm_setting_ip4_config_get_num_addresses: + * @setting: the #NMSettingIP4Config + * + * Returns: the number of configured addresses + **/ +guint32 +nm_setting_ip4_config_get_num_addresses (NMSettingIP4Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0); + + return g_slist_length (NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->addresses); +} + +/** + * nm_setting_ip4_config_get_address: + * @setting: the #NMSettingIP4Config + * @i: index number of the address to return + * + * Returns: the address at index @i + **/ +NMIP4Address * +nm_setting_ip4_config_get_address (NMSettingIP4Config *setting, guint32 i) +{ + NMSettingIP4ConfigPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->addresses), NULL); + + return (NMIP4Address *) g_slist_nth_data (priv->addresses, i); +} + +const char * +nm_setting_ip4_config_get_address_label (NMSettingIP4Config *setting, guint32 i) +{ + NMSettingIP4ConfigPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->address_labels), NULL); + + return (const char *) g_slist_nth_data (priv->address_labels, i); +} + +/** + * nm_setting_ip4_config_add_address: + * @setting: the #NMSettingIP4Config + * @address: the new address to add + * + * Adds a new IPv4 address and associated information to the setting. The + * given address is duplicated internally and is not changed by this function. + * + * Returns: %TRUE if the address was added; %FALSE if the address was already + * known. + **/ +gboolean +nm_setting_ip4_config_add_address (NMSettingIP4Config *setting, + NMIP4Address *address) +{ + return nm_setting_ip4_config_add_address_with_label (setting, address, NULL); +} + +gboolean +nm_setting_ip4_config_add_address_with_label (NMSettingIP4Config *setting, + NMIP4Address *address, + const char *label) +{ + NMSettingIP4ConfigPrivate *priv; + NMIP4Address *copy; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE); + g_return_val_if_fail (address != NULL, FALSE); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + for (iter = priv->addresses; iter; iter = g_slist_next (iter)) { + if (nm_ip4_address_compare ((NMIP4Address *) iter->data, address)) + return FALSE; + } + + copy = nm_ip4_address_dup (address); + priv->addresses = g_slist_append (priv->addresses, copy); + priv->address_labels = g_slist_append (priv->address_labels, g_strdup (label)); + + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ADDRESSES); + return TRUE; +} + +/** + * nm_setting_ip4_config_remove_address: + * @setting: the #NMSettingIP4Config + * @i: index number of the address to remove + * + * Removes the address at index @i. + **/ +void +nm_setting_ip4_config_remove_address (NMSettingIP4Config *setting, guint32 i) +{ + NMSettingIP4ConfigPrivate *priv; + GSList *addr, *label; + + g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + addr = g_slist_nth (priv->addresses, i); + label = g_slist_nth (priv->address_labels, i); + g_return_if_fail (addr != NULL && label != NULL); + + nm_ip4_address_unref ((NMIP4Address *) addr->data); + priv->addresses = g_slist_delete_link (priv->addresses, addr); + if (label->data) + g_free (label->data); + priv->address_labels = g_slist_delete_link (priv->address_labels, label); + + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ADDRESSES); +} + +/** + * nm_setting_ip4_config_remove_address_by_value: + * @setting: the #NMSettingIP4Config + * @address: the IP address to remove + * + * Removes the address @address. + * + * Returns: %TRUE if the address was found and removed; %FALSE if it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_ip4_config_remove_address_by_value (NMSettingIP4Config *setting, + NMIP4Address *address) +{ + NMSettingIP4ConfigPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE); + g_return_val_if_fail (address != NULL, FALSE); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + for (iter = priv->addresses; iter; iter = g_slist_next (iter)) { + if (nm_ip4_address_compare ((NMIP4Address *) iter->data, address)) { + nm_ip4_address_unref ((NMIP4Address *) iter->data); + priv->addresses = g_slist_delete_link (priv->addresses, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ADDRESSES); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_ip4_config_clear_addresses: + * @setting: the #NMSettingIP4Config + * + * Removes all configured addresses. + **/ +void +nm_setting_ip4_config_clear_addresses (NMSettingIP4Config *setting) +{ + NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + + g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); + + g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip4_address_unref); + priv->addresses = NULL; + g_slist_free_full (priv->address_labels, g_free); + priv->address_labels = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ADDRESSES); +} + +/** + * nm_setting_ip4_config_get_num_routes: + * @setting: the #NMSettingIP4Config + * + * Returns: the number of configured routes + **/ +guint32 +nm_setting_ip4_config_get_num_routes (NMSettingIP4Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0); + + return g_slist_length (NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->routes); +} + +/** + * nm_setting_ip4_config_get_route: + * @setting: the #NMSettingIP4Config + * @i: index number of the route to return + * + * Returns: the route at index @i + **/ +NMIP4Route * +nm_setting_ip4_config_get_route (NMSettingIP4Config *setting, guint32 i) +{ + NMSettingIP4ConfigPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->routes), NULL); + + return (NMIP4Route *) g_slist_nth_data (priv->routes, i); +} + +/** + * nm_setting_ip4_config_add_route: + * @setting: the #NMSettingIP4Config + * @route: the route to add + * + * Adds a new IPv4 route and associated information to the setting. The + * given route is duplicated internally and is not changed by this function. + * + * Returns: %TRUE if the route was added; %FALSE if the route was already known. + **/ +gboolean +nm_setting_ip4_config_add_route (NMSettingIP4Config *setting, + NMIP4Route *route) +{ + NMSettingIP4ConfigPrivate *priv; + NMIP4Route *copy; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE); + g_return_val_if_fail (route != NULL, FALSE); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + for (iter = priv->routes; iter; iter = g_slist_next (iter)) { + if (nm_ip4_route_compare ((NMIP4Route *) iter->data, route)) + return FALSE; + } + + copy = nm_ip4_route_dup (route); + priv->routes = g_slist_append (priv->routes, copy); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ROUTES); + return TRUE; +} + +/** + * nm_setting_ip4_config_remove_route: + * @setting: the #NMSettingIP4Config + * @i: index number of the route + * + * Removes the route at index @i. + **/ +void +nm_setting_ip4_config_remove_route (NMSettingIP4Config *setting, guint32 i) +{ + NMSettingIP4ConfigPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + elt = g_slist_nth (priv->routes, i); + g_return_if_fail (elt != NULL); + + nm_ip4_route_unref ((NMIP4Route *) elt->data); + priv->routes = g_slist_delete_link (priv->routes, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ROUTES); +} + +/** + * nm_setting_ip4_config_remove_route_by_value: + * @setting: the #NMSettingIP4Config + * @route: the route to remove + * + * Removes the route @route. + * + * Returns: %TRUE if the route was found and removed; %FALSE if it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_ip4_config_remove_route_by_value (NMSettingIP4Config *setting, + NMIP4Route *route) +{ + NMSettingIP4ConfigPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE); + g_return_val_if_fail (route != NULL, FALSE); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + for (iter = priv->routes; iter; iter = g_slist_next (iter)) { + if (nm_ip4_route_compare ((NMIP4Route *) iter->data, route)) { + nm_ip4_route_unref ((NMIP4Route *) iter->data); + priv->routes = g_slist_delete_link (priv->routes, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ROUTES); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_ip4_config_clear_routes: + * @setting: the #NMSettingIP4Config + * + * Removes all configured routes. + **/ +void +nm_setting_ip4_config_clear_routes (NMSettingIP4Config *setting) +{ + NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + + g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); + + g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref); + priv->routes = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ROUTES); +} + +/** + * nm_setting_ip4_config_get_ignore_auto_routes: + * @setting: the #NMSettingIP4Config + * + * Returns the value contained in the #NMSettingIP4Config:ignore-auto-routes + * property. + * + * Returns: %TRUE if automatically configured (ie via DHCP) routes should be + * ignored. + **/ +gboolean +nm_setting_ip4_config_get_ignore_auto_routes (NMSettingIP4Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE); + + return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->ignore_auto_routes; +} + +/** + * nm_setting_ip4_config_get_ignore_auto_dns: + * @setting: the #NMSettingIP4Config + * + * Returns the value contained in the #NMSettingIP4Config:ignore-auto-dns + * property. + * + * Returns: %TRUE if automatically configured (ie via DHCP) DNS information + * should be ignored. + **/ +gboolean +nm_setting_ip4_config_get_ignore_auto_dns (NMSettingIP4Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE); + + return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->ignore_auto_dns; +} + +/** + * nm_setting_ip4_config_get_dhcp_client_id: + * @setting: the #NMSettingIP4Config + * + * Returns the value contained in the #NMSettingIP4Config:dhcp-client-id + * property. + * + * Returns: the configured Client ID to send to the DHCP server when requesting + * addresses via DHCP. + **/ +const char * +nm_setting_ip4_config_get_dhcp_client_id (NMSettingIP4Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL); + + return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dhcp_client_id; +} + +/** + * nm_setting_ip4_config_get_dhcp_send_hostname: + * @setting: the #NMSettingIP4Config + * + * Returns the value contained in the #NMSettingIP4Config:dhcp-send-hostname + * property. + * + * Returns: %TRUE if NetworkManager should send the machine hostname to the + * DHCP server when requesting addresses to allow the server to automatically + * update DNS information for this machine. + **/ +gboolean +nm_setting_ip4_config_get_dhcp_send_hostname (NMSettingIP4Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE); + + return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dhcp_send_hostname; +} + +/** + * nm_setting_ip4_config_get_dhcp_hostname: + * @setting: the #NMSettingIP4Config + * + * Returns the value contained in the #NMSettingIP4Config:dhcp-hostname + * property. + * + * Returns: the configured hostname to send to the DHCP server + **/ +const char * +nm_setting_ip4_config_get_dhcp_hostname (NMSettingIP4Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL); + + return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dhcp_hostname; +} + +/** + * nm_setting_ip4_config_get_never_default: + * @setting: the #NMSettingIP4Config + * + * Returns the value contained in the #NMSettingIP4Config:never-default + * property. + * + * Returns: %TRUE if this connection should never be the default connection + * for IPv4 addressing + **/ +gboolean +nm_setting_ip4_config_get_never_default (NMSettingIP4Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE); + + return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->never_default; +} + +/** + * nm_setting_ip4_config_get_may_fail: + * @setting: the #NMSettingIP4Config + * + * Returns the value contained in the #NMSettingIP4Config:may-fail + * property. + * + * Returns: %TRUE if this connection doesn't require IPv4 addressing to complete + * for the connection to succeed. + **/ +gboolean +nm_setting_ip4_config_get_may_fail (NMSettingIP4Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE); + + return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->may_fail; +} + +static gboolean +verify_label (const char *label) +{ + const char *p; + char *iface; + + p = strchr (label, ':'); + if (!p) + return FALSE; + iface = g_strndup (label, p - label); + if (!nm_utils_iface_valid_name (iface)) { + g_free (iface); + return FALSE; + } + g_free (iface); + + for (p++; *p; p++) { + if (!g_ascii_isalnum (*p) && *p != '_') + return FALSE; + } + + return TRUE; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + GSList *iter, *l_iter; + int i; + + if (!priv->method) { + g_set_error_literal (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_METHOD); + return FALSE; + } + + if (!strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + if (!priv->addresses) { + g_set_error (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_MISSING_PROPERTY, + _("this property cannot be empty for '%s=%s'"), + NM_SETTING_IP4_CONFIG_METHOD, priv->method); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ADDRESSES); + return FALSE; + } + } else if ( !strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) + || !strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) + || !strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) { + if (priv->dns && priv->dns->len) { + g_set_error (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD, + _("this property is not allowed for '%s=%s'"), + NM_SETTING_IP4_CONFIG_METHOD, priv->method); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_DNS); + return FALSE; + } + + if (g_slist_length (priv->dns_search)) { + g_set_error (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD, + _("this property is not allowed for '%s=%s'"), + NM_SETTING_IP4_CONFIG_METHOD, priv->method); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_DNS_SEARCH); + return FALSE; + } + + /* Shared allows IP addresses; link-local and disabled do not */ + if (strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) != 0) { + if (g_slist_length (priv->addresses)) { + g_set_error (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD, + _("this property is not allowed for '%s=%s'"), + NM_SETTING_IP4_CONFIG_METHOD, priv->method); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ADDRESSES); + return FALSE; + } + } + } else if (!strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { + /* nothing to do */ + } else { + g_set_error_literal (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_METHOD); + return FALSE; + } + + if (priv->dhcp_client_id && !strlen (priv->dhcp_client_id)) { + g_set_error_literal (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID); + return FALSE; + } + + if (priv->dhcp_hostname && !strlen (priv->dhcp_hostname)) { + g_set_error_literal (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME); + return FALSE; + } + + /* Validate addresses */ + for (iter = priv->addresses, l_iter = priv->address_labels, i = 0; + iter && l_iter; + iter = g_slist_next (iter), l_iter = g_slist_next (l_iter), i++) { + NMIP4Address *addr = (NMIP4Address *) iter->data; + const char *label = (const char *) l_iter->data; + guint32 prefix = nm_ip4_address_get_prefix (addr); + + if (!nm_ip4_address_get_address (addr)) { + g_set_error (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY, + _("%d. IPv4 address is invalid"), + i+1); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ADDRESSES); + return FALSE; + } + + if (!prefix || prefix > 32) { + g_set_error (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY, + _("%d. IPv4 address has invalid prefix"), + i+1); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ADDRESSES); + return FALSE; + } + + if (label && !verify_label (label)) { + g_set_error (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY, + _("%d. IPv4 address has invalid label '%s'"), + i+1, label); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, "address-labels"); + return FALSE; + } + } + + if (iter || l_iter) { + g_set_error (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY, + _("IPv4 address / label count mismatch (%d vs %d)"), + g_slist_length (priv->addresses), + g_slist_length (priv->address_labels)); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, "address-labels"); + return FALSE; + } + + /* Validate routes */ + for (iter = priv->routes, i = 0; iter; iter = g_slist_next (iter), i++) { + NMIP4Route *route = (NMIP4Route *) iter->data; + guint32 prefix = nm_ip4_route_get_prefix (route); + + if (!nm_ip4_route_get_dest (route)) { + g_set_error (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY, + _("%d. route is invalid"), + i+1); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ROUTES); + return FALSE; + } + + if (!prefix || prefix > 32) { + g_set_error (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY, + _("%d. route has invalid prefix"), + i+1); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ROUTES); + return FALSE; + } + } + + return TRUE; +} + + +static void +nm_setting_ip4_config_init (NMSettingIP4Config *setting) +{ + NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + + + priv->dns = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3); +} + +static void +finalize (GObject *object) +{ + NMSettingIP4Config *self = NM_SETTING_IP4_CONFIG (object); + NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (self); + + g_free (priv->method); + g_free (priv->dhcp_hostname); + g_free (priv->dhcp_client_id); + + g_array_free (priv->dns, TRUE); + + g_slist_free_full (priv->dns_search, g_free); + g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip4_address_unref); + g_slist_free_full (priv->address_labels, g_free); + g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref); + + G_OBJECT_CLASS (nm_setting_ip4_config_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingIP4Config *setting = NM_SETTING_IP4_CONFIG (object); + NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + GSList *iter; + + switch (prop_id) { + case PROP_METHOD: + g_free (priv->method); + priv->method = g_value_dup_string (value); + break; + case PROP_DNS: + g_array_free (priv->dns, TRUE); + priv->dns = g_value_dup_boxed (value); + if (!priv->dns) + priv->dns = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3); + break; + case PROP_DNS_SEARCH: + g_slist_free_full (priv->dns_search, g_free); + priv->dns_search = g_value_dup_boxed (value); + break; + case PROP_ADDRESSES: + g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip4_address_unref); + priv->addresses = nm_utils_ip4_addresses_from_gvalue (value); + + if (g_slist_length (priv->addresses) != g_slist_length (priv->address_labels)) { + g_slist_free_full (priv->address_labels, g_free); + priv->address_labels = NULL; + for (iter = priv->addresses; iter; iter = iter->next) + priv->address_labels = g_slist_prepend (priv->address_labels, NULL); + } + break; + case PROP_ADDRESS_LABELS: + g_slist_free_full (priv->address_labels, g_free); + priv->address_labels = g_value_dup_boxed (value); + /* NULLs get converted to "" when this is sent over D-Bus. */ + for (iter = priv->address_labels; iter; iter = iter->next) { + if (!g_strcmp0 (iter->data, "")) { + g_free (iter->data); + iter->data = NULL; + } + } + break; + case PROP_ROUTES: + g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref); + priv->routes = nm_utils_ip4_routes_from_gvalue (value); + break; + case PROP_IGNORE_AUTO_ROUTES: + priv->ignore_auto_routes = g_value_get_boolean (value); + break; + case PROP_IGNORE_AUTO_DNS: + priv->ignore_auto_dns = g_value_get_boolean (value); + break; + case PROP_DHCP_CLIENT_ID: + g_free (priv->dhcp_client_id); + priv->dhcp_client_id = g_value_dup_string (value); + break; + case PROP_DHCP_SEND_HOSTNAME: + priv->dhcp_send_hostname = g_value_get_boolean (value); + break; + case PROP_DHCP_HOSTNAME: + g_free (priv->dhcp_hostname); + priv->dhcp_hostname = g_value_dup_string (value); + break; + case PROP_NEVER_DEFAULT: + priv->never_default = g_value_get_boolean (value); + break; + case PROP_MAY_FAIL: + priv->may_fail = g_value_get_boolean (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) +{ + NMSettingIP4Config *setting = NM_SETTING_IP4_CONFIG (object); + NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + + switch (prop_id) { + case PROP_METHOD: + g_value_set_string (value, nm_setting_ip4_config_get_method (setting)); + break; + case PROP_DNS: + g_value_set_boxed (value, priv->dns); + break; + case PROP_DNS_SEARCH: + g_value_set_boxed (value, priv->dns_search); + break; + case PROP_ADDRESSES: + nm_utils_ip4_addresses_to_gvalue (priv->addresses, value); + break; + case PROP_ADDRESS_LABELS: + g_value_set_boxed (value, priv->address_labels); + break; + case PROP_ROUTES: + nm_utils_ip4_routes_to_gvalue (priv->routes, value); + break; + case PROP_IGNORE_AUTO_ROUTES: + g_value_set_boolean (value, nm_setting_ip4_config_get_ignore_auto_routes (setting)); + break; + case PROP_IGNORE_AUTO_DNS: + g_value_set_boolean (value, nm_setting_ip4_config_get_ignore_auto_dns (setting)); + break; + case PROP_DHCP_CLIENT_ID: + g_value_set_string (value, nm_setting_ip4_config_get_dhcp_client_id (setting)); + break; + case PROP_DHCP_SEND_HOSTNAME: + g_value_set_boolean (value, nm_setting_ip4_config_get_dhcp_send_hostname (setting)); + break; + case PROP_DHCP_HOSTNAME: + g_value_set_string (value, nm_setting_ip4_config_get_dhcp_hostname (setting)); + break; + case PROP_NEVER_DEFAULT: + g_value_set_boolean (value, priv->never_default); + break; + case PROP_MAY_FAIL: + g_value_set_boolean (value, priv->may_fail); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingIP4ConfigPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + + /* Properties */ + /** + * NMSettingIP4Config:method: + * + * IPv4 configuration method. If "auto" is specified then the appropriate + * automatic method (DHCP, PPP, etc) is used for the interface and most + * other properties can be left unset. If "link-local" is specified, then a + * link-local address in the 169.254/16 range will be assigned to the + * interface. If "manual" is specified, static IP addressing is used and at + * least one IP address must be given in the "addresses" property. If + * "shared" is specified (indicating that this connection will provide + * network access to other computers) then the interface is assigned an + * address in the 10.42.x.1/24 range and a DHCP and forwarding DNS server + * are started, and the interface is NAT-ed to the current default network + * connection. "disabled" means IPv4 will not be used on this connection. + * This property must be set. + **/ + g_object_class_install_property + (object_class, PROP_METHOD, + g_param_spec_string (NM_SETTING_IP4_CONFIG_METHOD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP4Config:dns: + * + * List of DNS servers (network byte order). For the "auto" method, these + * DNS servers are appended to those (if any) returned by automatic + * configuration. DNS servers cannot be used with the "shared", + * "link-local", or "disabled" methods as there is no upstream network. In + * all other methods, these DNS servers are used as the only DNS servers for + * this connection. + **/ + g_object_class_install_property + (object_class, PROP_DNS, + _nm_param_spec_specialized (NM_SETTING_IP4_CONFIG_DNS, "", "", + DBUS_TYPE_G_UINT_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP4Config:dns-search: + * + * List of DNS search domains. For the "auto" method, these search domains + * are appended to those returned by automatic configuration. Search domains + * cannot be used with the "shared", "link-local", or "disabled" methods as + * there is no upstream network. In all other methods, these search domains + * are used as the only search domains for this connection. + **/ + g_object_class_install_property + (object_class, PROP_DNS_SEARCH, + _nm_param_spec_specialized (NM_SETTING_IP4_CONFIG_DNS_SEARCH, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP4Config:addresses: + * + * 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. For the "auto" method, given IP addresses are appended + * to those returned by automatic configuration. Addresses cannot be used + * with the "shared", "link-local", or "disabled" methods as addressing is + * either automatic or disabled with these methods. + **/ + g_object_class_install_property + (object_class, PROP_ADDRESSES, + _nm_param_spec_specialized (NM_SETTING_IP4_CONFIG_ADDRESSES, "", "", + DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP4Config:address-labels: + * + * Internal use only. + **/ + g_object_class_install_property + (object_class, PROP_ADDRESS_LABELS, + _nm_param_spec_specialized ("address-labels", "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP4Config:routes: + * + * 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. For the "auto" method, given + * IP routes are appended to those returned by automatic configuration. + * Routes cannot be used with the "shared", "link-local", or "disabled" + * methods because there is no upstream network. + **/ + g_object_class_install_property + (object_class, PROP_ROUTES, + _nm_param_spec_specialized (NM_SETTING_IP4_CONFIG_ROUTES, "", "", + DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP4Config:ignore-auto-routes: + * + * When the method is set to "auto" and this property to %TRUE, + * automatically configured routes are ignored and only routes specified in + * the #NMSettingIP4Config:routes property, if any, are used. + **/ + g_object_class_install_property + (object_class, PROP_IGNORE_AUTO_ROUTES, + g_param_spec_boolean (NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP4Config:ignore-auto-dns: + * + * When the method is set to "auto" and this property to %TRUE, + * automatically configured nameservers and search domains are ignored and + * only nameservers and search domains specified in the + * #NMSettingIP4Config:dns and #NMSettingIP4Config:dns-search properties, if + * any, are used. + **/ + g_object_class_install_property + (object_class, PROP_IGNORE_AUTO_DNS, + g_param_spec_boolean (NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP4Config:dhcp-client-id: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_DHCP_CLIENT_ID, + g_param_spec_string (NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP4Config:dhcp-send-hostname: + * + * 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 + * #NMSettingIP4Config:dhcp-hostname property is empty and this property is + * %TRUE, the current persistent hostname of the computer is sent. + **/ + g_object_class_install_property + (object_class, PROP_DHCP_SEND_HOSTNAME, + g_param_spec_boolean (NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME, "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP4Config:dhcp-hostname: + * + * If the #NMSettingIP4Config:dhcp-send-hostname property is %TRUE, then the + * specified name will be sent to the DHCP server when acquiring a lease. + **/ + g_object_class_install_property + (object_class, PROP_DHCP_HOSTNAME, + g_param_spec_string (NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP4Config:never-default: + * + * If %TRUE, this connection will never be the default IPv4 connection, + * meaning it will never be assigned the default route by NetworkManager. + **/ + g_object_class_install_property + (object_class, PROP_NEVER_DEFAULT, + g_param_spec_boolean (NM_SETTING_IP4_CONFIG_NEVER_DEFAULT, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP4Config:may-fail: + * + * If %TRUE, allow overall network configuration to proceed even if IPv4 + * configuration 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 allows the overall + * network configuration to succeed if IPv4 configuration fails but IPv6 + * configuration completes successfully. + **/ + g_object_class_install_property + (object_class, PROP_MAY_FAIL, + g_param_spec_boolean (NM_SETTING_IP4_CONFIG_MAY_FAIL, "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} + + +struct NMIP4Address { + guint32 refcount; + guint32 address; /* network byte order */ + guint32 prefix; + guint32 gateway; /* network byte order */ +}; + +/** + * nm_ip4_address_new: + * + * Creates and returns a new #NMIP4Address object. + * + * Returns: (transfer full): the new empty #NMIP4Address object + **/ +NMIP4Address * +nm_ip4_address_new (void) +{ + NMIP4Address *address; + + address = g_malloc0 (sizeof (NMIP4Address)); + address->refcount = 1; + return address; +} + +/** + * nm_ip4_address_dup: + * @source: the #NMIP4Address object to copy + * + * Copies a given #NMIP4Address object and returns the copy. + * + * Returns: (transfer full): the copy of the given #NMIP4Address copy + **/ +NMIP4Address * +nm_ip4_address_dup (NMIP4Address *source) +{ + NMIP4Address *address; + + g_return_val_if_fail (source != NULL, NULL); + g_return_val_if_fail (source->refcount > 0, NULL); + + address = nm_ip4_address_new (); + address->address = source->address; + address->prefix = source->prefix; + address->gateway = source->gateway; + + return address; +} + +/** + * nm_ip4_address_ref: + * @address: the #NMIP4Address + * + * Increases the reference count of the object. + **/ +void +nm_ip4_address_ref (NMIP4Address *address) +{ + g_return_if_fail (address != NULL); + g_return_if_fail (address->refcount > 0); + + address->refcount++; +} + +/** + * nm_ip4_address_unref: + * @address: the #NMIP4Address + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + **/ +void +nm_ip4_address_unref (NMIP4Address *address) +{ + g_return_if_fail (address != NULL); + g_return_if_fail (address->refcount > 0); + + address->refcount--; + if (address->refcount == 0) { + memset (address, 0, sizeof (NMIP4Address)); + g_free (address); + } +} + +/** + * nm_ip4_address_compare: + * @address: the #NMIP4Address + * @other: the #NMIP4Address to compare @address to. + * + * Determines if two #NMIP4Address objects contain the same values. + * + * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. + **/ +gboolean +nm_ip4_address_compare (NMIP4Address *address, NMIP4Address *other) +{ + g_return_val_if_fail (address != NULL, FALSE); + g_return_val_if_fail (address->refcount > 0, FALSE); + + g_return_val_if_fail (other != NULL, FALSE); + g_return_val_if_fail (other->refcount > 0, FALSE); + + if ( address->address != other->address + || address->prefix != other->prefix + || address->gateway != other->gateway) + return FALSE; + return TRUE; +} + +/** + * nm_ip4_address_get_address: + * @address: the #NMIP4Address + * + * Gets the IPv4 address property of this address object. + * + * Returns: the IPv4 address in network byte order + **/ +guint32 +nm_ip4_address_get_address (NMIP4Address *address) +{ + g_return_val_if_fail (address != NULL, 0); + g_return_val_if_fail (address->refcount > 0, 0); + + return address->address; +} + +/** + * nm_ip4_address_set_address: + * @address: the #NMIP4Address + * @addr: the IPv4 address in network byte order + * + * Sets the IPv4 address property of this object. + **/ +void +nm_ip4_address_set_address (NMIP4Address *address, guint32 addr) +{ + g_return_if_fail (address != NULL); + g_return_if_fail (address->refcount > 0); + + address->address = addr; +} + +/** + * nm_ip4_address_get_prefix: + * @address: the #NMIP4Address + * + * Gets the IPv4 address prefix (ie "24" or "30" etc) property of this address + * object. + * + * Returns: the IPv4 address prefix + **/ +guint32 +nm_ip4_address_get_prefix (NMIP4Address *address) +{ + g_return_val_if_fail (address != NULL, 0); + g_return_val_if_fail (address->refcount > 0, 0); + + return address->prefix; +} + +/** + * nm_ip4_address_set_prefix: + * @address: the #NMIP4Address + * @prefix: the address prefix, a number between 1 and 32 inclusive + * + * Sets the IPv4 address prefix. + **/ +void +nm_ip4_address_set_prefix (NMIP4Address *address, guint32 prefix) +{ + g_return_if_fail (address != NULL); + g_return_if_fail (address->refcount > 0); + g_return_if_fail (prefix <= 32); + g_return_if_fail (prefix > 0); + + address->prefix = prefix; +} + +/** + * nm_ip4_address_get_gateway: + * @address: the #NMIP4Address + * + * Gets the IPv4 default gateway property of this address object. + * + * Returns: the IPv4 gateway address in network byte order + **/ +guint32 +nm_ip4_address_get_gateway (NMIP4Address *address) +{ + g_return_val_if_fail (address != NULL, 0); + g_return_val_if_fail (address->refcount > 0, 0); + + return address->gateway; +} + +/** + * nm_ip4_address_set_gateway: + * @address: the #NMIP4Address + * @gateway: the IPv4 default gateway in network byte order + * + * Sets the IPv4 default gateway property of this address object. + **/ +void +nm_ip4_address_set_gateway (NMIP4Address *address, guint32 gateway) +{ + g_return_if_fail (address != NULL); + g_return_if_fail (address->refcount > 0); + + address->gateway = gateway; +} + + +struct NMIP4Route { + guint32 refcount; + + guint32 dest; /* network byte order */ + guint32 prefix; + guint32 next_hop; /* network byte order */ + guint32 metric; /* lower metric == more preferred */ +}; + +/** + * nm_ip4_route_new: + * + * Creates and returns a new #NMIP4Route object. + * + * Returns: (transfer full): the new empty #NMIP4Route object + **/ +NMIP4Route * +nm_ip4_route_new (void) +{ + NMIP4Route *route; + + route = g_malloc0 (sizeof (NMIP4Route)); + route->refcount = 1; + return route; +} + +/** + * nm_ip4_route_dup: + * @source: the #NMIP4Route object to copy + * + * Copies a given #NMIP4Route object and returns the copy. + * + * Returns: (transfer full): the copy of the given #NMIP4Route copy + **/ +NMIP4Route * +nm_ip4_route_dup (NMIP4Route *source) +{ + NMIP4Route *route; + + g_return_val_if_fail (source != NULL, NULL); + g_return_val_if_fail (source->refcount > 0, NULL); + + route = nm_ip4_route_new (); + route->dest = source->dest; + route->prefix = source->prefix; + route->next_hop = source->next_hop; + route->metric = source->metric; + + return route; +} + +/** + * nm_ip4_route_ref: + * @route: the #NMIP4Route + * + * Increases the reference count of the object. + **/ +void +nm_ip4_route_ref (NMIP4Route *route) +{ + g_return_if_fail (route != NULL); + g_return_if_fail (route->refcount > 0); + + route->refcount++; +} + +/** + * nm_ip4_route_unref: + * @route: the #NMIP4Route + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + **/ +void +nm_ip4_route_unref (NMIP4Route *route) +{ + g_return_if_fail (route != NULL); + g_return_if_fail (route->refcount > 0); + + route->refcount--; + if (route->refcount == 0) { + memset (route, 0, sizeof (NMIP4Route)); + g_free (route); + } +} + +/** + * nm_ip4_route_compare: + * @route: the #NMIP4Route + * @other: the #NMIP4Route to compare @route to. + * + * Determines if two #NMIP4Route objects contain the same values. + * + * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. + **/ +gboolean +nm_ip4_route_compare (NMIP4Route *route, NMIP4Route *other) +{ + g_return_val_if_fail (route != NULL, FALSE); + g_return_val_if_fail (route->refcount > 0, FALSE); + + g_return_val_if_fail (other != NULL, FALSE); + g_return_val_if_fail (other->refcount > 0, FALSE); + + if ( route->dest != other->dest + || route->prefix != other->prefix + || route->next_hop != other->next_hop + || route->metric != other->metric) + return FALSE; + return TRUE; +} + +/** + * nm_ip4_route_get_dest: + * @route: the #NMIP4Route + * + * Gets the IPv4 destination address property of this route object. + * + * Returns: the IPv4 address in network byte order + **/ +guint32 +nm_ip4_route_get_dest (NMIP4Route *route) +{ + g_return_val_if_fail (route != NULL, 0); + g_return_val_if_fail (route->refcount > 0, 0); + + return route->dest; +} + +/** + * nm_ip4_route_set_dest: + * @route: the #NMIP4Route + * @dest: the destination address in network byte order + * + * Sets the IPv4 destination address property of this route object. + **/ +void +nm_ip4_route_set_dest (NMIP4Route *route, guint32 dest) +{ + g_return_if_fail (route != NULL); + g_return_if_fail (route->refcount > 0); + + route->dest = dest; +} + +/** + * nm_ip4_route_get_prefix: + * @route: the #NMIP4Route + * + * Gets the IPv4 prefix (ie "24" or "30" etc) of this route. + * + * Returns: the IPv4 prefix + **/ +guint32 +nm_ip4_route_get_prefix (NMIP4Route *route) +{ + g_return_val_if_fail (route != NULL, 0); + g_return_val_if_fail (route->refcount > 0, 0); + + return route->prefix; +} + +/** + * nm_ip4_route_set_prefix: + * @route: the #NMIP4Route + * @prefix: the prefix, a number between 1 and 32 inclusive + * + * Sets the IPv4 prefix of this route. + **/ +void +nm_ip4_route_set_prefix (NMIP4Route *route, guint32 prefix) +{ + g_return_if_fail (route != NULL); + g_return_if_fail (route->refcount > 0); + g_return_if_fail (prefix <= 32); + g_return_if_fail (prefix > 0); + + route->prefix = prefix; +} + +/** + * nm_ip4_route_get_next_hop: + * @route: the #NMIP4Route + * + * Gets the IPv4 address of the next hop of this route. + * + * Returns: the IPv4 address in network byte order + **/ +guint32 +nm_ip4_route_get_next_hop (NMIP4Route *route) +{ + g_return_val_if_fail (route != NULL, 0); + g_return_val_if_fail (route->refcount > 0, 0); + + return route->next_hop; +} + +/** + * nm_ip4_route_set_next_hop: + * @route: the #NMIP4Route + * @next_hop: the IPv4 address of the next hop in network byte order + * + * Sets the IPv4 address of the next hop of this route. + **/ +void +nm_ip4_route_set_next_hop (NMIP4Route *route, guint32 next_hop) +{ + g_return_if_fail (route != NULL); + g_return_if_fail (route->refcount > 0); + + route->next_hop = next_hop; +} + +/** + * nm_ip4_route_get_metric: + * @route: the #NMIP4Route + * + * Gets the route metric property of this route object; lower values indicate + * "better" or more preferred routes. + * + * Returns: the route metric + **/ +guint32 +nm_ip4_route_get_metric (NMIP4Route *route) +{ + g_return_val_if_fail (route != NULL, 0); + g_return_val_if_fail (route->refcount > 0, 0); + + return route->metric; +} + +/** + * nm_ip4_route_set_metric: + * @route: the #NMIP4Route + * @metric: the route metric + * + * Sets the route metric property of this route object; lower values indicate + * "better" or more preferred routes. + **/ +void +nm_ip4_route_set_metric (NMIP4Route *route, guint32 metric) +{ + g_return_if_fail (route != NULL); + g_return_if_fail (route->refcount > 0); + + route->metric = metric; +} diff --git a/libnm-core/nm-setting-ip4-config.h b/libnm-core/nm-setting-ip4-config.h new file mode 100644 index 0000000000..1b7c23072d --- /dev/null +++ b/libnm-core/nm-setting-ip4-config.h @@ -0,0 +1,229 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SETTING_IP4_CONFIG_H +#define NM_SETTING_IP4_CONFIG_H + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_IP4_CONFIG (nm_setting_ip4_config_get_type ()) +#define NM_SETTING_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_IP4_CONFIG, NMSettingIP4Config)) +#define NM_SETTING_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_IP4CONFIG, NMSettingIP4ConfigClass)) +#define NM_IS_SETTING_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_IP4_CONFIG)) +#define NM_IS_SETTING_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_IP4_CONFIG)) +#define NM_SETTING_IP4_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_IP4_CONFIG, NMSettingIP4ConfigClass)) + +#define NM_SETTING_IP4_CONFIG_SETTING_NAME "ipv4" + +/** + * NMSettingIP4ConfigError: + * @NM_SETTING_IP4_CONFIG_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_IP4_CONFIG_ERROR_MISSING_PROPERTY: the property was missing and is + * required + * @NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD: the property's value is + * not valid with the given IP4 method + */ +typedef enum { + NM_SETTING_IP4_CONFIG_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_IP4_CONFIG_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ + NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD /*< nick=NotAllowedForMethod >*/ +} NMSettingIP4ConfigError; + +#define NM_SETTING_IP4_CONFIG_ERROR nm_setting_ip4_config_error_quark () +GQuark nm_setting_ip4_config_error_quark (void); + +#define NM_SETTING_IP4_CONFIG_METHOD "method" +#define NM_SETTING_IP4_CONFIG_DNS "dns" +#define NM_SETTING_IP4_CONFIG_DNS_SEARCH "dns-search" +#define NM_SETTING_IP4_CONFIG_ADDRESSES "addresses" +#define NM_SETTING_IP4_CONFIG_ROUTES "routes" +#define NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES "ignore-auto-routes" +#define NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS "ignore-auto-dns" +#define NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID "dhcp-client-id" +#define NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME "dhcp-send-hostname" +#define NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME "dhcp-hostname" +#define NM_SETTING_IP4_CONFIG_NEVER_DEFAULT "never-default" +#define NM_SETTING_IP4_CONFIG_MAY_FAIL "may-fail" + +/** + * NM_SETTING_IP4_CONFIG_METHOD_AUTO: + * + * IPv4 configuration should be automatically determined via a method appropriate + * for the hardware interface, ie DHCP or PPP or some other device-specific + * manner. + */ +#define NM_SETTING_IP4_CONFIG_METHOD_AUTO "auto" + +/** + * NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL: + * + * IPv4 configuration should be automatically configured for link-local-only + * operation. + */ +#define NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL "link-local" + +/** + * NM_SETTING_IP4_CONFIG_METHOD_MANUAL: + * + * All necessary IPv4 configuration (addresses, prefix, DNS, etc) is specified + * in the setting's properties. + */ +#define NM_SETTING_IP4_CONFIG_METHOD_MANUAL "manual" + +/** + * NM_SETTING_IP4_CONFIG_METHOD_SHARED: + * + * This connection specifies configuration that allows other computers to + * connect through it to the default network (usually the Internet). The + * connection's interface will be assigned a private address, and a DHCP server, + * caching DNS server, and Network Address Translation (NAT) functionality will + * be started on this connection's interface to allow other devices to connect + * through that interface to the default network. + */ +#define NM_SETTING_IP4_CONFIG_METHOD_SHARED "shared" + +/** + * NM_SETTING_IP4_CONFIG_METHOD_DISABLED: + * + * This connection does not use or require IPv4 address and it should be disabled. + */ +#define NM_SETTING_IP4_CONFIG_METHOD_DISABLED "disabled" + +typedef struct NMIP4Address NMIP4Address; + +GType nm_ip4_address_get_type (void); + +NMIP4Address * nm_ip4_address_new (void); +NMIP4Address * nm_ip4_address_dup (NMIP4Address *source); +void nm_ip4_address_ref (NMIP4Address *address); +void nm_ip4_address_unref (NMIP4Address *address); +/* Return TRUE if addresses are identical */ +gboolean nm_ip4_address_compare (NMIP4Address *address, NMIP4Address *other); + +guint32 nm_ip4_address_get_address (NMIP4Address *address); +void nm_ip4_address_set_address (NMIP4Address *address, + guint32 addr); /* network byte order */ + +guint32 nm_ip4_address_get_prefix (NMIP4Address *address); +void nm_ip4_address_set_prefix (NMIP4Address *address, + guint32 prefix); + +guint32 nm_ip4_address_get_gateway (NMIP4Address *address); +void nm_ip4_address_set_gateway (NMIP4Address *address, + guint32 gateway); /* network byte order */ + +typedef struct NMIP4Route NMIP4Route; + +GType nm_ip4_route_get_type (void); + +NMIP4Route * nm_ip4_route_new (void); +NMIP4Route * nm_ip4_route_dup (NMIP4Route *source); +void nm_ip4_route_ref (NMIP4Route *route); +void nm_ip4_route_unref (NMIP4Route *route); +/* Return TRUE if routes are identical */ +gboolean nm_ip4_route_compare (NMIP4Route *route, NMIP4Route *other); + +guint32 nm_ip4_route_get_dest (NMIP4Route *route); +void nm_ip4_route_set_dest (NMIP4Route *route, + guint32 dest); /* network byte order */ + +guint32 nm_ip4_route_get_prefix (NMIP4Route *route); +void nm_ip4_route_set_prefix (NMIP4Route *route, + guint32 prefix); + +guint32 nm_ip4_route_get_next_hop (NMIP4Route *route); +void nm_ip4_route_set_next_hop (NMIP4Route *route, + guint32 next_hop); /* network byte order */ + +guint32 nm_ip4_route_get_metric (NMIP4Route *route); +void nm_ip4_route_set_metric (NMIP4Route *route, + guint32 metric); + + +typedef struct { + NMSetting parent; +} NMSettingIP4Config; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingIP4ConfigClass; + +GType nm_setting_ip4_config_get_type (void); + +NMSetting * nm_setting_ip4_config_new (void); +const char * nm_setting_ip4_config_get_method (NMSettingIP4Config *setting); + +guint32 nm_setting_ip4_config_get_num_dns (NMSettingIP4Config *setting); +guint32 nm_setting_ip4_config_get_dns (NMSettingIP4Config *setting, guint32 i); +gboolean nm_setting_ip4_config_add_dns (NMSettingIP4Config *setting, guint32 dns); +void nm_setting_ip4_config_remove_dns (NMSettingIP4Config *setting, guint32 i); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_ip4_config_remove_dns_by_value (NMSettingIP4Config *setting, guint32 dns); +void nm_setting_ip4_config_clear_dns (NMSettingIP4Config *setting); + +guint32 nm_setting_ip4_config_get_num_dns_searches (NMSettingIP4Config *setting); +const char * nm_setting_ip4_config_get_dns_search (NMSettingIP4Config *setting, guint32 i); +gboolean nm_setting_ip4_config_add_dns_search (NMSettingIP4Config *setting, const char *dns_search); +void nm_setting_ip4_config_remove_dns_search (NMSettingIP4Config *setting, guint32 i); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_ip4_config_remove_dns_search_by_value (NMSettingIP4Config *setting, const char *dns_search); +void nm_setting_ip4_config_clear_dns_searches (NMSettingIP4Config *setting); + +guint32 nm_setting_ip4_config_get_num_addresses (NMSettingIP4Config *setting); +NMIP4Address *nm_setting_ip4_config_get_address (NMSettingIP4Config *setting, guint32 i); +gboolean nm_setting_ip4_config_add_address (NMSettingIP4Config *setting, NMIP4Address *address); +void nm_setting_ip4_config_remove_address (NMSettingIP4Config *setting, guint32 i); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_ip4_config_remove_address_by_value (NMSettingIP4Config *setting, NMIP4Address *address); +void nm_setting_ip4_config_clear_addresses (NMSettingIP4Config *setting); + +guint32 nm_setting_ip4_config_get_num_routes (NMSettingIP4Config *setting); +NMIP4Route * nm_setting_ip4_config_get_route (NMSettingIP4Config *setting, guint32 i); +gboolean nm_setting_ip4_config_add_route (NMSettingIP4Config *setting, NMIP4Route *route); +void nm_setting_ip4_config_remove_route (NMSettingIP4Config *setting, guint32 i); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_ip4_config_remove_route_by_value (NMSettingIP4Config *setting, NMIP4Route *route); +void nm_setting_ip4_config_clear_routes (NMSettingIP4Config *setting); + +gboolean nm_setting_ip4_config_get_ignore_auto_routes (NMSettingIP4Config *setting); +gboolean nm_setting_ip4_config_get_ignore_auto_dns (NMSettingIP4Config *setting); +const char * nm_setting_ip4_config_get_dhcp_client_id (NMSettingIP4Config *setting); +gboolean nm_setting_ip4_config_get_dhcp_send_hostname (NMSettingIP4Config *setting); +const char * nm_setting_ip4_config_get_dhcp_hostname (NMSettingIP4Config *setting); + +gboolean nm_setting_ip4_config_get_never_default (NMSettingIP4Config *setting); + +gboolean nm_setting_ip4_config_get_may_fail (NMSettingIP4Config *setting); + +G_END_DECLS + +#endif /* NM_SETTING_IP4_CONFIG_H */ diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c new file mode 100644 index 0000000000..49c32dd662 --- /dev/null +++ b/libnm-core/nm-setting-ip6-config.c @@ -0,0 +1,1683 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + */ + +#include <string.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "nm-setting-ip6-config.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-dbus-glib-types.h" +#include "nm-glib-compat.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-ip6-config + * @short_description: Describes IPv6 addressing, routing, and name service properties + * @include: nm-setting-ip6-config.h + * + * The #NMSettingIP6Config object is a #NMSetting subclass that describes + * properties related to IPv6 addressing, routing, and Domain Name Service + **/ + +/** + * nm_setting_ip6_config_error_quark: + * + * Registers an error quark for #NMSettingIP6Config if necessary. + * + * Returns: the error quark used for #NMSettingIP6Config errors. + **/ +GQuark +nm_setting_ip6_config_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-ip6-config-error-quark"); + return quark; +} + +G_DEFINE_BOXED_TYPE (NMIP6Address, nm_ip6_address, nm_ip6_address_dup, nm_ip6_address_unref) +G_DEFINE_BOXED_TYPE (NMIP6Route, nm_ip6_route, nm_ip6_route_dup, nm_ip6_route_unref) + +G_DEFINE_TYPE_WITH_CODE (NMSettingIP6Config, nm_setting_ip6_config, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_IP6_CONFIG_SETTING_NAME, + g_define_type_id, + 4, + NM_SETTING_IP6_CONFIG_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_IP6_CONFIG) + +#define NM_SETTING_IP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP6_CONFIG, NMSettingIP6ConfigPrivate)) + +typedef struct { + char *method; + char *dhcp_hostname; + GSList *dns; /* array of struct in6_addr */ + GSList *dns_search; /* list of strings */ + GSList *addresses; /* array of NMIP6Address */ + GSList *routes; /* array of NMIP6Route */ + gboolean ignore_auto_routes; + gboolean ignore_auto_dns; + gboolean never_default; + gboolean may_fail; + NMSettingIP6ConfigPrivacy ip6_privacy; +} NMSettingIP6ConfigPrivate; + + +enum { + PROP_0, + PROP_METHOD, + PROP_DHCP_HOSTNAME, + PROP_DNS, + PROP_DNS_SEARCH, + PROP_ADDRESSES, + PROP_ROUTES, + PROP_IGNORE_AUTO_ROUTES, + PROP_IGNORE_AUTO_DNS, + PROP_NEVER_DEFAULT, + PROP_MAY_FAIL, + PROP_IP6_PRIVACY, + + LAST_PROP +}; + +/** + * nm_setting_ip6_config_new: + * + * Creates a new #NMSettingIP6Config object with default values. + * + * Returns: (transfer full): the new empty #NMSettingIP6Config object + **/ +NMSetting * +nm_setting_ip6_config_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_IP6_CONFIG, NULL); +} + +/** + * nm_setting_ip6_config_get_method: + * @setting: the #NMSettingIP6Config + * + * Returns: the #NMSettingIP6Config:method property of the setting + **/ +const char * +nm_setting_ip6_config_get_method (NMSettingIP6Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL); + + return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->method; +} + +/** + * nm_setting_ip6_config_get_dhcp_hostname: + * @setting: the #NMSettingIP6Config + * + * Returns the value contained in the #NMSettingIP6Config:dhcp-hostname + * property. + * + * Returns: the configured hostname to send to the DHCP server + * + * Since: 0.9.8 + **/ +const char * +nm_setting_ip6_config_get_dhcp_hostname (NMSettingIP6Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL); + + return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dhcp_hostname; +} + +/** + * nm_setting_ip6_config_get_num_dns: + * @setting: the #NMSettingIP6Config + * + * Returns: the number of configured DNS servers + **/ +guint32 +nm_setting_ip6_config_get_num_dns (NMSettingIP6Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), 0); + + return g_slist_length (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns); +} + +/** + * nm_setting_ip6_config_get_dns: + * @setting: the #NMSettingIP6Config + * @i: index number of the DNS server to return + * + * Returns: (transfer none): the IPv6 address of the DNS server at index @i + **/ +const struct in6_addr * +nm_setting_ip6_config_get_dns (NMSettingIP6Config *setting, guint32 i) +{ + NMSettingIP6ConfigPrivate *priv; + + + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->dns), NULL); + + return (const struct in6_addr *) g_slist_nth_data (priv->dns, i); +} + +/** + * nm_setting_ip6_config_add_dns: + * @setting: the #NMSettingIP6Config + * @dns: the IPv6 address of the DNS server to add + * + * Adds a new DNS server to the setting. + * + * Returns: %TRUE if the DNS server was added; %FALSE if the server was already + * known + **/ +gboolean +nm_setting_ip6_config_add_dns (NMSettingIP6Config *setting, const struct in6_addr *addr) +{ + NMSettingIP6ConfigPrivate *priv; + struct in6_addr *copy; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + for (iter = priv->dns; iter; iter = g_slist_next (iter)) { + if (!memcmp (addr, (struct in6_addr *) iter->data, sizeof (struct in6_addr))) + return FALSE; + } + + copy = g_malloc0 (sizeof (struct in6_addr)); + memcpy (copy, addr, sizeof (struct in6_addr)); + priv->dns = g_slist_append (priv->dns, copy); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS); + + return TRUE; +} + +/** + * nm_setting_ip6_config_remove_dns: + * @setting: the #NMSettingIP6Config + * @i: index number of the DNS server to remove + * + * Removes the DNS server at index @i. + **/ +void +nm_setting_ip6_config_remove_dns (NMSettingIP6Config *setting, guint32 i) +{ + NMSettingIP6ConfigPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting)); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + elt = g_slist_nth (priv->dns, i); + g_return_if_fail (elt != NULL); + + g_free (elt->data); + priv->dns = g_slist_delete_link (priv->dns, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS); +} + +/** + * nm_setting_ip6_config_remove_dns_by_value: + * @setting: the #NMSettingIP6Config + * @dns: the IPv6 address of the DNS server to remove + * + * Removes the DNS server at index @i. + * + * Returns: %TRUE if the DNS server was found and removed; %FALSE if it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_ip6_config_remove_dns_by_value (NMSettingIP6Config *setting, + const struct in6_addr *addr) +{ + NMSettingIP6ConfigPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + for (iter = priv->dns; iter; iter = g_slist_next (iter)) { + if (!memcmp (addr, (struct in6_addr *) iter->data, sizeof (struct in6_addr))) { + priv->dns = g_slist_delete_link (priv->dns, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_ip6_config_clear_dns: + * @setting: the #NMSettingIP6Config + * + * Removes all configured DNS servers. + **/ +void +nm_setting_ip6_config_clear_dns (NMSettingIP6Config *setting) +{ + g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting)); + + g_slist_free_full (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns, g_free); + NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS); +} + +/** + * nm_setting_ip6_config_get_num_dns_searches: + * @setting: the #NMSettingIP6Config + * + * Returns: the number of configured DNS search domains + **/ +guint32 +nm_setting_ip6_config_get_num_dns_searches (NMSettingIP6Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), 0); + + return g_slist_length (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns_search); +} + +/** + * nm_setting_ip6_config_get_dns_search: + * @setting: the #NMSettingIP6Config + * @i: index number of the DNS search domain to return + * + * Returns: the DNS search domain at index @i + **/ +const char * +nm_setting_ip6_config_get_dns_search (NMSettingIP6Config *setting, guint32 i) +{ + NMSettingIP6ConfigPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->dns_search), NULL); + + return (const char *) g_slist_nth_data (priv->dns_search, i); +} + +/** + * nm_setting_ip6_config_add_dns_search: + * @setting: the #NMSettingIP6Config + * @dns_search: the search domain to add + * + * Adds a new DNS search domain to the setting. + * + * Returns: %TRUE if the DNS search domain was added; %FALSE if the search + * domain was already known + **/ +gboolean +nm_setting_ip6_config_add_dns_search (NMSettingIP6Config *setting, + const char *dns_search) +{ + NMSettingIP6ConfigPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE); + g_return_val_if_fail (dns_search != NULL, FALSE); + g_return_val_if_fail (dns_search[0] != '\0', FALSE); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + for (iter = priv->dns_search; iter; iter = g_slist_next (iter)) { + if (!strcmp (dns_search, (char *) iter->data)) + return FALSE; + } + + priv->dns_search = g_slist_append (priv->dns_search, g_strdup (dns_search)); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS_SEARCH); + return TRUE; +} + +/** + * nm_setting_ip6_config_remove_dns_search: + * @setting: the #NMSettingIP6Config + * @i: index number of the DNS search domain + * + * Removes the DNS search domain at index @i. + **/ +void +nm_setting_ip6_config_remove_dns_search (NMSettingIP6Config *setting, guint32 i) +{ + NMSettingIP6ConfigPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting)); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + elt = g_slist_nth (priv->dns_search, i); + g_return_if_fail (elt != NULL); + + g_free (elt->data); + priv->dns_search = g_slist_delete_link (priv->dns_search, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS_SEARCH); +} + +/** + * nm_setting_ip6_config_remove_dns_search_by_value: + * @setting: the #NMSettingIP6Config + * @dns_search: the search domain to remove + * + * Removes the DNS search domain @dns_search. + * + * Returns: %TRUE if the DNS search domain was found and removed; %FALSE if it was not. + * + * Since 0.9.10 + **/ +gboolean +nm_setting_ip6_config_remove_dns_search_by_value (NMSettingIP6Config *setting, + const char *dns_search) +{ + NMSettingIP6ConfigPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE); + g_return_val_if_fail (dns_search != NULL, FALSE); + g_return_val_if_fail (dns_search[0] != '\0', FALSE); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + for (iter = priv->dns_search; iter; iter = g_slist_next (iter)) { + if (!strcmp (dns_search, (char *) iter->data)) { + priv->dns_search = g_slist_delete_link (priv->dns_search, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS_SEARCH); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_ip6_config_clear_dns_searches: + * @setting: the #NMSettingIP6Config + * + * Removes all configured DNS search domains. + **/ +void +nm_setting_ip6_config_clear_dns_searches (NMSettingIP6Config *setting) +{ + g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting)); + + g_slist_free_full (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns_search, g_free); + NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns_search = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS_SEARCH); +} + +/** + * nm_setting_ip6_config_get_num_addresses: + * @setting: the #NMSettingIP6Config + * + * Returns: the number of configured addresses + **/ +guint32 +nm_setting_ip6_config_get_num_addresses (NMSettingIP6Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), 0); + + return g_slist_length (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->addresses); +} + +/** + * nm_setting_ip6_config_get_address: + * @setting: the #NMSettingIP6Config + * @i: index number of the address to return + * + * Returns: the address at index @i + **/ +NMIP6Address * +nm_setting_ip6_config_get_address (NMSettingIP6Config *setting, guint32 i) +{ + NMSettingIP6ConfigPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->addresses), NULL); + + return (NMIP6Address *) g_slist_nth_data (priv->addresses, i); +} + +/** + * nm_setting_ip6_config_add_address: + * @setting: the #NMSettingIP6Config + * @address: the new address to add + * + * Adds a new IPv6 address and associated information to the setting. The + * given address is duplicated internally and is not changed by this function. + * + * Returns: %TRUE if the address was added; %FALSE if the address was already + * known. + **/ +gboolean +nm_setting_ip6_config_add_address (NMSettingIP6Config *setting, + NMIP6Address *address) +{ + NMSettingIP6ConfigPrivate *priv; + NMIP6Address *copy; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE); + g_return_val_if_fail (address != NULL, FALSE); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + for (iter = priv->addresses; iter; iter = g_slist_next (iter)) { + if (nm_ip6_address_compare ((NMIP6Address *) iter->data, address)) + return FALSE; + } + + copy = nm_ip6_address_dup (address); + priv->addresses = g_slist_append (priv->addresses, copy); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ADDRESSES); + return TRUE; +} + +/** + * nm_setting_ip6_config_remove_address: + * @setting: the #NMSettingIP6Config + * @i: index number of the address to remove + * + * Removes the address at index @i. + **/ +void +nm_setting_ip6_config_remove_address (NMSettingIP6Config *setting, guint32 i) +{ + NMSettingIP6ConfigPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting)); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + elt = g_slist_nth (priv->addresses, i); + g_return_if_fail (elt != NULL); + + nm_ip6_address_unref ((NMIP6Address *) elt->data); + priv->addresses = g_slist_delete_link (priv->addresses, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ADDRESSES); +} + +/** + * nm_setting_ip6_config_remove_address_by_value: + * @setting: the #NMSettingIP6Config + * @address: the address to remove + * + * Removes the address @address. + * + * Returns: %TRUE if the address was found and removed; %FALSE if it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_ip6_config_remove_address_by_value (NMSettingIP6Config *setting, + NMIP6Address *address) +{ + NMSettingIP6ConfigPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE); + g_return_val_if_fail (address != NULL, FALSE); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + for (iter = priv->addresses; iter; iter = g_slist_next (iter)) { + if (nm_ip6_address_compare ((NMIP6Address *) iter->data, address)) { + priv->addresses = g_slist_delete_link (priv->addresses, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ADDRESSES); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_ip6_config_clear_addresses: + * @setting: the #NMSettingIP6Config + * + * Removes all configured addresses. + **/ +void +nm_setting_ip6_config_clear_addresses (NMSettingIP6Config *setting) +{ + NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + + g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting)); + + g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip6_address_unref); + priv->addresses = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ADDRESSES); +} + +/** + * nm_setting_ip6_config_get_num_routes: + * @setting: the #NMSettingIP6Config + * + * Returns: the number of configured routes + **/ +guint32 +nm_setting_ip6_config_get_num_routes (NMSettingIP6Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), 0); + + return g_slist_length (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->routes); +} + +/** + * nm_setting_ip6_config_get_route: + * @setting: the #NMSettingIP6Config + * @i: index number of the route to return + * + * Returns: the route at index @i + **/ +NMIP6Route * +nm_setting_ip6_config_get_route (NMSettingIP6Config *setting, guint32 i) +{ + NMSettingIP6ConfigPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->routes), NULL); + + return (NMIP6Route *) g_slist_nth_data (priv->routes, i); +} + +/** + * nm_setting_ip6_config_add_route: + * @setting: the #NMSettingIP6Config + * @route: the route to add + * + * Adds a new IPv6 route and associated information to the setting. The + * given route is duplicated internally and is not changed by this function. + * + * Returns: %TRUE if the route was added; %FALSE if the route was already known. + **/ +gboolean +nm_setting_ip6_config_add_route (NMSettingIP6Config *setting, + NMIP6Route *route) +{ + NMSettingIP6ConfigPrivate *priv; + NMIP6Route *copy; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE); + g_return_val_if_fail (route != NULL, FALSE); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + for (iter = priv->routes; iter; iter = g_slist_next (iter)) { + if (nm_ip6_route_compare ((NMIP6Route *) iter->data, route)) + return FALSE; + } + + copy = nm_ip6_route_dup (route); + priv->routes = g_slist_append (priv->routes, copy); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ROUTES); + return TRUE; +} + +/** + * nm_setting_ip6_config_remove_route: + * @setting: the #NMSettingIP6Config + * @i: index number of the route + * + * Removes the route at index @i. + **/ +void +nm_setting_ip6_config_remove_route (NMSettingIP6Config *setting, guint32 i) +{ + NMSettingIP6ConfigPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting)); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + elt = g_slist_nth (priv->routes, i); + g_return_if_fail (elt != NULL); + + nm_ip6_route_unref ((NMIP6Route *) elt->data); + priv->routes = g_slist_delete_link (priv->routes, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ROUTES); +} + +/** + * nm_setting_ip6_config_remove_route_by_value: + * @setting: the #NMSettingIP6Config + * @route: the route to remove + * + * Removes the route @route. + * + * Returns: %TRUE if the route was found and removed; %FALSE if it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_ip6_config_remove_route_by_value (NMSettingIP6Config *setting, + NMIP6Route *route) +{ + NMSettingIP6ConfigPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE); + g_return_val_if_fail (route != NULL, FALSE); + + priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + for (iter = priv->routes; iter; iter = g_slist_next (iter)) { + if (nm_ip6_route_compare ((NMIP6Route *) iter->data, route)) { + nm_ip6_route_unref ((NMIP6Route *) iter->data); + priv->routes = g_slist_delete_link (priv->routes, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ROUTES); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_ip6_config_clear_routes: + * @setting: the #NMSettingIP6Config + * + * Removes all configured routes. + **/ +void +nm_setting_ip6_config_clear_routes (NMSettingIP6Config *setting) +{ + NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + + g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting)); + + g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip6_route_unref); + priv->routes = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ROUTES); +} + +/** + * nm_setting_ip6_config_get_ignore_auto_routes: + * @setting: the #NMSettingIP6Config + * + * Returns the value contained in the #NMSettingIP6Config:ignore-auto-routes + * property. + * + * Returns: %TRUE if automatically configured (ie via DHCP) routes should be + * ignored. + **/ +gboolean +nm_setting_ip6_config_get_ignore_auto_routes (NMSettingIP6Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE); + + return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->ignore_auto_routes; +} + +/** + * nm_setting_ip6_config_get_ignore_auto_dns: + * @setting: the #NMSettingIP6Config + * + * Returns the value contained in the #NMSettingIP6Config:ignore-auto-dns + * property. + * + * Returns: %TRUE if automatically configured (ie via DHCP or router + * advertisements) DNS information should be ignored. + **/ +gboolean +nm_setting_ip6_config_get_ignore_auto_dns (NMSettingIP6Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE); + + return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->ignore_auto_dns; +} + +/** + * nm_setting_ip6_config_get_never_default: + * @setting: the #NMSettingIP6Config + * + * Returns the value contained in the #NMSettingIP6Config:never-default + * property. + * + * Returns: %TRUE if this connection should never be the default connection + * for IPv6 addressing + **/ +gboolean +nm_setting_ip6_config_get_never_default (NMSettingIP6Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE); + + return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->never_default; +} + +/** + * nm_setting_ip6_config_get_may_fail: + * @setting: the #NMSettingIP6Config + * + * Returns the value contained in the #NMSettingIP6Config:may-fail + * property. + * + * Returns: %TRUE if this connection doesn't require IPv6 addressing to complete + * for the connection to succeed. + **/ +gboolean +nm_setting_ip6_config_get_may_fail (NMSettingIP6Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE); + + return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->may_fail; +} + +/** + * nm_setting_ip6_config_get_ip6_privacy: + * @setting: the #NMSettingIP6Config + * + * Returns the value contained in the #NMSettingIP6Config:ip6-privacy + * property. + * + * Returns: IPv6 Privacy Extensions configuration value (#NMSettingIP6ConfigPrivacy). + **/ +NMSettingIP6ConfigPrivacy +nm_setting_ip6_config_get_ip6_privacy (NMSettingIP6Config *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + + return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->ip6_privacy; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting); + + if (!priv->method) { + g_set_error_literal (error, + NM_SETTING_IP6_CONFIG_ERROR, + NM_SETTING_IP6_CONFIG_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_METHOD); + return FALSE; + } + + if (!strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + if (!priv->addresses) { + g_set_error (error, + NM_SETTING_IP6_CONFIG_ERROR, + NM_SETTING_IP6_CONFIG_ERROR_MISSING_PROPERTY, + _("this property cannot be empty for '%s=%s'"), + NM_SETTING_IP6_CONFIG_METHOD, priv->method); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_ADDRESSES); + return FALSE; + } + } else if ( !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) + || !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) + || !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { + if (g_slist_length (priv->dns)) { + g_set_error (error, + NM_SETTING_IP6_CONFIG_ERROR, + NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD, + _("'%s' not allowed for %s=%s"), + _("this property is not allowed for '%s=%s'"), + NM_SETTING_IP6_CONFIG_METHOD, priv->method); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_DNS); + return FALSE; + } + + if (g_slist_length (priv->dns_search)) { + g_set_error (error, + NM_SETTING_IP6_CONFIG_ERROR, + NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD, + _("this property is not allowed for '%s=%s'"), + NM_SETTING_IP6_CONFIG_METHOD, priv->method); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_DNS_SEARCH); + return FALSE; + } + + if (g_slist_length (priv->addresses)) { + g_set_error (error, + NM_SETTING_IP6_CONFIG_ERROR, + NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD, + _("this property is not allowed for '%s=%s'"), + NM_SETTING_IP6_CONFIG_METHOD, priv->method); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_ADDRESSES); + return FALSE; + } + } else if ( !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) + || !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { + /* nothing to do */ + } else { + g_set_error_literal (error, + NM_SETTING_IP6_CONFIG_ERROR, + NM_SETTING_IP6_CONFIG_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_METHOD); + return FALSE; + } + + if (priv->dhcp_hostname && !strlen (priv->dhcp_hostname)) { + g_set_error_literal (error, + NM_SETTING_IP6_CONFIG_ERROR, + NM_SETTING_IP6_CONFIG_ERROR_INVALID_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME); + return FALSE; + } + + return TRUE; +} + + +static void +nm_setting_ip6_config_init (NMSettingIP6Config *setting) +{ +} + +static void +finalize (GObject *object) +{ + NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (object); + + g_free (priv->method); + g_free (priv->dhcp_hostname); + + g_slist_free_full (priv->dns, g_free); + g_slist_free_full (priv->dns_search, g_free); + g_slist_free_full (priv->addresses, g_free); + g_slist_free_full (priv->routes, g_free); + + G_OBJECT_CLASS (nm_setting_ip6_config_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_METHOD: + g_free (priv->method); + priv->method = g_value_dup_string (value); + break; + case PROP_DNS: + g_slist_free_full (priv->dns, g_free); + priv->dns = nm_utils_ip6_dns_from_gvalue (value); + break; + case PROP_DNS_SEARCH: + g_slist_free_full (priv->dns_search, g_free); + priv->dns_search = g_value_dup_boxed (value); + break; + case PROP_ADDRESSES: + g_slist_free_full (priv->addresses, g_free); + priv->addresses = nm_utils_ip6_addresses_from_gvalue (value); + break; + case PROP_ROUTES: + g_slist_free_full (priv->routes, g_free); + priv->routes = nm_utils_ip6_routes_from_gvalue (value); + break; + case PROP_IGNORE_AUTO_ROUTES: + priv->ignore_auto_routes = g_value_get_boolean (value); + break; + case PROP_IGNORE_AUTO_DNS: + priv->ignore_auto_dns = g_value_get_boolean (value); + break; + case PROP_DHCP_HOSTNAME: + g_free (priv->dhcp_hostname); + priv->dhcp_hostname = g_value_dup_string (value); + break; + case PROP_NEVER_DEFAULT: + priv->never_default = g_value_get_boolean (value); + break; + case PROP_MAY_FAIL: + priv->may_fail = g_value_get_boolean (value); + break; + case PROP_IP6_PRIVACY: + priv->ip6_privacy = g_value_get_int (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) +{ + NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_METHOD: + g_value_set_string (value, priv->method); + break; + case PROP_DNS: + nm_utils_ip6_dns_to_gvalue (priv->dns, value); + break; + case PROP_DNS_SEARCH: + g_value_set_boxed (value, priv->dns_search); + break; + case PROP_ADDRESSES: + nm_utils_ip6_addresses_to_gvalue (priv->addresses, value); + break; + case PROP_ROUTES: + nm_utils_ip6_routes_to_gvalue (priv->routes, value); + break; + case PROP_IGNORE_AUTO_ROUTES: + g_value_set_boolean (value, priv->ignore_auto_routes); + break; + case PROP_IGNORE_AUTO_DNS: + g_value_set_boolean (value, priv->ignore_auto_dns); + break; + case PROP_DHCP_HOSTNAME: + g_value_set_string (value, priv->dhcp_hostname); + break; + case PROP_NEVER_DEFAULT: + g_value_set_boolean (value, priv->never_default); + break; + case PROP_MAY_FAIL: + g_value_set_boolean (value, priv->may_fail); + break; + case PROP_IP6_PRIVACY: + g_value_set_int (value, priv->ip6_privacy); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingIP6ConfigPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + + /* Properties */ + /** + * NMSettingIP6Config:method: + * + * IPv6 configuration method. If "auto" is specified then the appropriate + * automatic method (PPP, router advertisement, etc) is used for the device + * and most other properties can be left unset. To force the use of DHCP + * only, specify "dhcp"; this method is only valid for Ethernet- based + * hardware. If "link-local" is specified, then an IPv6 link-local address + * will be assigned to the interface. If "manual" is specified, static IP + * addressing is used and at least one IP address must be given in the + * "addresses" property. If "ignore" is specified, IPv6 configuration is + * not done. This property must be set. Note: the "shared" method is not + * yet supported. + **/ + g_object_class_install_property + (object_class, PROP_METHOD, + g_param_spec_string (NM_SETTING_IP6_CONFIG_METHOD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP6Config:dhcp-hostname: + * + * The specified name will be sent to the DHCP server when acquiring a + * lease. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_DHCP_HOSTNAME, + g_param_spec_string (NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP6Config:dns: + * + * Array of DNS servers, where each member of the array is a byte array + * containing the IPv6 address of the DNS server (in network byte order). + * For the "auto" method, these DNS servers are appended to those (if any) + * returned by automatic configuration. DNS servers cannot be used with the + * "shared" or "link-local" methods as there is no usptream network. In all + * other methods, these DNS servers are used as the only DNS servers for + * this connection. + **/ + g_object_class_install_property + (object_class, PROP_DNS, + _nm_param_spec_specialized (NM_SETTING_IP6_CONFIG_DNS, "", "", + DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP6Config:dns-search: + * + * List of DNS search domains. For the "auto" method, these search domains + * are appended to those returned by automatic configuration. Search domains + * cannot be used with the "shared" or "link-local" methods as there is no + * upstream network. In all other methods, these search domains are used as + * the only search domains for this connection. + **/ + g_object_class_install_property + (object_class, PROP_DNS_SEARCH, + _nm_param_spec_specialized (NM_SETTING_IP6_CONFIG_DNS_SEARCH, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP6Config:addresses: + * + * Array of IPv6 address structures. Each IPv6 address structure is + * composed of 3 members, the first being a byte array containing the IPv6 + * address (network byte order), the second a 32-bit integer containing the + * IPv6 address prefix, and the third a byte array containing the IPv6 + * address (network byte order) of the gateway associated with this address, + * if any. If no gateway is given, the third element should be given as all + * zeros. For the "auto" method, given IP addresses are appended to those + * returned by automatic configuration. Addresses cannot be used with the + * "shared" or "link-local" methods as the interface is automatically + * assigned an address with these methods. + **/ + g_object_class_install_property + (object_class, PROP_ADDRESSES, + _nm_param_spec_specialized (NM_SETTING_IP6_CONFIG_ADDRESSES, "", "", + DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP6Config:routes: + * + * Array of IPv6 route structures. Each IPv6 route structure is composed of + * 4 members; the first being the destination IPv6 network or address + * (network byte order) as a byte array, the second the destination network + * or address IPv6 prefix, the third being the next-hop IPv6 address + * (network byte order) if any, and the fourth being the route metric. For + * the "auto" method, given IP routes are appended to those returned by + * automatic configuration. Routes cannot be used with the "shared" or + * "link-local" methods because there is no upstream network. + **/ + g_object_class_install_property + (object_class, PROP_ROUTES, + _nm_param_spec_specialized (NM_SETTING_IP6_CONFIG_ROUTES, "", "", + DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP6Config:ignore-auto-routes: + * + * When the method is set to "auto" or "dhcp" and this property is set to + * %TRUE, automatically configured routes are ignored and only routes + * specified in the #NMSettingIP6Config:routes property, if any, are used. + **/ + g_object_class_install_property + (object_class, PROP_IGNORE_AUTO_ROUTES, + g_param_spec_boolean (NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP6Config:ignore-auto-dns: + * + * When the method is set to "auto" or "dhcp" and this property is set to + * %TRUE, automatically configured nameservers and search domains are + * ignored and only nameservers and search domains specified in the + * #NMSettingIP6Config:dns and #NMSettingIP6Config:dns-search properties, if + * any, are used. + **/ + g_object_class_install_property + (object_class, PROP_IGNORE_AUTO_DNS, + g_param_spec_boolean (NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP6Config:never-default: + * + * If %TRUE, this connection will never be the default IPv6 connection, + * meaning it will never be assigned the default IPv6 route by + * NetworkManager. + **/ + g_object_class_install_property + (object_class, PROP_NEVER_DEFAULT, + g_param_spec_boolean (NM_SETTING_IP6_CONFIG_NEVER_DEFAULT, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP6Config:may-fail: + * + * If %TRUE, allow overall network configuration to proceed even if IPv6 + * configuration times out. Note that at least one IP configuration must + * succeed or overall network configuration will still fail. For example, + * in IPv4-only networks, setting this property to %TRUE allows the overall + * network configuration to succeed if IPv6 configuration fails but IPv4 + * configuration completes successfully. + **/ + g_object_class_install_property + (object_class, PROP_MAY_FAIL, + g_param_spec_boolean (NM_SETTING_IP6_CONFIG_MAY_FAIL, "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingIP6Config:ip6-privacy: + * + * 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: 0: disabled, + * 1: enabled (prefer public address), 2: enabled (prefer temporary + * addresses). + **/ + g_object_class_install_property + (object_class, PROP_IP6_PRIVACY, + g_param_spec_int (NM_SETTING_IP6_CONFIG_IP6_PRIVACY, "", "", + NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, + NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} + +/********************************************************************/ + +struct NMIP6Address { + guint32 refcount; + struct in6_addr address; + guint32 prefix; + struct in6_addr gateway; +}; + +/** + * nm_ip6_address_new: + * + * Creates and returns a new #NMIP6Address object. + * + * Returns: (transfer full): the new empty #NMIP6Address object + **/ +NMIP6Address * +nm_ip6_address_new (void) +{ + NMIP6Address *address; + + address = g_malloc0 (sizeof (NMIP6Address)); + address->refcount = 1; + return address; +} + +/** + * nm_ip6_address_dup: + * @source: the #NMIP6Address object to copy + * + * Copies a given #NMIP6Address object and returns the copy. + * + * Returns: (transfer full): the copy of the given #NMIP6Address copy + **/ +NMIP6Address * +nm_ip6_address_dup (NMIP6Address *source) +{ + NMIP6Address *address; + + g_return_val_if_fail (source != NULL, NULL); + g_return_val_if_fail (source->refcount > 0, NULL); + + address = nm_ip6_address_new (); + address->prefix = source->prefix; + memcpy (&address->address, &source->address, sizeof (struct in6_addr)); + memcpy (&address->gateway, &source->gateway, sizeof (struct in6_addr)); + + return address; +} + +/** + * nm_ip6_address_ref: + * @address: the #NMIP6Address + * + * Increases the reference count of the object. + **/ +void +nm_ip6_address_ref (NMIP6Address *address) +{ + g_return_if_fail (address != NULL); + g_return_if_fail (address->refcount > 0); + + address->refcount++; +} + +/** + * nm_ip6_address_unref: + * @address: the #NMIP6Address + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + **/ +void +nm_ip6_address_unref (NMIP6Address *address) +{ + g_return_if_fail (address != NULL); + g_return_if_fail (address->refcount > 0); + + address->refcount--; + if (address->refcount == 0) { + memset (address, 0, sizeof (NMIP6Address)); + g_free (address); + } +} + +/** + * nm_ip6_address_compare: + * @address: the #NMIP6Address + * @other: the #NMIP6Address to compare @address to. + * + * Determines if two #NMIP6Address objects contain the same values. + * + * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. + **/ +gboolean +nm_ip6_address_compare (NMIP6Address *address, NMIP6Address *other) +{ + g_return_val_if_fail (address != NULL, FALSE); + g_return_val_if_fail (address->refcount > 0, FALSE); + + g_return_val_if_fail (other != NULL, FALSE); + g_return_val_if_fail (other->refcount > 0, FALSE); + + if ( memcmp (&address->address, &other->address, sizeof (struct in6_addr)) + || address->prefix != other->prefix + || memcmp (&address->gateway, &other->gateway, sizeof (struct in6_addr))) + return FALSE; + return TRUE; +} + +/** + * nm_ip6_address_get_address: + * @address: the #NMIP6Address + * + * Gets the IPv6 address property of this address object. + * + * Returns: (array fixed-size=16) (element-type guint8) (transfer none): + * the IPv6 address + **/ +const struct in6_addr * +nm_ip6_address_get_address (NMIP6Address *address) +{ + g_return_val_if_fail (address != NULL, NULL); + g_return_val_if_fail (address->refcount > 0, NULL); + + return &address->address; +} + +/** + * nm_ip6_address_set_address: + * @address: the #NMIP6Address + * @addr: the IPv6 address + * + * Sets the IPv6 address property of this object. + **/ +void +nm_ip6_address_set_address (NMIP6Address *address, const struct in6_addr *addr) +{ + g_return_if_fail (address != NULL); + g_return_if_fail (address->refcount > 0); + g_return_if_fail (addr != NULL); + + memcpy (&address->address, addr, sizeof (struct in6_addr)); +} + +/** + * nm_ip6_address_get_prefix: + * @address: the #NMIP6Address + * + * Gets the IPv6 address prefix property of this address object. + * + * Returns: the IPv6 address prefix + **/ +guint32 +nm_ip6_address_get_prefix (NMIP6Address *address) +{ + g_return_val_if_fail (address != NULL, 0); + g_return_val_if_fail (address->refcount > 0, 0); + + return address->prefix; +} + +/** + * nm_ip6_address_set_prefix: + * @address: the #NMIP6Address + * @prefix: the address prefix, a number between 0 and 128 inclusive + * + * Sets the IPv6 address prefix. + **/ +void +nm_ip6_address_set_prefix (NMIP6Address *address, guint32 prefix) +{ + g_return_if_fail (address != NULL); + g_return_if_fail (address->refcount > 0); + g_return_if_fail (prefix <= 128); + g_return_if_fail (prefix > 0); + + address->prefix = prefix; +} + +/** + * nm_ip6_address_get_gateway: + * @address: the #NMIP6Address + * + * Gets the IPv6 default gateway property of this address object. + * + * Returns: (array fixed-size=16) (element-type guint8) (transfer none): + * the IPv6 gateway address + **/ +const struct in6_addr * +nm_ip6_address_get_gateway (NMIP6Address *address) +{ + g_return_val_if_fail (address != NULL, NULL); + g_return_val_if_fail (address->refcount > 0, NULL); + + return &address->gateway; +} + +/** + * nm_ip6_address_set_gateway: + * @address: the #NMIP6Address + * @gateway: the IPv6 default gateway + * + * Sets the IPv6 default gateway property of this address object. + **/ +void +nm_ip6_address_set_gateway (NMIP6Address *address, const struct in6_addr *gateway) +{ + g_return_if_fail (address != NULL); + g_return_if_fail (address->refcount > 0); + g_return_if_fail (gateway != NULL); + + memcpy (&address->gateway, gateway, sizeof (struct in6_addr)); +} + +/********************************************************************/ + +struct NMIP6Route { + guint32 refcount; + + struct in6_addr dest; + guint32 prefix; + struct in6_addr next_hop; + guint32 metric; /* lower metric == more preferred */ +}; + +/** + * nm_ip6_route_new: + * + * Creates and returns a new #NMIP6Route object. + * + * Returns: (transfer full): the new empty #NMIP6Route object + **/ +NMIP6Route * +nm_ip6_route_new (void) +{ + NMIP6Route *route; + + route = g_malloc0 (sizeof (NMIP6Route)); + route->refcount = 1; + return route; +} + +/** + * nm_ip6_route_dup: + * @source: the #NMIP6Route object to copy + * + * Copies a given #NMIP6Route object and returns the copy. + * + * Returns: (transfer full): the copy of the given #NMIP6Route copy + **/ +NMIP6Route * +nm_ip6_route_dup (NMIP6Route *source) +{ + NMIP6Route *route; + + g_return_val_if_fail (source != NULL, NULL); + g_return_val_if_fail (source->refcount > 0, NULL); + + route = nm_ip6_route_new (); + route->prefix = source->prefix; + route->metric = source->metric; + memcpy (&route->dest, &source->dest, sizeof (struct in6_addr)); + memcpy (&route->next_hop, &source->next_hop, sizeof (struct in6_addr)); + + return route; +} + +/** + * nm_ip6_route_ref: + * @route: the #NMIP6Route + * + * Increases the reference count of the object. + **/ +void +nm_ip6_route_ref (NMIP6Route *route) +{ + g_return_if_fail (route != NULL); + g_return_if_fail (route->refcount > 0); + + route->refcount++; +} + +/** + * nm_ip6_route_unref: + * @route: the #NMIP6Route + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + **/ +void +nm_ip6_route_unref (NMIP6Route *route) +{ + g_return_if_fail (route != NULL); + g_return_if_fail (route->refcount > 0); + + route->refcount--; + if (route->refcount == 0) { + memset (route, 0, sizeof (NMIP6Route)); + g_free (route); + } +} + +/** + * nm_ip6_route_compare: + * @route: the #NMIP6Route + * @other: the #NMIP6Route to compare @route to. + * + * Determines if two #NMIP6Route objects contain the same values. + * + * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. + **/ +gboolean +nm_ip6_route_compare (NMIP6Route *route, NMIP6Route *other) +{ + g_return_val_if_fail (route != NULL, FALSE); + g_return_val_if_fail (route->refcount > 0, FALSE); + + g_return_val_if_fail (other != NULL, FALSE); + g_return_val_if_fail (other->refcount > 0, FALSE); + + if ( memcmp (&route->dest, &other->dest, sizeof (struct in6_addr)) + || route->prefix != other->prefix + || memcmp (&route->next_hop, &other->next_hop, sizeof (struct in6_addr)) + || route->metric != other->metric) + return FALSE; + return TRUE; +} + +/** + * nm_ip6_route_get_dest: + * @route: the #NMIP6Route + * + * Gets the IPv6 destination address property of this route object. + * + * Returns: (array fixed-size=16) (element-type guint8) (transfer none): + * the IPv6 address of destination + **/ +const struct in6_addr * +nm_ip6_route_get_dest (NMIP6Route *route) +{ + g_return_val_if_fail (route != NULL, NULL); + g_return_val_if_fail (route->refcount > 0, NULL); + + return &route->dest; +} + +/** + * nm_ip6_route_set_dest: + * @route: the #NMIP6Route + * @dest: the destination address + * + * Sets the IPv6 destination address property of this route object. + **/ +void +nm_ip6_route_set_dest (NMIP6Route *route, const struct in6_addr *dest) +{ + g_return_if_fail (route != NULL); + g_return_if_fail (route->refcount > 0); + g_return_if_fail (dest != NULL); + + memcpy (&route->dest, dest, sizeof (struct in6_addr)); +} + +/** + * nm_ip6_route_get_prefix: + * @route: the #NMIP6Route + * + * Gets the IPv6 prefix (ie "32" or "64" etc) of this route. + * + * Returns: the IPv6 prefix + **/ +guint32 +nm_ip6_route_get_prefix (NMIP6Route *route) +{ + g_return_val_if_fail (route != NULL, 0); + g_return_val_if_fail (route->refcount > 0, 0); + + return route->prefix; +} + +/** + * nm_ip6_route_set_prefix: + * @route: the #NMIP6Route + * @prefix: the prefix, a number between 1 and 128 inclusive + * + * Sets the IPv6 prefix of this route. + **/ +void +nm_ip6_route_set_prefix (NMIP6Route *route, guint32 prefix) +{ + g_return_if_fail (route != NULL); + g_return_if_fail (route->refcount > 0); + g_return_if_fail (prefix <= 128); + g_return_if_fail (prefix > 0); + + route->prefix = prefix; +} + +/** + * nm_ip6_route_get_next_hop: + * @route: the #NMIP6Route + * + * Gets the IPv6 address of the next hop of this route. + * + * Returns: (array fixed-size=16) (element-type guint8) (transfer none): + * the IPv6 address of next hop + **/ +const struct in6_addr * +nm_ip6_route_get_next_hop (NMIP6Route *route) +{ + g_return_val_if_fail (route != NULL, NULL); + g_return_val_if_fail (route->refcount > 0, NULL); + + return &route->next_hop; +} + +/** + * nm_ip6_route_set_next_hop: + * @route: the #NMIP6Route + * @next_hop: the IPv6 address of the next hop + * + * Sets the IPv6 address of the next hop of this route. + **/ +void +nm_ip6_route_set_next_hop (NMIP6Route *route, const struct in6_addr *next_hop) +{ + g_return_if_fail (route != NULL); + g_return_if_fail (route->refcount > 0); + g_return_if_fail (next_hop != NULL); + + memcpy (&route->next_hop, next_hop, sizeof (struct in6_addr)); +} + +/** + * nm_ip6_route_get_metric: + * @route: the #NMIP6Route + * + * Gets the route metric property of this route object; lower values indicate + * "better" or more preferred routes. + * + * Returns: the route metric + **/ +guint32 +nm_ip6_route_get_metric (NMIP6Route *route) +{ + g_return_val_if_fail (route != NULL, 0); + g_return_val_if_fail (route->refcount > 0, 0); + + return route->metric; +} + +/** + * nm_ip6_route_set_metric: + * @route: the #NMIP6Route + * @metric: the route metric + * + * Sets the route metric property of this route object; lower values indicate + * "better" or more preferred routes. + **/ +void +nm_ip6_route_set_metric (NMIP6Route *route, guint32 metric) +{ + g_return_if_fail (route != NULL); + g_return_if_fail (route->refcount > 0); + + route->metric = metric; +} diff --git a/libnm-core/nm-setting-ip6-config.h b/libnm-core/nm-setting-ip6-config.h new file mode 100644 index 0000000000..4486a40e26 --- /dev/null +++ b/libnm-core/nm-setting-ip6-config.h @@ -0,0 +1,256 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + */ + +#ifndef NM_SETTING_IP6_CONFIG_H +#define NM_SETTING_IP6_CONFIG_H + +#include <arpa/inet.h> + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_IP6_CONFIG (nm_setting_ip6_config_get_type ()) +#define NM_SETTING_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_IP6_CONFIG, NMSettingIP6Config)) +#define NM_SETTING_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_IP6CONFIG, NMSettingIP6ConfigClass)) +#define NM_IS_SETTING_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_IP6_CONFIG)) +#define NM_IS_SETTING_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_IP6_CONFIG)) +#define NM_SETTING_IP6_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_IP6_CONFIG, NMSettingIP6ConfigClass)) + +#define NM_SETTING_IP6_CONFIG_SETTING_NAME "ipv6" + +/** + * NMSettingIP6ConfigError: + * @NM_SETTING_IP6_CONFIG_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_IP6_CONFIG_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_IP6_CONFIG_ERROR_MISSING_PROPERTY: the property was missing and is + * required + * @NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD: the property's value is + * not valid with the given IPv6 method + */ +typedef enum { + NM_SETTING_IP6_CONFIG_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_IP6_CONFIG_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_IP6_CONFIG_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ + NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD /*< nick=NotAllowedForMethod >*/ +} NMSettingIP6ConfigError; + +#define NM_SETTING_IP6_CONFIG_ERROR nm_setting_ip6_config_error_quark () +GQuark nm_setting_ip6_config_error_quark (void); + +#define NM_SETTING_IP6_CONFIG_METHOD "method" +#define NM_SETTING_IP6_CONFIG_DNS "dns" +#define NM_SETTING_IP6_CONFIG_DNS_SEARCH "dns-search" +#define NM_SETTING_IP6_CONFIG_ADDRESSES "addresses" +#define NM_SETTING_IP6_CONFIG_ROUTES "routes" +#define NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES "ignore-auto-routes" +#define NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS "ignore-auto-dns" +#define NM_SETTING_IP6_CONFIG_NEVER_DEFAULT "never-default" +#define NM_SETTING_IP6_CONFIG_MAY_FAIL "may-fail" +#define NM_SETTING_IP6_CONFIG_IP6_PRIVACY "ip6-privacy" +#define NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME "dhcp-hostname" + + +/** + * NM_SETTING_IP6_CONFIG_METHOD_IGNORE: + * + * IPv6 is not required or is handled by some other mechanism, and NetworkManager + * should not configure IPv6 for this connection. + */ +#define NM_SETTING_IP6_CONFIG_METHOD_IGNORE "ignore" + +/** + * NM_SETTING_IP6_CONFIG_METHOD_AUTO: + * + * IPv6 configuration should be automatically determined via a method appropriate + * for the hardware interface, ie router advertisements, DHCP, or PPP or some + * other device-specific manner. + */ +#define NM_SETTING_IP6_CONFIG_METHOD_AUTO "auto" + +/** + * NM_SETTING_IP6_CONFIG_METHOD_DHCP: + * + * IPv6 configuration should be automatically determined via DHCPv6 only and + * router advertisements should be ignored. + */ +#define NM_SETTING_IP6_CONFIG_METHOD_DHCP "dhcp" + +/** + * NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL: + * + * IPv6 configuration should be automatically configured for link-local-only + * operation. + */ +#define NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL "link-local" + +/** + * NM_SETTING_IP6_CONFIG_METHOD_MANUAL: + * + * All necessary IPv6 configuration (addresses, prefix, DNS, etc) is specified + * in the setting's properties. + */ +#define NM_SETTING_IP6_CONFIG_METHOD_MANUAL "manual" + +/** + * NM_SETTING_IP6_CONFIG_METHOD_SHARED: + * + * This connection specifies configuration that allows other computers to + * connect through it to the default network (usually the Internet). The + * connection's interface will be assigned a private address, and router + * advertisements, a caching DNS server, and Network Address Translation (NAT) + * functionality will be started on this connection's interface to allow other + * devices to connect through that interface to the default network. (not yet + * supported for IPv6) + */ +#define NM_SETTING_IP6_CONFIG_METHOD_SHARED "shared" + +/** + * NMSettingIP6ConfigPrivacy: + * @NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN: unknown or no value specified + * @NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED: IPv6 Privacy Extensions are disabled + * @NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR: IPv6 Privacy Extensions + * are enabled, but public addresses are preferred over temporary addresses + * @NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR: IPv6 Privacy Extensions + * are enabled and temporary addresses are preferred over public addresses + * + * #NMSettingIP6ConfigPrivacy values indicate if and how IPv6 Privacy + * Extensions are used (RFC4941). + */ +typedef enum { + NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN = -1, + NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED = 0, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR = 1, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR = 2 +} NMSettingIP6ConfigPrivacy; + + +typedef struct NMIP6Address NMIP6Address; + +GType nm_ip6_address_get_type (void); + +NMIP6Address * nm_ip6_address_new (void); +NMIP6Address * nm_ip6_address_dup (NMIP6Address *source); +void nm_ip6_address_ref (NMIP6Address *address); +void nm_ip6_address_unref (NMIP6Address *address); +/* Return TRUE if addresses are identical */ +gboolean nm_ip6_address_compare (NMIP6Address *address, NMIP6Address *other); + +const struct in6_addr *nm_ip6_address_get_address (NMIP6Address *address); +void nm_ip6_address_set_address (NMIP6Address *address, + const struct in6_addr *addr); + +guint32 nm_ip6_address_get_prefix (NMIP6Address *address); +void nm_ip6_address_set_prefix (NMIP6Address *address, + guint32 prefix); + +const struct in6_addr *nm_ip6_address_get_gateway (NMIP6Address *address); +void nm_ip6_address_set_gateway (NMIP6Address *address, + const struct in6_addr *gateway); + +typedef struct NMIP6Route NMIP6Route; + +GType nm_ip6_route_get_type (void); + +NMIP6Route * nm_ip6_route_new (void); +NMIP6Route * nm_ip6_route_dup (NMIP6Route *source); +void nm_ip6_route_ref (NMIP6Route *route); +void nm_ip6_route_unref (NMIP6Route *route); +/* Return TRUE if routes are identical */ +gboolean nm_ip6_route_compare (NMIP6Route *route, NMIP6Route *other); + +const struct in6_addr *nm_ip6_route_get_dest (NMIP6Route *route); +void nm_ip6_route_set_dest (NMIP6Route *route, + const struct in6_addr *dest); + +guint32 nm_ip6_route_get_prefix (NMIP6Route *route); +void nm_ip6_route_set_prefix (NMIP6Route *route, + guint32 prefix); + +const struct in6_addr *nm_ip6_route_get_next_hop (NMIP6Route *route); +void nm_ip6_route_set_next_hop (NMIP6Route *route, + const struct in6_addr *next_hop); + +guint32 nm_ip6_route_get_metric (NMIP6Route *route); +void nm_ip6_route_set_metric (NMIP6Route *route, + guint32 metric); + +typedef struct { + NMSetting parent; +} NMSettingIP6Config; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingIP6ConfigClass; + +GType nm_setting_ip6_config_get_type (void); + +NMSetting * nm_setting_ip6_config_new (void); +const char * nm_setting_ip6_config_get_method (NMSettingIP6Config *setting); + +guint32 nm_setting_ip6_config_get_num_dns (NMSettingIP6Config *setting); +const struct in6_addr *nm_setting_ip6_config_get_dns (NMSettingIP6Config *setting, guint32 i); +gboolean nm_setting_ip6_config_add_dns (NMSettingIP6Config *setting, const struct in6_addr *dns); +void nm_setting_ip6_config_remove_dns (NMSettingIP6Config *setting, guint32 i); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_ip6_config_remove_dns_by_value (NMSettingIP6Config *setting, const struct in6_addr *dns); +void nm_setting_ip6_config_clear_dns (NMSettingIP6Config *setting); + +guint32 nm_setting_ip6_config_get_num_dns_searches (NMSettingIP6Config *setting); +const char * nm_setting_ip6_config_get_dns_search (NMSettingIP6Config *setting, guint32 i); +gboolean nm_setting_ip6_config_add_dns_search (NMSettingIP6Config *setting, const char *dns_search); +void nm_setting_ip6_config_remove_dns_search (NMSettingIP6Config *setting, guint32 i); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_ip6_config_remove_dns_search_by_value (NMSettingIP6Config *setting, const char *dns_search); +void nm_setting_ip6_config_clear_dns_searches (NMSettingIP6Config *setting); + +guint32 nm_setting_ip6_config_get_num_addresses (NMSettingIP6Config *setting); +NMIP6Address * nm_setting_ip6_config_get_address (NMSettingIP6Config *setting, guint32 i); +gboolean nm_setting_ip6_config_add_address (NMSettingIP6Config *setting, NMIP6Address *address); +void nm_setting_ip6_config_remove_address (NMSettingIP6Config *setting, guint32 i); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_ip6_config_remove_address_by_value (NMSettingIP6Config *setting, NMIP6Address *address); +void nm_setting_ip6_config_clear_addresses (NMSettingIP6Config *setting); + +guint32 nm_setting_ip6_config_get_num_routes (NMSettingIP6Config *setting); +NMIP6Route * nm_setting_ip6_config_get_route (NMSettingIP6Config *setting, guint32 i); +gboolean nm_setting_ip6_config_add_route (NMSettingIP6Config *setting, NMIP6Route *route); +void nm_setting_ip6_config_remove_route (NMSettingIP6Config *setting, guint32 i); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_ip6_config_remove_route_by_value (NMSettingIP6Config *setting, NMIP6Route *route); +void nm_setting_ip6_config_clear_routes (NMSettingIP6Config *setting); +gboolean nm_setting_ip6_config_get_ignore_auto_routes (NMSettingIP6Config *setting); + +gboolean nm_setting_ip6_config_get_ignore_auto_dns (NMSettingIP6Config *setting); +const char * nm_setting_ip6_config_get_dhcp_hostname (NMSettingIP6Config *setting); +gboolean nm_setting_ip6_config_get_never_default (NMSettingIP6Config *setting); +gboolean nm_setting_ip6_config_get_may_fail (NMSettingIP6Config *setting); +NMSettingIP6ConfigPrivacy nm_setting_ip6_config_get_ip6_privacy (NMSettingIP6Config *setting); + +G_END_DECLS + +#endif /* NM_SETTING_IP6_CONFIG_H */ diff --git a/libnm-core/nm-setting-olpc-mesh.c b/libnm-core/nm-setting-olpc-mesh.c new file mode 100644 index 0000000000..408f6833f5 --- /dev/null +++ b/libnm-core/nm-setting-olpc-mesh.c @@ -0,0 +1,274 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2013 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2009 One Laptop per Child + */ + +#include <string.h> +#include <netinet/ether.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "NetworkManager.h" +#include "nm-setting-olpc-mesh.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-dbus-glib-types.h" +#include "nm-utils-private.h" +#include "nm-setting-private.h" + +GQuark +nm_setting_olpc_mesh_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-olpc-mesh-error-quark"); + return quark; +} + +static void nm_setting_olpc_mesh_init (NMSettingOlpcMesh *setting); + +G_DEFINE_TYPE_WITH_CODE (NMSettingOlpcMesh, nm_setting_olpc_mesh, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_OLPC_MESH_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_OLPC_MESH_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OLPC_MESH) + +#define NM_SETTING_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshPrivate)) + +typedef struct { + GByteArray *ssid; + guint32 channel; + GByteArray *dhcp_anycast_addr; +} NMSettingOlpcMeshPrivate; + +enum { + PROP_0, + PROP_SSID, + PROP_CHANNEL, + PROP_DHCP_ANYCAST_ADDRESS, + + LAST_PROP +}; + +/** + * nm_setting_olpc_mesh_new: + * + * Creates a new #NMSettingOlpcMesh object with default values. + * + * Returns: the new empty #NMSettingOlpcMesh object + **/ +NMSetting *nm_setting_olpc_mesh_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_OLPC_MESH, NULL); +} + +static void +nm_setting_olpc_mesh_init (NMSettingOlpcMesh *setting) +{ +} + +const GByteArray * +nm_setting_olpc_mesh_get_ssid (NMSettingOlpcMesh *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (setting), NULL); + + return NM_SETTING_OLPC_MESH_GET_PRIVATE (setting)->ssid; +} + +guint32 +nm_setting_olpc_mesh_get_channel (NMSettingOlpcMesh *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (setting), 0); + + return NM_SETTING_OLPC_MESH_GET_PRIVATE (setting)->channel; +} + +const GByteArray * +nm_setting_olpc_mesh_get_dhcp_anycast_address (NMSettingOlpcMesh *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (setting), NULL); + + return NM_SETTING_OLPC_MESH_GET_PRIVATE (setting)->dhcp_anycast_addr; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE (setting); + + if (!priv->ssid) { + g_set_error_literal (error, + NM_SETTING_OLPC_MESH_ERROR, + NM_SETTING_OLPC_MESH_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OLPC_MESH_SETTING_NAME, NM_SETTING_OLPC_MESH_SSID); + return FALSE; + } + + if (!priv->ssid->len || priv->ssid->len > 32) { + g_set_error_literal (error, + NM_SETTING_OLPC_MESH_ERROR, + NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY, + _("SSID length is out of range <1-32> bytes")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OLPC_MESH_SETTING_NAME, NM_SETTING_OLPC_MESH_SSID); + return FALSE; + } + + if (priv->channel == 0 || priv->channel > 13) { + g_set_error (error, + NM_SETTING_OLPC_MESH_ERROR, + NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY, + _("'%d' is not a valid channel"), + priv->channel); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OLPC_MESH_SETTING_NAME, NM_SETTING_OLPC_MESH_CHANNEL); + return FALSE; + } + + if (priv->dhcp_anycast_addr && priv->dhcp_anycast_addr->len != ETH_ALEN) { + g_set_error_literal (error, + NM_SETTING_OLPC_MESH_ERROR, + NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OLPC_MESH_SETTING_NAME, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS); + return FALSE; + } + + return TRUE; +} + +static void +finalize (GObject *object) +{ + NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE (object); + + if (priv->ssid) + g_byte_array_free (priv->ssid, TRUE); + if (priv->dhcp_anycast_addr) + g_byte_array_free (priv->dhcp_anycast_addr, TRUE); + + G_OBJECT_CLASS (nm_setting_olpc_mesh_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SSID: + if (priv->ssid) + g_byte_array_free (priv->ssid, TRUE); + priv->ssid = g_value_dup_boxed (value); + break; + case PROP_CHANNEL: + priv->channel = g_value_get_uint (value); + break; + case PROP_DHCP_ANYCAST_ADDRESS: + if (priv->dhcp_anycast_addr) + g_byte_array_free (priv->dhcp_anycast_addr, TRUE); + priv->dhcp_anycast_addr = g_value_dup_boxed (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) +{ + NMSettingOlpcMesh *setting = NM_SETTING_OLPC_MESH (object); + + switch (prop_id) { + case PROP_SSID: + g_value_set_boxed (value, nm_setting_olpc_mesh_get_ssid (setting)); + break; + case PROP_CHANNEL: + g_value_set_uint (value, nm_setting_olpc_mesh_get_channel (setting)); + break; + case PROP_DHCP_ANYCAST_ADDRESS: + g_value_set_boxed (value, nm_setting_olpc_mesh_get_dhcp_anycast_address (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_olpc_mesh_class_init (NMSettingOlpcMeshClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingOlpcMeshPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + + /* Properties */ + /** + * NMSettingOlpcMesh:ssid: + * + * SSID of the mesh network to join. + **/ + g_object_class_install_property + (object_class, PROP_SSID, + _nm_param_spec_specialized (NM_SETTING_OLPC_MESH_SSID, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingOlpcMesh:channel: + * + * Channel on which the mesh network to join is located. + **/ + g_object_class_install_property + (object_class, PROP_CHANNEL, + g_param_spec_uint (NM_SETTING_OLPC_MESH_CHANNEL, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingOlpcMesh:dhcp-anycast-address: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_DHCP_ANYCAST_ADDRESS, + _nm_param_spec_specialized (NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-olpc-mesh.h b/libnm-core/nm-setting-olpc-mesh.h new file mode 100644 index 0000000000..54b268e2c6 --- /dev/null +++ b/libnm-core/nm-setting-olpc-mesh.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2009 One Laptop per Child + */ + +#ifndef NM_SETTING_OLPC_MESH_H +#define NM_SETTING_OLPC_MESH_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_OLPC_MESH (nm_setting_olpc_mesh_get_type ()) +#define NM_SETTING_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMesh)) +#define NM_SETTING_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshClass)) +#define NM_IS_SETTING_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OLPC_MESH)) +#define NM_IS_SETTING_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OLPC_MESH)) +#define NM_SETTING_OLPC_MESH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshClass)) + +#define NM_SETTING_OLPC_MESH_SETTING_NAME "802-11-olpc-mesh" + +/** + * NMSettingOlpcMeshError: + * @NM_SETTING_OLPC_MESH_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_OLPC_MESH_ERROR_MISSING_PROPERTY: the property was missing and is + * required + */ +typedef enum { + NM_SETTING_OLPC_MESH_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_OLPC_MESH_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/ +} NMSettingOlpcMeshError; + +#define NM_SETTING_OLPC_MESH_ERROR nm_setting_olpc_mesh_error_quark () +GQuark nm_setting_olpc_mesh_error_quark (void); + +#define NM_SETTING_OLPC_MESH_SSID "ssid" +#define NM_SETTING_OLPC_MESH_CHANNEL "channel" +#define NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS "dhcp-anycast-address" + +typedef struct { + NMSetting parent; +} NMSettingOlpcMesh; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingOlpcMeshClass; + +GType nm_setting_olpc_mesh_get_type (void); + +NMSetting * nm_setting_olpc_mesh_new (void); +const GByteArray *nm_setting_olpc_mesh_get_ssid (NMSettingOlpcMesh *setting); +guint32 nm_setting_olpc_mesh_get_channel (NMSettingOlpcMesh *setting); +const GByteArray *nm_setting_olpc_mesh_get_dhcp_anycast_address (NMSettingOlpcMesh *setting); + +G_END_DECLS + +#endif /* NM_SETTING_OLPC_MESH_H */ diff --git a/libnm-core/nm-setting-ppp.c b/libnm-core/nm-setting-ppp.c new file mode 100644 index 0000000000..0e7c598b90 --- /dev/null +++ b/libnm-core/nm-setting-ppp.c @@ -0,0 +1,823 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2013 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include <glib/gi18n.h> + +#include "nm-setting-ppp.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-ppp + * @short_description: Describes connection properties for devices/networks + * that require PPP to deliver IP capability + * @include: nm-setting-ppp.h + * + * The #NMSettingPPP object is a #NMSetting subclass that describes properties + * necessary for connection to networks that require PPP transport, like PPPoE + * cable and DSL modems and some mobile broadband devices. + **/ + +/** + * nm_setting_ppp_error_quark: + * + * Registers an error quark for #NMSettingPPP if necessary. + * + * Returns: the error quark used for #NMSettingPPP errors. + **/ +GQuark +nm_setting_ppp_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-ppp-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingPPP, nm_setting_ppp, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_PPP_SETTING_NAME, + g_define_type_id, + 3, + NM_SETTING_PPP_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_PPP) + +#define NM_SETTING_PPP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PPP, NMSettingPPPPrivate)) + +typedef struct { + gboolean noauth; + gboolean refuse_eap; + gboolean refuse_pap; + gboolean refuse_chap; + gboolean refuse_mschap; + gboolean refuse_mschapv2; + gboolean nobsdcomp; + gboolean nodeflate; + gboolean no_vj_comp; + gboolean require_mppe; + gboolean require_mppe_128; + gboolean mppe_stateful; + gboolean crtscts; + guint32 baud; + guint32 mru; + guint32 mtu; + guint32 lcp_echo_failure; + guint32 lcp_echo_interval; +} NMSettingPPPPrivate; + +enum { + PROP_0, + PROP_NOAUTH, + PROP_REFUSE_EAP, + PROP_REFUSE_PAP, + PROP_REFUSE_CHAP, + PROP_REFUSE_MSCHAP, + PROP_REFUSE_MSCHAPV2, + PROP_NOBSDCOMP, + PROP_NODEFLATE, + PROP_NO_VJ_COMP, + PROP_REQUIRE_MPPE, + PROP_REQUIRE_MPPE_128, + PROP_MPPE_STATEFUL, + PROP_CRTSCTS, + PROP_BAUD, + PROP_MRU, + PROP_MTU, + PROP_LCP_ECHO_FAILURE, + PROP_LCP_ECHO_INTERVAL, + + LAST_PROP +}; + +/** + * nm_setting_ppp_new: + * + * Creates a new #NMSettingPPP object with default values. + * + * Returns: (transfer full): the new empty #NMSettingPPP object + **/ +NMSetting * +nm_setting_ppp_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_PPP, NULL); +} + +/** + * nm_setting_ppp_get_noauth: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:noauth property of the setting + **/ +gboolean +nm_setting_ppp_get_noauth (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->noauth; +} + +/** + * nm_setting_ppp_get_refuse_eap: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:refuse-eap property of the setting + **/ +gboolean +nm_setting_ppp_get_refuse_eap (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_eap; +} + +/** + * nm_setting_ppp_get_refuse_pap: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:refuse-pap property of the setting + **/ +gboolean +nm_setting_ppp_get_refuse_pap (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_pap; +} + +/** + * nm_setting_ppp_get_refuse_chap: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:refuse-chap property of the setting + **/ +gboolean +nm_setting_ppp_get_refuse_chap (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_chap; +} + +/** + * nm_setting_ppp_get_refuse_mschap: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:refuse-mschap property of the setting + **/ +gboolean +nm_setting_ppp_get_refuse_mschap (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_mschap; +} + +/** + * nm_setting_ppp_get_refuse_mschapv2: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:refuse-mschapv2 property of the setting + **/ +gboolean +nm_setting_ppp_get_refuse_mschapv2 (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_mschapv2; +} + +/** + * nm_setting_ppp_get_nobsdcomp: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:nobsdcomp property of the setting + **/ +gboolean +nm_setting_ppp_get_nobsdcomp (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->nobsdcomp; +} + +/** + * nm_setting_ppp_get_nodeflate: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:nodeflate property of the setting + **/ +gboolean +nm_setting_ppp_get_nodeflate (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->nodeflate; +} + +/** + * nm_setting_ppp_get_no_vj_comp: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:no-vj-comp property of the setting + **/ +gboolean +nm_setting_ppp_get_no_vj_comp (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->no_vj_comp; +} + +/** + * nm_setting_ppp_get_require_mppe: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:require-mppe property of the setting + **/ +gboolean +nm_setting_ppp_get_require_mppe (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->require_mppe; +} + +/** + * nm_setting_ppp_get_require_mppe_128: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:require-mppe-128 property of the setting + **/ +gboolean +nm_setting_ppp_get_require_mppe_128 (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->require_mppe_128; +} + +/** + * nm_setting_ppp_get_mppe_stateful: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:mppe-stateful property of the setting + **/ +gboolean +nm_setting_ppp_get_mppe_stateful (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->mppe_stateful; +} + +/** + * nm_setting_ppp_get_crtscts: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:crtscts property of the setting + **/ +gboolean +nm_setting_ppp_get_crtscts (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->crtscts; +} + +/** + * nm_setting_ppp_get_baud: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:baud property of the setting + **/ +guint32 +nm_setting_ppp_get_baud (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->baud; +} + +/** + * nm_setting_ppp_get_mru: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:mru property of the setting + **/ +guint32 +nm_setting_ppp_get_mru (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->mru; +} + +/** + * nm_setting_ppp_get_mtu: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:mtu property of the setting + **/ +guint32 +nm_setting_ppp_get_mtu (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->mtu; +} + +/** + * nm_setting_ppp_get_lcp_echo_failure: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:lcp-echo-failure property of the setting + **/ +guint32 +nm_setting_ppp_get_lcp_echo_failure (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->lcp_echo_failure; +} + +/** + * nm_setting_ppp_get_lcp_echo_interval: + * @setting: the #NMSettingPPP + * + * Returns: the #NMSettingPPP:lcp-echo-interval property of the setting + **/ +guint32 +nm_setting_ppp_get_lcp_echo_interval (NMSettingPPP *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0); + + return NM_SETTING_PPP_GET_PRIVATE (setting)->lcp_echo_interval; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingPPPPrivate *priv = NM_SETTING_PPP_GET_PRIVATE (setting); + + /* FIXME: Do we even want this or can we just let pppd evaluate the options? */ + if (priv->mru > 0) { + if (priv->mru < 128 || priv->mru > 16384) { + g_set_error (error, + NM_SETTING_PPP_ERROR, + NM_SETTING_PPP_ERROR_INVALID_PROPERTY, + _("'%d' is out of valid range <128-16384>"), + priv->mru); + g_prefix_error (error, "%s.%s: ", NM_SETTING_PPP_SETTING_NAME, NM_SETTING_PPP_MRU); + return FALSE; + } + } + + if (priv->lcp_echo_failure > 0) { + /* lcp_echo_interval must also be non-zero */ + if (priv->lcp_echo_interval == 0) { + g_set_error (error, + NM_SETTING_PPP_ERROR, + NM_SETTING_PPP_ERROR_INVALID_PROPERTY, + _("setting this property requires non-zero '%s' property"), + NM_SETTING_PPP_LCP_ECHO_INTERVAL); + g_prefix_error (error, "%s.%s: ", NM_SETTING_PPP_SETTING_NAME, NM_SETTING_PPP_LCP_ECHO_FAILURE); + return FALSE; + } + } + + return TRUE; +} + +static void +nm_setting_ppp_init (NMSettingPPP *setting) +{ +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingPPPPrivate *priv = NM_SETTING_PPP_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_NOAUTH: + priv->noauth = g_value_get_boolean (value); + break; + case PROP_REFUSE_EAP: + priv->refuse_eap = g_value_get_boolean (value); + break; + case PROP_REFUSE_PAP: + priv->refuse_pap = g_value_get_boolean (value); + break; + case PROP_REFUSE_CHAP: + priv->refuse_chap = g_value_get_boolean (value); + break; + case PROP_REFUSE_MSCHAP: + priv->refuse_mschap = g_value_get_boolean (value); + break; + case PROP_REFUSE_MSCHAPV2: + priv->refuse_mschapv2 = g_value_get_boolean (value); + break; + case PROP_NOBSDCOMP: + priv->nobsdcomp = g_value_get_boolean (value); + break; + case PROP_NODEFLATE: + priv->nodeflate = g_value_get_boolean (value); + break; + case PROP_NO_VJ_COMP: + priv->no_vj_comp = g_value_get_boolean (value); + break; + case PROP_REQUIRE_MPPE: + priv->require_mppe = g_value_get_boolean (value); + break; + case PROP_REQUIRE_MPPE_128: + priv->require_mppe_128 = g_value_get_boolean (value); + break; + case PROP_MPPE_STATEFUL: + priv->mppe_stateful = g_value_get_boolean (value); + break; + case PROP_CRTSCTS: + priv->crtscts = g_value_get_boolean (value); + break; + case PROP_BAUD: + priv->baud = g_value_get_uint (value); + break; + case PROP_MRU: + priv->mru = g_value_get_uint (value); + break; + case PROP_MTU: + priv->mtu = g_value_get_uint (value); + break; + case PROP_LCP_ECHO_FAILURE: + priv->lcp_echo_failure = g_value_get_uint (value); + break; + case PROP_LCP_ECHO_INTERVAL: + priv->lcp_echo_interval = g_value_get_uint (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) +{ + NMSettingPPP *setting = NM_SETTING_PPP (object); + + switch (prop_id) { + case PROP_NOAUTH: + g_value_set_boolean (value, nm_setting_ppp_get_noauth (setting)); + break; + case PROP_REFUSE_EAP: + g_value_set_boolean (value, nm_setting_ppp_get_refuse_eap (setting)); + break; + case PROP_REFUSE_PAP: + g_value_set_boolean (value, nm_setting_ppp_get_refuse_pap (setting)); + break; + case PROP_REFUSE_CHAP: + g_value_set_boolean (value, nm_setting_ppp_get_refuse_chap (setting)); + break; + case PROP_REFUSE_MSCHAP: + g_value_set_boolean (value, nm_setting_ppp_get_refuse_mschap (setting)); + break; + case PROP_REFUSE_MSCHAPV2: + g_value_set_boolean (value, nm_setting_ppp_get_refuse_mschapv2 (setting)); + break; + case PROP_NOBSDCOMP: + g_value_set_boolean (value, nm_setting_ppp_get_nobsdcomp (setting)); + break; + case PROP_NODEFLATE: + g_value_set_boolean (value, nm_setting_ppp_get_nodeflate (setting)); + break; + case PROP_NO_VJ_COMP: + g_value_set_boolean (value, nm_setting_ppp_get_no_vj_comp (setting)); + break; + case PROP_REQUIRE_MPPE: + g_value_set_boolean (value, nm_setting_ppp_get_require_mppe (setting)); + break; + case PROP_REQUIRE_MPPE_128: + g_value_set_boolean (value, nm_setting_ppp_get_require_mppe_128 (setting)); + break; + case PROP_MPPE_STATEFUL: + g_value_set_boolean (value, nm_setting_ppp_get_mppe_stateful (setting)); + break; + case PROP_CRTSCTS: + g_value_set_boolean (value, nm_setting_ppp_get_crtscts (setting)); + break; + case PROP_BAUD: + g_value_set_uint (value, nm_setting_ppp_get_baud (setting)); + break; + case PROP_MRU: + g_value_set_uint (value, nm_setting_ppp_get_mru (setting)); + break; + case PROP_MTU: + g_value_set_uint (value, nm_setting_ppp_get_mtu (setting)); + break; + case PROP_LCP_ECHO_FAILURE: + g_value_set_uint (value, nm_setting_ppp_get_lcp_echo_failure (setting)); + break; + case PROP_LCP_ECHO_INTERVAL: + g_value_set_uint (value, nm_setting_ppp_get_lcp_echo_interval (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_ppp_class_init (NMSettingPPPClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingPPPPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + parent_class->verify = verify; + + /* Properties */ + /** + * NMSettingPPP:noauth: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_NOAUTH, + g_param_spec_boolean (NM_SETTING_PPP_NOAUTH, "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:refuse-eap: + * + * If %TRUE, the EAP authentication method will not be used. + **/ + g_object_class_install_property + (object_class, PROP_REFUSE_EAP, + g_param_spec_boolean (NM_SETTING_PPP_REFUSE_EAP, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:refuse-pap: + * + * If %TRUE, the PAP authentication method will not be used. + **/ + g_object_class_install_property + (object_class, PROP_REFUSE_PAP, + g_param_spec_boolean (NM_SETTING_PPP_REFUSE_PAP, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:refuse-chap: + * + * If %TRUE, the CHAP authentication method will not be used. + **/ + g_object_class_install_property + (object_class, PROP_REFUSE_CHAP, + g_param_spec_boolean (NM_SETTING_PPP_REFUSE_CHAP, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:refuse-mschap: + * + * If %TRUE, the MSCHAP authentication method will not be used. + **/ + g_object_class_install_property + (object_class, PROP_REFUSE_MSCHAP, + g_param_spec_boolean (NM_SETTING_PPP_REFUSE_MSCHAP, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:refuse-mschapv2: + * + * If %TRUE, the MSCHAPv2 authentication method will not be used. + **/ + g_object_class_install_property + (object_class, PROP_REFUSE_MSCHAPV2, + g_param_spec_boolean (NM_SETTING_PPP_REFUSE_MSCHAPV2, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:nobsdcomp: + * + * If %TRUE, BSD compression will not be requested. + **/ + g_object_class_install_property + (object_class, PROP_NOBSDCOMP, + g_param_spec_boolean (NM_SETTING_PPP_NOBSDCOMP, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:nodeflate: + * + * If %TRUE, "deflate" compression will not be requested. + **/ + g_object_class_install_property + (object_class, PROP_NODEFLATE, + g_param_spec_boolean (NM_SETTING_PPP_NODEFLATE, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:no-vj-comp: + * + * If %TRUE, Van Jacobsen TCP header compression will not be requested. + **/ + g_object_class_install_property + (object_class, PROP_NO_VJ_COMP, + g_param_spec_boolean (NM_SETTING_PPP_NO_VJ_COMP, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:require-mppe: + * + * If %TRUE, MPPE (Microsoft Point-to-Point Encrpytion) 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. + **/ + g_object_class_install_property + (object_class, PROP_REQUIRE_MPPE, + g_param_spec_boolean (NM_SETTING_PPP_REQUIRE_MPPE, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:require-mppe-128: + * + * If %TRUE, 128-bit MPPE (Microsoft Point-to-Point Encrpytion) 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. + **/ + g_object_class_install_property + (object_class, PROP_REQUIRE_MPPE_128, + g_param_spec_boolean (NM_SETTING_PPP_REQUIRE_MPPE_128, "", "", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:mppe-stateful: + * + * If %TRUE, stateful MPPE is used. See pppd documentation for more + * information on stateful MPPE. + **/ + g_object_class_install_property + (object_class, PROP_MPPE_STATEFUL, + g_param_spec_boolean (NM_SETTING_PPP_MPPE_STATEFUL, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:crtscts: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_CRTSCTS, + g_param_spec_boolean (NM_SETTING_PPP_CRTSCTS, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:baud: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_BAUD, + g_param_spec_uint (NM_SETTING_PPP_BAUD, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:mru: + * + * 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. + */ + g_object_class_install_property + (object_class, PROP_MRU, + g_param_spec_uint (NM_SETTING_PPP_MRU, "", "", + 0, 16384, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:mtu: + * + * If non-zero, instruct pppd to send packets no larger than the specified + * size. + **/ + g_object_class_install_property + (object_class, PROP_MTU, + g_param_spec_uint (NM_SETTING_PPP_MTU, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:lcp-echo-failure: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_LCP_ECHO_FAILURE, + g_param_spec_uint (NM_SETTING_PPP_LCP_ECHO_FAILURE, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPP:lcp-echo-interval: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_LCP_ECHO_INTERVAL, + g_param_spec_uint (NM_SETTING_PPP_LCP_ECHO_INTERVAL, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-ppp.h b/libnm-core/nm-setting-ppp.h new file mode 100644 index 0000000000..5733902b83 --- /dev/null +++ b/libnm-core/nm-setting-ppp.h @@ -0,0 +1,115 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SETTING_PPP_H +#define NM_SETTING_PPP_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_PPP (nm_setting_ppp_get_type ()) +#define NM_SETTING_PPP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_PPP, NMSettingPPP)) +#define NM_SETTING_PPP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_PPP, NMSettingPPPClass)) +#define NM_IS_SETTING_PPP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_PPP)) +#define NM_IS_SETTING_PPP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_PPP)) +#define NM_SETTING_PPP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_PPP, NMSettingPPPClass)) + +#define NM_SETTING_PPP_SETTING_NAME "ppp" + +/** + * NMSettingPPPError: + * @NM_SETTING_PPP_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_PPP_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_PPP_ERROR_MISSING_PROPERTY: the property was missing and is + * required + * @NM_SETTING_PPP_ERROR_REQUIRE_MPPE_NOT_ALLOWED: requiring MPPE is not compatible + * with other setting configuration parameters + */ +typedef enum { + NM_SETTING_PPP_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_PPP_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_PPP_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ + NM_SETTING_PPP_ERROR_REQUIRE_MPPE_NOT_ALLOWED /*< nick=RequireMPPENotAllowed >*/ +} NMSettingPPPError; + +#define NM_SETTING_PPP_ERROR nm_setting_ppp_error_quark () +GQuark nm_setting_ppp_error_quark (void); + +#define NM_SETTING_PPP_NOAUTH "noauth" +#define NM_SETTING_PPP_REFUSE_EAP "refuse-eap" +#define NM_SETTING_PPP_REFUSE_PAP "refuse-pap" +#define NM_SETTING_PPP_REFUSE_CHAP "refuse-chap" +#define NM_SETTING_PPP_REFUSE_MSCHAP "refuse-mschap" +#define NM_SETTING_PPP_REFUSE_MSCHAPV2 "refuse-mschapv2" +#define NM_SETTING_PPP_NOBSDCOMP "nobsdcomp" +#define NM_SETTING_PPP_NODEFLATE "nodeflate" +#define NM_SETTING_PPP_NO_VJ_COMP "no-vj-comp" +#define NM_SETTING_PPP_REQUIRE_MPPE "require-mppe" +#define NM_SETTING_PPP_REQUIRE_MPPE_128 "require-mppe-128" +#define NM_SETTING_PPP_MPPE_STATEFUL "mppe-stateful" +#define NM_SETTING_PPP_CRTSCTS "crtscts" +#define NM_SETTING_PPP_BAUD "baud" +#define NM_SETTING_PPP_MRU "mru" +#define NM_SETTING_PPP_MTU "mtu" +#define NM_SETTING_PPP_LCP_ECHO_FAILURE "lcp-echo-failure" +#define NM_SETTING_PPP_LCP_ECHO_INTERVAL "lcp-echo-interval" + +typedef struct { + NMSetting parent; +} NMSettingPPP; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingPPPClass; + +GType nm_setting_ppp_get_type (void); + +NMSetting *nm_setting_ppp_new (void); +gboolean nm_setting_ppp_get_noauth (NMSettingPPP *setting); +gboolean nm_setting_ppp_get_refuse_eap (NMSettingPPP *setting); +gboolean nm_setting_ppp_get_refuse_pap (NMSettingPPP *setting); +gboolean nm_setting_ppp_get_refuse_chap (NMSettingPPP *setting); +gboolean nm_setting_ppp_get_refuse_mschap (NMSettingPPP *setting); +gboolean nm_setting_ppp_get_refuse_mschapv2 (NMSettingPPP *setting); +gboolean nm_setting_ppp_get_nobsdcomp (NMSettingPPP *setting); +gboolean nm_setting_ppp_get_nodeflate (NMSettingPPP *setting); +gboolean nm_setting_ppp_get_no_vj_comp (NMSettingPPP *setting); +gboolean nm_setting_ppp_get_require_mppe (NMSettingPPP *setting); +gboolean nm_setting_ppp_get_require_mppe_128 (NMSettingPPP *setting); +gboolean nm_setting_ppp_get_mppe_stateful (NMSettingPPP *setting); +gboolean nm_setting_ppp_get_crtscts (NMSettingPPP *setting); +guint32 nm_setting_ppp_get_baud (NMSettingPPP *setting); +guint32 nm_setting_ppp_get_mru (NMSettingPPP *setting); +guint32 nm_setting_ppp_get_mtu (NMSettingPPP *setting); +guint32 nm_setting_ppp_get_lcp_echo_failure (NMSettingPPP *setting); +guint32 nm_setting_ppp_get_lcp_echo_interval (NMSettingPPP *setting); + +G_END_DECLS + +#endif /* NM_SETTING_PPP_H */ diff --git a/libnm-core/nm-setting-pppoe.c b/libnm-core/nm-setting-pppoe.c new file mode 100644 index 0000000000..a3923336c1 --- /dev/null +++ b/libnm-core/nm-setting-pppoe.c @@ -0,0 +1,342 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2013 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include <string.h> +#include <glib/gi18n.h> + +#include "nm-setting-pppoe.h" +#include "nm-setting-ppp.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-pppoe + * @short_description: Describes PPPoE connection properties + * @include: nm-setting-pppoe.h + * + * The #NMSettingPPPOE object is a #NMSetting subclass that describes + * properties necessary for connection to networks that require PPPoE connections + * to provide IP transport, for example cable or DSL modems. + **/ + +/** + * nm_setting_pppoe_error_quark: + * + * Registers an error quark for #NMSettingPPPOE if necessary. + * + * Returns: the error quark used for #NMSettingPPPOE errors. + **/ +GQuark +nm_setting_pppoe_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-pppoe-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingPPPOE, nm_setting_pppoe, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_PPPOE_SETTING_NAME, + g_define_type_id, + 3, + NM_SETTING_PPPOE_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_PPPOE) + +#define NM_SETTING_PPPOE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PPPOE, NMSettingPPPOEPrivate)) + +typedef struct { + char *service; + char *username; + char *password; + NMSettingSecretFlags password_flags; +} NMSettingPPPOEPrivate; + +enum { + PROP_0, + PROP_SERVICE, + PROP_USERNAME, + PROP_PASSWORD, + PROP_PASSWORD_FLAGS, + + LAST_PROP +}; + +/** + * nm_setting_pppoe_new: + * + * Creates a new #NMSettingPPPOE object with default values. + * + * Returns: (transfer full): the new empty #NMSettingPPPOE object + **/ +NMSetting * +nm_setting_pppoe_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_PPPOE, NULL); +} + +/** + * nm_setting_pppoe_get_service: + * @setting: the #NMSettingPPPOE + * + * Returns: the #NMSettingPPPOE:service property of the setting + **/ +const char * +nm_setting_pppoe_get_service (NMSettingPPPOE *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPPOE (setting), NULL); + + return NM_SETTING_PPPOE_GET_PRIVATE (setting)->service; +} + +/** + * nm_setting_pppoe_get_username: + * @setting: the #NMSettingPPPOE + * + * Returns: the #NMSettingPPPOE:username property of the setting + **/ +const char * +nm_setting_pppoe_get_username (NMSettingPPPOE *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPPOE (setting), NULL); + + return NM_SETTING_PPPOE_GET_PRIVATE (setting)->username; +} + +/** + * nm_setting_pppoe_get_password: + * @setting: the #NMSettingPPPOE + * + * Returns: the #NMSettingPPPOE:password property of the setting + **/ +const char * +nm_setting_pppoe_get_password (NMSettingPPPOE *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPPOE (setting), NULL); + + return NM_SETTING_PPPOE_GET_PRIVATE (setting)->password; +} + +/** + * nm_setting_pppoe_get_password_flags: + * @setting: the #NMSettingPPPOE + * + * Returns: the #NMSettingSecretFlags pertaining to the #NMSettingPPPOE:password + **/ +NMSettingSecretFlags +nm_setting_pppoe_get_password_flags (NMSettingPPPOE *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_PPPOE (setting), NM_SETTING_SECRET_FLAG_NONE); + + return NM_SETTING_PPPOE_GET_PRIVATE (setting)->password_flags; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingPPPOEPrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (setting); + + if (!priv->username) { + g_set_error_literal (error, + NM_SETTING_PPPOE_ERROR, + NM_SETTING_PPPOE_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_PPPOE_SETTING_NAME, NM_SETTING_PPPOE_USERNAME); + return FALSE; + } else if (!strlen (priv->username)) { + g_set_error_literal (error, + NM_SETTING_PPPOE_ERROR, + NM_SETTING_PPPOE_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_PPPOE_SETTING_NAME, NM_SETTING_PPPOE_USERNAME); + return FALSE; + } + + if (priv->service && !strlen (priv->service)) { + g_set_error_literal (error, + NM_SETTING_PPPOE_ERROR, + NM_SETTING_PPPOE_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_PPPOE_SETTING_NAME, NM_SETTING_PPPOE_SERVICE); + return FALSE; + } + + return TRUE; +} + +static GPtrArray * +need_secrets (NMSetting *setting) +{ + NMSettingPPPOEPrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (setting); + GPtrArray *secrets = NULL; + + if (priv->password) + return NULL; + + if (!(priv->password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { + secrets = g_ptr_array_sized_new (1); + g_ptr_array_add (secrets, NM_SETTING_PPPOE_PASSWORD); + } + + return secrets; +} + +static void +nm_setting_pppoe_init (NMSettingPPPOE *setting) +{ +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingPPPOEPrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SERVICE: + g_free (priv->service); + priv->service = g_value_dup_string (value); + break; + case PROP_USERNAME: + g_free (priv->username); + priv->username = g_value_dup_string (value); + break; + case PROP_PASSWORD: + g_free (priv->password); + priv->password = g_value_dup_string (value); + break; + case PROP_PASSWORD_FLAGS: + priv->password_flags = g_value_get_uint (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) +{ + NMSettingPPPOE *setting = NM_SETTING_PPPOE (object); + + switch (prop_id) { + case PROP_SERVICE: + g_value_set_string (value, nm_setting_pppoe_get_service (setting)); + break; + case PROP_USERNAME: + g_value_set_string (value, nm_setting_pppoe_get_username (setting)); + break; + case PROP_PASSWORD: + g_value_set_string (value, nm_setting_pppoe_get_password (setting)); + break; + case PROP_PASSWORD_FLAGS: + g_value_set_uint (value, nm_setting_pppoe_get_password_flags (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject *object) +{ + NMSettingPPPOEPrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (object); + + g_free (priv->username); + g_free (priv->password); + g_free (priv->service); + + G_OBJECT_CLASS (nm_setting_pppoe_parent_class)->finalize (object); +} + +static void +nm_setting_pppoe_class_init (NMSettingPPPOEClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingPPPOEPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + parent_class->need_secrets = need_secrets; + + /* Properties */ + /** + * NMSettingPPPOE:service: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_SERVICE, + g_param_spec_string (NM_SETTING_PPPOE_SERVICE, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPPOE:username: + * + * Username used to authenticate with the PPPoE service. + **/ + g_object_class_install_property + (object_class, PROP_USERNAME, + g_param_spec_string (NM_SETTING_PPPOE_USERNAME, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPPOE:password: + * + * Password used to authenticate with the PPPoE service. + **/ + g_object_class_install_property + (object_class, PROP_PASSWORD, + g_param_spec_string (NM_SETTING_PPPOE_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingPPPOE:password-flags: + * + * Flags indicating how to handle the #NMSettingPPPOE:password property. + **/ + g_object_class_install_property + (object_class, PROP_PASSWORD_FLAGS, + g_param_spec_uint (NM_SETTING_PPPOE_PASSWORD_FLAGS, "", "", + NM_SETTING_SECRET_FLAG_NONE, + NM_SETTING_SECRET_FLAGS_ALL, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-pppoe.h b/libnm-core/nm-setting-pppoe.h new file mode 100644 index 0000000000..9d04af9e24 --- /dev/null +++ b/libnm-core/nm-setting-pppoe.h @@ -0,0 +1,87 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2011 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SETTING_PPPOE_H +#define NM_SETTING_PPPOE_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_PPPOE (nm_setting_pppoe_get_type ()) +#define NM_SETTING_PPPOE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_PPPOE, NMSettingPPPOE)) +#define NM_SETTING_PPPOE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_PPPOE, NMSettingPPPOEClass)) +#define NM_IS_SETTING_PPPOE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_PPPOE)) +#define NM_IS_SETTING_PPPOE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_PPPOE)) +#define NM_SETTING_PPPOE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_PPPOE, NMSettingPPPOEClass)) + +#define NM_SETTING_PPPOE_SETTING_NAME "pppoe" + +/** + * NMSettingPPPOEError: + * @NM_SETTING_PPPOE_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_PPPOE_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_PPPOE_ERROR_MISSING_PROPERTY: the property was missing and is + * required + * @NM_SETTING_PPPOE_ERROR_MISSING_PPP_SETTING: the connection + * did not contain a required PPP setting for PPP related options + */ +typedef enum { + NM_SETTING_PPPOE_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_PPPOE_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_PPPOE_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ + NM_SETTING_PPPOE_ERROR_MISSING_PPP_SETTING /*< nick=MissingPPPSetting >*/ +} NMSettingPPPOEError; + +#define NM_SETTING_PPPOE_ERROR nm_setting_pppoe_error_quark () +GQuark nm_setting_pppoe_error_quark (void); + +#define NM_SETTING_PPPOE_SERVICE "service" +#define NM_SETTING_PPPOE_USERNAME "username" +#define NM_SETTING_PPPOE_PASSWORD "password" +#define NM_SETTING_PPPOE_PASSWORD_FLAGS "password-flags" + +typedef struct { + NMSetting parent; +} NMSettingPPPOE; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingPPPOEClass; + +GType nm_setting_pppoe_get_type (void); + +NMSetting *nm_setting_pppoe_new (void); +const char *nm_setting_pppoe_get_service (NMSettingPPPOE *setting); +const char *nm_setting_pppoe_get_username (NMSettingPPPOE *setting); +const char *nm_setting_pppoe_get_password (NMSettingPPPOE *setting); +NMSettingSecretFlags nm_setting_pppoe_get_password_flags (NMSettingPPPOE *setting); + +G_END_DECLS + +#endif /* NM_SETTING_PPPOE_H */ diff --git a/libnm-core/nm-setting-private.h b/libnm-core/nm-setting-private.h new file mode 100644 index 0000000000..99590b9e14 --- /dev/null +++ b/libnm-core/nm-setting-private.h @@ -0,0 +1,125 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 Red Hat, Inc. + */ + +#ifndef NM_SETTING_PRIVATE_H +#define NM_SETTING_PRIVATE_H + +#include "nm-setting.h" +#include "nm-glib-compat.h" + +#define NM_SETTING_SECRET_FLAGS_ALL \ + (NM_SETTING_SECRET_FLAG_NONE | \ + NM_SETTING_SECRET_FLAG_AGENT_OWNED | \ + NM_SETTING_SECRET_FLAG_NOT_SAVED | \ + NM_SETTING_SECRET_FLAG_NOT_REQUIRED) + +/** + * NMSettingVerifyResult: + * @NM_SETTING_VERIFY_SUCCESS: the setting verifies successfully + * @NM_SETTING_VERIFY_ERROR: the setting has a serious misconfiguration + * @NM_SETTING_VERIFY_NORMALIZABLE: the setting is valid but has properties + * that should be normalized + * @NM_SETTING_VERIFY_NORMALIZABLE_ERROR: the setting is invalid but the + * errors can be fixed by nm_connection_normalize(). + */ +typedef enum { + NM_SETTING_VERIFY_SUCCESS = TRUE, + NM_SETTING_VERIFY_ERROR = FALSE, + NM_SETTING_VERIFY_NORMALIZABLE = 2, + NM_SETTING_VERIFY_NORMALIZABLE_ERROR = 3, +} NMSettingVerifyResult; + +void _nm_register_setting (const char *name, + const GType type, + const guint32 priority, + const GQuark error_quark); + +/* Ensure, that name is a compile time constant string. Put the function name in parenthesis to suppress expansion. */ +#define _nm_register_setting(name, type, priority, error_quark) _nm_register_setting ((name ""), type, priority, error_quark) + +gboolean _nm_setting_is_base_type (NMSetting *setting); +gboolean _nm_setting_type_is_base_type (GType type); +GType _nm_setting_lookup_setting_type (const char *name); +GType _nm_setting_lookup_setting_type_by_quark (GQuark error_quark); +gint _nm_setting_compare_priority (gconstpointer a, gconstpointer b); + +typedef enum NMSettingUpdateSecretResult { + NM_SETTING_UPDATE_SECRET_ERROR = FALSE, + NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED = TRUE, + NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED = 2, +} NMSettingUpdateSecretResult; + +NMSettingUpdateSecretResult _nm_setting_update_secrets (NMSetting *setting, + GHashTable *secrets, + GError **error); +gboolean _nm_setting_clear_secrets (NMSetting *setting); +gboolean _nm_setting_clear_secrets_with_flags (NMSetting *setting, + NMSettingClearSecretsWithFlagsFn func, + gpointer user_data); + + +/* NM_SETTING_COMPARE_FLAG_INFERRABLE: check whether a device-generated + * connection can be replaced by a already-defined connection. This flag only + * takes into account properties marked with the %NM_SETTING_PARAM_INFERRABLE + * flag. + */ +#define NM_SETTING_COMPARE_FLAG_INFERRABLE 0x80000000 + +/* The property of the #NMSetting should be considered during comparisons that + * use the %NM_SETTING_COMPARE_FLAG_INFERRABLE flag. Properties that don't have + * this flag, are ignored when doing an infrerrable comparison. This flag should + * be set on all properties that are read from the kernel or the system when a + * connection is generated. eg, IP addresses/routes can be read from the + * kernel, but the 'autoconnect' property cannot, so + * %NM_SETTING_IP4_CONFIG_ADDRESSES gets the INFERRABLE flag, but + * %NM_SETTING_CONNECTION_AUTOCONNECT would not. + * + * This flag should not be used with properties where the default cannot be + * read separately from the current value, like MTU or wired duplex mode. + */ +#define NM_SETTING_PARAM_INFERRABLE (1 << (4 + G_PARAM_USER_SHIFT)) + +/* Ensure the setting's GType is registered at library load time */ +#define NM_SETTING_REGISTER_TYPE(x) \ +static void __attribute__((constructor)) register_setting (void) \ +{ g_type_init (); g_type_ensure (x); } + +NMSetting *nm_setting_find_in_list (GSList *settings_list, const char *setting_name); + +/* Private NMSettingIP4Config methods */ +#include "nm-setting-ip4-config.h" +const char *nm_setting_ip4_config_get_address_label (NMSettingIP4Config *setting, guint32 i); +gboolean nm_setting_ip4_config_add_address_with_label (NMSettingIP4Config *setting, NMIP4Address *address, const char *label); + +NMSettingVerifyResult _nm_setting_verify_deprecated_virtual_iface_name (const char *interface_name, + gboolean allow_missing, + const char *setting_name, + const char *setting_property, + GQuark error_quark, + gint e_invalid_property, + gint e_missing_property, + GSList *all_settings, + GError **error); + +NMSettingVerifyResult _nm_setting_verify (NMSetting *setting, + GSList *all_settings, + GError **error); + +#endif /* NM_SETTING_PRIVATE_H */ diff --git a/libnm-core/nm-setting-serial.c b/libnm-core/nm-setting-serial.c new file mode 100644 index 0000000000..4a7ee89e65 --- /dev/null +++ b/libnm-core/nm-setting-serial.c @@ -0,0 +1,320 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2011 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include <string.h> + +#include "nm-setting-serial.h" +#include "nm-glib-compat.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-serial + * @short_description: Describes connection properties for devices that use + * serial communications + * @include: nm-setting-serial.h + * + * The #NMSettingSerial object is a #NMSetting subclass that describes + * properties necessary for connections that may use serial communications, + * such as mobile broadband or analog telephone connections. + **/ + +/** + * nm_setting_serial_error_quark: + * + * Registers an error quark for #NMSettingSerial if necessary. + * + * Returns: the error quark used for #NMSettingSerial errors. + **/ +GQuark +nm_setting_serial_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-serial-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingSerial, nm_setting_serial, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_SERIAL_SETTING_NAME, + g_define_type_id, + 2, + NM_SETTING_SERIAL_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_SERIAL) + +#define NM_SETTING_SERIAL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_SERIAL, NMSettingSerialPrivate)) + +typedef struct { + guint baud; + guint bits; + char parity; + guint stopbits; + guint64 send_delay; +} NMSettingSerialPrivate; + + +enum { + PROP_0, + PROP_BAUD, + PROP_BITS, + PROP_PARITY, + PROP_STOPBITS, + PROP_SEND_DELAY, + + LAST_PROP +}; + +/** + * nm_setting_serial_new: + * + * Creates a new #NMSettingSerial object with default values. + * + * Returns: (transfer full): the new empty #NMSettingSerial object + **/ +NMSetting * +nm_setting_serial_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_SERIAL, NULL); +} + +/** + * nm_setting_serial_get_baud: + * @setting: the #NMSettingSerial + * + * Returns: the #NMSettingSerial:baud property of the setting + **/ +guint +nm_setting_serial_get_baud (NMSettingSerial *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0); + + return NM_SETTING_SERIAL_GET_PRIVATE (setting)->baud; +} + +/** + * nm_setting_serial_get_bits: + * @setting: the #NMSettingSerial + * + * Returns: the #NMSettingSerial:bits property of the setting + **/ +guint +nm_setting_serial_get_bits (NMSettingSerial *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0); + + return NM_SETTING_SERIAL_GET_PRIVATE (setting)->bits; +} + +/** + * nm_setting_serial_get_parity: + * @setting: the #NMSettingSerial + * + * Returns: the #NMSettingSerial:parity property of the setting + **/ +char +nm_setting_serial_get_parity (NMSettingSerial *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0); + + return NM_SETTING_SERIAL_GET_PRIVATE (setting)->parity; +} + +/** + * nm_setting_serial_get_stopbits: + * @setting: the #NMSettingSerial + * + * Returns: the #NMSettingSerial:stopbits property of the setting + **/ +guint +nm_setting_serial_get_stopbits (NMSettingSerial *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0); + + return NM_SETTING_SERIAL_GET_PRIVATE (setting)->stopbits; +} + +/** + * nm_setting_serial_get_send_delay: + * @setting: the #NMSettingSerial + * + * Returns: the #NMSettingSerial:send-delay property of the setting + **/ +guint64 +nm_setting_serial_get_send_delay (NMSettingSerial *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0); + + return NM_SETTING_SERIAL_GET_PRIVATE (setting)->send_delay; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + return TRUE; +} + +static void +nm_setting_serial_init (NMSettingSerial *setting) +{ +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingSerialPrivate *priv = NM_SETTING_SERIAL_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_BAUD: + priv->baud = g_value_get_uint (value); + break; + case PROP_BITS: + priv->bits = g_value_get_uint (value); + break; + case PROP_PARITY: + priv->parity = g_value_get_schar (value); + break; + case PROP_STOPBITS: + priv->stopbits = g_value_get_uint (value); + break; + case PROP_SEND_DELAY: + priv->send_delay = g_value_get_uint64 (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) +{ + NMSettingSerial *setting = NM_SETTING_SERIAL (object); + + switch (prop_id) { + case PROP_BAUD: + g_value_set_uint (value, nm_setting_serial_get_baud (setting)); + break; + case PROP_BITS: + g_value_set_uint (value, nm_setting_serial_get_bits (setting)); + break; + case PROP_PARITY: + g_value_set_schar (value, nm_setting_serial_get_parity (setting)); + break; + case PROP_STOPBITS: + g_value_set_uint (value, nm_setting_serial_get_stopbits (setting)); + break; + case PROP_SEND_DELAY: + g_value_set_uint64 (value, nm_setting_serial_get_send_delay (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_serial_class_init (NMSettingSerialClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingSerialPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + parent_class->verify = verify; + + /* Properties */ + + /** + * NMSettingSerial:baud: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_BAUD, + g_param_spec_uint (NM_SETTING_SERIAL_BAUD, "", "", + 0, G_MAXUINT, 57600, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingSerial:bits: + * + * Byte-width of the serial communication. The 8 in "8n1" for example. + **/ + g_object_class_install_property + (object_class, PROP_BITS, + g_param_spec_uint (NM_SETTING_SERIAL_BITS, "", "", + 5, 8, 8, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingSerial:parity: + * + * Parity setting of the serial port. Either 'E' for even parity, 'o' for + * odd parity, or 'n' for no parity. + **/ + g_object_class_install_property + (object_class, PROP_PARITY, + g_param_spec_char (NM_SETTING_SERIAL_PARITY, "", "", + 'E', 'o', 'n', + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingSerial:stopbits: + * + * Number of stop bits for communication on the serial port. Either 1 or 2. + * The 1 in "8n1" for example. + **/ + g_object_class_install_property + (object_class, PROP_STOPBITS, + g_param_spec_uint (NM_SETTING_SERIAL_STOPBITS, "", "", + 1, 2, 1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingSerial:send-delay: + * + * Time to delay between each byte sent to the modem, in microseconds. + **/ + g_object_class_install_property + (object_class, PROP_SEND_DELAY, + g_param_spec_uint64 (NM_SETTING_SERIAL_SEND_DELAY, "", "", + 0, G_MAXUINT64, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-serial.h b/libnm-core/nm-setting-serial.h new file mode 100644 index 0000000000..44d46cf37a --- /dev/null +++ b/libnm-core/nm-setting-serial.h @@ -0,0 +1,89 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SETTING_SERIAL_H +#define NM_SETTING_SERIAL_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_SERIAL (nm_setting_serial_get_type ()) +#define NM_SETTING_SERIAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_SERIAL, NMSettingSerial)) +#define NM_SETTING_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_SERIAL, NMSettingSerialClass)) +#define NM_IS_SETTING_SERIAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_SERIAL)) +#define NM_IS_SETTING_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_SERIAL)) +#define NM_SETTING_SERIAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_SERIAL, NMSettingSerialClass)) + +#define NM_SETTING_SERIAL_SETTING_NAME "serial" + +/** + * NMSettingSerialError: + * @NM_SETTING_SERIAL_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_SERIAL_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_SERIAL_ERROR_MISSING_PROPERTY: the property was missing and is + * required + * @NM_SETTING_SERIAL_ERROR_MISSING_PPP_SETTING: one of the properties of the + * setting requires the connection to contain an #NMSettingPPP setting + */ +typedef enum { + NM_SETTING_SERIAL_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_SERIAL_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_SERIAL_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ + NM_SETTING_SERIAL_ERROR_MISSING_PPP_SETTING /*< nick=MissingPPPSetting >*/ +} NMSettingSerialError; + +#define NM_SETTING_SERIAL_ERROR nm_setting_serial_error_quark () +GQuark nm_setting_serial_error_quark (void); + +#define NM_SETTING_SERIAL_BAUD "baud" +#define NM_SETTING_SERIAL_BITS "bits" +#define NM_SETTING_SERIAL_PARITY "parity" +#define NM_SETTING_SERIAL_STOPBITS "stopbits" +#define NM_SETTING_SERIAL_SEND_DELAY "send-delay" + +typedef struct { + NMSetting parent; +} NMSettingSerial; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingSerialClass; + +GType nm_setting_serial_get_type (void); + +NMSetting *nm_setting_serial_new (void); +guint nm_setting_serial_get_baud (NMSettingSerial *setting); +guint nm_setting_serial_get_bits (NMSettingSerial *setting); +char nm_setting_serial_get_parity (NMSettingSerial *setting); +guint nm_setting_serial_get_stopbits (NMSettingSerial *setting); +guint64 nm_setting_serial_get_send_delay (NMSettingSerial *setting); + +G_END_DECLS + +#endif /* NM_SETTING_SERIAL_H */ diff --git a/libnm-core/nm-setting-team-port.c b/libnm-core/nm-setting-team-port.c new file mode 100644 index 0000000000..ad9d8327ed --- /dev/null +++ b/libnm-core/nm-setting-team-port.c @@ -0,0 +1,184 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2013 Jiri Pirko <jiri@resnulli.us> + */ + +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "nm-setting-team-port.h" +#include "nm-utils.h" +#include "nm-utils-private.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-team-port + * @short_description: Describes connection properties for team ports + * @include: nm-setting-team-port.h + * + * The #NMSettingTeamPort object is a #NMSetting subclass that describes + * optional properties that apply to team ports. + * + * Since: 0.9.10 + **/ + +/** + * nm_setting_team_port_error_quark: + * + * Registers an error quark for #NMSettingTeamPort if necessary. + * + * Returns: the error quark used for #NMSettingTeamPort errors. + * + * Since: 0.9.10 + **/ +GQuark +nm_setting_team_port_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-team-port-error-quark"); + return quark; +} + +G_DEFINE_TYPE_WITH_CODE (NMSettingTeamPort, nm_setting_team_port, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_TEAM_PORT_SETTING_NAME, + g_define_type_id, + 3, + NM_SETTING_TEAM_PORT_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TEAM_PORT) + +#define NM_SETTING_TEAM_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TEAM_PORT, NMSettingTeamPortPrivate)) + +typedef struct { + char *config; +} NMSettingTeamPortPrivate; + +enum { + PROP_0, + PROP_CONFIG, + LAST_PROP +}; + +/** + * nm_setting_team_port_new: + * + * Creates a new #NMSettingTeamPort object with default values. + * + * Returns: (transfer full): the new empty #NMSettingTeamPort object + * + * Since: 0.9.10 + **/ +NMSetting * +nm_setting_team_port_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_TEAM_PORT, NULL); +} + +/** + * nm_setting_team_port_get_config: + * @setting: the #NMSettingTeamPort + * + * Returns: the #NMSettingTeamPort:config property of the setting + * + * Since: 0.9.10 + **/ +const char * +nm_setting_team_port_get_config (NMSettingTeamPort *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), NULL); + + return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->config; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + return TRUE; +} + +static void +nm_setting_team_port_init (NMSettingTeamPort *setting) +{ +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_CONFIG: + priv->config = g_value_dup_string (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) +{ + NMSettingTeamPort *setting = NM_SETTING_TEAM_PORT (object); + + switch (prop_id) { + case PROP_CONFIG: + g_value_set_string (value, nm_setting_team_port_get_config (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingTeamPortPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + parent_class->verify = verify; + + /* Properties */ + /** + * NMSettingTeamPort:config: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_CONFIG, + g_param_spec_string (NM_SETTING_TEAM_PORT_CONFIG, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-team-port.h b/libnm-core/nm-setting-team-port.h new file mode 100644 index 0000000000..4e8022202a --- /dev/null +++ b/libnm-core/nm-setting-team-port.h @@ -0,0 +1,79 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2013 Jiri Pirko <jiri@resnulli.us> + */ + +#ifndef NM_SETTING_TEAM_PORT_H +#define NM_SETTING_TEAM_PORT_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_TEAM_PORT (nm_setting_team_port_get_type ()) +#define NM_SETTING_TEAM_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_TEAM_PORT, NMSettingTeamPort)) +#define NM_SETTING_TEAM_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_TEAM_PORT, NMSettingTeamPortClass)) +#define NM_IS_SETTING_TEAM_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_TEAM_PORT)) +#define NM_IS_SETTING_TEAM_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_TEAM_PORT)) +#define NM_SETTING_TEAM_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_TEAM_PORT, NMSettingTeamPortClass)) + +#define NM_SETTING_TEAM_PORT_SETTING_NAME "team-port" + +/** + * NMSettingTeamPortError: + * @NM_SETTING_TEAM_PORT_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_TEAM_PORT_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_TEAM_PORT_ERROR_MISSING_PROPERTY: the property was missing and + * is required + */ +typedef enum { + NM_SETTING_TEAM_PORT_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_TEAM_PORT_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_TEAM_PORT_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ +} NMSettingTeamPortError; + +#define NM_SETTING_TEAM_PORT_ERROR nm_setting_team_port_error_quark () +GQuark nm_setting_team_port_error_quark (void); + +#define NM_SETTING_TEAM_PORT_CONFIG "config" + +typedef struct { + NMSetting parent; +} NMSettingTeamPort; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingTeamPortClass; + +NM_AVAILABLE_IN_0_9_10 +GType nm_setting_team_port_get_type (void); + +NM_AVAILABLE_IN_0_9_10 +NMSetting * nm_setting_team_port_new (void); + +const char * nm_setting_team_port_get_config (NMSettingTeamPort *setting); + +G_END_DECLS + +#endif /* NM_SETTING_TEAM_PORT_H */ diff --git a/libnm-core/nm-setting-team.c b/libnm-core/nm-setting-team.c new file mode 100644 index 0000000000..046ce51129 --- /dev/null +++ b/libnm-core/nm-setting-team.c @@ -0,0 +1,254 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2013 Jiri Pirko <jiri@resnulli.us> + */ + +#include <string.h> +#include <stdlib.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "nm-setting-team.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-utils-private.h" +#include "nm-dbus-glib-types.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-team + * @short_description: Describes connection properties for teams + * @include: nm-setting-team.h + * + * The #NMSettingTeam object is a #NMSetting subclass that describes properties + * necessary for team connections. + * + * Since: 0.9.10 + **/ + +/** + * nm_setting_team_error_quark: + * + * Registers an error quark for #NMSettingTeam if necessary. + * + * Returns: the error quark used for #NMSettingTeam errors. + * + * Since: 0.9.10 + **/ +GQuark +nm_setting_team_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-team-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingTeam, nm_setting_team, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_TEAM_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_TEAM_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TEAM) + +#define NM_SETTING_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TEAM, NMSettingTeamPrivate)) + +typedef struct { + char *interface_name; + char *config; +} NMSettingTeamPrivate; + +enum { + PROP_0, + PROP_INTERFACE_NAME, + PROP_CONFIG, + LAST_PROP +}; + +/** + * nm_setting_team_new: + * + * Creates a new #NMSettingTeam object with default values. + * + * Returns: (transfer full): the new empty #NMSettingTeam object + * + * Since: 0.9.10 + **/ +NMSetting * +nm_setting_team_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_TEAM, NULL); +} + +/** + * nm_setting_team_get_interface_name: + * @setting: the #NMSettingTeam + * + * Returns: the #NMSettingTeam:interface-name property of the setting + * + * Since: 0.9.10 + **/ +const char * +nm_setting_team_get_interface_name (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->interface_name; +} + +/** + * nm_setting_team_get_config: + * @setting: the #NMSettingTeam + * + * Returns: the #NMSettingTeam:config property of the setting + * + * Since: 0.9.10 + **/ +const char * +nm_setting_team_get_config (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->config; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + + return _nm_setting_verify_deprecated_virtual_iface_name ( + priv->interface_name, FALSE, + NM_SETTING_TEAM_SETTING_NAME, NM_SETTING_TEAM_INTERFACE_NAME, + NM_SETTING_TEAM_ERROR, + NM_SETTING_TEAM_ERROR_INVALID_PROPERTY, + NM_SETTING_TEAM_ERROR_MISSING_PROPERTY, + all_settings, error); +} + +static const char * +get_virtual_iface_name (NMSetting *setting) +{ + NMSettingTeam *self = NM_SETTING_TEAM (setting); + + return nm_setting_team_get_interface_name (self); +} + +static void +nm_setting_team_init (NMSettingTeam *setting) +{ +} + +static void +finalize (GObject *object) +{ + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (object); + + g_free (priv->interface_name); + g_free (priv->config); + + G_OBJECT_CLASS (nm_setting_team_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_INTERFACE_NAME: + g_free (priv->interface_name); + priv->interface_name = g_value_dup_string (value); + break; + case PROP_CONFIG: + g_free (priv->config); + priv->config = g_value_dup_string (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) +{ + NMSettingTeam *setting = NM_SETTING_TEAM (object); + + switch (prop_id) { + case PROP_INTERFACE_NAME: + g_value_set_string (value, nm_setting_team_get_interface_name (setting)); + break; + case PROP_CONFIG: + g_value_set_string (value, nm_setting_team_get_config (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_team_class_init (NMSettingTeamClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingTeamPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + parent_class->get_virtual_iface_name = get_virtual_iface_name; + + /* Properties */ + /** + * NMSettingTeam:interface-name: + * + * The name of the virtual in-kernel team network interface + **/ + g_object_class_install_property + (object_class, PROP_INTERFACE_NAME, + g_param_spec_string (NM_SETTING_TEAM_INTERFACE_NAME, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:config: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_CONFIG, + g_param_spec_string (NM_SETTING_TEAM_CONFIG, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-team.h b/libnm-core/nm-setting-team.h new file mode 100644 index 0000000000..d67c8c71b6 --- /dev/null +++ b/libnm-core/nm-setting-team.h @@ -0,0 +1,81 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2013 Jiri Pirko <jiri@resnulli.us> + */ + +#ifndef NM_SETTING_TEAM_H +#define NM_SETTING_TEAM_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_TEAM (nm_setting_team_get_type ()) +#define NM_SETTING_TEAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_TEAM, NMSettingTeam)) +#define NM_SETTING_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_TEAM, NMSettingTeamClass)) +#define NM_IS_SETTING_TEAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_TEAM)) +#define NM_IS_SETTING_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_TEAM)) +#define NM_SETTING_TEAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_TEAM, NMSettingTeamClass)) + +#define NM_SETTING_TEAM_SETTING_NAME "team" + +/** + * NMSettingTeamError: + * @NM_SETTING_TEAM_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_TEAM_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_TEAM_ERROR_MISSING_PROPERTY: the property was missing and is + * required + */ +typedef enum { + NM_SETTING_TEAM_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_TEAM_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_TEAM_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ +} NMSettingTeamError; + +#define NM_SETTING_TEAM_ERROR nm_setting_team_error_quark () +GQuark nm_setting_team_error_quark (void); + +#define NM_SETTING_TEAM_INTERFACE_NAME "interface-name" +#define NM_SETTING_TEAM_CONFIG "config" + +typedef struct { + NMSetting parent; +} NMSettingTeam; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingTeamClass; + +NM_AVAILABLE_IN_0_9_10 +GType nm_setting_team_get_type (void); + +NM_AVAILABLE_IN_0_9_10 +NMSetting * nm_setting_team_new (void); + +const char * nm_setting_team_get_interface_name (NMSettingTeam *setting); +const char * nm_setting_team_get_config (NMSettingTeam *setting); + +G_END_DECLS + +#endif /* NM_SETTING_TEAM_H */ diff --git a/libnm-core/nm-setting-vlan.c b/libnm-core/nm-setting-vlan.c new file mode 100644 index 0000000000..21adfdb25d --- /dev/null +++ b/libnm-core/nm-setting-vlan.c @@ -0,0 +1,841 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2014 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <string.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "nm-setting-vlan.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-dbus-glib-types.h" +#include "nm-setting-connection.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-vlan + * @short_description: Describes connection properties for VLAN interfaces + * @include: nm-setting-vlan.h + * + * The #NMSettingVlan object is a #NMSetting subclass that describes properties + * necessary for connection to VLAN interfaces. + **/ + +/** + * nm_setting_vlan_error_quark: + * + * Registers an error quark for #NMSettingVlan if necessary. + * + * Returns: the error quark used for #NMSettingVlan errors. + **/ +GQuark +nm_setting_vlan_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-vlan-error-quark"); + return quark; +} + +G_DEFINE_TYPE_WITH_CODE (NMSettingVlan, nm_setting_vlan, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_VLAN_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_VLAN_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_VLAN) + +#define NM_SETTING_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VLAN, NMSettingVlanPrivate)) + +typedef struct { + char *interface_name; + char *parent; + guint32 id; + guint32 flags; + GSList *ingress_priority_map; + GSList *egress_priority_map; +} NMSettingVlanPrivate; + +enum { + PROP_0, + PROP_INTERFACE_NAME, + PROP_PARENT, + PROP_ID, + PROP_FLAGS, + PROP_INGRESS_PRIORITY_MAP, + PROP_EGRESS_PRIORITY_MAP, + LAST_PROP +}; + +#define MAX_SKB_PRIO G_MAXUINT32 +#define MAX_8021P_PRIO 7 /* Max 802.1p priority */ + +typedef struct { + guint32 from; + guint32 to; +} PriorityMap; + +/** + * nm_setting_vlan_new: + * + * Creates a new #NMSettingVlan object with default values. + * + * Returns: (transfer full): the new empty #NMSettingVlan object + **/ +NMSetting * +nm_setting_vlan_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_VLAN, NULL); +} + +/** + * nm_setting_vlan_get_interface_name: + * @setting: the #NMSettingVlan + * + * Returns: the #NMSettingVlan:interface_name property of the setting + **/ +const char * +nm_setting_vlan_get_interface_name (NMSettingVlan *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), NULL); + return NM_SETTING_VLAN_GET_PRIVATE (setting)->interface_name; +} + +/** + * nm_setting_vlan_get_parent: + * @setting: the #NMSettingVlan + * + * Returns: the #NMSettingVlan:parent property of the setting + **/ +const char * +nm_setting_vlan_get_parent (NMSettingVlan *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), NULL); + return NM_SETTING_VLAN_GET_PRIVATE (setting)->parent; +} + +/** + * nm_setting_vlan_get_id: + * @setting: the #NMSettingVlan + * + * Returns: the #NMSettingVlan:id property of the setting + **/ +guint32 +nm_setting_vlan_get_id (NMSettingVlan *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), 0); + return NM_SETTING_VLAN_GET_PRIVATE (setting)->id; +} + +/** + * nm_setting_vlan_get_flags: + * @setting: the #NMSettingVlan + * + * Returns: the #NMSettingVlan:flags property of the setting + **/ +guint32 +nm_setting_vlan_get_flags (NMSettingVlan *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), 0); + return NM_SETTING_VLAN_GET_PRIVATE (setting)->flags; +} + +static guint32 +get_max_prio (NMVlanPriorityMap map, gboolean from) +{ + if (map == NM_VLAN_INGRESS_MAP) + return from ? MAX_8021P_PRIO : MAX_SKB_PRIO; + else if (map == NM_VLAN_EGRESS_MAP) + return from ? MAX_SKB_PRIO : MAX_8021P_PRIO; + g_assert_not_reached (); +} + +static PriorityMap * +priority_map_new_from_str (NMVlanPriorityMap map, const char *str) +{ + PriorityMap *p = NULL; + gchar **t = NULL; + guint32 len; + guint64 from, to; + + g_return_val_if_fail (str && str[0], NULL); + + t = g_strsplit (str, ":", 0); + len = g_strv_length (t); + if (len == 2) { + from = g_ascii_strtoull (t[0], NULL, 10); + to = g_ascii_strtoull (t[1], NULL, 10); + + if ((from <= get_max_prio (map, TRUE)) && (to <= get_max_prio (map, FALSE))) { + p = g_malloc0 (sizeof (PriorityMap)); + p->from = from; + p->to = to; + } + } else { + /* Warn */ + g_warn_if_fail (len == 2); + } + + g_strfreev (t); + return p; +} + +static void +priority_map_free (PriorityMap *map) +{ + g_return_if_fail (map != NULL); + g_free (map); +} + +static GSList * +get_map (NMSettingVlan *self, NMVlanPriorityMap map) +{ + if (map == NM_VLAN_INGRESS_MAP) + return NM_SETTING_VLAN_GET_PRIVATE (self)->ingress_priority_map; + else if (map == NM_VLAN_EGRESS_MAP) + return NM_SETTING_VLAN_GET_PRIVATE (self)->egress_priority_map; + g_assert_not_reached (); + return NULL; +} + +static void +set_map (NMSettingVlan *self, NMVlanPriorityMap map, GSList *list) +{ + if (map == NM_VLAN_INGRESS_MAP) { + NM_SETTING_VLAN_GET_PRIVATE (self)->ingress_priority_map = list; + g_object_notify (G_OBJECT (self), NM_SETTING_VLAN_INGRESS_PRIORITY_MAP); + } else if (map == NM_VLAN_EGRESS_MAP) { + NM_SETTING_VLAN_GET_PRIVATE (self)->egress_priority_map = list; + g_object_notify (G_OBJECT (self), NM_SETTING_VLAN_EGRESS_PRIORITY_MAP); + } else + g_assert_not_reached (); +} + +/** + * nm_setting_vlan_add_priority_str: + * @setting: the #NMSettingVlan + * @map: the type of priority map + * @str: the string which contains a priority map, like "3:7" + * + * Adds a priority map entry into either the #NMSettingVlan:ingress_priority_map + * or the #NMSettingVlan:egress_priority_map properties. The priority map maps + * the Linux SKB priorities to 802.1p priorities. + * + * Returns: %TRUE if the entry was successfully added to the list, or it + * overwrote the old value, %FALSE if error + */ +gboolean +nm_setting_vlan_add_priority_str (NMSettingVlan *setting, + NMVlanPriorityMap map, + const char *str) +{ + GSList *list = NULL, *iter = NULL; + PriorityMap *item = NULL; + + g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE); + g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE); + g_return_val_if_fail (str && str[0], FALSE); + + list = get_map (setting, map); + + item = priority_map_new_from_str (map, str); + g_return_val_if_fail (item != NULL, FALSE); + + /* Duplicates get replaced */ + for (iter = list; iter; iter = g_slist_next (iter)) { + PriorityMap *p = iter->data; + + if (p->from == item->from) { + p->to = item->to; + g_free (item); + if (map == NM_VLAN_INGRESS_MAP) + g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_INGRESS_PRIORITY_MAP); + else + g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_EGRESS_PRIORITY_MAP); + return TRUE; + } + } + + set_map (setting, map, g_slist_append (list, item)); + return TRUE; +} + +/** + * nm_setting_vlan_get_num_priorities: + * @setting: the #NMSettingVlan + * @map: the type of priority map + * + * Returns the number of entires in the + * #NMSettingVlan:ingress_priority_map or #NMSettingVlan:egress_priority_map + * properties of this setting. + * + * Returns: return the number of ingress/egress priority entries, -1 if error + **/ +gint32 +nm_setting_vlan_get_num_priorities (NMSettingVlan *setting, NMVlanPriorityMap map) +{ + g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), -1); + g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, -1); + + return g_slist_length (get_map (setting, map)); +} + +/** + * nm_setting_vlan_get_priority: + * @setting: the #NMSettingVlan + * @map: the type of priority map + * @idx: the zero-based index of the ingress/egress priority map entry + * @out_from: (out): on return the value of the priority map's 'from' item + * @out_to: (out): on return the value of priority map's 'to' item + * + * Retrieve one of the entries of the #NMSettingVlan:ingress_priority_map + * or #NMSettingVlan:egress_priority_map properties of this setting. + * + * Returns: %TRUE if a priority map was returned, %FALSE if error + **/ +gboolean +nm_setting_vlan_get_priority (NMSettingVlan *setting, + NMVlanPriorityMap map, + guint32 idx, + guint32 *out_from, + guint32 *out_to) +{ + GSList *list = NULL; + PriorityMap *item = NULL; + + g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE); + g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE); + g_return_val_if_fail (out_from != NULL, FALSE); + g_return_val_if_fail (out_to != NULL, FALSE); + + list = get_map (setting, map); + g_return_val_if_fail (idx < g_slist_length (list), FALSE); + + item = g_slist_nth_data (list, idx); + g_assert (item); + *out_from = item->from; + *out_to = item->to; + return TRUE; +} + +/** + * nm_setting_vlan_add_priority: + * @setting: the #NMSettingVlan + * @map: the type of priority map + * @from: the priority to map to @to + * @to: the priority to map @from to + * + * Adds a priority mapping to the #NMSettingVlan:ingress_priority_map or + * #NMSettingVlan:egress_priority_map properties of the setting. If @from is + * already in the given priority map, this function will overwrite the + * existing entry with the new @to. + * + * If @map is #NM_VLAN_INGRESS_MAP then @from is the incoming 802.1q VLAN + * Priority Code Point (PCP) value, and @to is the Linux SKB priority value. + * + * If @map is #NM_VLAN_EGRESS_MAP then @from is the Linux SKB priority value and + * @to is the outgoing 802.1q VLAN Priority Code Point (PCP) value. + * + * Returns: %TRUE if the new priority mapping was successfully added to the + * list, %FALSE if error + */ +gboolean +nm_setting_vlan_add_priority (NMSettingVlan *setting, + NMVlanPriorityMap map, + guint32 from, + guint32 to) +{ + GSList *list = NULL, *iter = NULL; + PriorityMap *item; + + g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE); + g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE); + + list = get_map (setting, map); + for (iter = list; iter; iter = g_slist_next (iter)) { + item = iter->data; + if (item->from == from) { + item->to = to; + if (map == NM_VLAN_INGRESS_MAP) + g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_INGRESS_PRIORITY_MAP); + else + g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_EGRESS_PRIORITY_MAP); + return TRUE; + } + } + + item = g_malloc0 (sizeof (PriorityMap)); + item->from = from; + item->to = to; + set_map (setting, map, g_slist_append (list, item)); + + return TRUE; +} + +/** + * nm_setting_vlan_remove_priority: + * @setting: the #NMSettingVlan + * @map: the type of priority map + * @idx: the zero-based index of the priority map to remove + * + * Removes the priority map at index @idx from the + * #NMSettingVlan:ingress_priority_map or #NMSettingVlan:egress_priority_map + * properties. + */ +void +nm_setting_vlan_remove_priority (NMSettingVlan *setting, + NMVlanPriorityMap map, + guint32 idx) +{ + GSList *list = NULL, *item = NULL; + + g_return_if_fail (NM_IS_SETTING_VLAN (setting)); + g_return_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP); + + list = get_map (setting, map); + g_return_if_fail (idx < g_slist_length (list)); + + item = g_slist_nth (list, idx); + priority_map_free ((PriorityMap *) (item->data)); + set_map (setting, map, g_slist_delete_link (list, item)); +} + +/** + * nm_setting_vlan_remove_priority_by_value: + * @setting: the #NMSettingVlan + * @map: the type of priority map + * @from: the priority to map to @to + * @to: the priority to map @from to + * + * Removes the priority map @form:@to from the #NMSettingVlan:ingress_priority_map + * or #NMSettingVlan:egress_priority_map (according to @map argument) + * properties. + * + * Returns: %TRUE if the priority mapping was found and removed; %FALSE if it was not. + * + * Since: 0.9.10 + */ +gboolean +nm_setting_vlan_remove_priority_by_value (NMSettingVlan *setting, + NMVlanPriorityMap map, + guint32 from, + guint32 to) +{ + GSList *list = NULL, *iter = NULL; + PriorityMap *item; + + g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE); + g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE); + + list = get_map (setting, map); + for (iter = list; iter; iter = g_slist_next (iter)) { + item = iter->data; + if (item->from == from && item->to == to) { + priority_map_free ((PriorityMap *) (iter->data)); + set_map (setting, map, g_slist_delete_link (list, iter)); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_vlan_remove_priority_str_by_value: + * @setting: the #NMSettingVlan + * @map: the type of priority map + * @str: the string which contains a priority map, like "3:7" + * + * Removes the priority map @str from the #NMSettingVlan:ingress_priority_map + * or #NMSettingVlan:egress_priority_map (according to @map argument) + * properties. + * + * Returns: %TRUE if the priority mapping was found and removed; %FALSE if it was not. + * + * Since: 0.9.10 + */ +gboolean +nm_setting_vlan_remove_priority_str_by_value (NMSettingVlan *setting, + NMVlanPriorityMap map, + const char *str) +{ + PriorityMap *item; + gboolean found; + + g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE); + g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE); + + item = priority_map_new_from_str (map, str); + if (!item) + return FALSE; + + found = nm_setting_vlan_remove_priority_by_value (setting, map, item->from, item->to); + g_free (item); + return found; +} + +/** + * nm_setting_vlan_clear_priorities: + * @setting: the #NMSettingVlan + * @map: the type of priority map + * + * Clear all the entires from #NMSettingVlan:ingress_priority_map or + * #NMSettingVlan:egress_priority_map properties. + */ +void +nm_setting_vlan_clear_priorities (NMSettingVlan *setting, NMVlanPriorityMap map) +{ + GSList *list = NULL; + + g_return_if_fail (NM_IS_SETTING_VLAN (setting)); + g_return_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP); + + list = get_map (setting, map); + g_slist_free_full (list, g_free); + set_map (setting, map, NULL); +} + +/*********************************************************************/ + +static void +nm_setting_vlan_init (NMSettingVlan *setting) +{ +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingVlanPrivate *priv = NM_SETTING_VLAN_GET_PRIVATE (setting); + NMSettingConnection *s_con = NULL; + NMSettingWired *s_wired = NULL; + GSList *iter; + + for (iter = all_settings; iter; iter = iter->next) { + if (NM_IS_SETTING_CONNECTION (iter->data)) + s_con = iter->data; + else if (NM_IS_SETTING_WIRED (iter->data)) + s_wired = iter->data; + } + + if (priv->parent) { + if (nm_utils_is_uuid (priv->parent)) { + /* If we have an NMSettingConnection:master with slave-type="vlan", + * then it must be the same UUID. + */ + if (s_con) { + const char *master = NULL, *slave_type = NULL; + + slave_type = nm_setting_connection_get_slave_type (s_con); + if (!g_strcmp0 (slave_type, NM_SETTING_VLAN_SETTING_NAME)) + master = nm_setting_connection_get_master (s_con); + + if (master && g_strcmp0 (priv->parent, master) != 0) { + g_set_error (error, + NM_SETTING_VLAN_ERROR, + NM_SETTING_VLAN_ERROR_INVALID_PARENT, + _("'%s' value doesn't match '%s=%s'"), + priv->parent, NM_SETTING_CONNECTION_MASTER, master); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_PARENT); + return FALSE; + } + } + } else if (!nm_utils_iface_valid_name (priv->parent)) { + /* parent must be either a UUID or an interface name */ + g_set_error (error, + NM_SETTING_VLAN_ERROR, + NM_SETTING_VLAN_ERROR_INVALID_PROPERTY, + _("'%s' is neither an UUID nor an interface name"), + priv->parent); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_PARENT); + return FALSE; + } + } else { + /* If parent is NULL, the parent must be specified via + * NMSettingWired:mac-address. + */ + if (!s_wired || !nm_setting_wired_get_mac_address (s_wired)) { + g_set_error (error, + NM_SETTING_VLAN_ERROR, + NM_SETTING_VLAN_ERROR_MISSING_PROPERTY, + _("property is not specified and neither is '%s:%s'"), + NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_PARENT); + return FALSE; + } + } + + if (priv->flags & ~(NM_VLAN_FLAG_REORDER_HEADERS | + NM_VLAN_FLAG_GVRP | + NM_VLAN_FLAG_LOOSE_BINDING)) { + g_set_error_literal (error, + NM_SETTING_VLAN_ERROR, + NM_SETTING_VLAN_ERROR_INVALID_PROPERTY, + _("flags are invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_FLAGS); + return FALSE; + } + + /* If interface_name is specified, it must be a valid interface name. We + * don't check that it matches parent and/or id, because we allow + * renaming vlans to arbitrary names. + */ + return _nm_setting_verify_deprecated_virtual_iface_name ( + priv->interface_name, TRUE, + NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_INTERFACE_NAME, + NM_SETTING_VLAN_ERROR, + NM_SETTING_VLAN_ERROR_INVALID_PROPERTY, + NM_SETTING_VLAN_ERROR_MISSING_PROPERTY, + all_settings, error); +} + +static const char * +get_virtual_iface_name (NMSetting *setting) +{ + return nm_setting_vlan_get_interface_name (NM_SETTING_VLAN (setting)); +} + +static GSList * +priority_stringlist_to_maplist (NMVlanPriorityMap map, GSList *strlist) +{ + GSList *list = NULL, *iter; + + for (iter = strlist; iter; iter = g_slist_next (iter)) { + PriorityMap *item; + + item = priority_map_new_from_str (map, (const char *) iter->data); + if (item) + list = g_slist_prepend (list, item); + } + return g_slist_reverse (list); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingVlan *setting = NM_SETTING_VLAN (object); + NMSettingVlanPrivate *priv = NM_SETTING_VLAN_GET_PRIVATE (setting); + + switch (prop_id) { + case PROP_INTERFACE_NAME: + g_free (priv->interface_name); + priv->interface_name = g_value_dup_string (value); + break; + case PROP_PARENT: + g_free (priv->parent); + priv->parent = g_value_dup_string (value); + break; + case PROP_ID: + priv->id = g_value_get_uint (value); + break; + case PROP_FLAGS: + priv->flags = g_value_get_uint (value); + break; + case PROP_INGRESS_PRIORITY_MAP: + g_slist_free_full (priv->ingress_priority_map, g_free); + priv->ingress_priority_map = + priority_stringlist_to_maplist (NM_VLAN_INGRESS_MAP, g_value_get_boxed (value)); + break; + case PROP_EGRESS_PRIORITY_MAP: + g_slist_free_full (priv->egress_priority_map, g_free); + priv->egress_priority_map = + priority_stringlist_to_maplist (NM_VLAN_EGRESS_MAP, g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GSList * +priority_maplist_to_stringlist (GSList *list) +{ + GSList *strlist = NULL, *iter; + + for (iter = list; iter; iter = g_slist_next (iter)) { + PriorityMap *item = iter->data; + + strlist = g_slist_prepend (strlist, g_strdup_printf ("%d:%d", item->from, item->to)); + } + return g_slist_reverse (strlist); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSettingVlan *setting = NM_SETTING_VLAN (object); + NMSettingVlanPrivate *priv = NM_SETTING_VLAN_GET_PRIVATE (setting); + + switch (prop_id) { + case PROP_INTERFACE_NAME: + g_value_set_string (value, priv->interface_name); + break; + case PROP_PARENT: + g_value_set_string (value, priv->parent); + break; + case PROP_ID: + g_value_set_uint (value, priv->id); + break; + case PROP_FLAGS: + g_value_set_uint (value, priv->flags); + break; + case PROP_INGRESS_PRIORITY_MAP: + g_value_take_boxed (value, priority_maplist_to_stringlist (priv->ingress_priority_map)); + break; + case PROP_EGRESS_PRIORITY_MAP: + g_value_take_boxed (value, priority_maplist_to_stringlist (priv->egress_priority_map)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject *object) +{ + NMSettingVlan *setting = NM_SETTING_VLAN (object); + NMSettingVlanPrivate *priv = NM_SETTING_VLAN_GET_PRIVATE (setting); + + g_free (priv->interface_name); + g_free (priv->parent); + g_slist_free_full (priv->ingress_priority_map, g_free); + g_slist_free_full (priv->egress_priority_map, g_free); + + G_OBJECT_CLASS (nm_setting_vlan_parent_class)->finalize (object); +} + +static void +nm_setting_vlan_class_init (NMSettingVlanClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingVlanPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + parent_class->get_virtual_iface_name = get_virtual_iface_name; + + /* Properties */ + + /** + * NMSettingVlan:interface-name: + * + * If given, specifies the kernel name of the VLAN interface. If not given, + * a default name will be constructed from the interface described by the + * parent interface and the #NMSettingVlan:id property, eg "eth2.1". The + * parent interface may be given by the #NMSettingVlan:parent property or by + * the #NMSettingWired:mac-address property of an #NMSettingWired setting. + **/ + g_object_class_install_property + (object_class, PROP_INTERFACE_NAME, + g_param_spec_string (NM_SETTING_VLAN_INTERFACE_NAME, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingVlan:parent: + * + * 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 #NMSettingWired setting + * with a #NMSettingWired:mac-address property. + **/ + g_object_class_install_property + (object_class, PROP_PARENT, + g_param_spec_string (NM_SETTING_VLAN_PARENT, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingVlan:id: + * + * The VLAN identifier that the interface created by this connection should + * be assigned. + **/ + g_object_class_install_property + (object_class, PROP_ID, + g_param_spec_uint (NM_SETTING_VLAN_ID, "", "", + 0, 4095, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingVlan:flags: + * + * One or more flags which control the behavior and features of the VLAN + * interface. Flags include %NM_VLAN_FLAG_REORDER_HEADERS (reordering of + * output packet headers), %NM_VLAN_FLAG_GVRP (use of the GVRP protocol), + * and %NM_VLAN_FLAG_LOOSE_BINDING (loose binding of the interface to its + * master device's operating state). + **/ + g_object_class_install_property + (object_class, PROP_FLAGS, + g_param_spec_uint (NM_SETTING_VLAN_FLAGS, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingVlan:ingress-priority-map: + * + * 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". + **/ + g_object_class_install_property + (object_class, PROP_INGRESS_PRIORITY_MAP, + _nm_param_spec_specialized (NM_SETTING_VLAN_INGRESS_PRIORITY_MAP, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingVlan:egress-priority-map: + * + * 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". + **/ + g_object_class_install_property + (object_class, PROP_EGRESS_PRIORITY_MAP, + _nm_param_spec_specialized (NM_SETTING_VLAN_EGRESS_PRIORITY_MAP, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-vlan.h b/libnm-core/nm-setting-vlan.h new file mode 100644 index 0000000000..3e209480c8 --- /dev/null +++ b/libnm-core/nm-setting-vlan.h @@ -0,0 +1,156 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2014 Red Hat, Inc. + */ + +#ifndef NM_SETTING_VLAN_H +#define NM_SETTING_VLAN_H + +#include "nm-setting.h" +#include <linux/if_vlan.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_VLAN (nm_setting_vlan_get_type ()) +#define NM_SETTING_VLAN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_VLAN, NMSettingVlan)) +#define NM_SETTING_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_VLANCONFIG, NMSettingVlanClass)) +#define NM_IS_SETTING_VLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_VLAN)) +#define NM_IS_SETTING_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_VLAN)) +#define NM_SETTING_VLAN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_VLAN, NMSettingVlanClass)) + +#define NM_SETTING_VLAN_SETTING_NAME "vlan" + +/** + * NMSettingVlanError: + * @NM_SETTING_VLAN_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_VLAN_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_VLAN_ERROR_MISSING_PROPERTY: the property was missing and is + * required + * @NM_SETTING_VLAN_ERROR_INVALID_PARENT: the VLAN parent was specified + * inconsistently + */ +typedef enum { + NM_SETTING_VLAN_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/ + NM_SETTING_VLAN_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_VLAN_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ + NM_SETTING_VLAN_ERROR_INVALID_PARENT /*< nick=InvalidParent >*/ +} NMSettingVlanError; + +#define NM_SETTING_VLAN_ERROR nm_setting_vlan_error_quark () +GQuark nm_setting_vlan_error_quark (void); + +#define NM_SETTING_VLAN_INTERFACE_NAME "interface-name" +#define NM_SETTING_VLAN_PARENT "parent" +#define NM_SETTING_VLAN_ID "id" +#define NM_SETTING_VLAN_FLAGS "flags" +#define NM_SETTING_VLAN_INGRESS_PRIORITY_MAP "ingress-priority-map" +#define NM_SETTING_VLAN_EGRESS_PRIORITY_MAP "egress-priority-map" + +typedef struct { + NMSetting parent; +} NMSettingVlan; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingVlanClass; + +/** + * NMVlanPriorityMap: + * @NM_VLAN_INGRESS_MAP: map for incoming data + * @NM_VLAN_EGRESS_MAP: map for outgoing data + * + * A selector for traffic priority maps; these map Linux SKB priorities + * to 802.1p priorities used in VLANs. + **/ +typedef enum { + NM_VLAN_INGRESS_MAP, + NM_VLAN_EGRESS_MAP +} NMVlanPriorityMap; + +/** + * NMVlanFlags: + * @NM_VLAN_FLAG_REORDER_HEADERS: indicates that this interface should reorder + * outgoing packet headers to look more like a non-VLAN Ethernet interface + * @NM_VLAN_FLAG_GVRP: indicates that this interface should use GVRP to register + * itself with it's switch + * @NM_VLAN_FLAG_LOOSE_BINDING: indicates that this interface's operating + * state is tied to the underlying network interface but other details + * (like routing) are not. + * + * #NMVlanFlags values control the behavior of the VLAN interface. + **/ +typedef enum { + NM_VLAN_FLAG_REORDER_HEADERS = 0x1, + NM_VLAN_FLAG_GVRP = 0x2, + NM_VLAN_FLAG_LOOSE_BINDING = 0x4, + + /* NOTE: if adding flags update nm-setting-vlan.c::verify() */ +} NMVlanFlags; + +GType nm_setting_vlan_get_type (void); +NMSetting *nm_setting_vlan_new (void); + +const char *nm_setting_vlan_get_interface_name (NMSettingVlan *setting); +const char *nm_setting_vlan_get_parent (NMSettingVlan *setting); +guint32 nm_setting_vlan_get_id (NMSettingVlan *setting); +guint32 nm_setting_vlan_get_flags (NMSettingVlan *setting); + +gint32 nm_setting_vlan_get_num_priorities (NMSettingVlan *setting, NMVlanPriorityMap map); + +gboolean nm_setting_vlan_get_priority (NMSettingVlan *setting, + NMVlanPriorityMap map, + guint32 idx, + guint32 *out_from, + guint32 *out_to); + +gboolean nm_setting_vlan_add_priority (NMSettingVlan *setting, + NMVlanPriorityMap map, + guint32 from, + guint32 to); + +void nm_setting_vlan_remove_priority (NMSettingVlan *setting, + NMVlanPriorityMap map, + guint32 idx); + +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_vlan_remove_priority_by_value (NMSettingVlan *setting, + NMVlanPriorityMap map, + guint32 from, + guint32 to); + +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_vlan_remove_priority_str_by_value (NMSettingVlan *setting, + NMVlanPriorityMap map, + const char *str); + +void nm_setting_vlan_clear_priorities (NMSettingVlan *setting, NMVlanPriorityMap map); + +gboolean nm_setting_vlan_add_priority_str (NMSettingVlan *setting, + NMVlanPriorityMap map, + const char *str); + +G_END_DECLS + +#endif /* NM_SETTING_VLAN_H */ diff --git a/libnm-core/nm-setting-vpn.c b/libnm-core/nm-setting-vpn.c new file mode 100644 index 0000000000..3afc4dd93c --- /dev/null +++ b/libnm-core/nm-setting-vpn.c @@ -0,0 +1,870 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2013 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "nm-setting-vpn.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-dbus-glib-types.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-vpn + * @short_description: Describes connection properties for Virtual Private Networks + * @include: nm-setting-vpn.h + * + * The #NMSettingVPN object is a #NMSetting subclass that describes properties + * necessary for connection to Virtual Private Networks. NetworkManager uses + * a plugin architecture to allow easier use of new VPN types, and this + * setting abstracts the configuration for those plugins. Since the configuration + * options are only known to the VPN plugins themselves, the VPN configuration + * options are stored as key/value pairs of strings rather than GObject + * properties. + **/ + +/** + * nm_setting_vpn_error_quark: + * + * Registers an error quark for #NMSettingVPN if necessary. + * + * Returns: the error quark used for #NMSettingVPN errors. + **/ +GQuark +nm_setting_vpn_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-vpn-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingVPN, nm_setting_vpn, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_VPN_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_VPN_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_VPN) + +#define NM_SETTING_VPN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VPN, NMSettingVPNPrivate)) + +typedef struct { + char *service_type; + + /* username of the user requesting this connection, thus + * it's really only valid for user connections, and it also + * should never be saved out to persistent config. + */ + char *user_name; + + /* The hash table is created at setting object + * init time and should not be replaced. It is + * a char * -> char * mapping, and both the key + * and value are owned by the hash table, and should + * be allocated with functions whose value can be + * freed with g_free(). Should not contain secrets. + */ + GHashTable *data; + + /* The hash table is created at setting object + * init time and should not be replaced. It is + * a char * -> char * mapping, and both the key + * and value are owned by the hash table, and should + * be allocated with functions whose value can be + * freed with g_free(). Should contain secrets only. + */ + GHashTable *secrets; +} NMSettingVPNPrivate; + +enum { + PROP_0, + PROP_SERVICE_TYPE, + PROP_USER_NAME, + PROP_DATA, + PROP_SECRETS, + + LAST_PROP +}; + +/** + * nm_setting_vpn_new: + * + * Creates a new #NMSettingVPN object with default values. + * + * Returns: (transfer full): the new empty #NMSettingVPN object + **/ +NMSetting * +nm_setting_vpn_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_VPN, NULL); +} + +/** + * nm_setting_vpn_get_service_type: + * @setting: the #NMSettingVPN + * + * Returns the service name of the VPN, which identifies the specific VPN + * plugin that should be used to connect to this VPN. + * + * Returns: the VPN plugin's service name + **/ +const char * +nm_setting_vpn_get_service_type (NMSettingVPN *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL); + + return NM_SETTING_VPN_GET_PRIVATE (setting)->service_type; +} + +/** + * nm_setting_vpn_get_user_name: + * @setting: the #NMSettingVPN + * + * Returns: the #NMSettingVPN:user-name property of the setting + **/ +const char * +nm_setting_vpn_get_user_name (NMSettingVPN *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL); + + return NM_SETTING_VPN_GET_PRIVATE (setting)->user_name; +} + +/** + * nm_setting_vpn_get_num_data_items: + * @setting: the #NMSettingVPN + * + * Gets number of key/value pairs of VPN configuration data. + * + * Returns: the number of VPN plugin specific configuration data items + **/ +guint32 +nm_setting_vpn_get_num_data_items (NMSettingVPN *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_VPN (setting), 0); + + return g_hash_table_size (NM_SETTING_VPN_GET_PRIVATE (setting)->data); +} + +/** + * nm_setting_vpn_add_data_item: + * @setting: the #NMSettingVPN + * @key: a name that uniquely identifies the given value @item + * @item: the value to be referenced by @key + * + * Establishes a relationship between @key and @item internally in the + * setting which may be retrieved later. Should not be used to store passwords + * or other secrets, which is what nm_setting_vpn_add_secret() is for. + **/ +void +nm_setting_vpn_add_data_item (NMSettingVPN *setting, + const char *key, + const char *item) +{ + g_return_if_fail (NM_IS_SETTING_VPN (setting)); + g_return_if_fail (key != NULL); + g_return_if_fail (strlen (key) > 0); + g_return_if_fail (item != NULL); + g_return_if_fail (strlen (item) > 0); + + g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->data, + g_strdup (key), g_strdup (item)); + g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_DATA); +} + +/** + * nm_setting_vpn_get_data_item: + * @setting: the #NMSettingVPN + * @key: the name of the data item to retrieve + * + * Retrieves the data item of a key/value relationship previously established + * by nm_setting_vpn_add_data_item(). + * + * Returns: the data item, if any + **/ +const char * +nm_setting_vpn_get_data_item (NMSettingVPN *setting, const char *key) +{ + g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL); + + return (const char *) g_hash_table_lookup (NM_SETTING_VPN_GET_PRIVATE (setting)->data, key); +} + +/** + * nm_setting_vpn_remove_data_item: + * @setting: the #NMSettingVPN + * @key: the name of the data item to remove + * + * Deletes a key/value relationship previously established by + * nm_setting_vpn_add_data_item(). + * + * Returns: %TRUE if the data item was found and removed from the internal list, + * %FALSE if it was not. + **/ +gboolean +nm_setting_vpn_remove_data_item (NMSettingVPN *setting, const char *key) +{ + gboolean found; + + g_return_val_if_fail (NM_IS_SETTING_VPN (setting), FALSE); + + found = g_hash_table_remove (NM_SETTING_VPN_GET_PRIVATE (setting)->data, key); + if (found) + g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_DATA); + return found; +} + +static void +foreach_item_helper (GHashTable *hash, + NMVPNIterFunc func, + gpointer user_data) +{ + GList *keys, *liter; + GSList *copied = NULL, *siter; + + g_return_if_fail (hash != NULL); + + /* Grab keys and copy them so that the callback func can modify + * the hash table items if it wants to. + */ + keys = g_hash_table_get_keys (hash); + for (liter = keys; liter; liter = g_list_next (liter)) + copied = g_slist_prepend (copied, g_strdup (liter->data)); + copied = g_slist_reverse (copied); + g_list_free (keys); + + for (siter = copied; siter; siter = g_slist_next (siter)) { + gpointer value; + + value = g_hash_table_lookup (hash, siter->data); + func (siter->data, value, user_data); + } + + g_slist_free_full (copied, g_free); +} + +/** + * nm_setting_vpn_foreach_data_item: + * @setting: a #NMSettingVPN + * @func: (scope call): an user provided function + * @user_data: data to be passed to @func + * + * Iterates all data items stored in this setting. It is safe to add, remove, + * and modify data items inside @func, though any additions or removals made + * during iteration will not be part of the iteration. + */ +void +nm_setting_vpn_foreach_data_item (NMSettingVPN *setting, + NMVPNIterFunc func, + gpointer user_data) +{ + g_return_if_fail (NM_IS_SETTING_VPN (setting)); + + foreach_item_helper (NM_SETTING_VPN_GET_PRIVATE (setting)->data, func, user_data); +} + +/** + * nm_setting_vpn_get_num_secrets: + * @setting: the #NMSettingVPN + * + * Gets number of VPN plugin specific secrets in the setting. + * + * Returns: the number of VPN plugin specific secrets + **/ +guint32 +nm_setting_vpn_get_num_secrets (NMSettingVPN *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_VPN (setting), 0); + + return g_hash_table_size (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets); +} + +/** + * nm_setting_vpn_add_secret: + * @setting: the #NMSettingVPN + * @key: a name that uniquely identifies the given secret @secret + * @secret: the secret to be referenced by @key + * + * Establishes a relationship between @key and @secret internally in the + * setting which may be retrieved later. + **/ +void +nm_setting_vpn_add_secret (NMSettingVPN *setting, + const char *key, + const char *secret) +{ + g_return_if_fail (NM_IS_SETTING_VPN (setting)); + g_return_if_fail (key != NULL); + g_return_if_fail (strlen (key) > 0); + g_return_if_fail (secret != NULL); + g_return_if_fail (strlen (secret) > 0); + + g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, + g_strdup (key), g_strdup (secret)); + g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS); +} + +/** + * nm_setting_vpn_get_secret: + * @setting: the #NMSettingVPN + * @key: the name of the secret to retrieve + * + * Retrieves the secret of a key/value relationship previously established + * by nm_setting_vpn_add_secret(). + * + * Returns: the secret, if any + **/ +const char * +nm_setting_vpn_get_secret (NMSettingVPN *setting, const char *key) +{ + g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL); + + return (const char *) g_hash_table_lookup (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, key); +} + +/** + * nm_setting_vpn_remove_secret: + * @setting: the #NMSettingVPN + * @key: the name of the secret to remove + * + * Deletes a key/value relationship previously established by + * nm_setting_vpn_add_secret(). + * + * Returns: %TRUE if the secret was found and removed from the internal list, + * %FALSE if it was not. + **/ +gboolean +nm_setting_vpn_remove_secret (NMSettingVPN *setting, const char *key) +{ + gboolean found; + + g_return_val_if_fail (NM_IS_SETTING_VPN (setting), FALSE); + + found = g_hash_table_remove (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, key); + if (found) + g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS); + return found; +} + +/** + * nm_setting_vpn_foreach_secret: + * @setting: a #NMSettingVPN + * @func: (scope call): an user provided function + * @user_data: data to be passed to @func + * + * Iterates all secrets stored in this setting. It is safe to add, remove, + * and modify secrets inside @func, though any additions or removals made during + * iteration will not be part of the iteration. + */ +void +nm_setting_vpn_foreach_secret (NMSettingVPN *setting, + NMVPNIterFunc func, + gpointer user_data) +{ + g_return_if_fail (NM_IS_SETTING_VPN (setting)); + + foreach_item_helper (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, func, user_data); +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); + + if (!priv->service_type) { + g_set_error_literal (error, + NM_SETTING_VPN_ERROR, + NM_SETTING_VPN_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_SERVICE_TYPE); + return FALSE; + } + + if (!strlen (priv->service_type)) { + g_set_error_literal (error, + NM_SETTING_VPN_ERROR, + NM_SETTING_VPN_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_SERVICE_TYPE); + return FALSE; + } + + /* default username can be NULL, but can't be zero-length */ + if (priv->user_name && !strlen (priv->user_name)) { + g_set_error_literal (error, + NM_SETTING_VPN_ERROR, + NM_SETTING_VPN_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_USER_NAME); + return FALSE; + } + + return TRUE; +} + +static NMSettingUpdateSecretResult +update_secret_string (NMSetting *setting, + const char *key, + const char *value, + GError **error) +{ + NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); + + g_return_val_if_fail (key != NULL, NM_SETTING_UPDATE_SECRET_ERROR); + g_return_val_if_fail (value != NULL, NM_SETTING_UPDATE_SECRET_ERROR); + + if (!value || !strlen (value)) { + g_set_error (error, NM_SETTING_ERROR, + NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH, + "Secret %s was empty", key); + return NM_SETTING_UPDATE_SECRET_ERROR; + } + + if (g_strcmp0 (g_hash_table_lookup (priv->secrets, key), value) == 0) + return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED; + + g_hash_table_insert (priv->secrets, g_strdup (key), g_strdup (value)); + return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED; +} + +static NMSettingUpdateSecretResult +update_secret_hash (NMSetting *setting, + GHashTable *secrets, + GError **error) +{ + NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); + GHashTableIter iter; + const char *name, *value; + NMSettingUpdateSecretResult result = NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED; + + g_return_val_if_fail (secrets != NULL, NM_SETTING_UPDATE_SECRET_ERROR); + + /* Make sure the items are valid */ + g_hash_table_iter_init (&iter, secrets); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &value)) { + if (!name || !strlen (name)) { + g_set_error_literal (error, NM_SETTING_ERROR, + NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH, + "Secret name was empty"); + return NM_SETTING_UPDATE_SECRET_ERROR; + } + + if (!value || !strlen (value)) { + g_set_error (error, NM_SETTING_ERROR, + NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH, + "Secret %s value was empty", name); + return NM_SETTING_UPDATE_SECRET_ERROR; + } + } + + /* Now add the items to the settings' secrets list */ + g_hash_table_iter_init (&iter, secrets); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &value)) { + if (value == NULL) { + g_warn_if_fail (value != NULL); + continue; + } + if (strlen (value) == 0) { + g_warn_if_fail (strlen (value) > 0); + continue; + } + + if (g_strcmp0 (g_hash_table_lookup (priv->secrets, name), value) == 0) + continue; + + g_hash_table_insert (priv->secrets, g_strdup (name), g_strdup (value)); + result = NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED; + } + + return result; +} + +static int +update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **error) +{ + NMSettingUpdateSecretResult success = NM_SETTING_UPDATE_SECRET_ERROR; + + g_return_val_if_fail (key != NULL, NM_SETTING_UPDATE_SECRET_ERROR); + g_return_val_if_fail (value != NULL, NM_SETTING_UPDATE_SECRET_ERROR); + + if (G_VALUE_HOLDS_STRING (value)) { + /* Passing the string properties individually isn't correct, and won't + * produce the correct result, but for some reason that's how it used + * to be done. So even though it's not correct, keep the code around + * for compatibility's sake. + */ + success = update_secret_string (setting, key, g_value_get_string (value), error); + } else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_MAP_OF_STRING)) { + if (strcmp (key, NM_SETTING_VPN_SECRETS) != 0) { + g_set_error (error, NM_SETTING_ERROR, NM_SETTING_ERROR_PROPERTY_NOT_SECRET, + "Property %s not a secret property", key); + } else + success = update_secret_hash (setting, g_value_get_boxed (value), error); + } else + g_set_error_literal (error, NM_SETTING_ERROR, NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH, key); + + if (success == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED) + g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS); + + return success; +} + +static gboolean +get_secret_flags (NMSetting *setting, + const char *secret_name, + gboolean verify_secret, + NMSettingSecretFlags *out_flags, + GError **error) +{ + NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); + gboolean success = FALSE; + char *flags_key; + gpointer val; + unsigned long tmp; + + flags_key = g_strdup_printf ("%s-flags", secret_name); + if (g_hash_table_lookup_extended (priv->data, flags_key, NULL, &val)) { + errno = 0; + tmp = strtoul ((const char *) val, NULL, 10); + if ((errno == 0) && (tmp <= NM_SETTING_SECRET_FLAGS_ALL)) { + if (out_flags) + *out_flags = (guint32) tmp; + success = TRUE; + } else { + g_set_error (error, + NM_SETTING_ERROR, + NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH, + "Failed to convert '%s' value '%s' to uint", + flags_key, (const char *) val); + } + } else { + g_set_error (error, + NM_SETTING_ERROR, + NM_SETTING_ERROR_PROPERTY_NOT_FOUND, + "Secret flags property '%s' not found", flags_key); + } + g_free (flags_key); + return success; +} + +static gboolean +set_secret_flags (NMSetting *setting, + const char *secret_name, + gboolean verify_secret, + NMSettingSecretFlags flags, + GError **error) +{ + g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->data, + g_strdup_printf ("%s-flags", secret_name), + g_strdup_printf ("%u", flags)); + g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS); + return TRUE; +} + +static GPtrArray * +need_secrets (NMSetting *setting) +{ + /* Assume that VPN connections need secrets since they almost always will */ + return g_ptr_array_sized_new (1); +} + +static gboolean +compare_one_secret (NMSettingVPN *a, + NMSettingVPN *b, + NMSettingCompareFlags flags) +{ + GHashTable *a_secrets, *b_secrets; + GHashTableIter iter; + const char *key, *val; + + a_secrets = NM_SETTING_VPN_GET_PRIVATE (a)->secrets; + b_secrets = NM_SETTING_VPN_GET_PRIVATE (b)->secrets; + + g_hash_table_iter_init (&iter, a_secrets); + while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &val)) { + NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE; + NMSettingSecretFlags b_secret_flags = NM_SETTING_SECRET_FLAG_NONE; + + nm_setting_get_secret_flags (NM_SETTING (a), key, &a_secret_flags, NULL); + nm_setting_get_secret_flags (NM_SETTING (b), key, &b_secret_flags, NULL); + + /* If the secret flags aren't the same, the settings aren't the same */ + if (a_secret_flags != b_secret_flags) + return FALSE; + + if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS) + && (a_secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED)) + continue; + + if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS) + && (a_secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) + continue; + + /* Now compare the values themselves */ + if (g_strcmp0 (val, nm_setting_vpn_get_secret (b, key)) != 0) + return FALSE; + } + + return TRUE; +} + +static gboolean +compare_property (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags) +{ + gboolean same; + + /* We only need to treat the 'secrets' property specially */ + if (g_strcmp0 (prop_spec->name, NM_SETTING_VPN_SECRETS) != 0) + return NM_SETTING_CLASS (nm_setting_vpn_parent_class)->compare_property (setting, other, prop_spec, flags); + + /* Compare A to B to ensure everything in A is found in B */ + same = compare_one_secret (NM_SETTING_VPN (setting), NM_SETTING_VPN (other), flags); + if (same) { + /* And then B to A to ensure everything in B is also found in A */ + same = compare_one_secret (NM_SETTING_VPN (other), NM_SETTING_VPN (setting), flags); + } + + return same; +} + +static gboolean +clear_secrets_with_flags (NMSetting *setting, + GParamSpec *pspec, + NMSettingClearSecretsWithFlagsFn func, + gpointer user_data) +{ + NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); + GHashTableIter iter; + const char *secret; + gboolean changed = TRUE; + + if (priv->secrets == NULL) + return FALSE; + + /* Iterate through secrets hash and check each entry */ + g_hash_table_iter_init (&iter, priv->secrets); + while (g_hash_table_iter_next (&iter, (gpointer) &secret, NULL)) { + NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; + + nm_setting_get_secret_flags (setting, secret, &flags, NULL); + if (func (setting, pspec->name, flags, user_data) == TRUE) { + g_hash_table_iter_remove (&iter); + changed = TRUE; + } + } + + if (changed) + g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS); + + return changed; +} + +static void +destroy_one_secret (gpointer data) +{ + char *secret = (char *) data; + + /* Don't leave the secret lying around in memory */ + memset (secret, 0, strlen (secret)); + g_free (secret); +} + +static void +nm_setting_vpn_init (NMSettingVPN *setting) +{ + NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); + + priv->data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_one_secret); +} + +static void +finalize (GObject *object) +{ + NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (object); + + g_free (priv->service_type); + g_free (priv->user_name); + g_hash_table_destroy (priv->data); + g_hash_table_destroy (priv->secrets); + + G_OBJECT_CLASS (nm_setting_vpn_parent_class)->finalize (object); +} + +static void +copy_hash (gpointer key, gpointer value, gpointer user_data) +{ + g_return_if_fail (value != NULL); + g_return_if_fail (strlen (value)); + g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), g_strdup (value)); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (object); + GHashTable *new_hash; + + switch (prop_id) { + case PROP_SERVICE_TYPE: + g_free (priv->service_type); + priv->service_type = g_value_dup_string (value); + break; + case PROP_USER_NAME: + g_free (priv->user_name); + priv->user_name = g_value_dup_string (value); + break; + case PROP_DATA: + /* Must make a deep copy of the hash table here... */ + g_hash_table_remove_all (priv->data); + new_hash = g_value_get_boxed (value); + if (new_hash) + g_hash_table_foreach (new_hash, copy_hash, priv->data); + break; + case PROP_SECRETS: + /* Must make a deep copy of the hash table here... */ + g_hash_table_remove_all (priv->secrets); + new_hash = g_value_get_boxed (value); + if (new_hash) + g_hash_table_foreach (new_hash, copy_hash, priv->secrets); + 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) +{ + NMSettingVPN *setting = NM_SETTING_VPN (object); + NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); + + switch (prop_id) { + case PROP_SERVICE_TYPE: + g_value_set_string (value, nm_setting_vpn_get_service_type (setting)); + break; + case PROP_USER_NAME: + g_value_set_string (value, nm_setting_vpn_get_user_name (setting)); + break; + case PROP_DATA: + g_value_set_boxed (value, priv->data); + break; + case PROP_SECRETS: + g_value_set_boxed (value, priv->secrets); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_vpn_class_init (NMSettingVPNClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingVPNPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + parent_class->verify = verify; + parent_class->update_one_secret = update_one_secret; + parent_class->get_secret_flags = get_secret_flags; + parent_class->set_secret_flags = set_secret_flags; + parent_class->need_secrets = need_secrets; + parent_class->compare_property = compare_property; + parent_class->clear_secrets_with_flags = clear_secrets_with_flags; + + /* Properties */ + /** + * NMSettingVPN:service-type: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_SERVICE_TYPE, + g_param_spec_string (NM_SETTING_VPN_SERVICE_TYPE, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingVPN:user-name: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_USER_NAME, + g_param_spec_string (NM_SETTING_VPN_USER_NAME, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingVPN:data: + * + * Dictionary of key/value pairs of VPN plugin specific data. Both keys and + * values must be strings. + **/ + g_object_class_install_property + (object_class, PROP_DATA, + _nm_param_spec_specialized (NM_SETTING_VPN_DATA, "", "", + DBUS_TYPE_G_MAP_OF_STRING, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingVPN:secrets: + * + * Dictionary of key/value pairs of VPN plugin specific secrets like + * passwords or private keys. Both keys and values must be strings. + **/ + g_object_class_install_property + (object_class, PROP_SECRETS, + _nm_param_spec_specialized (NM_SETTING_VPN_SECRETS, "", "", + DBUS_TYPE_G_MAP_OF_STRING, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-vpn.h b/libnm-core/nm-setting-vpn.h new file mode 100644 index 0000000000..3eb51ee9d6 --- /dev/null +++ b/libnm-core/nm-setting-vpn.h @@ -0,0 +1,115 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2013 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SETTING_VPN_H +#define NM_SETTING_VPN_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_VPN (nm_setting_vpn_get_type ()) +#define NM_SETTING_VPN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_VPN, NMSettingVPN)) +#define NM_SETTING_VPN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_VPN, NMSettingVPNClass)) +#define NM_IS_SETTING_VPN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_VPN)) +#define NM_IS_SETTING_VPN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_VPN)) +#define NM_SETTING_VPN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_VPN, NMSettingVPNClass)) + +#define NM_SETTING_VPN_SETTING_NAME "vpn" + +/** + * NMSettingVpnError: + * @NM_SETTING_VPN_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_VPN_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_VPN_ERROR_MISSING_PROPERTY: the property was missing and is + * required + */ +typedef enum { + NM_SETTING_VPN_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_VPN_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_VPN_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ +} NMSettingVpnError; + +#define NM_SETTING_VPN_ERROR nm_setting_vpn_error_quark () +GQuark nm_setting_vpn_error_quark (void); + +#define NM_SETTING_VPN_SERVICE_TYPE "service-type" +#define NM_SETTING_VPN_USER_NAME "user-name" +#define NM_SETTING_VPN_DATA "data" +#define NM_SETTING_VPN_SECRETS "secrets" + +typedef struct { + NMSetting parent; +} NMSettingVPN; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingVPNClass; + +/** + * NMVPNIterFunc: + * @key: the name of the data or secret item + * @value: the value of the data or secret item + * @user_data: User data passed to nm_setting_vpn_foreach_data_item() or + * nm_setting_vpn_foreach_secret() + **/ +typedef void (*NMVPNIterFunc) (const char *key, const char *value, gpointer user_data); + +GType nm_setting_vpn_get_type (void); + +NMSetting *nm_setting_vpn_new (void); +const char *nm_setting_vpn_get_service_type (NMSettingVPN *setting); +const char *nm_setting_vpn_get_user_name (NMSettingVPN *setting); + +guint32 nm_setting_vpn_get_num_data_items (NMSettingVPN *setting); +void nm_setting_vpn_add_data_item (NMSettingVPN *setting, + const char *key, + const char *item); +const char * nm_setting_vpn_get_data_item (NMSettingVPN *setting, + const char *key); +gboolean nm_setting_vpn_remove_data_item (NMSettingVPN *setting, + const char *key); +void nm_setting_vpn_foreach_data_item (NMSettingVPN *setting, + NMVPNIterFunc func, + gpointer user_data); + +guint32 nm_setting_vpn_get_num_secrets (NMSettingVPN *setting); +void nm_setting_vpn_add_secret (NMSettingVPN *setting, + const char *key, + const char *secret); +const char * nm_setting_vpn_get_secret (NMSettingVPN *setting, + const char *key); +gboolean nm_setting_vpn_remove_secret (NMSettingVPN *setting, + const char *key); +void nm_setting_vpn_foreach_secret (NMSettingVPN *setting, + NMVPNIterFunc func, + gpointer user_data); + +G_END_DECLS + +#endif /* NM_SETTING_VPN_H */ diff --git a/libnm-core/nm-setting-wimax.c b/libnm-core/nm-setting-wimax.c new file mode 100644 index 0000000000..1a4a6ec384 --- /dev/null +++ b/libnm-core/nm-setting-wimax.c @@ -0,0 +1,262 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2013 Red Hat, Inc. + * Copyright 2009 Novell, Inc. + */ + +#include <string.h> +#include <net/ethernet.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "nm-setting-wimax.h" +#include "nm-param-spec-specialized.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-wimax + * @short_description: Describes 802.16e Mobile WiMAX connection properties + * @include: nm-setting-wimax.h + * + * The #NMSettingWimax object is a #NMSetting subclass that describes properties + * necessary for connection to 802.16e Mobile WiMAX networks. + **/ + +/** + * nm_setting_wimax_error_quark: + * + * Registers an error quark for #NMSettingWimax if necessary. + * + * Returns: the error quark used for #NMSettingWimax errors. + **/ +GQuark +nm_setting_wimax_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-wimax-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingWimax, nm_setting_wimax, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_WIMAX_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_WIMAX_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIMAX) + +#define NM_SETTING_WIMAX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIMAX, NMSettingWimaxPrivate)) + +typedef struct { + char *network_name; + GByteArray *mac_address; +} NMSettingWimaxPrivate; + +enum { + PROP_0, + PROP_NETWORK_NAME, + PROP_MAC_ADDRESS, + + LAST_PROP +}; + +/** + * nm_setting_wimax_new: + * + * Creates a new #NMSettingWimax object with default values. + * + * Returns: the new empty #NMSettingWimax object + **/ +NMSetting * +nm_setting_wimax_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIMAX, NULL); +} + +/** + * nm_setting_wimax_get_network_name: + * @setting: the #NMSettingWimax + * + * Returns the WiMAX NSP name (ex "Sprint" or "CLEAR") which identifies the + * specific WiMAX network this setting describes a connection to. + * + * Returns: the WiMAX NSP name + **/ +const char * +nm_setting_wimax_get_network_name (NMSettingWimax *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIMAX (setting), NULL); + + return NM_SETTING_WIMAX_GET_PRIVATE (setting)->network_name; +} + +/** + * nm_setting_wimax_get_mac_address: + * @setting: the #NMSettingWimax + * + * Returns the MAC address of a WiMAX device which this connection is locked + * to. + * + * Returns: the MAC address + **/ +const GByteArray * +nm_setting_wimax_get_mac_address (NMSettingWimax *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIMAX (setting), NULL); + + return NM_SETTING_WIMAX_GET_PRIVATE (setting)->mac_address; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingWimaxPrivate *priv = NM_SETTING_WIMAX_GET_PRIVATE (setting); + + if (!priv->network_name) { + g_set_error_literal (error, + NM_SETTING_WIMAX_ERROR, + NM_SETTING_WIMAX_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIMAX_SETTING_NAME, NM_SETTING_WIMAX_NETWORK_NAME); + return FALSE; + } + + if (!strlen (priv->network_name)) { + g_set_error_literal (error, + NM_SETTING_WIMAX_ERROR, + NM_SETTING_WIMAX_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIMAX_SETTING_NAME, NM_SETTING_WIMAX_NETWORK_NAME); + return FALSE; + } + + if (priv->mac_address && priv->mac_address->len != ETH_ALEN) { + g_set_error_literal (error, + NM_SETTING_WIMAX_ERROR, + NM_SETTING_WIMAX_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIMAX_SETTING_NAME, NM_SETTING_WIMAX_MAC_ADDRESS); + return FALSE; + } + + return TRUE; +} + +static void +nm_setting_wimax_init (NMSettingWimax *setting) +{ +} + +static void +finalize (GObject *object) +{ + NMSettingWimaxPrivate *priv = NM_SETTING_WIMAX_GET_PRIVATE (object); + + g_free (priv->network_name); + if (priv->mac_address) + g_byte_array_free (priv->mac_address, TRUE); + + G_OBJECT_CLASS (nm_setting_wimax_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingWimaxPrivate *priv = NM_SETTING_WIMAX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_NETWORK_NAME: + g_free (priv->network_name); + priv->network_name = g_value_dup_string (value); + break; + case PROP_MAC_ADDRESS: + if (priv->mac_address) + g_byte_array_free (priv->mac_address, TRUE); + priv->mac_address = g_value_dup_boxed (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) +{ + NMSettingWimax *setting = NM_SETTING_WIMAX (object); + + switch (prop_id) { + case PROP_NETWORK_NAME: + g_value_set_string (value, nm_setting_wimax_get_network_name (setting)); + break; + case PROP_MAC_ADDRESS: + g_value_set_boxed (value, nm_setting_wimax_get_mac_address (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_wimax_class_init (NMSettingWimaxClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingWimaxPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + + /* Properties */ + /** + * NMSettingWimax:network-name: + * + * Network Service Provider (NSP) name of the WiMAX network this connection + * should use. + **/ + g_object_class_install_property + (object_class, PROP_NETWORK_NAME, + g_param_spec_string (NM_SETTING_WIMAX_NETWORK_NAME, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWimax:mac-address: + * + * 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). + **/ + g_object_class_install_property + (object_class, PROP_MAC_ADDRESS, + _nm_param_spec_specialized (NM_SETTING_WIMAX_MAC_ADDRESS, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-wimax.h b/libnm-core/nm-setting-wimax.h new file mode 100644 index 0000000000..781d717c2d --- /dev/null +++ b/libnm-core/nm-setting-wimax.h @@ -0,0 +1,73 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2009 Novell, Inc. + */ + +#ifndef NM_SETTING_WIMAX_H +#define NM_SETTING_WIMAX_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_WIMAX (nm_setting_wimax_get_type ()) +#define NM_SETTING_WIMAX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WIMAX, NMSettingWimax)) +#define NM_SETTING_WIMAX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WIMAX, NMSettingWimaxClass)) +#define NM_IS_SETTING_WIMAX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WIMAX)) +#define NM_IS_SETTING_WIMAX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_WIMAX)) +#define NM_SETTING_WIMAX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WIMAX, NMSettingWimaxClass)) + +#define NM_SETTING_WIMAX_SETTING_NAME "wimax" + +/** + * NMSettingWimaxError: + * @NM_SETTING_WIMAX_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_WIMAX_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_WIMAX_ERROR_MISSING_PROPERTY: the property was missing and is + * required + */ +typedef enum { + NM_SETTING_WIMAX_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_WIMAX_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_WIMAX_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/ +} NMSettingWimaxError; + +#define NM_SETTING_WIMAX_ERROR nm_setting_wimax_error_quark () +GQuark nm_setting_wimax_error_quark (void); + +#define NM_SETTING_WIMAX_NETWORK_NAME "network-name" +#define NM_SETTING_WIMAX_MAC_ADDRESS "mac-address" + +typedef struct { + NMSetting parent; +} NMSettingWimax; + +typedef struct { + NMSettingClass parent; +} NMSettingWimaxClass; + +GType nm_setting_wimax_get_type (void); + +NMSetting *nm_setting_wimax_new (void); +const char *nm_setting_wimax_get_network_name (NMSettingWimax *setting); +const GByteArray *nm_setting_wimax_get_mac_address (NMSettingWimax *setting); + +G_END_DECLS + +#endif /* NM_SETTING_WIMAX_H */ diff --git a/libnm-core/nm-setting-wired.c b/libnm-core/nm-setting-wired.c new file mode 100644 index 0000000000..bc82bf3477 --- /dev/null +++ b/libnm-core/nm-setting-wired.c @@ -0,0 +1,1032 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include <string.h> +#include <net/ethernet.h> +#include <netinet/ether.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "nm-setting-wired.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-utils-private.h" +#include "nm-dbus-glib-types.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-wired + * @short_description: Describes connection properties for Ethernet-based networks + * @include: nm-setting-wired.h + * + * The #NMSettingWired object is a #NMSetting subclass that describes properties + * necessary for connection to Ethernet networks. + **/ + +/** + * nm_setting_wired_error_quark: + * + * Registers an error quark for #NMSettingWired if necessary. + * + * Returns: the error quark used for #NMSettingWired errors. + **/ +GQuark +nm_setting_wired_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-wired-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingWired, nm_setting_wired, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_WIRED_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_WIRED_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIRED) + +#define NM_SETTING_WIRED_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRED, NMSettingWiredPrivate)) + +typedef struct { + char *port; + guint32 speed; + char *duplex; + gboolean auto_negotiate; + GByteArray *device_mac_address; + GByteArray *cloned_mac_address; + GSList *mac_address_blacklist; + guint32 mtu; + GPtrArray *s390_subchannels; + char *s390_nettype; + GHashTable *s390_options; +} NMSettingWiredPrivate; + +enum { + PROP_0, + PROP_PORT, + PROP_SPEED, + PROP_DUPLEX, + PROP_AUTO_NEGOTIATE, + PROP_MAC_ADDRESS, + PROP_CLONED_MAC_ADDRESS, + PROP_MAC_ADDRESS_BLACKLIST, + PROP_MTU, + PROP_S390_SUBCHANNELS, + PROP_S390_NETTYPE, + PROP_S390_OPTIONS, + + LAST_PROP +}; + +static const char *valid_s390_opts[] = { + "portno", "layer2", "portname", "protocol", "priority_queueing", + "buffer_count", "isolation", "total", "inter", "inter_jumbo", "route4", + "route6", "fake_broadcast", "broadcast_mode", "canonical_macaddr", + "checksumming", "sniffer", "large_send", "ipato_enable", "ipato_invert4", + "ipato_add4", "ipato_invert6", "ipato_add6", "vipa_add4", "vipa_add6", + "rxip_add4", "rxip_add6", "lancmd_timeout", "ctcprot", + NULL +}; + +/** + * nm_setting_wired_new: + * + * Creates a new #NMSettingWired object with default values. + * + * Returns: (transfer full): the new empty #NMSettingWired object + **/ +NMSetting * +nm_setting_wired_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRED, NULL); +} + +/** + * nm_setting_wired_get_port: + * @setting: the #NMSettingWired + * + * Returns: the #NMSettingWired:port property of the setting + **/ +const char * +nm_setting_wired_get_port (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->port; +} + +/** + * nm_setting_wired_get_speed: + * @setting: the #NMSettingWired + * + * Returns: the #NMSettingWired:speed property of the setting + **/ +guint32 +nm_setting_wired_get_speed (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), 0); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->speed; +} + +/** + * nm_setting_wired_get_duplex: + * @setting: the #NMSettingWired + * + * Returns: the #NMSettingWired:duplex property of the setting + **/ +const char * +nm_setting_wired_get_duplex (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->duplex; +} + +/** + * nm_setting_wired_get_auto_negotiate: + * @setting: the #NMSettingWired + * + * Returns: the #NMSettingWired:auto-negotiate property of the setting + **/ +gboolean +nm_setting_wired_get_auto_negotiate (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->auto_negotiate; +} + +/** + * nm_setting_wired_get_mac_address: + * @setting: the #NMSettingWired + * + * Returns: the #NMSettingWired:mac-address property of the setting + **/ +const GByteArray * +nm_setting_wired_get_mac_address (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->device_mac_address; +} + +/** + * nm_setting_wired_get_cloned_mac_address: + * @setting: the #NMSettingWired + * + * Returns: the #NMSettingWired:cloned-mac-address property of the setting + **/ +const GByteArray * +nm_setting_wired_get_cloned_mac_address (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->cloned_mac_address; +} + +/** + * nm_setting_wired_get_mac_address_blacklist: + * @setting: the #NMSettingWired + * + * Returns: (element-type GLib.ByteArray): the #NMSettingWired:mac-address-blacklist + * property of the setting + **/ +const GSList * +nm_setting_wired_get_mac_address_blacklist (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->mac_address_blacklist; +} + +/** + * nm_setting_wired_get_num_mac_blacklist_items: + * @setting: the #NMSettingWired + * + * Returns: the number of blacklisted MAC addresses + * + * Since: 0.9.10 + **/ +guint32 +nm_setting_wired_get_num_mac_blacklist_items (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), 0); + + return g_slist_length (NM_SETTING_WIRED_GET_PRIVATE (setting)->mac_address_blacklist); +} + +/** + * nm_setting_wired_get_mac_blacklist_item: + * @setting: the #NMSettingWired + * @idx: the zero-based index of the MAC address entry + * + * Returns: the blacklisted MAC address string (hex-digits-and-colons notation) + * at index @idx + * + * Since: 0.9.10 + **/ +const char * +nm_setting_wired_get_mac_blacklist_item (NMSettingWired *setting, guint32 idx) +{ + NMSettingWiredPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + + priv = NM_SETTING_WIRED_GET_PRIVATE (setting); + g_return_val_if_fail (idx <= g_slist_length (priv->mac_address_blacklist), NULL); + + return (const char *) g_slist_nth_data (priv->mac_address_blacklist, idx); +} + +/** + * nm_setting_wired_add_mac_blacklist_item: + * @setting: the #NMSettingWired + * @mac: the MAC address string (hex-digits-and-colons notation) to blacklist + * + * Adds a new MAC address to the #NMSettingWired:mac-address-blacklist property. + * + * Returns: %TRUE if the MAC address was added; %FALSE if the MAC address + * is invalid or was already present + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_wired_add_mac_blacklist_item (NMSettingWired *setting, const char *mac) +{ + NMSettingWiredPrivate *priv; + GSList *iter; + guint8 buf[32]; + + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE); + g_return_val_if_fail (mac != NULL, FALSE); + + if (!nm_utils_hwaddr_aton (mac, ARPHRD_ETHER, buf)) + return FALSE; + + priv = NM_SETTING_WIRED_GET_PRIVATE (setting); + for (iter = priv->mac_address_blacklist; iter; iter = g_slist_next (iter)) { + if (!strcasecmp (mac, (char *) iter->data)) + return FALSE; + } + + priv->mac_address_blacklist = g_slist_append (priv->mac_address_blacklist, + g_ascii_strup (mac, -1)); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST); + return TRUE; +} + +/** + * nm_setting_wired_remove_mac_blacklist_item: + * @setting: the #NMSettingWired + * @idx: index number of the MAC address + * + * Removes the MAC address at index @idx from the blacklist. + * + * Since: 0.9.10 + **/ +void +nm_setting_wired_remove_mac_blacklist_item (NMSettingWired *setting, guint32 idx) +{ + NMSettingWiredPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_WIRED (setting)); + + priv = NM_SETTING_WIRED_GET_PRIVATE (setting); + elt = g_slist_nth (priv->mac_address_blacklist, idx); + g_return_if_fail (elt != NULL); + + g_free (elt->data); + priv->mac_address_blacklist = g_slist_delete_link (priv->mac_address_blacklist, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST); +} + +/** + * nm_setting_wired_remove_mac_blacklist_item_by_value: + * @setting: the #NMSettingWired + * @mac: the MAC address string (hex-digits-and-colons notation) to remove from + * the blacklist + * + * Removes the MAC address @mac from the blacklist. + * + * Returns: %TRUE if the MAC address was found and removed; %FALSE if it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_wired_remove_mac_blacklist_item_by_value (NMSettingWired *setting, const char *mac) +{ + NMSettingWiredPrivate *priv; + GSList *iter; + guint8 buf[32]; + + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE); + g_return_val_if_fail (mac != NULL, FALSE); + + if (!nm_utils_hwaddr_aton (mac, ARPHRD_ETHER, buf)) + return FALSE; + + priv = NM_SETTING_WIRED_GET_PRIVATE (setting); + for (iter = priv->mac_address_blacklist; iter; iter = g_slist_next (iter)) { + if (!strcasecmp (mac, (char *) iter->data)) { + priv->mac_address_blacklist = g_slist_delete_link (priv->mac_address_blacklist, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_wired_clear_mac_blacklist_items: + * @setting: the #NMSettingWired + * + * Removes all blacklisted MAC addresses. + * + * Since: 0.9.10 + **/ +void +nm_setting_wired_clear_mac_blacklist_items (NMSettingWired *setting) +{ + g_return_if_fail (NM_IS_SETTING_WIRED (setting)); + + g_slist_free_full (NM_SETTING_WIRED_GET_PRIVATE (setting)->mac_address_blacklist, g_free); + NM_SETTING_WIRED_GET_PRIVATE (setting)->mac_address_blacklist = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST); +} + +/** + * nm_setting_wired_get_mtu: + * @setting: the #NMSettingWired + * + * Returns: the #NMSettingWired:mtu property of the setting + **/ +guint32 +nm_setting_wired_get_mtu (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), 0); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->mtu; +} + +/** + * nm_setting_wired_get_s390_subchannels: + * @setting: the #NMSettingWired + * + * Return the list of s390 subchannels that identify the device that this + * connection is applicable to. The connection should only be used in + * conjunction with that device. + * + * Returns: (element-type utf8): #GPtrArray of strings, each specifying one + * subchannel the s390 device uses to communicate to the host. + **/ +const GPtrArray * +nm_setting_wired_get_s390_subchannels (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_subchannels; +} + +/** + * nm_setting_wired_get_s390_nettype: + * @setting: the #NMSettingWired + * + * Returns the s390 device type this connection should apply to. Will be one + * of 'qeth', 'lcs', or 'ctc'. + * + * Returns: the s390 device type + **/ +const char * +nm_setting_wired_get_s390_nettype (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_nettype; +} + +/** + * nm_setting_wired_get_num_s390_options: + * @setting: the #NMSettingWired + * + * Returns the number of s390-specific options that should be set for this + * device when it is activated. This can be used to retrieve each s390 + * option individually using nm_setting_wired_get_s390_option(). + * + * Returns: the number of s390-specific device options + **/ +guint32 +nm_setting_wired_get_num_s390_options (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), 0); + + return g_hash_table_size (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options); +} + +/** + * nm_setting_wired_get_s390_option: + * @setting: the #NMSettingWired + * @idx: index of the desired option, from 0 to + * nm_setting_wired_get_num_s390_options() - 1 + * @out_key: (out): on return, the key name of the s390 specific option; this + * value is owned by the setting and should not be modified + * @out_value: (out): on return, the value of the key of the s390 specific + * option; this value is owned by the setting and should not be modified + * + * Given an index, return the value of the s390 option at that index. indexes + * are *not* guaranteed to be static across modifications to options done by + * nm_setting_wired_add_s390_option() and nm_setting_wired_remove_s390_option(), + * and should not be used to refer to options except for short periods of time + * such as during option iteration. + * + * Returns: %TRUE on success if the index was valid and an option was found, + * %FALSE if the index was invalid (ie, greater than the number of options + * currently held by the setting) + **/ +gboolean +nm_setting_wired_get_s390_option (NMSettingWired *setting, + guint32 idx, + const char **out_key, + const char **out_value) +{ + NMSettingWiredPrivate *priv; + guint32 num_keys; + GList *keys; + const char *_key = NULL, *_value = NULL; + + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE); + + priv = NM_SETTING_WIRED_GET_PRIVATE (setting); + + num_keys = nm_setting_wired_get_num_s390_options (setting); + g_return_val_if_fail (idx < num_keys, FALSE); + + keys = g_hash_table_get_keys (priv->s390_options); + _key = g_list_nth_data (keys, idx); + _value = g_hash_table_lookup (priv->s390_options, _key); + + if (out_key) + *out_key = _key; + if (out_value) + *out_value = _value; + return TRUE; +} + +/** + * nm_setting_wired_get_s390_option_by_key: + * @setting: the #NMSettingWired + * @key: the key for which to retrieve the value + * + * Returns the value associated with the s390-specific option specified by + * @key, if it exists. + * + * Returns: the value, or %NULL if the key/value pair was never added to the + * setting; the value is owned by the setting and must not be modified + **/ +const char * +nm_setting_wired_get_s390_option_by_key (NMSettingWired *setting, + const char *key) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (strlen (key), NULL); + + return g_hash_table_lookup (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options, key); +} + +/** + * nm_setting_wired_add_s390_option: + * @setting: the #NMSettingWired + * @key: key name for the option + * @value: value for the option + * + * Add an option to the table. The option is compared to an internal list + * of allowed options. Key names may contain only alphanumeric characters + * (ie [a-zA-Z0-9]). Adding a new key replaces any existing key/value pair that + * may already exist. + * + * Returns: %TRUE if the option was valid and was added to the internal option + * list, %FALSE if it was not. + **/ +gboolean +nm_setting_wired_add_s390_option (NMSettingWired *setting, + const char *key, + const char *value) +{ + size_t value_len; + + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (strlen (key), FALSE); + g_return_val_if_fail (_nm_utils_string_in_list (key, valid_s390_opts), FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + value_len = strlen (value); + g_return_val_if_fail (value_len > 0 && value_len < 200, FALSE); + + g_hash_table_insert (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options, + g_strdup (key), + g_strdup (value)); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_S390_OPTIONS); + return TRUE; +} + +/** + * nm_setting_wired_remove_s390_option: + * @setting: the #NMSettingWired + * @key: key name for the option to remove + * + * Remove the s390-specific option referenced by @key from the internal option + * list. + * + * Returns: %TRUE if the option was found and removed from the internal option + * list, %FALSE if it was not. + **/ +gboolean +nm_setting_wired_remove_s390_option (NMSettingWired *setting, + const char *key) +{ + gboolean found; + + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (strlen (key), FALSE); + + found = g_hash_table_remove (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options, key); + if (found) + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_S390_OPTIONS); + return found; +} + +/** + * nm_setting_wired_get_valid_s390_options: + * @setting: the #NMSettingWired + * + * Returns a list of valid s390 options. + * + * Returns: (transfer none): a %NULL-terminated array of strings of valid s390 options. + * + * Since: 0.9.10 + **/ +const char ** +nm_setting_wired_get_valid_s390_options (NMSettingWired *setting) +{ + return valid_s390_opts; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting); + const char *valid_ports[] = { "tp", "aui", "bnc", "mii", NULL }; + const char *valid_duplex[] = { "half", "full", NULL }; + const char *valid_nettype[] = { "qeth", "lcs", "ctc", NULL }; + GHashTableIter iter; + GSList* mac_blacklist_iter; + const char *key, *value; + + if (priv->port && !_nm_utils_string_in_list (priv->port, valid_ports)) { + g_set_error (error, + NM_SETTING_WIRED_ERROR, + NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid Ethernet port value"), + priv->port); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_PORT); + return FALSE; + } + + if (priv->duplex && !_nm_utils_string_in_list (priv->duplex, valid_duplex)) { + g_set_error (error, + NM_SETTING_WIRED_ERROR, + NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid duplex value"), + priv->duplex); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_DUPLEX); + return FALSE; + } + + if (priv->device_mac_address && priv->device_mac_address->len != ETH_ALEN) { + g_set_error_literal (error, + NM_SETTING_WIRED_ERROR, + NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, + _("is not a valid MAC address")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS); + return FALSE; + } + + for (mac_blacklist_iter = priv->mac_address_blacklist; mac_blacklist_iter; + mac_blacklist_iter = mac_blacklist_iter->next) { + struct ether_addr addr; + + if (!ether_aton_r (mac_blacklist_iter->data, &addr)) { + g_set_error (error, + NM_SETTING_WIRED_ERROR, + NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid MAC address"), + (const char *) mac_blacklist_iter->data); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST); + return FALSE; + } + } + + if ( priv->s390_subchannels + && !(priv->s390_subchannels->len == 3 || priv->s390_subchannels->len == 2)) { + g_set_error_literal (error, + NM_SETTING_WIRED_ERROR, + NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_S390_SUBCHANNELS); + return FALSE; + } + + if (priv->s390_nettype && !_nm_utils_string_in_list (priv->s390_nettype, valid_nettype)) { + g_set_error_literal (error, + NM_SETTING_WIRED_ERROR, + NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_S390_NETTYPE); + return FALSE; + } + + g_hash_table_iter_init (&iter, priv->s390_options); + while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) { + if ( !_nm_utils_string_in_list (key, valid_s390_opts) + || !strlen (value) + || (strlen (value) > 200)) { + g_set_error (error, + NM_SETTING_WIRED_ERROR, + NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, + _("invalid '%s' or its value '%s'"), + key, value); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_S390_OPTIONS); + return FALSE; + } + } + + if (priv->cloned_mac_address && priv->cloned_mac_address->len != ETH_ALEN) { + g_set_error_literal (error, + NM_SETTING_WIRED_ERROR, + NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, + _("is not a valid MAC address")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_CLONED_MAC_ADDRESS); + return FALSE; + } + + return TRUE; +} + +static void +nm_setting_wired_init (NMSettingWired *setting) +{ + NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting); + + priv->s390_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); +} + +static void +finalize (GObject *object) +{ + NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (object); + + g_free (priv->port); + g_free (priv->duplex); + g_free (priv->s390_nettype); + + g_hash_table_destroy (priv->s390_options); + + if (priv->device_mac_address) + g_byte_array_free (priv->device_mac_address, TRUE); + + if (priv->cloned_mac_address) + g_byte_array_free (priv->cloned_mac_address, TRUE); + + g_slist_free_full (priv->mac_address_blacklist, g_free); + + if (priv->s390_subchannels) { + g_ptr_array_set_free_func (priv->s390_subchannels, g_free); + g_ptr_array_free (priv->s390_subchannels, TRUE); + } + + G_OBJECT_CLASS (nm_setting_wired_parent_class)->finalize (object); +} + +static void +copy_hash (gpointer key, gpointer value, gpointer user_data) +{ + g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), g_strdup (value)); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (object); + GHashTable *new_hash; + + switch (prop_id) { + case PROP_PORT: + g_free (priv->port); + priv->port = g_value_dup_string (value); + break; + case PROP_SPEED: + priv->speed = g_value_get_uint (value); + break; + case PROP_DUPLEX: + g_free (priv->duplex); + priv->duplex = g_value_dup_string (value); + break; + case PROP_AUTO_NEGOTIATE: + priv->auto_negotiate = g_value_get_boolean (value); + break; + case PROP_MAC_ADDRESS: + if (priv->device_mac_address) + g_byte_array_free (priv->device_mac_address, TRUE); + priv->device_mac_address = g_value_dup_boxed (value); + break; + case PROP_CLONED_MAC_ADDRESS: + if (priv->cloned_mac_address) + g_byte_array_free (priv->cloned_mac_address, TRUE); + priv->cloned_mac_address = g_value_dup_boxed (value); + break; + case PROP_MAC_ADDRESS_BLACKLIST: + g_slist_free_full (priv->mac_address_blacklist, g_free); + priv->mac_address_blacklist = g_value_dup_boxed (value); + break; + case PROP_MTU: + priv->mtu = g_value_get_uint (value); + break; + case PROP_S390_SUBCHANNELS: + if (priv->s390_subchannels) { + g_ptr_array_set_free_func (priv->s390_subchannels, g_free); + g_ptr_array_free (priv->s390_subchannels, TRUE); + } + priv->s390_subchannels = g_value_dup_boxed (value); + break; + case PROP_S390_NETTYPE: + g_free (priv->s390_nettype); + priv->s390_nettype = g_value_dup_string (value); + break; + case PROP_S390_OPTIONS: + /* Must make a deep copy of the hash table here... */ + g_hash_table_remove_all (priv->s390_options); + new_hash = g_value_get_boxed (value); + if (new_hash) + g_hash_table_foreach (new_hash, copy_hash, priv->s390_options); + 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) +{ + NMSettingWired *setting = NM_SETTING_WIRED (object); + NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting); + + switch (prop_id) { + case PROP_PORT: + g_value_set_string (value, nm_setting_wired_get_port (setting)); + break; + case PROP_SPEED: + g_value_set_uint (value, nm_setting_wired_get_speed (setting)); + break; + case PROP_DUPLEX: + g_value_set_string (value, nm_setting_wired_get_duplex (setting)); + break; + case PROP_AUTO_NEGOTIATE: + g_value_set_boolean (value, nm_setting_wired_get_auto_negotiate (setting)); + break; + case PROP_MAC_ADDRESS: + g_value_set_boxed (value, nm_setting_wired_get_mac_address (setting)); + break; + case PROP_CLONED_MAC_ADDRESS: + g_value_set_boxed (value, nm_setting_wired_get_cloned_mac_address (setting)); + break; + case PROP_MAC_ADDRESS_BLACKLIST: + g_value_set_boxed (value, nm_setting_wired_get_mac_address_blacklist (setting)); + break; + case PROP_MTU: + g_value_set_uint (value, nm_setting_wired_get_mtu (setting)); + break; + case PROP_S390_SUBCHANNELS: + g_value_set_boxed (value, nm_setting_wired_get_s390_subchannels (setting)); + break; + case PROP_S390_NETTYPE: + g_value_set_string (value, nm_setting_wired_get_s390_nettype (setting)); + break; + case PROP_S390_OPTIONS: + g_value_set_boxed (value, priv->s390_options); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_wired_class_init (NMSettingWiredClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingWiredPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + + /* Properties */ + /** + * NMSettingWired:port: + * + * Specific port type to use if multiple 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. + **/ + g_object_class_install_property + (object_class, PROP_PORT, + g_param_spec_string (NM_SETTING_WIRED_PORT, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWired:speed: + * + * If non-zero, request that the device use only the specified speed. In + * Mbit/s, ie 100 == 100Mbit/s. + **/ + g_object_class_install_property + (object_class, PROP_SPEED, + g_param_spec_uint (NM_SETTING_WIRED_SPEED, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWired:duplex: + * + * If specified, request that the device only use the specified duplex mode. + * Either "half" or "full". + **/ + g_object_class_install_property + (object_class, PROP_DUPLEX, + g_param_spec_string (NM_SETTING_WIRED_DUPLEX, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWired:auto-negotiate: + * + * If %TRUE, allow auto-negotiation of port speed and duplex mode. If + * %FALSE, do not allow auto-negotiation, in which case the "speed" and + * "duplex" properties should be set. + **/ + g_object_class_install_property + (object_class, PROP_AUTO_NEGOTIATE, + g_param_spec_boolean (NM_SETTING_WIRED_AUTO_NEGOTIATE, "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWired:mac-address: + * + * 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). + **/ + g_object_class_install_property + (object_class, PROP_MAC_ADDRESS, + _nm_param_spec_specialized (NM_SETTING_WIRED_MAC_ADDRESS, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWired:cloned-mac-address: + * + * If specified, request that the device use this MAC address instead of its + * permanent MAC address. This is known as MAC cloning or spoofing. + **/ + g_object_class_install_property + (object_class, PROP_CLONED_MAC_ADDRESS, + _nm_param_spec_specialized (NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWired:mac-address-blacklist: + * + * 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). + **/ + g_object_class_install_property + (object_class, PROP_MAC_ADDRESS_BLACKLIST, + _nm_param_spec_specialized (NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWired:mtu: + * + * If non-zero, only transmit packets of the specified size or smaller, + * breaking larger packets up into multiple Ethernet frames. + **/ + g_object_class_install_property + (object_class, PROP_MTU, + g_param_spec_uint (NM_SETTING_WIRED_MTU, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWired:s390-subchannels: + * + * Identifies specific subchannels that this network device uses for + * communication with z/VM or s390 host. Like the + * #NMSettingWired: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. + **/ + g_object_class_install_property + (object_class, PROP_S390_SUBCHANNELS, + _nm_param_spec_specialized (NM_SETTING_WIRED_S390_SUBCHANNELS, "", "", + DBUS_TYPE_G_ARRAY_OF_STRING, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWired:s390-nettype: + * + * s390 network device type; one of "qeth", "lcs", or "ctc", representing + * the different types of virtual network devices available on s390 systems. + **/ + g_object_class_install_property + (object_class, PROP_S390_NETTYPE, + g_param_spec_string (NM_SETTING_WIRED_S390_NETTYPE, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWired:s390-options: + * + * 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]). + **/ + g_object_class_install_property + (object_class, PROP_S390_OPTIONS, + _nm_param_spec_specialized (NM_SETTING_WIRED_S390_OPTIONS, "", "", + DBUS_TYPE_G_MAP_OF_STRING, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-wired.h b/libnm-core/nm-setting-wired.h new file mode 100644 index 0000000000..33d6d54dde --- /dev/null +++ b/libnm-core/nm-setting-wired.h @@ -0,0 +1,131 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SETTING_WIRED_H +#define NM_SETTING_WIRED_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_WIRED (nm_setting_wired_get_type ()) +#define NM_SETTING_WIRED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WIRED, NMSettingWired)) +#define NM_SETTING_WIRED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WIRED, NMSettingWiredClass)) +#define NM_IS_SETTING_WIRED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WIRED)) +#define NM_IS_SETTING_WIRED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_WIRED)) +#define NM_SETTING_WIRED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WIRED, NMSettingWiredClass)) + +#define NM_SETTING_WIRED_SETTING_NAME "802-3-ethernet" + +/** + * NMSettingWiredError: + * @NM_SETTING_WIRED_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_WIRED_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_WIRED_ERROR_MISSING_PROPERTY: the property was missing and is + * required + */ +typedef enum { + NM_SETTING_WIRED_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_WIRED_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/ +} NMSettingWiredError; + +#define NM_SETTING_WIRED_ERROR nm_setting_wired_error_quark () +GQuark nm_setting_wired_error_quark (void); + +#define NM_SETTING_WIRED_PORT "port" +#define NM_SETTING_WIRED_SPEED "speed" +#define NM_SETTING_WIRED_DUPLEX "duplex" +#define NM_SETTING_WIRED_AUTO_NEGOTIATE "auto-negotiate" +#define NM_SETTING_WIRED_MAC_ADDRESS "mac-address" +#define NM_SETTING_WIRED_CLONED_MAC_ADDRESS "cloned-mac-address" +#define NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST "mac-address-blacklist" +#define NM_SETTING_WIRED_MTU "mtu" +#define NM_SETTING_WIRED_S390_SUBCHANNELS "s390-subchannels" +#define NM_SETTING_WIRED_S390_NETTYPE "s390-nettype" +#define NM_SETTING_WIRED_S390_OPTIONS "s390-options" + +typedef struct { + NMSetting parent; +} NMSettingWired; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingWiredClass; + +GType nm_setting_wired_get_type (void); + +NMSetting * nm_setting_wired_new (void); +const char * nm_setting_wired_get_port (NMSettingWired *setting); +guint32 nm_setting_wired_get_speed (NMSettingWired *setting); +const char * nm_setting_wired_get_duplex (NMSettingWired *setting); +gboolean nm_setting_wired_get_auto_negotiate (NMSettingWired *setting); +const GByteArray *nm_setting_wired_get_mac_address (NMSettingWired *setting); +const GByteArray *nm_setting_wired_get_cloned_mac_address (NMSettingWired *setting); + +const GSList *nm_setting_wired_get_mac_address_blacklist (NMSettingWired *setting); +NM_AVAILABLE_IN_0_9_10 +guint32 nm_setting_wired_get_num_mac_blacklist_items (NMSettingWired *setting); +NM_AVAILABLE_IN_0_9_10 +const char * nm_setting_wired_get_mac_blacklist_item (NMSettingWired *setting, + guint32 idx); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_wired_add_mac_blacklist_item (NMSettingWired *setting, + const char *mac); +NM_AVAILABLE_IN_0_9_10 +void nm_setting_wired_remove_mac_blacklist_item (NMSettingWired *setting, + guint32 idx); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_wired_remove_mac_blacklist_item_by_value (NMSettingWired *setting, + const char *mac); +NM_AVAILABLE_IN_0_9_10 +void nm_setting_wired_clear_mac_blacklist_items (NMSettingWired *setting); + +guint32 nm_setting_wired_get_mtu (NMSettingWired *setting); + +const GPtrArray * nm_setting_wired_get_s390_subchannels (NMSettingWired *setting); +const char * nm_setting_wired_get_s390_nettype (NMSettingWired *setting); + +guint32 nm_setting_wired_get_num_s390_options (NMSettingWired *setting); +gboolean nm_setting_wired_get_s390_option (NMSettingWired *setting, + guint32 idx, + const char **out_key, + const char **out_value); +const char * nm_setting_wired_get_s390_option_by_key (NMSettingWired *setting, + const char *key); +gboolean nm_setting_wired_add_s390_option (NMSettingWired *setting, + const char *key, + const char *value); +gboolean nm_setting_wired_remove_s390_option (NMSettingWired *setting, + const char *key); +NM_AVAILABLE_IN_0_9_10 +const char ** nm_setting_wired_get_valid_s390_options (NMSettingWired *setting); + +G_END_DECLS + +#endif /* NM_SETTING_WIRED_H */ diff --git a/libnm-core/nm-setting-wireless-security.c b/libnm-core/nm-setting-wireless-security.c new file mode 100644 index 0000000000..2c46766476 --- /dev/null +++ b/libnm-core/nm-setting-wireless-security.c @@ -0,0 +1,1603 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include <config.h> +#include <string.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "nm-setting-wireless-security.h" +#include "nm-setting-8021x.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-dbus-glib-types.h" +#include "nm-utils-private.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-wireless-security + * @short_description: Describes connection properties for Wi-Fi networks that + * use WEP, LEAP, WPA or WPA2/RSN security + * @include: nm-setting-wireless-security.h + * + * The #NMSettingWirelessSecurity object is a #NMSetting subclass that describes + * properties necessary for connection to encrypted Wi-Fi networks. + * + * It's a good idea to read up on wpa_supplicant configuration before using this + * setting extensively, since most of the options here correspond closely with + * the relevant wpa_supplicant configuration options. To get a better overview + * of how Wi-Fi security works, you may want to get copies of the following books. + * + * 802.11 Wireless Networks: The Definitive Guide, Second Edition + * Author: Matthew Gast + * ISBN: 978-0596100520 + * + * Cisco Wireless LAN Security + * Authors: Krishna Sankar, Sri Sundaralingam, Darrin Miller, and Andrew Balinsky + * ISBN: 978-1587051548 + **/ + +/** + * nm_setting_wireless_security_error_quark: + * + * Registers an error quark for #NMSettingWired if necessary. + * + * Returns: the error quark used for #NMSettingWired errors. + **/ +GQuark +nm_setting_wireless_security_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-wireless-security-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingWirelessSecurity, nm_setting_wireless_security, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + g_define_type_id, + 2, + NM_SETTING_WIRELESS_SECURITY_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIRELESS_SECURITY) + +#define NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurityPrivate)) + +typedef struct { + char *key_mgmt; + char *auth_alg; + GSList *proto; /* GSList of strings */ + GSList *pairwise; /* GSList of strings */ + GSList *group; /* GSList of strings */ + + /* LEAP */ + char *leap_username; + char *leap_password; + NMSettingSecretFlags leap_password_flags; + + /* WEP */ + char *wep_key0; + char *wep_key1; + char *wep_key2; + char *wep_key3; + NMSettingSecretFlags wep_key_flags; + NMWepKeyType wep_key_type; + guint32 wep_tx_keyidx; + + /* WPA-PSK */ + char *psk; + NMSettingSecretFlags psk_flags; +} NMSettingWirelessSecurityPrivate; + +enum { + PROP_0, + PROP_KEY_MGMT, + PROP_WEP_TX_KEYIDX, + PROP_AUTH_ALG, + PROP_PROTO, + PROP_PAIRWISE, + PROP_GROUP, + PROP_LEAP_USERNAME, + PROP_WEP_KEY0, + PROP_WEP_KEY1, + PROP_WEP_KEY2, + PROP_WEP_KEY3, + PROP_WEP_KEY_FLAGS, + PROP_WEP_KEY_TYPE, + PROP_PSK, + PROP_PSK_FLAGS, + PROP_LEAP_PASSWORD, + PROP_LEAP_PASSWORD_FLAGS, + + LAST_PROP +}; + +/** + * nm_setting_wireless_security_new: + * + * Creates a new #NMSettingWirelessSecurity object with default values. + * + * Returns: (transfer full): the new empty #NMSettingWirelessSecurity object + **/ +NMSetting * +nm_setting_wireless_security_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRELESS_SECURITY, NULL); +} + +/** + * nm_setting_wireless_security_get_key_mgmt: + * @setting: the #NMSettingWirelessSecurity + * + * Returns: the #NMSettingWirelessSecurity:key-mgmt property of the setting + **/ +const char * +nm_setting_wireless_security_get_key_mgmt (NMSettingWirelessSecurity *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL); + + return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->key_mgmt; +} + +/** + * nm_setting_wireless_security_get_num_protos: + * @setting: the #NMSettingWirelessSecurity + * + * Returns: the number of security protocols this connection allows when + * connecting to secure Wi-Fi networks + **/ +guint32 +nm_setting_wireless_security_get_num_protos (NMSettingWirelessSecurity *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0); + + return g_slist_length (NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->proto); +} + +/** + * nm_setting_wireless_security_get_proto: + * @setting: the #NMSettingWirelessSecurity + * @i: an index into the protocol list + * + * Returns: the protocol at index @i + **/ +const char * +nm_setting_wireless_security_get_proto (NMSettingWirelessSecurity *setting, guint32 i) +{ + NMSettingWirelessSecurityPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->proto), NULL); + + return (const char *) g_slist_nth_data (priv->proto, i); +} + +/** + * nm_setting_wireless_security_add_proto: + * @setting: the #NMSettingWirelessSecurity + * @proto: the protocol to add, one of "wpa" or "rsn" + * + * Adds a Wi-Fi security protocol (one of "wpa" or "rsn") to the allowed list; + * only protocols in this list will be used when finding and connecting to + * the Wi-Fi network specified by this connection. For example, if the + * protocol list contains only "wpa" but the access point for the SSID specified + * by this connection only supports WPA2/RSN, the connection cannot be used + * with the access point. + * + * Returns: %TRUE if the protocol was new and and was added to the allowed + * protocol list, or %FALSE if it was already in the list + **/ +gboolean +nm_setting_wireless_security_add_proto (NMSettingWirelessSecurity *setting, const char *proto) +{ + NMSettingWirelessSecurityPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE); + g_return_val_if_fail (proto != NULL, FALSE); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + for (iter = priv->proto; iter; iter = g_slist_next (iter)) { + if (strcasecmp (proto, (char *) iter->data) == 0) + return FALSE; + } + + priv->proto = g_slist_append (priv->proto, g_ascii_strdown (proto, -1)); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PROTO); + return TRUE; +} + +/** + * nm_setting_wireless_security_remove_proto: + * @setting: the #NMSettingWirelessSecurity + * @i: index of the protocol to remove + * + * Removes a protocol from the allowed protocol list. + **/ +void +nm_setting_wireless_security_remove_proto (NMSettingWirelessSecurity *setting, guint32 i) +{ + NMSettingWirelessSecurityPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting)); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + elt = g_slist_nth (priv->proto, i); + g_return_if_fail (elt != NULL); + + g_free (elt->data); + priv->proto = g_slist_delete_link (priv->proto, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PROTO); +} + +/** + * nm_setting_wireless_security_remove_proto_by_value: + * @setting: the #NMSettingWirelessSecurity + * @proto: the protocol to remove, one of "wpa" or "rsn" + * + * Removes a protocol from the allowed protocol list. + * + * Returns: %TRUE if the protocol was found and removed; %FALSE it it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_wireless_security_remove_proto_by_value (NMSettingWirelessSecurity *setting, + const char *proto) +{ + NMSettingWirelessSecurityPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE); + g_return_val_if_fail (proto != NULL, FALSE); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + for (iter = priv->proto; iter; iter = g_slist_next (iter)) { + if (strcasecmp (proto, (char *) iter->data) == 0) { + priv->proto = g_slist_delete_link (priv->proto, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PROTO); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_wireless_security_clear_protos: + * @setting: the #NMSettingWirelessSecurity + * + * Removes all protocols from the allowed list. If there are no protocols + * specified then all protocols are allowed. + **/ +void +nm_setting_wireless_security_clear_protos (NMSettingWirelessSecurity *setting) +{ + NMSettingWirelessSecurityPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting)); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + g_slist_free_full (priv->proto, g_free); + priv->proto = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PROTO); +} + +/** + * nm_setting_wireless_security_get_num_pairwise: + * @setting: the #NMSettingWirelessSecurity + * + * Returns: the number of pairwise encryption algorithms in the allowed list + **/ +guint32 +nm_setting_wireless_security_get_num_pairwise (NMSettingWirelessSecurity *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0); + + return g_slist_length (NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->pairwise); +} + +/** + * nm_setting_wireless_security_get_pairwise: + * @setting: the #NMSettingWirelessSecurity + * @i: index of an item in the allowed pairwise encryption algorithm list + * + * Returns the allowed pairwise encryption algorithm from allowed algorithm + * list. + * + * Returns: the pairwise encryption algorithm at index @i + **/ +const char * +nm_setting_wireless_security_get_pairwise (NMSettingWirelessSecurity *setting, guint32 i) +{ + NMSettingWirelessSecurityPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->pairwise), NULL); + + return (const char *) g_slist_nth_data (priv->pairwise, i); +} + +/** + * nm_setting_wireless_security_add_pairwise: + * @setting: the #NMSettingWirelessSecurity + * @pairwise: the encryption algorithm to add, one of "tkip" or "ccmp" + * + * Adds an encryption algorithm to the list of allowed pairwise encryption + * algorithms. If the list is not empty, then only access points that support + * one or more of the encryption algorithms in the list will be considered + * compatible with this connection. + * + * Returns: %TRUE if the algorithm was added to the list, %FALSE if it was + * already in the list + **/ +gboolean +nm_setting_wireless_security_add_pairwise (NMSettingWirelessSecurity *setting, const char *pairwise) +{ + NMSettingWirelessSecurityPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE); + g_return_val_if_fail (pairwise != NULL, FALSE); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + for (iter = priv->pairwise; iter; iter = g_slist_next (iter)) { + if (strcasecmp (pairwise, (char *) iter->data) == 0) + return FALSE; + } + + priv->pairwise = g_slist_append (priv->pairwise, g_ascii_strdown (pairwise, -1)); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PAIRWISE); + return TRUE; +} + +/** + * nm_setting_wireless_security_remove_pairwise: + * @setting: the #NMSettingWirelessSecurity + * @i: the index of an item in the allowed pairwise encryption algorithm list + * + * Removes an encryption algorithm from the allowed pairwise encryption + * algorithm list. + **/ +void +nm_setting_wireless_security_remove_pairwise (NMSettingWirelessSecurity *setting, guint32 i) +{ + NMSettingWirelessSecurityPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting)); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + elt = g_slist_nth (priv->pairwise, i); + g_return_if_fail (elt != NULL); + + g_free (elt->data); + priv->pairwise = g_slist_delete_link (priv->pairwise, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PAIRWISE); +} + +/** + * nm_setting_wireless_security_remove_pairwise_by_value: + * @setting: the #NMSettingWirelessSecurity + * @pairwise: the encryption algorithm to remove, one of "tkip" or "ccmp" + * + * Removes an encryption algorithm from the allowed pairwise encryption + * algorithm list. + * + * Returns: %TRUE if the encryption algorith was found and removed; %FALSE it it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_wireless_security_remove_pairwise_by_value (NMSettingWirelessSecurity *setting, + const char *pairwise) +{ + NMSettingWirelessSecurityPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE); + g_return_val_if_fail (pairwise != NULL, FALSE); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + for (iter = priv->pairwise; iter; iter = g_slist_next (iter)) { + if (strcasecmp (pairwise, (char *) iter->data) == 0) { + priv->pairwise = g_slist_delete_link (priv->pairwise, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PAIRWISE); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_wireless_security_clear_pairwise: + * @setting: the #NMSettingWirelessSecurity + * + * Removes all algorithms from the allowed list. If there are no algorithms + * specified then all pairwise encryption algorithms are allowed. + **/ +void +nm_setting_wireless_security_clear_pairwise (NMSettingWirelessSecurity *setting) +{ + NMSettingWirelessSecurityPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting)); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + g_slist_free_full (priv->pairwise, g_free); + priv->pairwise = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PAIRWISE); +} + +/** + * nm_setting_wireless_security_get_num_groups: + * @setting: the #NMSettingWirelessSecurity + * + * Returns: the number of groupwise encryption algorithms in the allowed list + **/ +guint32 +nm_setting_wireless_security_get_num_groups (NMSettingWirelessSecurity *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0); + + return g_slist_length (NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->group); +} + +/** + * nm_setting_wireless_security_get_group: + * @setting: the #NMSettingWirelessSecurity + * @i: index of an item in the allowed groupwise encryption algorithm list + * + * Returns the allowed groupwise encryption algorithm from allowed algorithm + * list. + * + * Returns: the groupwise encryption algorithm at index @i + **/ +const char * +nm_setting_wireless_security_get_group (NMSettingWirelessSecurity *setting, guint32 i) +{ + NMSettingWirelessSecurityPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->group), NULL); + + return (const char *) g_slist_nth_data (priv->group, i); +} + +/** + * nm_setting_wireless_security_add_group: + * @setting: the #NMSettingWirelessSecurity + * @group: the encryption algorithm to add, one of "wep40", "wep104", + * "tkip", or "ccmp" + * + * Adds an encryption algorithm to the list of allowed groupwise encryption + * algorithms. If the list is not empty, then only access points that support + * one or more of the encryption algorithms in the list will be considered + * compatible with this connection. + * + * Returns: %TRUE if the algorithm was added to the list, %FALSE if it was + * already in the list + **/ +gboolean +nm_setting_wireless_security_add_group (NMSettingWirelessSecurity *setting, const char *group) +{ + NMSettingWirelessSecurityPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE); + g_return_val_if_fail (group != NULL, FALSE); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + for (iter = priv->group; iter; iter = g_slist_next (iter)) { + if (strcasecmp (group, (char *) iter->data) == 0) + return FALSE; + } + + priv->group = g_slist_append (priv->group, g_ascii_strdown (group, -1)); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_GROUP); + return TRUE; +} + +/** + * nm_setting_wireless_security_remove_group: + * @setting: the #NMSettingWirelessSecurity + * @i: the index of an item in the allowed groupwise encryption algorithm list + * + * Removes an encryption algorithm from the allowed groupwise encryption + * algorithm list. + **/ +void +nm_setting_wireless_security_remove_group (NMSettingWirelessSecurity *setting, guint32 i) +{ + NMSettingWirelessSecurityPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting)); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + elt = g_slist_nth (priv->group, i); + g_return_if_fail (elt != NULL); + + g_free (elt->data); + priv->group = g_slist_delete_link (priv->group, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_GROUP); +} + +/** + * nm_setting_wireless_security_remove_group_by_value: + * @setting: the #NMSettingWirelessSecurity + * @group: the encryption algorithm to remove, one of "wep40", "wep104", + * "tkip", or "ccmp" + * + * Removes an encryption algorithm from the allowed groupwise encryption + * algorithm list. + * + * Returns: %TRUE if the algorithm was found and removed; %FALSE it it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_wireless_security_remove_group_by_value (NMSettingWirelessSecurity *setting, + const char *group) +{ + NMSettingWirelessSecurityPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE); + g_return_val_if_fail (group != NULL, FALSE); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + for (iter = priv->group; iter; iter = g_slist_next (iter)) { + if (strcasecmp (group, (char *) iter->data) == 0) { + priv->group = g_slist_delete_link (priv->group, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_GROUP); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_wireless_security_clear_groups: + * @setting: the #NMSettingWirelessSecurity + * + * Removes all algorithms from the allowed list. If there are no algorithms + * specified then all groupwise encryption algorithms are allowed. + **/ +void +nm_setting_wireless_security_clear_groups (NMSettingWirelessSecurity *setting) +{ + NMSettingWirelessSecurityPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting)); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + g_slist_free_full (priv->group, g_free); + priv->group = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_GROUP); +} + +/** + * nm_setting_wireless_security_get_psk: + * @setting: the #NMSettingWirelessSecurity + * + * Returns: the #NMSettingWirelessSecurity:psk property of the setting + **/ +const char * +nm_setting_wireless_security_get_psk (NMSettingWirelessSecurity *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL); + + return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->psk; +} + +/** + * nm_setting_wireless_security_get_psk_flags: + * @setting: the #NMSettingWirelessSecurity + * + * Returns: the #NMSettingSecretFlags pertaining to the + * #NMSettingWirelessSecurity:psk + **/ +NMSettingSecretFlags +nm_setting_wireless_security_get_psk_flags (NMSettingWirelessSecurity *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NM_SETTING_SECRET_FLAG_NONE); + + return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->psk_flags; +} + +/** + * nm_setting_wireless_security_get_leap_username: + * @setting: the #NMSettingWirelessSecurity + * + * Returns: the #NMSettingWirelessSecurity:leap-username property of the setting + **/ +const char * +nm_setting_wireless_security_get_leap_username (NMSettingWirelessSecurity *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL); + + return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->leap_username; +} + +/** + * nm_setting_wireless_security_get_leap_password: + * @setting: the #NMSettingWirelessSecurity + * + * Returns: the #NMSettingWirelessSecurity:leap-password property of the setting + **/ +const char * +nm_setting_wireless_security_get_leap_password (NMSettingWirelessSecurity *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL); + + return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->leap_password; +} + +/** + * nm_setting_wireless_security_get_leap_password_flags: + * @setting: the #NMSettingWirelessSecurity + * + * Returns: the #NMSettingSecretFlags pertaining to the + * #NMSettingWirelessSecurity:leap-password + **/ +NMSettingSecretFlags +nm_setting_wireless_security_get_leap_password_flags (NMSettingWirelessSecurity *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NM_SETTING_SECRET_FLAG_NONE); + + return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->leap_password_flags; +} + +/** + * nm_setting_wireless_security_get_wep_key: + * @setting: the #NMSettingWirelessSecurity + * @idx: the WEP key index (0..3 inclusive) + * + * Returns: the WEP key at the given index + **/ +const char * +nm_setting_wireless_security_get_wep_key (NMSettingWirelessSecurity *setting, guint32 idx) +{ + NMSettingWirelessSecurityPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL); + g_return_val_if_fail (idx < 4, NULL); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + if (idx == 0) + return priv->wep_key0; + else if (idx == 1) + return priv->wep_key1; + else if (idx == 2) + return priv->wep_key2; + else if (idx == 3) + return priv->wep_key3; + + g_assert_not_reached (); + return NULL; +} + +/** + * nm_setting_wireless_security_set_wep_key: + * @setting: the #NMSettingWirelessSecurity + * @idx: the index of the key (0..3 inclusive) + * @key: the WEP key as a string, in either hexadecimal, ASCII, or passphrase + * form as determiend by the value of the #NMSettingWirelessSecurity:wep-key-type + * property. + * + * Sets a WEP key in the given index. + **/ +void +nm_setting_wireless_security_set_wep_key (NMSettingWirelessSecurity *setting, guint32 idx, const char *key) +{ + NMSettingWirelessSecurityPrivate *priv; + + g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting)); + g_return_if_fail (idx < 4); + + priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + switch (idx) { + case 0: + g_free (priv->wep_key0); + priv->wep_key0 = g_strdup (key); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_WEP_KEY0); + break; + case 1: + g_free (priv->wep_key1); + priv->wep_key1 = g_strdup (key); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_WEP_KEY1); + break; + case 2: + g_free (priv->wep_key2); + priv->wep_key2 = g_strdup (key); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_WEP_KEY2); + break; + case 3: + g_free (priv->wep_key3); + priv->wep_key3 = g_strdup (key); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_WEP_KEY3); + break; + default: + g_assert_not_reached (); + } +} + +/** + * nm_setting_wireless_security_get_wep_tx_keyidx: + * @setting: the #NMSettingWirelessSecurity + * + * Returns: the #NMSettingWirelessSecurity:wep-tx-keyidx property of the setting + **/ +guint32 +nm_setting_wireless_security_get_wep_tx_keyidx (NMSettingWirelessSecurity *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0); + + return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->wep_tx_keyidx; +} + +/** + * nm_setting_wireless_security_get_auth_alg: + * @setting: the #NMSettingWirelessSecurity + * + * Returns: the #NMSettingWirelessSecurity:auth-alg property of the setting + **/ +const char * +nm_setting_wireless_security_get_auth_alg (NMSettingWirelessSecurity *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL); + + return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->auth_alg; +} + +/** + * nm_setting_wireless_security_get_wep_key_flags: + * @setting: the #NMSettingWirelessSecurity + * + * Returns: the #NMSettingSecretFlags pertaining to the all WEP keys + **/ +NMSettingSecretFlags +nm_setting_wireless_security_get_wep_key_flags (NMSettingWirelessSecurity *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NM_SETTING_SECRET_FLAG_NONE); + + return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->wep_key_flags; +} + +/** + * nm_setting_wireless_security_get_wep_key_type: + * @setting: the #NMSettingWirelessSecurity + * + * Returns: the #NMSettingWirelessSecurity:wep-key-type property of the setting + **/ +NMWepKeyType +nm_setting_wireless_security_get_wep_key_type (NMSettingWirelessSecurity *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0); + + return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->wep_key_type; +} + +static GPtrArray * +need_secrets (NMSetting *setting) +{ + NMSettingWirelessSecurity *self = NM_SETTING_WIRELESS_SECURITY (setting); + NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (self); + GPtrArray *secrets; + + secrets = g_ptr_array_sized_new (4); + + g_assert (priv->key_mgmt); + + /* Static WEP */ + if (strcmp (priv->key_mgmt, "none") == 0) { + if ((priv->wep_tx_keyidx == 0) && !nm_utils_wep_key_valid (priv->wep_key0, priv->wep_key_type)) { + g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0); + return secrets; + } + if ((priv->wep_tx_keyidx == 1) && !nm_utils_wep_key_valid (priv->wep_key1, priv->wep_key_type)) { + g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1); + return secrets; + } + if ((priv->wep_tx_keyidx == 2) && !nm_utils_wep_key_valid (priv->wep_key2, priv->wep_key_type)) { + g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2); + return secrets; + } + if ((priv->wep_tx_keyidx == 3) && !nm_utils_wep_key_valid (priv->wep_key3, priv->wep_key_type)) { + g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3); + return secrets; + } + goto no_secrets; + } + + /* WPA-PSK infrastructure and adhoc */ + if ( (strcmp (priv->key_mgmt, "wpa-none") == 0) + || (strcmp (priv->key_mgmt, "wpa-psk") == 0)) { + if (!nm_utils_wpa_psk_valid (priv->psk)) { + g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_PSK); + return secrets; + } + goto no_secrets; + } + + /* LEAP */ + if ( priv->auth_alg + && !strcmp (priv->auth_alg, "leap") + && !strcmp (priv->key_mgmt, "ieee8021x")) { + if (!priv->leap_password || !strlen (priv->leap_password)) { + g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD); + return secrets; + } + goto no_secrets; + } + + if ( (strcmp (priv->key_mgmt, "ieee8021x") == 0) + || (strcmp (priv->key_mgmt, "wpa-eap") == 0)) { + /* Let caller check the 802.1x setting for secrets */ + goto no_secrets; + } + + g_assert_not_reached (); + return secrets; + +no_secrets: + if (secrets) + g_ptr_array_free (secrets, TRUE); + return NULL; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingWirelessSecurity *self = NM_SETTING_WIRELESS_SECURITY (setting); + NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (self); + const char *valid_key_mgmt[] = { "none", "ieee8021x", "wpa-none", "wpa-psk", "wpa-eap", NULL }; + const char *valid_auth_algs[] = { "open", "shared", "leap", NULL }; + const char *valid_protos[] = { "wpa", "rsn", NULL }; + const char *valid_pairwise[] = { "tkip", "ccmp", NULL }; + const char *valid_groups[] = { "wep40", "wep104", "tkip", "ccmp", NULL }; + + if (!priv->key_mgmt) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + return FALSE; + } + + if (!_nm_utils_string_in_list (priv->key_mgmt, valid_key_mgmt)) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->key_mgmt); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + return FALSE; + } + + if (priv->auth_alg && !strcmp (priv->auth_alg, "leap")) { + /* LEAP must use ieee8021x key management */ + if (strcmp (priv->key_mgmt, "ieee8021x")) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_802_1X, + _("'%s' security requires '%s=%s'"), + "leap", NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x"); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG); + return FALSE; + } + if (!priv->leap_username) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME); + return FALSE; + } + if (priv->leap_password && !strlen (priv->leap_password)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD); + return FALSE; + } + } else { + if ( (strcmp (priv->key_mgmt, "ieee8021x") == 0) + || (strcmp (priv->key_mgmt, "wpa-eap") == 0)) { + /* Need an 802.1x setting too */ + if (!nm_setting_find_in_list (all_settings, NM_SETTING_802_1X_SETTING_NAME)) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_802_1X_SETTING, + _("'%s' security requires '%s' setting presence"), + priv->key_mgmt, NM_SETTING_802_1X_SETTING_NAME); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + return FALSE; + } + } + } + + if (priv->leap_username && !strlen (priv->leap_username)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME); + return FALSE; + } + + if (priv->wep_tx_keyidx > 3) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("'%d' value is out of range <0-3>"), + priv->wep_tx_keyidx); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX); + return FALSE; + } + + if (priv->wep_key_type > NM_WEP_KEY_TYPE_LAST) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE); + return FALSE; + } + + if (priv->wep_key0 && !nm_utils_wep_key_valid (priv->wep_key0, priv->wep_key_type)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0); + return FALSE; + } + if (priv->wep_key1 && !nm_utils_wep_key_valid (priv->wep_key1, priv->wep_key_type)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1); + return FALSE; + } + if (priv->wep_key2 && !nm_utils_wep_key_valid (priv->wep_key2, priv->wep_key_type)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2); + return FALSE; + } + if (priv->wep_key3 && !nm_utils_wep_key_valid (priv->wep_key3, priv->wep_key_type)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3); + return FALSE; + } + + if (priv->auth_alg && !_nm_utils_string_in_list (priv->auth_alg, valid_auth_algs)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG); + return FALSE; + } + + if (priv->psk && !nm_utils_wpa_psk_valid (priv->psk)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_PSK); + return FALSE; + } + + if (priv->proto && !_nm_utils_string_slist_validate (priv->proto, valid_protos)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_PROTO); + return FALSE; + } + + if (priv->pairwise) { + const char *wpa_none[] = { "wpa-none", NULL }; + + /* For ad-hoc connections, pairwise must be "none" */ + if (_nm_utils_string_in_list (priv->key_mgmt, wpa_none)) { + GSList *iter; + gboolean found = FALSE; + + for (iter = priv->pairwise; iter; iter = g_slist_next (iter)) { + if (!strcmp ((char *) iter->data, "none")) { + found = TRUE; + break; + } + } + + /* pairwise cipher list didn't contain "none", which is invalid + * for WPA adhoc connections. + */ + if (!found) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("'%s' connections require '%s' in this property"), + NM_SETTING_WIRELESS_MODE_ADHOC, "none"); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_PAIRWISE); + return FALSE; + } + } else if (!_nm_utils_string_slist_validate (priv->pairwise, valid_pairwise)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_PAIRWISE); + return FALSE; + } + } + + if (priv->group && !_nm_utils_string_slist_validate (priv->group, valid_groups)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_GROUP); + return FALSE; + } + + /* Shared Key auth can only be used with WEP */ + if (priv->auth_alg && !strcmp (priv->auth_alg, "shared")) { + if (priv->key_mgmt && strcmp (priv->key_mgmt, "none")) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_SHARED_KEY_REQUIRES_WEP, + _("'%s' can only be used with '%s=%s' (WEP)"), + "shared", NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none"); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +get_secret_flags (NMSetting *setting, + const char *secret_name, + gboolean verify_secret, + NMSettingSecretFlags *out_flags, + GError **error) +{ + NMSettingClass *setting_class; + gboolean verify_override = verify_secret; + + /* There's only one 'flags' property for WEP keys, so alias all the WEP key + * property names to that flags property. + */ + if ( !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0) + || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1) + || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2) + || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3)) { + secret_name = "wep-key"; + verify_override = FALSE; /* Already know it's a secret */ + } + + /* Chain up to superclass with modified key name */ + setting_class = NM_SETTING_CLASS (nm_setting_wireless_security_parent_class); + return setting_class->get_secret_flags (setting, secret_name, verify_override, out_flags, error); +} + +static gboolean +set_secret_flags (NMSetting *setting, + const char *secret_name, + gboolean verify_secret, + NMSettingSecretFlags flags, + GError **error) +{ + NMSettingClass *setting_class; + gboolean verify_override = verify_secret; + + /* There's only one 'flags' property for WEP keys, so alias all the WEP key + * property names to that flags property. + */ + if ( !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0) + || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1) + || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2) + || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3)) { + secret_name = "wep-key"; + verify_override = FALSE; /* Already know it's a secret */ + } + + /* Chain up to superclass with modified key name */ + setting_class = NM_SETTING_CLASS (nm_setting_wireless_security_parent_class); + return setting_class->set_secret_flags (setting, secret_name, verify_override, flags, error); +} + +static void +nm_setting_wireless_security_init (NMSettingWirelessSecurity *setting) +{ +} + +static void +finalize (GObject *object) +{ + NMSettingWirelessSecurity *self = NM_SETTING_WIRELESS_SECURITY (object); + NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (self); + + /* Strings first. g_free() already checks for NULLs so we don't have to */ + + g_free (priv->key_mgmt); + g_free (priv->auth_alg); + g_free (priv->leap_username); + g_free (priv->wep_key0); + g_free (priv->wep_key1); + g_free (priv->wep_key2); + g_free (priv->wep_key3); + g_free (priv->psk); + g_free (priv->leap_password); + + g_slist_free_full (priv->proto, g_free); + g_slist_free_full (priv->pairwise, g_free); + g_slist_free_full (priv->group, g_free); + + G_OBJECT_CLASS (nm_setting_wireless_security_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingWirelessSecurity *setting = NM_SETTING_WIRELESS_SECURITY (object); + NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + const char *str; + + switch (prop_id) { + case PROP_KEY_MGMT: + g_free (priv->key_mgmt); + str = g_value_get_string (value); + priv->key_mgmt = str ? g_ascii_strdown (str, -1) : NULL; + break; + case PROP_WEP_TX_KEYIDX: + priv->wep_tx_keyidx = g_value_get_uint (value); + break; + case PROP_AUTH_ALG: + g_free (priv->auth_alg); + str = g_value_get_string (value); + priv->auth_alg = str ? g_ascii_strdown (str, -1) : NULL; + break; + case PROP_PROTO: + g_slist_free_full (priv->proto, g_free); + priv->proto = g_value_dup_boxed (value); + break; + case PROP_PAIRWISE: + g_slist_free_full (priv->pairwise, g_free); + priv->pairwise = g_value_dup_boxed (value); + break; + case PROP_GROUP: + g_slist_free_full (priv->group, g_free); + priv->group = g_value_dup_boxed (value); + break; + case PROP_LEAP_USERNAME: + g_free (priv->leap_username); + priv->leap_username = g_value_dup_string (value); + break; + case PROP_WEP_KEY0: + g_free (priv->wep_key0); + priv->wep_key0 = g_value_dup_string (value); + break; + case PROP_WEP_KEY1: + g_free (priv->wep_key1); + priv->wep_key1 = g_value_dup_string (value); + break; + case PROP_WEP_KEY2: + g_free (priv->wep_key2); + priv->wep_key2 = g_value_dup_string (value); + break; + case PROP_WEP_KEY3: + g_free (priv->wep_key3); + priv->wep_key3 = g_value_dup_string (value); + break; + case PROP_WEP_KEY_FLAGS: + priv->wep_key_flags = g_value_get_uint (value); + break; + case PROP_PSK: + g_free (priv->psk); + priv->psk = g_value_dup_string (value); + break; + case PROP_PSK_FLAGS: + priv->psk_flags = g_value_get_uint (value); + break; + case PROP_LEAP_PASSWORD: + g_free (priv->leap_password); + priv->leap_password = g_value_dup_string (value); + break; + case PROP_LEAP_PASSWORD_FLAGS: + priv->leap_password_flags = g_value_get_uint (value); + break; + case PROP_WEP_KEY_TYPE: + priv->wep_key_type = g_value_get_uint (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) +{ + NMSettingWirelessSecurity *setting = NM_SETTING_WIRELESS_SECURITY (object); + NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting); + + switch (prop_id) { + case PROP_KEY_MGMT: + g_value_set_string (value, priv->key_mgmt); + break; + case PROP_WEP_TX_KEYIDX: + g_value_set_uint (value, priv->wep_tx_keyidx); + break; + case PROP_AUTH_ALG: + g_value_set_string (value, priv->auth_alg); + break; + case PROP_PROTO: + g_value_set_boxed (value, priv->proto); + break; + case PROP_PAIRWISE: + g_value_set_boxed (value, priv->pairwise); + break; + case PROP_GROUP: + g_value_set_boxed (value, priv->group); + break; + case PROP_LEAP_USERNAME: + g_value_set_string (value, priv->leap_username); + break; + case PROP_WEP_KEY0: + g_value_set_string (value, priv->wep_key0); + break; + case PROP_WEP_KEY1: + g_value_set_string (value, priv->wep_key1); + break; + case PROP_WEP_KEY2: + g_value_set_string (value, priv->wep_key2); + break; + case PROP_WEP_KEY3: + g_value_set_string (value, priv->wep_key3); + break; + case PROP_WEP_KEY_FLAGS: + g_value_set_uint (value, priv->wep_key_flags); + break; + case PROP_PSK: + g_value_set_string (value, priv->psk); + break; + case PROP_PSK_FLAGS: + g_value_set_uint (value, priv->psk_flags); + break; + case PROP_LEAP_PASSWORD: + g_value_set_string (value, priv->leap_password); + break; + case PROP_LEAP_PASSWORD_FLAGS: + g_value_set_uint (value, priv->leap_password_flags); + break; + case PROP_WEP_KEY_TYPE: + g_value_set_uint (value, priv->wep_key_type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingWirelessSecurityPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + parent_class->verify = verify; + parent_class->need_secrets = need_secrets; + parent_class->get_secret_flags = get_secret_flags; + parent_class->set_secret_flags = set_secret_flags; + + /* Properties */ + /** + * NMSettingWirelessSecurity:key-mgmt: + * + * Key management used for the connection. One of "none" (WEP), "ieee8021x" + * (Dynamic WEP), "wpa-none" (Ad-Hoc WPA-PSK), "wpa-psk" (infrastructure + * WPA-PSK), or "wpa-eap" (WPA-Enterprise). This property must be set for + * any Wi-Fi connection that uses security. + **/ + g_object_class_install_property + (object_class, PROP_KEY_MGMT, + g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_REQUIRED | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:wep-tx-keyidx: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_WEP_TX_KEYIDX, + g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, "", "", + 0, 3, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:auth-alg: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_AUTH_ALG, + g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:proto: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_PROTO, + _nm_param_spec_specialized (NM_SETTING_WIRELESS_SECURITY_PROTO, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:pairwise: + * + * 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". + **/ + g_object_class_install_property + (object_class, PROP_PAIRWISE, + _nm_param_spec_specialized (NM_SETTING_WIRELESS_SECURITY_PAIRWISE, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:group: + * + * 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". + **/ + g_object_class_install_property + (object_class, PROP_GROUP, + _nm_param_spec_specialized (NM_SETTING_WIRELESS_SECURITY_GROUP, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:leap-username: + * + * The login username for legacy LEAP connections (ie, key-mgmt = + * "ieee8021x" and auth-alg = "leap"). + **/ + g_object_class_install_property + (object_class, PROP_LEAP_USERNAME, + g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:wep-key0: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_WEP_KEY0, + g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:wep-key1: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_WEP_KEY1, + g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:wep-key2: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_WEP_KEY2, + g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:wep-key3: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_WEP_KEY3, + g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:wep-key-flags: + * + * Flags indicating how to handle the #NMSettingWirelessSecurity:wep-key0, + * #NMSettingWirelessSecurity:wep-key1, #NMSettingWirelessSecurity:wep-key2, + * and #NMSettingWirelessSecurity:wep-key3 properties. + **/ + g_object_class_install_property + (object_class, PROP_WEP_KEY_FLAGS, + g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, "", "", + NM_SETTING_SECRET_FLAG_NONE, + NM_SETTING_SECRET_FLAGS_ALL, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:psk: + * + * Pre-Shared-Key for WPA networks. If the key is 64-characters long, it + * must contain only hexadecimal characters and is interpreted as a + * hexadecimal WPA key. Otherwise, the key must be between 8 and 63 ASCII + * characters (as specified in the 802.11i standard) and is interpreted as a + * WPA passphrase, and is hashed to derive the actual WPA-PSK used when + * connecting to the Wi-Fi network. + **/ + g_object_class_install_property + (object_class, PROP_PSK, + g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_PSK, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:psk-flags: + * + * Flags indicating how to handle the #NMSettingWirelessSecurity:psk + * property. + **/ + g_object_class_install_property + (object_class, PROP_PSK_FLAGS, + g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS, "", "", + NM_SETTING_SECRET_FLAG_NONE, + NM_SETTING_SECRET_FLAGS_ALL, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:leap-password: + * + * The login password for legacy LEAP connections (ie, key-mgmt = + * "ieee8021x" and auth-alg = "leap"). + **/ + g_object_class_install_property + (object_class, PROP_LEAP_PASSWORD, + g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:leap-password-flags: + * + * Flags indicating how to handle the + * #NMSettingWirelessSecurity:leap-password property. + **/ + g_object_class_install_property + (object_class, PROP_LEAP_PASSWORD_FLAGS, + g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, "", "", + NM_SETTING_SECRET_FLAG_NONE, + NM_SETTING_SECRET_FLAGS_ALL, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWirelessSecurity:wep-key-type: + * + * Controls the interpretation of WEP keys. Allowed values are + * %NM_WEP_KEY_TYPE_KEY, 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, 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. + **/ + g_object_class_install_property + (object_class, PROP_WEP_KEY_TYPE, + g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, "", "", + NM_WEP_KEY_TYPE_UNKNOWN, + NM_WEP_KEY_TYPE_LAST, + NM_WEP_KEY_TYPE_UNKNOWN, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-wireless-security.h b/libnm-core/nm-setting-wireless-security.h new file mode 100644 index 0000000000..97a456b048 --- /dev/null +++ b/libnm-core/nm-setting-wireless-security.h @@ -0,0 +1,178 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SETTING_WIRELESS_SECURITY_H +#define NM_SETTING_WIRELESS_SECURITY_H + +#include <nm-setting.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_get_type ()) +#define NM_SETTING_WIRELESS_SECURITY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurity)) +#define NM_SETTING_WIRELESS_SECURITY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurityClass)) +#define NM_IS_SETTING_WIRELESS_SECURITY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WIRELESS_SECURITY)) +#define NM_IS_SETTING_WIRELESS_SECURITY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_WIRELESS_SECURITY)) +#define NM_SETTING_WIRELESS_SECURITY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurityClass)) + +#define NM_SETTING_WIRELESS_SECURITY_SETTING_NAME "802-11-wireless-security" + +/** + * NMSettingWirelessSecurityError: + * @NM_SETTING_WIRELESS_SECURITY_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_PROPERTY: the property was + * missing and is required + * @NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_802_1X_SETTING: a property contained + * a value that requires the connection to contain a #NMSetting8021x setting + * @NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_802_1X: LEAP authentication + * was specified but key management was not set to "8021x" + * @NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME: LEAP authentication + * was specified but no LEAP username was given + * @NM_SETTING_WIRELESS_SECURITY_ERROR_SHARED_KEY_REQUIRES_WEP: Shared Key + * authentication was specified but the setting did not specify WEP as the + * encryption protocol + */ +typedef enum { + NM_SETTING_WIRELESS_SECURITY_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ + NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_802_1X_SETTING, /*< nick=Missing8021xSetting >*/ + NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_802_1X, /*< nick=LEAPRequires8021x >*/ + NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME, /*< nick=LEAPRequiresUsername >*/ + NM_SETTING_WIRELESS_SECURITY_ERROR_SHARED_KEY_REQUIRES_WEP /*< nick=SharedKeyRequiresWEP >*/ +} NMSettingWirelessSecurityError; + +#define NM_SETTING_WIRELESS_SECURITY_ERROR nm_setting_wireless_security_error_quark () +GQuark nm_setting_wireless_security_error_quark (void); + +/** + * NMWepKeyType: + * @NM_WEP_KEY_TYPE_UNKNOWN: unknown WEP key type + * @NM_WEP_KEY_TYPE_KEY: indicates a hexadecimal or ASCII formatted WEP key. + * Hex keys are either 10 or 26 hexadecimal characters (ie "5f782f2f5f" or + * "732f2d712e4a394a375d366931"), while ASCII keys are either 5 or 13 ASCII + * characters (ie "abcde" or "blahblah99$*1"). + * @NM_WEP_KEY_TYPE_PASSPHRASE: indicates a WEP passphrase (ex "I bought a duck + * on my way back from the market 235Q&^%^*%") instead of a hexadecimal or ASCII + * key. Passphrases are between 8 and 64 characters inclusive and are hashed + * the actual WEP key using the MD5 hash algorithm. + * @NM_WEP_KEY_TYPE_LAST: placeholder value for bounds-checking + * + * The #NMWepKeyType values specify how any WEP keys present in the setting + * are intepreted. There are no standards governing how to hash the various WEP + * key/passphrase formats into the actual WEP key. Unfortunately some WEP keys + * can be interpreted in multiple ways, requring the setting to specify how to + * interpret the any WEP keys. For example, the key "732f2d712e4a394a375d366931" + * is both a valid Hexadecimal WEP key and a WEP passphrase. Further, many + * ASCII keys are also valid WEP passphrases, but since passphrases and ASCII + * keys are hashed differently to determine the actual WEP key the type must be + * specified. + */ +typedef enum { + NM_WEP_KEY_TYPE_UNKNOWN = 0, + NM_WEP_KEY_TYPE_KEY = 1, /* Hex or ASCII */ + NM_WEP_KEY_TYPE_PASSPHRASE = 2, /* 104/128-bit Passphrase */ + + NM_WEP_KEY_TYPE_LAST = NM_WEP_KEY_TYPE_PASSPHRASE +} NMWepKeyType; + +#define NM_SETTING_WIRELESS_SECURITY_KEY_MGMT "key-mgmt" +#define NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX "wep-tx-keyidx" +#define NM_SETTING_WIRELESS_SECURITY_AUTH_ALG "auth-alg" +#define NM_SETTING_WIRELESS_SECURITY_PROTO "proto" +#define NM_SETTING_WIRELESS_SECURITY_PAIRWISE "pairwise" +#define NM_SETTING_WIRELESS_SECURITY_GROUP "group" +#define NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME "leap-username" +#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY0 "wep-key0" +#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY1 "wep-key1" +#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY2 "wep-key2" +#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY3 "wep-key3" +#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS "wep-key-flags" +#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE "wep-key-type" +#define NM_SETTING_WIRELESS_SECURITY_PSK "psk" +#define NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS "psk-flags" +#define NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD "leap-password" +#define NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS "leap-password-flags" + +typedef struct { + NMSetting parent; +} NMSettingWirelessSecurity; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingWirelessSecurityClass; + +GType nm_setting_wireless_security_get_type (void); + +NMSetting * nm_setting_wireless_security_new (void); + +const char *nm_setting_wireless_security_get_key_mgmt (NMSettingWirelessSecurity *setting); + +guint32 nm_setting_wireless_security_get_num_protos (NMSettingWirelessSecurity *setting); +const char *nm_setting_wireless_security_get_proto (NMSettingWirelessSecurity *setting, guint32 i); +gboolean nm_setting_wireless_security_add_proto (NMSettingWirelessSecurity *setting, const char *proto); +void nm_setting_wireless_security_remove_proto (NMSettingWirelessSecurity *setting, guint32 i); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_wireless_security_remove_proto_by_value (NMSettingWirelessSecurity *setting, const char *proto); +void nm_setting_wireless_security_clear_protos (NMSettingWirelessSecurity *setting); + +guint32 nm_setting_wireless_security_get_num_pairwise (NMSettingWirelessSecurity *setting); +const char *nm_setting_wireless_security_get_pairwise (NMSettingWirelessSecurity *setting, guint32 i); +gboolean nm_setting_wireless_security_add_pairwise (NMSettingWirelessSecurity *setting, const char *pairwise); +void nm_setting_wireless_security_remove_pairwise (NMSettingWirelessSecurity *setting, guint32 i); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_wireless_security_remove_pairwise_by_value (NMSettingWirelessSecurity *setting, const char *pairwise); +void nm_setting_wireless_security_clear_pairwise (NMSettingWirelessSecurity *setting); + +guint32 nm_setting_wireless_security_get_num_groups (NMSettingWirelessSecurity *setting); +const char *nm_setting_wireless_security_get_group (NMSettingWirelessSecurity *setting, guint32 i); +gboolean nm_setting_wireless_security_add_group (NMSettingWirelessSecurity *setting, const char *group); +void nm_setting_wireless_security_remove_group (NMSettingWirelessSecurity *setting, guint32 i); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_wireless_security_remove_group_by_value (NMSettingWirelessSecurity *setting, const char *group); +void nm_setting_wireless_security_clear_groups (NMSettingWirelessSecurity *setting); + +const char *nm_setting_wireless_security_get_psk (NMSettingWirelessSecurity *setting); +NMSettingSecretFlags nm_setting_wireless_security_get_psk_flags (NMSettingWirelessSecurity *setting); + +const char *nm_setting_wireless_security_get_leap_username (NMSettingWirelessSecurity *setting); +const char *nm_setting_wireless_security_get_leap_password (NMSettingWirelessSecurity *setting); +NMSettingSecretFlags nm_setting_wireless_security_get_leap_password_flags (NMSettingWirelessSecurity *setting); + +const char *nm_setting_wireless_security_get_wep_key (NMSettingWirelessSecurity *setting, guint32 idx); +void nm_setting_wireless_security_set_wep_key (NMSettingWirelessSecurity *setting, guint32 idx, const char *key); +guint32 nm_setting_wireless_security_get_wep_tx_keyidx (NMSettingWirelessSecurity *setting); +const char *nm_setting_wireless_security_get_auth_alg (NMSettingWirelessSecurity *setting); + +NMSettingSecretFlags nm_setting_wireless_security_get_wep_key_flags (NMSettingWirelessSecurity *setting); +NMWepKeyType nm_setting_wireless_security_get_wep_key_type (NMSettingWirelessSecurity *setting); + +G_END_DECLS + +#endif /* NM_SETTING_WIRELESS_SECURITY_H */ diff --git a/libnm-core/nm-setting-wireless.c b/libnm-core/nm-setting-wireless.c new file mode 100644 index 0000000000..24fae397b3 --- /dev/null +++ b/libnm-core/nm-setting-wireless.c @@ -0,0 +1,1245 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include <string.h> +#include <net/ethernet.h> +#include <netinet/ether.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n.h> + +#include "NetworkManager.h" +#include "nm-setting-wireless.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-dbus-glib-types.h" +#include "nm-utils-private.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-wireless + * @short_description: Describes connection properties for 802.11 Wi-Fi networks + * @include: nm-setting-wireless.h + * + * The #NMSettingWireless object is a #NMSetting subclass that describes properties + * necessary for connection to 802.11 Wi-Fi networks. + **/ + +/** + * nm_setting_wireless_error_quark: + * + * Registers an error quark for #NMSettingWireless if necessary. + * + * Returns: the error quark used for #NMSettingWireless errors. + **/ +GQuark +nm_setting_wireless_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-wireless-error-quark"); + return quark; +} + + +G_DEFINE_TYPE_WITH_CODE (NMSettingWireless, nm_setting_wireless, NM_TYPE_SETTING, + _nm_register_setting (NM_SETTING_WIRELESS_SETTING_NAME, + g_define_type_id, + 1, + NM_SETTING_WIRELESS_ERROR)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIRELESS) + +#define NM_SETTING_WIRELESS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRELESS, NMSettingWirelessPrivate)) + +typedef struct { + GByteArray *ssid; + char *mode; + char *band; + guint32 channel; + GByteArray *bssid; + guint32 rate; + guint32 tx_power; + GByteArray *device_mac_address; + GByteArray *cloned_mac_address; + GSList *mac_address_blacklist; + guint32 mtu; + GSList *seen_bssids; + char *security; + gboolean hidden; +} NMSettingWirelessPrivate; + +enum { + PROP_0, + PROP_SSID, + PROP_MODE, + PROP_BAND, + PROP_CHANNEL, + PROP_BSSID, + PROP_RATE, + PROP_TX_POWER, + PROP_MAC_ADDRESS, + PROP_CLONED_MAC_ADDRESS, + PROP_MAC_ADDRESS_BLACKLIST, + PROP_MTU, + PROP_SEEN_BSSIDS, + PROP_SEC, + PROP_HIDDEN, + + LAST_PROP +}; + +static gboolean +match_cipher (const char *cipher, + const char *expected, + guint32 wpa_flags, + guint32 rsn_flags, + guint32 flag) +{ + if (strcmp (cipher, expected) != 0) + return FALSE; + + if (!(wpa_flags & flag) && !(rsn_flags & flag)) + return FALSE; + + return TRUE; +} + +/** + * nm_setting_wireless_ap_security_compatible: + * @s_wireless: a #NMSettingWireless + * @s_wireless_sec: a #NMSettingWirelessSecurity or %NULL + * @ap_flags: the %NM80211ApFlags of the given access point + * @ap_wpa: the %NM80211ApSecurityFlags of the given access point's WPA + * capabilities + * @ap_rsn: the %NM80211ApSecurityFlags of the given access point's WPA2/RSN + * capabilities + * @ap_mode: the 802.11 mode of the AP, either Ad-Hoc or Infrastructure + * + * Given a #NMSettingWireless and an optional #NMSettingWirelessSecurity, + * determine if the configuration given by the settings is compatible with + * the security of an access point using that access point's capability flags + * and mode. Useful for clients that wish to filter a set of connections + * against a set of access points and determine which connections are + * compatible with which access points. + * + * Returns: %TRUE if the given settings are compatible with the access point's + * security flags and mode, %FALSE if they are not. + */ +gboolean +nm_setting_wireless_ap_security_compatible (NMSettingWireless *s_wireless, + NMSettingWirelessSecurity *s_wireless_sec, + NM80211ApFlags ap_flags, + NM80211ApSecurityFlags ap_wpa, + NM80211ApSecurityFlags ap_rsn, + NM80211Mode ap_mode) +{ + const char *key_mgmt = NULL, *cipher; + guint32 num, i; + gboolean found = FALSE; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (s_wireless), FALSE); + + if (!s_wireless_sec) { + if ( (ap_flags & NM_802_11_AP_FLAGS_PRIVACY) + || (ap_wpa != NM_802_11_AP_SEC_NONE) + || (ap_rsn != NM_802_11_AP_SEC_NONE)) + return FALSE; + return TRUE; + } + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec); + if (!key_mgmt) + return FALSE; + + /* Static WEP */ + if (!strcmp (key_mgmt, "none")) { + if ( !(ap_flags & NM_802_11_AP_FLAGS_PRIVACY) + || (ap_wpa != NM_802_11_AP_SEC_NONE) + || (ap_rsn != NM_802_11_AP_SEC_NONE)) + return FALSE; + return TRUE; + } + + /* Adhoc WPA */ + if (!strcmp (key_mgmt, "wpa-none")) { + if (ap_mode != NM_802_11_MODE_ADHOC) + return FALSE; + /* FIXME: validate ciphers if they're in the beacon */ + return TRUE; + } + + /* Adhoc WPA2 (ie, RSN IBSS) */ + if (ap_mode == NM_802_11_MODE_ADHOC) { + if (strcmp (key_mgmt, "wpa-psk")) + return FALSE; + + /* Ensure the AP has RSN PSK capability */ + if (!(ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_PSK)) + return FALSE; + + /* Fall through and check ciphers in generic WPA-PSK code */ + } + + /* Dynamic WEP or LEAP */ + if (!strcmp (key_mgmt, "ieee8021x")) { + if (!(ap_flags & NM_802_11_AP_FLAGS_PRIVACY)) + return FALSE; + + /* If the AP is advertising a WPA IE, make sure it supports WEP ciphers */ + if (ap_wpa != NM_802_11_AP_SEC_NONE) { + if (!(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) + return FALSE; + + /* quick check; can't use AP if it doesn't support at least one + * WEP cipher in both pairwise and group suites. + */ + if ( !(ap_wpa & (NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104)) + || !(ap_wpa & (NM_802_11_AP_SEC_GROUP_WEP40 | NM_802_11_AP_SEC_GROUP_WEP104))) + return FALSE; + + /* Match at least one pairwise cipher with AP's capability if the + * wireless-security setting explicitly lists pairwise ciphers + */ + num = nm_setting_wireless_security_get_num_pairwise (s_wireless_sec); + for (i = 0, found = FALSE; i < num; i++) { + cipher = nm_setting_wireless_security_get_pairwise (s_wireless_sec, i); + if ((found = match_cipher (cipher, "wep40", ap_wpa, ap_wpa, NM_802_11_AP_SEC_PAIR_WEP40))) + break; + if ((found = match_cipher (cipher, "wep104", ap_wpa, ap_wpa, NM_802_11_AP_SEC_PAIR_WEP104))) + break; + } + if (!found && num) + return FALSE; + + /* Match at least one group cipher with AP's capability if the + * wireless-security setting explicitly lists group ciphers + */ + num = nm_setting_wireless_security_get_num_groups (s_wireless_sec); + for (i = 0, found = FALSE; i < num; i++) { + cipher = nm_setting_wireless_security_get_group (s_wireless_sec, i); + if ((found = match_cipher (cipher, "wep40", ap_wpa, ap_wpa, NM_802_11_AP_SEC_GROUP_WEP40))) + break; + if ((found = match_cipher (cipher, "wep104", ap_wpa, ap_wpa, NM_802_11_AP_SEC_GROUP_WEP104))) + break; + } + if (!found && num) + return FALSE; + } + return TRUE; + } + + /* WPA[2]-PSK and WPA[2] Enterprise */ + if ( !strcmp (key_mgmt, "wpa-psk") + || !strcmp (key_mgmt, "wpa-eap")) { + + if (!strcmp (key_mgmt, "wpa-psk")) { + if ( !(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_PSK) + && !(ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_PSK)) + return FALSE; + } else if (!strcmp (key_mgmt, "wpa-eap")) { + if ( !(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + && !(ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) + return FALSE; + } + + // FIXME: should handle WPA and RSN separately here to ensure that + // if the Connection only uses WPA we don't match a cipher against + // the AP's RSN IE instead + + /* Match at least one pairwise cipher with AP's capability if the + * wireless-security setting explicitly lists pairwise ciphers + */ + num = nm_setting_wireless_security_get_num_pairwise (s_wireless_sec); + for (i = 0, found = FALSE; i < num; i++) { + cipher = nm_setting_wireless_security_get_pairwise (s_wireless_sec, i); + if ((found = match_cipher (cipher, "tkip", ap_wpa, ap_rsn, NM_802_11_AP_SEC_PAIR_TKIP))) + break; + if ((found = match_cipher (cipher, "ccmp", ap_wpa, ap_rsn, NM_802_11_AP_SEC_PAIR_CCMP))) + break; + } + if (!found && num) + return FALSE; + + /* Match at least one group cipher with AP's capability if the + * wireless-security setting explicitly lists group ciphers + */ + num = nm_setting_wireless_security_get_num_groups (s_wireless_sec); + for (i = 0, found = FALSE; i < num; i++) { + cipher = nm_setting_wireless_security_get_group (s_wireless_sec, i); + + if ((found = match_cipher (cipher, "wep40", ap_wpa, ap_rsn, NM_802_11_AP_SEC_GROUP_WEP40))) + break; + if ((found = match_cipher (cipher, "wep104", ap_wpa, ap_rsn, NM_802_11_AP_SEC_GROUP_WEP104))) + break; + if ((found = match_cipher (cipher, "tkip", ap_wpa, ap_rsn, NM_802_11_AP_SEC_GROUP_TKIP))) + break; + if ((found = match_cipher (cipher, "ccmp", ap_wpa, ap_rsn, NM_802_11_AP_SEC_GROUP_CCMP))) + break; + } + if (!found && num) + return FALSE; + + return TRUE; + } + + return FALSE; +} + +/** + * nm_setting_wireless_new: + * + * Creates a new #NMSettingWireless object with default values. + * + * Returns: (transfer full): the new empty #NMSettingWireless object + **/ +NMSetting * +nm_setting_wireless_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRELESS, NULL); +} + +/** + * nm_setting_wireless_get_ssid: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:ssid property of the setting + **/ +const GByteArray * +nm_setting_wireless_get_ssid (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->ssid; +} + +/** + * nm_setting_wireless_get_mode: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:mode property of the setting + **/ +const char * +nm_setting_wireless_get_mode (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mode; +} + +/** + * nm_setting_wireless_get_band: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:band property of the setting + **/ +const char * +nm_setting_wireless_get_band (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->band; +} + +/** + * nm_setting_wireless_get_channel: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:channel property of the setting + **/ +guint32 +nm_setting_wireless_get_channel (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->channel; +} + +/** + * nm_setting_wireless_get_bssid: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:bssid property of the setting + **/ +const GByteArray * +nm_setting_wireless_get_bssid (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->bssid; +} + +/** + * nm_setting_wireless_get_rate: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:rate property of the setting + **/ +guint32 +nm_setting_wireless_get_rate (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->rate; +} + +/** + * nm_setting_wireless_get_tx_power: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:tx-power property of the setting + **/ +guint32 +nm_setting_wireless_get_tx_power (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->tx_power; +} + +/** + * nm_setting_wireless_get_mac_address: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:mac-address property of the setting + **/ +const GByteArray * +nm_setting_wireless_get_mac_address (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->device_mac_address; +} + +/** + * nm_setting_wireless_get_cloned_mac_address: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:cloned-mac-address property of the setting + **/ +const GByteArray * +nm_setting_wireless_get_cloned_mac_address (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->cloned_mac_address; +} + +/** + * nm_setting_wireless_get_mac_address_blacklist: + * @setting: the #NMSettingWireless + * + * Returns: (element-type GLib.ByteArray): the + * #NMSettingWireless:mac-address-blacklist property of the setting + **/ +const GSList * +nm_setting_wireless_get_mac_address_blacklist (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mac_address_blacklist; +} + +/** + * nm_setting_wireless_get_num_mac_blacklist_items: + * @setting: the #NMSettingWireless + * + * Returns: the number of blacklisted MAC addresses + * + * Since: 0.9.10 + **/ +guint32 +nm_setting_wireless_get_num_mac_blacklist_items (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0); + + return g_slist_length (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mac_address_blacklist); +} + +/** + * nm_setting_wireless_get_mac_blacklist_item: + * @setting: the #NMSettingWireless + * @idx: the zero-based index of the MAC address entry + * + * Returns: the blacklisted MAC address string (hex-digits-and-colons notation) + * at index @idx + * + * Since: 0.9.10 + **/ +const char * +nm_setting_wireless_get_mac_blacklist_item (NMSettingWireless *setting, guint32 idx) +{ + NMSettingWirelessPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL); + + priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting); + g_return_val_if_fail (idx <= g_slist_length (priv->mac_address_blacklist), NULL); + + return (const char *) g_slist_nth_data (priv->mac_address_blacklist, idx); +} + +/** + * nm_setting_wireless_add_mac_blacklist_item: + * @setting: the #NMSettingWireless + * @mac: the MAC address string (hex-digits-and-colons notation) to blacklist + * + * Adds a new MAC address to the #NMSettingWireless:mac-address-blacklist property. + * + * Returns: %TRUE if the MAC address was added; %FALSE if the MAC address + * is invalid or was already present + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_wireless_add_mac_blacklist_item (NMSettingWireless *setting, const char *mac) +{ + NMSettingWirelessPrivate *priv; + GSList *iter; + guint8 buf[32]; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), FALSE); + g_return_val_if_fail (mac != NULL, FALSE); + + if (!nm_utils_hwaddr_aton (mac, ARPHRD_ETHER, buf)) + return FALSE; + + priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting); + for (iter = priv->mac_address_blacklist; iter; iter = g_slist_next (iter)) { + if (!strcasecmp (mac, (char *) iter->data)) + return FALSE; + } + + priv->mac_address_blacklist = g_slist_append (priv->mac_address_blacklist, + g_ascii_strup (mac, -1)); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST); + return TRUE; +} + +/** + * nm_setting_wireless_remove_mac_blacklist_item: + * @setting: the #NMSettingWireless + * @idx: index number of the MAC address + * + * Removes the MAC address at index @idx from the blacklist. + * + * Since: 0.9.10 + **/ +void +nm_setting_wireless_remove_mac_blacklist_item (NMSettingWireless *setting, guint32 idx) +{ + NMSettingWirelessPrivate *priv; + GSList *elt; + + g_return_if_fail (NM_IS_SETTING_WIRELESS (setting)); + + priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting); + elt = g_slist_nth (priv->mac_address_blacklist, idx); + g_return_if_fail (elt != NULL); + + g_free (elt->data); + priv->mac_address_blacklist = g_slist_delete_link (priv->mac_address_blacklist, elt); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST); +} + +/** + * nm_setting_wireless_remove_mac_blacklist_item_by_value: + * @setting: the #NMSettingWireless + * @mac: the MAC address string (hex-digits-and-colons notation) to remove from + * the blacklist + * + * Removes the MAC address @mac from the blacklist. + * + * Returns: %TRUE if the MAC address was found and removed; %FALSE if it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_wireless_remove_mac_blacklist_item_by_value (NMSettingWireless *setting, const char *mac) +{ + NMSettingWirelessPrivate *priv; + GSList *iter; + guint8 buf[32]; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), FALSE); + g_return_val_if_fail (mac != NULL, FALSE); + + if (!nm_utils_hwaddr_aton (mac, ARPHRD_ETHER, buf)) + return FALSE; + + priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting); + for (iter = priv->mac_address_blacklist; iter; iter = g_slist_next (iter)) { + if (!strcasecmp (mac, (char *) iter->data)) { + priv->mac_address_blacklist = g_slist_delete_link (priv->mac_address_blacklist, iter); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_wireless_clear_mac_blacklist_items: + * @setting: the #NMSettingWireless + * + * Removes all blacklisted MAC addresses. + * + * Since: 0.9.10 + **/ +void +nm_setting_wireless_clear_mac_blacklist_items (NMSettingWireless *setting) +{ + g_return_if_fail (NM_IS_SETTING_WIRELESS (setting)); + + g_slist_free_full (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mac_address_blacklist, g_free); + NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mac_address_blacklist = NULL; + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST); +} + +/** + * nm_setting_wireless_get_mtu: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:mtu property of the setting + **/ +guint32 +nm_setting_wireless_get_mtu (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mtu; +} + +/** + * nm_setting_wireless_get_security: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:security property of the setting + * + * Deprecated: 0.9.10: No longer used. Security rescrictions are recognized by + * the presence of NM_SETTING_WIRELESS_SECURITY_SETTING_NAME in the connection. + **/ +const char * +nm_setting_wireless_get_security (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->security; +} + +/** + * nm_setting_wireless_get_hidden: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:hidden property of the setting + **/ +gboolean +nm_setting_wireless_get_hidden (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), FALSE); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->hidden; +} + +/** + * nm_setting_wireless_add_seen_bssid: + * @setting: the #NMSettingWireless + * @bssid: the new BSSID to add to the list + * + * Adds a new Wi-Fi AP's BSSID to the previously seen BSSID list of the setting. + * NetworkManager now tracks previously seen BSSIDs internally so this function + * no longer has much use. Actually, changes you make using this function will + * not be preserved. + * + * Returns: %TRUE if @bssid was already known, %FALSE if not + **/ +gboolean +nm_setting_wireless_add_seen_bssid (NMSettingWireless *setting, + const char *bssid) +{ + NMSettingWirelessPrivate *priv; + char *lower_bssid; + GSList *iter; + gboolean found = FALSE; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), FALSE); + g_return_val_if_fail (bssid != NULL, FALSE); + + lower_bssid = g_ascii_strdown (bssid, -1); + if (!lower_bssid) + return FALSE; + + priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting); + + for (iter = priv->seen_bssids; iter; iter = iter->next) { + if (!strcmp ((char *) iter->data, lower_bssid)) { + found = TRUE; + break; + } + } + + if (!found) { + priv->seen_bssids = g_slist_prepend (priv->seen_bssids, lower_bssid); + g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SEEN_BSSIDS); + } else + g_free (lower_bssid); + + return !found; +} + +/** + * nm_setting_wireless_get_num_seen_bssids: + * @setting: the #NMSettingWireless + * + * Returns: the number of BSSIDs in the previously seen BSSID list + **/ +guint32 +nm_setting_wireless_get_num_seen_bssids (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0); + + return g_slist_length (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->seen_bssids); +} + +/** + * nm_setting_wireless_get_seen_bssid: + * @setting: the #NMSettingWireless + * @i: index of a BSSID in the previously seen BSSID list + * + * Returns: the BSSID at index @i + **/ +const char * +nm_setting_wireless_get_seen_bssid (NMSettingWireless *setting, + guint32 i) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL); + + return (const char *) g_slist_nth_data (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->seen_bssids, i); +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting); + const char *valid_modes[] = { NM_SETTING_WIRELESS_MODE_INFRA, NM_SETTING_WIRELESS_MODE_ADHOC, NM_SETTING_WIRELESS_MODE_AP, NULL }; + const char *valid_bands[] = { "a", "bg", NULL }; + GSList *iter; + + if (!priv->ssid) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID); + return FALSE; + } + + if (!priv->ssid->len || priv->ssid->len > 32) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + _("SSID length is out of range <1-32> bytes")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID); + return FALSE; + } + + if (priv->mode && !_nm_utils_string_in_list (priv->mode, valid_modes)) { + g_set_error (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid Wi-Fi mode"), + priv->mode); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MODE); + return FALSE; + } + + if (priv->band && !_nm_utils_string_in_list (priv->band, valid_bands)) { + g_set_error (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid band"), + priv->band); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_BAND); + return FALSE; + } + + if (priv->channel && !priv->band) { + g_set_error (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_CHANNEL_REQUIRES_BAND, + _("requires setting '%s' property"), + NM_SETTING_WIRELESS_BAND); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_CHANNEL); + return FALSE; + } + + if (priv->channel) { + if (!nm_utils_wifi_is_channel_valid (priv->channel, priv->band)) { + g_set_error (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + _("'%d' is not a valid channel"), + priv->channel); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_CHANNEL); + return FALSE; + } + } + + if (priv->bssid && priv->bssid->len != ETH_ALEN) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_BSSID); + return FALSE; + } + + if (priv->device_mac_address && priv->device_mac_address->len != ETH_ALEN) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS); + return FALSE; + } + + if (priv->cloned_mac_address && priv->cloned_mac_address->len != ETH_ALEN) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS); + return FALSE; + } + + for (iter = priv->mac_address_blacklist; iter; iter = iter->next) { + struct ether_addr addr; + + if (!ether_aton_r (iter->data, &addr)) { + g_set_error (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid MAC address"), + (const char *) iter->data); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST); + return FALSE; + } + } + + for (iter = priv->seen_bssids; iter; iter = iter->next) { + struct ether_addr addr; + + if (!ether_aton_r (iter->data, &addr)) { + g_set_error (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid MAC address"), + (const char *) iter->data); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SEEN_BSSIDS); + return FALSE; + } + } + + return TRUE; +} + +static void +nm_setting_wireless_init (NMSettingWireless *setting) +{ +} + +static void +finalize (GObject *object) +{ + NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE (object); + + g_free (priv->mode); + g_free (priv->band); + g_free (priv->security); + + if (priv->ssid) + g_byte_array_free (priv->ssid, TRUE); + if (priv->bssid) + g_byte_array_free (priv->bssid, TRUE); + if (priv->device_mac_address) + g_byte_array_free (priv->device_mac_address, TRUE); + if (priv->cloned_mac_address) + g_byte_array_free (priv->cloned_mac_address, TRUE); + g_slist_free_full (priv->mac_address_blacklist, g_free); + g_slist_free_full (priv->seen_bssids, g_free); + + G_OBJECT_CLASS (nm_setting_wireless_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SSID: + if (priv->ssid) + g_byte_array_free (priv->ssid, TRUE); + priv->ssid = g_value_dup_boxed (value); + break; + case PROP_MODE: + g_free (priv->mode); + priv->mode = g_value_dup_string (value); + break; + case PROP_BAND: + g_free (priv->band); + priv->band = g_value_dup_string (value); + break; + case PROP_CHANNEL: + priv->channel = g_value_get_uint (value); + break; + case PROP_BSSID: + if (priv->bssid) + g_byte_array_free (priv->bssid, TRUE); + priv->bssid = g_value_dup_boxed (value); + break; + case PROP_RATE: + priv->rate = g_value_get_uint (value); + break; + case PROP_TX_POWER: + priv->tx_power = g_value_get_uint (value); + break; + case PROP_MAC_ADDRESS: + if (priv->device_mac_address) + g_byte_array_free (priv->device_mac_address, TRUE); + priv->device_mac_address = g_value_dup_boxed (value); + break; + case PROP_CLONED_MAC_ADDRESS: + if (priv->cloned_mac_address) + g_byte_array_free (priv->cloned_mac_address, TRUE); + priv->cloned_mac_address = g_value_dup_boxed (value); + break; + case PROP_MAC_ADDRESS_BLACKLIST: + g_slist_free_full (priv->mac_address_blacklist, g_free); + priv->mac_address_blacklist = g_value_dup_boxed (value); + break; + case PROP_MTU: + priv->mtu = g_value_get_uint (value); + break; + case PROP_SEEN_BSSIDS: + g_slist_free_full (priv->seen_bssids, g_free); + priv->seen_bssids = g_value_dup_boxed (value); + break; + case PROP_SEC: + g_free (priv->security); + priv->security = g_value_dup_string (value); + break; + case PROP_HIDDEN: + priv->hidden = g_value_get_boolean (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) +{ + NMSettingWireless *setting = NM_SETTING_WIRELESS (object); + + switch (prop_id) { + case PROP_SSID: + g_value_set_boxed (value, nm_setting_wireless_get_ssid (setting)); + break; + case PROP_MODE: + g_value_set_string (value, nm_setting_wireless_get_mode (setting)); + break; + case PROP_BAND: + g_value_set_string (value, nm_setting_wireless_get_band (setting)); + break; + case PROP_CHANNEL: + g_value_set_uint (value, nm_setting_wireless_get_channel (setting)); + break; + case PROP_BSSID: + g_value_set_boxed (value, nm_setting_wireless_get_bssid (setting)); + break; + case PROP_RATE: + g_value_set_uint (value, nm_setting_wireless_get_rate (setting)); + break; + case PROP_TX_POWER: + g_value_set_uint (value, nm_setting_wireless_get_tx_power (setting)); + break; + case PROP_MAC_ADDRESS: + g_value_set_boxed (value, nm_setting_wireless_get_mac_address (setting)); + break; + case PROP_CLONED_MAC_ADDRESS: + g_value_set_boxed (value, nm_setting_wireless_get_cloned_mac_address (setting)); + break; + case PROP_MAC_ADDRESS_BLACKLIST: + g_value_set_boxed (value, nm_setting_wireless_get_mac_address_blacklist (setting)); + break; + case PROP_MTU: + g_value_set_uint (value, nm_setting_wireless_get_mtu (setting)); + break; + case PROP_SEEN_BSSIDS: + g_value_set_boxed (value, NM_SETTING_WIRELESS_GET_PRIVATE (setting)->seen_bssids); + break; + case PROP_SEC: + g_value_set_string (value, NM_SETTING_WIRELESS_GET_PRIVATE (setting)->security); + break; + case PROP_HIDDEN: + g_value_set_boolean (value, nm_setting_wireless_get_hidden (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingWirelessPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + + /* Properties */ + /** + * NMSettingWireless:ssid: + * + * SSID of the Wi-Fi network. Must be specified. + **/ + g_object_class_install_property + (object_class, PROP_SSID, + _nm_param_spec_specialized (NM_SETTING_WIRELESS_SSID, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWireless:mode: + * + * Wi-Fi network mode; one of "infrastructure", "adhoc" or "ap". If blank, + * infrastructure is assumed. + **/ + g_object_class_install_property + (object_class, PROP_MODE, + g_param_spec_string (NM_SETTING_WIRELESS_MODE, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWireless:band: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_BAND, + g_param_spec_string (NM_SETTING_WIRELESS_BAND, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWireless:channel: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_CHANNEL, + g_param_spec_uint (NM_SETTING_WIRELESS_CHANNEL, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWireless:bssid: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_BSSID, + _nm_param_spec_specialized (NM_SETTING_WIRELESS_BSSID, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWireless:rate: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_RATE, + g_param_spec_uint (NM_SETTING_WIRELESS_RATE, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWireless:tx-power: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_TX_POWER, + g_param_spec_uint (NM_SETTING_WIRELESS_TX_POWER, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWireless:mac-address: + * + * 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). + **/ + g_object_class_install_property + (object_class, PROP_MAC_ADDRESS, + _nm_param_spec_specialized (NM_SETTING_WIRELESS_MAC_ADDRESS, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWireless:cloned-mac-address: + * + * If specified, request that the Wi-Fi device use this MAC address instead + * of its permanent MAC address. This is known as MAC cloning or spoofing. + **/ + g_object_class_install_property + (object_class, PROP_CLONED_MAC_ADDRESS, + _nm_param_spec_specialized (NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWireless:mac-address-blacklist: + * + * 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"). + **/ + g_object_class_install_property + (object_class, PROP_MAC_ADDRESS_BLACKLIST, + _nm_param_spec_specialized (NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWireless:seen-bssids: + * + * 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. + **/ + g_object_class_install_property + (object_class, PROP_SEEN_BSSIDS, + _nm_param_spec_specialized (NM_SETTING_WIRELESS_SEEN_BSSIDS, "", "", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWireless:mtu: + * + * If non-zero, only transmit packets of the specified size or smaller, + * breaking larger packets up into multiple Ethernet frames. + **/ + g_object_class_install_property + (object_class, PROP_MTU, + g_param_spec_uint (NM_SETTING_WIRELESS_MTU, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWireless:security: + * + * If the wireless connection has any security restrictions, like 802.1x, + * WEP, or WPA, set this property to + * %NM_SETTING_WIRELESS_SECURITY_SETTING_NAME and ensure the connection + * contains a valid #NMSettingWirelessSecurity setting. + * + * Deprecated: 0.9.10: No longer used. Security restrictions are recognized + * by the presence of a #NMSettingWirelessSecurity setting in the + * connection. + **/ + g_object_class_install_property + (object_class, PROP_SEC, + g_param_spec_string (NM_SETTING_WIRELESS_SEC, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingWireless:hidden: + * + * If %TRUE, indicates this network is a non-broadcasting network that hides + * its SSID. In this case various workarounds may take place, such as + * probe-scanning the SSID for more reliable network discovery. However, + * these workarounds expose inherent insecurities with hidden SSID networks, + * and thus hidden SSID networks should be used with caution. + **/ + g_object_class_install_property + (object_class, PROP_HIDDEN, + g_param_spec_boolean (NM_SETTING_WIRELESS_HIDDEN, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-wireless.h b/libnm-core/nm-setting-wireless.h new file mode 100644 index 0000000000..18f78cff66 --- /dev/null +++ b/libnm-core/nm-setting-wireless.h @@ -0,0 +1,174 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SETTING_WIRELESS_H +#define NM_SETTING_WIRELESS_H + +#include <NetworkManager.h> +#include <nm-setting.h> +#include <nm-setting-wireless-security.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_WIRELESS (nm_setting_wireless_get_type ()) +#define NM_SETTING_WIRELESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WIRELESS, NMSettingWireless)) +#define NM_SETTING_WIRELESS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WIRELESS, NMSettingWirelessClass)) +#define NM_IS_SETTING_WIRELESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WIRELESS)) +#define NM_IS_SETTING_WIRELESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_WIRELESS)) +#define NM_SETTING_WIRELESS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WIRELESS, NMSettingWirelessClass)) + +#define NM_SETTING_WIRELESS_SETTING_NAME "802-11-wireless" + +/** + * NMSettingWirelessError: + * @NM_SETTING_WIRELESS_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_WIRELESS_ERROR_MISSING_PROPERTY: the property was missing and is + * required + * @NM_SETTING_WIRELESS_ERROR_MISSING_SECURITY_SETTING: property values require + * the presence of an #NMSettingWirelessSecurity object in the connection + * @NM_SETTING_WIRELESS_ERROR_CHANNEL_REQUIRES_BAND: the property channel was + * set to a value that requires the #NMSettingWireless:band property to be set + */ +typedef enum { + NM_SETTING_WIRELESS_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_WIRELESS_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ + NM_SETTING_WIRELESS_ERROR_MISSING_SECURITY_SETTING, /*< nick=MissingSecuritySetting >*/ + NM_SETTING_WIRELESS_ERROR_CHANNEL_REQUIRES_BAND /*< nick=ChannelRequiresBand >*/ +} NMSettingWirelessError; + +#define NM_SETTING_WIRELESS_ERROR nm_setting_wireless_error_quark () +GQuark nm_setting_wireless_error_quark (void); + +#define NM_SETTING_WIRELESS_SSID "ssid" +#define NM_SETTING_WIRELESS_MODE "mode" +#define NM_SETTING_WIRELESS_BAND "band" +#define NM_SETTING_WIRELESS_CHANNEL "channel" +#define NM_SETTING_WIRELESS_BSSID "bssid" +#define NM_SETTING_WIRELESS_RATE "rate" +#define NM_SETTING_WIRELESS_TX_POWER "tx-power" +#define NM_SETTING_WIRELESS_MAC_ADDRESS "mac-address" +#define NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS "cloned-mac-address" +#define NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST "mac-address-blacklist" +#define NM_SETTING_WIRELESS_MTU "mtu" +#define NM_SETTING_WIRELESS_SEEN_BSSIDS "seen-bssids" +#define NM_SETTING_WIRELESS_HIDDEN "hidden" + +/* Deprecated */ +#define NM_SETTING_WIRELESS_SEC "security" + +/** + * NM_SETTING_WIRELESS_MODE_ADHOC: + * + * Indicates Ad-Hoc mode where no access point is expected to be present. + */ +#define NM_SETTING_WIRELESS_MODE_ADHOC "adhoc" + +/** + * NM_SETTING_WIRELESS_MODE_AP: + * + * Indicates AP/master mode where the wireless device is started as an access + * point/hotspot. + * + * Since: 0.9.8 + */ +#define NM_SETTING_WIRELESS_MODE_AP "ap" + +/** + * NM_SETTING_WIRELESS_MODE_INFRA: + * + * Indicates infrastructure mode where an access point is expected to be present + * for this connection. + */ +#define NM_SETTING_WIRELESS_MODE_INFRA "infrastructure" + +typedef struct { + NMSetting parent; +} NMSettingWireless; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingWirelessClass; + +GType nm_setting_wireless_get_type (void); + +NMSetting *nm_setting_wireless_new (void); + +const GByteArray *nm_setting_wireless_get_ssid (NMSettingWireless *setting); +const char *nm_setting_wireless_get_mode (NMSettingWireless *setting); +const char *nm_setting_wireless_get_band (NMSettingWireless *setting); +guint32 nm_setting_wireless_get_channel (NMSettingWireless *setting); +const GByteArray *nm_setting_wireless_get_bssid (NMSettingWireless *setting); +guint32 nm_setting_wireless_get_rate (NMSettingWireless *setting); +guint32 nm_setting_wireless_get_tx_power (NMSettingWireless *setting); +const GByteArray *nm_setting_wireless_get_mac_address (NMSettingWireless *setting); +const GByteArray *nm_setting_wireless_get_cloned_mac_address (NMSettingWireless *setting); + +const GSList *nm_setting_wireless_get_mac_address_blacklist (NMSettingWireless *setting); +NM_AVAILABLE_IN_0_9_10 +guint32 nm_setting_wireless_get_num_mac_blacklist_items (NMSettingWireless *setting); +NM_AVAILABLE_IN_0_9_10 +const char * nm_setting_wireless_get_mac_blacklist_item (NMSettingWireless *setting, + guint32 idx); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_wireless_add_mac_blacklist_item (NMSettingWireless *setting, + const char *mac); +NM_AVAILABLE_IN_0_9_10 +void nm_setting_wireless_remove_mac_blacklist_item (NMSettingWireless *setting, + guint32 idx); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_setting_wireless_remove_mac_blacklist_item_by_value (NMSettingWireless *setting, + const char *mac); +NM_AVAILABLE_IN_0_9_10 +void nm_setting_wireless_clear_mac_blacklist_items (NMSettingWireless *setting); + +guint32 nm_setting_wireless_get_mtu (NMSettingWireless *setting); +gboolean nm_setting_wireless_get_hidden (NMSettingWireless *setting); + +gboolean nm_setting_wireless_add_seen_bssid (NMSettingWireless *setting, + const char *bssid); + +guint32 nm_setting_wireless_get_num_seen_bssids (NMSettingWireless *setting); +const char *nm_setting_wireless_get_seen_bssid (NMSettingWireless *setting, + guint32 i); + +gboolean nm_setting_wireless_ap_security_compatible (NMSettingWireless *s_wireless, + NMSettingWirelessSecurity *s_wireless_sec, + NM80211ApFlags ap_flags, + NM80211ApSecurityFlags ap_wpa, + NM80211ApSecurityFlags ap_rsn, + NM80211Mode ap_mode); + +/* Deprecated */ +NM_DEPRECATED_IN_0_9_10 +const char *nm_setting_wireless_get_security (NMSettingWireless *setting); + +G_END_DECLS + +#endif /* NM_SETTING_WIRELESS_H */ diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c new file mode 100644 index 0000000000..94a0cac6e9 --- /dev/null +++ b/libnm-core/nm-setting.c @@ -0,0 +1,1445 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2011 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include <string.h> +#include <glib/gi18n.h> + +#include "nm-setting.h" +#include "nm-setting-private.h" +#include "nm-setting-connection.h" +#include "nm-utils.h" +#include "nm-utils-private.h" + +/** + * SECTION:nm-setting + * @short_description: Describes related configuration information + * @include: nm-setting.h + * + * Each #NMSetting contains properties that describe configuration that applies + * to a specific network layer (like IPv4 or IPv6 configuration) or device type + * (like Ethernet, or Wi-Fi). A collection of individual settings together + * make up an #NMConnection. Each property is strongly typed and usually has + * a number of allowed values. See each #NMSetting subclass for a description + * of properties and allowed values. + */ + +/** + * nm_setting_error_quark: + * + * Registers an error quark for #NMSetting if necessary. + * + * Returns: the error quark used for NMSetting errors. + **/ +GQuark +nm_setting_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-error-quark"); + return quark; +} + +G_DEFINE_ABSTRACT_TYPE (NMSetting, nm_setting, G_TYPE_OBJECT) + +#define NM_SETTING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING, NMSettingPrivate)) + +typedef struct { + const char *name; + GType type; + guint32 priority; + GQuark error_quark; +} SettingInfo; + +typedef struct { + const SettingInfo *info; +} NMSettingPrivate; + +enum { + PROP_0, + PROP_NAME, + + PROP_LAST +}; + +/*************************************************************/ + +static GHashTable *registered_settings = NULL; +static GHashTable *registered_settings_by_type = NULL; + +static gboolean +_nm_gtype_equal (gconstpointer v1, gconstpointer v2) +{ + return *((const GType *) v1) == *((const GType *) v2); +} +static guint +_nm_gtype_hash (gconstpointer v) +{ + return *((const GType *) v); +} + +static void __attribute__((constructor)) +_ensure_registered (void) +{ + if (G_UNLIKELY (registered_settings == NULL)) { +#if !GLIB_CHECK_VERSION (2, 35, 0) + g_type_init (); +#endif + _nm_value_transforms_register (); + registered_settings = g_hash_table_new (g_str_hash, g_str_equal); + registered_settings_by_type = g_hash_table_new (_nm_gtype_hash, _nm_gtype_equal); + } +} + +#define _ensure_setting_info(self, priv) \ + G_STMT_START { \ + NMSettingPrivate *_priv_esi = (priv); \ + if (G_UNLIKELY (!_priv_esi->info)) { \ + _priv_esi->info = _nm_setting_lookup_setting_by_type (G_OBJECT_TYPE (self)); \ + g_assert (_priv_esi->info); \ + } \ + } G_STMT_END + +/*************************************************************/ + +/* + * _nm_register_setting: + * @name: the name of the #NMSetting object to register + * @type: the #GType of the #NMSetting + * @priority: the sort priority of the setting, see below + * @error_quark: the setting's error quark + * + * INTERNAL ONLY: registers a setting's internal properties, like its priority + * and its error quark type, with libnm-util. + * + * A setting's priority should roughly follow the OSI layer model, but it also + * controls which settings get asked for secrets first. Thus settings which + * relate to things that must be working first, like hardware, should get a + * higher priority than things which layer on top of the hardware. For example, + * the GSM/CDMA settings should provide secrets before the PPP setting does, + * because a PIN is required to unlock the device before PPP can even start. + * Even settings without secrets should be assigned the right priority. + * + * 0: reserved for the Connection setting + * + * 1: hardware-related settings like Ethernet, Wi-Fi, InfiniBand, Bridge, etc. + * These priority 1 settings are also "base types", which means that at least + * one of them is required for the connection to be valid, and their name is + * valid in the 'type' property of the Connection setting. + * + * 2: hardware-related auxiliary settings that require a base setting to be + * successful first, like Wi-Fi security, 802.1x, etc. + * + * 3: hardware-independent settings that are required before IP connectivity + * can be established, like PPP, PPPoE, etc. + * + * 4: IP-level stuff + */ +void +(_nm_register_setting) (const char *name, + const GType type, + const guint32 priority, + const GQuark error_quark) +{ + SettingInfo *info; + + g_return_if_fail (name != NULL && *name); + g_return_if_fail (type != G_TYPE_INVALID); + g_return_if_fail (type != G_TYPE_NONE); + g_return_if_fail (error_quark != 0); + g_return_if_fail (priority <= 4); + + _ensure_registered (); + + if (G_LIKELY ((info = g_hash_table_lookup (registered_settings, name)))) { + g_return_if_fail (info->type == type); + g_return_if_fail (info->error_quark == error_quark); + g_return_if_fail (info->priority == priority); + g_return_if_fail (g_strcmp0 (info->name, name) == 0); + return; + } + g_return_if_fail (g_hash_table_lookup (registered_settings_by_type, &type) == NULL); + + if (priority == 0) + g_assert_cmpstr (name, ==, NM_SETTING_CONNECTION_SETTING_NAME); + + info = g_slice_new0 (SettingInfo); + info->type = type; + info->priority = priority; + info->error_quark = error_quark; + info->name = name; + g_hash_table_insert (registered_settings, (void *) info->name, info); + g_hash_table_insert (registered_settings_by_type, &info->type, info); +} + +static const SettingInfo * +_nm_setting_lookup_setting_by_type (GType type) +{ + _ensure_registered (); + return g_hash_table_lookup (registered_settings_by_type, &type); +} + +static guint32 +_get_setting_type_priority (GType type) +{ + const SettingInfo *info; + + g_return_val_if_fail (g_type_is_a (type, NM_TYPE_SETTING), G_MAXUINT32); + + info = _nm_setting_lookup_setting_by_type (type); + return info->priority; +} + +gboolean +_nm_setting_type_is_base_type (GType type) +{ + /* Historical oddity: PPPoE is a base-type even though it's not + * priority 1. It needs to be sorted *after* lower-level stuff like + * Wi-Fi security or 802.1x for secrets, but it's still allowed as a + * base type. + */ + return _get_setting_type_priority (type) == 1 || (type == NM_TYPE_SETTING_PPPOE); +} + +gboolean +_nm_setting_is_base_type (NMSetting *setting) +{ + return _nm_setting_type_is_base_type (G_OBJECT_TYPE (setting)); +} + +GType +_nm_setting_lookup_setting_type (const char *name) +{ + SettingInfo *info; + + g_return_val_if_fail (name != NULL, G_TYPE_NONE); + + _ensure_registered (); + + info = g_hash_table_lookup (registered_settings, name); + return info ? info->type : G_TYPE_INVALID; +} + +GType +_nm_setting_lookup_setting_type_by_quark (GQuark error_quark) +{ + SettingInfo *info; + GHashTableIter iter; + + _ensure_registered (); + + g_hash_table_iter_init (&iter, registered_settings); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &info)) { + if (info->error_quark == error_quark) + return info->type; + } + return G_TYPE_INVALID; +} + +gint +_nm_setting_compare_priority (gconstpointer a, gconstpointer b) +{ + guint32 prio_a, prio_b; + + prio_a = _get_setting_type_priority (G_OBJECT_TYPE (a)); + prio_b = _get_setting_type_priority (G_OBJECT_TYPE (b)); + + if (prio_a < prio_b) + return -1; + else if (prio_a == prio_b) + return 0; + return 1; +} + +/*************************************************************/ + +static void +destroy_gvalue (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +/** + * nm_setting_to_hash: + * @setting: the #NMSetting + * @flags: hash flags, e.g. %NM_SETTING_HASH_FLAG_ALL + * + * Converts the #NMSetting into a #GHashTable mapping each setting property + * name to a GValue describing that property, suitable for marshalling over + * D-Bus or serializing. The mapping is string to GValue. + * + * Returns: (transfer full) (element-type utf8 GObject.Value): a new #GHashTable + * describing the setting's properties + **/ +GHashTable * +nm_setting_to_hash (NMSetting *setting, NMSettingHashFlags flags) +{ + GHashTable *hash; + GParamSpec **property_specs; + guint n_property_specs; + guint i; + + g_return_val_if_fail (NM_IS_SETTING (setting), NULL); + + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs); + if (!property_specs) { + g_warning ("%s: couldn't find property specs for object of type '%s'", + __func__, g_type_name (G_OBJECT_TYPE (setting))); + return NULL; + } + + hash = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, destroy_gvalue); + + for (i = 0; i < n_property_specs; i++) { + GParamSpec *prop_spec = property_specs[i]; + GValue *value; + + /* 'name' doesn't get serialized */ + if (strcmp (g_param_spec_get_name (prop_spec), NM_SETTING_NAME) == 0) + continue; + + if ( (flags & NM_SETTING_HASH_FLAG_NO_SECRETS) + && (prop_spec->flags & NM_SETTING_PARAM_SECRET)) + continue; + + if ( (flags & NM_SETTING_HASH_FLAG_ONLY_SECRETS) + && !(prop_spec->flags & NM_SETTING_PARAM_SECRET)) + continue; + + value = g_slice_new0 (GValue); + g_value_init (value, prop_spec->value_type); + g_object_get_property (G_OBJECT (setting), prop_spec->name, value); + + /* Don't serialize values with default values */ + if (!g_param_value_defaults (prop_spec, value)) + g_hash_table_insert (hash, g_strdup (prop_spec->name), value); + else + destroy_gvalue (value); + } + g_free (property_specs); + + /* Don't return empty hashes, except for base types */ + if (g_hash_table_size (hash) < 1 && !_nm_setting_is_base_type (setting)) { + g_hash_table_destroy (hash); + hash = NULL; + } + + return hash; +} + +/** + * nm_setting_new_from_hash: + * @setting_type: the #NMSetting type which the hash contains properties for + * @hash: (element-type utf8 GObject.Value): the #GHashTable containing a + * string to GValue mapping of properties that apply to the setting + * + * Creates a new #NMSetting object and populates that object with the properties + * contained in the hash table, using each hash key as the property to set, + * and each hash value as the value to set that property to. Setting properties + * are strongly typed, thus the GValue type of the hash value must be correct. + * See the documentation on each #NMSetting object subclass for the correct + * property names and value types. + * + * Returns: a new #NMSetting object populated with the properties from the + * hash table, or %NULL on failure + **/ +NMSetting * +nm_setting_new_from_hash (GType setting_type, GHashTable *hash) +{ + GHashTableIter iter; + NMSetting *setting; + const char *prop_name; + GValue *src_value; + GObjectClass *class; + guint n_params = 0; + GParameter *params; + int i; + + g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (setting_type), NULL); + g_return_val_if_fail (hash != NULL, NULL); + + /* g_type_class_ref() ensures the setting class is created if it hasn't + * already been used. + */ + class = g_type_class_ref (setting_type); + params = g_new0 (GParameter, g_hash_table_size (hash)); + + g_hash_table_iter_init (&iter, hash); + while (g_hash_table_iter_next (&iter, (gpointer) &prop_name, (gpointer) &src_value)) { + GValue *dst_value = ¶ms[n_params].value; + GParamSpec *param_spec; + + param_spec = g_object_class_find_property (class, prop_name); + if (!param_spec) { + /* Oh, we're so nice and only warn, maybe it should be a fatal error? */ + g_warning ("Ignoring invalid property '%s'", prop_name); + continue; + } + + g_value_init (dst_value, G_VALUE_TYPE (src_value)); + if (g_value_transform (src_value, dst_value)) + params[n_params++].name = prop_name; + else { + g_warning ("Ignoring property '%s' with invalid type (%s)", + prop_name, G_VALUE_TYPE_NAME (src_value)); + g_value_unset (dst_value); + } + } + + setting = (NMSetting *) g_object_newv (setting_type, n_params, params); + + for (i = 0; i < n_params; i++) + g_value_unset (¶ms[i].value); + + g_free (params); + g_type_class_unref (class); + + return setting; +} + +static void +duplicate_setting (NMSetting *setting, + const char *name, + const GValue *value, + GParamFlags flags, + gpointer user_data) +{ + if ((flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) == G_PARAM_WRITABLE) + g_object_set_property (G_OBJECT (user_data), name, value); +} + +/** + * nm_setting_duplicate: + * @setting: the #NMSetting to duplicate + * + * Duplicates a #NMSetting. + * + * Returns: (transfer full): a new #NMSetting containing the same properties and values as the + * source #NMSetting + **/ +NMSetting * +nm_setting_duplicate (NMSetting *setting) +{ + GObject *dup; + + g_return_val_if_fail (NM_IS_SETTING (setting), NULL); + + dup = g_object_new (G_OBJECT_TYPE (setting), NULL); + + g_object_freeze_notify (dup); + nm_setting_enumerate_values (setting, duplicate_setting, dup); + g_object_thaw_notify (dup); + + return NM_SETTING (dup); +} + +static gint +find_setting_by_name (gconstpointer a, gconstpointer b) +{ + NMSetting *setting = NM_SETTING (a); + const char *str = (const char *) b; + + return strcmp (nm_setting_get_name (setting), str); +} + +NMSetting * +nm_setting_find_in_list (GSList *settings_list, + const char *setting_name) +{ + GSList *found; + + found = g_slist_find_custom (settings_list, setting_name, find_setting_by_name); + if (found) + return found->data; + else + return NULL; +} + +/** + * nm_setting_get_name: + * @setting: the #NMSetting + * + * Returns the type name of the #NMSetting object + * + * Returns: a string containing the type name of the #NMSetting object, + * like 'ppp' or 'wireless' or 'wired'. + **/ +const char * +nm_setting_get_name (NMSetting *setting) +{ + NMSettingPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING (setting), NULL); + priv = NM_SETTING_GET_PRIVATE (setting); + _ensure_setting_info (setting, priv); + return priv->info->name; +} + +/** + * nm_setting_verify: + * @setting: the #NMSetting to verify + * @all_settings: (element-type NMSetting): a #GSList of all settings + * in the connection from which @setting came + * @error: location to store error, or %NULL + * + * Validates the setting. Each setting's properties have allowed values, and + * some are dependent on other values (hence the need for @all_settings). The + * returned #GError contains information about which property of the setting + * failed validation, and in what way that property failed validation. + * + * Returns: %TRUE if the setting is valid, %FALSE if it is not + **/ +gboolean +nm_setting_verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingVerifyResult result = _nm_setting_verify (setting, all_settings, error); + + if (result == NM_SETTING_VERIFY_NORMALIZABLE) + g_clear_error (error); + + return result == NM_SETTING_VERIFY_SUCCESS || result == NM_SETTING_VERIFY_NORMALIZABLE; +} + +NMSettingVerifyResult +_nm_setting_verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_VERIFY_ERROR); + g_return_val_if_fail (!error || *error == NULL, NM_SETTING_VERIFY_ERROR); + + if (NM_SETTING_GET_CLASS (setting)->verify) + return NM_SETTING_GET_CLASS (setting)->verify (setting, all_settings, error); + + return NM_SETTING_VERIFY_SUCCESS; +} + +static gboolean +compare_property (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags) +{ + GValue value1 = G_VALUE_INIT; + GValue value2 = G_VALUE_INIT; + gboolean different; + + /* Handle compare flags */ + if (prop_spec->flags & NM_SETTING_PARAM_SECRET) { + NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE; + NMSettingSecretFlags b_secret_flags = NM_SETTING_SECRET_FLAG_NONE; + + nm_setting_get_secret_flags (setting, prop_spec->name, &a_secret_flags, NULL); + nm_setting_get_secret_flags (other, prop_spec->name, &b_secret_flags, NULL); + + /* If the secret flags aren't the same the settings aren't the same */ + if (a_secret_flags != b_secret_flags) + return FALSE; + + /* Check for various secret flags that might cause us to ignore comparing + * this property. + */ + if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS) + && (a_secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED)) + return TRUE; + + if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS) + && (a_secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) + return TRUE; + } + + g_value_init (&value1, prop_spec->value_type); + g_object_get_property (G_OBJECT (setting), prop_spec->name, &value1); + + g_value_init (&value2, prop_spec->value_type); + g_object_get_property (G_OBJECT (other), prop_spec->name, &value2); + + different = g_param_values_cmp ((GParamSpec *) prop_spec, &value1, &value2); + + g_value_unset (&value1); + g_value_unset (&value2); + + return different == 0 ? TRUE : FALSE; +} + +/** + * nm_setting_compare: + * @a: a #NMSetting + * @b: a second #NMSetting to compare with the first + * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT + * + * Compares two #NMSetting objects for similarity, with comparison behavior + * modified by a set of flags. See the documentation for #NMSettingCompareFlags + * for a description of each flag's behavior. + * + * Returns: %TRUE if the comparison succeeds, %FALSE if it does not + **/ +gboolean +nm_setting_compare (NMSetting *a, + NMSetting *b, + NMSettingCompareFlags flags) +{ + GParamSpec **property_specs; + guint n_property_specs; + gint same = TRUE; + guint i; + + g_return_val_if_fail (NM_IS_SETTING (a), FALSE); + g_return_val_if_fail (NM_IS_SETTING (b), FALSE); + + /* First check that both have the same type */ + if (G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b)) + return FALSE; + + /* And now all properties */ + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs); + for (i = 0; i < n_property_specs && same; i++) { + GParamSpec *prop_spec = property_specs[i]; + + /* Fuzzy compare ignores secrets and properties defined with the FUZZY_IGNORE flag */ + if ( (flags & NM_SETTING_COMPARE_FLAG_FUZZY) + && (prop_spec->flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET))) + continue; + + if ((flags & NM_SETTING_COMPARE_FLAG_INFERRABLE) && !(prop_spec->flags & NM_SETTING_PARAM_INFERRABLE)) + continue; + + if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS) + && (prop_spec->flags & NM_SETTING_PARAM_SECRET)) + continue; + + same = NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags); + } + g_free (property_specs); + + return same; +} + +static inline gboolean +should_compare_prop (NMSetting *setting, + const char *prop_name, + NMSettingCompareFlags comp_flags, + GParamFlags prop_flags) +{ + /* Fuzzy compare ignores secrets and properties defined with the FUZZY_IGNORE flag */ + if ( (comp_flags & NM_SETTING_COMPARE_FLAG_FUZZY) + && (prop_flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET))) + return FALSE; + + if ((comp_flags & NM_SETTING_COMPARE_FLAG_INFERRABLE) && !(prop_flags & NM_SETTING_PARAM_INFERRABLE)) + return FALSE; + + if (prop_flags & NM_SETTING_PARAM_SECRET) { + NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; + + if (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS) + return FALSE; + + nm_setting_get_secret_flags (setting, prop_name, &secret_flags, NULL); + + if ( (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS) + && (secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED)) + return FALSE; + + if ( (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS) + && (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) + return FALSE; + } + + if ( (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_ID) + && NM_IS_SETTING_CONNECTION (setting) + && !strcmp (prop_name, NM_SETTING_CONNECTION_ID)) + return FALSE; + + return TRUE; +} + +/** + * nm_setting_diff: + * @a: a #NMSetting + * @b: a second #NMSetting to compare with the first + * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT + * @invert_results: this parameter is used internally by libnm-util and should + * be set to %FALSE. If %TRUE inverts the meaning of the #NMSettingDiffResult. + * @results: (inout) (transfer full) (element-type utf8 guint32): if the + * settings differ, on return a hash table mapping the differing keys to one or + * more %NMSettingDiffResult values OR-ed together. If the settings do not + * differ, any hash table passed in is unmodified. If no hash table is passed + * in and the settings differ, a new one is created and returned. + * + * Compares two #NMSetting objects for similarity, with comparison behavior + * modified by a set of flags. See the documentation for #NMSettingCompareFlags + * for a description of each flag's behavior. If the settings differ, the keys + * of each setting that differ from the other are added to @results, mapped to + * one or more #NMSettingDiffResult values. + * + * Returns: %TRUE if the settings contain the same values, %FALSE if they do not + **/ +gboolean +nm_setting_diff (NMSetting *a, + NMSetting *b, + NMSettingCompareFlags flags, + gboolean invert_results, + GHashTable **results) +{ + GParamSpec **property_specs; + guint n_property_specs; + guint i; + NMSettingDiffResult a_result = NM_SETTING_DIFF_RESULT_IN_A; + NMSettingDiffResult b_result = NM_SETTING_DIFF_RESULT_IN_B; + gboolean results_created = FALSE; + + g_return_val_if_fail (results != NULL, FALSE); + g_return_val_if_fail (NM_IS_SETTING (a), FALSE); + if (b) { + g_return_val_if_fail (NM_IS_SETTING (b), FALSE); + g_return_val_if_fail (G_OBJECT_TYPE (a) == G_OBJECT_TYPE (b), FALSE); + } + + /* If the caller is calling this function in a pattern like this to get + * complete diffs: + * + * nm_setting_diff (A, B, FALSE, &results); + * nm_setting_diff (B, A, TRUE, &results); + * + * and wants us to invert the results so that the second invocation comes + * out correctly, do that here. + */ + if (invert_results) { + a_result = NM_SETTING_DIFF_RESULT_IN_B; + b_result = NM_SETTING_DIFF_RESULT_IN_A; + } + + if (*results == NULL) { + *results = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + results_created = TRUE; + } + + /* And now all properties */ + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs); + + for (i = 0; i < n_property_specs; i++) { + GParamSpec *prop_spec = property_specs[i]; + NMSettingDiffResult r = NM_SETTING_DIFF_RESULT_UNKNOWN, tmp; + gboolean different = TRUE; + + /* Handle compare flags */ + if (!should_compare_prop (a, prop_spec->name, flags, prop_spec->flags)) + continue; + if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0) + continue; + + if (b) { + different = !NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags); + if (different) { + GValue value = G_VALUE_INIT; + + g_value_init (&value, prop_spec->value_type); + g_object_get_property (G_OBJECT (a), prop_spec->name, &value); + if (!g_param_value_defaults (prop_spec, &value)) + r |= a_result; + + g_value_reset (&value); + g_object_get_property (G_OBJECT (b), prop_spec->name, &value); + if (!g_param_value_defaults (prop_spec, &value)) + r |= b_result; + + g_value_unset (&value); + } + } else + r = a_result; /* only in A */ + + if (different) { + tmp = GPOINTER_TO_UINT (g_hash_table_lookup (*results, prop_spec->name)); + g_hash_table_insert (*results, g_strdup (prop_spec->name), GUINT_TO_POINTER (tmp | r)); + } + } + g_free (property_specs); + + /* Don't return an empty hash table */ + if (results_created && !g_hash_table_size (*results)) { + g_hash_table_destroy (*results); + *results = NULL; + } + + return !(*results); +} + +/** + * nm_setting_enumerate_values: + * @setting: the #NMSetting + * @func: (scope call): user-supplied function called for each property of the setting + * @user_data: user data passed to @func at each invocation + * + * Iterates over each property of the #NMSetting object, calling the supplied + * user function for each property. + **/ +void +nm_setting_enumerate_values (NMSetting *setting, + NMSettingValueIterFn func, + gpointer user_data) +{ + GParamSpec **property_specs; + guint n_property_specs; + int i; + + g_return_if_fail (NM_IS_SETTING (setting)); + g_return_if_fail (func != NULL); + + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs); + for (i = 0; i < n_property_specs; i++) { + GParamSpec *prop_spec = property_specs[i]; + GValue value = G_VALUE_INIT; + + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (prop_spec)); + g_object_get_property (G_OBJECT (setting), prop_spec->name, &value); + func (setting, prop_spec->name, &value, prop_spec->flags, user_data); + g_value_unset (&value); + } + + g_free (property_specs); +} + +/** + * nm_setting_clear_secrets: + * @setting: the #NMSetting + * + * Resets and clears any secrets in the setting. Secrets should be added to the + * setting only when needed, and cleared immediately after use to prevent + * leakage of information. + **/ +void +nm_setting_clear_secrets (NMSetting *setting) +{ + _nm_setting_clear_secrets (setting); +} + +gboolean +_nm_setting_clear_secrets (NMSetting *setting) +{ + GParamSpec **property_specs; + guint n_property_specs; + guint i; + gboolean changed = FALSE; + + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs); + + for (i = 0; i < n_property_specs; i++) { + GParamSpec *prop_spec = property_specs[i]; + + if (prop_spec->flags & NM_SETTING_PARAM_SECRET) { + GValue value = G_VALUE_INIT; + + g_value_init (&value, prop_spec->value_type); + g_object_get_property (G_OBJECT (setting), prop_spec->name, &value); + if (!g_param_value_defaults (prop_spec, &value)) { + g_param_value_set_default (prop_spec, &value); + g_object_set_property (G_OBJECT (setting), prop_spec->name, &value); + changed = TRUE; + } + g_value_unset (&value); + } + } + + g_free (property_specs); + + return changed; +} + +static gboolean +clear_secrets_with_flags (NMSetting *setting, + GParamSpec *pspec, + NMSettingClearSecretsWithFlagsFn func, + gpointer user_data) +{ + NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; + gboolean changed = FALSE; + + /* Clear the secret if the user function says to do so */ + nm_setting_get_secret_flags (setting, pspec->name, &flags, NULL); + if (func (setting, pspec->name, flags, user_data) == TRUE) { + GValue value = G_VALUE_INIT; + + g_value_init (&value, pspec->value_type); + g_object_get_property (G_OBJECT (setting), pspec->name, &value); + if (!g_param_value_defaults (pspec, &value)) { + g_param_value_set_default (pspec, &value); + g_object_set_property (G_OBJECT (setting), pspec->name, &value); + changed = TRUE; + } + g_value_unset (&value); + } + + return changed; +} + +/** + * nm_setting_clear_secrets_with_flags: + * @setting: the #NMSetting + * @func: (scope call): function to be called to determine whether a + * specific secret should be cleared or not + * @user_data: caller-supplied data passed to @func + * + * Clears and frees secrets determined by @func. + **/ +void +nm_setting_clear_secrets_with_flags (NMSetting *setting, + NMSettingClearSecretsWithFlagsFn func, + gpointer user_data) +{ + _nm_setting_clear_secrets_with_flags (setting, func, user_data); +} + +gboolean +_nm_setting_clear_secrets_with_flags (NMSetting *setting, + NMSettingClearSecretsWithFlagsFn func, + gpointer user_data) +{ + GParamSpec **property_specs; + guint n_property_specs; + guint i; + gboolean changed = FALSE; + + g_return_val_if_fail (setting, FALSE); + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + g_return_val_if_fail (func != NULL, FALSE); + + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs); + for (i = 0; i < n_property_specs; i++) { + if (property_specs[i]->flags & NM_SETTING_PARAM_SECRET) { + changed |= NM_SETTING_GET_CLASS (setting)->clear_secrets_with_flags (setting, + property_specs[i], + func, + user_data); + } + } + + g_free (property_specs); + return changed; +} + +/** + * nm_setting_need_secrets: + * @setting: the #NMSetting + * + * Returns an array of property names for each secret which may be required + * to make a successful connection. The returned hints are only intended as a + * guide to what secrets may be required, because in some circumstances, there + * is no way to conclusively determine exactly which secrets are needed. + * + * Returns: (transfer container) (element-type utf8): a #GPtrArray containing + * the property names of secrets of the #NMSetting which may be required; the + * caller owns the array and must free it with g_ptr_array_free(), but must not + * free the elements. + **/ +GPtrArray * +nm_setting_need_secrets (NMSetting *setting) +{ + GPtrArray *secrets = NULL; + + g_return_val_if_fail (NM_IS_SETTING (setting), NULL); + + if (NM_SETTING_GET_CLASS (setting)->need_secrets) + secrets = NM_SETTING_GET_CLASS (setting)->need_secrets (setting); + + return secrets; +} + +static int +update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **error) +{ + GParamSpec *prop_spec; + GValue transformed_value = G_VALUE_INIT; + + prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key); + if (!prop_spec) { + g_set_error (error, + NM_SETTING_ERROR, + NM_SETTING_ERROR_PROPERTY_NOT_FOUND, + "%s", key); + return NM_SETTING_UPDATE_SECRET_ERROR; + } + + /* Silently ignore non-secrets */ + if (!(prop_spec->flags & NM_SETTING_PARAM_SECRET)) + return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED; + + if (g_value_type_compatible (G_VALUE_TYPE (value), G_PARAM_SPEC_VALUE_TYPE (prop_spec))) { + if (G_VALUE_HOLDS_STRING (value) && G_IS_PARAM_SPEC_STRING (prop_spec)) { + /* String is expected to be a common case. Handle it specially and check whether + * the value is already set. Otherwise, we just reset the property and + * assume the value got modified. */ + char *v; + + g_object_get (G_OBJECT (setting), prop_spec->name, &v, NULL); + if (g_strcmp0 (v, g_value_get_string (value)) == 0) { + g_free (v); + return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED; + } + g_free (v); + } + g_object_set_property (G_OBJECT (setting), prop_spec->name, value); + return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED; + } + if (g_value_transform (value, &transformed_value)) { + g_object_set_property (G_OBJECT (setting), prop_spec->name, &transformed_value); + g_value_unset (&transformed_value); + return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED; + } + g_set_error (error, + NM_SETTING_ERROR, + NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH, + "%s", key); + return NM_SETTING_UPDATE_SECRET_ERROR; +} + +/** + * nm_setting_update_secrets: + * @setting: the #NMSetting + * @secrets: (element-type utf8 GObject.Value): a #GHashTable mapping + * string to #GValue of setting property names and secrets + * @error: location to store error, or %NULL + * + * Update the setting's secrets, given a hash table of secrets intended for that + * setting (deserialized from D-Bus for example). + * + * Returns: %TRUE if the secrets were successfully updated, %FALSE on failure to + * update one or more of the secrets. + **/ +gboolean +nm_setting_update_secrets (NMSetting *setting, GHashTable *secrets, GError **error) +{ + return _nm_setting_update_secrets (setting, secrets, error) != NM_SETTING_UPDATE_SECRET_ERROR; +} + +NMSettingUpdateSecretResult +_nm_setting_update_secrets (NMSetting *setting, GHashTable *secrets, GError **error) +{ + GHashTableIter iter; + gpointer key, data; + GError *tmp_error = NULL; + NMSettingUpdateSecretResult result = NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED; + + g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_UPDATE_SECRET_ERROR); + g_return_val_if_fail (secrets != NULL, NM_SETTING_UPDATE_SECRET_ERROR); + if (error) + g_return_val_if_fail (*error == NULL, NM_SETTING_UPDATE_SECRET_ERROR); + + g_hash_table_iter_init (&iter, secrets); + while (g_hash_table_iter_next (&iter, &key, &data)) { + int success; + const char *secret_key = (const char *) key; + GValue *secret_value = (GValue *) data; + + success = NM_SETTING_GET_CLASS (setting)->update_one_secret (setting, secret_key, secret_value, &tmp_error); + g_assert (!((success == NM_SETTING_UPDATE_SECRET_ERROR) ^ (!!tmp_error))); + + if (success == NM_SETTING_UPDATE_SECRET_ERROR) { + g_propagate_error (error, tmp_error); + return NM_SETTING_UPDATE_SECRET_ERROR; + } + + if (success == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED) + result = NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED; + } + + return result; +} + +static gboolean +is_secret_prop (NMSetting *setting, const char *secret_name, GError **error) +{ + GParamSpec *pspec; + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), secret_name); + if (!pspec) { + g_set_error (error, + NM_SETTING_ERROR, + NM_SETTING_ERROR_PROPERTY_NOT_FOUND, + "Secret %s not provided by this setting", secret_name); + return FALSE; + } + + if (!(pspec->flags & NM_SETTING_PARAM_SECRET)) { + g_set_error (error, + NM_SETTING_ERROR, + NM_SETTING_ERROR_PROPERTY_NOT_SECRET, + "Property %s is not a secret", secret_name); + return FALSE; + } + + return TRUE; +} + +static gboolean +get_secret_flags (NMSetting *setting, + const char *secret_name, + gboolean verify_secret, + NMSettingSecretFlags *out_flags, + GError **error) +{ + char *flags_prop; + NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; + + if (verify_secret) + g_return_val_if_fail (is_secret_prop (setting, secret_name, error), FALSE); + + flags_prop = g_strdup_printf ("%s-flags", secret_name); + g_object_get (G_OBJECT (setting), flags_prop, &flags, NULL); + g_free (flags_prop); + + if (out_flags) + *out_flags = flags; + return TRUE; +} + +/** + * nm_setting_get_secret_flags: + * @setting: the #NMSetting + * @secret_name: the secret key name to get flags for + * @out_flags: on success, the #NMSettingSecretFlags for the secret + * @error: location to store error, or %NULL + * + * For a given secret, retrieves the #NMSettingSecretFlags describing how to + * handle that secret. + * + * Returns: %TRUE on success (if the given secret name was a valid property of + * this setting, and if that property is secret), %FALSE if not + **/ +gboolean +nm_setting_get_secret_flags (NMSetting *setting, + const char *secret_name, + NMSettingSecretFlags *out_flags, + GError **error) +{ + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + g_return_val_if_fail (secret_name != NULL, FALSE); + + return NM_SETTING_GET_CLASS (setting)->get_secret_flags (setting, secret_name, TRUE, out_flags, error); +} + +static gboolean +set_secret_flags (NMSetting *setting, + const char *secret_name, + gboolean verify_secret, + NMSettingSecretFlags flags, + GError **error) +{ + char *flags_prop; + + if (verify_secret) + g_return_val_if_fail (is_secret_prop (setting, secret_name, error), FALSE); + + flags_prop = g_strdup_printf ("%s-flags", secret_name); + g_object_set (G_OBJECT (setting), flags_prop, flags, NULL); + g_free (flags_prop); + return TRUE; +} + +/** + * nm_setting_set_secret_flags: + * @setting: the #NMSetting + * @secret_name: the secret key name to set flags for + * @flags: the #NMSettingSecretFlags for the secret + * @error: location to store error, or %NULL + * + * For a given secret, stores the #NMSettingSecretFlags describing how to + * handle that secret. + * + * Returns: %TRUE on success (if the given secret name was a valid property of + * this setting, and if that property is secret), %FALSE if not + **/ +gboolean +nm_setting_set_secret_flags (NMSetting *setting, + const char *secret_name, + NMSettingSecretFlags flags, + GError **error) +{ + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + g_return_val_if_fail (secret_name != NULL, FALSE); + g_return_val_if_fail (flags <= NM_SETTING_SECRET_FLAGS_ALL, FALSE); + + return NM_SETTING_GET_CLASS (setting)->set_secret_flags (setting, secret_name, TRUE, flags, error); +} + +/** + * nm_setting_to_string: + * @setting: the #NMSetting + * + * Convert the setting into a string. For debugging purposes ONLY, should NOT + * be used for serialization of the setting, or machine-parsed in any way. The + * output format is not guaranteed to be stable and may change at any time. + * + * Returns: an allocated string containing a textual representation of the + * setting's properties and values (including secrets!), which the caller should + * free with g_free() + **/ +char * +nm_setting_to_string (NMSetting *setting) +{ + GString *string; + GParamSpec **property_specs; + guint n_property_specs; + guint i; + + g_return_val_if_fail (NM_IS_SETTING (setting), NULL); + + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs); + if (!property_specs) + return NULL; + + string = g_string_new (nm_setting_get_name (setting)); + g_string_append_c (string, '\n'); + + for (i = 0; i < n_property_specs; i++) { + GParamSpec *prop_spec = property_specs[i]; + GValue value = G_VALUE_INIT; + char *value_str; + gboolean is_default; + + if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0) + continue; + + g_value_init (&value, prop_spec->value_type); + g_object_get_property (G_OBJECT (setting), prop_spec->name, &value); + + value_str = g_strdup_value_contents (&value); + g_string_append_printf (string, "\t%s : %s", prop_spec->name, value_str); + g_free (value_str); + + is_default = g_param_value_defaults (prop_spec, &value); + g_value_unset (&value); + + g_string_append (string, " ("); + g_string_append_c (string, 's'); + if (is_default) + g_string_append_c (string, 'd'); + g_string_append_c (string, ')'); + g_string_append_c (string, '\n'); + } + + g_free (property_specs); + g_string_append_c (string, '\n'); + + return g_string_free (string, FALSE); +} + +/** + * nm_setting_get_virtual_iface_name: + * @setting: the #NMSetting + * + * Returns the name of the virtual kernel interface which the connection + * needs to use if specified in the settings. + * + * Returns: Name of the virtual interface or %NULL if the setting does not + * support this feature + **/ +const char * +nm_setting_get_virtual_iface_name (NMSetting *setting) +{ + g_return_val_if_fail (NM_IS_SETTING (setting), NULL); + + if (NM_SETTING_GET_CLASS (setting)->get_virtual_iface_name) + return NM_SETTING_GET_CLASS (setting)->get_virtual_iface_name (setting); + + return NULL; +} + + +NMSettingVerifyResult +_nm_setting_verify_deprecated_virtual_iface_name (const char *interface_name, + gboolean allow_missing, + const char *setting_name, + const char *setting_property, + GQuark error_quark, + gint e_invalid_property, + gint e_missing_property, + GSList *all_settings, + GError **error) +{ + NMSettingConnection *s_con; + const char *con_name; + + s_con = NM_SETTING_CONNECTION (nm_setting_find_in_list (all_settings, NM_SETTING_CONNECTION_SETTING_NAME)); + con_name = s_con ? nm_setting_connection_get_interface_name (s_con) : NULL; + if (!interface_name && !con_name) { + if (allow_missing) + return NM_SETTING_VERIFY_SUCCESS; + + g_set_error_literal (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME); + return NM_SETTING_VERIFY_ERROR; + } + if (!con_name && !nm_utils_iface_valid_name (interface_name)) { + /* the interface_name is invalid, we cannot normalize it. Only do this if !con_name, + * because if con_name is set, it can overwrite interface_name. */ + g_set_error_literal (error, + error_quark, + e_invalid_property, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", setting_name, setting_property); + return NM_SETTING_VERIFY_ERROR; + } + if (!con_name) { + /* NMSettingConnection has interface not set, it should be normalized to interface_name */ + g_set_error_literal (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME); + return NM_SETTING_VERIFY_NORMALIZABLE; + } + if (!nm_utils_iface_valid_name (con_name)) { + /* NMSettingConnection:interface_name is invalid, we cannot normalize it. */ + g_set_error_literal (error, + NM_SETTING_CONNECTION_ERROR, + NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME); + return NM_SETTING_VERIFY_ERROR; + } + if (!interface_name) { + /* Normalize by setting NMSettingConnection:interface_name. */ + g_set_error_literal (error, + error_quark, + e_missing_property, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", setting_name, setting_property); + return NM_SETTING_VERIFY_NORMALIZABLE; + } + if (strcmp (con_name, interface_name) != 0) { + /* con_name and interface_name are different. It can be normalized by setting interface_name + * to con_name. */ + g_set_error_literal (error, + error_quark, + e_missing_property, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", setting_name, setting_property); + /* we would like to make this a NORMALIZEABLE_ERROR, but that might + * break older connections. */ + return NM_SETTING_VERIFY_NORMALIZABLE; + } + + return NM_SETTING_VERIFY_SUCCESS; +} + +/*****************************************************************************/ + +static void +nm_setting_init (NMSetting *setting) +{ +} + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + + object = G_OBJECT_CLASS (nm_setting_parent_class)->constructor (type, + n_construct_params, + construct_params); + + _ensure_setting_info (object, NM_SETTING_GET_PRIVATE (object)); + return object; +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_NAME: + /* The setter for NAME is deprecated and should not be used anymore. + * Keep the setter for NAME to remain backward compatible. + * Only assert that the caller does not try to set the name to a different value + * then the registered name, which would be extra wrong. + **/ + _ensure_setting_info (object, priv); + g_return_if_fail (!g_strcmp0 (priv->info->name, g_value_get_string (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) +{ + NMSetting *setting = NM_SETTING (object); + + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, nm_setting_get_name (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_class_init (NMSettingClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingPrivate)); + + /* virtual methods */ + object_class->constructor = constructor; + object_class->set_property = set_property; + object_class->get_property = get_property; + + setting_class->update_one_secret = update_one_secret; + setting_class->get_secret_flags = get_secret_flags; + setting_class->set_secret_flags = set_secret_flags; + setting_class->compare_property = compare_property; + setting_class->clear_secrets_with_flags = clear_secrets_with_flags; + + /* Properties */ + + /** + * NMSetting:name: + * + * The setting's name, which uniquely identifies the setting within the + * connection. Each setting type has a name unique to that type, for + * example "ppp" or "wireless" or "wired". + **/ + g_object_class_install_property + (object_class, PROP_NAME, + g_param_spec_string (NM_SETTING_NAME, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting.h b/libnm-core/nm-setting.h new file mode 100644 index 0000000000..b58137a6e7 --- /dev/null +++ b/libnm-core/nm-setting.h @@ -0,0 +1,317 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2011 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SETTING_H +#define NM_SETTING_H + +#include <glib.h> +#include <glib-object.h> + +#include <nm-version.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING (nm_setting_get_type ()) +#define NM_SETTING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING, NMSetting)) +#define NM_SETTING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING, NMSettingClass)) +#define NM_IS_SETTING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING)) +#define NM_IS_SETTING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING)) +#define NM_SETTING_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING, NMSettingClass)) + +/** + * NMSettingError: + * @NM_SETTING_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_ERROR_PROPERTY_NOT_FOUND: a property required by the operation + * was not found; for example, an attempt to update an invalid secret + * @NM_SETTING_ERROR_PROPERTY_NOT_SECRET: an operation which requires a secret + * was attempted on a non-secret property + * @NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH: the operation requires a property + * of a specific type, or the value couldn't be transformed to the same type + * as the property being acted upon + * + * Describes errors that may result from operations involving a #NMSetting. + * + **/ +typedef enum +{ + NM_SETTING_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_SETTING_ERROR_PROPERTY_NOT_FOUND, /*< nick=PropertyNotFound >*/ + NM_SETTING_ERROR_PROPERTY_NOT_SECRET, /*< nick=PropertyNotSecret >*/ + NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH /*< nick=PropertyTypeMismatch >*/ +} NMSettingError; + +#define NM_SETTING_ERROR nm_setting_error_quark () +GQuark nm_setting_error_quark (void); + + +/* DEPRECATED AND UNUSED */ +#define NM_SETTING_PARAM_SERIALIZE (1 << (0 + G_PARAM_USER_SHIFT)) + +/* The property of the #NMSetting is required for the setting to be valid */ +#define NM_SETTING_PARAM_REQUIRED (1 << (1 + G_PARAM_USER_SHIFT)) + +/* The property of the #NMSetting is a secret */ +#define NM_SETTING_PARAM_SECRET (1 << (2 + G_PARAM_USER_SHIFT)) + +/* The property of the #NMSetting should be ignored during comparisons that + * use the %NM_SETTING_COMPARE_FLAG_FUZZY flag. + */ +#define NM_SETTING_PARAM_FUZZY_IGNORE (1 << (3 + G_PARAM_USER_SHIFT)) + +/* Note: all non-glib GParamFlags bits are reserved by NetworkManager */ + + +#define NM_SETTING_NAME "name" + +/** + * NMSettingSecretFlags: + * @NM_SETTING_SECRET_FLAG_NONE: the system is responsible for providing and + * storing this secret (default) + * @NM_SETTING_SECRET_FLAG_AGENT_OWNED: a user secret agent is responsible + * for providing and storing this secret; when it is required agents will be + * asked to retrieve it + * @NM_SETTING_SECRET_FLAG_NOT_SAVED: this secret should not be saved, but + * should be requested from the user each time it is needed + * @NM_SETTING_SECRET_FLAG_NOT_REQUIRED: in situations where it cannot be + * automatically determined that the secret is required (some VPNs and PPP + * providers dont require all secrets) this flag indicates that the specific + * secret is not required + * + * These flags indicate specific behavior related to handling of a secret. Each + * secret has a corresponding set of these flags which indicate how the secret + * is to be stored and/or requested when it is needed. + * + **/ +typedef enum { + NM_SETTING_SECRET_FLAG_NONE = 0x00000000, + NM_SETTING_SECRET_FLAG_AGENT_OWNED = 0x00000001, + NM_SETTING_SECRET_FLAG_NOT_SAVED = 0x00000002, + NM_SETTING_SECRET_FLAG_NOT_REQUIRED = 0x00000004 + + /* NOTE: if adding flags, update nm-setting-private.h as well */ +} NMSettingSecretFlags; + +/** + * NMSettingCompareFlags: + * @NM_SETTING_COMPARE_FLAG_EXACT: match all properties exactly + * @NM_SETTING_COMPARE_FLAG_FUZZY: match only important attributes, like SSID, + * type, security settings, etc. Does not match, for example, connection ID + * or UUID. + * @NM_SETTING_COMPARE_FLAG_IGNORE_ID: ignore the connection's ID + * @NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS: ignore all secrets + * @NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS: ignore secrets for which + * the secret's flags indicate the secret is owned by a user secret agent + * (ie, the secret's flag includes @NM_SETTING_SECRET_FLAG_AGENT_OWNED) + * @NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS: ignore secrets for which + * the secret's flags indicate the secret should not be saved to persistent + * storage (ie, the secret's flag includes @NM_SETTING_SECRET_FLAG_NOT_SAVED) + * + * These flags modify the comparison behavior when comparing two settings or + * two connections. + * + **/ +typedef enum { + NM_SETTING_COMPARE_FLAG_EXACT = 0x00000000, + NM_SETTING_COMPARE_FLAG_FUZZY = 0x00000001, + NM_SETTING_COMPARE_FLAG_IGNORE_ID = 0x00000002, + NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS = 0x00000004, + NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS = 0x00000008, + NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS = 0x00000010 + + /* 0x80000000 is used for a private flag */ +} NMSettingCompareFlags; + + +/** + * NMSetting: + * + * The NMSetting struct contains only private data. + * It should only be accessed through the functions described below. + */ +typedef struct { + GObject parent; +} NMSetting; + + +/** + * NMSettingClearSecretsWithFlagsFn: + * @setting: The setting for which secrets are being iterated + * @secret: The secret's name + * @flags: The secret's flags, eg %NM_SETTING_SECRET_FLAG_AGENT_OWNED + * @user_data: User data passed to nm_connection_clear_secrets_with_flags() + * + * Returns: %TRUE to clear the secret, %FALSE to not clear the secret + */ +typedef gboolean (*NMSettingClearSecretsWithFlagsFn) (NMSetting *setting, + const char *secret, + NMSettingSecretFlags flags, + gpointer user_data); + +typedef struct { + GObjectClass parent; + + /* Virtual functions */ + gint (*verify) (NMSetting *setting, + GSList *all_settings, + GError **error); + + GPtrArray *(*need_secrets) (NMSetting *setting); + + int (*update_one_secret) (NMSetting *setting, + const char *key, + GValue *value, + GError **error); + + gboolean (*get_secret_flags) (NMSetting *setting, + const char *secret_name, + gboolean verify_secret, + NMSettingSecretFlags *out_flags, + GError **error); + + gboolean (*set_secret_flags) (NMSetting *setting, + const char *secret_name, + gboolean verify_secret, + NMSettingSecretFlags flags, + GError **error); + + /* Returns TRUE if the given property contains the same value in both settings */ + gboolean (*compare_property) (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags); + + gboolean (*clear_secrets_with_flags) (NMSetting *setting, + GParamSpec *pspec, + NMSettingClearSecretsWithFlagsFn func, + gpointer user_data); + + const char *(*get_virtual_iface_name) (NMSetting *setting); + + /* Padding for future expansion */ + void (*_reserved1) (void); +} NMSettingClass; + +/** + * NMSettingValueIterFn: + * @setting: The setting for which properties are being iterated, given to + * nm_setting_enumerate_values() + * @key: The value/property name + * @value: The property's value + * @flags: The property's flags, like %NM_SETTING_PARAM_SECRET + * @user_data: User data passed to nm_setting_enumerate_values() + */ +typedef void (*NMSettingValueIterFn) (NMSetting *setting, + const char *key, + const GValue *value, + GParamFlags flags, + gpointer user_data); + + +GType nm_setting_get_type (void); + +/** + * NMSettingHashFlags: + * @NM_SETTING_HASH_FLAG_ALL: hash all properties (including secrets) + * @NM_SETTING_HASH_FLAG_NO_SECRETS: do not include secrets + * @NM_SETTING_HASH_FLAG_ONLY_SECRETS: only hash secrets + * + * These flags determine which properties are added to the resulting hash + * when calling nm_setting_to_hash(). + * + **/ +typedef enum { + NM_SETTING_HASH_FLAG_ALL = 0x00000000, + NM_SETTING_HASH_FLAG_NO_SECRETS = 0x00000001, + NM_SETTING_HASH_FLAG_ONLY_SECRETS = 0x00000002, +} NMSettingHashFlags; + +GHashTable *nm_setting_to_hash (NMSetting *setting, + NMSettingHashFlags flags); + +NMSetting *nm_setting_new_from_hash (GType setting_type, + GHashTable *hash); + +NMSetting *nm_setting_duplicate (NMSetting *setting); + +const char *nm_setting_get_name (NMSetting *setting); + +gboolean nm_setting_verify (NMSetting *setting, + GSList *all_settings, + GError **error); + +gboolean nm_setting_compare (NMSetting *a, + NMSetting *b, + NMSettingCompareFlags flags); + +/** + * NMSettingDiffResult: + * @NM_SETTING_DIFF_RESULT_UNKNOWN: unknown result + * @NM_SETTING_DIFF_RESULT_IN_A: the property is present in setting A + * @NM_SETTING_DIFF_RESULT_IN_B: the property is present in setting B + * + * These values indicate the result of a setting difference operation. + **/ +typedef enum { + NM_SETTING_DIFF_RESULT_UNKNOWN = 0x00000000, + NM_SETTING_DIFF_RESULT_IN_A = 0x00000001, + NM_SETTING_DIFF_RESULT_IN_B = 0x00000002, +} NMSettingDiffResult; + +gboolean nm_setting_diff (NMSetting *a, + NMSetting *b, + NMSettingCompareFlags flags, + gboolean invert_results, + GHashTable **results); + +void nm_setting_enumerate_values (NMSetting *setting, + NMSettingValueIterFn func, + gpointer user_data); + +char *nm_setting_to_string (NMSetting *setting); + +/* Secrets */ +void nm_setting_clear_secrets (NMSetting *setting); + +void nm_setting_clear_secrets_with_flags (NMSetting *setting, + NMSettingClearSecretsWithFlagsFn func, + gpointer user_data); + +GPtrArray *nm_setting_need_secrets (NMSetting *setting); +gboolean nm_setting_update_secrets (NMSetting *setting, + GHashTable *secrets, + GError **error); + +gboolean nm_setting_get_secret_flags (NMSetting *setting, + const char *secret_name, + NMSettingSecretFlags *out_flags, + GError **error); + +gboolean nm_setting_set_secret_flags (NMSetting *setting, + const char *secret_name, + NMSettingSecretFlags flags, + GError **error); + +const char *nm_setting_get_virtual_iface_name (NMSetting *setting); + +G_END_DECLS + +#endif /* NM_SETTING_H */ diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h new file mode 100644 index 0000000000..6e6d7b7247 --- /dev/null +++ b/libnm-core/nm-utils-private.h @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2005 - 2008 Red Hat, Inc. + */ + +#ifndef __NM_UTILS_PRIVATE_H__ +#define __NM_UTILS_PRIVATE_H__ + +#include "nm-setting-private.h" + +gboolean _nm_utils_string_in_list (const char *str, + const char **valid_strings); + +gboolean _nm_utils_string_slist_validate (GSList *list, + const char **valid_values); + +gboolean _nm_utils_gvalue_array_validate (GValueArray *elements, + guint n_expected, ...); + +void _nm_value_transforms_register (void); + +/***********************************************************/ + +typedef struct NMUtilsPrivateData { + const char * (*nm_setting_ip4_config_get_address_label) (NMSettingIP4Config *setting, + guint32 i); + gboolean (*nm_setting_ip4_config_add_address_with_label) (NMSettingIP4Config *setting, + NMIP4Address *address, + const char *label); +} NMUtilsPrivateData; + +const NMUtilsPrivateData *nm_utils_get_private (void); + +/** + * NM_UTILS_PRIVATE_CALL: + * @call: a call to a private libnm-util function + * + * Used to call private libnm-util functions. Eg, if there was a + * private function called nm_foo_get_bar(), you could call it like: + * + * bar = NM_UTILS_PRIVATE_CALL (nm_foo_get_bar (foo, x, y, z)); + * + * This macro only exists inside the NetworkManager source tree and + * is not part of the public API. + * + * Since: 0.9.10 + */ +#define NM_UTILS_PRIVATE_CALL(call) (nm_utils_get_private ()->call) + +#endif diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c new file mode 100644 index 0000000000..753edd8473 --- /dev/null +++ b/libnm-core/nm-utils.c @@ -0,0 +1,2531 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2005 - 2013 Red Hat, Inc. + */ + +#include "config.h" + +#include <string.h> +#include <stdlib.h> +#include <netinet/ether.h> +#include <linux/if_infiniband.h> +#include <uuid/uuid.h> + +#include "nm-utils.h" +#include "nm-utils-private.h" +#include "nm-glib-compat.h" +#include "nm-dbus-glib-types.h" +#include "nm-setting-private.h" +#include "crypto.h" + +/** + * SECTION:nm-utils + * @short_description: Utility functions + * @include: nm-utils.h + * + * A collection of utility functions for working with SSIDs, IP addresses, Wi-Fi + * access points and devices, among other things. + */ + +struct EncodingTriplet +{ + const char *encoding1; + const char *encoding2; + const char *encoding3; +}; + +struct IsoLangToEncodings +{ + const char * lang; + struct EncodingTriplet encodings; +}; + +/* 5-letter language codes */ +static const struct IsoLangToEncodings isoLangEntries5[] = +{ + /* Simplified Chinese */ + { "zh_cn", {"euc-cn", "gb2312", "gb18030"} }, /* PRC */ + { "zh_sg", {"euc-cn", "gb2312", "gb18030"} }, /* Singapore */ + + /* Traditional Chinese */ + { "zh_tw", {"big5", "euc-tw", NULL} }, /* Taiwan */ + { "zh_hk", {"big5", "euc-tw", "big5-hkcs"} },/* Hong Kong */ + { "zh_mo", {"big5", "euc-tw", NULL} }, /* Macau */ + + /* Table end */ + { NULL, {NULL, NULL, NULL} } +}; + +/* 2-letter language codes; we don't care about the other 3 in this table */ +static const struct IsoLangToEncodings isoLangEntries2[] = +{ + /* Japanese */ + { "ja", {"euc-jp", "shift_jis", "iso-2022-jp"} }, + + /* Korean */ + { "ko", {"euc-kr", "iso-2022-kr", "johab"} }, + + /* Thai */ + { "th", {"iso-8859-11","windows-874", NULL} }, + + /* Central European */ + { "hu", {"iso-8859-2", "windows-1250", NULL} }, /* Hungarian */ + { "cs", {"iso-8859-2", "windows-1250", NULL} }, /* Czech */ + { "hr", {"iso-8859-2", "windows-1250", NULL} }, /* Croatian */ + { "pl", {"iso-8859-2", "windows-1250", NULL} }, /* Polish */ + { "ro", {"iso-8859-2", "windows-1250", NULL} }, /* Romanian */ + { "sk", {"iso-8859-2", "windows-1250", NULL} }, /* Slovakian */ + { "sl", {"iso-8859-2", "windows-1250", NULL} }, /* Slovenian */ + { "sh", {"iso-8859-2", "windows-1250", NULL} }, /* Serbo-Croatian */ + + /* Cyrillic */ + { "ru", {"koi8-r", "windows-1251", "iso-8859-5"} }, /* Russian */ + { "be", {"koi8-r", "windows-1251", "iso-8859-5"} }, /* Belorussian */ + { "bg", {"windows-1251","koi8-r", "iso-8859-5"} }, /* Bulgarian */ + { "mk", {"koi8-r", "windows-1251", "iso-8859-5"} }, /* Macedonian */ + { "sr", {"koi8-r", "windows-1251", "iso-8859-5"} }, /* Serbian */ + { "uk", {"koi8-u", "koi8-r", "windows-1251"} }, /* Ukranian */ + + /* Arabic */ + { "ar", {"iso-8859-6", "windows-1256", NULL} }, + + /* Baltic */ + { "et", {"iso-8859-4", "windows-1257", NULL} }, /* Estonian */ + { "lt", {"iso-8859-4", "windows-1257", NULL} }, /* Lithuanian */ + { "lv", {"iso-8859-4", "windows-1257", NULL} }, /* Latvian */ + + /* Greek */ + { "el", {"iso-8859-7", "windows-1253", NULL} }, + + /* Hebrew */ + { "he", {"iso-8859-8", "windows-1255", NULL} }, + { "iw", {"iso-8859-8", "windows-1255", NULL} }, + + /* Turkish */ + { "tr", {"iso-8859-9", "windows-1254", NULL} }, + + /* Table end */ + { NULL, {NULL, NULL, NULL} } +}; + + +static GHashTable * langToEncodings5 = NULL; +static GHashTable * langToEncodings2 = NULL; + +static void +init_lang_to_encodings_hash (void) +{ + struct IsoLangToEncodings *enc; + + if (G_UNLIKELY (langToEncodings5 == NULL)) { + /* Five-letter codes */ + enc = (struct IsoLangToEncodings *) &isoLangEntries5[0]; + langToEncodings5 = g_hash_table_new (g_str_hash, g_str_equal); + while (enc->lang) { + g_hash_table_insert (langToEncodings5, (gpointer) enc->lang, + (gpointer) &enc->encodings); + enc++; + } + } + + if (G_UNLIKELY (langToEncodings2 == NULL)) { + /* Two-letter codes */ + enc = (struct IsoLangToEncodings *) &isoLangEntries2[0]; + langToEncodings2 = g_hash_table_new (g_str_hash, g_str_equal); + while (enc->lang) { + g_hash_table_insert (langToEncodings2, (gpointer) enc->lang, + (gpointer) &enc->encodings); + enc++; + } + } +} + + +static gboolean +get_encodings_for_lang (const char *lang, + char **encoding1, + char **encoding2, + char **encoding3) +{ + struct EncodingTriplet * encodings; + gboolean success = FALSE; + char * tmp_lang; + + g_return_val_if_fail (lang != NULL, FALSE); + g_return_val_if_fail (encoding1 != NULL, FALSE); + g_return_val_if_fail (encoding2 != NULL, FALSE); + g_return_val_if_fail (encoding3 != NULL, FALSE); + + *encoding1 = "iso-8859-1"; + *encoding2 = "windows-1251"; + *encoding3 = NULL; + + init_lang_to_encodings_hash (); + + tmp_lang = g_strdup (lang); + if ((encodings = g_hash_table_lookup (langToEncodings5, tmp_lang))) { + *encoding1 = (char *) encodings->encoding1; + *encoding2 = (char *) encodings->encoding2; + *encoding3 = (char *) encodings->encoding3; + success = TRUE; + } + + /* Truncate tmp_lang to length of 2 */ + if (strlen (tmp_lang) > 2) + tmp_lang[2] = '\0'; + if (!success && (encodings = g_hash_table_lookup (langToEncodings2, tmp_lang))) { + *encoding1 = (char *) encodings->encoding1; + *encoding2 = (char *) encodings->encoding2; + *encoding3 = (char *) encodings->encoding3; + success = TRUE; + } + + g_free (tmp_lang); + return success; +} + +/* init, deinit for libnm_util */ + +static gboolean initialized = FALSE; + +/** + * nm_utils_init: + * @error: location to store error, or %NULL + * + * Initializes libnm-util; should be called when starting and program that + * uses libnm-util. Sets up an atexit() handler to ensure de-initialization + * is performed, but calling nm_utils_deinit() to explicitly deinitialize + * libnm-util can also be done. This function can be called more than once. + * + * Returns: %TRUE if the initialization was successful, %FALSE on failure. + **/ +gboolean +nm_utils_init (GError **error) +{ + if (!initialized) { + initialized = TRUE; + + if (!crypto_init (error)) + return FALSE; + + _nm_value_transforms_register (); + } + return TRUE; +} + +/** + * nm_utils_deinit: + * + * Frees all resources used internally by libnm-util. This function is called + * from an atexit() handler, set up by nm_utils_init(), but is safe to be called + * more than once. Subsequent calls have no effect until nm_utils_init() is + * called again. + **/ +void +nm_utils_deinit (void) +{ + if (initialized) { + crypto_deinit (); + initialized = FALSE; + } +} + +/* ssid helpers */ + +/** + * nm_utils_ssid_to_utf8: + * @ssid: a byte array containing the SSID data + * + * Wi-Fi SSIDs are byte arrays, they are _not_ strings. Thus, an SSID may + * contain embedded NULLs and other unprintable characters. Often it is + * useful to print the SSID out for debugging purposes, but that should be the + * _only_ use of this function. Do not use this function for any persistent + * storage of the SSID, since the printable SSID returned from this function + * cannot be converted back into the real SSID of the access point. + * + * This function does almost everything humanly possible to convert the input + * into a printable UTF-8 string, using roughly the following procedure: + * + * 1) if the input data is already UTF-8 safe, no conversion is performed + * 2) attempts to get the current system language from the LANG environment + * variable, and depending on the language, uses a table of alternative + * encodings to try. For example, if LANG=hu_HU, the table may first try + * the ISO-8859-2 encoding, and if that fails, try the Windows-1250 encoding. + * If all fallback encodings fail, replaces non-UTF-8 characters with '?'. + * 3) If the system language was unable to be determined, falls back to the + * ISO-8859-1 encoding, then to the Windows-1251 encoding. + * 4) If step 3 fails, replaces non-UTF-8 characters with '?'. + * + * Again, this function should be used for debugging and display purposes + * _only_. + * + * Returns: (transfer full): an allocated string containing a UTF-8 + * representation of the SSID, which must be freed by the caller using g_free(). + * Returns %NULL on errors. + **/ +char * +nm_utils_ssid_to_utf8 (const GByteArray *ssid) +{ + char *converted = NULL; + char *lang, *e1 = NULL, *e2 = NULL, *e3 = NULL; + + g_return_val_if_fail (ssid != NULL, NULL); + + if (g_utf8_validate ((const gchar *) ssid->data, ssid->len, NULL)) + return g_strndup ((const gchar *) ssid->data, ssid->len); + + /* LANG may be a good encoding hint */ + g_get_charset ((const char **)(&e1)); + if ((lang = getenv ("LANG"))) { + char * dot; + + lang = g_ascii_strdown (lang, -1); + if ((dot = strchr (lang, '.'))) + *dot = '\0'; + + get_encodings_for_lang (lang, &e1, &e2, &e3); + g_free (lang); + } + + converted = g_convert ((const gchar *) ssid->data, ssid->len, "UTF-8", e1, NULL, NULL, NULL); + if (!converted && e2) + converted = g_convert ((const gchar *) ssid->data, ssid->len, "UTF-8", e2, NULL, NULL, NULL); + + if (!converted && e3) + converted = g_convert ((const gchar *) ssid->data, ssid->len, "UTF-8", e3, NULL, NULL, NULL); + + if (!converted) { + converted = g_convert_with_fallback ((const gchar *) ssid->data, ssid->len, + "UTF-8", e1, "?", NULL, NULL, NULL); + } + + return converted; +} + +/* Shamelessly ripped from the Linux kernel ieee80211 stack */ +/** + * nm_utils_is_empty_ssid: + * @ssid: pointer to a buffer containing the SSID data + * @len: length of the SSID data in @ssid + * + * Different manufacturers use different mechanisms for not broadcasting the + * AP's SSID. This function attempts to detect blank/empty SSIDs using a + * number of known SSID-cloaking methods. + * + * Returns: %TRUE if the SSID is "empty", %FALSE if it is not + **/ +gboolean +nm_utils_is_empty_ssid (const guint8 * ssid, int len) +{ + /* Single white space is for Linksys APs */ + if (len == 1 && ssid[0] == ' ') + return TRUE; + + /* Otherwise, if the entire ssid is 0, we assume it is hidden */ + while (len--) { + if (ssid[len] != '\0') + return FALSE; + } + return TRUE; +} + +#define ESSID_MAX_SIZE 32 + +/** + * nm_utils_escape_ssid: + * @ssid: pointer to a buffer containing the SSID data + * @len: length of the SSID data in @ssid + * + * This function does a quick printable character conversion of the SSID, simply + * replacing embedded NULLs and non-printable characters with the hexadecimal + * representation of that character. Intended for debugging only, should not + * be used for display of SSIDs. + * + * Returns: pointer to the escaped SSID, which uses an internal static buffer + * and will be overwritten by subsequent calls to this function + **/ +const char * +nm_utils_escape_ssid (const guint8 * ssid, guint32 len) +{ + static char escaped[ESSID_MAX_SIZE * 2 + 1]; + const guint8 *s = ssid; + char *d = escaped; + + if (nm_utils_is_empty_ssid (ssid, len)) { + memcpy (escaped, "<hidden>", sizeof ("<hidden>")); + return escaped; + } + + len = MIN (len, (guint32) ESSID_MAX_SIZE); + while (len--) { + if (*s == '\0') { + *d++ = '\\'; + *d++ = '0'; + s++; + } else { + *d++ = *s++; + } + } + *d = '\0'; + return escaped; +} + +/** + * nm_utils_same_ssid: + * @ssid1: first SSID data to compare + * @ssid2: second SSID data to compare + * @ignore_trailing_null: %TRUE to ignore one trailing NULL byte + * + * Earlier versions of the Linux kernel added a NULL byte to the end of the + * SSID to enable easy printing of the SSID on the console or in a terminal, + * but this behavior was problematic (SSIDs are simply byte arrays, not strings) + * and thus was changed. This function compensates for that behavior at the + * cost of some compatibility with odd SSIDs that may legitimately have trailing + * NULLs, even though that is functionally pointless. + * + * Returns: %TRUE if the SSIDs are the same, %FALSE if they are not + **/ +gboolean +nm_utils_same_ssid (const GByteArray * ssid1, + const GByteArray * ssid2, + gboolean ignore_trailing_null) +{ + guint32 ssid1_len, ssid2_len; + + if (ssid1 == ssid2) + return TRUE; + if (!ssid1 || !ssid2) + return FALSE; + + ssid1_len = ssid1->len; + ssid2_len = ssid2->len; + if (ssid1_len && ssid2_len && ignore_trailing_null) { + if (ssid1->data[ssid1_len - 1] == '\0') + ssid1_len--; + if (ssid2->data[ssid2_len - 1] == '\0') + ssid2_len--; + } + + if (ssid1_len != ssid2_len) + return FALSE; + + return memcmp (ssid1->data, ssid2->data, ssid1_len) == 0 ? TRUE : FALSE; +} + +static void +value_destroy (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +static void +value_dup (gpointer key, gpointer val, gpointer user_data) +{ + GHashTable *table = (GHashTable *) user_data; + GValue *value = (GValue *) val; + GValue *dup_value; + + dup_value = g_slice_new0 (GValue); + g_value_init (dup_value, G_VALUE_TYPE (val)); + g_value_copy (value, dup_value); + + g_hash_table_insert (table, g_strdup ((char *) key), dup_value); +} + +/** + * nm_utils_gvalue_hash_dup: + * @hash: a #GHashTable mapping string:GValue + * + * Utility function to duplicate a hash table of #GValues. + * + * Returns: (transfer container) (element-type utf8 GObject.Value): a newly allocated duplicated #GHashTable, caller must free the + * returned hash with g_hash_table_unref() or g_hash_table_destroy() + **/ +GHashTable * +nm_utils_gvalue_hash_dup (GHashTable *hash) +{ + GHashTable *table; + + g_return_val_if_fail (hash != NULL, NULL); + + table = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + value_destroy); + + g_hash_table_foreach (hash, value_dup, table); + + return table; +} + +/** + * nm_utils_slist_free: (skip) + * @list: a #GSList + * @elem_destroy_fn: user function called for each element in @list + * + * Utility function to free a #GSList. + * + * Deprecated: use g_slist_free_full(). + **/ +void +nm_utils_slist_free (GSList *list, GDestroyNotify elem_destroy_fn) +{ + g_slist_free_full (list, elem_destroy_fn); +} + +gboolean +_nm_utils_string_in_list (const char *str, const char **valid_strings) +{ + int i; + + for (i = 0; valid_strings[i]; i++) + if (strcmp (str, valid_strings[i]) == 0) + break; + + return valid_strings[i] != NULL; +} + +gboolean +_nm_utils_string_slist_validate (GSList *list, const char **valid_values) +{ + GSList *iter; + + for (iter = list; iter; iter = iter->next) { + if (!_nm_utils_string_in_list ((char *) iter->data, valid_values)) + return FALSE; + } + + return TRUE; +} + +gboolean +_nm_utils_gvalue_array_validate (GValueArray *elements, guint n_expected, ...) +{ + va_list args; + GValue *tmp; + int i; + gboolean valid = FALSE; + + if (n_expected != elements->n_values) + return FALSE; + + va_start (args, n_expected); + for (i = 0; i < n_expected; i++) { + tmp = g_value_array_get_nth (elements, i); + if (G_VALUE_TYPE (tmp) != va_arg (args, GType)) + goto done; + } + valid = TRUE; + +done: + va_end (args); + return valid; +} + +static gboolean +device_supports_ap_ciphers (guint32 dev_caps, + guint32 ap_flags, + gboolean static_wep) +{ + gboolean have_pair = FALSE; + gboolean have_group = FALSE; + /* Device needs to support at least one pairwise and one group cipher */ + + /* Pairwise */ + if (static_wep) { + /* Static WEP only uses group ciphers */ + have_pair = TRUE; + } else { + if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP40) + if (ap_flags & NM_802_11_AP_SEC_PAIR_WEP40) + have_pair = TRUE; + if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP104) + if (ap_flags & NM_802_11_AP_SEC_PAIR_WEP104) + have_pair = TRUE; + if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP) + if (ap_flags & NM_802_11_AP_SEC_PAIR_TKIP) + have_pair = TRUE; + if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP) + if (ap_flags & NM_802_11_AP_SEC_PAIR_CCMP) + have_pair = TRUE; + } + + /* Group */ + if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP40) + if (ap_flags & NM_802_11_AP_SEC_GROUP_WEP40) + have_group = TRUE; + if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP104) + if (ap_flags & NM_802_11_AP_SEC_GROUP_WEP104) + have_group = TRUE; + if (!static_wep) { + if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP) + if (ap_flags & NM_802_11_AP_SEC_GROUP_TKIP) + have_group = TRUE; + if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP) + if (ap_flags & NM_802_11_AP_SEC_GROUP_CCMP) + have_group = TRUE; + } + + return (have_pair && have_group); +} + +/** + * nm_utils_ap_mode_security_valid: + * @type: the security type to check device capabilties against, + * e.g. #NMU_SEC_STATIC_WEP + * @wifi_caps: bitfield of the capabilities of the specific Wi-Fi device, e.g. + * #NM_WIFI_DEVICE_CAP_CIPHER_WEP40 + * + * Given a set of device capabilities, and a desired security type to check + * against, determines whether the combination of device capabilities and + * desired security type are valid for AP/Hotspot connections. + * + * Returns: %TRUE if the device capabilities are compatible with the desired + * @type, %FALSE if they are not. + * + * Since: 0.9.8 + **/ +gboolean +nm_utils_ap_mode_security_valid (NMUtilsSecurityType type, + NMDeviceWifiCapabilities wifi_caps) +{ + if (!(wifi_caps & NM_WIFI_DEVICE_CAP_AP)) + return FALSE; + + /* Return TRUE for any security that wpa_supplicant's lightweight AP + * mode can handle: which is open, WEP, and WPA/WPA2 PSK. + */ + switch (type) { + case NMU_SEC_NONE: + case NMU_SEC_STATIC_WEP: + case NMU_SEC_WPA_PSK: + case NMU_SEC_WPA2_PSK: + return TRUE; + default: + break; + } + return FALSE; +} + +/** + * nm_utils_security_valid: + * @type: the security type to check AP flags and device capabilties against, + * e.g. #NMU_SEC_STATIC_WEP + * @wifi_caps: bitfield of the capabilities of the specific Wi-Fi device, e.g. + * #NM_WIFI_DEVICE_CAP_CIPHER_WEP40 + * @have_ap: whether the @ap_flags, @ap_wpa, and @ap_rsn arguments are valid + * @adhoc: whether the capabilities being tested are from an Ad-Hoc AP (IBSS) + * @ap_flags: bitfield of AP capabilities, e.g. #NM_802_11_AP_FLAGS_PRIVACY + * @ap_wpa: bitfield of AP capabilties derived from the AP's WPA beacon, + * e.g. (#NM_802_11_AP_SEC_PAIR_TKIP | #NM_802_11_AP_SEC_KEY_MGMT_PSK) + * @ap_rsn: bitfield of AP capabilties derived from the AP's RSN/WPA2 beacon, + * e.g. (#NM_802_11_AP_SEC_PAIR_CCMP | #NM_802_11_AP_SEC_PAIR_TKIP) + * + * Given a set of device capabilities, and a desired security type to check + * against, determines whether the combination of device, desired security + * type, and AP capabilities intersect. + * + * NOTE: this function cannot handle checking security for AP/Hotspot mode; + * use nm_utils_ap_mode_security_valid() instead. + * + * Returns: %TRUE if the device capabilities and AP capabilties intersect and are + * compatible with the desired @type, %FALSE if they are not + **/ +gboolean +nm_utils_security_valid (NMUtilsSecurityType type, + NMDeviceWifiCapabilities wifi_caps, + gboolean have_ap, + gboolean adhoc, + NM80211ApFlags ap_flags, + NM80211ApSecurityFlags ap_wpa, + NM80211ApSecurityFlags ap_rsn) +{ + gboolean good = TRUE; + + if (!have_ap) { + if (type == NMU_SEC_NONE) + return TRUE; + if ( (type == NMU_SEC_STATIC_WEP) + || ((type == NMU_SEC_DYNAMIC_WEP) && !adhoc) + || ((type == NMU_SEC_LEAP) && !adhoc)) { + if (wifi_caps & (NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104)) + return TRUE; + else + return FALSE; + } + } + + switch (type) { + case NMU_SEC_NONE: + g_assert (have_ap); + if (ap_flags & NM_802_11_AP_FLAGS_PRIVACY) + return FALSE; + if (ap_wpa || ap_rsn) + return FALSE; + break; + case NMU_SEC_LEAP: /* require PRIVACY bit for LEAP? */ + if (adhoc) + return FALSE; + /* Fall through */ + case NMU_SEC_STATIC_WEP: + g_assert (have_ap); + if (!(ap_flags & NM_802_11_AP_FLAGS_PRIVACY)) + return FALSE; + if (ap_wpa || ap_rsn) { + if (!device_supports_ap_ciphers (wifi_caps, ap_wpa, TRUE)) + if (!device_supports_ap_ciphers (wifi_caps, ap_rsn, TRUE)) + return FALSE; + } + break; + case NMU_SEC_DYNAMIC_WEP: + if (adhoc) + return FALSE; + g_assert (have_ap); + if (ap_rsn || !(ap_flags & NM_802_11_AP_FLAGS_PRIVACY)) + return FALSE; + /* Some APs broadcast minimal WPA-enabled beacons that must be handled */ + if (ap_wpa) { + if (!(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) + return FALSE; + if (!device_supports_ap_ciphers (wifi_caps, ap_wpa, FALSE)) + return FALSE; + } + break; + case NMU_SEC_WPA_PSK: + if (adhoc) + return FALSE; /* FIXME: Kernel WPA Ad-Hoc support is buggy */ + if (!(wifi_caps & NM_WIFI_DEVICE_CAP_WPA)) + return FALSE; + if (have_ap) { + /* Ad-Hoc WPA APs won't necessarily have the PSK flag set, and + * they don't have any pairwise ciphers. */ + if (adhoc) { + /* coverity[dead_error_line] */ + if ( (ap_wpa & NM_802_11_AP_SEC_GROUP_TKIP) + && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP)) + return TRUE; + if ( (ap_wpa & NM_802_11_AP_SEC_GROUP_CCMP) + && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP)) + return TRUE; + } else { + if (ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_PSK) { + if ( (ap_wpa & NM_802_11_AP_SEC_PAIR_TKIP) + && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP)) + return TRUE; + if ( (ap_wpa & NM_802_11_AP_SEC_PAIR_CCMP) + && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP)) + return TRUE; + } + } + return FALSE; + } + break; + case NMU_SEC_WPA2_PSK: + if (adhoc) + return FALSE; /* FIXME: Kernel WPA Ad-Hoc support is buggy */ + if (!(wifi_caps & NM_WIFI_DEVICE_CAP_RSN)) + return FALSE; + if (have_ap) { + /* Ad-Hoc WPA APs won't necessarily have the PSK flag set, and + * they don't have any pairwise ciphers, nor any RSA flags yet. */ + if (adhoc) { + /* coverity[dead_error_line] */ + if (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP) + return TRUE; + if (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP) + return TRUE; + } else { + if (ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_PSK) { + if ( (ap_rsn & NM_802_11_AP_SEC_PAIR_TKIP) + && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP)) + return TRUE; + if ( (ap_rsn & NM_802_11_AP_SEC_PAIR_CCMP) + && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP)) + return TRUE; + } + } + return FALSE; + } + break; + case NMU_SEC_WPA_ENTERPRISE: + if (adhoc) + return FALSE; + if (!(wifi_caps & NM_WIFI_DEVICE_CAP_WPA)) + return FALSE; + if (have_ap) { + if (!(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) + return FALSE; + /* Ensure at least one WPA cipher is supported */ + if (!device_supports_ap_ciphers (wifi_caps, ap_wpa, FALSE)) + return FALSE; + } + break; + case NMU_SEC_WPA2_ENTERPRISE: + if (adhoc) + return FALSE; + if (!(wifi_caps & NM_WIFI_DEVICE_CAP_RSN)) + return FALSE; + if (have_ap) { + if (!(ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) + return FALSE; + /* Ensure at least one WPA cipher is supported */ + if (!device_supports_ap_ciphers (wifi_caps, ap_rsn, FALSE)) + return FALSE; + } + break; + default: + good = FALSE; + break; + } + + return good; +} + +/** + * nm_utils_wep_key_valid: + * @key: a string that might be a WEP key + * @wep_type: the #NMWepKeyType type of the WEP key + * + * Checks if @key is a valid WEP key + * + * Returns: %TRUE if @key is a WEP key, %FALSE if not + * + * Since: 0.9.8 + */ +gboolean +nm_utils_wep_key_valid (const char *key, NMWepKeyType wep_type) +{ + int keylen, i; + + if (!key) + return FALSE; + + keylen = strlen (key); + if ( wep_type == NM_WEP_KEY_TYPE_KEY + || wep_type == NM_WEP_KEY_TYPE_UNKNOWN) { + if (keylen == 10 || keylen == 26) { + /* Hex key */ + for (i = 0; i < keylen; i++) { + if (!g_ascii_isxdigit (key[i])) + return FALSE; + } + } else if (keylen == 5 || keylen == 13) { + /* ASCII key */ + for (i = 0; i < keylen; i++) { + if (!g_ascii_isprint (key[i])) + return FALSE; + } + } else + return FALSE; + + } else if (wep_type == NM_WEP_KEY_TYPE_PASSPHRASE) { + if (!keylen || keylen > 64) + return FALSE; + } + + return TRUE; +} + +/** + * nm_utils_wpa_psk_valid: + * @psk: a string that might be a WPA PSK + * + * Checks if @psk is a valid WPA PSK + * + * Returns: %TRUE if @psk is a WPA PSK, %FALSE if not + * + * Since: 0.9.8 + */ +gboolean +nm_utils_wpa_psk_valid (const char *psk) +{ + int psklen, i; + + if (!psk) + return FALSE; + + psklen = strlen (psk); + if (psklen < 8 || psklen > 64) + return FALSE; + + if (psklen == 64) { + /* Hex PSK */ + for (i = 0; i < psklen; i++) { + if (!g_ascii_isxdigit (psk[i])) + return FALSE; + } + } + + return TRUE; +} + +/** + * nm_utils_ip4_addresses_from_gvalue: + * @value: #GValue containing a #GPtrArray of #GArrays of #guint32s + * + * Utility function to convert a #GPtrArray of #GArrays of #guint32s representing + * a list of NetworkManager IPv4 addresses (which is a tuple of address, gateway, + * and prefix) into a #GSList of #NMIP4Address objects. The specific format of + * this serialization is not guaranteed to be stable and the #GArray may be + * extended in the future. + * + * Returns: (transfer full) (element-type NMIP4Address): a newly allocated #GSList of #NMIP4Address objects + **/ +GSList * +nm_utils_ip4_addresses_from_gvalue (const GValue *value) +{ + GPtrArray *addresses; + int i; + GSList *list = NULL; + + addresses = (GPtrArray *) g_value_get_boxed (value); + for (i = 0; addresses && (i < addresses->len); i++) { + GArray *array = (GArray *) g_ptr_array_index (addresses, i); + NMIP4Address *addr; + + if (array->len < 3) { + g_warning ("Ignoring invalid IP4 address"); + continue; + } + + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, g_array_index (array, guint32, 0)); + nm_ip4_address_set_prefix (addr, g_array_index (array, guint32, 1)); + nm_ip4_address_set_gateway (addr, g_array_index (array, guint32, 2)); + list = g_slist_prepend (list, addr); + } + + return g_slist_reverse (list); +} + +/** + * nm_utils_ip4_addresses_to_gvalue: + * @list: (element-type NMIP4Address): a list of #NMIP4Address objects + * @value: a pointer to a #GValue into which to place the converted addresses, + * which should be unset by the caller (when no longer needed) with + * g_value_unset(). + * + * Utility function to convert a #GSList of #NMIP4Address objects into a + * #GPtrArray of #GArrays of #guint32s representing a list of NetworkManager IPv4 + * addresses (which is a tuple of address, gateway, and prefix). The specific + * format of this serialization is not guaranteed to be stable and may be + * extended in the future. + **/ +void +nm_utils_ip4_addresses_to_gvalue (GSList *list, GValue *value) +{ + GPtrArray *addresses; + GSList *iter; + + addresses = g_ptr_array_new (); + + for (iter = list; iter; iter = iter->next) { + NMIP4Address *addr = (NMIP4Address *) iter->data; + GArray *array; + guint32 tmp; + + array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3); + + tmp = nm_ip4_address_get_address (addr); + g_array_append_val (array, tmp); + + tmp = nm_ip4_address_get_prefix (addr); + g_array_append_val (array, tmp); + + tmp = nm_ip4_address_get_gateway (addr); + g_array_append_val (array, tmp); + + g_ptr_array_add (addresses, array); + } + + g_value_take_boxed (value, addresses); +} + +/** + * nm_utils_ip4_routes_from_gvalue: + * @value: #GValue containing a #GPtrArray of #GArrays of #guint32s + * + * Utility function to convert a #GPtrArray of #GArrays of #guint32s representing + * a list of NetworkManager IPv4 routes (which is a tuple of route, next hop, + * prefix, and metric) into a #GSList of #NMIP4Route objects. The specific + * format of this serialization is not guaranteed to be stable and may be + * extended in the future. + * + * Returns: (transfer full) (element-type NMIP4Route): a newly allocated #GSList of #NMIP4Route objects + **/ +GSList * +nm_utils_ip4_routes_from_gvalue (const GValue *value) +{ + GPtrArray *routes; + int i; + GSList *list = NULL; + + routes = (GPtrArray *) g_value_get_boxed (value); + for (i = 0; routes && (i < routes->len); i++) { + GArray *array = (GArray *) g_ptr_array_index (routes, i); + NMIP4Route *route; + + if (array->len < 4) { + g_warning ("Ignoring invalid IP4 route"); + continue; + } + + route = nm_ip4_route_new (); + nm_ip4_route_set_dest (route, g_array_index (array, guint32, 0)); + nm_ip4_route_set_prefix (route, g_array_index (array, guint32, 1)); + nm_ip4_route_set_next_hop (route, g_array_index (array, guint32, 2)); + nm_ip4_route_set_metric (route, g_array_index (array, guint32, 3)); + list = g_slist_prepend (list, route); + } + + return g_slist_reverse (list); +} + +/** + * nm_utils_ip4_routes_to_gvalue: + * @list: (element-type NMIP4Route): a list of #NMIP4Route objects + * @value: a pointer to a #GValue into which to place the converted routes, + * which should be unset by the caller (when no longer needed) with + * g_value_unset(). + * + * Utility function to convert a #GSList of #NMIP4Route objects into a + * #GPtrArray of #GArrays of #guint32s representing a list of NetworkManager IPv4 + * routes (which is a tuple of route, next hop, prefix, and metric). The + * specific format of this serialization is not guaranteed to be stable and may + * be extended in the future. + **/ +void +nm_utils_ip4_routes_to_gvalue (GSList *list, GValue *value) +{ + GPtrArray *routes; + GSList *iter; + + routes = g_ptr_array_new (); + + for (iter = list; iter; iter = iter->next) { + NMIP4Route *route = (NMIP4Route *) iter->data; + GArray *array; + guint32 tmp; + + array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3); + + tmp = nm_ip4_route_get_dest (route); + g_array_append_val (array, tmp); + + tmp = nm_ip4_route_get_prefix (route); + g_array_append_val (array, tmp); + + tmp = nm_ip4_route_get_next_hop (route); + g_array_append_val (array, tmp); + + tmp = nm_ip4_route_get_metric (route); + g_array_append_val (array, tmp); + + g_ptr_array_add (routes, array); + } + + g_value_take_boxed (value, routes); +} + +/** + * nm_utils_ip4_netmask_to_prefix: + * @netmask: an IPv4 netmask in network byte order + * + * Returns: the CIDR prefix represented by the netmask + **/ +guint32 +nm_utils_ip4_netmask_to_prefix (guint32 netmask) +{ + guint32 prefix; + guint8 v; + const guint8 *p = (guint8 *) &netmask; + + if (p[3]) { + prefix = 24; + v = p[3]; + } else if (p[2]) { + prefix = 16; + v = p[2]; + } else if (p[1]) { + prefix = 8; + v = p[1]; + } else { + prefix = 0; + v = p[0]; + } + + while (v) { + prefix++; + v <<= 1; + } + + return prefix; +} + +/** + * nm_utils_ip4_prefix_to_netmask: + * @prefix: a CIDR prefix + * + * Returns: the netmask represented by the prefix, in network byte order + **/ +guint32 +nm_utils_ip4_prefix_to_netmask (guint32 prefix) +{ + return prefix < 32 ? ~htonl(0xFFFFFFFF >> prefix) : 0xFFFFFFFF; +} + + +/** + * nm_utils_ip4_get_default_prefix: + * @ip: an IPv4 address (in network byte order) + * + * When the Internet was originally set up, various ranges of IP addresses were + * segmented into three network classes: A, B, and C. This function will return + * a prefix that is associated with the IP address specified defining where it + * falls in the predefined classes. + * + * Returns: the default class prefix for the given IP + **/ +/* The function is originally from ipcalc.c of Red Hat's initscripts. */ +guint32 +nm_utils_ip4_get_default_prefix (guint32 ip) +{ + if (((ntohl (ip) & 0xFF000000) >> 24) <= 127) + return 8; /* Class A - 255.0.0.0 */ + else if (((ntohl (ip) & 0xFF000000) >> 24) <= 191) + return 16; /* Class B - 255.255.0.0 */ + + return 24; /* Class C - 255.255.255.0 */ +} + +/** + * nm_utils_ip6_addresses_from_gvalue: + * @value: gvalue containing a GPtrArray of GValueArrays of (GArray of guchars) and #guint32 + * + * Utility function to convert a #GPtrArray of #GValueArrays of (#GArray of guchars) and #guint32 + * representing a list of NetworkManager IPv6 addresses (which is a tuple of address, + * prefix, and gateway), into a #GSList of #NMIP6Address objects. The specific format of + * this serialization is not guaranteed to be stable and the #GValueArray may be + * extended in the future. + * + * Returns: (transfer full) (element-type NMIP6Address): a newly allocated #GSList of #NMIP6Address objects + **/ +GSList * +nm_utils_ip6_addresses_from_gvalue (const GValue *value) +{ + GPtrArray *addresses; + int i; + GSList *list = NULL; + + addresses = (GPtrArray *) g_value_get_boxed (value); + + for (i = 0; addresses && (i < addresses->len); i++) { + GValueArray *elements = (GValueArray *) g_ptr_array_index (addresses, i); + GValue *tmp; + GByteArray *ba_addr; + GByteArray *ba_gw = NULL; + NMIP6Address *addr; + guint32 prefix; + + if (elements->n_values < 2 || elements->n_values > 3) { + g_warning ("%s: ignoring invalid IP6 address structure", __func__); + continue; + } + + /* Third element (gateway) is optional */ + if ( !_nm_utils_gvalue_array_validate (elements, 2, DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT) + && !_nm_utils_gvalue_array_validate (elements, 3, DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, DBUS_TYPE_G_UCHAR_ARRAY)) { + g_warning ("%s: ignoring invalid IP6 address structure", __func__); + continue; + } + + tmp = g_value_array_get_nth (elements, 0); + ba_addr = g_value_get_boxed (tmp); + if (ba_addr->len != 16) { + g_warning ("%s: ignoring invalid IP6 address of length %d", + __func__, ba_addr->len); + continue; + } + + tmp = g_value_array_get_nth (elements, 1); + prefix = g_value_get_uint (tmp); + if (prefix > 128) { + g_warning ("%s: ignoring invalid IP6 prefix %d", + __func__, prefix); + continue; + } + + if (elements->n_values == 3) { + tmp = g_value_array_get_nth (elements, 2); + ba_gw = g_value_get_boxed (tmp); + if (ba_gw->len != 16) { + g_warning ("%s: ignoring invalid IP6 gateway address of length %d", + __func__, ba_gw->len); + continue; + } + } + + addr = nm_ip6_address_new (); + nm_ip6_address_set_prefix (addr, prefix); + nm_ip6_address_set_address (addr, (const struct in6_addr *) ba_addr->data); + if (ba_gw) + nm_ip6_address_set_gateway (addr, (const struct in6_addr *) ba_gw->data); + + list = g_slist_prepend (list, addr); + } + + return g_slist_reverse (list); +} + +/** + * nm_utils_ip6_addresses_to_gvalue: + * @list: (element-type NMIP6Address): a list of #NMIP6Address objects + * @value: a pointer to a #GValue into which to place the converted addresses, + * which should be unset by the caller (when no longer needed) with + * g_value_unset(). + * + * Utility function to convert a #GSList of #NMIP6Address objects into a + * #GPtrArray of #GValueArrays representing a list of NetworkManager IPv6 addresses + * (which is a tuple of address, prefix, and gateway). The specific format of + * this serialization is not guaranteed to be stable and may be extended in the + * future. + **/ +void +nm_utils_ip6_addresses_to_gvalue (GSList *list, GValue *value) +{ + GPtrArray *addresses; + GSList *iter; + + addresses = g_ptr_array_new (); + + for (iter = list; iter; iter = iter->next) { + NMIP6Address *addr = (NMIP6Address *) iter->data; + GValueArray *array; + GValue element = G_VALUE_INIT; + GByteArray *ba; + + array = g_value_array_new (3); + + /* IP address */ + g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY); + ba = g_byte_array_new (); + g_byte_array_append (ba, (guint8 *) nm_ip6_address_get_address (addr), 16); + g_value_take_boxed (&element, ba); + g_value_array_append (array, &element); + g_value_unset (&element); + + /* Prefix */ + g_value_init (&element, G_TYPE_UINT); + g_value_set_uint (&element, nm_ip6_address_get_prefix (addr)); + g_value_array_append (array, &element); + g_value_unset (&element); + + /* Gateway */ + g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY); + ba = g_byte_array_new (); + g_byte_array_append (ba, (guint8 *) nm_ip6_address_get_gateway (addr), 16); + g_value_take_boxed (&element, ba); + g_value_array_append (array, &element); + g_value_unset (&element); + + g_ptr_array_add (addresses, array); + } + + g_value_take_boxed (value, addresses); +} + +/** + * nm_utils_ip6_routes_from_gvalue: + * @value: #GValue containing a #GPtrArray of #GValueArrays of (#GArray of #guchars), #guint32, + * (#GArray of #guchars), and #guint32 + * + * Utility function #GPtrArray of #GValueArrays of (#GArray of #guchars), #guint32, + * (#GArray of #guchars), and #guint32 representing a list of NetworkManager IPv6 + * routes (which is a tuple of destination, prefix, next hop, and metric) + * into a #GSList of #NMIP6Route objects. The specific format of this serialization + * is not guaranteed to be stable and may be extended in the future. + * + * Returns: (transfer full) (element-type NMIP6Route): a newly allocated #GSList of #NMIP6Route objects + **/ +GSList * +nm_utils_ip6_routes_from_gvalue (const GValue *value) +{ + GPtrArray *routes; + int i; + GSList *list = NULL; + + routes = (GPtrArray *) g_value_get_boxed (value); + for (i = 0; routes && (i < routes->len); i++) { + GValueArray *route_values = (GValueArray *) g_ptr_array_index (routes, i); + GByteArray *dest, *next_hop; + guint prefix, metric; + NMIP6Route *route; + + if (!_nm_utils_gvalue_array_validate (route_values, 4, + DBUS_TYPE_G_UCHAR_ARRAY, + G_TYPE_UINT, + DBUS_TYPE_G_UCHAR_ARRAY, + G_TYPE_UINT)) { + g_warning ("Ignoring invalid IP6 route"); + continue; + } + + dest = g_value_get_boxed (g_value_array_get_nth (route_values, 0)); + if (dest->len != 16) { + g_warning ("%s: ignoring invalid IP6 dest address of length %d", + __func__, dest->len); + continue; + } + + prefix = g_value_get_uint (g_value_array_get_nth (route_values, 1)); + + next_hop = g_value_get_boxed (g_value_array_get_nth (route_values, 2)); + if (next_hop->len != 16) { + g_warning ("%s: ignoring invalid IP6 next_hop address of length %d", + __func__, next_hop->len); + continue; + } + + metric = g_value_get_uint (g_value_array_get_nth (route_values, 3)); + + route = nm_ip6_route_new (); + nm_ip6_route_set_dest (route, (struct in6_addr *)dest->data); + nm_ip6_route_set_prefix (route, prefix); + nm_ip6_route_set_next_hop (route, (struct in6_addr *)next_hop->data); + nm_ip6_route_set_metric (route, metric); + list = g_slist_prepend (list, route); + } + + return g_slist_reverse (list); +} + +/** + * nm_utils_ip6_routes_to_gvalue: + * @list: (element-type NMIP6Route): a list of #NMIP6Route objects + * @value: a pointer to a #GValue into which to place the converted routes, + * which should be unset by the caller (when no longer needed) with + * g_value_unset(). + * + * Utility function to convert a #GSList of #NMIP6Route objects into a #GPtrArray of + * #GValueArrays of (#GArray of #guchars), #guint32, (#GArray of #guchars), and #guint32 + * representing a list of NetworkManager IPv6 routes (which is a tuple of destination, + * prefix, next hop, and metric). The specific format of this serialization is not + * guaranteed to be stable and may be extended in the future. + **/ +void +nm_utils_ip6_routes_to_gvalue (GSList *list, GValue *value) +{ + GPtrArray *routes; + GSList *iter; + + routes = g_ptr_array_new (); + + for (iter = list; iter; iter = iter->next) { + NMIP6Route *route = (NMIP6Route *) iter->data; + GValueArray *array; + const struct in6_addr *addr; + GByteArray *ba; + GValue element = G_VALUE_INIT; + + array = g_value_array_new (4); + + g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY); + addr = nm_ip6_route_get_dest (route); + ba = g_byte_array_new (); + g_byte_array_append (ba, (guchar *)addr, sizeof (*addr)); + g_value_take_boxed (&element, ba); + g_value_array_append (array, &element); + g_value_unset (&element); + + g_value_init (&element, G_TYPE_UINT); + g_value_set_uint (&element, nm_ip6_route_get_prefix (route)); + g_value_array_append (array, &element); + g_value_unset (&element); + + g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY); + addr = nm_ip6_route_get_next_hop (route); + ba = g_byte_array_new (); + g_byte_array_append (ba, (guchar *)addr, sizeof (*addr)); + g_value_take_boxed (&element, ba); + g_value_array_append (array, &element); + g_value_unset (&element); + + g_value_init (&element, G_TYPE_UINT); + g_value_set_uint (&element, nm_ip6_route_get_metric (route)); + g_value_array_append (array, &element); + g_value_unset (&element); + + g_ptr_array_add (routes, array); + } + + g_value_take_boxed (value, routes); +} + +/** + * nm_utils_ip6_dns_from_gvalue: (skip) + * @value: a #GValue + * + * Converts a #GValue containing a #GPtrArray of IP6 DNS, represented as + * #GByteArrays into a #GSList of <literal><type>struct in6_addr</type></literal>s. + * + * Returns: a #GSList of IP6 addresses. + */ +GSList * +nm_utils_ip6_dns_from_gvalue (const GValue *value) +{ + GPtrArray *dns; + int i; + GSList *list = NULL; + + dns = (GPtrArray *) g_value_get_boxed (value); + for (i = 0; dns && (i < dns->len); i++) { + GByteArray *bytearray = (GByteArray *) g_ptr_array_index (dns, i); + struct in6_addr *addr; + + if (bytearray->len != 16) { + g_warning ("%s: ignoring invalid IP6 address of length %d", + __func__, bytearray->len); + continue; + } + + addr = g_malloc0 (sizeof (struct in6_addr)); + memcpy (addr->s6_addr, bytearray->data, bytearray->len); + list = g_slist_prepend (list, addr); + } + + return g_slist_reverse (list); +} + +/** + * nm_utils_ip6_dns_to_gvalue: (skip) + * @list: a list of #NMIP6Route objects + * @value: a pointer to a #GValue into which to place the converted DNS server + * addresses, which should be unset by the caller (when no longer needed) with + * g_value_unset(). + * + * Utility function to convert a #GSList of <literal><type>struct + * in6_addr</type></literal> structs into a #GPtrArray of #GByteArrays + * representing each server's IPv6 addresses in network byte order. + * The specific format of this serialization is not guaranteed to be + * stable and may be extended in the future. + */ +void +nm_utils_ip6_dns_to_gvalue (GSList *list, GValue *value) +{ + GPtrArray *dns; + GSList *iter; + + dns = g_ptr_array_new (); + + for (iter = list; iter; iter = iter->next) { + struct in6_addr *addr = (struct in6_addr *) iter->data; + GByteArray *bytearray; + + bytearray = g_byte_array_sized_new (16); + g_byte_array_append (bytearray, (guint8 *) addr->s6_addr, 16); + g_ptr_array_add (dns, bytearray); + } + + g_value_take_boxed (value, dns); +} + +/** + * nm_utils_uuid_generate: + * + * Returns: a newly allocated UUID suitable for use as the #NMSettingConnection + * object's #NMSettingConnection:id: property. Should be freed with g_free() + **/ +char * +nm_utils_uuid_generate (void) +{ + uuid_t uuid; + char *buf; + + buf = g_malloc0 (37); + uuid_generate_random (uuid); + uuid_unparse_lower (uuid, &buf[0]); + return buf; +} + +/** + * nm_utils_uuid_generate_from_string: + * @s: a string to use as the seed for the UUID + * + * For a given @s, this function will always return the same UUID. + * + * Returns: a newly allocated UUID suitable for use as the #NMSettingConnection + * object's #NMSettingConnection:id: property + **/ +char * +nm_utils_uuid_generate_from_string (const char *s) +{ + GError *error = NULL; + uuid_t *uuid; + char *buf = NULL; + + if (!nm_utils_init (&error)) { + g_warning ("error initializing crypto: (%d) %s", + error ? error->code : 0, + error ? error->message : "unknown"); + if (error) + g_error_free (error); + return NULL; + } + + uuid = g_malloc0 (sizeof (*uuid)); + if (!crypto_md5_hash (NULL, 0, s, strlen (s), (char *) uuid, sizeof (*uuid), &error)) { + g_warning ("error generating UUID: (%d) %s", + error ? error->code : 0, + error ? error->message : "unknown"); + if (error) + g_error_free (error); + goto out; + } + + buf = g_malloc0 (37); + uuid_unparse_lower (*uuid, &buf[0]); + +out: + g_free (uuid); + return buf; +} + +static char * +make_key (const char *cipher, + const char *salt, + const gsize salt_len, + const char *password, + gsize *out_len, + GError **error) +{ + char *key; + guint32 digest_len = 24; /* DES-EDE3-CBC */ + + g_return_val_if_fail (salt != NULL, NULL); + g_return_val_if_fail (salt_len >= 8, NULL); + g_return_val_if_fail (password != NULL, NULL); + g_return_val_if_fail (out_len != NULL, NULL); + + if (!strcmp (cipher, "DES-EDE3-CBC")) + digest_len = 24; + else if (!strcmp (cipher, "AES-128-CBC")) + digest_len = 16; + + key = g_malloc0 (digest_len + 1); + + if (!crypto_md5_hash (salt, salt_len, password, strlen (password), key, digest_len, error)) { + *out_len = 0; + memset (key, 0, digest_len); + g_free (key); + key = NULL; + } else + *out_len = digest_len; + + return key; +} + +/** + * nm_utils_rsa_key_encrypt_helper: + * @cipher: cipher to use for encryption ("DES-EDE3-CBC" or "AES-128-CBC") + * @data: RSA private key data to be encrypted + * @in_password: (allow-none): existing password to use, if any + * @out_password: (out) (allow-none): if @in_password was %NULL, a random password will be generated + * and returned in this argument + * @error: detailed error information on return, if an error occurred + * + * Encrypts the given RSA private key data with the given password (or generates + * a password if no password was given) and converts the data to PEM format + * suitable for writing to a file. + * + * Returns: (transfer full): on success, PEM-formatted data suitable for writing to a PEM-formatted + * certificate/private key file. + **/ +static GByteArray * +nm_utils_rsa_key_encrypt_helper (const char *cipher, + const GByteArray *data, + const char *in_password, + char **out_password, + GError **error) +{ + char salt[16]; + int salt_len; + char *key = NULL, *enc = NULL, *pw_buf[32]; + gsize key_len = 0, enc_len = 0; + GString *pem = NULL; + char *tmp, *tmp_password = NULL; + int left; + const char *p; + GByteArray *ret = NULL; + + g_return_val_if_fail (!g_strcmp0 (cipher, CIPHER_DES_EDE3_CBC) || !g_strcmp0 (cipher, CIPHER_AES_CBC), NULL); + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (data->len > 0, NULL); + if (out_password) + g_return_val_if_fail (*out_password == NULL, NULL); + + /* Make the password if needed */ + if (!in_password) { + if (!crypto_randomize (pw_buf, sizeof (pw_buf), error)) + return NULL; + in_password = tmp_password = nm_utils_bin2hexstr ((const char *) pw_buf, sizeof (pw_buf), -1); + } + + if (g_strcmp0 (cipher, CIPHER_AES_CBC) == 0) + salt_len = 16; + else + salt_len = 8; + + if (!crypto_randomize (salt, salt_len, error)) + goto out; + + key = make_key (cipher, &salt[0], salt_len, in_password, &key_len, error); + if (!key) + goto out; + + enc = crypto_encrypt (cipher, data, salt, salt_len, key, key_len, &enc_len, error); + if (!enc) + goto out; + + pem = g_string_sized_new (enc_len * 2 + 100); + g_string_append (pem, "-----BEGIN RSA PRIVATE KEY-----\n"); + g_string_append (pem, "Proc-Type: 4,ENCRYPTED\n"); + + /* Convert the salt to a hex string */ + tmp = nm_utils_bin2hexstr ((const char *) salt, salt_len, salt_len * 2); + g_string_append_printf (pem, "DEK-Info: %s,%s\n\n", cipher, tmp); + g_free (tmp); + + /* Convert the encrypted key to a base64 string */ + p = tmp = g_base64_encode ((const guchar *) enc, enc_len); + left = strlen (tmp); + while (left > 0) { + g_string_append_len (pem, p, (left < 64) ? left : 64); + g_string_append_c (pem, '\n'); + left -= 64; + p += 64; + } + g_free (tmp); + + g_string_append (pem, "-----END RSA PRIVATE KEY-----\n"); + + ret = g_byte_array_sized_new (pem->len); + g_byte_array_append (ret, (const unsigned char *) pem->str, pem->len); + if (tmp_password && out_password) + *out_password = g_strdup (tmp_password); + +out: + if (key) { + memset (key, 0, key_len); + g_free (key); + } + if (enc) { + memset (enc, 0, enc_len); + g_free (enc); + } + if (pem) + g_string_free (pem, TRUE); + + if (tmp_password) { + memset (tmp_password, 0, strlen (tmp_password)); + g_free (tmp_password); + } + + return ret; +} + +/** + * nm_utils_rsa_key_encrypt: + * @data: RSA private key data to be encrypted + * @in_password: (allow-none): existing password to use, if any + * @out_password: (out) (allow-none): if @in_password was %NULL, a random password will be generated + * and returned in this argument + * @error: detailed error information on return, if an error occurred + * + * Encrypts the given RSA private key data with the given password (or generates + * a password if no password was given) and converts the data to PEM format + * suitable for writing to a file. It uses Triple DES cipher for the encryption. + * + * Returns: (transfer full): on success, PEM-formatted data suitable for writing to a PEM-formatted + * certificate/private key file. + **/ +GByteArray * +nm_utils_rsa_key_encrypt (const GByteArray *data, + const char *in_password, + char **out_password, + GError **error) +{ + + + return nm_utils_rsa_key_encrypt_helper (CIPHER_DES_EDE3_CBC, + data, + in_password, + out_password, + error); +} + +/** + * nm_utils_rsa_key_encrypt_aes: + * @data: RSA private key data to be encrypted + * @in_password: (allow-none): existing password to use, if any + * @out_password: (out) (allow-none): if @in_password was %NULL, a random password will be generated + * and returned in this argument + * @error: detailed error information on return, if an error occurred + * + * Encrypts the given RSA private key data with the given password (or generates + * a password if no password was given) and converts the data to PEM format + * suitable for writing to a file. It uses AES cipher for the encryption. + * + * Returns: (transfer full): on success, PEM-formatted data suitable for writing to a PEM-formatted + * certificate/private key file. + **/ +GByteArray * +nm_utils_rsa_key_encrypt_aes (const GByteArray *data, + const char *in_password, + char **out_password, + GError **error) +{ + + return nm_utils_rsa_key_encrypt_helper (CIPHER_AES_CBC, + data, + in_password, + out_password, + error); +} + +/** + * nm_utils_file_is_pkcs12: + * @filename: name of the file to test + * + * Utility function to find out if the @filename is in PKCS#12 format. + * + * Returns: %TRUE if the file is PKCS#12, %FALSE if it is not + **/ +gboolean +nm_utils_file_is_pkcs12 (const char *filename) +{ + return crypto_is_pkcs12_file (filename, NULL); +} + +/* Band, channel/frequency stuff for wireless */ +struct cf_pair { + guint32 chan; + guint32 freq; +}; + +static struct cf_pair a_table[] = { + /* A band */ + { 7, 5035 }, + { 8, 5040 }, + { 9, 5045 }, + { 11, 5055 }, + { 12, 5060 }, + { 16, 5080 }, + { 34, 5170 }, + { 36, 5180 }, + { 38, 5190 }, + { 40, 5200 }, + { 42, 5210 }, + { 44, 5220 }, + { 46, 5230 }, + { 48, 5240 }, + { 50, 5250 }, + { 52, 5260 }, + { 56, 5280 }, + { 58, 5290 }, + { 60, 5300 }, + { 64, 5320 }, + { 100, 5500 }, + { 104, 5520 }, + { 108, 5540 }, + { 112, 5560 }, + { 116, 5580 }, + { 120, 5600 }, + { 124, 5620 }, + { 128, 5640 }, + { 132, 5660 }, + { 136, 5680 }, + { 140, 5700 }, + { 149, 5745 }, + { 152, 5760 }, + { 153, 5765 }, + { 157, 5785 }, + { 160, 5800 }, + { 161, 5805 }, + { 165, 5825 }, + { 183, 4915 }, + { 184, 4920 }, + { 185, 4925 }, + { 187, 4935 }, + { 188, 4945 }, + { 192, 4960 }, + { 196, 4980 }, + { 0, -1 } +}; + +static struct cf_pair bg_table[] = { + /* B/G band */ + { 1, 2412 }, + { 2, 2417 }, + { 3, 2422 }, + { 4, 2427 }, + { 5, 2432 }, + { 6, 2437 }, + { 7, 2442 }, + { 8, 2447 }, + { 9, 2452 }, + { 10, 2457 }, + { 11, 2462 }, + { 12, 2467 }, + { 13, 2472 }, + { 14, 2484 }, + { 0, -1 } +}; + +/** + * nm_utils_wifi_freq_to_channel: + * @freq: frequency + * + * Utility function to translate a Wi-Fi frequency to its corresponding channel. + * + * Returns: the channel represented by the frequency or 0 + **/ +guint32 +nm_utils_wifi_freq_to_channel (guint32 freq) +{ + int i = 0; + + if (freq > 4900) { + while (a_table[i].chan && (a_table[i].freq != freq)) + i++; + return a_table[i].chan; + } else { + while (bg_table[i].chan && (bg_table[i].freq != freq)) + i++; + return bg_table[i].chan; + } + + return 0; +} + +/** + * nm_utils_wifi_channel_to_freq: + * @channel: channel + * @band: frequency band for wireless ("a" or "bg") + * + * Utility function to translate a Wi-Fi channel to its corresponding frequency. + * + * Returns: the frequency represented by the channel of the band, + * or -1 when the freq is invalid, or 0 when the band + * is invalid + **/ +guint32 +nm_utils_wifi_channel_to_freq (guint32 channel, const char *band) +{ + int i = 0; + + if (!strcmp (band, "a")) { + while (a_table[i].chan && (a_table[i].chan != channel)) + i++; + return a_table[i].freq; + } else if (!strcmp (band, "bg")) { + while (bg_table[i].chan && (bg_table[i].chan != channel)) + i++; + return bg_table[i].freq; + } + + return 0; +} + +/** + * nm_utils_wifi_find_next_channel: + * @channel: current channel + * @direction: whether going downward (0 or less) or upward (1 or more) + * @band: frequency band for wireless ("a" or "bg") + * + * Utility function to find out next/previous Wi-Fi channel for a channel. + * + * Returns: the next channel in the specified direction or 0 + **/ +guint32 +nm_utils_wifi_find_next_channel (guint32 channel, int direction, char *band) +{ + size_t a_size = sizeof (a_table) / sizeof (struct cf_pair); + size_t bg_size = sizeof (bg_table) / sizeof (struct cf_pair); + struct cf_pair *pair = NULL; + + if (!strcmp (band, "a")) { + if (channel < a_table[0].chan) + return a_table[0].chan; + if (channel > a_table[a_size - 2].chan) + return a_table[a_size - 2].chan; + pair = &a_table[0]; + } else if (!strcmp (band, "bg")) { + if (channel < bg_table[0].chan) + return bg_table[0].chan; + if (channel > bg_table[bg_size - 2].chan) + return bg_table[bg_size - 2].chan; + pair = &bg_table[0]; + } else { + g_assert_not_reached (); + return 0; + } + + while (pair->chan) { + if (channel == pair->chan) + return channel; + if ((channel < (pair+1)->chan) && (channel > pair->chan)) { + if (direction > 0) + return (pair+1)->chan; + else + return pair->chan; + } + pair++; + } + return 0; +} + +/** + * nm_utils_wifi_is_channel_valid: + * @channel: channel + * @band: frequency band for wireless ("a" or "bg") + * + * Utility function to verify Wi-Fi channel validity. + * + * Returns: %TRUE or %FALSE + **/ +gboolean +nm_utils_wifi_is_channel_valid (guint32 channel, const char *band) +{ + struct cf_pair *table = NULL; + int i = 0; + + if (!strcmp (band, "a")) + table = a_table; + else if (!strcmp (band, "bg")) + table = bg_table; + else + return FALSE; + + while (table[i].chan && (table[i].chan != channel)) + i++; + + if (table[i].chan != 0) + return TRUE; + else + return FALSE; +} + +/** + * nm_utils_hwaddr_len: + * @type: the type of address; either %ARPHRD_ETHER or %ARPHRD_INFINIBAND + * + * Returns the length in octets of a hardware address of type @type. + * + * Return value: the positive length, or -1 if the type is unknown/unsupported. + */ +int +nm_utils_hwaddr_len (int type) +{ + if (type == ARPHRD_ETHER) + return ETH_ALEN; + else if (type == ARPHRD_INFINIBAND) + return INFINIBAND_ALEN; + else + return -1; +} + +/** + * nm_utils_hwaddr_type: + * @len: the length of hardware address in bytes + * + * Returns the type (either %ARPHRD_ETHER or %ARPHRD_INFINIBAND) of + * the raw address given its length. + * + * Return value: the type, either %ARPHRD_ETHER or %ARPHRD_INFINIBAND. + * If the length is unexpected, return -1 (unsupported type/length). + * + * Deprecated: This could not be extended to cover other types, since + * there is not a one-to-one mapping between types and lengths. This + * was mostly only used to get a type to pass to + * nm_utils_hwaddr_ntoa() or nm_utils_hwaddr_aton() when you only had + * a length; but you can just use nm_utils_hwaddr_ntoa_len() or + * nm_utils_hwaddr_aton_len() now instead. + */ +int +nm_utils_hwaddr_type (int len) +{ + if (len == ETH_ALEN) + return ARPHRD_ETHER; + else if (len == INFINIBAND_ALEN) + return ARPHRD_INFINIBAND; + else + return -1; +} + +#define HEXVAL(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10) + +/** + * nm_utils_hwaddr_aton: + * @asc: the ASCII representation of a hardware address + * @type: the type of address; either %ARPHRD_ETHER or %ARPHRD_INFINIBAND + * @buffer: buffer to store the result into + * + * Parses @asc and converts it to binary form in @buffer. See + * nm_utils_hwaddr_atoba() if you'd rather have the result in a + * #GByteArray. + * + * See also nm_utils_hwaddr_aton_len(), which takes an output length + * instead of a type. + * + * Return value: @buffer, or %NULL if @asc couldn't be parsed + */ +guint8 * +nm_utils_hwaddr_aton (const char *asc, int type, gpointer buffer) +{ + int len = nm_utils_hwaddr_len (type); + + if (len <= 0) { + g_return_val_if_reached (NULL); + return NULL; + } + return nm_utils_hwaddr_aton_len (asc, buffer, len); +} + +/** + * nm_utils_hwaddr_atoba: + * @asc: the ASCII representation of a hardware address + * @type: the type of address; either %ARPHRD_ETHER or %ARPHRD_INFINIBAND + * + * Parses @asc and converts it to binary form in a #GByteArray. See + * nm_utils_hwaddr_aton() if you don't want a #GByteArray. + * + * Return value: (transfer full): a new #GByteArray, or %NULL if @asc couldn't + * be parsed + */ +GByteArray * +nm_utils_hwaddr_atoba (const char *asc, int type) +{ + GByteArray *ba; + int len = nm_utils_hwaddr_len (type); + + if (len <= 0) { + g_return_val_if_reached (NULL); + return NULL; + } + + ba = g_byte_array_sized_new (len); + g_byte_array_set_size (ba, len); + if (!nm_utils_hwaddr_aton_len (asc, ba->data, len)) { + g_byte_array_unref (ba); + return NULL; + } + + return ba; +} + +/** + * nm_utils_hwaddr_ntoa: + * @addr: a binary hardware address + * @type: the type of address; either %ARPHRD_ETHER or %ARPHRD_INFINIBAND + * + * Converts @addr to textual form. + * + * See also nm_utils_hwaddr_ntoa_len(), which takes a length instead of + * a type. + * + * Return value: (transfer full): the textual form of @addr + */ +char * +nm_utils_hwaddr_ntoa (gconstpointer addr, int type) +{ + int len = nm_utils_hwaddr_len (type); + + if (len <= 0) { + g_return_val_if_reached (NULL); + return NULL; + } + + return nm_utils_hwaddr_ntoa_len (addr, len); +} + +/** + * nm_utils_hwaddr_aton_len: + * @asc: the ASCII representation of a hardware address + * @buffer: buffer to store the result into + * @length: the expected length in bytes of the result and + * the size of the buffer in bytes. + * + * Parses @asc and converts it to binary form in @buffer. + * Bytes in @asc can be sepatared by colons (:), or hyphens (-), but not mixed. + * + * Return value: @buffer, or %NULL if @asc couldn't be parsed + * or would be shorter or longer than @length. + * + * Since: 0.9.10 + */ +guint8 * +nm_utils_hwaddr_aton_len (const char *asc, gpointer buffer, gsize length) +{ + const char *in = asc; + guint8 *out = (guint8 *)buffer; + char delimiter = '\0'; + + if (!asc) { + g_return_val_if_reached (NULL); + return NULL; + } + g_return_val_if_fail (buffer, NULL); + g_return_val_if_fail (length, NULL); + + while (length && *in) { + guint8 d1 = in[0], d2 = in[1]; + + if (!g_ascii_isxdigit (d1)) + return NULL; + + /* If there's no leading zero (ie "aa:b:cc") then fake it */ + if (d2 && g_ascii_isxdigit (d2)) { + *out++ = (HEXVAL (d1) << 4) + HEXVAL (d2); + in += 2; + } else { + /* Fake leading zero */ + *out++ = (HEXVAL ('0') << 4) + HEXVAL (d1); + in += 1; + } + + length--; + if (*in) { + if (delimiter == '\0') { + if (*in == ':' || *in == '-') + delimiter = *in; + else + return NULL; + } else { + if (*in != delimiter) + return NULL; + } + in++; + } + } + + if (length == 0 && !*in) + return buffer; + else + return NULL; +} + +/** + * nm_utils_hwaddr_ntoa_len: + * @addr: a binary hardware address + * @length: the length of @addr + * + * Converts @addr to textual form. + * + * Return value: (transfer full): the textual form of @addr + * + * Since: 0.9.10 + */ +char * +nm_utils_hwaddr_ntoa_len (gconstpointer addr, gsize length) +{ + const guint8 *in = addr; + char *out, *result; + const char *LOOKUP = "0123456789ABCDEF"; + + g_return_val_if_fail (addr != NULL, g_strdup ("")); + g_return_val_if_fail (length != 0, g_strdup ("")); + + result = out = g_malloc (length * 3); + for (;;) { + guint8 v = *in++; + + *out++ = LOOKUP[v >> 4]; + *out++ = LOOKUP[v & 0x0F]; + if (--length == 0) { + *out = 0; + return result; + } + *out++ = ':'; + } +} + +/** + * nm_utils_hwaddr_valid: + * @asc: the ASCII representation of a hardware address + * + * Parses @asc to see if it is a valid hardware address of some type. + * + * Return value: %TRUE if @asc appears to be a valid hardware address + * of some type, %FALSE if not. + * + * Since: 0.9.10 + */ +gboolean +nm_utils_hwaddr_valid (const char *asc) +{ + guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; + gsize in_len, out_len; + + if (!asc || !*asc) + return FALSE; + in_len = strlen (asc); + if ((in_len + 1) % 3 != 0) + return FALSE; + out_len = (in_len + 1) / 3; + if (out_len > NM_UTILS_HWADDR_LEN_MAX) + return FALSE; + return nm_utils_hwaddr_aton_len (asc, buf, out_len) != NULL; +} + +/** + * nm_utils_bin2hexstr: + * @bytes: an array of bytes + * @len: the length of the @bytes array + * @final_len: an index where to cut off the returned string, or -1 + * + * Converts a byte-array @bytes into a hexadecimal string. + * If @final_len is greater than -1, the returned string is terminated at + * that index (returned_string[final_len] == '\0'), + * + * Return value: (transfer full): the textual form of @bytes + * + * Since: 0.9.10 + */ +/* + * Code originally by Alex Larsson <alexl@redhat.com> and + * copyright Red Hat, Inc. under terms of the LGPL. + */ +char * +nm_utils_bin2hexstr (const char *bytes, int len, int final_len) +{ + static char hex_digits[] = "0123456789abcdef"; + char *result; + int i; + gsize buflen = (len * 2) + 1; + + g_return_val_if_fail (bytes != NULL, NULL); + g_return_val_if_fail (len > 0, NULL); + g_return_val_if_fail (len < 4096, NULL); /* Arbitrary limit */ + if (final_len > -1) + g_return_val_if_fail (final_len < buflen, NULL); + + result = g_malloc0 (buflen); + for (i = 0; i < len; i++) { + result[2*i] = hex_digits[(bytes[i] >> 4) & 0xf]; + result[2*i+1] = hex_digits[bytes[i] & 0xf]; + } + /* Cut converted key off at the correct length for this cipher type */ + if (final_len > -1) + result[final_len] = '\0'; + else + result[buflen - 1] = '\0'; + + return result; +} + +/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */ +/** + * nm_utils_hex2byte: + * @hex: a string representing a hex byte + * + * Converts a hex string (2 characters) into its byte representation. + * + * Return value: a byte, or -1 if @hex doesn't represent a hex byte + * + * Since: 0.9.10 + */ +int +nm_utils_hex2byte (const char *hex) +{ + int a, b; + a = g_ascii_xdigit_value (*hex++); + if (a < 0) + return -1; + b = g_ascii_xdigit_value (*hex++); + if (b < 0) + return -1; + return (a << 4) | b; +} + +/** + * nm_utils_hexstr2bin: + * @hex: an hex string + * @len: the length of the @hex string (it has to be even) + * + * Converts a hexadecimal string @hex into a byte-array. The returned array + * length is @len/2. + * + * Return value: (transfer full): a array of bytes, or %NULL on error + * + * Since: 0.9.10 + */ +char * +nm_utils_hexstr2bin (const char *hex, size_t len) +{ + size_t i; + int a; + const char * ipos = hex; + char * buf = NULL; + char * opos; + + /* Length must be a multiple of 2 */ + if ((len % 2) != 0) + return NULL; + + opos = buf = g_malloc0 ((len / 2) + 1); + for (i = 0; i < len; i += 2) { + a = nm_utils_hex2byte (ipos); + if (a < 0) { + g_free (buf); + return NULL; + } + *opos++ = a; + ipos += 2; + } + return buf; +} +/* End from hostap */ + +/** + * nm_utils_iface_valid_name: + * @name: Name of interface + * + * This function is a 1:1 copy of the kernel's interface validation + * function in net/core/dev.c. + * + * Returns: %TRUE if interface name is valid, otherwise %FALSE is returned. + * + * Since: 0.9.8 + */ +gboolean +nm_utils_iface_valid_name (const char *name) +{ + g_return_val_if_fail (name != NULL, FALSE); + + if (*name == '\0') + return FALSE; + + if (strlen (name) >= 16) + return FALSE; + + if (!strcmp (name, ".") || !strcmp (name, "..")) + return FALSE; + + while (*name) { + if (*name == '/' || g_ascii_isspace (*name)) + return FALSE; + name++; + } + + return TRUE; +} + +/** + * nm_utils_is_uuid: + * @str: a string that might be a UUID + * + * Checks if @str is a UUID + * + * Returns: %TRUE if @str is a UUID, %FALSE if not + * + * Since: 0.9.8 + */ +gboolean +nm_utils_is_uuid (const char *str) +{ + const char *p = str; + int num_dashes = 0; + + while (*p) { + if (*p == '-') + num_dashes++; + else if (!g_ascii_isxdigit (*p)) + return FALSE; + p++; + } + + if ((num_dashes == 4) && (p - str == 36)) + return TRUE; + + /* Backwards compat for older configurations */ + if ((num_dashes == 0) && (p - str == 40)) + return TRUE; + + return FALSE; +} + +static char _nm_utils_inet_ntop_buffer[NM_UTILS_INET_ADDRSTRLEN]; + +/** + * nm_utils_inet4_ntop: (skip) + * @inaddr: the address that should be converted to string. + * @dst: the destination buffer, it must contain at least %INET_ADDRSTRLEN + * or %NM_UTILS_INET_ADDRSTRLEN characters. If set to %NULL, it will return + * a pointer to an internal, static buffer (shared with nm_utils_inet6_ntop()). + * Beware, that the internal buffer will be overwritten with ever new call + * of nm_utils_inet4_ntop() or nm_utils_inet6_ntop() that does not provied it's + * own @dst buffer. Also, using the internal buffer is not thread safe. When + * in doubt, pass your own @dst buffer to avoid these issues. + * + * Wrapper for inet_ntop. + * + * Returns: the input buffer @dst, or a pointer to an + * internal, static buffer. This function cannot fail. + * + * Since: 0.9.10 + **/ +const char * +nm_utils_inet4_ntop (in_addr_t inaddr, char *dst) +{ + return inet_ntop (AF_INET, &inaddr, dst ? dst : _nm_utils_inet_ntop_buffer, + INET_ADDRSTRLEN); +} + +/** + * nm_utils_inet6_ntop: (skip) + * @in6addr: the address that should be converted to string. + * @dst: the destination buffer, it must contain at least %INET6_ADDRSTRLEN + * or %NM_UTILS_INET_ADDRSTRLEN characters. If set to %NULL, it will return + * a pointer to an internal, static buffer (shared with nm_utils_inet4_ntop()). + * Beware, that the internal buffer will be overwritten with ever new call + * of nm_utils_inet4_ntop() or nm_utils_inet6_ntop() that does not provied it's + * own @dst buffer. Also, using the internal buffer is not thread safe. When + * in doubt, pass your own @dst buffer to avoid these issues. + * + * Wrapper for inet_ntop. + * + * Returns: the input buffer @dst, or a pointer to an + * internal, static buffer. %NULL is not allowed as @in6addr, + * otherwise, this function cannot fail. + * + * Since: 0.9.10 + **/ +const char * +nm_utils_inet6_ntop (const struct in6_addr *in6addr, char *dst) +{ + g_return_val_if_fail (in6addr, NULL); + return inet_ntop (AF_INET6, in6addr, dst ? dst : _nm_utils_inet_ntop_buffer, + INET6_ADDRSTRLEN); +} + +/** + * nm_utils_check_virtual_device_compatibility: + * @virtual_type: a virtual connection type + * @other_type: a connection type to test against @virtual_type + * + * Determines if a connection of type @virtual_type can (in the + * general case) work with connections of type @other_type. + * + * If @virtual_type is %NM_TYPE_SETTING_VLAN, then this checks if + * @other_type is a valid type for the parent of a VLAN. + * + * If @virtual_type is a "master" type (eg, %NM_TYPE_SETTING_BRIDGE), + * then this checks if @other_type is a valid type for a slave of that + * master. + * + * Note that even if this returns %TRUE it is not guaranteed that + * <emphasis>every</emphasis> connection of type @other_type is + * compatible with @virtual_type; it may depend on the exact + * configuration of the two connections, or on the capabilities of an + * underlying device driver. + * + * Returns: %TRUE or %FALSE + * + * Since: 0.9.10 + */ +gboolean +nm_utils_check_virtual_device_compatibility (GType virtual_type, GType other_type) +{ + g_return_val_if_fail (_nm_setting_type_is_base_type (virtual_type), FALSE); + g_return_val_if_fail (_nm_setting_type_is_base_type (other_type), FALSE); + + if (virtual_type == NM_TYPE_SETTING_BOND) { + return ( other_type == NM_TYPE_SETTING_INFINIBAND + || other_type == NM_TYPE_SETTING_WIRED + || other_type == NM_TYPE_SETTING_BRIDGE + || other_type == NM_TYPE_SETTING_BOND + || other_type == NM_TYPE_SETTING_TEAM + || other_type == NM_TYPE_SETTING_VLAN); + } else if (virtual_type == NM_TYPE_SETTING_BRIDGE) { + return ( other_type == NM_TYPE_SETTING_WIRED + || other_type == NM_TYPE_SETTING_BOND + || other_type == NM_TYPE_SETTING_TEAM + || other_type == NM_TYPE_SETTING_VLAN); + } else if (virtual_type == NM_TYPE_SETTING_TEAM) { + return ( other_type == NM_TYPE_SETTING_WIRED + || other_type == NM_TYPE_SETTING_BRIDGE + || other_type == NM_TYPE_SETTING_BOND + || other_type == NM_TYPE_SETTING_TEAM + || other_type == NM_TYPE_SETTING_VLAN); + } else if (virtual_type == NM_TYPE_SETTING_VLAN) { + return ( other_type == NM_TYPE_SETTING_WIRED + || other_type == NM_TYPE_SETTING_WIRELESS + || other_type == NM_TYPE_SETTING_BRIDGE + || other_type == NM_TYPE_SETTING_BOND + || other_type == NM_TYPE_SETTING_TEAM + || other_type == NM_TYPE_SETTING_VLAN); + } else { + g_warn_if_reached (); + return FALSE; + } +} + +/***********************************************************/ + +/* Unused prototype to make the compiler happy */ +const NMUtilsPrivateData *nm_util_get_private (void); + +static const NMUtilsPrivateData data = { + .nm_setting_ip4_config_get_address_label = nm_setting_ip4_config_get_address_label, + .nm_setting_ip4_config_add_address_with_label = nm_setting_ip4_config_add_address_with_label, +}; + +/** + * nm_utils_get_private: + * + * Entry point for NetworkManager-internal API. You should not use this + * function for any reason. + * + * Returns: Who knows? It's a mystery. + * + * Since: 0.9.10 + */ +const NMUtilsPrivateData * +nm_utils_get_private (void) +{ + return &data; +} + +/** + * nm_util_get_private: + * + * You should not use this function for any reason. + * + * Returns: Who knows? It's a mystery. + * + * Since: 0.9.10 + */ +const NMUtilsPrivateData * +nm_util_get_private (void) +{ + /* Compat function to preserve ABI */ + return nm_utils_get_private (); +} diff --git a/libnm-core/nm-utils.h b/libnm-core/nm-utils.h new file mode 100644 index 0000000000..896b3e92a8 --- /dev/null +++ b/libnm-core/nm-utils.h @@ -0,0 +1,180 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2005 - 2013 Red Hat, Inc. + */ + +#ifndef NM_UTILS_H +#define NM_UTILS_H + +#include <glib.h> + +#include "nm-connection.h" +#include "nm-utils-enum-types.h" + +G_BEGIN_DECLS + +/* init, deinit nm_utils */ +gboolean nm_utils_init (GError **error); +void nm_utils_deinit (void); + +/* SSID helpers */ +gboolean nm_utils_is_empty_ssid (const guint8 *ssid, int len); +const char *nm_utils_escape_ssid (const guint8 *ssid, guint32 len); +gboolean nm_utils_same_ssid (const GByteArray *ssid1, + const GByteArray *ssid2, + gboolean ignore_trailing_null); +char * nm_utils_ssid_to_utf8 (const GByteArray *ssid); + +GHashTable *nm_utils_gvalue_hash_dup (GHashTable *hash); + +NM_DEPRECATED_IN_0_9_10 +void nm_utils_slist_free (GSList *list, GDestroyNotify elem_destroy_fn); + +/** + * NMUtilsSecurityType: + * @NMU_SEC_INVALID: unknown or invalid security, placeholder and not used + * @NMU_SEC_NONE: unencrypted and open + * @NMU_SEC_STATIC_WEP: static WEP keys are used for encryption + * @NMU_SEC_LEAP: Cisco LEAP is used for authentication and for generating the + * dynamic WEP keys automatically + * @NMU_SEC_DYNAMIC_WEP: standard 802.1x is used for authentication and + * generating the dynamic WEP keys automatically + * @NMU_SEC_WPA_PSK: WPA1 is used with Pre-Shared Keys (PSK) + * @NMU_SEC_WPA_ENTERPRISE: WPA1 is used with 802.1x authentication + * @NMU_SEC_WPA2_PSK: WPA2/RSN is used with Pre-Shared Keys (PSK) + * @NMU_SEC_WPA2_ENTERPRISE: WPA2 is used with 802.1x authentication + * + * Describes generic security mechanisms that 802.11 access points may offer. + * Used with nm_utils_security_valid() for checking whether a given access + * point is compatible with a network device. + **/ +typedef enum { + NMU_SEC_INVALID = 0, + NMU_SEC_NONE, + NMU_SEC_STATIC_WEP, + NMU_SEC_LEAP, + NMU_SEC_DYNAMIC_WEP, + NMU_SEC_WPA_PSK, + NMU_SEC_WPA_ENTERPRISE, + NMU_SEC_WPA2_PSK, + NMU_SEC_WPA2_ENTERPRISE +} NMUtilsSecurityType; + +gboolean nm_utils_security_valid (NMUtilsSecurityType type, + NMDeviceWifiCapabilities wifi_caps, + gboolean have_ap, + gboolean adhoc, + NM80211ApFlags ap_flags, + NM80211ApSecurityFlags ap_wpa, + NM80211ApSecurityFlags ap_rsn); + +gboolean nm_utils_ap_mode_security_valid (NMUtilsSecurityType type, + NMDeviceWifiCapabilities wifi_caps); + +gboolean nm_utils_wep_key_valid (const char *key, NMWepKeyType wep_type); +gboolean nm_utils_wpa_psk_valid (const char *psk); + +GSList *nm_utils_ip4_addresses_from_gvalue (const GValue *value); +void nm_utils_ip4_addresses_to_gvalue (GSList *list, GValue *value); + +GSList *nm_utils_ip4_routes_from_gvalue (const GValue *value); +void nm_utils_ip4_routes_to_gvalue (GSList *list, GValue *value); + +guint32 nm_utils_ip4_netmask_to_prefix (guint32 netmask); +guint32 nm_utils_ip4_prefix_to_netmask (guint32 prefix); +guint32 nm_utils_ip4_get_default_prefix (guint32 ip); + +GSList *nm_utils_ip6_addresses_from_gvalue (const GValue *value); +void nm_utils_ip6_addresses_to_gvalue (GSList *list, GValue *value); + +GSList *nm_utils_ip6_routes_from_gvalue (const GValue *value); +void nm_utils_ip6_routes_to_gvalue (GSList *list, GValue *value); + +GSList *nm_utils_ip6_dns_from_gvalue (const GValue *value); +void nm_utils_ip6_dns_to_gvalue (GSList *list, GValue *value); + +char *nm_utils_uuid_generate (void); +char *nm_utils_uuid_generate_from_string (const char *s); + +GByteArray *nm_utils_rsa_key_encrypt (const GByteArray *data, + const char *in_password, + char **out_password, + GError **error); +GByteArray *nm_utils_rsa_key_encrypt_aes (const GByteArray *data, + const char *in_password, + char **out_password, + GError **error); +gboolean nm_utils_file_is_pkcs12 (const char *filename); + +guint32 nm_utils_wifi_freq_to_channel (guint32 freq); +guint32 nm_utils_wifi_channel_to_freq (guint32 channel, const char *band); +guint32 nm_utils_wifi_find_next_channel (guint32 channel, int direction, char *band); +gboolean nm_utils_wifi_is_channel_valid (guint32 channel, const char *band); + +/** + * NM_UTILS_HWADDR_LEN_MAX: + * + * The maximum length of hardware addresses handled by NetworkManager itself, + * nm_utils_hwaddr_len(), and nm_utils_hwaddr_aton(). + */ +#define NM_UTILS_HWADDR_LEN_MAX 20 /* INFINIBAND_ALEN */ + +int nm_utils_hwaddr_len (int type) G_GNUC_PURE; +NM_DEPRECATED_IN_0_9_10 +int nm_utils_hwaddr_type (int len) G_GNUC_PURE; +char *nm_utils_hwaddr_ntoa (gconstpointer addr, int type); +GByteArray *nm_utils_hwaddr_atoba (const char *asc, int type); +guint8 *nm_utils_hwaddr_aton (const char *asc, int type, gpointer buffer); + +NM_AVAILABLE_IN_0_9_10 +char *nm_utils_hwaddr_ntoa_len (gconstpointer addr, gsize length); +NM_AVAILABLE_IN_0_9_10 +guint8 *nm_utils_hwaddr_aton_len (const char *asc, gpointer buffer, gsize length); + +NM_AVAILABLE_IN_0_9_10 +gboolean nm_utils_hwaddr_valid (const char *asc); + +NM_AVAILABLE_IN_0_9_10 +char *nm_utils_bin2hexstr (const char *bytes, int len, int final_len); +NM_AVAILABLE_IN_0_9_10 +int nm_utils_hex2byte (const char *hex); +NM_AVAILABLE_IN_0_9_10 +char *nm_utils_hexstr2bin (const char *hex, size_t len); + +gboolean nm_utils_iface_valid_name(const char *name); + +gboolean nm_utils_is_uuid (const char *str); + +/** + * NM_UTILS_INET_ADDRSTRLEN: + * + * Defines the minimal length for a char buffer that is suitable as @dst argument + * for both nm_utils_inet4_ntop() and nm_utils_inet6_ntop(). + **/ +#define NM_UTILS_INET_ADDRSTRLEN INET6_ADDRSTRLEN +NM_AVAILABLE_IN_0_9_10 +const char *nm_utils_inet4_ntop (in_addr_t inaddr, char *dst); +NM_AVAILABLE_IN_0_9_10 +const char *nm_utils_inet6_ntop (const struct in6_addr *in6addr, char *dst); + +NM_AVAILABLE_IN_0_9_10 +gboolean nm_utils_check_virtual_device_compatibility (GType virtual_type, GType other_type); + +G_END_DECLS + +#endif /* NM_UTILS_H */ diff --git a/libnm-core/nm-value-transforms.c b/libnm-core/nm-value-transforms.c new file mode 100644 index 0000000000..00e7e77579 --- /dev/null +++ b/libnm-core/nm-value-transforms.c @@ -0,0 +1,593 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2005 - 2014 Red Hat, Inc. + */ + +#include "config.h" + +#include <string.h> + +#include "nm-utils.h" +#include "nm-utils-private.h" +#include "nm-dbus-glib-types.h" +#include "nm-glib-compat.h" + +static void +_nm_utils_convert_op_to_string (const GValue *src_value, GValue *dest_value) +{ + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_OBJECT_PATH)); + + g_value_set_string (dest_value, (const char *) g_value_get_boxed (src_value)); +} + +static void +_nm_utils_convert_strv_to_slist (const GValue *src_value, GValue *dest_value) +{ + char **str; + GSList *list = NULL; + guint i = 0; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), G_TYPE_STRV)); + + str = (char **) g_value_get_boxed (src_value); + + while (str && str[i]) + list = g_slist_prepend (list, g_strdup (str[i++])); + + g_value_take_boxed (dest_value, g_slist_reverse (list)); +} + +static void +_nm_utils_convert_slist_to_strv (const GValue *src_value, GValue *dest_value) +{ + GSList *slist; + char **strv; + int len, i = 0; + + slist = g_value_get_boxed (src_value); + len = g_slist_length (slist); + + strv = g_new (char *, len + 1); + for (i = 0; slist; slist = slist->next, i++) + strv[i] = g_strdup (slist->data); + strv[i] = NULL; + + g_value_take_boxed (dest_value, strv); +} + +static void +_nm_utils_convert_strv_to_ptrarray (const GValue *src_value, GValue *dest_value) +{ + char **str; + GPtrArray *array = NULL; + guint i = 0; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), G_TYPE_STRV)); + + str = (char **) g_value_get_boxed (src_value); + + array = g_ptr_array_sized_new (3); + while (str && str[i]) + g_ptr_array_add (array, g_strdup (str[i++])); + + g_value_take_boxed (dest_value, array); +} + +static void +_nm_utils_convert_string_list_to_string (const GValue *src_value, GValue *dest_value) +{ + GSList *strings; + GString *printable; + GSList *iter; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_LIST_OF_STRING)); + + strings = (GSList *) g_value_get_boxed (src_value); + + printable = g_string_new (NULL); + for (iter = strings; iter; iter = iter->next) { + if (iter != strings) + g_string_append_c (printable, ','); + g_string_append (printable, iter->data ? iter->data : "(null)"); + } + + g_value_take_string (dest_value, g_string_free (printable, FALSE)); +} + +static void +_string_array_to_string (const GPtrArray *strings, GValue *dest_value) +{ + GString *printable; + guint i; + + printable = g_string_new (NULL); + for (i = 0; strings && i < strings->len; i++) { + if (i > 0) + g_string_append_c (printable, ','); + g_string_append (printable, strings->pdata[i]); + } + + g_value_take_string (dest_value, g_string_free (printable, FALSE)); +} + +static void +_nm_utils_convert_string_array_to_string (const GValue *src_value, GValue *dest_value) +{ + const GPtrArray *strings; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_STRING)); + + strings = (const GPtrArray *) g_value_get_boxed (src_value); + _string_array_to_string (strings, dest_value); +} + +static void +_nm_utils_convert_op_array_to_string (const GValue *src_value, GValue *dest_value) +{ + const GPtrArray *strings; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH)); + + strings = (const GPtrArray *) g_value_get_boxed (src_value); + _string_array_to_string (strings, dest_value); +} + +static void +_nm_utils_convert_uint_array_to_string (const GValue *src_value, GValue *dest_value) +{ + GArray *array; + GString *printable; + guint i = 0; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_UINT_ARRAY)); + + array = (GArray *) g_value_get_boxed (src_value); + + printable = g_string_new (NULL); + while (array && (i < array->len)) { + guint32 addr; + + if (i > 0) + g_string_append (printable, ", "); + + addr = g_array_index (array, guint32, i++); + g_string_append (printable, nm_utils_inet4_ntop (addr, NULL)); + } + + g_value_take_string (dest_value, g_string_free (printable, FALSE)); +} + +static void +_nm_utils_convert_ip4_addr_route_struct_array_to_string (const GValue *src_value, GValue *dest_value) +{ + GPtrArray *ptr_array; + GString *printable; + guint i = 0; + char buf[INET_ADDRSTRLEN]; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT)); + + ptr_array = (GPtrArray *) g_value_get_boxed (src_value); + + printable = g_string_new (NULL); + while (ptr_array && (i < ptr_array->len)) { + GArray *array; + gboolean is_addr; /* array contains address x route */ + + if (i > 0) + g_string_append (printable, "; "); + + g_string_append (printable, "{ "); + array = (GArray *) g_ptr_array_index (ptr_array, i++); + if (array->len < 2) { + g_string_append (printable, "invalid"); + continue; + } + is_addr = (array->len < 4); + + nm_utils_inet4_ntop (g_array_index (array, guint32, 0), buf); + if (is_addr) + g_string_append_printf (printable, "ip = %s", buf); + else + g_string_append_printf (printable, "dst = %s", buf); + + g_string_append_printf (printable, "/%u", + g_array_index (array, guint32, 1)); + + if (array->len > 2) { + nm_utils_inet4_ntop (g_array_index (array, guint32, 2), buf); + if (is_addr) + g_string_append_printf (printable, ", gw = %s", buf); + else + g_string_append_printf (printable, ", nh = %s", buf); + } + + if (array->len > 3) { + g_string_append_printf (printable, ", mt = %u", + g_array_index (array, guint32, 3)); + } + + g_string_append (printable, " }"); + } + + g_value_take_string (dest_value, g_string_free (printable, FALSE)); +} + +static void +convert_one_gvalue_hash_entry (gpointer key, gpointer value, gpointer user_data) +{ + GString *printable = (GString *) user_data; + char *value_as_string; + + value_as_string = g_strdup_value_contents ((GValue *) value); + g_string_append_printf (printable, " { '%s': %s },", (const char *) key, value_as_string); + g_free (value_as_string); +} + +static void +_nm_utils_convert_gvalue_hash_to_string (const GValue *src_value, GValue *dest_value) +{ + GHashTable *hash; + GString *printable; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_MAP_OF_VARIANT)); + + hash = (GHashTable *) g_value_get_boxed (src_value); + + printable = g_string_new ("["); + g_hash_table_foreach (hash, convert_one_gvalue_hash_entry, printable); + g_string_append (printable, " ]"); + + g_value_take_string (dest_value, printable->str); + g_string_free (printable, FALSE); +} + +static void +convert_one_string_hash_entry (gpointer key, gpointer value, gpointer user_data) +{ + GString *printable = (GString *) user_data; + + if (printable->len) + g_string_append_c (printable, ','); + g_string_append_printf (printable, "%s=%s", (const char *) key, (const char *) value); +} + +static void +_nm_utils_convert_string_hash_to_string (const GValue *src_value, GValue *dest_value) +{ + GHashTable *hash; + GString *printable; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_MAP_OF_STRING)); + + hash = (GHashTable *) g_value_get_boxed (src_value); + + printable = g_string_new (NULL); + if (hash) + g_hash_table_foreach (hash, convert_one_string_hash_entry, printable); + + g_value_take_string (dest_value, g_string_free (printable, FALSE)); +} + +static void +_nm_utils_convert_byte_array_to_string (const GValue *src_value, GValue *dest_value) +{ + GArray *array; + GString *printable; + guint i = 0; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_UCHAR_ARRAY)); + + array = (GArray *) g_value_get_boxed (src_value); + + printable = g_string_new ("["); + if (array) { + while (i < MIN (array->len, 35)) { + if (i > 0) + g_string_append_c (printable, ' '); + g_string_append_printf (printable, "0x%02X", + g_array_index (array, unsigned char, i++)); + } + if (i < array->len) + g_string_append (printable, " ... "); + } + g_string_append_c (printable, ']'); + + g_value_take_string (dest_value, g_string_free (printable, FALSE)); +} + +static void +_nm_utils_convert_ip6_dns_array_to_string (const GValue *src_value, GValue *dest_value) +{ + GPtrArray *ptr_array; + GString *printable; + guint i = 0; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR)); + + ptr_array = (GPtrArray *) g_value_get_boxed (src_value); + + printable = g_string_new (NULL); + while (ptr_array && (i < ptr_array->len)) { + GByteArray *bytearray; + struct in6_addr *addr; + + if (i > 0) + g_string_append (printable, ", "); + + bytearray = (GByteArray *) g_ptr_array_index (ptr_array, i++); + if (bytearray->len != 16) { + g_string_append (printable, "invalid"); + continue; + } + addr = (struct in6_addr *) bytearray->data; + g_string_append (printable, nm_utils_inet6_ntop (addr, NULL)); + } + + g_value_take_string (dest_value, g_string_free (printable, FALSE)); +} + +static void +_nm_utils_convert_ip6_addr_struct_array_to_string (const GValue *src_value, GValue *dest_value) +{ + GPtrArray *ptr_array; + GString *printable; + guint i = 0; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS)); + + ptr_array = (GPtrArray *) g_value_get_boxed (src_value); + + printable = g_string_new (NULL); + while (ptr_array && (i < ptr_array->len)) { + GValueArray *elements; + GValue *tmp; + GByteArray *ba_addr; + struct in6_addr *addr; + guint32 prefix; + + if (i > 0) + g_string_append (printable, "; "); + + g_string_append (printable, "{ "); + elements = (GValueArray *) g_ptr_array_index (ptr_array, i++); + if (!_nm_utils_gvalue_array_validate (elements, 3, + DBUS_TYPE_G_UCHAR_ARRAY, + G_TYPE_UINT, + DBUS_TYPE_G_UCHAR_ARRAY)) { + g_string_append (printable, "invalid }"); + continue; + } + + /* IPv6 address */ + tmp = g_value_array_get_nth (elements, 0); + ba_addr = g_value_get_boxed (tmp); + if (ba_addr->len != 16) { + g_string_append (printable, "invalid }"); + continue; + } + addr = (struct in6_addr *) ba_addr->data; + g_string_append_printf (printable, "ip = %s", nm_utils_inet6_ntop (addr, NULL)); + + /* Prefix */ + tmp = g_value_array_get_nth (elements, 1); + prefix = g_value_get_uint (tmp); + if (prefix > 128) { + g_string_append (printable, "/invalid }"); + continue; + } + g_string_append_printf (printable, "/%u", prefix); + g_string_append (printable, ", "); + + /* IPv6 Gateway */ + tmp = g_value_array_get_nth (elements, 2); + ba_addr = g_value_get_boxed (tmp); + if (ba_addr->len != 16) { + g_string_append (printable, "invalid }"); + continue; + } + addr = (struct in6_addr *) ba_addr->data; + g_string_append_printf (printable, "gw = %s", nm_utils_inet6_ntop (addr, NULL)); + g_string_append (printable, " }"); + } + + g_value_take_string (dest_value, g_string_free (printable, FALSE)); +} + +static void +_nm_utils_convert_ip6_route_struct_array_to_string (const GValue *src_value, GValue *dest_value) +{ + GPtrArray *ptr_array; + GString *printable; + guint i = 0; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE)); + + ptr_array = (GPtrArray *) g_value_get_boxed (src_value); + + printable = g_string_new (NULL); + while (ptr_array && (i < ptr_array->len)) { + GValueArray *elements; + GValue *tmp; + GByteArray *ba_addr; + struct in6_addr *addr; + guint32 prefix, metric; + + if (i > 0) + g_string_append (printable, "; "); + + g_string_append (printable, "{ "); + elements = (GValueArray *) g_ptr_array_index (ptr_array, i++); + if (!_nm_utils_gvalue_array_validate (elements, 4, + DBUS_TYPE_G_UCHAR_ARRAY, + G_TYPE_UINT, + DBUS_TYPE_G_UCHAR_ARRAY, + G_TYPE_UINT)) { + g_string_append (printable, "invalid"); + continue; + } + + /* Destination address */ + tmp = g_value_array_get_nth (elements, 0); + ba_addr = g_value_get_boxed (tmp); + if (ba_addr->len != 16) { + g_string_append (printable, "invalid"); + continue; + } + addr = (struct in6_addr *) ba_addr->data; + g_string_append_printf (printable, "dst = %s", nm_utils_inet6_ntop (addr, NULL)); + + /* Prefix */ + tmp = g_value_array_get_nth (elements, 1); + prefix = g_value_get_uint (tmp); + if (prefix > 128) { + g_string_append (printable, "/invalid"); + continue; + } + g_string_append_printf (printable, "/%u", prefix); + g_string_append (printable, ", "); + + /* Next hop addresses */ + tmp = g_value_array_get_nth (elements, 2); + ba_addr = g_value_get_boxed (tmp); + if (ba_addr->len != 16) { + g_string_append (printable, "invalid"); + continue; + } + addr = (struct in6_addr *) ba_addr->data; + g_string_append_printf (printable, "nh = %s", nm_utils_inet6_ntop (addr, NULL)); + g_string_append (printable, ", "); + + /* Metric */ + tmp = g_value_array_get_nth (elements, 3); + metric = g_value_get_uint (tmp); + g_string_append_printf (printable, "mt = %u", metric); + + g_string_append (printable, " }"); + } + + g_value_take_string (dest_value, g_string_free (printable, FALSE)); +} + +#define OLD_DBUS_TYPE_G_IP6_ADDRESS (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID)) +#define OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS (dbus_g_type_get_collection ("GPtrArray", OLD_DBUS_TYPE_G_IP6_ADDRESS)) + +static void +_nm_utils_convert_old_ip6_addr_array (const GValue *src_value, GValue *dst_value) +{ + GPtrArray *src_outer_array; + GPtrArray *dst_outer_array; + guint i; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS)); + + src_outer_array = (GPtrArray *) g_value_get_boxed (src_value); + dst_outer_array = g_ptr_array_new (); + + for (i = 0; src_outer_array && (i < src_outer_array->len); i++) { + GValueArray *src_addr_array; + GValueArray *dst_addr_array; + GValue element = G_VALUE_INIT; + GValue *src_addr, *src_prefix; + GByteArray *ba; + + src_addr_array = (GValueArray *) g_ptr_array_index (src_outer_array, i); + if (!_nm_utils_gvalue_array_validate (src_addr_array, 2, DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT)) { + g_warning ("%s: invalid old IPv6 address type", __func__); + return; + } + + dst_addr_array = g_value_array_new (3); + + src_addr = g_value_array_get_nth (src_addr_array, 0); + g_value_array_append (dst_addr_array, src_addr); + src_prefix = g_value_array_get_nth (src_addr_array, 1); + g_value_array_append (dst_addr_array, src_prefix); + + /* Blank Gateway */ + g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY); + ba = g_byte_array_new (); + g_byte_array_append (ba, (guint8 *) "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16); + g_value_take_boxed (&element, ba); + g_value_array_append (dst_addr_array, &element); + g_value_unset (&element); + + g_ptr_array_add (dst_outer_array, dst_addr_array); + } + + g_value_take_boxed (dst_value, dst_outer_array); +} + +void +_nm_value_transforms_register (void) +{ + static gboolean registered = FALSE; + + if (G_UNLIKELY (!registered)) { + g_value_register_transform_func (DBUS_TYPE_G_OBJECT_PATH, + G_TYPE_STRING, + _nm_utils_convert_op_to_string); + g_value_register_transform_func (G_TYPE_STRV, + DBUS_TYPE_G_LIST_OF_STRING, + _nm_utils_convert_strv_to_slist); + g_value_register_transform_func (DBUS_TYPE_G_LIST_OF_STRING, + G_TYPE_STRV, + _nm_utils_convert_slist_to_strv); + g_value_register_transform_func (G_TYPE_STRV, + DBUS_TYPE_G_ARRAY_OF_STRING, + _nm_utils_convert_strv_to_ptrarray); + g_value_register_transform_func (DBUS_TYPE_G_LIST_OF_STRING, + G_TYPE_STRING, + _nm_utils_convert_string_list_to_string); + g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_STRING, + G_TYPE_STRING, + _nm_utils_convert_string_array_to_string); + g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, + G_TYPE_STRING, + _nm_utils_convert_op_array_to_string); + g_value_register_transform_func (DBUS_TYPE_G_UINT_ARRAY, + G_TYPE_STRING, + _nm_utils_convert_uint_array_to_string); + g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT, + G_TYPE_STRING, + _nm_utils_convert_ip4_addr_route_struct_array_to_string); + g_value_register_transform_func (DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_STRING, + _nm_utils_convert_gvalue_hash_to_string); + g_value_register_transform_func (DBUS_TYPE_G_MAP_OF_STRING, + G_TYPE_STRING, + _nm_utils_convert_string_hash_to_string); + g_value_register_transform_func (DBUS_TYPE_G_UCHAR_ARRAY, + G_TYPE_STRING, + _nm_utils_convert_byte_array_to_string); + g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR, + G_TYPE_STRING, + _nm_utils_convert_ip6_dns_array_to_string); + g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS, + G_TYPE_STRING, + _nm_utils_convert_ip6_addr_struct_array_to_string); + g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE, + G_TYPE_STRING, + _nm_utils_convert_ip6_route_struct_array_to_string); + g_value_register_transform_func (OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS, + DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS, + _nm_utils_convert_old_ip6_addr_array); + registered = TRUE; + } +} diff --git a/libnm-core/nm-version.h.in b/libnm-core/nm-version.h.in new file mode 100644 index 0000000000..0a33ac295e --- /dev/null +++ b/libnm-core/nm-version.h.in @@ -0,0 +1,123 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 Red Hat, Inc. + */ + +#ifndef NM_VERSION_H +#define NM_VERSION_H + +/** + * NM_MAJOR_VERSION: + * + * Evaluates to the major version number of NetworkManager which this source + * is compiled against. + */ +#define NM_MAJOR_VERSION (@NM_MAJOR_VERSION@) + +/** + * NM_MINOR_VERSION: + * + * Evaluates to the minor version number of NetworkManager which this source + * is compiled against. + */ +#define NM_MINOR_VERSION (@NM_MINOR_VERSION@) + +/** + * NM_MICRO_VERSION: + * + * Evaluates to the micro version number of NetworkManager which this source + * compiled against. + */ +#define NM_MICRO_VERSION (@NM_MICRO_VERSION@) + +/** + * NM_CHECK_VERSION: + * @major: major version (e.g. 1 for version 1.2.5) + * @minor: minor version (e.g. 2 for version 1.2.5) + * @micro: micro version (e.g. 5 for version 1.2.5) + * + * Returns: %TRUE if the version of the NetworkManager header files + * is the same as or newer than the passed-in version. + */ +#define NM_CHECK_VERSION(major,minor,micro) \ + (NM_MAJOR_VERSION > (major) || \ + (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION > (minor)) || \ + (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION == (minor) && NM_MICRO_VERSION >= (micro))) + + +/* Deprecation / Availability macros */ + +#define NM_ENCODE_VERSION(major,minor,micro) ((major) << 16 | (minor) << 8 | (micro)) + +#define NM_VERSION_0_9_8 (NM_ENCODE_VERSION (0, 9, 8)) +#define NM_VERSION_0_9_10 (NM_ENCODE_VERSION (0, 9, 10)) +#define NM_VERSION_1_0 (NM_ENCODE_VERSION (1, 0, 0)) + +#define NM_VERSION_CUR_STABLE NM_VERSION_0_9_10 +#define NM_VERSION_NEXT_STABLE NM_VERSION_1_0 + +#if !defined (NM_VERSION_MIN_REQUIRED) || (NM_VERSION_MIN_REQUIRED == 0) +# undef NM_VERSION_MIN_REQUIRED +# define NM_VERSION_MIN_REQUIRED (NM_VERSION_CUR_STABLE) +#endif + +#if !defined (NM_VERSION_MAX_ALLOWED) || (NM_VERSION_MAX_ALLOWED == 0) +# undef NM_VERSION_MAX_ALLOWED +# define NM_VERSION_MAX_ALLOWED (NM_VERSION_CUR_STABLE) +#endif + +/* sanity checks */ +#if NM_VERSION_MIN_REQUIRED > NM_VERSION_NEXT_STABLE +#error "NM_VERSION_MIN_REQUIRED must be <= NM_VERSION_NEXT_STABLE" +#endif +#if NM_VERSION_MAX_ALLOWED < NM_VERSION_MIN_REQUIRED +#error "NM_VERSION_MAX_ALLOWED must be >= NM_VERSION_MIN_REQUIRED" +#endif +#if NM_VERSION_MIN_REQUIRED < NM_VERSION_0_9_8 +#error "NM_VERSION_MIN_REQUIRED must be >= NM_VERSION_0_9_8" +#endif + +#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_0_9_10 +# define NM_DEPRECATED_IN_0_9_10 G_DEPRECATED +# define NM_DEPRECATED_IN_0_9_10_FOR(f) G_DEPRECATED_FOR(f) +#else +# define NM_DEPRECATED_IN_0_9_10 +# define NM_DEPRECATED_IN_0_9_10_FOR(f) +#endif + +#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_1_0 +# define NM_DEPRECATED_IN_1_0 G_DEPRECATED +# define NM_DEPRECATED_IN_1_0_FOR(f) G_DEPRECATED_FOR(f) +#else +# define NM_DEPRECATED_IN_1_0 +# define NM_DEPRECATED_IN_1_0_FOR(f) +#endif + +#if NM_VERSION_MAX_ALLOWED < NM_VERSION_0_9_10 +# define NM_AVAILABLE_IN_0_9_10 G_UNAVAILABLE(0.9,10) +#else +# define NM_AVAILABLE_IN_0_9_10 +#endif + +#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_0 +# define NM_AVAILABLE_IN_1_0 G_UNAVAILABLE(1,0) +#else +# define NM_AVAILABLE_IN_1_0 +#endif + +#endif /* NM_VERSION_H */ diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c new file mode 100644 index 0000000000..4a8e77524f --- /dev/null +++ b/libnm-core/tests/test-crypto.c @@ -0,0 +1,384 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * Dan Williams <dcbw@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2011 Red Hat, Inc. + */ + +#include <glib.h> +#include <unistd.h> +#include <stdlib.h> +#include <glib/gi18n.h> +#include <stdio.h> +#include <string.h> + +#include "crypto.h" +#include "nm-utils.h" + +#include "nm-test-utils.h" + +#if 0 +static const char *pem_rsa_key_begin = "-----BEGIN RSA PRIVATE KEY-----"; +static const char *pem_rsa_key_end = "-----END RSA PRIVATE KEY-----"; + +static const char *pem_dsa_key_begin = "-----BEGIN DSA PRIVATE KEY-----"; +static const char *pem_dsa_key_end = "-----END DSA PRIVATE KEY-----"; + +static void +dump_key_to_pem (const char *key, gsize key_len, int key_type) +{ + char *b64 = NULL; + GString *str = NULL; + const char *start_tag; + const char *end_tag; + char *p; + + switch (key_type) { + case NM_CRYPTO_KEY_TYPE_RSA: + start_tag = pem_rsa_key_begin; + end_tag = pem_rsa_key_end; + break; + case NM_CRYPTO_KEY_TYPE_DSA: + start_tag = pem_dsa_key_begin; + end_tag = pem_dsa_key_end; + break; + default: + g_warning ("Unknown key type %d", key_type); + return; + } + + b64 = g_base64_encode ((const unsigned char *) key, key_len); + if (!b64) { + g_warning ("Couldn't base64 encode the key."); + goto out; + } + + str = g_string_new (NULL); + + g_string_append (str, start_tag); + g_string_append_c (str, '\n'); + + for (p = b64; p < (b64 + strlen (b64)); p += 64) { + g_string_append_len (str, p, strnlen (p, 64)); + g_string_append_c (str, '\n'); + } + + g_string_append (str, end_tag); + g_string_append_c (str, '\n'); + + g_message ("Decrypted private key:\n\n%s", str->str); + +out: + g_free (b64); + if (str) + g_string_free (str, TRUE); +} +#endif + +static void +test_load_cert (const char *path, const char *desc) +{ + GByteArray *array; + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + GError *error = NULL; + + array = crypto_load_and_verify_certificate (path, &format, &error); + ASSERT (array != NULL, desc, + "couldn't read certificate file '%s': %d %s", + path, error->code, error->message); + + ASSERT (format == NM_CRYPTO_FILE_FORMAT_X509, desc, + "%s: unexpected certificate format (expected %d, got %d)", + path, NM_CRYPTO_FILE_FORMAT_X509, format); + + g_byte_array_free (array, TRUE); +} + +static GByteArray * +file_to_byte_array (const char *filename) +{ + char *contents; + GByteArray *array = NULL; + gsize length = 0; + + if (g_file_get_contents (filename, &contents, &length, NULL)) { + array = g_byte_array_sized_new (length); + g_byte_array_append (array, (guint8 *) contents, length); + g_assert (array->len == length); + g_free (contents); + } + return array; +} + +static void +test_load_private_key (const char *path, + const char *password, + const char *decrypted_path, + gboolean expect_fail, + const char *desc) +{ + NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; + GByteArray *array, *decrypted; + GError *error = NULL; + + array = crypto_decrypt_private_key (path, password, &key_type, &error); + if (expect_fail) { + ASSERT (array == NULL, desc, + "unexpected success reading private key file '%s' with " + "invalid password", + path); + + ASSERT (key_type != NM_CRYPTO_KEY_TYPE_UNKNOWN, desc, + "unexpected failure determining private key file '%s' " + "type with invalid password (expected %d, got %d)", + path, NM_CRYPTO_KEY_TYPE_UNKNOWN, key_type); + return; + } + + ASSERT (array != NULL, desc, + "couldn't read private key file '%s': %d %s", + path, error->code, error->message); + + ASSERT (key_type == NM_CRYPTO_KEY_TYPE_RSA, desc, + "%s: unexpected private key type (expected %d, got %d)", + path, NM_CRYPTO_KEY_TYPE_RSA, key_type); + + if (decrypted_path) { + /* Compare the crypto decrypted key against a known-good decryption */ + decrypted = file_to_byte_array (decrypted_path); + ASSERT (decrypted != NULL, desc, + "couldn't read decrypted private key file '%s': %d %s", + decrypted_path, error->code, error->message); + + ASSERT (decrypted->len > 0, desc, "decrypted key file invalid (size 0)"); + + ASSERT (decrypted->len == array->len, + desc, "decrypted key file (%d) and decrypted key data (%d) lengths don't match", + decrypted->len, array->len); + + ASSERT (memcmp (decrypted->data, array->data, array->len) == 0, + desc, "decrypted key file and decrypted key data don't match"); + + g_byte_array_free (decrypted, TRUE); + } + + g_byte_array_free (array, TRUE); +} + +static void +test_load_pkcs12 (const char *path, + const char *password, + gboolean expect_fail, + const char *desc) +{ + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + GError *error = NULL; + + format = crypto_verify_private_key (path, password, &error); + if (expect_fail) { + ASSERT (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, desc, + "unexpected success reading PKCS#12 private key file " + "'%s' with invalid password", + path); + } else { + ASSERT (format == NM_CRYPTO_FILE_FORMAT_PKCS12, desc, + "%s: unexpected PKCS#12 private key file format (expected %d, got " + "%d): %d %s", + path, NM_CRYPTO_FILE_FORMAT_PKCS12, format, error->code, error->message); + } +} + +static void +test_load_pkcs12_no_password (const char *path, const char *desc) +{ + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + GError *error = NULL; + + /* We should still get a valid returned crypto file format */ + format = crypto_verify_private_key (path, NULL, &error); + ASSERT (format == NM_CRYPTO_FILE_FORMAT_PKCS12, desc, + "%s: unexpected PKCS#12 private key file format (expected %d, got " + "%d): %d %s", + path, NM_CRYPTO_FILE_FORMAT_PKCS12, format, error->code, error->message); +} + +static void +test_is_pkcs12 (const char *path, gboolean expect_fail, const char *desc) +{ + gboolean is_pkcs12; + + is_pkcs12 = crypto_is_pkcs12_file (path, NULL); + if (expect_fail) { + ASSERT (is_pkcs12 == FALSE, desc, + "unexpected success reading non-PKCS#12 file '%s'", + path); + } else { + ASSERT (is_pkcs12 == TRUE, desc, "couldn't read PKCS#12 file '%s'", path); + } +} + +static void +test_load_pkcs8 (const char *path, + const char *password, + gboolean expect_fail, + const char *desc) +{ + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + GError *error = NULL; + + format = crypto_verify_private_key (path, password, &error); + if (expect_fail) { + ASSERT (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, desc, + "unexpected success reading PKCS#8 private key file " + "'%s' with invalid password", + path); + } else { + ASSERT (format == NM_CRYPTO_FILE_FORMAT_RAW_KEY, desc, + "%s: unexpected PKCS#8 private key file format (expected %d, got " + "%d): %d %s", + path, NM_CRYPTO_FILE_FORMAT_RAW_KEY, format, error->code, error->message); + } +} + +static gboolean +is_cipher_aes (const char *path) +{ + char *contents; + gsize length = 0; + const char *cipher; + gboolean is_aes = FALSE; + + if (!g_file_get_contents (path, &contents, &length, NULL)) + return FALSE; + + cipher = strstr (contents, "DEK-Info: "); + if (cipher) { + cipher += strlen ("DEK-Info: "); + if (g_str_has_prefix (cipher, "AES-128-CBC")) + is_aes = TRUE; + } + + g_free (contents); + return is_aes; +} + +static void +test_encrypt_private_key (const char *path, + const char *password, + const char *desc) +{ + NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; + GByteArray *array, *encrypted, *re_decrypted; + GError *error = NULL; + + array = crypto_decrypt_private_key (path, password, &key_type, &error); + ASSERT (array != NULL, desc, + "couldn't read private key file '%s': %d %s", + path, error->code, error->message); + + ASSERT (key_type == NM_CRYPTO_KEY_TYPE_RSA, desc, + "%s: unexpected private key type (expected %d, got %d)", + path, NM_CRYPTO_KEY_TYPE_RSA, key_type); + + /* Now re-encrypt the private key */ + if (is_cipher_aes (path)) + encrypted = nm_utils_rsa_key_encrypt_aes (array, password, NULL, &error); + else + encrypted = nm_utils_rsa_key_encrypt (array, password, NULL, &error); + ASSERT (encrypted != NULL, desc, + "couldn't re-encrypt private key file '%s': %d %s", + path, error->code, error->message); + + /* Then re-decrypt the private key */ + key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; + re_decrypted = crypto_decrypt_private_key_data (encrypted, password, &key_type, &error); + ASSERT (re_decrypted != NULL, desc, + "couldn't read private key file '%s': %d %s", + path, error->code, error->message); + + ASSERT (key_type == NM_CRYPTO_KEY_TYPE_RSA, desc, + "%s: unexpected private key type (expected %d, got %d)", + path, NM_CRYPTO_KEY_TYPE_RSA, key_type); + + /* Compare the original decrypted key with the re-decrypted key */ + ASSERT (array->len == re_decrypted->len, desc, + "%s: unexpected re-decrypted private key length (expected %d, got %d)", + path, array->len, re_decrypted->len); + + ASSERT (!memcmp (array->data, re_decrypted->data, array->len), desc, + "%s: unexpected private key data", + path); + + g_byte_array_free (re_decrypted, TRUE); + g_byte_array_free (encrypted, TRUE); + g_byte_array_free (array, TRUE); +} + +int main (int argc, char **argv) +{ + GError *error = NULL; + char *progname; + + ASSERT (argc > 2, "test-crypto", + "wrong number of arguments (expected at least an operation and an object)"); + + if (!crypto_init (&error)) + FAIL ("crypto-init", "failed to initialize crypto: %s", error->message); + + if (!strcmp (argv[1], "--cert")) + test_load_cert (argv[2], "cert"); + else if (!strcmp (argv[1], "--key")) { + const char *decrypted_path = (argc == 5) ? argv[4] : NULL; + + ASSERT (argc == 4 || argc == 5, "test-crypto", + "wrong number of arguments (--key <key file> <password> [<decrypted key file>])"); + + test_is_pkcs12 (argv[2], TRUE, "not-pkcs12"); + test_load_private_key (argv[2], argv[3], decrypted_path, FALSE, "private-key"); + test_load_private_key (argv[2], "blahblahblah", NULL, TRUE, "private-key-bad-password"); + test_load_private_key (argv[2], NULL, NULL, TRUE, "private-key-no-password"); + test_encrypt_private_key (argv[2], argv[3], "private-key-rencrypt"); + } else if (!strcmp (argv[1], "--p12")) { + test_is_pkcs12 (argv[2], FALSE, "is-pkcs12"); + test_load_pkcs12 (argv[2], argv[3], FALSE, "pkcs12-private-key"); + test_load_pkcs12 (argv[2], "blahblahblah", TRUE, "pkcs12-private-key-bad-password"); + test_load_pkcs12_no_password (argv[2], "pkcs12-private-key-no-password"); + } else if (!strcmp (argv[1], "--pkcs8")) { + ASSERT (argc == 4, "test-crypto", + "wrong number of arguments (--pkcs8 <key file> <password>)"); + + test_is_pkcs12 (argv[2], TRUE, "not-pkcs12"); + test_load_pkcs8 (argv[2], argv[3], FALSE, "pkcs8-private-key"); + /* Until gnutls and NSS grow support for all the ciphers that openssl + * can use with PKCS#8, we can't actually verify the password. So we + * expect a bad password to work for the time being. + */ + test_load_pkcs8 (argv[2], "blahblahblah", FALSE, "pkcs8-private-key-bad-password"); + } else { + ASSERT (argc > 2, "test-crypto", "unknown test type (not --cert, --key, or --p12)"); + } + + crypto_deinit (); + + progname = g_path_get_basename (argv[0]); + fprintf (stdout, "%s: SUCCESS\n", progname); + g_free (progname); + return 0; +} + diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c new file mode 100644 index 0000000000..0b3283ed51 --- /dev/null +++ b/libnm-core/tests/test-general.c @@ -0,0 +1,2681 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2008 - 2011 Red Hat, Inc. + * + */ + +#include <glib.h> +#include <dbus/dbus-glib.h> +#include <string.h> +#include <netinet/ether.h> +#include <linux/if_infiniband.h> + +#include <nm-utils.h> + +#include "nm-setting-private.h" +#include "nm-setting-connection.h" +#include "nm-setting-vpn.h" +#include "nm-setting-gsm.h" +#include "nm-setting-cdma.h" +#include "nm-setting-wired.h" +#include "nm-setting-wireless-security.h" +#include "nm-setting-ip6-config.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-pppoe.h" +#include "nm-setting-serial.h" +#include "nm-setting-vlan.h" +#include "nm-setting-bond.h" +#include "nm-utils.h" +#include "nm-utils-private.h" +#include "nm-dbus-glib-types.h" + +#include "nm-test-utils.h" + +static void +vpn_check_func (const char *key, const char *value, gpointer user_data) +{ + const char *test = user_data; + + if (!strcmp (key, "foobar1")) { + ASSERT (strcmp (value, "blahblah1") == 0, + test, "unexpected vpn item '%s' / '%s'", key, value); + return; + } + + if (!strcmp (key, "foobar2")) { + ASSERT (strcmp (value, "blahblah2") == 0, + test, "unexpected vpn item '%s' / '%s'", key, value); + return; + } + + if (!strcmp (key, "foobar3")) { + ASSERT (strcmp (value, "blahblah3") == 0, + test, "unexpected vpn item '%s' / '%s'", key, value); + return; + } + + if (!strcmp (key, "foobar4")) { + ASSERT (strcmp (value, "blahblah4") == 0, + test, "unexpected vpn item '%s' / '%s'", key, value); + return; + } + + ASSERT (FALSE, test, "unexpected vpn item '%s'", key); +} + +static void +vpn_check_empty_func (const char *key, const char *value, gpointer user_data) +{ + const char *test = user_data; + + /* We don't expect any values */ + ASSERT (FALSE, test, "unexpected vpn item '%s'", key); +} + +static void +test_setting_vpn_items (void) +{ + NMSettingVPN *s_vpn; + + s_vpn = (NMSettingVPN *) nm_setting_vpn_new (); + ASSERT (s_vpn != NULL, + "vpn-items", + "error creating vpn setting"); + + nm_setting_vpn_add_data_item (s_vpn, "foobar1", "blahblah1"); + nm_setting_vpn_add_data_item (s_vpn, "foobar2", "blahblah2"); + nm_setting_vpn_add_data_item (s_vpn, "foobar3", "blahblah3"); + nm_setting_vpn_add_data_item (s_vpn, "foobar4", "blahblah4"); + + /* Ensure that added values are all present */ + nm_setting_vpn_foreach_data_item (s_vpn, vpn_check_func, "vpn-data"); + nm_setting_vpn_remove_data_item (s_vpn, "foobar1"); + nm_setting_vpn_remove_data_item (s_vpn, "foobar2"); + nm_setting_vpn_remove_data_item (s_vpn, "foobar3"); + nm_setting_vpn_remove_data_item (s_vpn, "foobar4"); + + nm_setting_vpn_add_secret (s_vpn, "foobar1", "blahblah1"); + nm_setting_vpn_add_secret (s_vpn, "foobar2", "blahblah2"); + nm_setting_vpn_add_secret (s_vpn, "foobar3", "blahblah3"); + nm_setting_vpn_add_secret (s_vpn, "foobar4", "blahblah4"); + + /* Ensure that added values are all present */ + nm_setting_vpn_foreach_secret (s_vpn, vpn_check_func, "vpn-secrets"); + nm_setting_vpn_remove_secret (s_vpn, "foobar1"); + nm_setting_vpn_remove_secret (s_vpn, "foobar2"); + nm_setting_vpn_remove_secret (s_vpn, "foobar3"); + nm_setting_vpn_remove_secret (s_vpn, "foobar4"); + + /* Try to add some blank values and make sure they are rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*key != NULL*"); + nm_setting_vpn_add_data_item (s_vpn, NULL, NULL); + g_test_assert_expected_messages (); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strlen (key) > 0*"); + nm_setting_vpn_add_data_item (s_vpn, "", ""); + g_test_assert_expected_messages (); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*item != NULL*"); + nm_setting_vpn_add_data_item (s_vpn, "foobar1", NULL); + g_test_assert_expected_messages (); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strlen (item) > 0*"); + nm_setting_vpn_add_data_item (s_vpn, "foobar1", ""); + g_test_assert_expected_messages (); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*key != NULL*"); + nm_setting_vpn_add_data_item (s_vpn, NULL, "blahblah1"); + g_test_assert_expected_messages (); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strlen (key) > 0*"); + nm_setting_vpn_add_data_item (s_vpn, "", "blahblah1"); + g_test_assert_expected_messages (); + + nm_setting_vpn_foreach_data_item (s_vpn, vpn_check_empty_func, "vpn-data-empty"); + + /* Try to add some blank secrets and make sure they are rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*key != NULL*"); + nm_setting_vpn_add_secret (s_vpn, NULL, NULL); + g_test_assert_expected_messages (); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strlen (key) > 0*"); + nm_setting_vpn_add_secret (s_vpn, "", ""); + g_test_assert_expected_messages (); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*secret != NULL*"); + nm_setting_vpn_add_secret (s_vpn, "foobar1", NULL); + g_test_assert_expected_messages (); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strlen (secret) > 0*"); + nm_setting_vpn_add_secret (s_vpn, "foobar1", ""); + g_test_assert_expected_messages (); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*key != NULL*"); + nm_setting_vpn_add_secret (s_vpn, NULL, "blahblah1"); + g_test_assert_expected_messages (); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strlen (key) > 0*"); + nm_setting_vpn_add_secret (s_vpn, "", "blahblah1"); + g_test_assert_expected_messages (); + + nm_setting_vpn_foreach_secret (s_vpn, vpn_check_empty_func, "vpn-secrets-empty"); + + g_object_unref (s_vpn); +} + +static void +test_setting_vpn_update_secrets (void) +{ + NMConnection *connection; + NMSettingVPN *s_vpn; + GHashTable *settings, *vpn, *secrets; + GValue val = G_VALUE_INIT; + gboolean success; + GError *error = NULL; + const char *tmp; + const char *key1 = "foobar"; + const char *key2 = "blahblah"; + const char *val1 = "value1"; + const char *val2 = "value2"; + + connection = nm_connection_new (); + ASSERT (connection != NULL, + "vpn-update-secrets", + "error creating connection"); + + s_vpn = (NMSettingVPN *) nm_setting_vpn_new (); + ASSERT (s_vpn != NULL, + "vpn-update-secrets", + "error creating vpn setting"); + nm_connection_add_setting (connection, NM_SETTING (s_vpn)); + + settings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_hash_table_destroy); + vpn = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_value_unset); + g_hash_table_insert (settings, NM_SETTING_VPN_SETTING_NAME, vpn); + + secrets = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + g_value_init (&val, DBUS_TYPE_G_MAP_OF_STRING); + g_value_take_boxed (&val, secrets); + g_hash_table_insert (vpn, NM_SETTING_VPN_SECRETS, &val); + + /* Add some secrets */ + g_hash_table_insert (secrets, (char *) key1, (char *) val1); + g_hash_table_insert (secrets, (char *) key2, (char *) val2); + + success = nm_connection_update_secrets (connection, NM_SETTING_VPN_SETTING_NAME, settings, &error); + ASSERT (success == TRUE, + "vpn-update-secrets", "failed to update VPN secrets: %s", error->message); + + /* Read the secrets back out */ + tmp = nm_setting_vpn_get_secret (s_vpn, key1); + ASSERT (tmp != NULL, + "vpn-update-secrets", "unexpected failure getting key #1"); + ASSERT (strcmp (tmp, val1) == 0, + "vpn-update-secrets", "unexpected key #1 value"); + + tmp = nm_setting_vpn_get_secret (s_vpn, key2); + ASSERT (tmp != NULL, + "vpn-update-secrets", "unexpected failure getting key #2"); + ASSERT (strcmp (tmp, val2) == 0, + "vpn-update-secrets", "unexpected key #2 value"); + + g_hash_table_destroy (settings); + g_object_unref (connection); +} + +#define TO_DEL_NUM 50 +typedef struct { + NMSettingVPN *s_vpn; + char *to_del[TO_DEL_NUM]; + guint called; +} IterInfo; + +static void +del_iter_func (const char *key, const char *value, gpointer user_data) +{ + IterInfo *info = user_data; + int i; + + /* Record how many times this function gets called; it should get called + * exactly as many times as there are keys in the hash table, regardless + * of what keys we delete from the table. + */ + info->called++; + + /* During the iteration, remove a bunch of stuff from the table */ + if (info->called == 1) { + for (i = 0; i < TO_DEL_NUM; i++) + nm_setting_vpn_remove_data_item (info->s_vpn, info->to_del[i]); + } +} + +static void +test_setting_vpn_modify_during_foreach (void) +{ + NMSettingVPN *s_vpn; + IterInfo info; + char *key, *val; + int i, u = 0; + + s_vpn = (NMSettingVPN *) nm_setting_vpn_new (); + g_assert (s_vpn); + + for (i = 0; i < TO_DEL_NUM * 2; i++) { + key = g_strdup_printf ("adsfasdfadf%d", i); + val = g_strdup_printf ("42263236236awt%d", i); + nm_setting_vpn_add_data_item (s_vpn, key, val); + + /* Cache some keys to delete */ + if (i % 2) + info.to_del[u++] = g_strdup (key); + + g_free (key); + g_free (val); + } + + /* Iterate over current table keys */ + info.s_vpn = s_vpn; + info.called = 0; + nm_setting_vpn_foreach_data_item (s_vpn, del_iter_func, &info); + + /* Make sure all the things we removed during iteration are really gone */ + for (i = 0; i < TO_DEL_NUM; i++) { + g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, info.to_del[i]), ==, NULL); + g_free (info.to_del[i]); + } + + /* And make sure the foreach callback was called the same number of times + * as there were keys in the table at the beginning of the foreach. + */ + g_assert_cmpint (info.called, ==, TO_DEL_NUM * 2); + + g_object_unref (s_vpn); +} + +static void +test_setting_ip4_config_labels (void) +{ + NMSettingIP4Config *s_ip4; + NMIP4Address *addr; + const char *label; + GPtrArray *addrs; + GSList *labels; + GError *error = NULL; + + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + g_object_set (G_OBJECT (s_ip4), + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NULL); + + /* addr 1 */ + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, 0x01010101); + nm_ip4_address_set_prefix (addr, 24); + + nm_setting_ip4_config_add_address (s_ip4, addr); + nm_ip4_address_unref (addr); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_no_error (error); + + label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 0)); + g_assert_cmpstr (label, ==, NULL); + + /* addr 2 */ + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, 0x02020202); + nm_ip4_address_set_prefix (addr, 24); + + NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_add_address_with_label (s_ip4, addr, "eth0:1")); + nm_ip4_address_unref (addr); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_no_error (error); + + label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 1)); + g_assert_cmpstr (label, ==, "eth0:1"); + + /* addr 3 */ + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, 0x03030303); + nm_ip4_address_set_prefix (addr, 24); + + NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_add_address_with_label (s_ip4, addr, NULL)); + nm_ip4_address_unref (addr); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_no_error (error); + + label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 2)); + g_assert_cmpstr (label, ==, NULL); + + /* Remove addr 1 and re-verify remaining addresses */ + nm_setting_ip4_config_remove_address (s_ip4, 0); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_no_error (error); + + addr = nm_setting_ip4_config_get_address (s_ip4, 0); + g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x02020202); + label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 0)); + g_assert_cmpstr (label, ==, "eth0:1"); + + addr = nm_setting_ip4_config_get_address (s_ip4, 1); + g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x03030303); + label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 1)); + g_assert_cmpstr (label, ==, NULL); + + + /* Test explicit property assignment */ + g_object_get (G_OBJECT (s_ip4), + NM_SETTING_IP4_CONFIG_ADDRESSES, &addrs, + "address-labels", &labels, + NULL); + + nm_setting_ip4_config_clear_addresses (s_ip4); + g_assert_cmpint (nm_setting_ip4_config_get_num_addresses (s_ip4), ==, 0); + + /* Setting addrs but not labels will result in empty labels */ + g_object_set (G_OBJECT (s_ip4), + NM_SETTING_IP4_CONFIG_ADDRESSES, addrs, + NULL); + g_boxed_free (DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT, addrs); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (nm_setting_ip4_config_get_num_addresses (s_ip4), ==, 2); + + addr = nm_setting_ip4_config_get_address (s_ip4, 0); + g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x02020202); + label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 0)); + g_assert_cmpstr (label, ==, NULL); + + addr = nm_setting_ip4_config_get_address (s_ip4, 1); + g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x03030303); + label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 1)); + g_assert_cmpstr (label, ==, NULL); + + /* Setting labels now will leave addresses untouched */ + g_object_set (G_OBJECT (s_ip4), + "address-labels", labels, + NULL); + g_boxed_free (DBUS_TYPE_G_LIST_OF_STRING, labels); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (nm_setting_ip4_config_get_num_addresses (s_ip4), ==, 2); + + addr = nm_setting_ip4_config_get_address (s_ip4, 0); + g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x02020202); + label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 0)); + g_assert_cmpstr (label, ==, "eth0:1"); + + addr = nm_setting_ip4_config_get_address (s_ip4, 1); + g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x03030303); + label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 1)); + g_assert_cmpstr (label, ==, NULL); + + /* Setting labels to a value that's too short or too long will result in + * the setting not verifying. + */ + labels = g_slist_append (NULL, "eth0:2"); + g_object_set (G_OBJECT (s_ip4), + "address-labels", labels, + NULL); + + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_error (error, NM_SETTING_IP4_CONFIG_ERROR, NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY); + g_assert (g_str_has_prefix (error->message, "ipv4.address-labels:")); + g_clear_error (&error); + + labels = g_slist_append (labels, "eth0:3"); + g_object_set (G_OBJECT (s_ip4), + "address-labels", labels, + NULL); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_no_error (error); + + labels = g_slist_append (labels, "eth0:4"); + g_object_set (G_OBJECT (s_ip4), + "address-labels", labels, + NULL); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_error (error, NM_SETTING_IP4_CONFIG_ERROR, NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY); + g_assert (g_str_has_prefix (error->message, "ipv4.address-labels:")); + g_clear_error (&error); + + + g_object_unref (s_ip4); +} + +#define OLD_DBUS_TYPE_G_IP6_ADDRESS (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID)) +#define OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS (dbus_g_type_get_collection ("GPtrArray", OLD_DBUS_TYPE_G_IP6_ADDRESS)) + +/* Test that setting the IPv6 setting's 'addresses' property using the old + * IPv6 address format still works, i.e. that the GValue transformation function + * from old->new is working correctly. + */ +static void +test_setting_ip6_config_old_address_array (void) +{ + NMSettingIP6Config *s_ip6; + GPtrArray *addresses, *read_addresses; + GValueArray *array, *read_array; + GValue element = G_VALUE_INIT, written_value = G_VALUE_INIT, read_value = G_VALUE_INIT; + GByteArray *ba; + const guint8 addr[16] = { 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, + 0x11, 0x22, 0x33, 0x44, 0x66, 0x77, 0x88, 0x99 }; + const guint8 gw[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + guint32 prefix = 56; + GValue *read_addr, *read_prefix, *read_gw; + + s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); + ASSERT (s_ip6 != NULL, + "ip6-old-addr", "error creating IP6 setting"); + + g_value_init (&written_value, OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS); + + addresses = g_ptr_array_new (); + array = g_value_array_new (3); + + /* IP address */ + g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY); + ba = g_byte_array_new (); + g_byte_array_append (ba, &addr[0], sizeof (addr)); + g_value_take_boxed (&element, ba); + g_value_array_append (array, &element); + g_value_unset (&element); + + /* Prefix */ + g_value_init (&element, G_TYPE_UINT); + g_value_set_uint (&element, prefix); + g_value_array_append (array, &element); + g_value_unset (&element); + + g_ptr_array_add (addresses, array); + g_value_set_boxed (&written_value, addresses); + + /* Set the address array on the object */ + g_object_set_property (G_OBJECT (s_ip6), NM_SETTING_IP6_CONFIG_ADDRESSES, &written_value); + + /* Get it back so we can compare it */ + g_value_init (&read_value, DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS); + g_object_get_property (G_OBJECT (s_ip6), NM_SETTING_IP6_CONFIG_ADDRESSES, &read_value); + + ASSERT (G_VALUE_HOLDS (&read_value, DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS), + "ip6-old-addr", "wrong addresses property value type '%s'", + G_VALUE_TYPE_NAME (&read_value)); + + read_addresses = (GPtrArray *) g_value_get_boxed (&read_value); + ASSERT (read_addresses != NULL, + "ip6-old-addr", "missing addresses on readback"); + ASSERT (read_addresses->len == 1, + "ip6-old-addr", "expected one address on readback"); + + read_array = (GValueArray *) g_ptr_array_index (read_addresses, 0); + + read_addr = g_value_array_get_nth (read_array, 0); + ba = g_value_get_boxed (read_addr); + ASSERT (ba->len == sizeof (addr), + "ip6-old-addr", "unexpected address item length %d", ba->len); + ASSERT (memcmp (ba->data, &addr[0], sizeof (addr)) == 0, + "ip6-old-addr", "unexpected failure comparing addresses"); + + read_prefix = g_value_array_get_nth (read_array, 1); + ASSERT (g_value_get_uint (read_prefix) == prefix, + "ip6-old-addr", "unexpected failure comparing prefix"); + + /* Ensure the gateway is all zeros, which is how the 2-item to 3-item + * conversion happens. + */ + read_gw = g_value_array_get_nth (read_array, 2); + ba = g_value_get_boxed (read_gw); + ASSERT (ba->len == sizeof (gw), + "ip6-old-addr", "unexpected gateway item length %d", ba->len); + ASSERT (memcmp (ba->data, &gw[0], sizeof (gw)) == 0, + "ip6-old-addr", "unexpected failure comparing gateways"); + + g_value_unset (&written_value); + g_value_unset (&read_value); + g_object_unref (s_ip6); +} + +static void +test_setting_gsm_apn_spaces (void) +{ + NMSettingGsm *s_gsm; + const char *tmp; + + s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); + ASSERT (s_gsm != NULL, + "gsm-apn-spaces", + "error creating GSM setting"); + + /* Trailing space */ + g_object_set (s_gsm, NM_SETTING_GSM_APN, "foobar ", NULL); + tmp = nm_setting_gsm_get_apn (s_gsm); + ASSERT (tmp != NULL, + "gsm-apn-spaces", "empty APN"); + ASSERT (strcmp (tmp, "foobar") == 0, + "gsm-apn-spaces", "unexpected APN"); + + /* Leading space */ + g_object_set (s_gsm, NM_SETTING_GSM_APN, " foobar", NULL); + tmp = nm_setting_gsm_get_apn (s_gsm); + ASSERT (tmp != NULL, + "gsm-apn-spaces", "empty APN"); + ASSERT (strcmp (tmp, "foobar") == 0, + "gsm-apn-spaces", "unexpected APN"); +} + +static void +test_setting_gsm_apn_bad_chars (void) +{ + NMSettingGsm *s_gsm; + + s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); + ASSERT (s_gsm != NULL, + "gsm-apn-bad-chars", + "error creating GSM setting"); + + g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL); + + /* Make sure a valid APN works */ + g_object_set (s_gsm, NM_SETTING_GSM_APN, "foobar123.-baz", NULL); + ASSERT (nm_setting_verify (NM_SETTING (s_gsm), NULL, NULL) == TRUE, + "gsm-apn-bad-chars", "unexpectedly invalid GSM setting"); + + /* Random invalid chars */ + g_object_set (s_gsm, NM_SETTING_GSM_APN, "@#%$@#%@#%", NULL); + ASSERT (nm_setting_verify (NM_SETTING (s_gsm), NULL, NULL) == FALSE, + "gsm-apn-bad-chars", "unexpectedly valid GSM setting"); + + /* Spaces */ + g_object_set (s_gsm, NM_SETTING_GSM_APN, "foobar baz", NULL); + ASSERT (nm_setting_verify (NM_SETTING (s_gsm), NULL, NULL) == FALSE, + "gsm-apn-bad-chars", "unexpectedly valid GSM setting"); + + /* 0 characters long */ + g_object_set (s_gsm, NM_SETTING_GSM_APN, "", NULL); + ASSERT (nm_setting_verify (NM_SETTING (s_gsm), NULL, NULL) == FALSE, + "gsm-apn-bad-chars", "unexpectedly valid GSM setting"); + + /* 65-character long */ + g_object_set (s_gsm, NM_SETTING_GSM_APN, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl1", NULL); + ASSERT (nm_setting_verify (NM_SETTING (s_gsm), NULL, NULL) == FALSE, + "gsm-apn-bad-chars", "unexpectedly valid GSM setting"); +} + +static void +test_setting_gsm_apn_underscore (void) +{ + NMSettingGsm *s_gsm; + GError *error = NULL; + gboolean success; + + s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); + g_assert (s_gsm); + + g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL); + + /* 65-character long */ + g_object_set (s_gsm, NM_SETTING_GSM_APN, "foobar_baz", NULL); + success = nm_setting_verify (NM_SETTING (s_gsm), NULL, &error); + g_assert_no_error (error); + g_assert (success == TRUE); +} + +static void +test_setting_gsm_without_number (void) +{ + NMSettingGsm *s_gsm; + GError *error = NULL; + gboolean success; + + s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); + g_assert (s_gsm); + + g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, NULL, NULL); + success = nm_setting_verify (NM_SETTING (s_gsm), NULL, &error); + g_assert_no_error (error); + g_assert (success == TRUE); + + g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "", NULL); + success = nm_setting_verify (NM_SETTING (s_gsm), NULL, &error); + g_assert_error (error, NM_SETTING_GSM_ERROR, NM_SETTING_GSM_ERROR_INVALID_PROPERTY); + g_error_free (error); +} + +static NMSettingWirelessSecurity * +make_test_wsec_setting (const char *detail) +{ + NMSettingWirelessSecurity *s_wsec; + + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + ASSERT (s_wsec != NULL, detail, "error creating setting"); + + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", + NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, "foobarbaz", + NM_SETTING_WIRELESS_SECURITY_PSK, "random psk", + NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, "aaaaaaaaaa", + NULL); + + return s_wsec; +} + +static void +test_setting_to_hash_all (void) +{ + NMSettingWirelessSecurity *s_wsec; + GHashTable *hash; + + s_wsec = make_test_wsec_setting ("setting-to-hash-all"); + + hash = nm_setting_to_hash (NM_SETTING (s_wsec), NM_SETTING_HASH_FLAG_ALL); + + /* Make sure all keys are there */ + ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT), + "setting-to-hash-all", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME), + "setting-to-hash-all", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME); + ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_PSK), + "setting-to-hash-all", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_PSK); + ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0), + "setting-to-hash-all", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_WEP_KEY0); + + g_hash_table_destroy (hash); + g_object_unref (s_wsec); +} + +static void +test_setting_to_hash_no_secrets (void) +{ + NMSettingWirelessSecurity *s_wsec; + GHashTable *hash; + + s_wsec = make_test_wsec_setting ("setting-to-hash-no-secrets"); + + hash = nm_setting_to_hash (NM_SETTING (s_wsec), NM_SETTING_HASH_FLAG_NO_SECRETS); + + /* Make sure non-secret keys are there */ + ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT), + "setting-to-hash-no-secrets", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME), + "setting-to-hash-no-secrets", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME); + + /* Make sure secrets are not there */ + ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_PSK) == NULL, + "setting-to-hash-no-secrets", "unexpectedly present " NM_SETTING_WIRELESS_SECURITY_PSK); + ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0) == NULL, + "setting-to-hash-no-secrets", "unexpectedly present " NM_SETTING_WIRELESS_SECURITY_WEP_KEY0); + + g_hash_table_destroy (hash); + g_object_unref (s_wsec); +} + +static void +test_setting_to_hash_only_secrets (void) +{ + NMSettingWirelessSecurity *s_wsec; + GHashTable *hash; + + s_wsec = make_test_wsec_setting ("setting-to-hash-only-secrets"); + + hash = nm_setting_to_hash (NM_SETTING (s_wsec), NM_SETTING_HASH_FLAG_ONLY_SECRETS); + + /* Make sure non-secret keys are there */ + ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT) == NULL, + "setting-to-hash-only-secrets", "unexpectedly present " NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME) == NULL, + "setting-to-hash-only-secrets", "unexpectedly present " NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME); + + /* Make sure secrets are not there */ + ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_PSK), + "setting-to-hash-only-secrets", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_PSK); + ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0), + "setting-to-hash-only-secrets", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_WEP_KEY0); + + g_hash_table_destroy (hash); + g_object_unref (s_wsec); +} + +static void +test_connection_to_hash_setting_name (void) +{ + NMConnection *connection; + NMSettingWirelessSecurity *s_wsec; + GHashTable *hash; + + connection = nm_connection_new (); + s_wsec = make_test_wsec_setting ("connection-to-hash-setting-name"); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + + hash = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_ALL); + + /* Make sure the keys of the first level hash are setting names, not + * the GType name of the setting objects. + */ + ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) != NULL, + "connection-to-hash-setting-name", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + + g_hash_table_destroy (hash); + g_object_unref (connection); +} + +static void +test_setting_new_from_hash (void) +{ + NMSettingWirelessSecurity *s_wsec; + GHashTable *hash; + + s_wsec = make_test_wsec_setting ("setting-to-hash-all"); + hash = nm_setting_to_hash (NM_SETTING (s_wsec), NM_SETTING_HASH_FLAG_ALL); + g_object_unref (s_wsec); + + s_wsec = (NMSettingWirelessSecurity *) nm_setting_new_from_hash (NM_TYPE_SETTING_WIRELESS_SECURITY, hash); + g_hash_table_destroy (hash); + + 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_leap_username (s_wsec), ==, "foobarbaz"); + g_assert_cmpstr (nm_setting_wireless_security_get_psk (s_wsec), ==, "random psk"); + g_object_unref (s_wsec); +} + +static NMConnection * +new_test_connection (void) +{ + NMConnection *connection; + NMSetting *setting; + char *uuid; + guint64 timestamp = time (NULL); + + connection = nm_connection_new (); + + setting = nm_setting_connection_new (); + uuid = nm_utils_uuid_generate (); + g_object_set (G_OBJECT (setting), + NM_SETTING_CONNECTION_ID, "foobar", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_TIMESTAMP, timestamp, + NULL); + g_free (uuid); + nm_connection_add_setting (connection, setting); + + setting = nm_setting_wired_new (); + g_object_set (G_OBJECT (setting), + NM_SETTING_WIRED_MTU, 1592, + NULL); + nm_connection_add_setting (connection, setting); + + setting = nm_setting_ip4_config_new (); + g_object_set (G_OBJECT (setting), + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, "eyeofthetiger", + NULL); + nm_connection_add_setting (connection, setting); + + return connection; +} + +static GValue * +string_to_gvalue (const char *str) +{ + GValue *val; + + val = g_slice_new0 (GValue); + g_value_init (val, G_TYPE_STRING); + g_value_set_string (val, str); + return val; +} + +static void +destroy_gvalue (gpointer data) +{ + g_value_unset ((GValue *) data); + g_slice_free (GValue, data); +} + +static GHashTable * +new_connection_hash (char **out_uuid, + const char **out_expected_id, + const char **out_expected_ip6_method) +{ + GHashTable *hash; + GHashTable *setting; + + hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy); + + *out_uuid = nm_utils_uuid_generate (); + *out_expected_id = "My happy connection"; + *out_expected_ip6_method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; + + /* Connection setting */ + setting = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_gvalue); + g_hash_table_insert (setting, + g_strdup (NM_SETTING_NAME), + string_to_gvalue (NM_SETTING_CONNECTION_SETTING_NAME)); + g_hash_table_insert (setting, + g_strdup (NM_SETTING_CONNECTION_ID), + string_to_gvalue (*out_expected_id)); + g_hash_table_insert (setting, + g_strdup (NM_SETTING_CONNECTION_UUID), + string_to_gvalue (*out_uuid)); + g_hash_table_insert (setting, + g_strdup (NM_SETTING_CONNECTION_TYPE), + string_to_gvalue (NM_SETTING_WIRED_SETTING_NAME)); + g_hash_table_insert (hash, g_strdup (NM_SETTING_CONNECTION_SETTING_NAME), setting); + + /* Wired setting */ + setting = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_gvalue); + g_hash_table_insert (hash, g_strdup (NM_SETTING_WIRED_SETTING_NAME), setting); + + /* IP6 */ + setting = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_gvalue); + g_hash_table_insert (setting, + g_strdup (NM_SETTING_IP6_CONFIG_METHOD), + string_to_gvalue (*out_expected_ip6_method)); + g_hash_table_insert (hash, g_strdup (NM_SETTING_IP6_CONFIG_SETTING_NAME), setting); + + return hash; +} + +static void +test_connection_replace_settings () +{ + NMConnection *connection; + GHashTable *new_settings; + GError *error = NULL; + gboolean success; + NMSettingConnection *s_con; + NMSettingIP6Config *s_ip6; + char *uuid = NULL; + const char *expected_id = NULL, *expected_method = NULL; + + connection = new_test_connection (); + + new_settings = new_connection_hash (&uuid, &expected_id, &expected_method); + g_assert (new_settings); + + /* Replace settings and test */ + success = nm_connection_replace_settings (connection, new_settings, &error); + g_assert_no_error (error); + g_assert (success); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id); + g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, uuid); + + g_assert (nm_connection_get_setting_wired (connection)); + g_assert (!nm_connection_get_setting_ip4_config (connection)); + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + g_assert (s_ip6); + g_assert_cmpstr (nm_setting_ip6_config_get_method (s_ip6), ==, expected_method); + + g_free (uuid); + g_hash_table_destroy (new_settings); + g_object_unref (connection); +} + +static void +test_connection_replace_settings_from_connection () +{ + NMConnection *connection, *replacement; + GError *error = NULL; + gboolean success; + NMSettingConnection *s_con; + NMSetting *setting; + GByteArray *ssid; + char *uuid = NULL; + const char *expected_id = "Awesome connection"; + + connection = new_test_connection (); + g_assert (connection); + + replacement = nm_connection_new (); + g_assert (replacement); + + /* New connection setting */ + setting = nm_setting_connection_new (); + g_assert (setting); + + uuid = nm_utils_uuid_generate (); + g_object_set (setting, + NM_SETTING_CONNECTION_ID, expected_id, + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + nm_connection_add_setting (replacement, setting); + + /* New wifi setting */ + setting = nm_setting_wireless_new (); + g_assert (setting); + + ssid = g_byte_array_new (); + g_byte_array_append (ssid, (const guint8 *) "1234567", 7); + g_object_set (setting, + NM_SETTING_WIRELESS_SSID, ssid, + NM_SETTING_WIRELESS_MODE, "infrastructure", + NULL); + g_byte_array_free (ssid, TRUE); + nm_connection_add_setting (replacement, setting); + + /* Replace settings and test */ + success = nm_connection_replace_settings_from_connection (connection, replacement, &error); + g_assert_no_error (error); + g_assert (success); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id); + g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, uuid); + + g_assert (!nm_connection_get_setting_wired (connection)); + g_assert (!nm_connection_get_setting_ip6_config (connection)); + g_assert (nm_connection_get_setting_wireless (connection)); + + g_free (uuid); + g_object_unref (replacement); + g_object_unref (connection); +} + +static void +test_connection_new_from_hash () +{ + NMConnection *connection; + GHashTable *new_settings; + GError *error = NULL; + NMSettingConnection *s_con; + NMSettingIP6Config *s_ip6; + char *uuid = NULL; + const char *expected_id = NULL, *expected_method = NULL; + + new_settings = new_connection_hash (&uuid, &expected_id, &expected_method); + g_assert (new_settings); + + /* Replace settings and test */ + connection = nm_connection_new_from_hash (new_settings, &error); + g_assert_no_error (error); + g_assert (connection); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id); + g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, uuid); + + g_assert (nm_connection_get_setting_wired (connection)); + g_assert (!nm_connection_get_setting_ip4_config (connection)); + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + g_assert (s_ip6); + g_assert_cmpstr (nm_setting_ip6_config_get_method (s_ip6), ==, expected_method); + + g_free (uuid); + g_hash_table_destroy (new_settings); + g_object_unref (connection); +} + +static void +check_permission (NMSettingConnection *s_con, + guint32 idx, + const char *expected_uname, + const char *tag) +{ + gboolean success; + const char *ptype = NULL, *pitem = NULL, *detail = NULL; + + success = nm_setting_connection_get_permission (s_con, 0, &ptype, &pitem, &detail); + ASSERT (success == TRUE, tag, "unexpected failure getting added permission"); + + /* Permission type */ + ASSERT (ptype != NULL, tag, "unexpected failure getting permission type"); + ASSERT (strcmp (ptype, "user") == 0, tag, "retrieved unexpected permission type"); + + /* Permission item */ + ASSERT (pitem != NULL, tag, "unexpected failure getting permission item"); + ASSERT (strcmp (pitem, expected_uname) == 0, tag, "retrieved unexpected permission item"); + + ASSERT (detail == NULL, tag, "unexpected success getting permission detail"); +} + +#define TEST_UNAME "asdfasfasdf" + +static void +test_setting_connection_permissions_helpers (void) +{ + NMSettingConnection *s_con; + gboolean success; + char buf[9] = { 0x61, 0x62, 0x63, 0xff, 0xfe, 0xfd, 0x23, 0x01, 0x00 }; + GSList *list = NULL; + const char *expected_perm = "user:" TEST_UNAME ":"; + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + + /* Ensure a bad [type] is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strcmp (ptype, \"user\") == 0*"); + success = nm_setting_connection_add_permission (s_con, "foobar", "blah", NULL); + g_test_assert_expected_messages (); + ASSERT (success == FALSE, + "setting-connection-permissions-helpers", "unexpected success adding bad permission type #1"); + + /* Ensure a bad [type] is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*ptype*"); + success = nm_setting_connection_add_permission (s_con, NULL, "blah", NULL); + g_test_assert_expected_messages (); + ASSERT (success == FALSE, + "setting-connection-permissions-helpers", "unexpected success adding bad permission type #2"); + + /* Ensure a bad [item] is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*uname*"); + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*p != NULL*"); + success = nm_setting_connection_add_permission (s_con, "user", NULL, NULL); + g_test_assert_expected_messages (); + ASSERT (success == FALSE, + "setting-connection-permissions-helpers", "unexpected success adding bad permission item #1"); + + /* Ensure a bad [item] is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*uname[0] != '\\0'*"); + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*p != NULL*"); + success = nm_setting_connection_add_permission (s_con, "user", "", NULL); + g_test_assert_expected_messages (); + ASSERT (success == FALSE, + "setting-connection-permissions-helpers", "unexpected success adding bad permission item #2"); + + /* Ensure an [item] with ':' is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strchr (uname, ':')*"); + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*p != NULL*"); + success = nm_setting_connection_add_permission (s_con, "user", "ad:asdf", NULL); + g_test_assert_expected_messages (); + ASSERT (success == FALSE, + "setting-connection-permissions-helpers", "unexpected success adding bad permission item #3"); + + /* Ensure a non-UTF-8 [item] is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*g_utf8_validate (uname, -1, NULL)*"); + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*p != NULL*"); + success = nm_setting_connection_add_permission (s_con, "user", buf, NULL); + g_test_assert_expected_messages (); + ASSERT (success == FALSE, + "setting-connection-permissions-helpers", "unexpected success adding bad permission item #4"); + + /* Ensure a non-NULL [detail] is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*detail == NULL*"); + success = nm_setting_connection_add_permission (s_con, "user", "dafasdf", "asdf"); + g_test_assert_expected_messages (); + ASSERT (success == FALSE, + "setting-connection-permissions-helpers", "unexpected success adding bad detail"); + + /* Ensure a valid call results in success */ + success = nm_setting_connection_add_permission (s_con, "user", TEST_UNAME, NULL); + ASSERT (success == TRUE, + "setting-connection-permissions-helpers", "unexpected failure adding valid user permisson"); + + ASSERT (nm_setting_connection_get_num_permissions (s_con) == 1, + "setting-connection-permissions-helpers", "unexpected failure getting number of permissions"); + + check_permission (s_con, 0, TEST_UNAME, "setting-connection-permissions-helpers"); + + /* Check the actual GObject property just to be paranoid */ + g_object_get (G_OBJECT (s_con), NM_SETTING_CONNECTION_PERMISSIONS, &list, NULL); + ASSERT (list != NULL, + "setting-connection-permissions-helpers", "unexpected failure getting permissions list"); + ASSERT (g_slist_length (list) == 1, + "setting-connection-permissions-helpers", "unexpected failure getting number of permissions in list"); + ASSERT (strcmp (list->data, expected_perm) == 0, + "setting-connection-permissions-helpers", "unexpected permission property data"); + g_slist_free_full (list, g_free); + + /* Now remove that permission and ensure we have 0 permissions */ + nm_setting_connection_remove_permission (s_con, 0); + ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0, + "setting-connection-permissions-helpers", "unexpected failure removing permission"); + + g_object_unref (s_con); +} + +static void +add_permission_property (NMSettingConnection *s_con, + const char *ptype, + const char *pitem, + int pitem_len, + const char *detail) +{ + GString *str; + GSList *list = NULL; + + str = g_string_sized_new (50); + if (ptype) + g_string_append (str, ptype); + g_string_append_c (str, ':'); + + if (pitem) { + if (pitem_len >= 0) + g_string_append_len (str, pitem, pitem_len); + else + g_string_append (str, pitem); + } + + g_string_append_c (str, ':'); + + if (detail) + g_string_append (str, detail); + + list = g_slist_append (list, str->str); + g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_PERMISSIONS, list, NULL); + + g_string_free (str, TRUE); + g_slist_free (list); +} + +static void +test_setting_connection_permissions_property (void) +{ + NMSettingConnection *s_con; + gboolean success; + char buf[9] = { 0x61, 0x62, 0x63, 0xff, 0xfe, 0xfd, 0x23, 0x01, 0x00 }; + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + + /* Ensure a bad [type] is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strncmp (str, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0*"); + add_permission_property (s_con, "foobar", "blah", -1, NULL); + g_test_assert_expected_messages (); + ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0, + "setting-connection-permissions-property", "unexpected success adding bad permission type #1"); + + /* Ensure a bad [type] is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strncmp (str, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0*"); + add_permission_property (s_con, NULL, "blah", -1, NULL); + g_test_assert_expected_messages (); + ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0, + "setting-connection-permissions-property", "unexpected success adding bad permission type #2"); + + /* Ensure a bad [item] is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*last_colon > str*"); + add_permission_property (s_con, "user", NULL, -1, NULL); + g_test_assert_expected_messages (); + ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0, + "setting-connection-permissions-property", "unexpected success adding bad permission item #1"); + + /* Ensure a bad [item] is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*last_colon > str*"); + add_permission_property (s_con, "user", "", -1, NULL); + g_test_assert_expected_messages (); + ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0, + "setting-connection-permissions-property", "unexpected success adding bad permission item #2"); + + /* Ensure an [item] with ':' in the middle is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*str[i] != ':'*"); + add_permission_property (s_con, "user", "ad:asdf", -1, NULL); + g_test_assert_expected_messages (); + ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0, + "setting-connection-permissions-property", "unexpected success adding bad permission item #3"); + + /* Ensure an [item] with ':' at the end is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*str[i] != ':'*"); + add_permission_property (s_con, "user", "adasdfaf:", -1, NULL); + g_test_assert_expected_messages (); + ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0, + "setting-connection-permissions-property", "unexpected success adding bad permission item #4"); + + /* Ensure a non-UTF-8 [item] is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*g_utf8_validate (str, -1, NULL)*"); + add_permission_property (s_con, "user", buf, (int) sizeof (buf), NULL); + g_test_assert_expected_messages (); + ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0, + "setting-connection-permissions-property", "unexpected success adding bad permission item #5"); + + /* Ensure a non-NULL [detail] is rejected */ + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*(last_colon + 1) == '\\0'*"); + add_permission_property (s_con, "user", "dafasdf", -1, "asdf"); + g_test_assert_expected_messages (); + ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0, + "setting-connection-permissions-property", "unexpected success adding bad detail"); + + /* Ensure a valid call results in success */ + success = nm_setting_connection_add_permission (s_con, "user", TEST_UNAME, NULL); + ASSERT (nm_setting_connection_get_num_permissions (s_con) == 1, + "setting-connection-permissions-property", "unexpected failure adding valid user permisson"); + + check_permission (s_con, 0, TEST_UNAME, "setting-connection-permissions-property"); + + /* Now remove that permission and ensure we have 0 permissions */ + nm_setting_connection_remove_permission (s_con, 0); + ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0, + "setting-connection-permissions-property", "unexpected failure removing permission"); + + g_object_unref (s_con); +} + +static void +test_connection_compare_same (void) +{ + NMConnection *a, *b; + + a = new_test_connection (); + b = nm_connection_duplicate (a); + g_assert (nm_connection_compare (a, b, NM_SETTING_COMPARE_FLAG_EXACT)); + g_object_unref (a); + g_object_unref (b); +} + +static void +test_connection_compare_key_only_in_a (void) +{ + NMConnection *a, *b; + NMSettingConnection *s_con; + + a = new_test_connection (); + b = nm_connection_duplicate (a); + s_con = (NMSettingConnection *) nm_connection_get_setting (b, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, (guint64) 0, NULL); + + g_assert (!nm_connection_compare (a, b, NM_SETTING_COMPARE_FLAG_EXACT)); + g_object_unref (a); + g_object_unref (b); +} + +static void +test_connection_compare_setting_only_in_a (void) +{ + NMConnection *a, *b; + + a = new_test_connection (); + b = nm_connection_duplicate (a); + nm_connection_remove_setting (b, NM_TYPE_SETTING_IP4_CONFIG); + g_assert (!nm_connection_compare (a, b, NM_SETTING_COMPARE_FLAG_EXACT)); + g_object_unref (a); + g_object_unref (b); +} + +static void +test_connection_compare_key_only_in_b (void) +{ + NMConnection *a, *b; + NMSettingConnection *s_con; + + a = new_test_connection (); + b = nm_connection_duplicate (a); + s_con = (NMSettingConnection *) nm_connection_get_setting (b, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, (guint64) 0, NULL); + + g_assert (!nm_connection_compare (a, b, NM_SETTING_COMPARE_FLAG_EXACT)); + g_object_unref (a); + g_object_unref (b); +} + +static void +test_connection_compare_setting_only_in_b (void) +{ + NMConnection *a, *b; + + a = new_test_connection (); + b = nm_connection_duplicate (a); + nm_connection_remove_setting (a, NM_TYPE_SETTING_IP4_CONFIG); + g_assert (!nm_connection_compare (a, b, NM_SETTING_COMPARE_FLAG_EXACT)); + g_object_unref (a); + g_object_unref (b); +} + +typedef struct { + const char *key_name; + guint32 result; +} DiffKey; + +typedef struct { + const char *name; + DiffKey keys[30]; +} DiffSetting; + +#define ARRAY_LEN(a) (sizeof (a) / sizeof (a[0])) + +static void +ensure_diffs (GHashTable *diffs, const DiffSetting *check, gsize n_check) +{ + guint i; + + g_assert (g_hash_table_size (diffs) == n_check); + + /* Loop through the settings */ + for (i = 0; i < n_check; i++) { + GHashTable *setting_hash; + guint z = 0; + + setting_hash = g_hash_table_lookup (diffs, check[i].name); + g_assert (setting_hash); + + /* Get the number of keys to check */ + while (check[i].keys[z].key_name) + z++; + g_assert (g_hash_table_size (setting_hash) == z); + + /* Now compare the actual keys */ + for (z = 0; check[i].keys[z].key_name; z++) { + NMSettingDiffResult result; + + result = GPOINTER_TO_UINT (g_hash_table_lookup (setting_hash, check[i].keys[z].key_name)); + g_assert (result == check[i].keys[z].result); + } + } +} + +static void +test_connection_diff_a_only (void) +{ + NMConnection *connection; + GHashTable *out_diffs = NULL; + gboolean same; + const DiffSetting settings[] = { + { NM_SETTING_CONNECTION_SETTING_NAME, { + { NM_SETTING_CONNECTION_ID, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_UUID, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_INTERFACE_NAME, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_TYPE, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_TIMESTAMP, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_AUTOCONNECT, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_READ_ONLY, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_PERMISSIONS, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_ZONE, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_MASTER, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_SECONDARIES, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A }, + { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN } + } }, + { NM_SETTING_WIRED_SETTING_NAME, { + { NM_SETTING_WIRED_PORT, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_SPEED, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_DUPLEX, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_AUTO_NEGOTIATE, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_MAC_ADDRESS, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_CLONED_MAC_ADDRESS, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_MTU, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_S390_SUBCHANNELS, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_S390_NETTYPE, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_S390_OPTIONS, NM_SETTING_DIFF_RESULT_IN_A }, + { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }, + } }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, { + { NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_DNS, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_DNS_SEARCH, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_ADDRESSES, NM_SETTING_DIFF_RESULT_IN_A }, + { "address-labels", NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_ROUTES, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_NEVER_DEFAULT, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_MAY_FAIL, NM_SETTING_DIFF_RESULT_IN_A }, + { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }, + } }, + }; + + connection = new_test_connection (); + + same = nm_connection_diff (connection, NULL, NM_SETTING_COMPARE_FLAG_EXACT, &out_diffs); + g_assert (same == FALSE); + g_assert (out_diffs != NULL); + g_assert (g_hash_table_size (out_diffs) > 0); + + ensure_diffs (out_diffs, settings, ARRAY_LEN (settings)); + + g_hash_table_destroy (out_diffs); + g_object_unref (connection); +} + +static void +test_connection_diff_same (void) +{ + NMConnection *a, *b; + GHashTable *out_diffs = NULL; + gboolean same; + + a = new_test_connection (); + b = nm_connection_duplicate (a); + + same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_EXACT, &out_diffs); + g_assert (same == TRUE); + g_assert (out_diffs == NULL); + g_object_unref (a); + g_object_unref (b); +} + +static void +test_connection_diff_different (void) +{ + NMConnection *a, *b; + GHashTable *out_diffs = NULL; + NMSettingIP4Config *s_ip4; + gboolean same; + const DiffSetting settings[] = { + { NM_SETTING_IP4_CONFIG_SETTING_NAME, { + { NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_DIFF_RESULT_IN_A | NM_SETTING_DIFF_RESULT_IN_B }, + { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }, + } }, + }; + + a = new_test_connection (); + b = nm_connection_duplicate (a); + s_ip4 = nm_connection_get_setting_ip4_config (a); + g_assert (s_ip4); + g_object_set (G_OBJECT (s_ip4), + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NULL); + + same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_EXACT, &out_diffs); + g_assert (same == FALSE); + g_assert (out_diffs != NULL); + g_assert (g_hash_table_size (out_diffs) > 0); + + ensure_diffs (out_diffs, settings, ARRAY_LEN (settings)); + + g_hash_table_destroy (out_diffs); + g_object_unref (a); + g_object_unref (b); +} + +static void +test_connection_diff_no_secrets (void) +{ + NMConnection *a, *b; + GHashTable *out_diffs = NULL; + NMSetting *s_pppoe; + gboolean same; + const DiffSetting settings[] = { + { NM_SETTING_PPPOE_SETTING_NAME, { + { NM_SETTING_PPPOE_PASSWORD, NM_SETTING_DIFF_RESULT_IN_B }, + { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }, + } }, + }; + + a = new_test_connection (); + s_pppoe = nm_setting_pppoe_new (); + g_object_set (G_OBJECT (s_pppoe), + NM_SETTING_PPPOE_USERNAME, "thomas", + NULL); + nm_connection_add_setting (a, s_pppoe); + + b = nm_connection_duplicate (a); + + /* Add a secret to B */ + s_pppoe = NM_SETTING (nm_connection_get_setting_pppoe (b)); + g_assert (s_pppoe); + g_object_set (G_OBJECT (s_pppoe), + NM_SETTING_PPPOE_PASSWORD, "secretpassword", + NULL); + + /* Make sure the diff returns no results as secrets are ignored */ + same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS, &out_diffs); + g_assert (same == TRUE); + g_assert (out_diffs == NULL); + + /* Now make sure the diff returns results if secrets are not ignored */ + same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_EXACT, &out_diffs); + g_assert (same == FALSE); + g_assert (out_diffs != NULL); + g_assert (g_hash_table_size (out_diffs) > 0); + + ensure_diffs (out_diffs, settings, ARRAY_LEN (settings)); + + g_hash_table_destroy (out_diffs); + g_object_unref (a); + g_object_unref (b); +} + +static void +test_connection_diff_inferrable (void) +{ + NMConnection *a, *b; + GHashTable *out_diffs = NULL; + gboolean same; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIP4Config *s_ip4; + char *uuid; + const DiffSetting settings[] = { + { NM_SETTING_CONNECTION_SETTING_NAME, { + { NM_SETTING_CONNECTION_INTERFACE_NAME, NM_SETTING_DIFF_RESULT_IN_A }, + { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }, + } }, + }; + + a = new_test_connection (); + b = nm_connection_duplicate (a); + + /* Change the UUID, wired MTU, and set ignore-auto-dns */ + s_con = nm_connection_get_setting_connection (a); + g_assert (s_con); + uuid = nm_utils_uuid_generate (); + g_object_set (G_OBJECT (s_con), + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_ID, "really neat connection", + NULL); + g_free (uuid); + + s_wired = nm_connection_get_setting_wired (a); + g_assert (s_wired); + g_object_set (G_OBJECT (s_wired), NM_SETTING_WIRED_MTU, 300, NULL); + + s_ip4 = nm_connection_get_setting_ip4_config (a); + g_assert (s_ip4); + g_object_set (G_OBJECT (s_ip4), NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS, TRUE, NULL); + + /* Make sure the diff returns no results as secrets are ignored */ + same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_INFERRABLE, &out_diffs); + g_assert (same == TRUE); + g_assert (out_diffs == NULL); + + /* And change a INFERRABLE property to ensure that it shows up in the diff results */ + g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_INTERFACE_NAME, "usb0", NULL); + + /* Make sure the diff returns no results as secrets are ignored */ + same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_INFERRABLE, &out_diffs); + g_assert (same == FALSE); + g_assert (out_diffs != NULL); + g_assert (g_hash_table_size (out_diffs) > 0); + + ensure_diffs (out_diffs, settings, ARRAY_LEN (settings)); + + g_hash_table_destroy (out_diffs); + g_object_unref (a); + g_object_unref (b); +} + +static void +add_generic_settings (NMConnection *connection, const char *ctype) +{ + NMSetting *setting; + char *uuid; + + uuid = nm_utils_uuid_generate (); + + setting = nm_setting_connection_new (); + g_object_set (setting, + NM_SETTING_CONNECTION_ID, "asdfasdfadf", + NM_SETTING_CONNECTION_TYPE, ctype, + NM_SETTING_CONNECTION_UUID, uuid, + NULL); + nm_connection_add_setting (connection, setting); + + g_free (uuid); + + setting = nm_setting_ip4_config_new (); + g_object_set (setting, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + nm_connection_add_setting (connection, setting); + + setting = nm_setting_ip6_config_new (); + g_object_set (setting, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); + nm_connection_add_setting (connection, setting); +} + +static void +test_connection_good_base_types (void) +{ + NMConnection *connection; + NMSetting *setting; + gboolean success; + GError *error = NULL; + GByteArray *array; + const guint8 bdaddr[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }; + + /* Try a basic wired connection */ + connection = nm_connection_new (); + add_generic_settings (connection, NM_SETTING_WIRED_SETTING_NAME); + setting = nm_setting_wired_new (); + nm_connection_add_setting (connection, setting); + + success = nm_connection_verify (connection, &error); + g_assert_no_error (error); + g_assert (success); + g_object_unref (connection); + + /* Try a wired PPPoE connection */ + connection = nm_connection_new (); + add_generic_settings (connection, NM_SETTING_PPPOE_SETTING_NAME); + setting = nm_setting_pppoe_new (); + g_object_set (setting, NM_SETTING_PPPOE_USERNAME, "bob smith", NULL); + nm_connection_add_setting (connection, setting); + + success = nm_connection_verify (connection, &error); + g_assert_no_error (error); + g_assert (success); + g_object_unref (connection); + + /* Wifi connection */ + connection = nm_connection_new (); + add_generic_settings (connection, NM_SETTING_WIRELESS_SETTING_NAME); + + setting = nm_setting_wireless_new (); + array = g_byte_array_new (); + g_byte_array_append (array, (const guint8 *) "1234567", 7); + g_object_set (setting, + NM_SETTING_WIRELESS_SSID, array, + NM_SETTING_WIRELESS_MODE, "infrastructure", + NULL); + g_byte_array_free (array, TRUE); + nm_connection_add_setting (connection, setting); + + success = nm_connection_verify (connection, &error); + g_assert_no_error (error); + g_assert (success); + g_object_unref (connection); + + /* Bluetooth connection */ + connection = nm_connection_new (); + add_generic_settings (connection, NM_SETTING_BLUETOOTH_SETTING_NAME); + + setting = nm_setting_bluetooth_new (); + array = g_byte_array_new (); + g_byte_array_append (array, bdaddr, sizeof (bdaddr)); + g_object_set (setting, + NM_SETTING_BLUETOOTH_BDADDR, array, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU, + NULL); + g_byte_array_free (array, TRUE); + nm_connection_add_setting (connection, setting); + + success = nm_connection_verify (connection, &error); + g_assert_no_error (error); + g_assert (success); + g_object_unref (connection); + + /* WiMAX connection */ + connection = nm_connection_new (); + add_generic_settings (connection, NM_SETTING_WIMAX_SETTING_NAME); + setting = nm_setting_wimax_new (); + g_object_set (setting, NM_SETTING_WIMAX_NETWORK_NAME, "CLEAR", NULL); + nm_connection_add_setting (connection, setting); + + success = nm_connection_verify (connection, &error); + g_assert_no_error (error); + g_assert (success); + g_object_unref (connection); + + /* GSM connection */ + connection = nm_connection_new (); + add_generic_settings (connection, NM_SETTING_GSM_SETTING_NAME); + + setting = nm_setting_gsm_new (); + g_object_set (setting, + NM_SETTING_GSM_NUMBER, "*99#", + NM_SETTING_GSM_APN, "metered.billing.sucks", + NULL); + nm_connection_add_setting (connection, setting); + + /* CDMA connection */ + connection = nm_connection_new (); + add_generic_settings (connection, NM_SETTING_CDMA_SETTING_NAME); + + setting = nm_setting_cdma_new (); + g_object_set (setting, + NM_SETTING_CDMA_NUMBER, "#777", + NM_SETTING_CDMA_USERNAME, "foobar@vzw.com", + NULL); + nm_connection_add_setting (connection, setting); + + success = nm_connection_verify (connection, &error); + g_assert_no_error (error); + g_assert (success); + g_object_unref (connection); +} + +static void +test_connection_bad_base_types (void) +{ + NMConnection *connection; + NMSetting *setting; + gboolean success; + GError *error = NULL; + + /* Test various non-base connection types to make sure they are rejected; + * using a fake 'wired' connection so the rest of it verifies + */ + + /* Connection setting */ + connection = nm_connection_new (); + add_generic_settings (connection, NM_SETTING_CONNECTION_SETTING_NAME); + setting = nm_setting_wired_new (); + nm_connection_add_setting (connection, setting); + + success = nm_connection_verify (connection, &error); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID); + g_assert (success == FALSE); + g_object_unref (connection); + g_clear_error (&error); + + /* PPP setting */ + connection = nm_connection_new (); + add_generic_settings (connection, NM_SETTING_PPP_SETTING_NAME); + setting = nm_setting_wired_new (); + nm_connection_add_setting (connection, setting); + setting = nm_setting_ppp_new (); + nm_connection_add_setting (connection, setting); + + success = nm_connection_verify (connection, &error); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID); + g_assert (success == FALSE); + g_object_unref (connection); + g_clear_error (&error); + + /* Serial setting */ + connection = nm_connection_new (); + add_generic_settings (connection, NM_SETTING_SERIAL_SETTING_NAME); + setting = nm_setting_wired_new (); + nm_connection_add_setting (connection, setting); + setting = nm_setting_serial_new (); + nm_connection_add_setting (connection, setting); + + success = nm_connection_verify (connection, &error); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID); + g_assert (success == FALSE); + g_object_unref (connection); + g_clear_error (&error); + + /* IP4 setting */ + connection = nm_connection_new (); + add_generic_settings (connection, NM_SETTING_IP4_CONFIG_SETTING_NAME); + setting = nm_setting_wired_new (); + nm_connection_add_setting (connection, setting); + + success = nm_connection_verify (connection, &error); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID); + g_assert (success == FALSE); + g_object_unref (connection); + g_clear_error (&error); + + /* IP6 setting */ + connection = nm_connection_new (); + add_generic_settings (connection, NM_SETTING_IP6_CONFIG_SETTING_NAME); + setting = nm_setting_wired_new (); + nm_connection_add_setting (connection, setting); + + success = nm_connection_verify (connection, &error); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID); + g_assert (success == FALSE); + g_object_unref (connection); + g_clear_error (&error); +} + +static void +test_setting_compare_id (void) +{ + NMSetting *old, *new; + gboolean success; + + old = nm_setting_connection_new (); + g_object_set (old, + NM_SETTING_CONNECTION_ID, "really awesome cool connection", + NM_SETTING_CONNECTION_UUID, "fbbd59d5-acab-4e30-8f86-258d272617e7", + NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NULL); + + new = nm_setting_duplicate (old); + g_object_set (new, NM_SETTING_CONNECTION_ID, "some different connection id", NULL); + + /* First make sure they are different */ + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (success == FALSE); + + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_IGNORE_ID); + g_assert (success); +} + +static void +test_setting_compare_secrets (NMSettingSecretFlags secret_flags, + NMSettingCompareFlags comp_flags, + gboolean remove_secret) +{ + NMSetting *old, *new; + gboolean success; + + /* Make sure that a connection with transient/unsaved secrets compares + * successfully to the same connection without those secrets. + */ + + old = nm_setting_wireless_security_new (); + g_object_set (old, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", + NM_SETTING_WIRELESS_SECURITY_PSK, "really cool psk", + NULL); + nm_setting_set_secret_flags (old, NM_SETTING_WIRELESS_SECURITY_PSK, secret_flags, NULL); + + /* Clear the PSK from the duplicated setting */ + new = nm_setting_duplicate (old); + if (remove_secret) { + g_object_set (new, NM_SETTING_WIRELESS_SECURITY_PSK, NULL, NULL); + + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (success == FALSE); + } + + success = nm_setting_compare (old, new, comp_flags); + g_assert (success); +} + +static void +test_setting_compare_vpn_secrets (NMSettingSecretFlags secret_flags, + NMSettingCompareFlags comp_flags, + gboolean remove_secret) +{ + NMSetting *old, *new; + gboolean success; + + /* Make sure that a connection with transient/unsaved secrets compares + * successfully to the same connection without those secrets. + */ + + old = nm_setting_vpn_new (); + nm_setting_vpn_add_secret (NM_SETTING_VPN (old), "foobarbaz", "really secret password"); + nm_setting_vpn_add_secret (NM_SETTING_VPN (old), "asdfasdfasdf", "really adfasdfasdfasdf"); + nm_setting_vpn_add_secret (NM_SETTING_VPN (old), "0123456778", "abcdefghijklmnpqrstuvqxyz"); + nm_setting_vpn_add_secret (NM_SETTING_VPN (old), "borkbork", "yet another really secret password"); + nm_setting_set_secret_flags (old, "borkbork", secret_flags, NULL); + + /* Clear "borkbork" from the duplicated setting */ + new = nm_setting_duplicate (old); + if (remove_secret) { + nm_setting_vpn_remove_secret (NM_SETTING_VPN (new), "borkbork"); + + /* First make sure they are different */ + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (success == FALSE); + } + + success = nm_setting_compare (old, new, comp_flags); + g_assert (success); +} + +static void +test_hwaddr_aton_ether_normal (void) +{ + guint8 buf[100]; + guint8 expected[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; + + g_assert (nm_utils_hwaddr_aton ("00:11:22:33:44:55", ARPHRD_ETHER, buf) != NULL); + g_assert (memcmp (buf, expected, sizeof (expected)) == 0); +} + +static void +test_hwaddr_aton_ib_normal (void) +{ + guint8 buf[100]; + const char *source = "00:11:22:33:44:55:66:77:88:99:01:12:23:34:45:56:67:78:89:90"; + guint8 expected[INFINIBAND_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, + 0x77, 0x88, 0x99, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, + 0x90 }; + + g_assert (nm_utils_hwaddr_aton (source, ARPHRD_INFINIBAND, buf) != NULL); + g_assert (memcmp (buf, expected, sizeof (expected)) == 0); +} + +static void +test_hwaddr_aton_no_leading_zeros (void) +{ + guint8 buf[100]; + guint8 expected[ETH_ALEN] = { 0x00, 0x1A, 0x2B, 0x03, 0x44, 0x05 }; + + g_assert (nm_utils_hwaddr_aton ("0:1a:2B:3:44:5", ARPHRD_ETHER, buf) != NULL); + g_assert (memcmp (buf, expected, sizeof (expected)) == 0); +} + +static void +test_hwaddr_aton_malformed (void) +{ + guint8 buf[100]; + + g_assert (nm_utils_hwaddr_aton ("0:1a:2B:3:a@%%", ARPHRD_ETHER, buf) == NULL); +} + +static void +test_connection_changed_cb (NMConnection *connection, gboolean *data) +{ + *data = TRUE; +} + +static void +test_ip4_prefix_to_netmask (void) +{ + int i; + + for (i = 0; i<=32; i++) { + guint32 netmask = nm_utils_ip4_prefix_to_netmask (i); + int plen = nm_utils_ip4_netmask_to_prefix (netmask); + + g_assert_cmpint (i, ==, plen); + { + guint32 msk = 0x80000000; + guint32 netmask2 = 0; + guint32 prefix = i; + while (prefix > 0) { + netmask2 |= msk; + msk >>= 1; + prefix--; + } + g_assert_cmpint (netmask, ==, (guint32) htonl (netmask2)); + } + } +} + +static void +test_ip4_netmask_to_prefix (void) +{ + int i, j; + + GRand *rand = g_rand_new (); + + g_rand_set_seed (rand, 1); + + for (i = 2; i<=32; i++) { + guint32 netmask = nm_utils_ip4_prefix_to_netmask (i); + guint32 netmask_lowest_bit = netmask & ~nm_utils_ip4_prefix_to_netmask (i-1); + + g_assert_cmpint (i, ==, nm_utils_ip4_netmask_to_prefix (netmask)); + + for (j = 0; j < 2*i; j++) { + guint32 r = g_rand_int (rand); + guint32 netmask_holey; + guint32 prefix_holey; + + netmask_holey = (netmask & r) | netmask_lowest_bit; + + if (netmask_holey == netmask) + continue; + + /* create an invalid netmask with holes and check that the function + * returns the longest prefix. */ + prefix_holey = nm_utils_ip4_netmask_to_prefix (netmask_holey); + + g_assert_cmpint (i, ==, prefix_holey); + } + } + + g_rand_free (rand); +} + +#define ASSERT_CHANGED(statement) \ +{ \ + changed = FALSE; \ + statement; \ + g_assert (changed); \ +} + +#define ASSERT_UNCHANGED(statement) \ +{ \ + changed = FALSE; \ + statement; \ + g_assert (!changed); \ +} + +static void +test_connection_changed_signal (void) +{ + NMConnection *connection; + gboolean changed = FALSE; + + connection = new_test_connection (); + g_signal_connect (connection, + NM_CONNECTION_CHANGED, + (GCallback) test_connection_changed_cb, + &changed); + + /* Add new setting */ + ASSERT_CHANGED (nm_connection_add_setting (connection, nm_setting_vlan_new ())); + + /* Remove existing setting */ + ASSERT_CHANGED (nm_connection_remove_setting (connection, NM_TYPE_SETTING_VLAN)); + + /* Remove non-existing setting */ + ASSERT_UNCHANGED (nm_connection_remove_setting (connection, NM_TYPE_SETTING_VLAN)); + + g_object_unref (connection); +} + +static void +test_setting_connection_changed_signal (void) +{ + NMConnection *connection; + gboolean changed = FALSE; + NMSettingConnection *s_con; + char *uuid; + + connection = nm_connection_new (); + g_signal_connect (connection, + NM_CONNECTION_CHANGED, + (GCallback) test_connection_changed_cb, + &changed); + + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + ASSERT_CHANGED (g_object_set (s_con, NM_SETTING_CONNECTION_ID, "adfadfasdfaf", NULL)); + + ASSERT_CHANGED (nm_setting_connection_add_permission (s_con, "user", "billsmith", NULL)); + ASSERT_CHANGED (nm_setting_connection_remove_permission (s_con, 0)); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*iter != NULL*"); + ASSERT_UNCHANGED (nm_setting_connection_remove_permission (s_con, 1)); + g_test_assert_expected_messages (); + + uuid = nm_utils_uuid_generate (); + ASSERT_CHANGED (nm_setting_connection_add_secondary (s_con, uuid)); + ASSERT_CHANGED (nm_setting_connection_remove_secondary (s_con, 0)); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + ASSERT_UNCHANGED (nm_setting_connection_remove_secondary (s_con, 1)); + g_test_assert_expected_messages (); + + g_object_unref (connection); +} + +static void +test_setting_bond_changed_signal (void) +{ + NMConnection *connection; + gboolean changed = FALSE; + NMSettingBond *s_bond; + + connection = nm_connection_new (); + g_signal_connect (connection, + NM_CONNECTION_CHANGED, + (GCallback) test_connection_changed_cb, + &changed); + + s_bond = (NMSettingBond *) nm_setting_bond_new (); + nm_connection_add_setting (connection, NM_SETTING (s_bond)); + + ASSERT_CHANGED (nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, "10")); + ASSERT_CHANGED (nm_setting_bond_remove_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY)); + ASSERT_UNCHANGED (nm_setting_bond_remove_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY)); + + g_object_unref (connection); +} + +static void +test_setting_ip4_changed_signal (void) +{ + NMConnection *connection; + gboolean changed = FALSE; + NMSettingIP4Config *s_ip4; + NMIP4Address *addr; + NMIP4Route *route; + + connection = nm_connection_new (); + g_signal_connect (connection, + NM_CONNECTION_CHANGED, + (GCallback) test_connection_changed_cb, + &changed); + + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + ASSERT_CHANGED (nm_setting_ip4_config_add_dns (s_ip4, 0x1122)); + ASSERT_CHANGED (nm_setting_ip4_config_remove_dns (s_ip4, 0)); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*i <= priv->dns->len*"); + ASSERT_UNCHANGED (nm_setting_ip4_config_remove_dns (s_ip4, 1)); + g_test_assert_expected_messages (); + + nm_setting_ip4_config_add_dns (s_ip4, 0x3344); + ASSERT_CHANGED (nm_setting_ip4_config_clear_dns (s_ip4)); + + ASSERT_CHANGED (nm_setting_ip4_config_add_dns_search (s_ip4, "foobar.com")); + ASSERT_CHANGED (nm_setting_ip4_config_remove_dns_search (s_ip4, 0)); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + ASSERT_UNCHANGED (nm_setting_ip4_config_remove_dns_search (s_ip4, 1)); + g_test_assert_expected_messages (); + + ASSERT_CHANGED (nm_setting_ip4_config_add_dns_search (s_ip4, "foobar.com")); + ASSERT_CHANGED (nm_setting_ip4_config_clear_dns_searches (s_ip4)); + + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, 0x2233); + nm_ip4_address_set_prefix (addr, 24); + ASSERT_CHANGED (nm_setting_ip4_config_add_address (s_ip4, addr)); + ASSERT_CHANGED (nm_setting_ip4_config_remove_address (s_ip4, 0)); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*addr != NULL && label != NULL*"); + ASSERT_UNCHANGED (nm_setting_ip4_config_remove_address (s_ip4, 1)); + g_test_assert_expected_messages (); + + nm_setting_ip4_config_add_address (s_ip4, addr); + ASSERT_CHANGED (nm_setting_ip4_config_clear_addresses (s_ip4)); + + route = nm_ip4_route_new (); + nm_ip4_route_set_dest (route, 0x2233); + nm_ip4_route_set_prefix (route, 24); + + ASSERT_CHANGED (nm_setting_ip4_config_add_route (s_ip4, route)); + ASSERT_CHANGED (nm_setting_ip4_config_remove_route (s_ip4, 0)); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + ASSERT_UNCHANGED (nm_setting_ip4_config_remove_route (s_ip4, 1)); + g_test_assert_expected_messages (); + + nm_setting_ip4_config_add_route (s_ip4, route); + ASSERT_CHANGED (nm_setting_ip4_config_clear_routes (s_ip4)); + + nm_ip4_address_unref (addr); + nm_ip4_route_unref (route); + g_object_unref (connection); +} + +static void +test_setting_ip6_changed_signal (void) +{ + NMConnection *connection; + gboolean changed = FALSE; + NMSettingIP6Config *s_ip6; + NMIP6Address *addr; + NMIP6Route *route; + const struct in6_addr t = { { { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 } } }; + + connection = nm_connection_new (); + g_signal_connect (connection, + NM_CONNECTION_CHANGED, + (GCallback) test_connection_changed_cb, + &changed); + + s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + ASSERT_CHANGED (nm_setting_ip6_config_add_dns (s_ip6, &t)); + ASSERT_CHANGED (nm_setting_ip6_config_remove_dns (s_ip6, 0)); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + ASSERT_UNCHANGED (nm_setting_ip6_config_remove_dns (s_ip6, 1)); + g_test_assert_expected_messages (); + + nm_setting_ip6_config_add_dns (s_ip6, &t); + ASSERT_CHANGED (nm_setting_ip6_config_clear_dns (s_ip6)); + + ASSERT_CHANGED (nm_setting_ip6_config_add_dns_search (s_ip6, "foobar.com")); + ASSERT_CHANGED (nm_setting_ip6_config_remove_dns_search (s_ip6, 0)); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + ASSERT_UNCHANGED (nm_setting_ip6_config_remove_dns_search (s_ip6, 1)); + g_test_assert_expected_messages (); + + nm_setting_ip6_config_add_dns_search (s_ip6, "foobar.com"); + ASSERT_CHANGED (nm_setting_ip6_config_clear_dns_searches (s_ip6)); + + addr = nm_ip6_address_new (); + nm_ip6_address_set_address (addr, &t); + nm_ip6_address_set_prefix (addr, 64); + + ASSERT_CHANGED (nm_setting_ip6_config_add_address (s_ip6, addr)); + ASSERT_CHANGED (nm_setting_ip6_config_remove_address (s_ip6, 0)); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + ASSERT_UNCHANGED (nm_setting_ip6_config_remove_address (s_ip6, 1)); + g_test_assert_expected_messages (); + + nm_setting_ip6_config_add_address (s_ip6, addr); + ASSERT_CHANGED (nm_setting_ip6_config_clear_addresses (s_ip6)); + + route = nm_ip6_route_new (); + nm_ip6_route_set_dest (route, &t); + nm_ip6_route_set_prefix (route, 128); + + ASSERT_CHANGED (nm_setting_ip6_config_add_route (s_ip6, route)); + ASSERT_CHANGED (nm_setting_ip6_config_remove_route (s_ip6, 0)); + + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + ASSERT_UNCHANGED (nm_setting_ip6_config_remove_route (s_ip6, 1)); + g_test_assert_expected_messages (); + + nm_setting_ip6_config_add_route (s_ip6, route); + ASSERT_CHANGED (nm_setting_ip6_config_clear_routes (s_ip6)); + + nm_ip6_address_unref (addr); + nm_ip6_route_unref (route); + g_object_unref (connection); +} + +static void +test_setting_vlan_changed_signal (void) +{ + NMConnection *connection; + gboolean changed = FALSE; + NMSettingVlan *s_vlan; + + connection = nm_connection_new (); + g_signal_connect (connection, + NM_CONNECTION_CHANGED, + (GCallback) test_connection_changed_cb, + &changed); + + s_vlan = (NMSettingVlan *) nm_setting_vlan_new (); + nm_connection_add_setting (connection, NM_SETTING (s_vlan)); + + ASSERT_CHANGED (nm_setting_vlan_add_priority (s_vlan, NM_VLAN_INGRESS_MAP, 1, 3)); + ASSERT_CHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_INGRESS_MAP, 0)); + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*idx < g_slist_length (list)*"); + ASSERT_UNCHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_INGRESS_MAP, 1)); + g_test_assert_expected_messages (); + ASSERT_CHANGED (nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, "1:3")); + ASSERT_CHANGED (nm_setting_vlan_clear_priorities (s_vlan, NM_VLAN_INGRESS_MAP)); + + ASSERT_CHANGED (nm_setting_vlan_add_priority (s_vlan, NM_VLAN_EGRESS_MAP, 1, 3)); + ASSERT_CHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_EGRESS_MAP, 0)); + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*idx < g_slist_length (list)*"); + ASSERT_UNCHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_EGRESS_MAP, 1)); + g_test_assert_expected_messages (); + ASSERT_CHANGED (nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, "1:3")); + ASSERT_CHANGED (nm_setting_vlan_clear_priorities (s_vlan, NM_VLAN_EGRESS_MAP)); + + g_object_unref (connection); +} + +static void +test_setting_vpn_changed_signal (void) +{ + NMConnection *connection; + gboolean changed = FALSE; + NMSettingVPN *s_vpn; + + connection = nm_connection_new (); + g_signal_connect (connection, + NM_CONNECTION_CHANGED, + (GCallback) test_connection_changed_cb, + &changed); + + s_vpn = (NMSettingVPN *) nm_setting_vpn_new (); + nm_connection_add_setting (connection, NM_SETTING (s_vpn)); + + ASSERT_CHANGED (nm_setting_vpn_add_data_item (s_vpn, "foobar", "baz")); + ASSERT_CHANGED (nm_setting_vpn_remove_data_item (s_vpn, "foobar")); + ASSERT_UNCHANGED (nm_setting_vpn_remove_data_item (s_vpn, "not added")); + + ASSERT_CHANGED (nm_setting_vpn_add_secret (s_vpn, "foobar", "baz")); + ASSERT_CHANGED (nm_setting_vpn_remove_secret (s_vpn, "foobar")); + ASSERT_UNCHANGED (nm_setting_vpn_remove_secret (s_vpn, "not added")); + + g_object_unref (connection); +} + +static void +test_setting_wired_changed_signal (void) +{ + NMConnection *connection; + gboolean changed = FALSE; + NMSettingWired *s_wired; + + connection = nm_connection_new (); + g_signal_connect (connection, + NM_CONNECTION_CHANGED, + (GCallback) test_connection_changed_cb, + &changed); + + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + ASSERT_CHANGED (nm_setting_wired_add_s390_option (s_wired, "portno", "1")); + ASSERT_CHANGED (nm_setting_wired_remove_s390_option (s_wired, "portno")); + ASSERT_UNCHANGED (nm_setting_wired_remove_s390_option (s_wired, "layer2")); + + g_object_unref (connection); +} + +static void +test_setting_wireless_changed_signal (void) +{ + NMConnection *connection; + gboolean changed = FALSE; + NMSettingWireless *s_wifi; + + connection = nm_connection_new (); + g_signal_connect (connection, + NM_CONNECTION_CHANGED, + (GCallback) test_connection_changed_cb, + &changed); + + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + ASSERT_CHANGED (nm_setting_wireless_add_seen_bssid (s_wifi, "00:11:22:33:44:55")); + + g_object_unref (connection); +} + +static void +test_setting_wireless_security_changed_signal (void) +{ + NMConnection *connection; + gboolean changed = FALSE; + NMSettingWirelessSecurity *s_wsec; + + connection = nm_connection_new (); + g_signal_connect (connection, + NM_CONNECTION_CHANGED, + (GCallback) test_connection_changed_cb, + &changed); + + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + + /* Protos */ + ASSERT_CHANGED (nm_setting_wireless_security_add_proto (s_wsec, "wpa")); + ASSERT_CHANGED (nm_setting_wireless_security_remove_proto (s_wsec, 0)); + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + ASSERT_UNCHANGED (nm_setting_wireless_security_remove_proto (s_wsec, 1)); + g_test_assert_expected_messages (); + + nm_setting_wireless_security_add_proto (s_wsec, "wep"); + ASSERT_CHANGED (nm_setting_wireless_security_clear_protos (s_wsec)); + + /* Pairwise ciphers */ + ASSERT_CHANGED (nm_setting_wireless_security_add_pairwise (s_wsec, "tkip")); + ASSERT_CHANGED (nm_setting_wireless_security_remove_pairwise (s_wsec, 0)); + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + ASSERT_UNCHANGED (nm_setting_wireless_security_remove_pairwise (s_wsec, 1)); + g_test_assert_expected_messages (); + + nm_setting_wireless_security_add_pairwise (s_wsec, "tkip"); + ASSERT_CHANGED (nm_setting_wireless_security_clear_pairwise (s_wsec)); + + /* Group ciphers */ + ASSERT_CHANGED (nm_setting_wireless_security_add_group (s_wsec, "ccmp")); + ASSERT_CHANGED (nm_setting_wireless_security_remove_group (s_wsec, 0)); + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + ASSERT_UNCHANGED (nm_setting_wireless_security_remove_group (s_wsec, 1)); + g_test_assert_expected_messages (); + + nm_setting_wireless_security_add_group (s_wsec, "tkip"); + ASSERT_CHANGED (nm_setting_wireless_security_clear_groups (s_wsec)); + + /* WEP key secret flags */ + ASSERT_CHANGED (g_assert (nm_setting_set_secret_flags (NM_SETTING (s_wsec), "wep-key0", NM_SETTING_SECRET_FLAG_AGENT_OWNED, NULL))); + ASSERT_CHANGED (g_assert (nm_setting_set_secret_flags (NM_SETTING (s_wsec), "wep-key1", NM_SETTING_SECRET_FLAG_AGENT_OWNED, NULL))); + ASSERT_CHANGED (g_assert (nm_setting_set_secret_flags (NM_SETTING (s_wsec), "wep-key2", NM_SETTING_SECRET_FLAG_AGENT_OWNED, NULL))); + ASSERT_CHANGED (g_assert (nm_setting_set_secret_flags (NM_SETTING (s_wsec), "wep-key3", NM_SETTING_SECRET_FLAG_AGENT_OWNED, NULL))); + + g_object_unref (connection); +} + +static void +test_setting_802_1x_changed_signal (void) +{ + NMConnection *connection; + gboolean changed = FALSE; + NMSetting8021x *s_8021x; + + connection = nm_connection_new (); + g_signal_connect (connection, + NM_CONNECTION_CHANGED, + (GCallback) test_connection_changed_cb, + &changed); + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + nm_connection_add_setting (connection, NM_SETTING (s_8021x)); + + /* EAP methods */ + ASSERT_CHANGED (nm_setting_802_1x_add_eap_method (s_8021x, "tls")); + ASSERT_CHANGED (nm_setting_802_1x_remove_eap_method (s_8021x, 0)); + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + ASSERT_UNCHANGED (nm_setting_802_1x_remove_eap_method (s_8021x, 1)); + g_test_assert_expected_messages (); + + nm_setting_802_1x_add_eap_method (s_8021x, "ttls"); + ASSERT_CHANGED (nm_setting_802_1x_clear_eap_methods (s_8021x)); + + /* alternate subject matches */ + ASSERT_CHANGED (nm_setting_802_1x_add_altsubject_match (s_8021x, "EMAIL:server@example.com")); + ASSERT_CHANGED (nm_setting_802_1x_remove_altsubject_match (s_8021x, 0)); + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + ASSERT_UNCHANGED (nm_setting_802_1x_remove_altsubject_match (s_8021x, 1)); + g_test_assert_expected_messages (); + + nm_setting_802_1x_add_altsubject_match (s_8021x, "EMAIL:server@example.com"); + ASSERT_CHANGED (nm_setting_802_1x_clear_altsubject_matches (s_8021x)); + + /* phase2 alternate subject matches */ + ASSERT_CHANGED (nm_setting_802_1x_add_phase2_altsubject_match (s_8021x, "EMAIL:server@example.com")); + ASSERT_CHANGED (nm_setting_802_1x_remove_phase2_altsubject_match (s_8021x, 0)); + g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + ASSERT_UNCHANGED (nm_setting_802_1x_remove_phase2_altsubject_match (s_8021x, 1)); + g_test_assert_expected_messages (); + + nm_setting_802_1x_add_phase2_altsubject_match (s_8021x, "EMAIL:server@example.com"); + ASSERT_CHANGED (nm_setting_802_1x_clear_phase2_altsubject_matches (s_8021x)); + + g_object_unref (connection); +} + +static void +test_setting_old_uuid (void) +{ + GError *error = NULL; + NMSetting *setting; + gboolean success; + + /* NetworkManager-0.9.4.0 generated 40-character UUIDs with no dashes, + * like this one. Test that we maintain compatibility. */ + const char *uuid = "f43bec2cdd60e5da381ebb1eb1fa39f3cc52660c"; + + setting = nm_setting_connection_new (); + g_object_set (G_OBJECT (setting), + NM_SETTING_CONNECTION_ID, "uuidtest", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + + success = nm_setting_verify (NM_SETTING (setting), NULL, &error); + g_assert_no_error (error); + g_assert (success == TRUE); +} + +/* + * nm_connection_verify() modifies the connection by setting + * the interface-name property to the virtual_iface_name of + * the type specific settings. + * + * It would be preferable of verify() not to touch the connection, + * but as it is now, stick with it and test it. + **/ +static void +test_connection_verify_sets_interface_name (void) +{ + NMConnection *con; + NMSettingConnection *s_con; + NMSettingBond *s_bond; + GError *error = NULL; + gboolean success; + + s_con = (NMSettingConnection *) nm_setting_connection_new (); + g_object_set (G_OBJECT (s_con), + NM_SETTING_CONNECTION_ID, "test1", + NM_SETTING_CONNECTION_UUID, "22001632-bbb4-4616-b277-363dce3dfb5b", + NM_SETTING_CONNECTION_TYPE, NM_SETTING_BOND_SETTING_NAME, + NULL); + s_bond = (NMSettingBond *) nm_setting_bond_new (); + g_object_set (G_OBJECT (s_bond), + NM_SETTING_BOND_INTERFACE_NAME, "bond-x", + NULL); + + con = nm_connection_new (); + nm_connection_add_setting (con, NM_SETTING (s_con)); + nm_connection_add_setting (con, NM_SETTING (s_bond)); + + g_assert_cmpstr (nm_connection_get_interface_name (con), ==, NULL); + + /* for backward compatiblity, normalizes the interface name */ + success = nm_connection_verify (con, &error); + g_assert (success && !error); + + g_assert_cmpstr (nm_connection_get_interface_name (con), ==, "bond-x"); + + g_object_unref (con); +} + +/* + * Test normalization of interface-name + **/ +static void +test_connection_normalize_virtual_iface_name (void) +{ + NMConnection *con; + NMSettingConnection *s_con; + NMSettingVlan *s_vlan; + NMSetting *setting; + GError *error = NULL; + gboolean success; + const char *IFACE_NAME = "iface"; + const char *IFACE_VIRT = "iface-X"; + gboolean modified = FALSE; + + con = nm_connection_new (); + + setting = nm_setting_ip4_config_new (); + g_object_set (setting, + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + nm_connection_add_setting (con, setting); + + setting = nm_setting_ip6_config_new (); + g_object_set (setting, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); + nm_connection_add_setting (con, setting); + + s_con = (NMSettingConnection *) nm_setting_connection_new (); + g_object_set (G_OBJECT (s_con), + NM_SETTING_CONNECTION_ID, "test1", + NM_SETTING_CONNECTION_UUID, "22001632-bbb4-4616-b277-363dce3dfb5b", + NM_SETTING_CONNECTION_TYPE, NM_SETTING_VLAN_SETTING_NAME, + NM_SETTING_CONNECTION_INTERFACE_NAME, IFACE_NAME, + NULL); + s_vlan = (NMSettingVlan *) nm_setting_vlan_new (); + g_object_set (G_OBJECT (s_vlan), + NM_SETTING_VLAN_INTERFACE_NAME, IFACE_VIRT, + NM_SETTING_VLAN_PARENT, "eth0", + NULL); + + nm_connection_add_setting (con, NM_SETTING (s_con)); + nm_connection_add_setting (con, NM_SETTING (s_vlan)); + + g_assert_cmpstr (nm_connection_get_interface_name (con), ==, IFACE_NAME); + g_assert_cmpstr (nm_setting_vlan_get_interface_name (s_vlan), ==, IFACE_VIRT); + + /* for backward compatiblity, normalizes the interface name */ + success = nm_connection_verify (con, &error); + g_assert (success && !error); + + g_assert_cmpstr (nm_connection_get_interface_name (con), ==, IFACE_NAME); + g_assert_cmpstr (nm_setting_vlan_get_interface_name (s_vlan), ==, IFACE_VIRT); + + success = nm_connection_normalize (con, NULL, &modified, &error); + g_assert (success && !error); + g_assert (modified); + + g_assert_cmpstr (nm_connection_get_interface_name (con), ==, IFACE_NAME); + g_assert_cmpstr (nm_setting_vlan_get_interface_name (s_vlan), ==, IFACE_NAME); + + success = nm_connection_verify (con, &error); + g_assert (success && !error); + + g_object_unref (con); +} + +NMTST_DEFINE (); + +int main (int argc, char **argv) +{ + char *base; + + nmtst_init (&argc, &argv, TRUE); + + /* The tests */ + test_setting_vpn_items (); + test_setting_vpn_update_secrets (); + test_setting_vpn_modify_during_foreach (); + test_setting_ip4_config_labels (); + test_setting_ip6_config_old_address_array (); + test_setting_gsm_apn_spaces (); + test_setting_gsm_apn_bad_chars (); + test_setting_gsm_apn_underscore (); + test_setting_gsm_without_number (); + test_setting_to_hash_all (); + test_setting_to_hash_no_secrets (); + test_setting_to_hash_only_secrets (); + test_setting_compare_id (); + test_setting_compare_secrets (NM_SETTING_SECRET_FLAG_AGENT_OWNED, NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS, TRUE); + test_setting_compare_secrets (NM_SETTING_SECRET_FLAG_NOT_SAVED, NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS, TRUE); + test_setting_compare_secrets (NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS, TRUE); + test_setting_compare_secrets (NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_COMPARE_FLAG_EXACT, FALSE); + test_setting_compare_vpn_secrets (NM_SETTING_SECRET_FLAG_AGENT_OWNED, NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS, TRUE); + test_setting_compare_vpn_secrets (NM_SETTING_SECRET_FLAG_NOT_SAVED, NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS, TRUE); + test_setting_compare_vpn_secrets (NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS, TRUE); + test_setting_compare_vpn_secrets (NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_COMPARE_FLAG_EXACT, FALSE); + test_setting_old_uuid (); + + test_connection_to_hash_setting_name (); + test_setting_new_from_hash (); + test_connection_replace_settings (); + test_connection_replace_settings_from_connection (); + test_connection_new_from_hash (); + test_connection_verify_sets_interface_name (); + test_connection_normalize_virtual_iface_name (); + + test_setting_connection_permissions_helpers (); + test_setting_connection_permissions_property (); + + test_connection_compare_same (); + test_connection_compare_key_only_in_a (); + test_connection_compare_setting_only_in_a (); + test_connection_compare_key_only_in_b (); + test_connection_compare_setting_only_in_b (); + + test_connection_diff_a_only (); + test_connection_diff_same (); + test_connection_diff_different (); + test_connection_diff_no_secrets (); + test_connection_diff_inferrable (); + test_connection_good_base_types (); + test_connection_bad_base_types (); + + test_hwaddr_aton_ether_normal (); + test_hwaddr_aton_ib_normal (); + test_hwaddr_aton_no_leading_zeros (); + test_hwaddr_aton_malformed (); + test_ip4_prefix_to_netmask (); + test_ip4_netmask_to_prefix (); + + test_connection_changed_signal (); + test_setting_connection_changed_signal (); + test_setting_bond_changed_signal (); + test_setting_ip4_changed_signal (); + test_setting_ip6_changed_signal (); + test_setting_vlan_changed_signal (); + test_setting_vpn_changed_signal (); + test_setting_wired_changed_signal (); + test_setting_wireless_changed_signal (); + test_setting_wireless_security_changed_signal (); + test_setting_802_1x_changed_signal (); + + base = g_path_get_basename (argv[0]); + fprintf (stdout, "%s: SUCCESS\n", base); + g_free (base); + return 0; +} + diff --git a/libnm-core/tests/test-secrets.c b/libnm-core/tests/test-secrets.c new file mode 100644 index 0000000000..b829d12eed --- /dev/null +++ b/libnm-core/tests/test-secrets.c @@ -0,0 +1,754 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2008 - 2011 Red Hat, Inc. + * + */ + +#include <glib.h> +#include <string.h> + +#include <nm-utils.h> + +#include "nm-setting-connection.h" +#include "nm-setting-wired.h" +#include "nm-setting-8021x.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" +#include "nm-setting-cdma.h" +#include "nm-setting-gsm.h" +#include "nm-setting-ppp.h" +#include "nm-setting-pppoe.h" +#include "nm-setting-vpn.h" + +#include "nm-test-utils.h" + +#define TEST_NEED_SECRETS_EAP_TLS_CA_CERT TEST_CERT_DIR "/test_ca_cert.pem" +#define TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT TEST_CERT_DIR "/test_key_and_cert.pem" +#define TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY TEST_CERT_DIR "/test_key_and_cert.pem" + +static gboolean +find_hints_item (GPtrArray *hints, const char *item) +{ + int i; + + for (i = 0; i < hints->len; i++) { + if (!strcmp (item, (const char *) g_ptr_array_index (hints, i))) + return TRUE; + } + return FALSE; +} + +static NMConnection * +make_tls_connection (const char *detail, NMSetting8021xCKScheme scheme) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSetting8021x *s_8021x; + NMSettingWired *s_wired; + NMSettingIP4Config *s_ip4; + char *uuid; + gboolean success; + GError *error = NULL; + + connection = nm_connection_new (); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Need TLS Secrets", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + g_free (uuid); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + /* Wireless security setting */ + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + nm_connection_add_setting (connection, NM_SETTING (s_8021x)); + + g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL); + + nm_setting_802_1x_add_eap_method (s_8021x, "tls"); + + success = nm_setting_802_1x_set_ca_cert (s_8021x, + TEST_NEED_SECRETS_EAP_TLS_CA_CERT, + scheme, + NULL, + &error); + ASSERT (success == TRUE, + detail, "failed to set CA certificate '%s': %s", + TEST_NEED_SECRETS_EAP_TLS_CA_CERT, error->message); + + success = nm_setting_802_1x_set_client_cert (s_8021x, + TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT, + scheme, + NULL, + &error); + ASSERT (success == TRUE, + detail, "failed to set client certificate '%s': %s", + TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT, error->message); + + success = nm_setting_802_1x_set_private_key (s_8021x, + TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY, + "test", + scheme, + NULL, + &error); + ASSERT (success == TRUE, + detail, "failed to set private key '%s': %s", + TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY, error->message); + + /* IP4 setting */ + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + ASSERT (nm_connection_verify (connection, &error) == TRUE, + detail, "failed to verify connection: %s", + (error && error->message) ? error->message : "(unknown)"); + + return connection; +} + +static void +test_need_tls_secrets_path (void) +{ + NMConnection *connection; + const char *setting_name; + GPtrArray *hints = NULL; + + connection = make_tls_connection ("need-tls-secrets-path-key", NM_SETTING_802_1X_CK_SCHEME_PATH); + ASSERT (connection != NULL, + "need-tls-secrets-path-key", + "error creating test connection"); + + /* Ensure we don't need any secrets since we just set up the connection */ + setting_name = nm_connection_need_secrets (connection, &hints); + ASSERT (setting_name == NULL, + "need-tls-secrets-path-key", + "secrets are unexpectedly required"); + ASSERT (hints == NULL, + "need-tls-secrets-path-key", + "hints should be NULL since no secrets were required"); + + /* Connection is good; clear secrets and ensure private key password is then required */ + nm_connection_clear_secrets (connection); + + hints = NULL; + setting_name = nm_connection_need_secrets (connection, &hints); + ASSERT (setting_name != NULL, + "need-tls-secrets-path-key-password", + "unexpected secrets success"); + ASSERT (strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) == 0, + "need-tls-secrets-path-key-password", + "unexpected setting secrets required"); + + ASSERT (hints != NULL, + "need-tls-secrets-path-key-password", + "expected returned secrets hints"); + ASSERT (find_hints_item (hints, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD), + "need-tls-secrets-path-key-password", + "expected to require private key password, but it wasn't"); + + g_object_unref (connection); +} + +static void +test_need_tls_secrets_blob (void) +{ + NMConnection *connection; + const char *setting_name; + GPtrArray *hints = NULL; + + connection = make_tls_connection ("need-tls-secrets-blob-key", NM_SETTING_802_1X_CK_SCHEME_BLOB); + ASSERT (connection != NULL, + "need-tls-secrets-blob-key", + "error creating test connection"); + + /* Ensure we don't need any secrets since we just set up the connection */ + setting_name = nm_connection_need_secrets (connection, &hints); + ASSERT (setting_name == NULL, + "need-tls-secrets-blob-key", + "secrets are unexpectedly required"); + ASSERT (hints == NULL, + "need-tls-secrets-blob-key", + "hints should be NULL since no secrets were required"); + + /* Clear secrets and ensure password is again required */ + nm_connection_clear_secrets (connection); + + hints = NULL; + setting_name = nm_connection_need_secrets (connection, &hints); + ASSERT (setting_name != NULL, + "need-tls-secrets-blob-key-password", + "unexpected secrets success"); + ASSERT (strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) == 0, + "need-tls-secrets-blob-key-password", + "unexpected setting secrets required"); + + ASSERT (hints != NULL, + "need-tls-secrets-blob-key-password", + "expected returned secrets hints"); + ASSERT (find_hints_item (hints, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD), + "need-tls-secrets-blob-key-password", + "expected to require private key password, but it wasn't"); + + g_object_unref (connection); +} + +static NMConnection * +make_tls_phase2_connection (const char *detail, NMSetting8021xCKScheme scheme) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSetting8021x *s_8021x; + NMSettingWired *s_wired; + NMSettingIP4Config *s_ip4; + char *uuid; + gboolean success; + GError *error = NULL; + + connection = nm_connection_new (); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Need TLS Secrets", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + g_free (uuid); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + /* Wireless security setting */ + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + nm_connection_add_setting (connection, NM_SETTING (s_8021x)); + + g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, "blahblah", NULL); + g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL); + + nm_setting_802_1x_add_eap_method (s_8021x, "ttls"); + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, "tls", NULL); + + success = nm_setting_802_1x_set_phase2_ca_cert (s_8021x, + TEST_NEED_SECRETS_EAP_TLS_CA_CERT, + scheme, + NULL, + &error); + ASSERT (success == TRUE, + detail, "failed to set phase2 CA certificate '%s': %s", + TEST_NEED_SECRETS_EAP_TLS_CA_CERT, error->message); + + success = nm_setting_802_1x_set_phase2_client_cert (s_8021x, + TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT, + scheme, + NULL, + &error); + ASSERT (success == TRUE, + detail, "failed to set phase2 client certificate '%s': %s", + TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT, error->message); + + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY, + "test", + scheme, + NULL, + &error); + ASSERT (success == TRUE, + detail, "failed to set phase2 private key '%s': %s", + TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY, error->message); + + /* IP4 setting */ + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + ASSERT (nm_connection_verify (connection, &error) == TRUE, + detail, "failed to verify connection: %s", + (error && error->message) ? error->message : "(unknown)"); + + return connection; +} + +static void +test_need_tls_phase2_secrets_path (void) +{ + NMConnection *connection; + const char *setting_name; + GPtrArray *hints = NULL; + + connection = make_tls_phase2_connection ("need-tls-phase2-secrets-path-key", + NM_SETTING_802_1X_CK_SCHEME_PATH); + ASSERT (connection != NULL, + "need-tls-phase2-secrets-path-key", + "error creating test connection"); + + /* Ensure we don't need any secrets since we just set up the connection */ + setting_name = nm_connection_need_secrets (connection, &hints); + ASSERT (setting_name == NULL, + "need-tls-phase2-secrets-path-key", + "secrets are unexpectedly required"); + ASSERT (hints == NULL, + "need-tls-phase2-secrets-path-key", + "hints should be NULL since no secrets were required"); + + /* Connection is good; clear secrets and ensure private key password is then required */ + nm_connection_clear_secrets (connection); + + hints = NULL; + setting_name = nm_connection_need_secrets (connection, &hints); + ASSERT (setting_name != NULL, + "need-tls-phase2-secrets-path-key-password", + "unexpected secrets success"); + ASSERT (strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) == 0, + "need-tls-phase2-secrets-path-key-password", + "unexpected setting secrets required"); + + ASSERT (hints != NULL, + "need-tls-phase2-secrets-path-key-password", + "expected returned secrets hints"); + ASSERT (find_hints_item (hints, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD), + "need-tls-phase2-secrets-path-key-password", + "expected to require private key password, but it wasn't"); + + g_object_unref (connection); +} + +static void +test_need_tls_phase2_secrets_blob (void) +{ + NMConnection *connection; + const char *setting_name; + GPtrArray *hints = NULL; + + connection = make_tls_phase2_connection ("need-tls-phase2-secrets-blob-key", + NM_SETTING_802_1X_CK_SCHEME_BLOB); + ASSERT (connection != NULL, + "need-tls-phase2-secrets-blob-key", + "error creating test connection"); + + /* Ensure we don't need any secrets since we just set up the connection */ + setting_name = nm_connection_need_secrets (connection, &hints); + ASSERT (setting_name == NULL, + "need-tls-phase2-secrets-blob-key", + "secrets are unexpectedly required"); + ASSERT (hints == NULL, + "need-tls-phase2-secrets-blob-key", + "hints should be NULL since no secrets were required"); + + /* Connection is good; clear secrets and ensure private key password is then required */ + nm_connection_clear_secrets (connection); + + hints = NULL; + setting_name = nm_connection_need_secrets (connection, &hints); + ASSERT (setting_name != NULL, + "need-tls-phase2-secrets-blob-key-password", + "unexpected secrets success"); + ASSERT (strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) == 0, + "need-tls-phase2-secrets-blob-key-password", + "unexpected setting secrets required"); + + ASSERT (hints != NULL, + "need-tls-phase2-secrets-blob-key-password", + "expected returned secrets hints"); + ASSERT (find_hints_item (hints, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD), + "need-tls-phase2-secrets-blob-key-password", + "expected to require private key password, but it wasn't"); + + g_object_unref (connection); +} + +static NMConnection * +wifi_connection_new (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + unsigned char tmpssid[] = { 0x31, 0x33, 0x33, 0x37 }; + char *uuid; + GByteArray *ssid; + + connection = nm_connection_new (); + g_assert (connection); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + g_assert (s_con); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Wireless", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + g_free (uuid); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + /* Wireless setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + g_assert (s_wifi); + + ssid = g_byte_array_sized_new (sizeof (tmpssid)); + g_byte_array_append (ssid, &tmpssid[0], sizeof (tmpssid)); + g_object_set (s_wifi, + NM_SETTING_WIRELESS_SSID, ssid, + NULL); + g_byte_array_free (ssid, TRUE); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + /* Wifi security */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + g_assert (s_wsec); + + g_object_set (G_OBJECT (s_wsec), + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", + NULL); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + + return connection; +} + +static void +value_destroy (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +static GValue * +string_to_gvalue (const char *str) +{ + GValue *val = g_slice_new0 (GValue); + + g_value_init (val, G_TYPE_STRING); + g_value_set_string (val, str); + return val; +} + +static GValue * +uint_to_gvalue (guint32 i) +{ + GValue *val; + + val = g_slice_new0 (GValue); + g_value_init (val, G_TYPE_UINT); + g_value_set_uint (val, i); + return val; +} + +static void +test_update_secrets_wifi_single_setting (void) +{ + NMConnection *connection; + NMSettingWirelessSecurity *s_wsec; + GHashTable *secrets; + GError *error = NULL; + gboolean success; + const char *wepkey = "11111111111111111111111111"; + const char *tmp; + + /* Test update with a hashed setting of 802-11-wireless secrets */ + + connection = wifi_connection_new (); + + /* Build up the secrets hash */ + secrets = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, value_destroy); + g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, string_to_gvalue (wepkey)); + g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, uint_to_gvalue (NM_WEP_KEY_TYPE_KEY)); + + success = nm_connection_update_secrets (connection, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + secrets, + &error); + g_assert_no_error (error); + g_assert (success); + + /* Make sure the secret is now in the connection */ + s_wsec = nm_connection_get_setting_wireless_security (connection); + g_assert (s_wsec); + tmp = nm_setting_wireless_security_get_wep_key (s_wsec, 0); + g_assert_cmpstr (tmp, ==, wepkey); + + g_object_unref (connection); +} + +static void +test_update_secrets_wifi_full_hash (void) +{ + NMConnection *connection; + NMSettingWirelessSecurity *s_wsec; + GHashTable *secrets, *all; + GError *error = NULL; + gboolean success; + const char *wepkey = "11111111111111111111111111"; + const char *tmp; + + /* Test update with a hashed connection containing only 802-11-wireless + * setting and secrets. + */ + + connection = wifi_connection_new (); + + /* Build up the secrets hash */ + all = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_hash_table_destroy); + secrets = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, value_destroy); + g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, string_to_gvalue (wepkey)); + g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, uint_to_gvalue (NM_WEP_KEY_TYPE_KEY)); + g_hash_table_insert (all, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, secrets); + + success = nm_connection_update_secrets (connection, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + all, + &error); + g_assert_no_error (error); + g_assert (success); + + /* Make sure the secret is now in the connection */ + s_wsec = nm_connection_get_setting_wireless_security (connection); + g_assert (s_wsec); + tmp = nm_setting_wireless_security_get_wep_key (s_wsec, 0); + g_assert_cmpstr (tmp, ==, wepkey); + + g_object_unref (connection); +} + +static void +test_update_secrets_wifi_bad_setting_name (void) +{ + NMConnection *connection; + GHashTable *secrets; + GError *error = NULL; + gboolean success; + const char *wepkey = "11111111111111111111111111"; + + /* Test that passing an invalid setting name to + * nm_connection_update_secrets() fails with the correct error. + */ + + connection = wifi_connection_new (); + + /* Build up the secrets hash */ + secrets = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, value_destroy); + g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, string_to_gvalue (wepkey)); + g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, uint_to_gvalue (NM_WEP_KEY_TYPE_KEY)); + + success = nm_connection_update_secrets (connection, + "asdfasdfasdfasf", + secrets, + &error); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND); + g_assert (success == FALSE); + + g_object_unref (connection); +} + +static void +test_update_secrets_whole_connection (void) +{ + NMConnection *connection; + NMSettingWirelessSecurity *s_wsec; + GHashTable *secrets, *wsec_hash; + GError *error = NULL; + gboolean success; + const char *wepkey = "11111111111111111111111111"; + + /* Test calling nm_connection_update_secrets() with an entire hashed + * connection including non-secrets. + */ + + connection = wifi_connection_new (); + + /* Build up the secrets hash */ + secrets = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_ALL); + wsec_hash = g_hash_table_lookup (secrets, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + g_assert (wsec_hash); + g_hash_table_insert (wsec_hash, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, string_to_gvalue (wepkey)); + + success = nm_connection_update_secrets (connection, NULL, secrets, &error); + g_assert_no_error (error); + g_assert (success == TRUE); + + s_wsec = nm_connection_get_setting_wireless_security (connection); + g_assert (s_wsec); + g_assert_cmpstr (nm_setting_wireless_security_get_wep_key (s_wsec, 0), ==, wepkey); + + g_object_unref (connection); +} + +static void +test_update_secrets_whole_connection_empty_hash (void) +{ + NMConnection *connection; + GHashTable *secrets; + GError *error = NULL; + gboolean success; + + /* Test that updating secrets with an empty hash returns success */ + + connection = wifi_connection_new (); + secrets = g_hash_table_new (g_str_hash, g_str_equal); + success = nm_connection_update_secrets (connection, NULL, secrets, &error); + g_assert_no_error (error); + g_assert (success == TRUE); + g_object_unref (connection); +} + +static void +test_update_secrets_whole_connection_bad_setting (void) +{ + NMConnection *connection; + GHashTable *secrets, *wsec_hash; + GError *error = NULL; + gboolean success; + const char *wepkey = "11111111111111111111111111"; + + /* Test that sending a hashed connection containing an invalid setting + * name fails with the right error. + */ + + connection = wifi_connection_new (); + + /* Build up the secrets hash */ + secrets = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_ALL); + wsec_hash = g_hash_table_lookup (secrets, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + g_assert (wsec_hash); + g_hash_table_insert (wsec_hash, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, string_to_gvalue (wepkey)); + + /* Steal the wsec setting hash so it's not deallocated, and stuff it back + * in with a different name so we ensure libnm-util is returning the right + * error when it finds an entry in the connection hash that doesn't match + * any setting in the connection. + */ + g_hash_table_steal (secrets, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + g_hash_table_insert (secrets, "asdfasdfasdfasdf", wsec_hash); + + success = nm_connection_update_secrets (connection, NULL, secrets, &error); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND); + g_assert (success == FALSE); + + g_object_unref (connection); +} + +static void +test_update_secrets_whole_connection_empty_base_setting (void) +{ + NMConnection *connection; + GHashTable *secrets; + GError *error = NULL; + gboolean success; + + /* Test that a hashed connection which does not have any hashed secrets + * for the requested setting returns success. + */ + + connection = wifi_connection_new (); + secrets = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_ONLY_SECRETS); + g_assert_cmpint (g_hash_table_size (secrets), ==, 1); + g_assert (g_hash_table_lookup (secrets, NM_SETTING_WIRELESS_SETTING_NAME)); + + success = nm_connection_update_secrets (connection, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + secrets, + &error); + g_assert_no_error (error); + g_assert (success); + + g_hash_table_destroy (secrets); + g_object_unref (connection); +} + +static void +test_update_secrets_null_setting_name_with_setting_hash (void) +{ + NMConnection *connection; + GHashTable *secrets; + GError *error = NULL; + gboolean success; + const char *wepkey = "11111111111111111111111111"; + + /* Ensure that a NULL setting name and only a hashed setting fails */ + + connection = wifi_connection_new (); + + secrets = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, value_destroy); + g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, string_to_gvalue (wepkey)); + g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, uint_to_gvalue (NM_WEP_KEY_TYPE_KEY)); + + success = nm_connection_update_secrets (connection, NULL, secrets, &error); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND); + g_assert (!success); + + g_hash_table_destroy (secrets); + g_object_unref (connection); +} + +int main (int argc, char **argv) +{ + GError *error = NULL; + char *base; + +#if !GLIB_CHECK_VERSION (2, 35, 0) + g_type_init (); +#endif + + if (!nm_utils_init (&error)) + FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message); + + /* The tests */ + test_need_tls_secrets_path (); + test_need_tls_secrets_blob (); + test_need_tls_phase2_secrets_path (); + test_need_tls_phase2_secrets_blob (); + + test_update_secrets_wifi_single_setting (); + test_update_secrets_wifi_full_hash (); + test_update_secrets_wifi_bad_setting_name (); + + test_update_secrets_whole_connection (); + test_update_secrets_whole_connection_empty_hash (); + test_update_secrets_whole_connection_bad_setting (); + test_update_secrets_whole_connection_empty_base_setting (); + test_update_secrets_null_setting_name_with_setting_hash (); + + base = g_path_get_basename (argv[0]); + fprintf (stdout, "%s: SUCCESS\n", base); + g_free (base); + return 0; +} + diff --git a/libnm-core/tests/test-setting-8021x.c b/libnm-core/tests/test-setting-8021x.c new file mode 100644 index 0000000000..c496e47bcf --- /dev/null +++ b/libnm-core/tests/test-setting-8021x.c @@ -0,0 +1,443 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2008 - 2011 Red Hat, Inc. + * + */ + +#include <glib.h> +#include <string.h> + +#include <nm-utils.h> + +#include "nm-setting-connection.h" +#include "nm-setting-8021x.h" + +#include "nm-test-utils.h" + +static void +compare_blob_data (const char *test, + const char *key_path, + const GByteArray *key) +{ + char *contents = NULL; + gsize len = 0; + GError *error = NULL; + gboolean success; + + success = g_file_get_contents (key_path, &contents, &len, &error); + ASSERT (success == TRUE, + test, "failed to read blob key file: %s", error->message); + + ASSERT (len > 0, test, "blob key file invalid (size 0)"); + + ASSERT (len == key->len, + test, "blob key file (%d) and setting key data (%d) lengths don't match", + len, key->len); + + ASSERT (memcmp (contents, key->data, len) == 0, + test, "blob key file and blob key data don't match"); + + g_free (contents); +} + +#define SCHEME_PATH "file://" + +static void +check_scheme_path (GByteArray *value, const char *path) +{ + guint8 *p = value->data; + + g_assert (memcmp (p, SCHEME_PATH, strlen (SCHEME_PATH)) == 0); + p += strlen (SCHEME_PATH); + g_assert (memcmp (p, path, strlen (path)) == 0); + p += strlen (path); + g_assert (*p == '\0'); +} + +static void +test_private_key_import (const char *path, + const char *password, + NMSetting8021xCKScheme scheme) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + NMSetting8021xCKFormat tmp_fmt; + GError *error = NULL; + GByteArray *tmp_key = NULL, *client_cert = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + ASSERT (s_8021x != NULL, "private-key-import", "setting was NULL"); + + success = nm_setting_802_1x_set_private_key (s_8021x, + path, + password, + scheme, + &format, + &error); + ASSERT (success == TRUE, + "private-key-import", "error reading private key: %s", error->message); + ASSERT (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, + "private-key-import", "unexpected private key format (got %d)", format); + tmp_fmt = nm_setting_802_1x_get_private_key_format (s_8021x); + ASSERT (tmp_fmt == format, + "private-key-import", "unexpected re-read private key format (expected %d, got %d)", + format, tmp_fmt); + + /* Make sure the password is what we expect */ + pw = nm_setting_802_1x_get_private_key_password (s_8021x); + ASSERT (pw != NULL, + "private-key-import", "failed to get previous private key password"); + ASSERT (strcmp (pw, password) == 0, + "private-key-import", "failed to compare private key password"); + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + tmp_key = (GByteArray *) nm_setting_802_1x_get_private_key_blob (s_8021x); + ASSERT (tmp_key != NULL, "private-key-import", "missing private key blob"); + compare_blob_data ("private-key-import", path, tmp_key); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + g_object_get (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY, &tmp_key, NULL); + ASSERT (tmp_key != NULL, "private-key-import", "missing private key value"); + check_scheme_path (tmp_key, path); + g_byte_array_free (tmp_key, TRUE); + } else + g_assert_not_reached (); + + /* If it's PKCS#12 ensure the client cert is the same value */ + if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { + g_object_get (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY, &tmp_key, NULL); + ASSERT (tmp_key != NULL, "private-key-import", "missing private key value"); + + g_object_get (s_8021x, NM_SETTING_802_1X_CLIENT_CERT, &client_cert, NULL); + ASSERT (client_cert != NULL, "private-key-import", "missing client certificate value"); + + /* make sure they are the same */ + ASSERT (tmp_key->len == client_cert->len, + "private-key-import", "unexpected different private key and client cert lengths"); + ASSERT (memcmp (tmp_key->data, client_cert->data, tmp_key->len) == 0, + "private-key-import", "unexpected different private key and client cert data"); + + g_byte_array_free (tmp_key, TRUE); + g_byte_array_free (client_cert, TRUE); + } + + g_object_unref (s_8021x); +} + +static void +test_phase2_private_key_import (const char *path, + const char *password, + NMSetting8021xCKScheme scheme) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + NMSetting8021xCKFormat tmp_fmt; + GError *error = NULL; + GByteArray *tmp_key = NULL, *client_cert = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + ASSERT (s_8021x != NULL, "phase2-private-key-import", "setting was NULL"); + + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + path, + password, + scheme, + &format, + &error); + ASSERT (success == TRUE, + "phase2-private-key-import", "error reading private key: %s", error->message); + ASSERT (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, + "phase2-private-key-import", "unexpected private key format"); + tmp_fmt = nm_setting_802_1x_get_phase2_private_key_format (s_8021x); + ASSERT (tmp_fmt == format, + "phase2-private-key-import", "unexpected re-read private key format (expected %d, got %d)", + format, tmp_fmt); + + /* Make sure the password is what we expect */ + pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); + ASSERT (pw != NULL, + "phase2-private-key-import", "failed to get previous private key password"); + ASSERT (strcmp (pw, password) == 0, + "phase2-private-key-import", "failed to compare private key password"); + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + tmp_key = (GByteArray *) nm_setting_802_1x_get_phase2_private_key_blob (s_8021x); + ASSERT (tmp_key != NULL, "phase2-private-key-import", "missing private key blob"); + compare_blob_data ("phase2-private-key-import", path, tmp_key); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &tmp_key, NULL); + ASSERT (tmp_key != NULL, "phase2-private-key-import", "missing private key value"); + check_scheme_path (tmp_key, path); + } else + g_assert_not_reached (); + + /* If it's PKCS#12 ensure the client cert is the same value */ + if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { + g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &tmp_key, NULL); + ASSERT (tmp_key != NULL, "private-key-import", "missing private key value"); + + g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, &client_cert, NULL); + ASSERT (client_cert != NULL, "private-key-import", "missing client certificate value"); + + /* make sure they are the same */ + ASSERT (tmp_key->len == client_cert->len, + "private-key-import", "unexpected different private key and client cert lengths"); + ASSERT (memcmp (tmp_key->data, client_cert->data, tmp_key->len) == 0, + "private-key-import", "unexpected different private key and client cert data"); + + g_byte_array_free (tmp_key, TRUE); + g_byte_array_free (client_cert, TRUE); + } + + g_object_unref (s_8021x); +} + +static void +test_wrong_password_keeps_data (const char *path, const char *password) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + GError *error = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + ASSERT (s_8021x != NULL, "wrong-password-keeps-data", "setting was NULL"); + + success = nm_setting_802_1x_set_private_key (s_8021x, + path, + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + ASSERT (success == TRUE, + "wrong-password-keeps-data", "error reading private key: %s", error->message); + ASSERT (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, + "wrong-password-keeps-data", "unexpected private key format (got %d)", format); + + /* Now try to set it to something that's not a certificate */ + format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + success = nm_setting_802_1x_set_private_key (s_8021x, + "Makefile.am", + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + ASSERT (success == FALSE, + "wrong-password-keeps-data", "unexpected success reading private key"); + ASSERT (error != NULL, + "wrong-password-keeps-data", "unexpected missing error"); + ASSERT (format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, + "wrong-password-keeps-data", "unexpected success reading private key format"); + + /* Make sure the password hasn't changed */ + pw = nm_setting_802_1x_get_private_key_password (s_8021x); + ASSERT (pw != NULL, + "wrong-password-keeps-data", "failed to get previous private key password"); + ASSERT (strcmp (pw, password) == 0, + "wrong-password-keeps-data", "failed to compare private key password"); + + g_object_unref (s_8021x); +} + +static void +test_clear_private_key (const char *path, const char *password) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + GError *error = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + ASSERT (s_8021x != NULL, "clear-private-key", "setting was NULL"); + + success = nm_setting_802_1x_set_private_key (s_8021x, + path, + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + ASSERT (success == TRUE, + "clear-private-key", "error reading private key: %s", error->message); + ASSERT (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, + "clear-private-key", "unexpected private key format (got %d)", format); + + /* Make sure the password is what we expect */ + pw = nm_setting_802_1x_get_private_key_password (s_8021x); + ASSERT (pw != NULL, + "clear-private-key", "failed to get previous private key password"); + ASSERT (strcmp (pw, password) == 0, + "clear-private-key", "failed to compare private key password"); + + /* Now clear it */ + success = nm_setting_802_1x_set_private_key (s_8021x, + NULL, + NULL, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + NULL, + &error); + ASSERT (success == TRUE, + "clear-private-key", "unexpected failure clearing private key"); + ASSERT (error == NULL, + "clear-private-key", "unexpected error clearing private key"); + + /* Ensure the password is also now clear */ + ASSERT (nm_setting_802_1x_get_private_key_password (s_8021x) == NULL, + "clear-private-key", "unexpected private key password"); + + g_object_unref (s_8021x); +} + +static void +test_wrong_phase2_password_keeps_data (const char *path, const char *password) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + GError *error = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + ASSERT (s_8021x != NULL, "wrong-phase2-password-keeps-data", "setting was NULL"); + + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + path, + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + ASSERT (success == TRUE, + "wrong-phase2-password-keeps-data", "error reading private key: %s", error->message); + ASSERT (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, + "wrong-phase2-password-keeps-data", "unexpected private key format (got %d)", format); + + /* Now try to set it to something that's not a certificate */ + format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + "Makefile.am", + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + ASSERT (success == FALSE, + "wrong-phase2-password-keeps-data", "unexpected success reading private key"); + ASSERT (error != NULL, + "wrong-phase2-password-keeps-data", "unexpected missing error"); + ASSERT (format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, + "wrong-phase2-password-keeps-data", "unexpected success reading private key format"); + + /* Make sure the password hasn't changed */ + pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); + ASSERT (pw != NULL, + "wrong-phase2-password-keeps-data", "failed to get previous private key password"); + ASSERT (strcmp (pw, password) == 0, + "wrong-phase2-password-keeps-data", "failed to compare private key password"); + + g_object_unref (s_8021x); +} + +static void +test_clear_phase2_private_key (const char *path, const char *password) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + GError *error = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + ASSERT (s_8021x != NULL, "clear-phase2-private-key", "setting was NULL"); + + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + path, + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + ASSERT (success == TRUE, + "clear-phase2-private-key", "error reading private key: %s", error->message); + ASSERT (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, + "clear-phase2-private-key", "unexpected private key format (got %d)", format); + + /* Make sure the password is what we expect */ + pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); + ASSERT (pw != NULL, + "clear-phase2-private-key", "failed to get previous private key password"); + ASSERT (strcmp (pw, password) == 0, + "clear-phase2-private-key", "failed to compare private key password"); + + /* Now clear it */ + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + NULL, + NULL, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + NULL, + &error); + ASSERT (success == TRUE, + "clear-phase2-private-key", "unexpected failure clearing private key"); + ASSERT (error == NULL, + "clear-phase2-private-key", "unexpected error clearing private key"); + + /* Ensure the password is also now clear */ + ASSERT (nm_setting_802_1x_get_phase2_private_key_password (s_8021x) == NULL, + "clear-phase2-private-key", "unexpected private key password"); + + g_object_unref (s_8021x); +} + +int main (int argc, char **argv) +{ + GError *error = NULL; + char *base; + + if (argc < 3) + FAIL ("init", "need at least two arguments: <path> <password>"); + +#if !GLIB_CHECK_VERSION (2, 35, 0) + g_type_init (); +#endif + + if (!nm_utils_init (&error)) + FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message); + + /* Test phase1 and phase2 path scheme */ + test_private_key_import (argv[1], argv[2], NM_SETTING_802_1X_CK_SCHEME_PATH); + test_phase2_private_key_import (argv[1], argv[2], NM_SETTING_802_1X_CK_SCHEME_PATH); + + /* Test phase1 and phase2 blob scheme */ + test_private_key_import (argv[1], argv[2], NM_SETTING_802_1X_CK_SCHEME_BLOB); + test_phase2_private_key_import (argv[1], argv[2], NM_SETTING_802_1X_CK_SCHEME_BLOB); + + /* Test that using a wrong password does not change existing data */ + test_wrong_password_keeps_data (argv[1], argv[2]); + test_wrong_phase2_password_keeps_data (argv[1], argv[2]); + + /* Test clearing the private key */ + test_clear_private_key (argv[1], argv[2]); + test_clear_phase2_private_key (argv[1], argv[2]); + + base = g_path_get_basename (argv[0]); + fprintf (stdout, "%s: SUCCESS\n", base); + g_free (base); + return 0; +} + diff --git a/libnm-core/tests/test-setting-dcb.c b/libnm-core/tests/test-setting-dcb.c new file mode 100644 index 0000000000..4b114ada35 --- /dev/null +++ b/libnm-core/tests/test-setting-dcb.c @@ -0,0 +1,328 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2013 Red Hat, Inc. + * + */ + +#include <glib.h> +#include <string.h> +#include <nm-utils.h> +#include <nm-glib-compat.h> +#include "nm-setting-dcb.h" + +#define DCB_FLAGS_ALL (NM_SETTING_DCB_FLAG_ENABLE | \ + NM_SETTING_DCB_FLAG_ADVERTISE | \ + NM_SETTING_DCB_FLAG_WILLING) + +static void +test_dcb_flags_valid (void) +{ + NMSettingDcb *s_dcb; + GError *error = NULL; + gboolean success; + guint i; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + g_assert_cmpint (nm_setting_dcb_get_app_fcoe_flags (s_dcb), ==, 0); + g_assert_cmpint (nm_setting_dcb_get_app_iscsi_flags (s_dcb), ==, 0); + g_assert_cmpint (nm_setting_dcb_get_app_fip_flags (s_dcb), ==, 0); + g_assert_cmpint (nm_setting_dcb_get_priority_flow_control_flags (s_dcb), ==, 0); + g_assert_cmpint (nm_setting_dcb_get_priority_group_flags (s_dcb), ==, 0); + + g_object_set (G_OBJECT (s_dcb), + NM_SETTING_DCB_APP_FCOE_FLAGS, DCB_FLAGS_ALL, + NM_SETTING_DCB_APP_ISCSI_FLAGS, DCB_FLAGS_ALL, + NM_SETTING_DCB_APP_FIP_FLAGS, DCB_FLAGS_ALL, + NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, DCB_FLAGS_ALL, + NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, DCB_FLAGS_ALL, + NULL); + /* Priority Group Bandwidth must total 100% */ + for (i = 0; i < 7; i++) + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, i, 12); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); + + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); + g_assert_no_error (error); + g_assert (success); + + g_assert_cmpint (nm_setting_dcb_get_app_fcoe_flags (s_dcb), ==, DCB_FLAGS_ALL); + g_assert_cmpint (nm_setting_dcb_get_app_iscsi_flags (s_dcb), ==, DCB_FLAGS_ALL); + g_assert_cmpint (nm_setting_dcb_get_app_fip_flags (s_dcb), ==, DCB_FLAGS_ALL); + g_assert_cmpint (nm_setting_dcb_get_priority_flow_control_flags (s_dcb), ==, DCB_FLAGS_ALL); + g_assert_cmpint (nm_setting_dcb_get_priority_group_flags (s_dcb), ==, DCB_FLAGS_ALL); +} + +#define TEST_FLAG(p, f, v) \ +{ \ + /* GObject property min/max should ensure the property does not get set to \ + * the invalid value, so we ensure the value we just tried to set is 0 and \ + * that verify is successful since the property never got set. \ + */ \ + g_object_set (G_OBJECT (s_dcb), p, v, NULL); \ + g_assert_cmpint (f (s_dcb), ==, 0); \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + g_assert_no_error (error); \ + g_assert (success); \ +} + +static void +test_dcb_flags_invalid (void) +{ + NMSettingDcb *s_dcb; + GError *error = NULL; + gboolean success; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_APP_FCOE_FLAGS, nm_setting_dcb_get_app_fcoe_flags, 0x332523); + g_test_assert_expected_messages (); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_APP_ISCSI_FLAGS, nm_setting_dcb_get_app_iscsi_flags, 0xFF); + g_test_assert_expected_messages (); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_APP_FIP_FLAGS, nm_setting_dcb_get_app_fip_flags, 0x1111); + g_test_assert_expected_messages (); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, nm_setting_dcb_get_priority_flow_control_flags, G_MAXUINT32); + g_test_assert_expected_messages (); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, nm_setting_dcb_get_priority_group_flags, + (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE | NM_SETTING_DCB_FLAG_WILLING) + 1); + g_test_assert_expected_messages (); +} + +#define TEST_APP_PRIORITY(lcprop, ucprop, v) \ +{ \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_FLAGS, NM_SETTING_DCB_FLAG_NONE, NULL); \ + \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_PRIORITY, v, NULL); \ + g_assert_cmpint (nm_setting_dcb_get_app_##lcprop##_priority (s_dcb), ==, v); \ + \ + /* Assert that the setting is invalid while the app is disabled unless v is default */ \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + if (v >= 0) { \ + g_assert_error (error, NM_SETTING_DCB_ERROR, NM_SETTING_DCB_ERROR_INVALID_PROPERTY); \ + g_assert (success == FALSE); \ + } else { \ + g_assert_no_error (error); \ + g_assert (success); \ + } \ + g_clear_error (&error); \ + \ + /* Set the enable flag and re-verify, this time it should be valid */ \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + g_assert_no_error (error); \ + g_assert (success); \ + \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_PRIORITY, 0, NULL); \ +} + +static void +test_dcb_app_priorities (void) +{ + NMSettingDcb *s_dcb; + GError *error = NULL; + gboolean success; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + /* Defaults */ + g_assert_cmpint (nm_setting_dcb_get_app_fcoe_priority (s_dcb), ==, -1); + g_assert_cmpint (nm_setting_dcb_get_app_iscsi_priority (s_dcb), ==, -1); + g_assert_cmpint (nm_setting_dcb_get_app_fip_priority (s_dcb), ==, -1); + + TEST_APP_PRIORITY (fcoe, FCOE, 6); + TEST_APP_PRIORITY (iscsi, ISCSI, 5); + TEST_APP_PRIORITY (fip, FIP, 4); + + TEST_APP_PRIORITY (fcoe, FCOE, -1); + TEST_APP_PRIORITY (iscsi, ISCSI, -1); + TEST_APP_PRIORITY (fip, FIP, -1); +} + +#define TEST_PRIORITY_VALID(fn, id, val, flagsprop, verify) \ +{ \ + /* Assert that setting the value gets the same value back out */ \ + nm_setting_dcb_set_priority_##fn (s_dcb, id, val); \ + g_assert_cmpint (nm_setting_dcb_get_priority_##fn (s_dcb, id), ==, val); \ + \ + if (verify) { \ + if (val != 0) { \ + /* Assert that verify fails because the flags do not include 'enabled' \ + * and a value has been set. \ + */ \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + g_assert_error (error, NM_SETTING_DCB_ERROR, NM_SETTING_DCB_ERROR_INVALID_PROPERTY); \ + g_assert (success == FALSE); \ + g_clear_error (&error); \ + } \ + \ + /* Assert that adding the 'enabled' flag verifies the setting */ \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_##flagsprop##_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + g_assert_no_error (error); \ + g_assert (success); \ + } \ + \ + /* Reset everything */ \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_##flagsprop##_FLAGS, NM_SETTING_DCB_FLAG_NONE, NULL); \ + nm_setting_dcb_set_priority_##fn (s_dcb, id, 0); \ +} + +/* If Priority Groups are enabled, PG bandwidth must equal 100% */ +#define SET_VALID_PRIORITY_GROUP_BANDWIDTH \ +{ \ + guint x; \ + for (x = 0; x < 7; x++) \ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, x, 12); \ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); \ +} + +static void +test_dcb_priorities_valid (void) +{ + NMSettingDcb *s_dcb; + GError *error = NULL; + gboolean success; + guint i; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + for (i = 0; i < 8; i++) + TEST_PRIORITY_VALID (flow_control, i, TRUE, FLOW_CONTROL, TRUE); + + SET_VALID_PRIORITY_GROUP_BANDWIDTH + for (i = 0; i < 8; i++) { + TEST_PRIORITY_VALID (group_id, i, i, GROUP, TRUE); + TEST_PRIORITY_VALID (group_id, i, 7 - i, GROUP, TRUE); + } + + /* Clear PG bandwidth from earlier tests */ + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, i, 0); + + /* Priority Group Bandwidth must add up to 100% if enabled, which requires + * some dancing for verifying individual values here. + */ + for (i = 0; i < 8; i++) { + guint other = 7 - (i % 8); + + /* Set another priority group to the remaining bandwidth */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 100 - i); + TEST_PRIORITY_VALID (group_bandwidth, i, i, GROUP, TRUE); + + /* Set another priority group to the remaining bandwidth */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 100 - (7 - i)); + TEST_PRIORITY_VALID (group_bandwidth, i, 7 - i, GROUP, TRUE); + + /* Clear remaining bandwidth */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 0); + } + + SET_VALID_PRIORITY_GROUP_BANDWIDTH + for (i = 0; i < 8; i++) { + TEST_PRIORITY_VALID (bandwidth, i, i, GROUP, TRUE); + TEST_PRIORITY_VALID (bandwidth, i, 7 - i, GROUP, TRUE); + } + + SET_VALID_PRIORITY_GROUP_BANDWIDTH + for (i = 0; i < 8; i++) + TEST_PRIORITY_VALID (strict_bandwidth, i, TRUE, GROUP, TRUE); + + SET_VALID_PRIORITY_GROUP_BANDWIDTH + for (i = 0; i < 8; i++) { + TEST_PRIORITY_VALID (traffic_class, i, i, GROUP, TRUE); + TEST_PRIORITY_VALID (traffic_class, i, 7 - i, GROUP, TRUE); + } +} + +static void +test_dcb_bandwidth_sums (void) +{ + NMSettingDcb *s_dcb; + GError *error = NULL; + gboolean success; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + /* Assert that setting the value gets the same value back out */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 0, 9); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 1, 10); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 2, 11); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 3, 12); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 4, 13); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 5, 14); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 6, 15); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); + + /* Assert verify success when sums total 100% */ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); + g_assert_no_error (error); + g_assert (success); + + /* Assert verify fails when sums do not total 100% */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 4, 20); + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); + g_assert_error (error, NM_SETTING_DCB_ERROR, NM_SETTING_DCB_ERROR_INVALID_PROPERTY); + g_assert (success == FALSE); + g_clear_error (&error); +} + +#define TPATH "/libnm-util/settings/dcb/" + +int main (int argc, char **argv) +{ + GError *error = NULL; + gboolean success; + + g_test_init (&argc, &argv, NULL); + +#if !GLIB_CHECK_VERSION (2, 35, 0) + g_type_init (); +#endif + + success = nm_utils_init (&error); + g_assert_no_error (error); + g_assert (success); + +#if !GLIB_CHECK_VERSION(2,34,0) + g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL); +#endif + + g_test_add_func (TPATH "flags-valid", test_dcb_flags_valid); + g_test_add_func (TPATH "flags-invalid", test_dcb_flags_invalid); + g_test_add_func (TPATH "app-priorities", test_dcb_app_priorities); + g_test_add_func (TPATH "priorities", test_dcb_priorities_valid); + g_test_add_func (TPATH "bandwidth-sums", test_dcb_bandwidth_sums); + + return g_test_run (); +} + diff --git a/libnm-core/tests/test-settings-defaults.c b/libnm-core/tests/test-settings-defaults.c new file mode 100644 index 0000000000..9104b4bc6c --- /dev/null +++ b/libnm-core/tests/test-settings-defaults.c @@ -0,0 +1,134 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2008 - 2011 Red Hat, Inc. + * + */ + +#include <glib.h> +#include <string.h> + +#include <nm-utils.h> + +#include "nm-setting-8021x.h" +#include "nm-setting-cdma.h" +#include "nm-setting-connection.h" +#include "nm-setting-gsm.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-ip6-config.h" +#include "nm-setting-ppp.h" +#include "nm-setting-pppoe.h" +#include "nm-setting-serial.h" +#include "nm-setting-vpn.h" +#include "nm-setting-wired.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" + +#include "nm-test-utils.h" + +static void +test_defaults (GType type, const char *name) +{ + GParamSpec **property_specs; + guint n_property_specs; + GObject *setting; + int i; + + setting = g_object_new (type, NULL); + + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs); + ASSERT (property_specs != NULL, + name, "couldn't find property specs for object of type '%s'", + g_type_name (G_OBJECT_TYPE (setting))); + + for (i = 0; i < n_property_specs; i++) { + GParamSpec *prop_spec = property_specs[i]; + GValue value = G_VALUE_INIT; + GValue defvalue = G_VALUE_INIT; + char *actual, *expected; + gboolean ok = FALSE; + + /* Ignore non-fundamental types since they won't really have + * defaults. + */ + if (!G_TYPE_IS_FUNDAMENTAL (prop_spec->value_type)) + continue; + + g_value_init (&value, prop_spec->value_type); + g_object_get_property (G_OBJECT (setting), prop_spec->name, &value); + + g_value_init (&defvalue, prop_spec->value_type); + g_param_value_set_default (prop_spec, &defvalue); + + actual = g_strdup_value_contents (&value); + expected = g_strdup_value_contents (&defvalue); + + if (!strcmp (prop_spec->name, NM_SETTING_NAME)) { + /* 'name' is always the setting name, not the default value */ + ok = !strcmp (nm_setting_get_name (NM_SETTING (setting)), name); + g_free (expected); + expected = g_strdup (name); + } else + ok = g_param_value_defaults (prop_spec, &value); + + ASSERT (ok, + name, "property '%s' value '%s' not the expected default value '%s'", + prop_spec->name, actual, expected); + + g_free (actual); + g_free (expected); + g_value_unset (&value); + g_value_unset (&defvalue); + } + + g_free (property_specs); + g_object_unref (setting); +} + +int main (int argc, char **argv) +{ + GError *error = NULL; + char *base; + +#if !GLIB_CHECK_VERSION (2, 35, 0) + g_type_init (); +#endif + + if (!nm_utils_init (&error)) + FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message); + + /* The tests */ + test_defaults (NM_TYPE_SETTING_CONNECTION, NM_SETTING_CONNECTION_SETTING_NAME); + test_defaults (NM_TYPE_SETTING_802_1X, NM_SETTING_802_1X_SETTING_NAME); + test_defaults (NM_TYPE_SETTING_CDMA, NM_SETTING_CDMA_SETTING_NAME); + test_defaults (NM_TYPE_SETTING_GSM, NM_SETTING_GSM_SETTING_NAME); + test_defaults (NM_TYPE_SETTING_IP4_CONFIG, NM_SETTING_IP4_CONFIG_SETTING_NAME); + test_defaults (NM_TYPE_SETTING_IP6_CONFIG, NM_SETTING_IP6_CONFIG_SETTING_NAME); + test_defaults (NM_TYPE_SETTING_PPP, NM_SETTING_PPP_SETTING_NAME); + test_defaults (NM_TYPE_SETTING_PPPOE, NM_SETTING_PPPOE_SETTING_NAME); + test_defaults (NM_TYPE_SETTING_SERIAL, NM_SETTING_SERIAL_SETTING_NAME); + test_defaults (NM_TYPE_SETTING_VPN, NM_SETTING_VPN_SETTING_NAME); + test_defaults (NM_TYPE_SETTING_WIRED, NM_SETTING_WIRED_SETTING_NAME); + test_defaults (NM_TYPE_SETTING_WIRELESS, NM_SETTING_WIRELESS_SETTING_NAME); + test_defaults (NM_TYPE_SETTING_WIRELESS_SECURITY, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + + base = g_path_get_basename (argv[0]); + fprintf (stdout, "%s: SUCCESS\n", base); + g_free (base); + return 0; +} + |