Commit 28616812 authored by Jiří Klimeš's avatar Jiří Klimeš

editor: let users edit connection.interface-name property (rh #1139536)

"Device MAC address" has been changed to "Device" that now accepts both
interface name and MAC. Either of the two data can be set, or both:
"em1 (3C:97:0E:18:4C:C7)", "3C:97:0E:18:4C:C7 (em1)", "em1", "3C:97:0E:18:4C:C7".
This lets user also lock connection to an interface name for Ethernet, Wi-Fi,
WiMAX, PPPoE, InfiniBand and mobile broadband connections. It is more useful
than MAC address now that interface names are stable.

For virtual connections, interface name entry is on the corresponding
page (like bond, bridge, etc.) because it is mandatory.

https://bugzilla.redhat.com/show_bug.cgi?id=1139536
parent 99fb78ee
......@@ -163,11 +163,11 @@
</packing>
</child>
<child>
<object class="GtkLabel" id="ethernet_device_mac_label">
<object class="GtkLabel" id="ethernet_device_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Device MAC address:</property>
<property name="label" translatable="yes">_Device:</property>
<property name="use_underline">True</property>
</object>
<packing>
......@@ -178,7 +178,7 @@
</packing>
</child>
<child>
<object class="GtkVBox" id="ethernet_device_mac_vbox">
<object class="GtkVBox" id="ethernet_device_vbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
......
......@@ -28,11 +28,11 @@
</packing>
</child>
<child>
<object class="GtkLabel" id="infiniband_device_mac_label">
<object class="GtkLabel" id="infiniband_device_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Device MAC address:</property>
<property name="label" translatable="yes">_Device:</property>
<property name="use_underline">True</property>
</object>
<packing>
......@@ -43,7 +43,7 @@
</packing>
</child>
<child>
<object class="GtkVBox" id="infiniband_device_mac_vbox">
<object class="GtkVBox" id="infiniband_device_vbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
......
......@@ -118,7 +118,7 @@
</packing>
</child>
<child>
<object class="GtkVBox" id="wifi_device_mac_vbox">
<object class="GtkVBox" id="wifi_device_vbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
......@@ -134,11 +134,11 @@
</packing>
</child>
<child>
<object class="GtkLabel" id="wifi_device_mac_label">
<object class="GtkLabel" id="wifi_device_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Device MAC address:</property>
<property name="label" translatable="yes">_Device:</property>
<property name="use_underline">True</property>
</object>
<packing>
......
......@@ -10,7 +10,7 @@
<property name="column_spacing">12</property>
<property name="row_spacing">6</property>
<child>
<object class="GtkVBox" id="wimax_device_mac_vbox">
<object class="GtkVBox" id="wimax_device_vbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
......@@ -26,11 +26,11 @@
</packing>
</child>
<child>
<object class="GtkLabel" id="wimax_device_mac_label">
<object class="GtkLabel" id="wimax_device_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Device MAC address:</property>
<property name="label" translatable="yes">_Device:</property>
<property name="use_underline">True</property>
</object>
<packing>
......
......@@ -146,105 +146,280 @@ ce_page_last_update (CEPage *self, NMConnection *connection, GError **error)
return TRUE;
}
char **
ce_page_get_mac_list (CEPage *self, GType device_type, const char *mac_property)
static void
_set_active_combo_item (GtkComboBox *combo, const char *item,
const char *combo_item, int combo_idx)
{
GtkWidget *entry;
if (item) {
/* set active item */
gtk_combo_box_set_active (combo, combo_idx);
if (!combo_item)
gtk_combo_box_text_prepend_text (GTK_COMBO_BOX_TEXT (combo), item);
entry = gtk_bin_get_child (GTK_BIN (combo));
if (entry)
gtk_entry_set_text (GTK_ENTRY (entry), combo_item ? combo_item : item);
}
}
/* Combo box storing data in the form of "text1 (text2)" */
void
ce_page_setup_data_combo (CEPage *self, GtkComboBox *combo,
const char *data, char **list)
{
char **iter, *active_item = NULL;
int i, active_idx = -1;
int data_len;
if (data)
data_len = strlen (data);
else
data_len = -1;
for (iter = list, i = 0; iter && *iter; iter++, i++) {
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), *iter);
if ( data
&& g_ascii_strncasecmp (*iter, data, data_len) == 0
&& ((*iter)[data_len] == '\0' || (*iter)[data_len] == ' ')) {
active_item = *iter;
active_idx = i;
}
}
_set_active_combo_item (combo, data, active_item, active_idx);
}
/* Combo box storing MAC addresses only */
void
ce_page_setup_mac_combo (CEPage *self, GtkComboBox *combo,
const char *mac, char **mac_list)
{
char **iter, *active_mac = NULL;
int i, active_idx = -1;
for (iter = mac_list, i = 0; iter && *iter; iter++, i++) {
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), *iter);
if (mac && *iter && nm_utils_hwaddr_matches (mac, -1, *iter, -1)) {
active_mac = *iter;
active_idx = i;
}
}
_set_active_combo_item (combo, mac, active_mac, active_idx);
}
gboolean
ce_page_mac_entry_valid (GtkEntry *entry, int type)
{
const char *mac;
g_return_val_if_fail (entry != NULL, FALSE);
g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
mac = gtk_entry_get_text (entry);
if (!mac || !*mac)
return TRUE;
return nm_utils_hwaddr_valid (mac, nm_utils_hwaddr_len (type));
}
static char **
_get_device_list (CEPage *self,
GType device_type,
gboolean set_ifname,
const char *mac_property,
gboolean ifname_first)
{
const GPtrArray *devices;
GPtrArray *macs;
GPtrArray *interfaces;
int i;
g_return_val_if_fail (CE_IS_PAGE (self), NULL);
g_return_val_if_fail (set_ifname || mac_property, NULL);
if (!self->client)
return NULL;
macs = g_ptr_array_new ();
interfaces = g_ptr_array_new ();
devices = nm_client_get_devices (self->client);
for (i = 0; i < devices->len; i++) {
NMDevice *dev = g_ptr_array_index (devices, i);
const char *iface;
char *mac, *item;
const char *ifname;
char *mac;
char *item;
if (!G_TYPE_CHECK_INSTANCE_TYPE (dev, device_type))
continue;
g_object_get (G_OBJECT (dev), mac_property, &mac, NULL);
iface = nm_device_get_iface (NM_DEVICE (dev));
item = g_strdup_printf ("%s (%s)", mac, iface);
g_free (mac);
g_ptr_array_add (macs, item);
ifname = nm_device_get_iface (NM_DEVICE (dev));
if (mac_property)
g_object_get (G_OBJECT (dev), mac_property, &mac, NULL);
if (set_ifname && mac_property) {
if (ifname_first)
item = g_strdup_printf ("%s (%s)", ifname, mac);
else
item = g_strdup_printf ("%s (%s)", mac, ifname);
} else
item = g_strdup (set_ifname ? ifname : mac);
g_ptr_array_add (interfaces, item);
if (mac_property)
g_free (mac);
}
g_ptr_array_add (interfaces, NULL);
return (char **)g_ptr_array_free (interfaces, FALSE);
}
static gboolean
_device_entry_parse (const char *entry_text, char **first, char **second)
{
const char *sp, *left, *right;
if (!entry_text || !*entry_text) {
*first = NULL;
*second = NULL;
return TRUE;
}
g_ptr_array_add (macs, NULL);
return (char **)g_ptr_array_free (macs, FALSE);
sp = strchr (entry_text, ' ');
if (sp) {
*first = g_strndup (entry_text, sp - entry_text);
left = sp + 1;
right = strchr (left, ')');
if (*left == '(' && right && right > left)
*second = g_strndup (left + 1, right - left - 1);
else {
*second = NULL;
return FALSE;
}
} else {
*first = g_strdup (entry_text);
*second = NULL;
}
return TRUE;
}
static gboolean
_device_entries_match (const char *ifname, const char *mac, const char *entry)
{
char *first, *second;
gboolean ifname_match = FALSE, mac_match = FALSE;
gboolean both;
if (!ifname && !mac)
return FALSE;
_device_entry_parse (entry, &first, &second);
both = first && second;
if ( ifname
&& ( !g_strcmp0 (ifname, first)
|| !g_strcmp0 (ifname, second)))
ifname_match = TRUE;
if ( mac
&& ( (first && nm_utils_hwaddr_matches (mac, -1, first, -1))
|| (second && nm_utils_hwaddr_matches (mac, -1, second, -1))))
mac_match = TRUE;
g_free (first);
g_free (second);
if (both)
return ifname_match && mac_match;
else {
if (ifname)
return ifname_match;
if (mac)
return mac_match;
return FALSE;
}
}
/* Combo box storing ifname and/or MAC */
void
ce_page_setup_mac_combo (CEPage *self, GtkComboBox *combo,
const char *current_mac, char **mac_list)
ce_page_setup_device_combo (CEPage *self,
GtkComboBox *combo,
GType device_type,
const char *ifname,
const char *mac,
const char *mac_property,
gboolean ifname_first)
{
char **iter, *active_mac = NULL;
char **iter, *active_item = NULL;
int i, active_idx = -1;
int current_mac_len;
GtkWidget *entry;
char **device_list;
char *item;
device_list = _get_device_list (self, device_type, TRUE, mac_property, ifname_first);
if (current_mac)
current_mac_len = strlen (current_mac);
if (ifname && mac)
item = g_strdup_printf ("%s (%s)", ifname, mac);
else if (!ifname && !mac)
item = NULL;
else
current_mac_len = -1;
item = g_strdup (ifname ? ifname : mac);
for (iter = mac_list, i = 0; iter && *iter; iter++, i++) {
for (iter = device_list, i = 0; iter && *iter; iter++, i++) {
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), *iter);
if ( current_mac
&& g_ascii_strncasecmp (*iter, current_mac, current_mac_len) == 0
&& ((*iter)[current_mac_len] == '\0' || (*iter)[current_mac_len] == ' ')) {
active_mac = *iter;
if (_device_entries_match (ifname, mac, *iter)) {
active_item = *iter;
active_idx = i;
}
}
_set_active_combo_item (combo, item, active_item, active_idx);
if (current_mac) {
/* set active item */
gtk_combo_box_set_active (combo, active_idx);
if (!active_mac)
gtk_combo_box_text_prepend_text (GTK_COMBO_BOX_TEXT (combo), current_mac);
entry = gtk_bin_get_child (GTK_BIN (combo));
if (entry)
gtk_entry_set_text (GTK_ENTRY (entry), active_mac ? active_mac : current_mac);
}
g_free (item);
g_strfreev (device_list);
}
char *
ce_page_entry_to_mac (GtkEntry *entry, int type, gboolean *invalid)
gboolean
ce_page_device_entry_get (GtkEntry *entry, int type, char **ifname, char **mac)
{
const char *sp, *temp;
char *mac;
char *first, *second;
const char *ifname_tmp = NULL, *mac_tmp = NULL;
gboolean valid = TRUE;
g_return_val_if_fail (entry != NULL, NULL);
g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
g_return_val_if_fail (entry != NULL, FALSE);
g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
if (invalid)
g_return_val_if_fail (*invalid == FALSE, NULL);
valid = _device_entry_parse (gtk_entry_get_text (entry), &first, &second);
temp = gtk_entry_get_text (entry);
if (!temp || !*temp)
return NULL;
if (first) {
if (nm_utils_hwaddr_valid (first, nm_utils_hwaddr_len (type)))
mac_tmp = first;
else if (nm_utils_iface_valid_name (first))
ifname_tmp = first;
else
valid = FALSE;
}
if (second) {
if (nm_utils_hwaddr_valid (second, nm_utils_hwaddr_len (type))) {
if (!mac_tmp)
mac_tmp = second;
else
valid = FALSE;
} else if (nm_utils_iface_valid_name (second)) {
if (!ifname_tmp)
ifname_tmp = second;
else
valid = FALSE;
} else
valid = FALSE;
}
sp = strchr (temp, ' ');
if (sp)
mac = g_strndup (temp, sp - temp);
else
mac = g_strdup (temp);
if (ifname)
*ifname = g_strdup (ifname_tmp);
if (mac)
*mac = g_strdup (mac_tmp);
if (!nm_utils_hwaddr_valid (mac, nm_utils_hwaddr_len (type))) {
g_free (mac);
if (invalid)
*invalid = TRUE;
return NULL;
}
return mac;
g_free (first);
g_free (second);
return valid;
}
char *
......
......@@ -103,14 +103,23 @@ const char * ce_page_get_title (CEPage *self);
gboolean ce_page_validate (CEPage *self, NMConnection *connection, GError **error);
gboolean ce_page_last_update (CEPage *self, NMConnection *connection, GError **error);
char **ce_page_get_mac_list (CEPage *self, GType device_type, const char *mac_property);
void ce_page_setup_mac_combo (CEPage *self, GtkComboBox *combo,
const char *current_mac, char **mac_list);
const char *mac, char **mac_list);
void ce_page_setup_data_combo (CEPage *self, GtkComboBox *combo,
const char *data, char **list);
void ce_page_setup_device_combo (CEPage *self,
GtkComboBox *combo,
GType device_type,
const char *ifname,
const char *mac,
const char *mac_property,
gboolean ifname_first);
gboolean ce_page_mac_entry_valid (GtkEntry *entry, int type);
gboolean ce_page_device_entry_get (GtkEntry *entry, int type,
char **ifname, char **mac);
void ce_page_changed (CEPage *self);
char *ce_page_entry_to_mac (GtkEntry *entry, int type, gboolean *invalid);
gboolean ce_spin_output_with_automatic (GtkSpinButton *spin, gpointer user_data);
gboolean ce_spin_output_with_default (GtkSpinButton *spin, gpointer user_data);
......
......@@ -131,13 +131,12 @@ static void
ui_to_setting (CEPageBluetooth *self)
{
CEPageBluetoothPrivate *priv = CE_PAGE_BLUETOOTH_GET_PRIVATE (self);
char *bdaddr;
const char *bdaddr;
bdaddr = ce_page_entry_to_mac (priv->bdaddr, ARPHRD_ETHER, NULL);
bdaddr = gtk_entry_get_text (priv->bdaddr);
g_object_set (priv->setting,
NM_SETTING_BLUETOOTH_BDADDR, bdaddr,
NM_SETTING_BLUETOOTH_BDADDR, bdaddr && *bdaddr ? bdaddr : NULL,
NULL);
g_free (bdaddr);
}
static gboolean
......@@ -145,13 +144,9 @@ validate (CEPage *page, NMConnection *connection, GError **error)
{
CEPageBluetooth *self = CE_PAGE_BLUETOOTH (page);
CEPageBluetoothPrivate *priv = CE_PAGE_BLUETOOTH_GET_PRIVATE (self);
char *bdaddr;
gboolean invalid;
bdaddr = ce_page_entry_to_mac (priv->bdaddr, ARPHRD_ETHER, &invalid);
if (invalid)
if (!ce_page_mac_entry_valid (priv->bdaddr, ARPHRD_ETHER))
return FALSE;
g_free (bdaddr);
ui_to_setting (self);
return nm_setting_verify (NM_SETTING (priv->setting), NULL, error);
......
......@@ -37,8 +37,8 @@ G_DEFINE_TYPE (CEPageEthernet, ce_page_ethernet, CE_TYPE_PAGE)
typedef struct {
NMSettingWired *setting;
GtkComboBoxText *device_mac; /* Permanent MAC of the device */
GtkEntry *cloned_mac; /* Cloned MAC - used for MAC spoofing */
GtkComboBoxText *device_combo; /* Device identification (ifname and/or MAC) */
GtkEntry *cloned_mac; /* Cloned MAC - used for MAC spoofing */
GtkComboBox *port;
GtkComboBox *speed;
GtkToggleButton *duplex;
......@@ -68,19 +68,21 @@ ethernet_private_init (CEPageEthernet *self)
builder = CE_PAGE (self)->builder;
priv->device_mac = GTK_COMBO_BOX_TEXT (gtk_combo_box_text_new_with_entry ());
gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (priv->device_mac), 0);
gtk_widget_set_tooltip_text (GTK_WIDGET (priv->device_mac),
_("This option locks this connection to the network device specified by its permanent MAC address entered here. Example: 00:11:22:33:44:55"));
priv->device_combo = GTK_COMBO_BOX_TEXT (gtk_combo_box_text_new_with_entry ());
gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (priv->device_combo), 0);
gtk_widget_set_tooltip_text (GTK_WIDGET (priv->device_combo),
_("This option locks this connection to the network device specified "
"either by its interface name or permanent MAC or both. Examples: "
"\"em1\", \"3C:97:0E:42:1A:19\", \"em1 (3C:97:0E:42:1A:19)\""));
vbox = GTK_WIDGET (gtk_builder_get_object (builder, "ethernet_device_mac_vbox"));
gtk_container_add (GTK_CONTAINER (vbox), GTK_WIDGET (priv->device_mac));
gtk_widget_set_halign (GTK_WIDGET (priv->device_mac), GTK_ALIGN_FILL);
gtk_widget_show_all (GTK_WIDGET (priv->device_mac));
vbox = GTK_WIDGET (gtk_builder_get_object (builder, "ethernet_device_vbox"));
gtk_container_add (GTK_CONTAINER (vbox), GTK_WIDGET (priv->device_combo));
gtk_widget_set_halign (GTK_WIDGET (priv->device_combo), GTK_ALIGN_FILL);
gtk_widget_show_all (GTK_WIDGET (priv->device_combo));
/* Set mnemonic widget for device MAC label */
label = GTK_LABEL (gtk_builder_get_object (builder, "ethernet_device_mac_label"));
gtk_label_set_mnemonic_widget (label, GTK_WIDGET (priv->device_mac));
/* Set mnemonic widget for Device label */
label = GTK_LABEL (gtk_builder_get_object (builder, "ethernet_device_label"));
gtk_label_set_mnemonic_widget (label, GTK_WIDGET (priv->device_combo));
priv->cloned_mac = GTK_ENTRY (gtk_builder_get_object (builder, "ethernet_cloned_mac"));
priv->port = GTK_COMBO_BOX (gtk_builder_get_object (builder, "ethernet_port"));
......@@ -106,8 +108,7 @@ populate_ui (CEPageEthernet *self)
int port_idx = PORT_DEFAULT;
int speed_idx;
int mtu_def;
char **mac_list;
const char *s_mac_str;
const char *s_mac, *s_ifname;
/* Port */
port = nm_setting_wired_get_port (setting);
......@@ -154,19 +155,18 @@ populate_ui (CEPageEthernet *self)
gtk_toggle_button_set_active (priv->autonegotiate,
nm_setting_wired_get_auto_negotiate (setting));
/* Device MAC address */
mac_list = ce_page_get_mac_list (CE_PAGE (self), NM_TYPE_DEVICE_ETHERNET,
NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS);
s_mac_str = nm_setting_wired_get_mac_address (setting);
ce_page_setup_mac_combo (CE_PAGE (self), GTK_COMBO_BOX (priv->device_mac),
s_mac_str, mac_list);
g_strfreev (mac_list);
g_signal_connect (priv->device_mac, "changed", G_CALLBACK (stuff_changed), self);
/* Device ifname/MAC */
s_ifname = nm_connection_get_interface_name (CE_PAGE (self)->connection);
s_mac = nm_setting_wired_get_mac_address (setting);
ce_page_setup_device_combo (CE_PAGE (self), GTK_COMBO_BOX (priv->device_combo),
NM_TYPE_DEVICE_ETHERNET, s_ifname,
s_mac, NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS, TRUE);
g_signal_connect (priv->device_combo, "changed", G_CALLBACK (stuff_changed), self);
/* Cloned MAC address */
s_mac_str = nm_setting_wired_get_cloned_mac_address (setting);
if (s_mac_str)
gtk_entry_set_text (priv->cloned_mac, s_mac_str);
s_mac = nm_setting_wired_get_cloned_mac_address (setting);
if (s_mac)
gtk_entry_set_text (priv->cloned_mac, s_mac);
g_signal_connect (priv->cloned_mac, "changed", G_CALLBACK (stuff_changed), self);
/* MTU */
......@@ -253,12 +253,17 @@ static void
ui_to_setting (CEPageEthernet *self)
{
CEPageEthernetPrivate *priv = CE_PAGE_ETHERNET_GET_PRIVATE (self);
NMSettingConnection *s_con;
const char *port;
guint32 speed;
char *ifname = NULL;
char *device_mac = NULL;
char *cloned_mac = NULL;
const char *cloned_mac;
GtkWidget *entry;
s_con = nm_connection_get_setting_connection (CE_PAGE (self)->connection);
g_return_if_fail (s_con != NULL);
/* Port */
switch (gtk_combo_box_get_active (priv->port)) {
case PORT_TP:
......@@ -297,14 +302,17 @@ ui_to_setting (CEPageEthernet *self)
break;
}