/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2013 Red Hat, Inc. */ /** * SECTION:nmt-page-bond * @short_description: The editor page for Bond connections * * Note that this is fairly different from most of the other editor * pages, because #NMSettingBond doesn't have properties, so we * can't just use #GBinding. */ #include "libnm/nm-default-client.h" #include "nmt-page-bond.h" #include #include #include "nm-libnm-core-intern/nm-libnm-core-utils.h" #include "nmt-mac-entry.h" #include "nmt-address-list.h" #include "nmt-slave-list.h" G_DEFINE_TYPE(NmtPageBond, nmt_page_bond, NMT_TYPE_EDITOR_PAGE_DEVICE) #define NMT_PAGE_BOND_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_PAGE_BOND, NmtPageBondPrivate)) typedef enum { NMT_PAGE_BOND_MONITORING_UNKNOWN = -1, NMT_PAGE_BOND_MONITORING_MII = 0, NMT_PAGE_BOND_MONITORING_ARP = 1, } NmtPageBondMonitoringMode; typedef struct { NmtSlaveList *slaves; /* Note: when adding new options to the UI also ensure they are * initialized in bond_connection_setup_func() */ NmtNewtPopup * mode; NmtNewtEntry * primary; NmtNewtPopup * monitoring; NmtNewtEntry * miimon; NmtNewtEntry * updelay; NmtNewtEntry * downdelay; NmtNewtEntry * arp_interval; NmtAddressList *arp_ip_target; NmtPageBondMonitoringMode monitoring_mode; NMSettingBond *s_bond; GType slave_type; gboolean updating; } NmtPageBondPrivate; /*****************************************************************************/ static void arp_ip_target_widget_changed(GObject *object, GParamSpec *pspec, gpointer user_data); /*****************************************************************************/ NmtEditorPage * nmt_page_bond_new(NMConnection *conn, NmtDeviceEntry *deventry) { return g_object_new(NMT_TYPE_PAGE_BOND, "connection", conn, "device-entry", deventry, NULL); } static void nmt_page_bond_init(NmtPageBond *bond) { NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_UNKNOWN; priv->slave_type = G_TYPE_NONE; } static NmtNewtPopupEntry bond_mode[] = { {N_("Round-robin"), "balance-rr"}, {N_("Active Backup"), "active-backup"}, {N_("XOR"), "balance-xor"}, {N_("Broadcast"), "broadcast"}, {N_("802.3ad"), "802.3ad"}, {N_("Adaptive Transmit Load Balancing (tlb)"), "balance-tlb"}, {N_("Adaptive Load Balancing (alb)"), "balance-alb"}, {NULL, NULL}}; /* NB: the ordering/numbering here corresponds to NmtPageBondMonitoringMode */ static NmtNewtPopupEntry bond_monitoring[] = {{N_("MII (recommended)"), "mii"}, {N_("ARP"), "arp"}, {NULL, NULL}}; static void bond_options_changed(GObject *object, GParamSpec *pspec, gpointer user_data) { NMSettingBond * s_bond = NM_SETTING_BOND(object); NmtPageBond * bond = NMT_PAGE_BOND(user_data); NmtPageBondPrivate * priv = NMT_PAGE_BOND_GET_PRIVATE(bond); gs_free const char **ips = NULL; const char * val; gboolean visible_mii; NMBondMode mode; if (priv->updating) return; priv->updating = TRUE; val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_MODE); nmt_newt_popup_set_active_id(priv->mode, val); mode = _nm_setting_bond_mode_from_string(val ?: ""); val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_PRIMARY); nmt_newt_entry_set_text(priv->primary, val); nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->primary), mode == NM_BOND_MODE_ACTIVEBACKUP); if (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_UNKNOWN) { val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL); if (_nm_utils_ascii_str_to_int64(val, 10, 0, G_MAXINT, 0) > 0) priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_ARP; else priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_MII; } nmt_newt_popup_set_active(priv->monitoring, priv->monitoring_mode); val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_MIIMON); nmt_newt_entry_set_text(priv->miimon, val ?: "0"); val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_UPDELAY); nmt_newt_entry_set_text(priv->updelay, val ?: "0"); val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY); nmt_newt_entry_set_text(priv->downdelay, val ?: "0"); val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL); nmt_newt_entry_set_text(priv->arp_interval, val ?: "0"); val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET); ips = nm_utils_bond_option_arp_ip_targets_split(val); g_object_set(G_OBJECT(priv->arp_ip_target), "strings", ips ?: NM_PTRARRAY_EMPTY(const char *), NULL); visible_mii = (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_MII); nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->miimon), visible_mii); nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->updelay), visible_mii); nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->downdelay), visible_mii); nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->arp_interval), !visible_mii); nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->arp_ip_target), !visible_mii); priv->updating = FALSE; } static void slaves_changed(GObject *object, GParamSpec *pspec, gpointer user_data) { NmtPageBond * bond = NMT_PAGE_BOND(user_data); NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); GPtrArray * slaves; g_object_get(object, "connections", &slaves, NULL); if (slaves->len == 0) { if (priv->slave_type == G_TYPE_NONE) return; priv->slave_type = G_TYPE_NONE; } else { NMConnection *slave = slaves->pdata[0]; if (priv->slave_type != G_TYPE_NONE) return; if (nm_connection_is_type(slave, NM_SETTING_INFINIBAND_SETTING_NAME)) priv->slave_type = NM_TYPE_SETTING_INFINIBAND; else priv->slave_type = NM_TYPE_SETTING_WIRED; } if (priv->slave_type == NM_TYPE_SETTING_INFINIBAND) { nmt_newt_popup_set_active_id(priv->mode, "active-backup"); nmt_newt_component_set_sensitive(NMT_NEWT_COMPONENT(priv->mode), FALSE); } else nmt_newt_component_set_sensitive(NMT_NEWT_COMPONENT(priv->mode), TRUE); } static void _bond_add_option(NMSettingBond *s_bond, const char *option, const char *value) { if (nm_str_is_empty(value)) nm_setting_bond_remove_option(s_bond, option); else nm_setting_bond_add_option(s_bond, option, value); if (nm_streq(option, NM_SETTING_BOND_OPTION_ARP_INTERVAL)) _nm_setting_bond_remove_options_miimon(s_bond); else if (nm_streq(option, NM_SETTING_BOND_OPTION_MIIMON)) _nm_setting_bond_remove_options_arp_interval(s_bond); nm_setting_bond_remove_option(s_bond, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE); } #define WIDGET_CHANGED_FUNC(widget, func, option, dflt) \ static void widget##_widget_changed(GObject *object, GParamSpec *pspec, gpointer user_data) \ { \ NmtPageBond * bond = NMT_PAGE_BOND(user_data); \ NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); \ const char * v; \ \ if (priv->updating) \ return; \ \ v = func(priv->widget); \ priv->updating = TRUE; \ _bond_add_option(priv->s_bond, option, v ?: dflt); \ priv->updating = FALSE; \ } WIDGET_CHANGED_FUNC(primary, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_PRIMARY, NULL) WIDGET_CHANGED_FUNC(miimon, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_MIIMON, "0") WIDGET_CHANGED_FUNC(updelay, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_UPDELAY, "0") WIDGET_CHANGED_FUNC(downdelay, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_DOWNDELAY, "0") WIDGET_CHANGED_FUNC(arp_interval, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_ARP_INTERVAL, "0") static void mode_widget_changed(GObject *object, GParamSpec *pspec, gpointer user_data) { NmtPageBond * bond = NMT_PAGE_BOND(user_data); NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); const char * mode; if (priv->updating) return; mode = nmt_newt_popup_get_active_id(priv->mode); priv->updating = TRUE; _bond_add_option(priv->s_bond, NM_SETTING_BOND_OPTION_MODE, mode); priv->updating = FALSE; if (NM_IN_STRSET(mode, "balance-tlb", "balance-alb")) { nmt_newt_popup_set_active(priv->monitoring, NMT_PAGE_BOND_MONITORING_MII); nmt_newt_component_set_sensitive(NMT_NEWT_COMPONENT(priv->monitoring), FALSE); } else nmt_newt_component_set_sensitive(NMT_NEWT_COMPONENT(priv->monitoring), TRUE); if (NM_IN_STRSET(mode, "active-backup", "balance-alb", "balance-tlb")) { nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->primary), TRUE); _bond_add_option(priv->s_bond, NM_SETTING_BOND_OPTION_PRIMARY, nmt_newt_entry_get_text(priv->primary)); } else { nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->primary), FALSE); nm_setting_bond_remove_option(priv->s_bond, NM_SETTING_BOND_OPTION_PRIMARY); } } static void monitoring_widget_changed(GObject *object, GParamSpec *pspec, gpointer user_data) { NmtPageBond * bond = NMT_PAGE_BOND(user_data); NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); gboolean visible_mii; if (priv->updating) return; priv->monitoring_mode = nmt_newt_popup_get_active(priv->monitoring); visible_mii = (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_MII); nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->miimon), visible_mii); nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->updelay), visible_mii); nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->downdelay), visible_mii); nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->arp_interval), !visible_mii); nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->arp_ip_target), !visible_mii); if (visible_mii) { miimon_widget_changed(NULL, NULL, bond); updelay_widget_changed(NULL, NULL, bond); downdelay_widget_changed(NULL, NULL, bond); } else { arp_interval_widget_changed(NULL, NULL, bond); arp_ip_target_widget_changed(NULL, NULL, bond); } } static void arp_ip_target_widget_changed(GObject *object, GParamSpec *pspec, gpointer user_data) { NmtPageBond * bond = NMT_PAGE_BOND(user_data); NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); char ** ips, *target; if (priv->updating) return; g_object_get(G_OBJECT(priv->arp_ip_target), "strings", &ips, NULL); target = g_strjoinv(",", ips); priv->updating = TRUE; _bond_add_option(priv->s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, target); priv->updating = FALSE; g_free(target); g_strfreev(ips); } static gboolean bond_connection_type_filter(GType connection_type, gpointer user_data) { NmtPageBond * bond = user_data; NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); if (priv->slave_type != NM_TYPE_SETTING_WIRED && connection_type == NM_TYPE_SETTING_INFINIBAND) return TRUE; if (priv->slave_type != NM_TYPE_SETTING_INFINIBAND && connection_type == NM_TYPE_SETTING_WIRED) return TRUE; return FALSE; } static void nmt_page_bond_constructed(GObject *object) { NmtPageBond * bond = NMT_PAGE_BOND(object); NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); NmtEditorSection * section; NmtEditorGrid * grid; NMSettingWired * s_wired; NMSettingBond * s_bond; NmtNewtWidget * widget, *label; NMConnection * conn; conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(bond)); s_bond = nm_connection_get_setting_bond(conn); if (!s_bond) { nm_connection_add_setting(conn, nm_setting_bond_new()); s_bond = nm_connection_get_setting_bond(conn); } priv->s_bond = s_bond; s_wired = nm_connection_get_setting_wired(conn); if (!s_wired) { nm_connection_add_setting(conn, nm_setting_wired_new()); s_wired = nm_connection_get_setting_wired(conn); } section = nmt_editor_section_new(_("BOND"), NULL, TRUE); grid = nmt_editor_section_get_body(section); widget = nmt_newt_separator_new(); nmt_editor_grid_append(grid, _("Slaves"), widget, NULL); nmt_editor_grid_set_row_flags(grid, widget, NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT); widget = nmt_slave_list_new(conn, bond_connection_type_filter, bond); g_signal_connect(widget, "notify::connections", G_CALLBACK(slaves_changed), bond); nmt_editor_grid_append(grid, NULL, widget, NULL); priv->slaves = NMT_SLAVE_LIST(widget); widget = nmt_newt_popup_new(bond_mode); g_signal_connect(widget, "notify::active-id", G_CALLBACK(mode_widget_changed), bond); nmt_editor_grid_append(grid, _("Mode"), widget, NULL); priv->mode = NMT_NEWT_POPUP(widget); widget = nmt_newt_entry_new(40, 0); g_signal_connect(widget, "notify::text", G_CALLBACK(primary_widget_changed), bond); nmt_editor_grid_append(grid, _("Primary"), widget, NULL); priv->primary = NMT_NEWT_ENTRY(widget); widget = nmt_newt_popup_new(bond_monitoring); g_signal_connect(widget, "notify::active", G_CALLBACK(monitoring_widget_changed), bond); nmt_editor_grid_append(grid, _("Link monitoring"), widget, NULL); priv->monitoring = NMT_NEWT_POPUP(widget); widget = nmt_newt_entry_numeric_new(10, 0, G_MAXINT); g_signal_connect(widget, "notify::text", G_CALLBACK(miimon_widget_changed), bond); label = nmt_newt_label_new(C_("milliseconds", "ms")); nmt_editor_grid_append(grid, _("Monitoring frequency"), widget, label); priv->miimon = NMT_NEWT_ENTRY(widget); widget = nmt_newt_entry_numeric_new(10, 0, G_MAXINT); g_signal_connect(widget, "notify::text", G_CALLBACK(updelay_widget_changed), bond); label = nmt_newt_label_new(C_("milliseconds", "ms")); nmt_editor_grid_append(grid, _("Link up delay"), widget, label); priv->updelay = NMT_NEWT_ENTRY(widget); widget = nmt_newt_entry_numeric_new(10, 0, G_MAXINT); g_signal_connect(widget, "notify::text", G_CALLBACK(downdelay_widget_changed), bond); label = nmt_newt_label_new(C_("milliseconds", "ms")); nmt_editor_grid_append(grid, _("Link down delay"), widget, label); priv->downdelay = NMT_NEWT_ENTRY(widget); widget = nmt_newt_entry_numeric_new(10, 0, G_MAXINT); g_signal_connect(widget, "notify::text", G_CALLBACK(arp_interval_widget_changed), bond); label = nmt_newt_label_new(C_("milliseconds", "ms")); nmt_editor_grid_append(grid, _("Monitoring frequency"), widget, label); priv->arp_interval = NMT_NEWT_ENTRY(widget); widget = nmt_address_list_new(NMT_ADDRESS_LIST_IP4); g_signal_connect(widget, "notify::strings", G_CALLBACK(arp_ip_target_widget_changed), bond); nmt_editor_grid_append(grid, _("ARP targets"), widget, NULL); priv->arp_ip_target = NMT_ADDRESS_LIST(widget); widget = nmt_mac_entry_new(40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED); g_object_bind_property(s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, widget, "mac-address", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); nmt_editor_grid_append(grid, _("Cloned MAC address"), widget, NULL); g_signal_connect(s_bond, "notify::" NM_SETTING_BOND_OPTIONS, G_CALLBACK(bond_options_changed), bond); bond_options_changed(G_OBJECT(s_bond), NULL, bond); slaves_changed(G_OBJECT(priv->slaves), NULL, bond); nmt_editor_page_add_section(NMT_EDITOR_PAGE(bond), section); G_OBJECT_CLASS(nmt_page_bond_parent_class)->constructed(object); } static void nmt_page_bond_saved(NmtEditorPage *editor_page) { NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(editor_page); nmt_edit_connection_list_recommit(NMT_EDIT_CONNECTION_LIST(priv->slaves)); } static void nmt_page_bond_class_init(NmtPageBondClass *bond_class) { GObjectClass * object_class = G_OBJECT_CLASS(bond_class); NmtEditorPageClass *editor_page_class = NMT_EDITOR_PAGE_CLASS(bond_class); g_type_class_add_private(bond_class, sizeof(NmtPageBondPrivate)); object_class->constructed = nmt_page_bond_constructed; editor_page_class->saved = nmt_page_bond_saved; }