summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2015-06-09 22:15:26 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2015-06-09 22:15:26 +0200
commit7d09debdf0b1bd6d76a645d5195e89e3ad16892f (patch)
treef3b03af96f8bf10d798dbe6c82daa113af476700
parentacef8c6a1baf5fc5d03accfe4e1eb941570028a2 (diff)
parent04d5804dd5826cc36398027a023b0e639a54bd26 (diff)
downloadNetworkManager-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
-rw-r--r--clients/cli/common.c17
-rw-r--r--clients/cli/common.h1
-rw-r--r--clients/cli/devices.c4
-rw-r--r--clients/cli/settings.c57
-rw-r--r--clients/cli/utils.c38
-rw-r--r--clients/cli/utils.h7
-rw-r--r--introspection/nm-device.xml34
-rw-r--r--introspection/nm-manager.xml8
-rw-r--r--libnm-core/nm-dbus-interface.h20
-rw-r--r--libnm-core/nm-setting-connection.c60
-rw-r--r--libnm-core/nm-setting-connection.h3
-rw-r--r--libnm-core/tests/test-general.c1
-rw-r--r--libnm/libnm.ver3
-rw-r--r--libnm/nm-client.c16
-rw-r--r--libnm/nm-client.h1
-rw-r--r--libnm/nm-device.c38
-rw-r--r--libnm/nm-device.h3
-rw-r--r--libnm/nm-manager.c19
-rw-r--r--libnm/nm-manager.h1
-rw-r--r--src/devices/nm-device.c95
-rw-r--r--src/devices/nm-device.h3
-rw-r--r--src/dhcp-manager/nm-dhcp-systemd.c7
-rw-r--r--src/dhcp-manager/nm-dhcp-utils.c3
-rw-r--r--src/dhcp-manager/tests/test-dhcp-utils.c25
-rw-r--r--src/nm-active-connection.c24
-rw-r--r--src/nm-active-connection.h6
-rw-r--r--src/nm-ip4-config.c30
-rw-r--r--src/nm-ip4-config.h4
-rw-r--r--src/nm-manager.c67
-rw-r--r--src/nm-manager.h1
-rw-r--r--src/settings/plugins/ifcfg-rh/reader.c9
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c6
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.h2
-rw-r--r--src/settings/plugins/ifcfg-rh/writer.c11
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-lease-internal.h2
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-protocol.h1
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-lease.c52
-rw-r--r--src/systemd/src/systemd/sd-dhcp-lease.h1
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);