diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2015-06-09 22:15:26 +0200 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2015-06-09 22:15:26 +0200 |
commit | 7d09debdf0b1bd6d76a645d5195e89e3ad16892f (patch) | |
tree | f3b03af96f8bf10d798dbe6c82daa113af476700 | |
parent | acef8c6a1baf5fc5d03accfe4e1eb941570028a2 (diff) | |
parent | 04d5804dd5826cc36398027a023b0e639a54bd26 (diff) | |
download | NetworkManager-7d09debdf0b1bd6d76a645d5195e89e3ad16892f.tar.gz |
merge branch 'bg/metered-connections-bgo741725'
Expose information about whether a connection is metered and use some
heuristics to choose a reasonable default when the value is not
configured.
https://bugzilla.gnome.org/show_bug.cgi?id=741725
https://bugzilla.redhat.com/show_bug.cgi?id=1200452
38 files changed, 668 insertions, 12 deletions
diff --git a/clients/cli/common.c b/clients/cli/common.c index 5b07360d74..d92aeaf263 100644 --- a/clients/cli/common.c +++ b/clients/cli/common.c @@ -522,6 +522,23 @@ nmc_device_state_to_string (NMDeviceState state) } const char * +nmc_device_metered_to_string (NMMetered value) +{ + switch (value) { + case NM_METERED_YES: + return _("yes"); + case NM_METERED_NO: + return _("no"); + case NM_METERED_GUESS_YES: + return _("yes (guessed)"); + case NM_METERED_GUESS_NO: + return _("no (guessed)"); + default: + return _("unknown"); + } +} + +const char * nmc_device_reason_to_string (NMDeviceStateReason reason) { switch (reason) { diff --git a/clients/cli/common.h b/clients/cli/common.h index 8bb043f4d7..bb3f44e851 100644 --- a/clients/cli/common.h +++ b/clients/cli/common.h @@ -35,6 +35,7 @@ NMIPRoute *nmc_parse_and_build_route (int family, const char *first, const char const char * nmc_device_state_to_string (NMDeviceState state); const char * nmc_device_reason_to_string (NMDeviceStateReason reason); +const char * nmc_device_metered_to_string (NMMetered value); char ** nmc_vlan_parse_priority_maps (const char *priority_map, diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 1c9f5881d6..fd2f7b6167 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -79,11 +79,12 @@ static NmcOutputField nmc_fields_dev_show_general[] = { {"CONNECTION", N_("CONNECTION"), 20}, /* 21 */ {"CON-UUID", N_("CON-UUID"), 38}, /* 22 */ {"CON-PATH", N_("CON-PATH"), 51}, /* 23 */ + {"METERED", N_("METERED"), 10}, /* 24 */ {NULL, NULL, 0} }; #define NMC_FIELDS_DEV_SHOW_GENERAL_ALL "NAME,DEVICE,TYPE,NM-TYPE,VENDOR,PRODUCT,DRIVER,DRIVER-VERSION,FIRMWARE-VERSION,HWADDR,MTU,"\ "STATE,REASON,UDI,IP-IFACE,IS-SOFTWARE,NM-MANAGED,AUTOCONNECT,FIRMWARE-MISSING,NM-PLUGIN-MISSING,"\ - "PHYS-PORT-ID,CONNECTION,CON-UUID,CON-PATH" + "PHYS-PORT-ID,CONNECTION,CON-UUID,CON-PATH,METERED" #define NMC_FIELDS_DEV_SHOW_GENERAL_COMMON "NAME,DEVICE,TYPE,VENDOR,PRODUCT,DRIVER,HWADDR,STATE" /* Available fields for 'device show' - CONNECTIONS part */ @@ -845,6 +846,7 @@ show_device_info (NMDevice *device, NmCli *nmc) set_val_strc (arr, 21, get_active_connection_id (device)); set_val_strc (arr, 22, acon ? nm_active_connection_get_uuid (acon) : NULL); set_val_strc (arr, 23, acon ? nm_object_get_path (NM_OBJECT (acon)) : NULL); + set_val_strc (arr, 24, nmc_device_metered_to_string (nm_device_get_metered (device))); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ diff --git a/clients/cli/settings.c b/clients/cli/settings.c index e0ae53904f..58149a37c4 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -59,6 +59,7 @@ NmcOutputField nmc_fields_setting_connection[] = { SETTING_FIELD (NM_SETTING_CONNECTION_SLAVE_TYPE, 20), /* 12 */ SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES, 40), /* 13 */ SETTING_FIELD (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, 30), /* 14 */ + SETTING_FIELD (NM_SETTING_CONNECTION_METERED, 10), /* 15 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_CONNECTION_ALL "name"","\ @@ -75,7 +76,8 @@ NmcOutputField nmc_fields_setting_connection[] = { NM_SETTING_CONNECTION_MASTER","\ NM_SETTING_CONNECTION_SLAVE_TYPE","\ NM_SETTING_CONNECTION_SECONDARIES","\ - NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT + NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT","\ + NM_SETTING_CONNECTION_METERED #define NMC_FIELDS_SETTING_CONNECTION_COMMON NMC_FIELDS_SETTING_CONNECTION_ALL /* Available fields for NM_SETTING_WIRED_SETTING_NAME */ @@ -2691,6 +2693,51 @@ nmc_property_connection_describe_secondaries (NMSetting *setting, const char *pr "Example: private-openvpn, fe6ba5d8-c2fc-4aae-b2e3-97efddd8d9a7\n"); } +/* 'metered' */ +static char * +nmc_property_connection_get_metered (NMSetting *setting, NmcPropertyGetType get_type) +{ + NMSettingConnection *s_conn = NM_SETTING_CONNECTION (setting); + + switch (nm_setting_connection_get_metered (s_conn)) { + case NM_METERED_YES: + return g_strdup (_("yes")); + case NM_METERED_NO: + return g_strdup (_("no")); + case NM_METERED_UNKNOWN: + default: + return g_strdup (_("unknown")); + } +} + +static gboolean +nmc_property_connection_set_metered (NMSetting *setting, const char *prop, + const char *val, GError **error) +{ + NMMetered metered; + NMCTriStateValue ts_val; + + if (!nmc_string_to_tristate (val, &ts_val, error)) + return FALSE; + + switch (ts_val) { + case NMC_TRI_STATE_YES: + metered = NM_METERED_YES; + break; + case NMC_TRI_STATE_NO: + metered = NM_METERED_NO; + break; + case NMC_TRI_STATE_UNKNOWN: + metered = NM_METERED_UNKNOWN; + break; + default: + g_assert_not_reached(); + } + + g_object_set (setting, prop, metered, NULL); + return TRUE; +} + /* --- NM_SETTING_802_1X_SETTING_NAME property setter functions --- */ #define DEFINE_SETTER_STR_LIST(def_func, set_func) \ static gboolean \ @@ -5372,6 +5419,13 @@ nmc_properties_init (void) NULL, NULL, NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, METERED), + nmc_property_connection_get_metered, + nmc_property_connection_set_metered, + NULL, + NULL, + NULL, + NULL); /* Add editable properties for NM_SETTING_DCB_SETTING_NAME */ nmc_add_prop_funcs (GLUE (DCB, APP_FCOE_FLAGS), @@ -6752,6 +6806,7 @@ setting_connection_details (NMSetting *setting, NmCli *nmc, const char *one_pro set_val_str (arr, 12, nmc_property_connection_get_slave_type (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 13, nmc_property_connection_get_secondaries (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 14, nmc_property_connection_get_gateway_ping_timeout (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 15, nmc_property_connection_get_metered (setting, NMC_PROPERTY_GET_PRETTY)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ diff --git a/clients/cli/utils.c b/clients/cli/utils.c index 727e309d1c..575ece7c0c 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -500,7 +500,10 @@ nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error) if (g_strcmp0 (str, "o") == 0) { g_set_error (error, 1, 0, - _("'%s' is ambiguous (on x off)"), str); + /* Translators: the first %s is the partial value entered by + * the user, the second %s a list of compatible values. + */ + _("'%s' is ambiguous (%s)"), str, "on x off"); return FALSE; } @@ -517,6 +520,39 @@ nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error) return TRUE; } +gboolean +nmc_string_to_tristate (const char *str, NMCTriStateValue *val, GError **error) +{ + const char *s_true[] = { "true", "yes", "on", NULL }; + const char *s_false[] = { "false", "no", "off", NULL }; + const char *s_unknown[] = { "unknown", NULL }; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (g_strcmp0 (str, "o") == 0) { + g_set_error (error, 1, 0, + /* Translators: the first %s is the partial value entered by + * the user, the second %s a list of compatible values. + */ + _("'%s' is ambiguous (%s)"), str, "on x off"); + return FALSE; + } + + if (nmc_string_is_valid (str, s_true, NULL)) + *val = NMC_TRI_STATE_YES; + else if (nmc_string_is_valid (str, s_false, NULL)) + *val = NMC_TRI_STATE_NO; + else if (nmc_string_is_valid (str, s_unknown, NULL)) + *val = NMC_TRI_STATE_UNKNOWN; + else { + g_set_error (error, 1, 0, + _("'%s' is not valid; use [%s], [%s] or [%s]"), + str, "true, yes, on", "false, no, off", "unknown"); + return FALSE; + } + return TRUE; +} + /* * Ask user for input and return the string. * The caller is responsible for freeing the returned string. diff --git a/clients/cli/utils.h b/clients/cli/utils.h index 6defb1de14..54f76b071a 100644 --- a/clients/cli/utils.h +++ b/clients/cli/utils.h @@ -32,6 +32,12 @@ typedef struct { gboolean found; } nmc_arg_t; +typedef enum { + NMC_TRI_STATE_NO, + NMC_TRI_STATE_YES, + NMC_TRI_STATE_UNKNOWN, +} NMCTriStateValue; + /* === Functions === */ int matches (const char *cmd, const char *pattern); int next_arg (int *argc, char ***argv); @@ -62,6 +68,7 @@ gboolean nmc_string_to_uint (const char *str, unsigned long int max, unsigned long int *value); gboolean nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error); +gboolean nmc_string_to_tristate (const char *str, NMCTriStateValue *val, GError **error); char *nmc_ip4_address_as_string (guint32 ip, GError **error); char *nmc_ip6_address_as_string (const struct in6_addr *ip, GError **error); void nmc_terminal_erase_line (void); diff --git a/introspection/nm-device.xml b/introspection/nm-device.xml index 71b830f108..239358a1e7 100644 --- a/introspection/nm-device.xml +++ b/introspection/nm-device.xml @@ -145,6 +145,12 @@ The device MTU (maximum transmission unit). </tp:docstring> </property> + <property name="Metered" type="u" access="read" tp:type="NM_METERED"> + <tp:docstring> + Whether the amount of traffic flowing through the device is + subject to limitations, for example set by service providers. + </tp:docstring> + </property> <method name="Disconnect"> <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_device_disconnect"/> @@ -660,6 +666,34 @@ </tp:enumvalue> </tp:enum> + <tp:enum name="NM_METERED" type="u"> + <tp:enumvalue suffix="UNKNOWN" value="0"> + <tp:docstring> + The device metered status is unknown. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="YES" value="1"> + <tp:docstring> + The device is metered and the value was statically set. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="NO" value="2"> + <tp:docstring> + The device is not metered and the value was statically set. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="GUESS_YES" value="3"> + <tp:docstring> + The device is metered and the value was guessed. + </tp:docstring> + </tp:enumvalue> + <tp:enumvalue suffix="GUESS_NO" value="4"> + <tp:docstring> + The device is not metered and the value was guessed. + </tp:docstring> + </tp:enumvalue> + </tp:enum> + <tp:struct name="NM_DEVICE_STATE_REASON_STRUCT"> <tp:member type="u" name="state" tp:type="NM_DEVICE_STATE"> <tp:docstring> diff --git a/introspection/nm-manager.xml b/introspection/nm-manager.xml index a9bb6dcf14..f0d04438d5 100644 --- a/introspection/nm-manager.xml +++ b/introspection/nm-manager.xml @@ -335,6 +335,14 @@ </tp:docstring> </property> + <property name="Metered" type="u" access="read" tp:type="NM_METERED"> + <tp:docstring> + Wheter the connectivity is metered. This is equivalent to the + metered property of the device associated with the primary + connection. + </tp:docstring> + </property> + <property name="ActivatingConnection" type="o" access="read"> <tp:docstring> The object path of an active connection that is currently diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h index ccc3b3a9e9..0934add6fd 100644 --- a/libnm-core/nm-dbus-interface.h +++ b/libnm-core/nm-dbus-interface.h @@ -403,7 +403,6 @@ typedef enum { NM_DEVICE_STATE_FAILED = 120 } NMDeviceState; - /** * NMDeviceStateReason: * @NM_DEVICE_STATE_REASON_NONE: No reason given @@ -540,6 +539,25 @@ typedef enum { NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED = 62, } NMDeviceStateReason; +/** + * NMMetered: + * @NM_METERED_UNKNOWN: The metered status is unknown + * @NM_METERED_YES: Metered, the value was statically set + * @NM_METERED_NO: Not metered, the value was statically set + * @NM_METERED_GUESS_YES: Metered, the value was guessed + * @NM_METERED_GUESS_NO: Not metered, the value was guessed + * + * (Corresponds to the NM_METERED type in nm-device.xml.) + * + * Since: 1.2 + **/ +typedef enum { + NM_METERED_UNKNOWN = 0, + NM_METERED_YES = 1, + NM_METERED_NO = 2, + NM_METERED_GUESS_YES = 3, + NM_METERED_GUESS_NO = 4, +} NMMetered; /** * NMActiveConnectionState: diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c index 7bc927bc1d..5812a0a442 100644 --- a/libnm-core/nm-setting-connection.c +++ b/libnm-core/nm-setting-connection.c @@ -74,6 +74,7 @@ typedef struct { char *zone; GSList *secondaries; /* secondary connections to activate with the base connection */ guint gateway_ping_timeout; + NMMetered metered; } NMSettingConnectionPrivate; enum { @@ -92,6 +93,7 @@ enum { PROP_SLAVE_TYPE, PROP_SECONDARIES, PROP_GATEWAY_PING_TIMEOUT, + PROP_METERED, LAST_PROP }; @@ -738,6 +740,23 @@ nm_setting_connection_get_gateway_ping_timeout (NMSettingConnection *setting) return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->gateway_ping_timeout; } +/** + * nm_setting_connection_get_metered: + * @setting: the #NMSettingConnection + * + * Returns: the #NMSettingConnection:metered property of the setting. + * + * Since: 1.2 + **/ +NMMetered +nm_setting_connection_get_metered (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), + NM_METERED_UNKNOWN); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->metered; +} + static void _set_error_missing_base_setting (GError **error, const char *type) { @@ -899,6 +918,18 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } + if (priv->metered != NM_METERED_UNKNOWN && + priv->metered != NM_METERED_YES && + priv->metered != NM_METERED_NO) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("metered value %d is not valid"), priv->metered); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_METERED); + return FALSE; + } + /* *** errors above here should be always fatal, below NORMALIZABLE_ERROR *** */ if (!priv->uuid) { @@ -1129,6 +1160,9 @@ set_property (GObject *object, guint prop_id, case PROP_GATEWAY_PING_TIMEOUT: priv->gateway_ping_timeout = g_value_get_uint (value); break; + case PROP_METERED: + priv->metered = g_value_get_enum (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1199,6 +1233,9 @@ get_property (GObject *object, guint prop_id, case PROP_GATEWAY_PING_TIMEOUT: g_value_set_uint (value, priv->gateway_ping_timeout); break; + case PROP_METERED: + g_value_set_enum (value, priv->metered); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1567,4 +1604,27 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:metered: + * + * Whether the connection is metered. + * + * Since: 1.2 + **/ + /* ---ifcfg-rh--- + * property: metered + * variable: CONNECTION_METERED + * values: yes,no,unknown + * description: Whether the device is metered + * example: CONNECTION_METERED=yes + * ---end--- + */ + g_object_class_install_property + (object_class, PROP_METERED, + g_param_spec_enum (NM_SETTING_CONNECTION_METERED, "", "", + NM_TYPE_METERED, + NM_METERED_UNKNOWN, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); } diff --git a/libnm-core/nm-setting-connection.h b/libnm-core/nm-setting-connection.h index d1ad0fe1e5..1692fe2c53 100644 --- a/libnm-core/nm-setting-connection.h +++ b/libnm-core/nm-setting-connection.h @@ -58,6 +58,7 @@ G_BEGIN_DECLS #define NM_SETTING_CONNECTION_SLAVE_TYPE "slave-type" #define NM_SETTING_CONNECTION_SECONDARIES "secondaries" #define NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT "gateway-ping-timeout" +#define NM_SETTING_CONNECTION_METERED "metered" /** * NMSettingConnection: @@ -119,6 +120,8 @@ void nm_setting_connection_remove_secondary (NMSettingConnection *set gboolean nm_setting_connection_remove_secondary_by_value (NMSettingConnection *setting, const char *sec_uuid); guint32 nm_setting_connection_get_gateway_ping_timeout (NMSettingConnection *setting); +NM_AVAILABLE_IN_1_2 +NMMetered nm_setting_connection_get_metered (NMSettingConnection *setting); G_END_DECLS diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 55e905d5e8..1aca515a88 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -1967,6 +1967,7 @@ test_connection_diff_a_only (void) { 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 }, + { NM_SETTING_CONNECTION_METERED, NM_SETTING_DIFF_RESULT_IN_A }, { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN } } }, { NM_SETTING_WIRED_SETTING_NAME, { diff --git a/libnm/libnm.ver b/libnm/libnm.ver index c15f0381c0..34890414c3 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -848,9 +848,12 @@ local: libnm_1_2_0 { global: nm_access_point_get_last_seen; + nm_device_get_metered; nm_device_get_nm_plugin_missing; + nm_metered_get_type; nm_setting_802_1x_check_cert_scheme; nm_setting_bridge_get_multicast_snooping; + nm_setting_connection_get_metered; nm_setting_ip_config_add_dns_option; nm_setting_ip_config_clear_dns_options; nm_setting_ip_config_get_dns_option; diff --git a/libnm/nm-client.c b/libnm/nm-client.c index e0a2a7b4f4..3d683a1c67 100644 --- a/libnm/nm-client.c +++ b/libnm/nm-client.c @@ -76,6 +76,7 @@ enum { PROP_CONNECTIONS, PROP_HOSTNAME, PROP_CAN_MODIFY, + PROP_METERED, LAST_PROP }; @@ -1871,6 +1872,7 @@ get_property (GObject *object, guint prop_id, case PROP_PRIMARY_CONNECTION: case PROP_ACTIVATING_CONNECTION: case PROP_DEVICES: + case PROP_METERED: g_object_get_property (G_OBJECT (NM_CLIENT_GET_PRIVATE (object)->manager), pspec->name, value); break; @@ -2143,6 +2145,20 @@ nm_client_class_init (NMClientClass *client_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMClient:metered: + * + * Whether the connectivity is metered. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_METERED, + g_param_spec_uint (NM_CLIENT_METERED, "", "", + 0, G_MAXUINT32, NM_METERED_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* signals */ /** diff --git a/libnm/nm-client.h b/libnm/nm-client.h index 619ad8585f..fc309fa1a0 100644 --- a/libnm/nm-client.h +++ b/libnm/nm-client.h @@ -56,6 +56,7 @@ G_BEGIN_DECLS #define NM_CLIENT_CONNECTIONS "connections" #define NM_CLIENT_HOSTNAME "hostname" #define NM_CLIENT_CAN_MODIFY "can-modify" +#define NM_CLIENT_METERED "metered" #define NM_CLIENT_DEVICE_ADDED "device-added" #define NM_CLIENT_DEVICE_REMOVED "device-removed" diff --git a/libnm/nm-device.c b/libnm/nm-device.c index 44cdc89153..9781095dbe 100644 --- a/libnm/nm-device.c +++ b/libnm/nm-device.c @@ -81,6 +81,7 @@ typedef struct { char *driver_version; char *firmware_version; char *type_description; + NMMetered metered; NMDeviceCapabilities capabilities; gboolean managed; gboolean firmware_missing; @@ -132,6 +133,7 @@ enum { PROP_AVAILABLE_CONNECTIONS, PROP_PHYSICAL_PORT_ID, PROP_MTU, + PROP_METERED, LAST_PROP }; @@ -196,6 +198,7 @@ init_dbus (NMObject *object) { NM_DEVICE_AVAILABLE_CONNECTIONS, &priv->available_connections, NULL, NM_TYPE_REMOTE_CONNECTION }, { NM_DEVICE_PHYSICAL_PORT_ID, &priv->physical_port_id }, { NM_DEVICE_MTU, &priv->mtu }, + { NM_DEVICE_METERED, &priv->metered }, /* Properties that exist in D-Bus but that we don't track */ { "ip4-address", NULL }, @@ -455,6 +458,9 @@ get_property (GObject *object, case PROP_MTU: g_value_set_uint (value, nm_device_get_mtu (device)); break; + case PROP_METERED: + g_value_set_uint (value, nm_device_get_metered (device)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -814,6 +820,20 @@ nm_device_class_init (NMDeviceClass *device_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMDevice:metered: + * + * Whether the device is metered. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_METERED, + g_param_spec_uint (NM_DEVICE_METERED, "", "", + 0, G_MAXUINT32, NM_METERED_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* signals */ /** @@ -1942,6 +1962,24 @@ nm_device_get_mtu (NMDevice *device) } /** + * nm_device_get_metered: + * @device: a #NMDevice + * + * Gets the metered setting of a #NMDevice. + * + * Returns: the metered setting. + * + * Since: 1.2 + **/ +NMMetered +nm_device_get_metered (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), NM_METERED_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE (device)->metered; +} + +/** * nm_device_is_software: * @device: a #NMDevice * diff --git a/libnm/nm-device.h b/libnm/nm-device.h index 91cf13b1a0..66e1d5780c 100644 --- a/libnm/nm-device.h +++ b/libnm/nm-device.h @@ -61,6 +61,7 @@ G_BEGIN_DECLS #define NM_DEVICE_PRODUCT "product" #define NM_DEVICE_PHYSICAL_PORT_ID "physical-port-id" #define NM_DEVICE_MTU "mtu" +#define NM_DEVICE_METERED "metered" struct _NMDevice { NMObject parent; @@ -122,6 +123,8 @@ gboolean nm_device_is_software (NMDevice *device); const char * nm_device_get_product (NMDevice *device); const char * nm_device_get_vendor (NMDevice *device); const char * nm_device_get_description (NMDevice *device); +NM_AVAILABLE_IN_1_2 +NMMetered nm_device_get_metered (NMDevice *device); char ** nm_device_disambiguate_names (NMDevice **devices, int num_devices); diff --git a/libnm/nm-manager.c b/libnm/nm-manager.c index 003d6b8755..2fd40273a3 100644 --- a/libnm/nm-manager.c +++ b/libnm/nm-manager.c @@ -64,6 +64,7 @@ typedef struct { NMConnectivityState connectivity; NMActiveConnection *primary_connection; NMActiveConnection *activating_connection; + NMMetered metered; GCancellable *perm_call_cancellable; GHashTable *permissions; @@ -102,6 +103,7 @@ enum { PROP_PRIMARY_CONNECTION, PROP_ACTIVATING_CONNECTION, PROP_DEVICES, + PROP_METERED, LAST_PROP }; @@ -179,6 +181,7 @@ init_dbus (NMObject *object) { NM_MANAGER_PRIMARY_CONNECTION, &priv->primary_connection, NULL, NM_TYPE_ACTIVE_CONNECTION }, { NM_MANAGER_ACTIVATING_CONNECTION, &priv->activating_connection, NULL, NM_TYPE_ACTIVE_CONNECTION }, { NM_MANAGER_DEVICES, &priv->devices, NULL, NM_TYPE_DEVICE, "device" }, + { NM_MANAGER_METERED, &priv->metered }, { NULL }, }; @@ -1537,6 +1540,9 @@ get_property (GObject *object, case PROP_DEVICES: g_value_take_boxed (value, _nm_utils_copy_object_array (nm_manager_get_devices (self))); break; + case PROP_METERED: + g_value_set_uint (value, priv->metered); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1669,6 +1675,19 @@ nm_manager_class_init (NMManagerClass *manager_class) G_TYPE_PTR_ARRAY, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMManager:metered: + * + * Whether the connectivity is metered. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_METERED, + g_param_spec_uint (NM_MANAGER_METERED, "", "", + 0, G_MAXUINT32, NM_METERED_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); /* signals */ diff --git a/libnm/nm-manager.h b/libnm/nm-manager.h index ca9f7dd60b..8d04a060f6 100644 --- a/libnm/nm-manager.h +++ b/libnm/nm-manager.h @@ -50,6 +50,7 @@ G_BEGIN_DECLS #define NM_MANAGER_PRIMARY_CONNECTION "primary-connection" #define NM_MANAGER_ACTIVATING_CONNECTION "activating-connection" #define NM_MANAGER_DEVICES "devices" +#define NM_MANAGER_METERED "metered" typedef struct { NMObject parent; diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index dec33d4086..63baa63270 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -75,6 +75,7 @@ _LOG_DECLARE_SELF (NMDevice); static void impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context); static void impl_device_delete (NMDevice *self, DBusGMethodInvocation *context); +static void nm_device_update_metered (NMDevice *self); #include "nm-device-glue.h" @@ -129,6 +130,7 @@ enum { PROP_MASTER, PROP_HW_ADDRESS, PROP_HAS_PENDING_ACTION, + PROP_METERED, LAST_PROP }; @@ -326,6 +328,8 @@ typedef struct { gboolean is_master; GSList * slaves; /* list of SlaveInfo */ + NMMetered metered; + NMConnectionProvider *con_provider; } NMDevicePrivate; @@ -673,6 +677,21 @@ nm_device_get_device_type (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->type; } +/** + * nm_device_get_metered: + * @setting: the #NMDevice + * + * Returns: the #NMDevice:metered property of the device. + * + * Since: 1.2 + **/ +NMMetered +nm_device_get_metered (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), NM_METERED_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE (self)->metered; +} /** * nm_device_get_priority(): @@ -3344,8 +3363,10 @@ dhcp4_state_changed (NMDhcpClient *client, if (priv->ip4_state == IP_CONF) nm_device_activate_schedule_ip4_config_result (self, ip4_config); - else if (priv->ip4_state == IP_DONE) + else if (priv->ip4_state == IP_DONE) { dhcp4_lease_change (self, ip4_config); + nm_device_update_metered (self); + } break; case NM_DHCP_STATE_TIMEOUT: dhcp4_fail (self, TRUE); @@ -7274,6 +7295,59 @@ nm_device_set_dhcp_anycast_address (NMDevice *self, const char *addr) priv->dhcp_anycast_address = g_strdup (addr); } +static void +nm_device_update_metered (NMDevice *self) +{ +#define NM_METERED_INVALID ((NMMetered) -1) + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMSettingConnection *setting; + NMMetered conn_value, value = NM_METERED_INVALID; + NMConnection *connection = NULL; + NMDeviceState state; + + g_return_if_fail (NM_IS_DEVICE (self)); + + state = nm_device_get_state (self); + if ( state <= NM_DEVICE_STATE_DISCONNECTED + || state > NM_DEVICE_STATE_ACTIVATED) + value = NM_METERED_UNKNOWN; + + if (value == NM_METERED_INVALID) { + connection = nm_device_get_connection (self); + if (connection) { + setting = nm_connection_get_setting_connection (connection); + if (setting) { + conn_value = nm_setting_connection_get_metered (setting); + if (conn_value != NM_METERED_UNKNOWN) + value = conn_value; + } + } + } + + /* Try to guess a value using the metered flag in IP configuration */ + if (value == NM_METERED_INVALID) { + if ( priv->ip4_config + && priv->ip4_state == IP_DONE + && nm_ip4_config_get_metered (priv->ip4_config)) + value = NM_METERED_GUESS_YES; + } + + /* Otherwise look at connection type */ + if (value == NM_METERED_INVALID) { + if ( nm_connection_is_type (connection, NM_SETTING_GSM_SETTING_NAME) + || nm_connection_is_type (connection, NM_SETTING_CDMA_SETTING_NAME)) + value = NM_METERED_GUESS_YES; + else + value = NM_METERED_GUESS_NO; + } + + if (value != priv->metered) { + _LOGD (LOGD_DEVICE, "set metered value %d", value); + priv->metered = value; + g_object_notify (G_OBJECT (self), NM_DEVICE_METERED); + } +} + /** * nm_device_check_connection_available(): * @self: the #NMDevice @@ -7731,6 +7805,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, gboolean deconfig nm_platform_address_flush (NM_PLATFORM_GET, ifindex); } + nm_device_update_metered (self); _cleanup_generic_post (self, deconfigure); } @@ -8203,6 +8278,7 @@ _set_state_full (NMDevice *self, break; case NM_DEVICE_STATE_ACTIVATED: _LOGI (LOGD_DEVICE, "Activation: successful, device activated."); + nm_device_update_metered (self); nm_dispatcher_call (DISPATCHER_ACTION_UP, nm_act_request_get_connection (req), self, NULL, NULL, NULL); break; case NM_DEVICE_STATE_FAILED: @@ -9092,6 +9168,9 @@ get_property (GObject *object, guint prop_id, case PROP_HAS_PENDING_ACTION: g_value_set_boolean (value, nm_device_has_pending_action (self)); break; + case PROP_METERED: + g_value_set_uint (value, priv->metered); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -9362,6 +9441,20 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMDevice:metered: + * + * Whether the connection is metered. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_METERED, + g_param_spec_uint (NM_DEVICE_METERED, "", "", + 0, G_MAXUINT32, NM_METERED_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* Signals */ signals[STATE_CHANGED] = g_signal_new ("state-changed", diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 424b096528..9cdab4c3fc 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -59,6 +59,7 @@ #define NM_DEVICE_PHYSICAL_PORT_ID "physical-port-id" #define NM_DEVICE_MTU "mtu" #define NM_DEVICE_HW_ADDRESS "hw-address" +#define NM_DEVICE_METERED "metered" #define NM_DEVICE_TYPE_DESC "type-desc" /* Internal only */ #define NM_DEVICE_RFKILL_TYPE "rfkill-type" /* Internal only */ @@ -75,7 +76,6 @@ #define NM_DEVICE_RECHECK_AUTO_ACTIVATE "recheck-auto-activate" #define NM_DEVICE_RECHECK_ASSUME "recheck-assume" - G_BEGIN_DECLS #define NM_TYPE_DEVICE (nm_device_get_type ()) @@ -282,6 +282,7 @@ const char * nm_device_get_driver_version (NMDevice *dev); const char * nm_device_get_type_desc (NMDevice *dev); const char * nm_device_get_type_description (NMDevice *dev); NMDeviceType nm_device_get_device_type (NMDevice *dev); +NMMetered nm_device_get_metered (NMDevice *dev); int nm_device_get_priority (NMDevice *dev); guint32 nm_device_get_ip4_route_metric (NMDevice *dev); diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c index de5bf2a9be..05de2b340b 100644 --- a/src/dhcp-manager/nm-dhcp-systemd.c +++ b/src/dhcp-manager/nm-dhcp-systemd.c @@ -226,6 +226,8 @@ lease_to_ip4_config (const char *iface, guint16 mtu; int r, num; guint64 end_time; + uint8_t *data; + gboolean metered = FALSE; g_return_val_if_fail (lease != NULL, NULL); @@ -357,6 +359,11 @@ lease_to_ip4_config (const char *iface, g_string_free (l, TRUE); } + num = sd_dhcp_lease_get_vendor_specific (lease, &data); + if (num > 0) + metered = !!memmem (data, num, "ANDROID_METERED", STRLEN ("ANDROID_METERED")); + nm_ip4_config_set_metered (ip4_config, metered); + return ip4_config; } diff --git a/src/dhcp-manager/nm-dhcp-utils.c b/src/dhcp-manager/nm-dhcp-utils.c index e50b4e3b8b..4dde1a4454 100644 --- a/src/dhcp-manager/nm-dhcp-utils.c +++ b/src/dhcp-manager/nm-dhcp-utils.c @@ -576,6 +576,9 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex, g_strfreev (nis); } + str = g_hash_table_lookup (options, "vendor_encapsulated_options"); + nm_ip4_config_set_metered (ip4_config, str && strstr (str, "ANDROID_METERED")); + return ip4_config; error: diff --git a/src/dhcp-manager/tests/test-dhcp-utils.c b/src/dhcp-manager/tests/test-dhcp-utils.c index 286c1d7476..3cdae732a7 100644 --- a/src/dhcp-manager/tests/test-dhcp-utils.c +++ b/src/dhcp-manager/tests/test-dhcp-utils.c @@ -176,6 +176,30 @@ test_wins_options (void) } static void +test_vendor_option_metered (void) +{ + GHashTable *options; + NMIP4Config *ip4_config; + static const Option data[] = { + { "vendor_encapsulated_options", "ANDROID_METERED" }, + { NULL, NULL } + }; + + options = fill_table (generic_options, NULL); + ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); + g_assert (ip4_config); + g_assert (nm_ip4_config_get_metered (ip4_config) == FALSE); + g_hash_table_destroy (options); + + options = fill_table (generic_options, NULL); + options = fill_table (data, options); + ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); + g_assert (ip4_config); + g_assert (nm_ip4_config_get_metered (ip4_config) == TRUE); + g_hash_table_destroy (options); +} + +static void ip4_test_route (NMIP4Config *ip4_config, guint route_num, const char *expected_dest, @@ -716,6 +740,7 @@ int main (int argc, char **argv) g_test_add_func ("/dhcp/ip4-missing-prefix-8", test_ip4_missing_prefix_8); g_test_add_func ("/dhcp/ip4-prefix-classless", test_ip4_prefix_classless); g_test_add_func ("/dhcp/client-id-from-string", test_client_id_from_string); + g_test_add_func ("/dhcp/vendor-option-metered", test_vendor_option_metered); return g_test_run (); } diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index d0b5b8753f..2b7296196d 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -100,6 +100,7 @@ enum { enum { DEVICE_CHANGED, + DEVICE_METERED_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -401,6 +402,18 @@ device_master_changed (GObject *object, } } +static void +device_metered_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMActiveConnection *self = (NMActiveConnection *) user_data; + NMDevice *device = NM_DEVICE (object); + + g_return_if_fail (NM_IS_ACTIVE_CONNECTION (self)); + g_signal_emit (self, signals[DEVICE_METERED_CHANGED], 0, nm_device_get_metered (device)); +} + gboolean nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device) { @@ -427,6 +440,8 @@ nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device) G_CALLBACK (device_state_changed), self); g_signal_connect (device, "notify::master", G_CALLBACK (device_master_changed), self); + g_signal_connect (device, "notify::" NM_DEVICE_METERED, + G_CALLBACK (device_metered_changed), self); if (!priv->assumed) { priv->pending_activation_id = g_strdup_printf ("activation::%p", (void *)self); @@ -837,6 +852,7 @@ _device_cleanup (NMActiveConnection *self) if (priv->device) { g_signal_handlers_disconnect_by_func (priv->device, G_CALLBACK (device_state_changed), self); g_signal_handlers_disconnect_by_func (priv->device, G_CALLBACK (device_master_changed), self); + g_signal_handlers_disconnect_by_func (priv->device, G_CALLBACK (device_metered_changed), self); } if (priv->pending_activation_id) { @@ -1042,6 +1058,14 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class) NULL, NULL, NULL, G_TYPE_NONE, 2, NM_TYPE_DEVICE, NM_TYPE_DEVICE); + signals[DEVICE_METERED_CHANGED] = + g_signal_new (NM_ACTIVE_CONNECTION_DEVICE_METERED_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMActiveConnectionClass, device_metered_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_UINT); + nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), G_TYPE_FROM_CLASS (ac_class), &dbus_glib_nm_active_connection_object_info); diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h index 710cfeed9f..4db5e6ff56 100644 --- a/src/nm-active-connection.h +++ b/src/nm-active-connection.h @@ -57,7 +57,8 @@ #define NM_ACTIVE_CONNECTION_INT_MASTER_READY "int-master-ready" /* Internal signals*/ -#define NM_ACTIVE_CONNECTION_DEVICE_CHANGED "device-changed" +#define NM_ACTIVE_CONNECTION_DEVICE_CHANGED "device-changed" +#define NM_ACTIVE_CONNECTION_DEVICE_METERED_CHANGED "device-metered-changed" struct _NMActiveConnection { GObject parent; @@ -78,6 +79,9 @@ typedef struct { void (*device_changed) (NMActiveConnection *connection, NMDevice *new_device, NMDevice *old_device); + + void (*device_metered_changed) (NMActiveConnection *connection, + NMMetered new_value); } NMActiveConnectionClass; GType nm_active_connection_get_type (void); diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 5426e23019..1e24b2ec67 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -59,6 +59,7 @@ typedef struct { NMIPConfigSource mtu_source; int ifindex; gint64 route_metric; + gboolean metered; } NMIP4ConfigPrivate; /* internal guint32 are assigned to gobject properties of type uint. Ensure, that uint is large enough */ @@ -607,6 +608,10 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src) for (i = 0; i < nm_ip4_config_get_num_wins (src); i++) nm_ip4_config_add_wins (dst, nm_ip4_config_get_wins (src, i)); + /* metered flag */ + nm_ip4_config_set_metered (dst, nm_ip4_config_get_metered (dst) || + nm_ip4_config_get_metered (src)); + g_object_thaw_notify (G_OBJECT (dst)); } @@ -1117,6 +1122,12 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev has_minor_changes = TRUE; } + /* metered */ + if (src_priv->metered != dst_priv->metered) { + dst_priv->metered = src_priv->metered; + has_minor_changes = TRUE; + } + /* config_equal does not compare *all* the fields, therefore, we might have has_minor_changes * regardless of config_equal. But config_equal must correspond to has_relevant_changes. */ g_assert (config_equal == !has_relevant_changes); @@ -1192,6 +1203,7 @@ nm_ip4_config_dump (const NMIP4Config *config, const char *detail) } g_message (" n-dflt: %d", nm_ip4_config_get_never_default (config)); + g_message (" mtrd: %d", (int) nm_ip4_config_get_metered (config)); } gboolean @@ -1906,6 +1918,24 @@ nm_ip4_config_get_mtu_source (const NMIP4Config *config) /******************************************************************/ +void +nm_ip4_config_set_metered (NMIP4Config *config, gboolean metered) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + + priv->metered = !!metered; +} + +gboolean +nm_ip4_config_get_metered (const NMIP4Config *config) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + + return priv->metered; +} + +/******************************************************************/ + static inline void hash_u32 (GChecksum *sum, guint32 n) { diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index 991725b5a1..69880c1322 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -161,6 +161,10 @@ void nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu, NMIPConfigSource s guint32 nm_ip4_config_get_mtu (const NMIP4Config *config); NMIPConfigSource nm_ip4_config_get_mtu_source (const NMIP4Config *config); +/* Metered */ +void nm_ip4_config_set_metered (NMIP4Config *config, gboolean metered); +gboolean nm_ip4_config_get_metered (const NMIP4Config *config); + void nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only); gboolean nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b); diff --git a/src/nm-manager.c b/src/nm-manager.c index 5e2aff8c65..bf9e69a368 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -157,6 +157,7 @@ typedef struct { guint ac_cleanup_id; NMActiveConnection *primary_connection; NMActiveConnection *activating_connection; + NMMetered metered; GSList *devices; NMState state; @@ -230,6 +231,7 @@ enum { PROP_PRIMARY_CONNECTION_TYPE, PROP_ACTIVATING_CONNECTION, PROP_DEVICES, + PROP_METERED, /* Not exported */ PROP_HOSTNAME, @@ -655,6 +657,30 @@ find_best_device_state (NMManager *manager) } static void +nm_manager_update_metered (NMManager *manager) +{ + NMManagerPrivate *priv; + NMDevice *device; + NMMetered value = NM_METERED_UNKNOWN; + + g_return_if_fail (NM_IS_MANAGER (manager)); + priv = NM_MANAGER_GET_PRIVATE (manager); + + if (priv->primary_connection) { + device = nm_active_connection_get_device (priv->primary_connection); + if (device) + value = nm_device_get_metered (device); + } + + if (value != priv->metered) { + priv->metered = value; + nm_log_dbg (LOGD_CORE, "New manager metered value: %d", + (int) priv->metered); + g_object_notify (G_OBJECT (manager), NM_MANAGER_METERED); + } +} + +static void nm_manager_update_state (NMManager *manager) { NMManagerPrivate *priv; @@ -4056,6 +4082,14 @@ firmware_dir_changed (GFileMonitor *monitor, } static void +connection_metered_changed (GObject *object, + NMMetered metered, + gpointer user_data) +{ + nm_manager_update_metered (NM_MANAGER (user_data)); +} + +static void policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data) { NMManager *self = NM_MANAGER (user_data); @@ -4077,11 +4111,23 @@ policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user ac = NULL; if (ac != priv->primary_connection) { - g_clear_object (&priv->primary_connection); + if (priv->primary_connection) { + g_signal_handlers_disconnect_by_func (priv->primary_connection, + G_CALLBACK (connection_metered_changed), + self); + g_clear_object (&priv->primary_connection); + } + priv->primary_connection = ac ? g_object_ref (ac) : NULL; + + if (priv->primary_connection) { + g_signal_connect (priv->primary_connection, NM_ACTIVE_CONNECTION_DEVICE_METERED_CHANGED, + G_CALLBACK (connection_metered_changed), self); + } nm_log_dbg (LOGD_CORE, "PrimaryConnection now %s", ac ? nm_active_connection_get_id (ac) : "(none)"); g_object_notify (G_OBJECT (self), NM_MANAGER_PRIMARY_CONNECTION); g_object_notify (G_OBJECT (self), NM_MANAGER_PRIMARY_CONNECTION_TYPE); + nm_manager_update_metered (self); } } @@ -4649,6 +4695,8 @@ nm_manager_init (NMManager *manager) /* Update timestamps in active connections */ priv->timestamp_update_id = g_timeout_add_seconds (300, (GSourceFunc) periodic_update_active_connection_timestamps, manager); + + priv->metered = NM_METERED_UNKNOWN; } static void @@ -4733,6 +4781,9 @@ get_property (GObject *object, guint prop_id, } g_value_take_boxed (value, array); break; + case PROP_METERED: + g_value_set_uint (value, priv->metered); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -5011,6 +5062,20 @@ nm_manager_class_init (NMManagerClass *manager_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMManager:metered: + * + * Whether the connectivity is metered. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_METERED, + g_param_spec_uint (NM_MANAGER_METERED, "", "", + 0, G_MAXUINT32, NM_METERED_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* signals */ signals[DEVICE_ADDED] = g_signal_new ("device-added", diff --git a/src/nm-manager.h b/src/nm-manager.h index 3b00e8053a..a8e7d7ad32 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -51,6 +51,7 @@ #define NM_MANAGER_PRIMARY_CONNECTION_TYPE "primary-connection-type" #define NM_MANAGER_ACTIVATING_CONNECTION "activating-connection" #define NM_MANAGER_DEVICES "devices" +#define NM_MANAGER_METERED "metered" /* Not exported */ #define NM_MANAGER_HOSTNAME "hostname" diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index 71bb2b942d..2387be3fcd 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -248,6 +248,15 @@ make_connection_setting (const char *file, g_free (value); } + switch (svTrueValue (ifcfg, "CONNECTION_METERED", -1)) { + case TRUE: + g_object_set (s_con, NM_SETTING_CONNECTION_METERED, NM_METERED_YES, NULL); + break; + case FALSE: + g_object_set (s_con, NM_SETTING_CONNECTION_METERED, NM_METERED_NO, NULL); + break; + } + return NM_SETTING (s_con); } diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index 4a5ca1d186..283aa82611 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -303,11 +303,11 @@ svGetValueFull (shvarFile *s, const char *key, gboolean verbatim) * return FALSE if <key> resolves to any non-truth value (e.g. "no", "n", "false") * return <default> otherwise */ -gboolean -svTrueValue (shvarFile *s, const char *key, gboolean def) +gint +svTrueValue (shvarFile *s, const char *key, gint def) { char *tmp; - gboolean returnValue = def; + gint returnValue = def; tmp = svGetValue (s, key, FALSE); if (!tmp) diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index 4902541b00..de7a358556 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -62,7 +62,7 @@ char *svGetValueFull (shvarFile *s, const char *key, gboolean verbatim); * return FALSE if <key> resolves to any non-truth value (e.g. "no", "n", "false") * return <def> otherwise */ -gboolean svTrueValue (shvarFile *s, const char *key, gboolean def); +gint svTrueValue (shvarFile *s, const char *key, gint def); gint64 svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback); diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index e432d1b95e..df2308e405 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -1777,6 +1777,17 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) svSetValue (ifcfg, "GATEWAY_PING_TIMEOUT", tmp, FALSE); g_free (tmp); } + + switch (nm_setting_connection_get_metered (s_con)) { + case NM_METERED_YES: + svSetValue (ifcfg, "CONNECTION_METERED", "yes", FALSE); + break; + case NM_METERED_NO: + svSetValue (ifcfg, "CONNECTION_METERED", "no", FALSE); + break; + default: + svSetValue (ifcfg, "CONNECTION_METERED", NULL, FALSE); + } } static gboolean diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h index 2d7bf03bab..91843beae5 100644 --- a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h @@ -74,6 +74,8 @@ struct sd_dhcp_lease { char *root_path; uint8_t *client_id; size_t client_id_len; + uint8_t *vendor_specific; + size_t vendor_specific_size; }; int dhcp_lease_new(sd_dhcp_lease **ret); diff --git a/src/systemd/src/libsystemd-network/dhcp-protocol.h b/src/systemd/src/libsystemd-network/dhcp-protocol.h index da483feadf..3a1e473afa 100644 --- a/src/systemd/src/libsystemd-network/dhcp-protocol.h +++ b/src/systemd/src/libsystemd-network/dhcp-protocol.h @@ -127,6 +127,7 @@ enum { DHCP_OPTION_BROADCAST = 28, DHCP_OPTION_STATIC_ROUTE = 33, DHCP_OPTION_NTP_SERVER = 42, + DHCP_OPTION_VENDOR_SPECIFIC = 43, DHCP_OPTION_REQUESTED_IP_ADDRESS = 50, DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51, DHCP_OPTION_OVERLOAD = 52, diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index ee6d72dffc..ae44415df2 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -180,6 +180,19 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes return 0; } +int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, uint8_t **data) { + assert_return(lease, -EINVAL); + assert_return(data, -EINVAL); + + if (lease->vendor_specific) { + *data = lease->vendor_specific; + return lease->vendor_specific_size; + } else + return -ENOENT; + + return 0; +} + sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) { if (lease) assert_se(REFCNT_INC(lease->n_ref) >= 2); @@ -275,6 +288,24 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) { return 0; } +static int lease_parse_binary(const uint8_t *option, size_t len, uint8_t **ret) { + assert (option); + assert (ret); + + if (len >= 1) { + uint8_t *data; + + data = memdup(option, len); + if (!data) + return -errno; + + free(*ret); + *ret = data; + } + + return 0; +} + static int lease_parse_in_addrs_aux(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size, size_t mult) { assert(option); assert(ret); @@ -571,6 +602,14 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option, return r; break; + + case DHCP_OPTION_VENDOR_SPECIFIC: + r = lease_parse_binary(option, len, &lease->vendor_specific); + if (r < 0) + return r; + lease->vendor_specific_size = len; + + break; } return 0; @@ -598,6 +637,7 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { const uint8_t *client_id; size_t client_id_len; const char *string; + uint8_t *data; uint16_t mtu; struct sd_dhcp_route *routes; int r; @@ -670,6 +710,18 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { if (r >= 0) serialize_dhcp_routes(f, "ROUTES", routes, r); + r = sd_dhcp_lease_get_vendor_specific(lease, &data); + if (r >= 0) { + _cleanup_free_ char *option_hex = NULL; + + option_hex = hexmem(data, r); + if (!option_hex) { + r = -ENOMEM; + goto finish; + } + fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex); + } + r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len); if (r >= 0) { _cleanup_free_ char *client_id_hex = NULL; diff --git a/src/systemd/src/systemd/sd-dhcp-lease.h b/src/systemd/src/systemd/sd-dhcp-lease.h index 80d32134b1..28ee120bcb 100644 --- a/src/systemd/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/src/systemd/sd-dhcp-lease.h @@ -47,6 +47,7 @@ int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname); int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname); int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path); int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routesgn); +int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, uint8_t **data); int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id, size_t *client_id_len); |