/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2012, 2013 Red Hat, Inc. */ /** * SECTION:nm-editor-utils * @short_description: Miscellaneous connection editor utilities * * nm-editor-utils contains helper functions for connection editors. * The goal is that this should eventually be shared between nmtui, * nm-connection-editor, and gnome-control-center. */ #include "libnm-client-aux-extern/nm-default-client.h" #include "nm-editor-utils.h" #if 0 #include "libnmc-base/nm-vpn-helpers.h" static GSList *vpn_plugins; static int sort_vpn_plugins (gconstpointer a, gconstpointer b) { NMVpnEditorPlugin *aa = NM_VPN_EDITOR_PLUGIN (a); NMVpnEditorPlugin *bb = NM_VPN_EDITOR_PLUGIN (b); char *aa_desc = NULL, *bb_desc = NULL; int ret; g_object_get (aa, NM_VPN_EDITOR_PLUGIN_NAME, &aa_desc, NULL); g_object_get (bb, NM_VPN_EDITOR_PLUGIN_NAME, &bb_desc, NULL); ret = g_strcmp0 (aa_desc, bb_desc); g_free (aa_desc); g_free (bb_desc); return ret; } #endif static void wifi_connection_setup_func(NMConnection *connection, NMSettingConnection *s_con, NMSetting *s_hw) { g_object_set(G_OBJECT(s_hw), NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, NULL); } static void bond_connection_setup_func(NMConnection *connection, NMSettingConnection *s_con, NMSetting *s_hw) { NMSettingBond * s_bond = NM_SETTING_BOND(s_hw); guint i; const char * value; static const char *const options[] = { NM_SETTING_BOND_OPTION_MODE, NM_SETTING_BOND_OPTION_MIIMON, NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_UPDELAY, }; for (i = 0; i < G_N_ELEMENTS(options); i++) { value = nm_setting_bond_get_option_default(s_bond, options[i]); if (value) nm_setting_bond_add_option(s_bond, options[i], value); } } static void wireguard_connection_setup_func(NMConnection * connection, NMSettingConnection *s_con, NMSetting * s_hw) { NMSettingIPConfig *s_ip; s_ip = (NMSettingIPConfig *) nm_setting_ip4_config_new(); g_object_set(s_ip, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NULL); nm_connection_add_setting(connection, (NMSetting *) s_ip); s_ip = (NMSettingIPConfig *) nm_setting_ip6_config_new(); g_object_set(s_ip, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_DISABLED, NULL); nm_connection_add_setting(connection, (NMSetting *) s_ip); } typedef void (*NMEditorNewConnectionSetupFunc)(NMConnection * connection, NMSettingConnection *s_con, NMSetting * s_hw); typedef struct { NMEditorConnectionTypeData data; const char * id_format; NMEditorNewConnectionSetupFunc connection_setup_func; gboolean no_autoconnect; } NMEditorConnectionTypeDataReal; static int sort_types(gconstpointer a, gconstpointer b) { NMEditorConnectionTypeData *typea = *(NMEditorConnectionTypeData **) a; NMEditorConnectionTypeData *typeb = *(NMEditorConnectionTypeData **) b; if (typea->virtual && !typeb->virtual) return 1; else if (typeb->virtual && !typea->virtual) return -1; if (typea->setting_type == NM_TYPE_SETTING_VPN && typeb->setting_type != NM_TYPE_SETTING_VPN) return 1; else if (typeb->setting_type == NM_TYPE_SETTING_VPN && typea->setting_type != NM_TYPE_SETTING_VPN) return -1; return g_utf8_collate(typea->name, typeb->name); } /** * nm_editor_utils_get_connection_type_list: * * Gets an array of information about supported connection types. The * array is sorted in a standard presentation order (hardware types * first, alphabetized, then virtual types, alphabetized, then VPN * types, alphabetized). * * Returns: the array of connection type information */ NMEditorConnectionTypeData ** nm_editor_utils_get_connection_type_list(void) { GPtrArray * array; NMEditorConnectionTypeDataReal * item; static NMEditorConnectionTypeData **list; #if 0 GHashTable *vpn_plugins_hash; gboolean have_vpn_plugins; #endif if (list) return list; array = g_ptr_array_new(); item = g_new0(NMEditorConnectionTypeDataReal, 1); item->data.name = _("Ethernet"); item->data.setting_type = NM_TYPE_SETTING_WIRED; item->data.device_type = NM_TYPE_DEVICE_ETHERNET; item->data.virtual = FALSE; item->id_format = _("Ethernet connection %d"); g_ptr_array_add(array, item); item = g_new0(NMEditorConnectionTypeDataReal, 1); item->data.name = _("Veth"); item->data.setting_type = NM_TYPE_SETTING_VETH; item->data.device_type = NM_TYPE_DEVICE_VETH; item->data.virtual = TRUE; item->id_format = _("Veth connection %d"); g_ptr_array_add(array, item); item = g_new0(NMEditorConnectionTypeDataReal, 1); item->data.name = _("Wi-Fi"); item->data.setting_type = NM_TYPE_SETTING_WIRELESS; item->data.device_type = NM_TYPE_DEVICE_WIFI; item->data.virtual = FALSE; item->id_format = _("Wi-Fi connection %d"); item->connection_setup_func = wifi_connection_setup_func; g_ptr_array_add(array, item); item = g_new0(NMEditorConnectionTypeDataReal, 1); item->data.name = _("InfiniBand"); item->data.setting_type = NM_TYPE_SETTING_INFINIBAND; item->data.device_type = NM_TYPE_DEVICE_INFINIBAND; item->data.virtual = FALSE; item->id_format = _("InfiniBand connection %d"); g_ptr_array_add(array, item); #if 0 item = g_new0 (NMEditorConnectionTypeDataReal, 1); item->data.name = _("Mobile Broadband"); item->data.setting_type = NM_TYPE_SETTING_GSM; item->data.virtual = FALSE; item->id_format = _("Mobile broadband connection %d"); item->no_autoconnect = TRUE; g_ptr_array_add (array, item); #endif item = g_new0(NMEditorConnectionTypeDataReal, 1); item->data.name = _("DSL"); item->data.setting_type = NM_TYPE_SETTING_PPPOE; item->data.device_type = NM_TYPE_DEVICE_ETHERNET; item->data.virtual = FALSE; item->id_format = _("DSL connection %d"); item->no_autoconnect = TRUE; g_ptr_array_add(array, item); item = g_new0(NMEditorConnectionTypeDataReal, 1); item->data.name = _("Bond"); item->data.setting_type = NM_TYPE_SETTING_BOND; item->data.device_type = NM_TYPE_DEVICE_BOND; item->data.virtual = TRUE; item->id_format = _("Bond connection %d"); item->connection_setup_func = bond_connection_setup_func; g_ptr_array_add(array, item); item = g_new0(NMEditorConnectionTypeDataReal, 1); item->data.name = _("Bridge"); item->data.setting_type = NM_TYPE_SETTING_BRIDGE; item->data.slave_setting_type = NM_TYPE_SETTING_BRIDGE_PORT; item->data.device_type = NM_TYPE_DEVICE_BRIDGE; item->data.virtual = TRUE; item->id_format = _("Bridge connection %d"); g_ptr_array_add(array, item); item = g_new0(NMEditorConnectionTypeDataReal, 1); item->data.name = _("Team"); item->data.setting_type = NM_TYPE_SETTING_TEAM; item->data.slave_setting_type = NM_TYPE_SETTING_TEAM_PORT; item->data.device_type = NM_TYPE_DEVICE_TEAM; item->data.virtual = TRUE; item->id_format = _("Team connection %d"); g_ptr_array_add(array, item); item = g_new0(NMEditorConnectionTypeDataReal, 1); item->data.name = _("VLAN"); item->data.setting_type = NM_TYPE_SETTING_VLAN; item->data.device_type = NM_TYPE_DEVICE_VLAN; item->data.virtual = TRUE; item->id_format = _("VLAN connection %d"); g_ptr_array_add(array, item); item = g_new0(NMEditorConnectionTypeDataReal, 1); item->data.name = _("IP tunnel"); item->data.setting_type = NM_TYPE_SETTING_IP_TUNNEL; item->data.device_type = NM_TYPE_DEVICE_IP_TUNNEL; item->data.virtual = TRUE; item->id_format = _("IP tunnel connection %d"); g_ptr_array_add(array, item); #if 0 /* Add "VPN" only if there are plugins */ vpn_plugins_hash = nm_vpn_get_plugin_infos (); have_vpn_plugins = vpn_plugins_hash && g_hash_table_size (vpn_plugins_hash); if (have_vpn_plugins) { GHashTableIter iter; gpointer name, plugin; item = g_new0 (NMEditorConnectionTypeDataReal, 1); item->data.name = _("VPN"); item->data.setting_type = NM_TYPE_SETTING_VPN; item->data.virtual = TRUE; item->id_format = _("VPN connection %d"); item->no_autoconnect = TRUE; g_ptr_array_add (array, item); vpn_plugins = NULL; g_hash_table_iter_init (&iter, vpn_plugins_hash); while (g_hash_table_iter_next (&iter, &name, &plugin)) vpn_plugins = g_slist_prepend (vpn_plugins, plugin); vpn_plugins = g_slist_sort (vpn_plugins, sort_vpn_plugins); } #endif item = g_new0(NMEditorConnectionTypeDataReal, 1); item->data.name = _("WireGuard"); item->data.setting_type = NM_TYPE_SETTING_WIREGUARD; item->data.device_type = NM_TYPE_DEVICE_WIREGUARD; item->data.virtual = TRUE; item->id_format = _("WireGuard connection %d"); item->connection_setup_func = wireguard_connection_setup_func; g_ptr_array_add(array, item); g_ptr_array_sort(array, sort_types); g_ptr_array_add(array, NULL); list = (NMEditorConnectionTypeData **) g_ptr_array_free(array, FALSE); return list; } static gboolean _assert_format_int(const char *format) { g_assert(format); format = strchr(format, '%'); g_assert(format); format++; g_assert(!strchr(format, '%')); g_assert(format[0] == 'd'); return TRUE; } static char * get_available_connection_name(const char *format, NMClient *client) { const GPtrArray *conns; GSList * names = NULL, *iter; char * cname = NULL; int i = 0; nm_assert(_assert_format_int(format)); conns = nm_client_get_connections(client); for (i = 0; i < conns->len; i++) { const char *id; id = nm_connection_get_id(NM_CONNECTION(conns->pdata[i])); g_assert(id); names = g_slist_append(names, (gpointer) id); } /* Find the next available unique connection name */ for (i = 1; !cname && i < 10000; i++) { char * temp; gboolean found = FALSE; NM_PRAGMA_WARNING_DISABLE("-Wformat-nonliteral") temp = g_strdup_printf(format, i); NM_PRAGMA_WARNING_REENABLE for (iter = names; iter; iter = g_slist_next(iter)) { if (!strcmp(iter->data, temp)) { found = TRUE; break; } } if (!found) cname = temp; else g_free(temp); } g_slist_free(names); return cname; } static char * get_available_iface_name(const char *try_name, NMClient *client) { const GPtrArray *connections; NMConnection * connection; char * new_name; unsigned num = 1; int i = 0; const char * ifname = NULL; connections = nm_client_get_connections(client); new_name = g_strdup(try_name); while (i < connections->len) { connection = NM_CONNECTION(connections->pdata[i]); ifname = nm_connection_get_interface_name(connection); if (g_strcmp0(new_name, ifname) == 0) { g_free(new_name); new_name = g_strdup_printf("%s%d", try_name, num++); i = 0; } else i++; } return new_name; } /** * nm_editor_utils_create_connection: * @type: the type of the connection's primary #NMSetting * @master: (allow-none): the connection's master, if any * @client: an #NMClient * * Creates a new #NMConnection of the given type, automatically * creating a UUID and an appropriate not-currently-in-use connection * name, setting #NMSettingConnection:autoconnect appropriately for * the connection type, filling in slave-related information if * @master is not %NULL, and initializing any other mandatory-to-set * properties to reasonable initial values. * * Returns: a new #NMConnection */ NMConnection * nm_editor_utils_create_connection(GType type, NMConnection *master, NMClient *client) { NMEditorConnectionTypeData ** types; NMEditorConnectionTypeDataReal *type_data = NULL; const char * master_setting_type = NULL, *master_uuid = NULL; GType master_type = G_TYPE_INVALID, slave_setting_type = G_TYPE_INVALID; NMConnection * connection; NMSettingConnection *s_con; NMSetting * s_hw, *s_slave; char * uuid, *id, *ifname; int i; if (master) { NMSettingConnection *master_s_con; master_s_con = nm_connection_get_setting_connection(master); master_setting_type = nm_setting_connection_get_connection_type(master_s_con); master_uuid = nm_setting_connection_get_uuid(master_s_con); master_type = nm_setting_lookup_type(master_setting_type); } types = nm_editor_utils_get_connection_type_list(); for (i = 0; types[i]; i++) { if (types[i]->setting_type == type) type_data = (NMEditorConnectionTypeDataReal *) types[i]; if (types[i]->setting_type == master_type) slave_setting_type = types[i]->slave_setting_type; } if (!type_data) { g_return_val_if_reached(NULL); return NULL; } connection = nm_simple_connection_new(); s_con = NM_SETTING_CONNECTION(nm_setting_connection_new()); nm_connection_add_setting(connection, NM_SETTING(s_con)); s_hw = g_object_new(type, NULL); nm_connection_add_setting(connection, s_hw); if (type == NM_TYPE_SETTING_BOND) ifname = get_available_iface_name("nm-bond", client); else if (type == NM_TYPE_SETTING_TEAM) ifname = get_available_iface_name("nm-team", client); else if (type == NM_TYPE_SETTING_BRIDGE) ifname = get_available_iface_name("nm-bridge", client); else ifname = NULL; if (slave_setting_type != G_TYPE_INVALID) { s_slave = g_object_new(slave_setting_type, NULL); nm_connection_add_setting(connection, s_slave); } uuid = nm_utils_uuid_generate(); id = get_available_connection_name(type_data->id_format, client); g_object_set(s_con, NM_SETTING_CONNECTION_UUID, uuid, NM_SETTING_CONNECTION_ID, id, NM_SETTING_CONNECTION_TYPE, nm_setting_get_name(s_hw), NM_SETTING_CONNECTION_AUTOCONNECT, !type_data->no_autoconnect, NM_SETTING_CONNECTION_MASTER, master_uuid, NM_SETTING_CONNECTION_SLAVE_TYPE, master_setting_type, NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, NULL); g_free(uuid); g_free(id); g_free(ifname); if (type_data->connection_setup_func) type_data->connection_setup_func(connection, s_con, s_hw); return connection; } /** * nm_editor_utils_get_connection_type_data: * @conn: an #NMConnection * * Gets the #NMEditorConnectionTypeData corresponding to * @conn's connection type. * * Returns: the #NMEditorConnectionTypeData */ NMEditorConnectionTypeData * nm_editor_utils_get_connection_type_data(NMConnection *conn) { NMSettingConnection * s_con; const char * conn_type; GType conn_gtype; NMEditorConnectionTypeData **types; int i; s_con = nm_connection_get_setting_connection(conn); g_return_val_if_fail(s_con != NULL, NULL); conn_type = nm_setting_connection_get_connection_type(s_con); conn_gtype = nm_setting_lookup_type(conn_type); g_return_val_if_fail(conn_gtype != G_TYPE_INVALID, NULL); types = nm_editor_utils_get_connection_type_list(); for (i = 0; types[i]; i++) { if (types[i]->setting_type == conn_gtype) return types[i]; } return NULL; }