diff --git a/client-gtk/Makefile.am b/client-gtk/Makefile.am index 643e464..ce3bc27 100644 --- a/client-gtk/Makefile.am +++ b/client-gtk/Makefile.am @@ -11,6 +11,8 @@ inador_gtk_client_SOURCES = main.c \ ni-window-interface.c ni-window-interface.h \ ni-ip.c ni-ip.h \ ni-ip-add-dialog.c ni-ip-add-dialog.h \ + ni-interface-filter.c ni-interface-filter.h \ + ni-interface-chooser-dialog.c ni-interface-chooser-dialog.h \ $(BUILT_SOURCES) #inador_gtk_client_CPPFLAGS = -DGAMEDATA_DIR=\"$(gamedatadir)/\" -DLOCALEDIR=\"$(localedir)\" $(AM_CPPFLAGS) diff --git a/client-gtk/main.c b/client-gtk/main.c index 8c90f30..b6fa204 100644 --- a/client-gtk/main.c +++ b/client-gtk/main.c @@ -50,9 +50,10 @@ const char *group_names[NUM_INTERFACES_GROUP] = { GtkListStore *interfaces_store; NIClient *ni_client; GtkWidget *ventana; -GtkWidget *vbox; +GtkWidget *main_vbox, *network_vbox; GtkWidget *empty_label = NULL; GHashTable *network_blocks_list; +GtkWidget *create_bridge_menu; static void click_interface_cb (GtkIconView *iconview, GtkTreePath *path, gpointer data) { UINetworkType *network_block = (UINetworkType *) data; @@ -126,7 +127,7 @@ UINetworkType * create_block_list (int type) { gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (network_block->model_filter), network_block_model_filter_by_type, network_block, NULL); network_block->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); - gtk_box_pack_start (GTK_BOX (vbox), network_block->vbox, FALSE, FALSE, 5); + gtk_box_pack_start (GTK_BOX (network_vbox), network_block->vbox, FALSE, FALSE, 5); /* Reordenar los grupos presentes, dentro del vbox */ pos = 0; for (g = 0; g < NUM_INTERFACES_GROUP && g < type; g++) { @@ -134,7 +135,7 @@ UINetworkType * create_block_list (int type) { pos++; } } - gtk_box_reorder_child (GTK_BOX (vbox), network_block->vbox, pos); + gtk_box_reorder_child (GTK_BOX (network_vbox), network_block->vbox, pos); network_block->label = gtk_label_new (group_names [type]); gtk_box_pack_start (GTK_BOX (network_block->vbox), network_block->label, FALSE, FALSE, 0); @@ -233,6 +234,41 @@ void delete_interface_cb (NIClient *ni_client, NIInterface *ni_interface, gpoint } } +void create_bridge_cb (GtkWidget *menu_item, gpointer data) { + static GtkWidget *dialog = NULL; + GtkWidget *content; + GtkWidget *label, *entry; + guint res; + gchar *name; + + if (dialog != NULL) { + gtk_window_present (GTK_WINDOW (dialog)); + return; + } + + dialog = gtk_message_dialog_new (GTK_WINDOW (ventana), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, "Nombre del nuevo bridge"); + + content = gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG (dialog)); + + entry = gtk_entry_new (); + gtk_entry_set_text (GTK_ENTRY (entry), "br0"); + gtk_box_pack_start (GTK_BOX (content), entry, FALSE, FALSE, 0); + gtk_widget_show (entry); + + res = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (res == GTK_RESPONSE_OK) { + name = gtk_entry_get_text (GTK_ENTRY (entry)); + + if (name != NULL && name[0] != 0) { + ni_client_ask_create_bridge (ni_client, name); + } + } + + gtk_widget_destroy (dialog); + dialog = NULL; +} + void cerrar_ventana_cb (GtkWidget *ventana, gpointer data) { g_object_unref (ni_client); gtk_main_quit (); @@ -241,6 +277,8 @@ void cerrar_ventana_cb (GtkWidget *ventana, gpointer data) { int main (int argc, char *argv[]) { gboolean connected; GtkWidget *label, *scrolled; + GtkWidget *menu_bar, *menu, *menu_item; + GtkWidget *icon; gtk_init (&argc, &argv); @@ -259,22 +297,46 @@ int main (int argc, char *argv[]) { gtk_window_set_default_size (GTK_WINDOW (ventana), 640, 480); g_signal_connect (ventana, "destroy", G_CALLBACK (cerrar_ventana_cb), NULL); + main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); + gtk_container_add (GTK_CONTAINER (ventana), main_vbox); + + menu_bar = gtk_menu_bar_new (); + gtk_box_pack_start (GTK_BOX (main_vbox), menu_bar, FALSE, FALSE, 0); + + menu_item = gtk_menu_item_new_with_mnemonic ("_Interfaces"); + gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), menu_item); + + menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu); + + create_bridge_menu = gtk_image_menu_item_new_with_mnemonic ("Crear _bridge"); + if (connected == FALSE) { + gtk_widget_set_sensitive (create_bridge_menu, FALSE); + } + icon = gtk_image_new_from_icon_name ("list-add", GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (create_bridge_menu), icon); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), create_bridge_menu); + gtk_widget_show_all (create_bridge_menu); + g_signal_connect (create_bridge_menu, "activate", G_CALLBACK (create_bridge_cb), NULL); + + gtk_widget_show_all (menu); + scrolled = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); - gtk_container_add (GTK_CONTAINER (ventana), scrolled); + gtk_box_pack_start (GTK_BOX (main_vbox), scrolled, TRUE, TRUE, 0); - vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_widget_set_size_request (vbox, 260, -1); + network_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_set_size_request (network_vbox, 260, -1); - gtk_container_add (GTK_CONTAINER (scrolled), vbox); + gtk_container_add (GTK_CONTAINER (scrolled), network_vbox); if (connected == FALSE) { empty_label = gtk_label_new ("El servicio del network inador no está corriendo\n"); } else { empty_label = gtk_label_new ("Recuperando información...\n"); } - gtk_box_pack_start (GTK_BOX (vbox), empty_label, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (network_vbox), empty_label, TRUE, TRUE, 0); gtk_widget_show_all (ventana); diff --git a/client-gtk/ni-client.c b/client-gtk/ni-client.c index d49fb43..f11f4f4 100644 --- a/client-gtk/ni-client.c +++ b/client-gtk/ni-client.c @@ -267,6 +267,13 @@ static gboolean ni_client_read_from_socket (GIOChannel *source, GIOCondition con close (ni_client->priv->client_socket); ni_client->priv->client_socket = -1; + /* Eliminar el source */ + if (ni_client->priv->source > 0) { + g_source_remove (ni_client->priv->source); + ni_client->priv->source = 0; + } + + /* TODO: Disparar aquí una señal de desconexión al network-inador */ return FALSE; } @@ -544,6 +551,79 @@ void ni_client_ask_up_down_interface (NIClient *ni_client, NIInterface *ni_inter send (ni_client->priv->client_socket, buffer, 6, 0); } +void ni_client_ask_create_bridge (NIClient *ni_client, const gchar *name) { + unsigned char buffer[128]; + int n_len; + + if (ni_client->priv->client_socket < 0) { + return; + } + + if (name[0] == 0) return; + + buffer[0] = NET_INADOR_TYPE_COMMAND; + buffer[1] = NET_INADOR_COMMAND_CREATE_BRIDGE; + n_len = strlen (name); + buffer[2] = n_len; + memcpy (&buffer[3], name, buffer[2]); + + send (ni_client->priv->client_socket, buffer, 3 + n_len, 0); +} + +void ni_client_ask_interface_clear_master (NIClient *ni_client, NIInterface *ni_interface) { + unsigned char buffer[128]; + uint32_t index; + + if (ni_client->priv->client_socket < 0) { + return; + } + + buffer[0] = NET_INADOR_TYPE_COMMAND; + buffer[1] = NET_INADOR_COMMAND_CLEAR_MASTER; + index = ni_interface_get_index (ni_interface); + memcpy (&buffer[2], &index, 4); + + send (ni_client->priv->client_socket, buffer, 6, 0); +} + +void ni_client_ask_interface_set_master (NIClient *ni_client, NIInterface *ni_interface, NIInterface *master) { + unsigned char buffer[128]; + uint32_t index; + + if (ni_client->priv->client_socket < 0) { + return; + } + + buffer[0] = NET_INADOR_TYPE_COMMAND; + buffer[1] = NET_INADOR_COMMAND_SET_MASTER; + index = ni_interface_get_index (ni_interface); + memcpy (&buffer[2], &index, 4); + index = ni_interface_get_index (master); + memcpy (&buffer[6], &index, 4); + + send (ni_client->priv->client_socket, buffer, 10, 0); +} + +static void ni_client_foreach_has_table (gpointer key, gpointer value, gpointer data) { + GList **lista = (GList **) data; + + *lista = g_list_append (*lista, value); +} + +GList *ni_client_get_list_interfaces (NIClient *ni_client) { + g_return_val_if_fail (NI_IS_CLIENT (ni_client), NULL); + GList *all_ifaces = NULL; + + g_hash_table_foreach (ni_client->priv->interfaces, ni_client_foreach_has_table, &all_ifaces); + + return all_ifaces; +} + +NIInterface *ni_client_get_interface_by_index (NIClient *ni_client, guint index) { + g_return_val_if_fail (NI_IS_CLIENT (ni_client), NULL); + return g_hash_table_lookup (ni_client->priv->interfaces, GINT_TO_POINTER(index)); +} + gboolean ni_client_connect (NIClient *ni_client) { int s, ret; struct sockaddr_un path_dest; diff --git a/client-gtk/ni-client.h b/client-gtk/ni-client.h index 18dcd06..37981d1 100644 --- a/client-gtk/ni-client.h +++ b/client-gtk/ni-client.h @@ -83,6 +83,12 @@ void ni_client_ask_ip_delete (NIClient *ni_client, NIInterface *ni_interface, NI void ni_client_ask_change_iface_name (NIClient *ni_client, NIInterface *ni_interface, const gchar *new_name); void ni_client_ask_change_iface_mtu (NIClient *ni_client, NIInterface *ni_interface, guint new_mtu); void ni_client_ask_up_down_interface (NIClient *ni_client, NIInterface *ni_interface, gboolean is_up); +void ni_client_ask_create_bridge (NIClient *ni_client, const gchar *name); +GList *ni_client_get_list_interfaces (NIClient *ni_client); +NIInterface *ni_client_get_interface_by_index (NIClient *ni_client, guint index); + +void ni_client_ask_interface_clear_master (NIClient *ni_client, NIInterface *ni_interface); +void ni_client_ask_interface_set_master (NIClient *ni_client, NIInterface *ni_interface, NIInterface *master); G_END_DECLS diff --git a/client-gtk/ni-interface-chooser-dialog.c b/client-gtk/ni-interface-chooser-dialog.c new file mode 100644 index 0000000..3648759 --- /dev/null +++ b/client-gtk/ni-interface-chooser-dialog.c @@ -0,0 +1,389 @@ +/* + * ni-interface-chooser-dialog.c + * This file is part of Network Inador + * + * Copyright (C) 2021 - Gatuno + * + * Network Inador is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Network Inador is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Network Inador; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include + +#include + +#include +#include +#include + +#include "ni-interface-chooser-dialog.h" +#include "ni-client.h" +#include "ni-interface.h" +#include "ni-interface-filter.h" + +#include "../src/link-types.h" + +enum { + COL_INTERFACE_CHOOSER_NAME, + COL_INTERFACE_CHOOSER_SENSITIVE, + COL_INTERFACE_CHOOSER_INDEX, + + NUM_INTERFACE_CHOOSER_COLS +}; + +struct _NIInterfaceChooserDialogPrivate { + NIClient *ni_client; + int order; + gboolean group; + + GtkListStore *store; + GtkWidget *combo; + + GtkWidget *button_add; + NIInterfaceFilter *filter; +}; + +enum { + PROP_ORDER = 1, + PROP_GROUP, + PROP_NI_CLIENT, + + N_PROPERTIES +}; + +G_DEFINE_TYPE_WITH_PRIVATE (NIInterfaceChooserDialog, ni_interface_chooser_dialog, GTK_TYPE_DIALOG) + +static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; + +#if 0 +static void ni_ip_add_dialog_update_response (NIInterfaceChooserDialog *dialog) { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->check_p2p)) && dialog->priv->valid_p2p_ip == FALSE) { + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, FALSE); + return; + } + + if (dialog->priv->valid_main_ip == FALSE) { + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, FALSE); + return; + } + + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, TRUE); +} +#endif + +static void ni_interface_chooser_dialog_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { + NIInterfaceChooserDialog *dialog = NI_INTERFACE_CHOOSER_DIALOG (object); + g_return_if_fail (NI_IS_INTERFACE_CHOOSER_DIALOG (object)); + + switch (prop_id) { + case PROP_ORDER: + dialog->priv->order = g_value_get_uint (value); + break; + case PROP_GROUP: + dialog->priv->group = g_value_get_boolean (value); + break; + case PROP_NI_CLIENT: + dialog->priv->ni_client = NI_CLIENT (g_value_get_object (value)); + g_object_ref (dialog->priv->ni_client); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void ni_interface_chooser_dialog_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { + NIInterfaceChooserDialog *dialog = NI_INTERFACE_CHOOSER_DIALOG (object); + g_return_if_fail (NI_IS_INTERFACE_CHOOSER_DIALOG (object)); + + switch (prop_id) { + case PROP_ORDER: + g_value_set_uint (value, dialog->priv->order); + break; + case PROP_GROUP: + g_value_set_boolean (value, dialog->priv->group); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static gint ni_interface_chooser_order_by_name (NIInterface *a, NIInterface *b) { + return g_strcmp0 (ni_interface_get_name (a), ni_interface_get_name (b)); +} + +static gint ni_interface_chooser_order_by_index (NIInterface *a, NIInterface *b) { + return ni_interface_get_index (a) - ni_interface_get_index (b); +} + +static gint ni_interface_chooser_order_by_type (NIInterface *a, NIInterface *b) { + guint at, bt; + + at = ni_interface_get_iface_type (a); + bt = ni_interface_get_iface_type (b); + + if (at == bt) { + return ni_interface_chooser_order_by_name (a, b); + } else if (at < bt) { + return -1; + } else { + return 1; + } +} + +static const char * ni_interface_chooser_guess_interface_type (guint type) { + if (type == NI_LINK_TYPE_ETHERNET) { + return "Redes cableadas"; + } else if (type == NI_LINK_TYPE_WIFI) { + return "Redes inalámbricas"; + } else if (type == NI_LINK_TYPE_DUMMY || type == NI_LINK_TYPE_LOOPBACK) { + return "Loopback y otros tipos"; + } else if (type == NI_LINK_TYPE_BRIDGE) { + return "Puentes"; + } + + return "Otros"; +} + +static void ni_interface_chooser_refilter_all (NIInterfaceChooserDialog *dialog) { + GList *all_interfaces, *g; + NIInterface *interface; + guint last_type = 0, iface_type, index, master, previous_index, c; + const gchar *name; + GtkTreeIter iter; + NIInterfaceFilterInfo filter_info; + + previous_index = -1; + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (dialog->priv->combo), &iter)) { + gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->store), &iter, COL_INTERFACE_CHOOSER_INDEX, &previous_index, -1); + } + gtk_list_store_clear (dialog->priv->store); + + all_interfaces = ni_client_get_list_interfaces (dialog->priv->ni_client); + + /* Aplicar el ordenamiento primero */ + switch (dialog->priv->order) { + case NI_INTERFACE_CHOOSER_ORDER_INDEX: + all_interfaces = g_list_sort (all_interfaces, (GCompareFunc) ni_interface_chooser_order_by_index); + break; + case NI_INTERFACE_CHOOSER_ORDER_NAME: + all_interfaces = g_list_sort (all_interfaces, (GCompareFunc) ni_interface_chooser_order_by_name); + break; + case NI_INTERFACE_CHOOSER_ORDER_TYPE: + all_interfaces = g_list_sort (all_interfaces, (GCompareFunc) ni_interface_chooser_order_by_type); + break; + } + + c = 0; + for (g = all_interfaces; g != NULL; g = g->next) { + interface = (NIInterface *) g->data; + + iface_type = ni_interface_get_iface_type (interface); + index = ni_interface_get_index (interface); + name = ni_interface_get_name (interface); + master = ni_interface_get_master (interface); + /* Ejecutar el filtro */ + if (dialog->priv->filter != NULL) { + filter_info.index = index; + filter_info.name = name; + filter_info.type = iface_type; + filter_info.master = master; + + if (ni_interface_filter_filter (dialog->priv->filter, &filter_info) == FALSE) continue; + } + + if (dialog->priv->group && last_type != iface_type) { + /* Crear el item dummy nulo */ + gtk_list_store_append (dialog->priv->store, &iter); + + gtk_list_store_set (dialog->priv->store, &iter, COL_INTERFACE_CHOOSER_NAME, ni_interface_chooser_guess_interface_type (iface_type), COL_INTERFACE_CHOOSER_SENSITIVE, FALSE, COL_INTERFACE_CHOOSER_INDEX, 0, -1); + last_type = iface_type; + continue; + } + + gtk_list_store_append (dialog->priv->store, &iter); + gtk_list_store_set (dialog->priv->store, &iter, COL_INTERFACE_CHOOSER_NAME, name, COL_INTERFACE_CHOOSER_SENSITIVE, TRUE, COL_INTERFACE_CHOOSER_INDEX, index, -1); + + c++; + if (previous_index != -1 && index == previous_index) { + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (dialog->priv->combo), &iter); + } + } + + if (previous_index == -1 && gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->priv->combo)) == -1 && c > 0) { + gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->priv->combo), 0); + } +} + +void ni_interface_chooser_dialog_set_filter (NIInterfaceChooserDialog *dialog, NIInterfaceFilter *filter) { + NIInterfaceFilter *old_filter; + + g_return_if_fail (NI_IS_INTERFACE_CHOOSER_DIALOG (dialog)); + g_return_if_fail (filter == NULL || NI_IS_INTERFACE_FILTER (filter)); + + if (filter) { + g_object_ref (filter); + } + + old_filter = dialog->priv->filter; + dialog->priv->filter = filter; + + if (old_filter) { + g_object_unref (old_filter); + } + + ni_interface_chooser_refilter_all (dialog); +} + +static void ni_interface_chooser_dialog_changed_interface_cb (NIClient *ni_client, NIInterface *iface, gpointer data) { + NIInterfaceChooserDialog *dialog = NI_INTERFACE_CHOOSER_DIALOG (data); + ni_interface_chooser_refilter_all (dialog); +} + +/* Construcción del objeto */ +static void ni_interface_chooser_dialog_constructed (GObject *obj) { + GObjectClass *parent_class = G_OBJECT_CLASS (g_type_class_peek (GTK_TYPE_DIALOG)); + NIInterfaceChooserDialog *dialog = NI_INTERFACE_CHOOSER_DIALOG (obj); + const GList *tmp_list, *g; + NIInterface *ni_interface; + gchar *name; + guint flags; + + parent_class->constructed (obj); + + if (dialog->priv->ni_client == NULL) return; + + /* Conectar la señal de IP agregada y la señal de IP eliminada */ + g_signal_connect (dialog->priv->ni_client, "new-interface", G_CALLBACK (ni_interface_chooser_dialog_changed_interface_cb), dialog); + g_signal_connect (dialog->priv->ni_client, "delete-interface", G_CALLBACK (ni_interface_chooser_dialog_changed_interface_cb), dialog); + + ni_interface_chooser_refilter_all (dialog); +} + +static void ni_interface_chooser_dialog_dispose (GObject *obj) { + NIInterfaceChooserDialog *dialog; + GObjectClass *parent_class = G_OBJECT_CLASS (g_type_class_peek (GTK_TYPE_DIALOG)); + + dialog = NI_INTERFACE_CHOOSER_DIALOG (obj); + + if (dialog->priv->ni_client != NULL) { + g_object_unref (dialog->priv->ni_client); + dialog->priv->ni_client = NULL; + } + + if (dialog->priv->filter != NULL) { + g_object_unref (dialog->priv->filter); + dialog->priv->filter = NULL; + } + + parent_class->dispose (obj); +} + +static void ni_interface_chooser_dialog_class_init (NIInterfaceChooserDialogClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWindowClass *window_class = GTK_WINDOW_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass); + + object_class->set_property = ni_interface_chooser_dialog_set_property; + object_class->get_property = ni_interface_chooser_dialog_get_property; + object_class->constructed = ni_interface_chooser_dialog_constructed; + object_class->dispose = ni_interface_chooser_dialog_dispose; + + obj_properties[PROP_ORDER] = g_param_spec_uint ( + "order", + "Order of the interfaces", + "Order to show the interfaces", + 0, 10, NI_INTERFACE_CHOOSER_ORDER_INDEX, + G_PARAM_READWRITE); + + obj_properties[PROP_GROUP] = g_param_spec_boolean ( + "group", + "Group or not the interfaces", + "If interfaces should be grouped on dialog", + FALSE, + G_PARAM_READWRITE); + + obj_properties[PROP_NI_CLIENT] = g_param_spec_object ( + "ni-client", + "Network Inador Client", + "The client object", + NI_TYPE_CLIENT, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + + g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties); +} + +static void ni_interface_chooser_dialog_init (NIInterfaceChooserDialog *dialog) { + NIInterfaceChooserDialogPrivate *priv = ni_interface_chooser_dialog_get_instance_private (dialog); + GtkWidget *image; + GtkWidget *vbox, *expander; + GtkWidget *container, *label, *hbox; + GtkCellRenderer *render; + + dialog->priv = priv; + + priv->order = NI_INTERFACE_CHOOSER_ORDER_INDEX; + priv->group = FALSE; + priv->ni_client = NULL; + priv->filter = NULL; + + gtk_window_set_title (GTK_WINDOW (dialog), "Elija una interfaz"); + + gtk_dialog_add_button (GTK_DIALOG (dialog), "Cancelar", GTK_RESPONSE_CANCEL); + + priv->button_add = gtk_dialog_add_button (GTK_DIALOG (dialog), "Seleccionar", GTK_RESPONSE_OK); + + container = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); + gtk_box_pack_start (GTK_BOX (container), vbox, TRUE, TRUE, 0); + + label = gtk_label_new ("Seleccione una interfaz:"); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5); + + /* Preparar el store box */ + priv->store = gtk_list_store_new (NUM_INTERFACE_CHOOSER_COLS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INT); + + priv->combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (priv->store)); + + render = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo), render, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->combo), render, "markup", COL_INTERFACE_CHOOSER_NAME, "sensitive", COL_INTERFACE_CHOOSER_SENSITIVE, NULL); + gtk_box_pack_start (GTK_BOX (vbox), priv->combo, FALSE, FALSE, 5); + + gtk_widget_show_all (container); +} + +guint ni_interface_chooser_dialog_get_index (NIInterfaceChooserDialog *dialog) { + GtkTreeIter iter; + guint index; + + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (dialog->priv->combo), &iter) == FALSE) { + return 0; + } + + gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->store), &iter, COL_INTERFACE_CHOOSER_INDEX, &index, -1); + + return index; +} + +GtkWidget* ni_interface_chooser_dialog_new (NIClient *ni_client) { + NIInterfaceChooserDialog *dialog; + + dialog = g_object_new (NI_TYPE_INTERFACE_CHOOSER_DIALOG, "ni-client", ni_client, NULL); + + return GTK_WIDGET (dialog); +} + diff --git a/client-gtk/ni-interface-chooser-dialog.h b/client-gtk/ni-interface-chooser-dialog.h new file mode 100644 index 0000000..e8167ec --- /dev/null +++ b/client-gtk/ni-interface-chooser-dialog.h @@ -0,0 +1,78 @@ +/* + * ni-interface-chooser-dialog.h + * This file is part of Network Inador + * + * Copyright (C) 2021 - Gatuno + * + * Network Inador is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Network Inador is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Network Inador; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef __NI_INTERFACE_CHOOSER_DIALOG_H__ +#define __NI_INTERFACE_CHOOSER_DIALOG_H__ + +#include + +#include "ni-client.h" +#include "ni-interface-filter.h" + +G_BEGIN_DECLS + +typedef struct _NIInterfaceChooserDialogClass NIInterfaceChooserDialogClass; +typedef struct _NIInterfaceChooserDialog NIInterfaceChooserDialog; +typedef struct _NIInterfaceChooserDialogPrivate NIInterfaceChooserDialogPrivate; + +/* + * Type declaration. + */ +#define NI_TYPE_INTERFACE_CHOOSER_DIALOG (ni_interface_chooser_dialog_get_type ()) +#define NI_INTERFACE_CHOOSER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NI_TYPE_INTERFACE_CHOOSER_DIALOG, NIInterfaceChooserDialog)) +#define NI_INTERFACE_CHOOSER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NI_TYPE_INTERFACE_CHOOSER_DIALOG, NIInterfaceChooserDialogClass)) +#define NI_IS_INTERFACE_CHOOSER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NI_TYPE_INTERFACE_CHOOSER_DIALOG)) +#define NI_IS_INTERFACE_CHOOSER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NI_TYPE_INTERFACE_CHOOSER_DIALOG)) +#define NI_INTERFACE_CHOOSER_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NI_TYPE_INTERFACE_CHOOSER_DIALOG, NIInterfaceChooserDialogClass)) + +GType ni_interface_chooser_dialog_get_type (void); + +struct _NIInterfaceChooserDialogClass { + GtkDialogClass parent_class; + +}; + +struct _NIInterfaceChooserDialog { + GtkDialog parent_instance; + + NIInterfaceChooserDialogPrivate *priv; +}; + +enum { + NI_INTERFACE_CHOOSER_ORDER_INDEX, + NI_INTERFACE_CHOOSER_ORDER_NAME, + NI_INTERFACE_CHOOSER_ORDER_TYPE +} NIInterfaceChooserOrder; + +/* + * Method definitions. + */ +GtkWidget* ni_interface_chooser_dialog_new (NIClient *ni_client); + +void ni_interface_chooser_dialog_set_filter (NIInterfaceChooserDialog *dialog, NIInterfaceFilter *filter); + +guint ni_interface_chooser_dialog_get_index (NIInterfaceChooserDialog *dialog); + +G_END_DECLS + +#endif /* __NI_INTERFACE_CHOOSER_DIALOG_H__ */ + diff --git a/client-gtk/ni-interface-filter.c b/client-gtk/ni-interface-filter.c new file mode 100644 index 0000000..4fa9964 --- /dev/null +++ b/client-gtk/ni-interface-filter.c @@ -0,0 +1,231 @@ +/* + * ni-interface-filter.c + * This file is part of NetworInador + * + * Copyright (C) 2021 - Félix Arreola Rodríguez + * + * NetworInador is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * NetworInador is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NetworInador; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "ni-interface-filter.h" + +typedef struct _NIInterfaceFilterClass NIInterfaceFilterClass; +typedef struct _FilterRule FilterRule; + +#define NI_INTERFACE_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NI_TYPE_INTERFACE_FILTER, NIInterfaceFilterClass)) +#define NI_IS_INTERFACE_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NI_TYPE_INTERFACE_FILTER)) +#define NI_INTERFACE_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NI_TYPE_INTERFACE_FILTER, NIInterfaceFilterClass)) + +typedef enum { + FILTER_RULE_TYPE, + FILTER_RULE_NAME, + FILTER_RULE_INDEX, + FILTER_RULE_CUSTOM +} FilterRuleType; + +struct _NIInterfaceFilterClass { + GInitiallyUnownedClass parent_class; +}; + +struct _NIInterfaceFilter { + GInitiallyUnowned parent_instance; + + //NIInterfaceFilterType type; + GSList *rules; +}; + +struct _FilterRule { + FilterRuleType type; + gboolean neg; + + union { + guint index; + guint type; + gchar *name; + struct { + NIInterfaceFilterFunc func; + gpointer data; + GDestroyNotify notify; + } custom; + } u; +}; + +static void ni_interface_filter_finalize (GObject *object); + +G_DEFINE_TYPE (NIInterfaceFilter, ni_interface_filter, G_TYPE_INITIALLY_UNOWNED) + +static void ni_interface_filter_init (NIInterfaceFilter *object) { + +} + +static void ni_interface_filter_class_init (NIInterfaceFilterClass *class) { + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + + gobject_class->finalize = ni_interface_filter_finalize; +} + +static void filter_rule_free (FilterRule *rule) { + switch (rule->type) { + case FILTER_RULE_NAME: + g_free (rule->u.name); + break; + case FILTER_RULE_CUSTOM: + if (rule->u.custom.notify) { + rule->u.custom.notify (rule->u.custom.data); + } + break; + //default: + /* No importa */ + } + + g_slice_free (FilterRule, rule); +} + +static void ni_interface_filter_finalize (GObject *object) { + NIInterfaceFilter *filter = NI_INTERFACE_FILTER (object); + + g_slist_free_full (filter->rules, (GDestroyNotify)filter_rule_free); + + //g_free (filter->name); + + G_OBJECT_CLASS (ni_interface_filter_parent_class)->finalize (object); +} + +NIInterfaceFilter * ni_interface_filter_new (void) { + NIInterfaceFilter *obj; + obj = g_object_new (NI_TYPE_INTERFACE_FILTER, NULL); + /*ni_interface_filter_set_type (obj, type);*/ + return obj; +} + +/*void ni_interface_filter_set_type (NIInterfaceFilter *filter, const NIInterfaceFilterType type) { + g_return_if_fail (NI_IS_INTERFACE_FILTER (filter)); + + filter->type = type; +} + +NIInterfaceFilterType ni_interface_filter_get_type (NIInterfaceFilter *filter) { + g_return_if_fail (NI_IS_INTERFACE_FILTER (filter)); + + return filter->type; +}*/ + +static void interface_filter_add_rule (NIInterfaceFilter *filter, FilterRule *rule) { + filter->rules = g_slist_append (filter->rules, rule); +} + +void ni_interface_filter_add_name (NIInterfaceFilter *filter, const gchar *name, gboolean negated) { + FilterRule *rule; + + g_return_if_fail (NI_IS_INTERFACE_FILTER (filter)); + g_return_if_fail (name != NULL); + + rule = g_slice_new (FilterRule); + rule->type = FILTER_RULE_NAME; + rule->neg = negated; + rule->u.name = g_strdup (name); + + interface_filter_add_rule (filter, rule); +} + +void ni_interface_filter_add_index (NIInterfaceFilter *filter, const guint index, gboolean negated) { + FilterRule *rule; + + g_return_if_fail (NI_IS_INTERFACE_FILTER (filter)); + + rule = g_slice_new (FilterRule); + rule->type = FILTER_RULE_INDEX; + rule->neg = negated; + rule->u.index = index; + + interface_filter_add_rule (filter, rule); +} + +void ni_interface_filter_add_type (NIInterfaceFilter *filter, const guint type, gboolean negated) { + FilterRule *rule; + + g_return_if_fail (NI_IS_INTERFACE_FILTER (filter)); + + rule = g_slice_new (FilterRule); + rule->type = FILTER_RULE_TYPE; + rule->neg = negated; + rule->u.type = type; + + interface_filter_add_rule (filter, rule); +} + +void ni_interface_filter_add_custom (NIInterfaceFilter *filter, NIInterfaceFilterFunc func, gpointer data, GDestroyNotify notify) { + FilterRule *rule; + + g_return_if_fail (NI_IS_INTERFACE_FILTER (filter)); + g_return_if_fail (func != NULL); + + rule = g_slice_new (FilterRule); + rule->type = FILTER_RULE_CUSTOM; + rule->neg = FALSE; + rule->u.custom.func = func; + rule->u.custom.data = data; + rule->u.custom.notify = notify; + + interface_filter_add_rule (filter, rule); +} + +gboolean ni_interface_filter_filter (NIInterfaceFilter *filter, const NIInterfaceFilterInfo *filter_info) { + GSList *tmp_list; + FilterRule *rule; + + /* Primero recorrer las reglas negadas */ + for (tmp_list = filter->rules; tmp_list; tmp_list = tmp_list->next) { + rule = tmp_list->data; + + if (rule->neg == FALSE) continue; + switch (rule->type) { + case FILTER_RULE_INDEX: + if (rule->u.index == filter_info->index) return FALSE; + break; + case FILTER_RULE_TYPE: + if (rule->u.type == filter_info->type) return FALSE; + break; + case FILTER_RULE_NAME: + if (g_regex_match_simple (rule->u.name, filter_info->name, 0, 0)) return FALSE; + break; + } + } + + for (tmp_list = filter->rules; tmp_list; tmp_list = tmp_list->next) { + rule = tmp_list->data; + + if (rule->neg == TRUE) continue; + switch (rule->type) { + case FILTER_RULE_INDEX: + if (rule->u.index == filter_info->index) return TRUE; + break; + case FILTER_RULE_TYPE: + if (rule->u.type == filter_info->type) return TRUE; + break; + case FILTER_RULE_NAME: + if (g_regex_match_simple (rule->u.name, filter_info->name, 0, 0)) return TRUE; + break; + case FILTER_RULE_CUSTOM: + if (rule->u.custom.func (filter_info, rule->u.custom.data)) return TRUE; + else return FALSE; + break; + } + } + + return FALSE; +} + diff --git a/client-gtk/ni-interface-filter.h b/client-gtk/ni-interface-filter.h new file mode 100644 index 0000000..36f6daf --- /dev/null +++ b/client-gtk/ni-interface-filter.h @@ -0,0 +1,65 @@ +/* + * ni-interface-filter.h + * This file is part of NetworInador + * + * Copyright (C) 2021 - Félix Arreola Rodríguez + * + * NetworInador is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * NetworInador is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NetworInador; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef __NI_INTERFACE_FILTER_H__ +#define __NI_INTERFACE_FILTER_H__ + +#include + +G_BEGIN_DECLS + +#define NI_TYPE_INTERFACE_FILTER (ni_interface_filter_get_type ()) +#define NI_INTERFACE_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NI_TYPE_INTERFACE_FILTER, NIInterfaceFilter)) +#define NI_IS_INTERFACE_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NI_TYPE_INTERFACE_FILTER)) + +typedef struct _NIInterfaceFilter NIInterfaceFilter; +typedef struct _NIInterfaceFilterInfo NIInterfaceFilterInfo; + +/*enum { + NI_INTERFACE_FILTER_OR, + NI_INTERFACE_FILTER_AND +} NIInterfaceFilterType;*/ + +struct _NIInterfaceFilterInfo { + guint index; + const gchar *name; + guint type; + guint master; +}; + +typedef gboolean (*NIInterfaceFilterFunc) (const NIInterfaceFilterInfo *filter_info, gpointer data); + +GType ni_interface_filter_get_type (void) G_GNUC_CONST; + +NIInterfaceFilter * ni_interface_filter_new (void); + +void ni_interface_filter_add_name (NIInterfaceFilter *filter, const gchar *name, gboolean negated); +void ni_interface_filter_add_index (NIInterfaceFilter *filter, const guint index, gboolean negated); +void ni_interface_filter_add_type (NIInterfaceFilter *filter, const guint type, gboolean negated); +void ni_interface_filter_add_custom (NIInterfaceFilter *filter, NIInterfaceFilterFunc func, gpointer data, GDestroyNotify notify); + +gboolean ni_interface_filter_filter (NIInterfaceFilter *filter, const NIInterfaceFilterInfo *filter_info); + +G_END_DECLS + +#endif /* __NI_INTERFACE_FILTER_H__ */ + diff --git a/client-gtk/ni-interface.c b/client-gtk/ni-interface.c index 6e3ac13..fe3c080 100644 --- a/client-gtk/ni-interface.c +++ b/client-gtk/ni-interface.c @@ -70,6 +70,9 @@ enum { DELETE_IP, + ADDED_TO_BRIDGE, + REMOVED_FROM_BRIDGE, + LAST_SIGNAL }; @@ -319,6 +322,28 @@ static void ni_interface_class_init (NIInterfaceClass *klass) { G_TYPE_NONE, 1, NI_TYPE_IP); + + signals[ADDED_TO_BRIDGE] = g_signal_new ("added-to-bridge", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + signals[REMOVED_FROM_BRIDGE] = g_signal_new ("removed-from-bridge", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, + G_TYPE_UINT); } static gint ni_interface_locate_ip (gconstpointer *a, gconstpointer *b) { @@ -356,7 +381,7 @@ static gint ni_interface_locate_ip (gconstpointer *a, gconstpointer *b) { static void ni_interface_process_interface_update (NIInterface *ni_interface, gpointer data, guint size) { unsigned char *buffer = (unsigned char *) data; - uint32_t master_index, mtu; + uint32_t master_index, mtu, previous_m_index; uint16_t flags; guint name_size; char iface_name[128]; @@ -379,10 +404,16 @@ static void ni_interface_process_interface_update (NIInterface *ni_interface, gp ni_interface->priv->name = g_strdup (iface_name); g_object_notify_by_pspec (G_OBJECT (ni_interface), obj_properties[PROP_NAME]); } - + if (ni_interface->priv->master_index != master_index) { + previous_m_index = ni_interface->priv->master_index; ni_interface->priv->master_index = master_index; g_object_notify_by_pspec (G_OBJECT (ni_interface), obj_properties[PROP_MASTER_INDEX]); + if (previous_m_index == 0) { + g_signal_emit (ni_interface, signals[ADDED_TO_BRIDGE], 0, master_index); + } else { + g_signal_emit (ni_interface, signals[REMOVED_FROM_BRIDGE], 0, previous_m_index); + } } if (ni_interface->priv->mtu != mtu) { @@ -614,6 +645,26 @@ guint ni_interface_get_mtu (NIInterface *ni_interface) { return ni_interface->priv->mtu; } +guint ni_interface_get_master (NIInterface *ni_interface) { + g_return_val_if_fail (NI_IS_INTERFACE (ni_interface), 0); + return ni_interface->priv->master_index; +} + +void ni_interface_clear_master (NIInterface *ni_interface) { + g_return_if_fail (NI_IS_INTERFACE (ni_interface)); + + if (ni_interface->priv->master_index == 0) return; + ni_client_ask_interface_clear_master (ni_interface->priv->ni_client, ni_interface); +} + +void ni_interface_set_master (NIInterface *ni_interface, NIInterface *master) { + g_return_if_fail (NI_IS_INTERFACE (ni_interface)); + g_return_if_fail (NI_IS_INTERFACE (master)); + + if (ni_interface->priv->master_index != 0) return; + ni_client_ask_interface_set_master (ni_interface->priv->ni_client, ni_interface, master); +} + void ni_interface_set_down (NIInterface *ni_interface) { g_return_if_fail (NI_IS_INTERFACE (ni_interface)); diff --git a/client-gtk/ni-interface.h b/client-gtk/ni-interface.h index e9bbbd0..e166351 100644 --- a/client-gtk/ni-interface.h +++ b/client-gtk/ni-interface.h @@ -71,6 +71,7 @@ guint ni_interface_get_iface_type (NIInterface *ni_interface); gboolean ni_interface_is_wireless (NIInterface *ni_interface); guint ni_interface_get_mtu (NIInterface *ni_interface); void ni_interface_set_mtu (NIInterface *ni_interface, guint mtu); +guint ni_interface_get_master (NIInterface *ni_interface); NIClient *ni_interface_get_client (NIInterface *ni_interface); const GList *ni_interface_get_ip_list (NIInterface *ni_interface); guint ni_interface_get_flags (NIInterface *ni_interface); diff --git a/client-gtk/ni-window-interface.c b/client-gtk/ni-window-interface.c index 6b90bef..026d68a 100644 --- a/client-gtk/ni-window-interface.c +++ b/client-gtk/ni-window-interface.c @@ -38,19 +38,26 @@ #include "ni-interface.h" #include "ni-ip.h" #include "ni-ip-add-dialog.h" +#include "../src/link-types.h" +#include "ni-interface-chooser-dialog.h" +#include "ni-interface-filter.h" struct _NIWindowInterfacePrivate { NIInterface *ni_interface; GtkWidget *vbox, *notebook; - GtkWidget *vbox_ipv4, *vbox_ipv6, *vbox_info; - GtkWidget *info_name, *info_updown, *info_mtu; + GtkWidget *vbox_ipv4, *vbox_ipv6, *vbox_info, *vbox_bridges; + GtkWidget *info_name, *info_updown, *info_mtu, *info_slave; GtkWidget *button_name_apply, *button_name_revert; GtkWidget *button_mtu_apply, *button_mtu_revert; GtkWidget *button_up, *button_down; - GtkListStore *ipv4_store, *ipv6_store; - GtkWidget *del_ipv4_button, *del_ipv6_button; - GtkWidget *tree_ipv4, *tree_ipv6; + GtkWidget *button_slave_remove, *button_master_add; + GtkListStore *ipv4_store, *ipv6_store, *bridge_ports_store; + GtkWidget *del_ipv4_button, *del_ipv6_button, *del_bport_button; + GtkWidget *tree_ipv4, *tree_ipv6, *tree_bridge_ports; + + /* Cosas de los bridges */ + }; enum { @@ -67,10 +74,19 @@ enum { NUM_IP_STORE_COLS }; +enum { + BRIDGE_PORT_STORE_COL_IFACE_NAME, + BRIDGE_PORT_STORE_COL_OBJECT, + + NUM_BRIDGE_PORT_STORE_COLS +}; + static void ni_window_interface_addr_added_cb (NIWindowInterface *window_iface, NIIP *ni_ip); void ni_window_interface_update_state_from_flags (NIWindowInterface *window_iface); +void ni_window_interface_update_state_from_master (NIWindowInterface *window_iface); static void ni_window_interface_name_cancel_cb (GtkWidget *widget, gpointer data); static void ni_window_interface_mtu_cancel_cb (GtkWidget *widget, gpointer data); +static void prepare_bridge_tab (NIWindowInterface *window_iface); G_DEFINE_TYPE_WITH_PRIVATE (NIWindowInterface, ni_window_interface, GTK_TYPE_WINDOW) @@ -205,6 +221,70 @@ static void ni_window_interface_addr_v6_button_add_cb (GtkWidget *widget, gpoint ni_window_interface_addr_button_add_cb (widget, AF_INET6, data); } +static void has_bport_selected_cb (GtkTreeSelection *selection, gpointer user_data) { + NIWindowInterface *window_iface = (NIWindowInterface *) user_data; + + if (gtk_tree_selection_count_selected_rows (selection) > 0) { + gtk_widget_set_sensitive (window_iface->priv->del_bport_button, TRUE); + } else { + gtk_widget_set_sensitive (window_iface->priv->del_bport_button, FALSE); + } +} + +static gboolean ni_window_interface_filter_only_master_zero (const NIInterfaceFilterInfo *filter_info, gpointer data) { + return (filter_info->master == 0); +} + +static void ni_window_interface_bport_button_add_cb (GtkWidget *widget, gpointer data) { + NIWindowInterface *window_iface = (NIWindowInterface *) data; + NIInterfaceFilter *iface_filter; + int res; + NIInterface *slave_iface; + guint new_slave; + NIClient *ni_client; + + ni_client = ni_interface_get_client (window_iface->priv->ni_interface); + GtkWidget *dialog = ni_interface_chooser_dialog_new (ni_client); + + iface_filter = ni_interface_filter_new (); + /* Ella no puede ser su propio esclavo */ + ni_interface_filter_add_index (iface_filter, ni_interface_get_index (window_iface->priv->ni_interface), TRUE); + + /* Agregar un filtro especial que solo permita interfaces que tengan por master == 0 */ + ni_interface_filter_add_custom (iface_filter, ni_window_interface_filter_only_master_zero, NULL, NULL); + + ni_interface_chooser_dialog_set_filter (NI_INTERFACE_CHOOSER_DIALOG (dialog), iface_filter); + + res = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (res == GTK_RESPONSE_OK) { + new_slave = ni_interface_chooser_dialog_get_index (NI_INTERFACE_CHOOSER_DIALOG (dialog)); + + slave_iface = ni_client_get_interface_by_index (ni_client, new_slave); + + if (slave_iface != NULL) { + ni_client_ask_interface_set_master (ni_client, slave_iface, window_iface->priv->ni_interface); + } + } + gtk_widget_destroy (dialog); +} + +static void ni_window_interface_bport_button_del_cb (GtkWidget *widget, gpointer data) { + NIWindowInterface *window_iface = (NIWindowInterface *) data; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreeSelection *selection; + NIInterface *slave; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window_iface->priv->tree_bridge_ports)); + + gtk_tree_selection_get_selected (selection, &model, &iter); + + gtk_tree_model_get (model, &iter, BRIDGE_PORT_STORE_COL_OBJECT, &slave, -1); + + ni_client_ask_interface_clear_master (ni_interface_get_client (window_iface->priv->ni_interface), slave); +} + static void ni_window_interface_addr_v4_button_del_cb (GtkWidget *widget, gpointer data) { ni_window_interface_addr_button_del_cb (widget, AF_INET, data); } @@ -312,6 +392,49 @@ static void ni_window_interface_up_cb (GtkWidget *widget, gpointer data) { ni_interface_set_up (window_iface->priv->ni_interface); } +static void ni_window_interface_make_slave_click_cb (GtkWidget *widget, gpointer data) { + NIWindowInterface *window_iface = (NIWindowInterface *) data; + NIInterfaceFilter *iface_filter; + int res; + NIInterface *master_iface; + guint new_master; + NIClient *ni_client; + + ni_client = ni_interface_get_client (window_iface->priv->ni_interface); + GtkWidget *dialog = ni_interface_chooser_dialog_new (ni_client); + + iface_filter = ni_interface_filter_new (); + /* Ella no puede ser su propia master */ + ni_interface_filter_add_index (iface_filter, ni_interface_get_index (window_iface->priv->ni_interface), TRUE); + + /* TODO: ¿Solo los bridges pueden ser maestros? */ + ni_interface_filter_add_type (iface_filter, NI_LINK_TYPE_BRIDGE, FALSE); + + ni_interface_chooser_dialog_set_filter (NI_INTERFACE_CHOOSER_DIALOG (dialog), iface_filter); + + res = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (res == GTK_RESPONSE_OK) { + new_master = ni_interface_chooser_dialog_get_index (NI_INTERFACE_CHOOSER_DIALOG (dialog)); + + master_iface = ni_client_get_interface_by_index (ni_client, new_master); + + if (master_iface != NULL) { + ni_client_ask_interface_set_master (ni_client, window_iface->priv->ni_interface, master_iface); + } + } + gtk_widget_destroy (dialog); +} + +static void ni_window_interface_clear_slave_click_cb (GtkWidget *widget, gpointer data) { + NIWindowInterface *window_iface = (NIWindowInterface *) data; + NIClient *ni_client; + + ni_client = ni_interface_get_client (window_iface->priv->ni_interface); + + ni_client_ask_interface_clear_master (ni_client, window_iface->priv->ni_interface); +} + /* Eventos que vienen del network-inador */ static void ni_window_interface_addr_added_cb (NIWindowInterface *window_iface, NIIP *ni_ip) { GtkTreeIter iter; @@ -403,6 +526,83 @@ static void ni_window_interface_changed_flags_cb (GObject *object, GParamSpec *s ni_window_interface_update_state_from_flags (window_iface); } +static void ni_window_interface_changed_master_cb (GObject *object, GParamSpec *sp, gpointer data) { + NIWindowInterface *window_iface = NI_WINDOW_INTERFACE (data); + + ni_window_interface_update_state_from_master (window_iface); +} + +/* Eventos de bridges */ +static gboolean ni_window_interface_check_bport_to_delete_foreach_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); +static void ni_window_interface_removed_from_bridge_cb (NIInterface *ni_interface, guint old_master, gpointer data); + +static void ni_window_interface_added_to_bridge_cb (NIInterface *ni_interface, guint new_master, gpointer data) { + NIWindowInterface *window_iface = NI_WINDOW_INTERFACE (data); + GtkTreeIter iter; + guint our_index; + + our_index = ni_interface_get_index (window_iface->priv->ni_interface); + if (new_master == our_index) { + /* Esta interfaz acaba de ser esclavizada a nuestra interfaz */ + gtk_list_store_insert (window_iface->priv->bridge_ports_store, &iter, -1); + gtk_list_store_set (window_iface->priv->bridge_ports_store, &iter, BRIDGE_PORT_STORE_COL_IFACE_NAME, ni_interface_get_name (ni_interface), BRIDGE_PORT_STORE_COL_OBJECT, ni_interface, -1); + } +} + +static void ni_window_interface_removed_from_bridge_cb (NIInterface *ni_interface, guint old_master, gpointer data) { + NIWindowInterface *window_iface = NI_WINDOW_INTERFACE (data); + guint our_index; + + our_index = ni_interface_get_index (window_iface->priv->ni_interface); + + if (old_master == our_index) { + gtk_tree_model_foreach (GTK_TREE_MODEL (window_iface->priv->bridge_ports_store), ni_window_interface_check_bport_to_delete_foreach_cb, ni_interface); + } +} + +static void ni_window_interface_new_interface_cb (NIClient *ni_client, NIInterface *ni_interface, gpointer data) { + NIWindowInterface *window_iface = NI_WINDOW_INTERFACE (data); + guint our_index, master_other; + GtkTreeIter iter; + + our_index = ni_interface_get_index (window_iface->priv->ni_interface); + + g_signal_connect (ni_interface, "added-to-bridge", G_CALLBACK (ni_window_interface_added_to_bridge_cb), window_iface); + g_signal_connect (ni_interface, "removed-from-bridge", G_CALLBACK (ni_window_interface_removed_from_bridge_cb), window_iface); + + /* Estoy esperando esta señal porque la interfaz podría ser esclava mía */ + master_other = ni_interface_get_master (ni_interface); + if (master_other == our_index) { + /* Esta interfaz está esclavizada a nuestra interfaz */ + gtk_list_store_insert (window_iface->priv->bridge_ports_store, &iter, -1); + gtk_list_store_set (window_iface->priv->bridge_ports_store, &iter, BRIDGE_PORT_STORE_COL_IFACE_NAME, ni_interface_get_name (ni_interface), BRIDGE_PORT_STORE_COL_OBJECT, ni_interface, -1); + } +} + +static gboolean ni_window_interface_check_bport_to_delete_foreach_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { + NIInterface *deleted_iface, *iface; + + deleted_iface = (NIInterface *) data; + + gtk_tree_model_get (model, iter, BRIDGE_PORT_STORE_COL_OBJECT, &iface, -1); + + if (ni_interface_get_index (deleted_iface) == ni_interface_get_index (iface)) { + gtk_list_store_remove (GTK_LIST_STORE (model), iter); + + return TRUE; + } + + return FALSE; +} + +static void ni_window_interface_del_interface_cb (NIClient *ni_client, NIInterface *ni_interface, gpointer data) { + NIWindowInterface *window_iface = NI_WINDOW_INTERFACE (data); + /* Espero esta señal porque podría ser que alguno de mis interfaces esclavizadas en el bridge, se haya eliminado */ + + gtk_tree_model_foreach (GTK_TREE_MODEL (window_iface->priv->bridge_ports_store), ni_window_interface_check_bport_to_delete_foreach_cb, ni_interface); +} + +/* Eventos de actualización del estado de la propia interfaz */ void ni_window_interface_update_state_from_flags (NIWindowInterface *window_iface) { guint flags; @@ -421,6 +621,54 @@ void ni_window_interface_update_state_from_flags (NIWindowInterface *window_ifac } } +void ni_window_interface_update_state_from_master (NIWindowInterface *window_iface) { + guint master_index; + NIInterface *other_iface; + + master_index = ni_interface_get_master (window_iface->priv->ni_interface); + + if (master_index == 0) { + /* No hay master de esta intefaz */ + gtk_label_set_text (GTK_LABEL (window_iface->priv->info_slave), ""); + gtk_widget_set_sensitive (window_iface->priv->button_slave_remove, FALSE); + gtk_widget_set_sensitive (window_iface->priv->button_master_add, TRUE); + } else { + other_iface = ni_client_get_interface_by_index (ni_interface_get_client (window_iface->priv->ni_interface), master_index); + + gtk_label_set_text (GTK_LABEL (window_iface->priv->info_slave), ni_interface_get_name (other_iface)); + gtk_widget_set_sensitive (window_iface->priv->button_slave_remove, TRUE); + gtk_widget_set_sensitive (window_iface->priv->button_master_add, FALSE); + } +} + +static void ni_window_interface_aditional_setup_for_bridge (NIWindowInterface *window_iface) { + NIClient *ni_client; + GList *all_ifaces, *g; + NIInterface *other_iface; + guint our_index, other_index; + GtkTreeIter iter; + + /* Crear la tab de los bridges */ + prepare_bridge_tab (window_iface); + + ni_client = ni_interface_get_client (window_iface->priv->ni_interface); + /* Conectar la señal de agregado al bridge y la de nueva interfaz */ + g_signal_connect (ni_client, "new-interface", G_CALLBACK (ni_window_interface_new_interface_cb), window_iface); + + g_signal_connect (ni_client, "delete-interface", G_CALLBACK (ni_window_interface_del_interface_cb), window_iface); + + /* Listar todas las interfaces que tengan listado como esclavo mi interfaz */ + all_ifaces = ni_client_get_list_interfaces (ni_client); + + our_index = ni_interface_get_index (window_iface->priv->ni_interface); + for (g = all_ifaces; g != NULL; g = g->next) { + other_iface = (NIInterface *) g->data; + + if (window_iface->priv->ni_interface == other_iface) continue; /* No me interesa mi propia interfaz */ + ni_window_interface_new_interface_cb (ni_client, other_iface, window_iface); + } +} + /* Construcción del objeto */ static void ni_window_interface_constructed (GObject *obj) { GObjectClass *parent_class = G_OBJECT_CLASS (g_type_class_peek (GTK_TYPE_WINDOW)); @@ -439,6 +687,13 @@ static void ni_window_interface_constructed (GObject *obj) { gtk_window_set_title (GTK_WINDOW (obj), name); g_free (name); + /* Conectar la señal de IP agregada y la señal de IP eliminada */ + g_signal_connect (window_iface->priv->ni_interface, "new-ip", G_CALLBACK (ni_window_interface_new_ip_cb), window_iface); + g_signal_connect (window_iface->priv->ni_interface, "delete-ip", G_CALLBACK (ni_window_interface_delete_ip_cb), window_iface); + g_signal_connect (window_iface->priv->ni_interface, "notify::name", G_CALLBACK (ni_window_interface_changed_name_cb), window_iface); + g_signal_connect (window_iface->priv->ni_interface, "notify::flags", G_CALLBACK (ni_window_interface_changed_flags_cb), window_iface); + g_signal_connect (window_iface->priv->ni_interface, "notify::master-index", G_CALLBACK (ni_window_interface_changed_master_cb), window_iface); + /* TODO: Recorrer las ips, y procesar la información */ ip_list = ni_interface_get_ip_list (window_iface->priv->ni_interface); @@ -448,15 +703,14 @@ static void ni_window_interface_constructed (GObject *obj) { ni_window_interface_addr_added_cb (window_iface, ni_ip); } - /* Conectar la señal de IP agregada y la señal de IP eliminada */ - g_signal_connect (window_iface->priv->ni_interface, "new-ip", G_CALLBACK (ni_window_interface_new_ip_cb), window_iface); - g_signal_connect (window_iface->priv->ni_interface, "delete-ip", G_CALLBACK (ni_window_interface_delete_ip_cb), window_iface); - g_signal_connect (window_iface->priv->ni_interface, "notify::name", G_CALLBACK (ni_window_interface_changed_name_cb), window_iface); - g_signal_connect (window_iface->priv->ni_interface, "notify::flags", G_CALLBACK (ni_window_interface_changed_flags_cb), window_iface); - gtk_entry_set_text (GTK_ENTRY (window_iface->priv->info_name), ni_interface_get_name (window_iface->priv->ni_interface)); gtk_spin_button_set_value (GTK_SPIN_BUTTON (window_iface->priv->info_mtu), ni_interface_get_mtu(window_iface->priv->ni_interface)); ni_window_interface_update_state_from_flags (window_iface); + ni_window_interface_update_state_from_master (window_iface); + + if (ni_interface_get_iface_type (window_iface->priv->ni_interface) == NI_LINK_TYPE_BRIDGE) { + ni_window_interface_aditional_setup_for_bridge (window_iface); + } } static void ni_window_interface_dispose (GObject *obj) { @@ -533,7 +787,7 @@ static void ni_window_interface_class_init (NIWindowInterfaceClass *klass) { g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties); } -GtkWidget *ni_window_interface_create_tree (GtkListStore *store, GtkWidget **tree) { +GtkWidget *ni_window_interface_create_tree_for_ip (GtkListStore *store, GtkWidget **tree) { GtkWidget *scrolled; GtkAdjustment *h, *v; GtkTreeViewColumn *column; @@ -556,27 +810,35 @@ GtkWidget *ni_window_interface_create_tree (GtkListStore *store, GtkWidget **tre return scrolled; } -static void ni_window_interface_init (NIWindowInterface *window_iface) { - NIWindowInterfacePrivate *priv = ni_window_interface_get_instance_private (window_iface); - window_iface->priv = priv; - GtkWindow *window = GTK_WINDOW (window_iface); - GtkWidget *hbox, *label, *button, *image; - GtkWidget *vbox, *scrolled, *vbox2; - GtkTreeSelection *selection; +GtkWidget *ni_window_interface_create_tree_for_bridge_ports (GtkListStore *store, GtkWidget **tree) { + GtkWidget *scrolled; + GtkAdjustment *h, *v; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + *tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); + + h = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (*tree)); + v = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (*tree)); + + scrolled = gtk_scrolled_window_new (h, v); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Interfaz", renderer, "text", BRIDGE_PORT_STORE_COL_IFACE_NAME, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (*tree), column); + + gtk_container_add (GTK_CONTAINER (scrolled), *tree); + + return scrolled; +} + +static void prepare_info_tab (NIWindowInterface *window_iface) { + GtkWidget *vbox, *hbox, *label; GtkSizeGroup *size_l; + GtkWindow *window = GTK_WINDOW (window_iface); + NIWindowInterfacePrivate *priv = window_iface->priv; - /* initialize all public and private members to reasonable default values. - * They are all automatically initialized to 0 to begin with. */ - priv->ni_interface = NULL; - gtk_window_set_title (window, "Interfaz"); - - priv->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); - gtk_container_add (GTK_CONTAINER (window), priv->vbox); - - priv->notebook = gtk_notebook_new (); - gtk_box_pack_start (GTK_BOX (priv->vbox), priv->notebook, TRUE, TRUE, 0); - - /* Página de la información general de la interfaz */ priv->vbox_info = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); label = gtk_label_new ("Info"); gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook), priv->vbox_info, label); @@ -588,7 +850,7 @@ static void ni_window_interface_init (NIWindowInterface *window_iface) { hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); gtk_box_pack_start (GTK_BOX (priv->vbox_info), hbox, FALSE, FALSE, 0); - label = gtk_label_new ("Interfaz name:"); + label = gtk_label_new ("Nombre de la interfaz:"); gtk_label_set_xalign (GTK_LABEL (label), 0.0); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_size_group_add_widget (size_l, label); @@ -619,7 +881,6 @@ static void ni_window_interface_init (NIWindowInterface *window_iface) { gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_size_group_add_widget (size_l, label); - priv->button_mtu_revert = gtk_button_new_from_icon_name ("view-refresh", GTK_ICON_SIZE_BUTTON); gtk_widget_set_sensitive (priv->button_mtu_revert, FALSE); g_signal_connect (priv->button_mtu_revert, "clicked", G_CALLBACK (ni_window_interface_mtu_cancel_cb), window_iface); @@ -658,6 +919,104 @@ static void ni_window_interface_init (NIWindowInterface *window_iface) { priv->info_updown = gtk_label_new (""); gtk_box_pack_end (GTK_BOX (hbox), priv->info_updown, FALSE, FALSE, 0); + /* Presenta si la interfaz está esclavizada o no */ + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_pack_start (GTK_BOX (priv->vbox_info), hbox, FALSE, FALSE, 0); + + label = gtk_label_new ("Interfaz maestra:"); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_size_group_add_widget (size_l, label); + + priv->button_slave_remove = gtk_button_new_from_icon_name ("edit-delete", GTK_ICON_SIZE_BUTTON); + g_signal_connect (priv->button_slave_remove, "clicked", G_CALLBACK (ni_window_interface_clear_slave_click_cb), window_iface); + gtk_box_pack_end (GTK_BOX (hbox), priv->button_slave_remove, FALSE, FALSE, 0); + + priv->button_master_add = gtk_button_new_from_icon_name ("insert-link", GTK_ICON_SIZE_BUTTON); + g_signal_connect (priv->button_master_add, "clicked", G_CALLBACK (ni_window_interface_make_slave_click_cb), window_iface); + gtk_box_pack_end (GTK_BOX (hbox), priv->button_master_add, FALSE, FALSE, 0); + + priv->info_slave = gtk_label_new (""); + gtk_box_pack_end (GTK_BOX (hbox), priv->info_slave, FALSE, FALSE, 0); +} + +static void prepare_bridge_tab (NIWindowInterface *window_iface) { + GtkWidget *vbox, *hbox, *vbox2; + GtkSizeGroup *size_l; + GtkWindow *window = GTK_WINDOW (window_iface); + NIWindowInterfacePrivate *priv = window_iface->priv; + GtkWidget *scrolled; + GtkWidget *button, *label, *label_tab; + GtkTreeSelection *selection; + + priv->vbox_bridges = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); + label_tab = gtk_label_new ("Bridge"); + gtk_notebook_insert_page (GTK_NOTEBOOK (priv->notebook), priv->vbox_bridges, label_tab, 1); + gtk_container_set_border_width (GTK_CONTAINER (priv->vbox_bridges), 5); + + size_l = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + /* TODO: Poner aquí las labels de información de bridges */ + /* Preparar el list store */ + priv->bridge_ports_store = gtk_list_store_new (NUM_BRIDGE_PORT_STORE_COLS, G_TYPE_STRING, G_TYPE_POINTER); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); + gtk_box_pack_start (GTK_BOX (priv->vbox_bridges), vbox, TRUE, TRUE, 5); + + label = gtk_label_new ("Interfaces en el bridge:"); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); + + scrolled = ni_window_interface_create_tree_for_bridge_ports (priv->bridge_ports_store, &priv->tree_bridge_ports); + gtk_box_pack_start (GTK_BOX (hbox), scrolled, TRUE, TRUE, 0); + + /* Botonera del tree view */ + vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); + gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0); + + button = gtk_button_new_from_icon_name ("list-add", GTK_ICON_SIZE_LARGE_TOOLBAR); + gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0); + g_signal_connect (button, "clicked", G_CALLBACK (ni_window_interface_bport_button_add_cb), window_iface); + + priv->del_bport_button = gtk_button_new_from_icon_name ("list-remove", GTK_ICON_SIZE_LARGE_TOOLBAR); + gtk_box_pack_start (GTK_BOX (vbox2), priv->del_bport_button, FALSE, FALSE, 0); + g_signal_connect (priv->del_bport_button, "clicked", G_CALLBACK (ni_window_interface_bport_button_del_cb), window_iface); + gtk_widget_set_sensitive (priv->del_bport_button, FALSE); + + /* Conectar la señal de cambio de selección del tree view */ + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_bridge_ports)); + g_signal_connect (selection, "changed", G_CALLBACK (has_bport_selected_cb), window_iface); + + gtk_widget_show_all (priv->vbox_bridges); + gtk_widget_show (label_tab); +} + +static void ni_window_interface_init (NIWindowInterface *window_iface) { + NIWindowInterfacePrivate *priv = ni_window_interface_get_instance_private (window_iface); + window_iface->priv = priv; + GtkWindow *window = GTK_WINDOW (window_iface); + GtkWidget *hbox, *label, *button, *image; + GtkWidget *vbox, *scrolled, *vbox2; + GtkTreeSelection *selection; + GtkSizeGroup *size_l; + + /* initialize all public and private members to reasonable default values. + * They are all automatically initialized to 0 to begin with. */ + priv->ni_interface = NULL; + gtk_window_set_title (window, "Interfaz"); + + priv->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); + gtk_container_add (GTK_CONTAINER (window), priv->vbox); + + priv->notebook = gtk_notebook_new (); + gtk_box_pack_start (GTK_BOX (priv->vbox), priv->notebook, TRUE, TRUE, 0); + + /* Página de la información general de la interfaz */ + prepare_info_tab (window_iface); + /* Página de IPv4 */ priv->vbox_ipv4 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); label = gtk_label_new ("IPv4"); @@ -677,7 +1036,7 @@ static void ni_window_interface_init (NIWindowInterface *window_iface) { hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); - scrolled = ni_window_interface_create_tree (priv->ipv4_store, &priv->tree_ipv4); + scrolled = ni_window_interface_create_tree_for_ip (priv->ipv4_store, &priv->tree_ipv4); gtk_box_pack_start (GTK_BOX (hbox), scrolled, TRUE, TRUE, 0); /* Botonera del tree view */ @@ -716,7 +1075,7 @@ static void ni_window_interface_init (NIWindowInterface *window_iface) { hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); - scrolled = ni_window_interface_create_tree (priv->ipv6_store, &priv->tree_ipv6); + scrolled = ni_window_interface_create_tree_for_ip (priv->ipv6_store, &priv->tree_ipv6); gtk_box_pack_start (GTK_BOX (hbox), scrolled, TRUE, TRUE, 0); /* Botonera del tree view */ diff --git a/src/bridge.c b/src/bridge.c index f770f26..6c01e5e 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -71,6 +71,17 @@ int interfaces_bridge_create (NetworkInadorHandle *handle, const char *name) { return -1; } + /* Anexar el nombre, si es que especificaron uno */ + if (name != NULL) { + ret = nla_put (msg, IFLA_IFNAME, strlen (name) + 1, name); + + if (ret != 0) { + nlmsg_free (msg); + + return -1; + } + } + info = nla_nest_start (msg, IFLA_LINKINFO); if (info == NULL) { diff --git a/src/interfaces.c b/src/interfaces.c index 2afd479..5029703 100644 --- a/src/interfaces.c +++ b/src/interfaces.c @@ -54,7 +54,7 @@ static uint32_t interfaces_check_link_type (Interface *iface) { for (g = 0; g < (sizeof (linktypes) / sizeof (linktypes[0])); g++) { if (linktypes[g].rtnl_type == NULL) continue; if (strcmp (iface->rtnl_type, linktypes[g].rtnl_type) == 0) { - return linktypes->link_type; + return linktypes[g].link_type; } } @@ -135,7 +135,9 @@ static int _interfaces_receive_message_interface (struct nl_msg *msg, void *arg, } printf ("Interface %d agregada a la interfaz %d (bridge)\n", iface->index, iface->master_index); - /* TODO: Generar EVENTO AQUI */ + was_update = 1; + manager_send_event_interface_update (handle, iface); + return NL_SKIP; } @@ -314,8 +316,11 @@ int interface_receive_message_dellink (struct nl_msg *msg, void *arg) { } } - printf ("Interface %d eliminada de la interfaz %d (bridge)\n", iface->index, iface->master_index); + printf ("Interface %d se sacó de su bridge\n", iface->index); + /* Generar EVENTO AQUI */ + manager_send_event_interface_update (handle, iface); + return NL_SKIP; } diff --git a/src/ip-address.c b/src/ip-address.c index ad4be95..14aac4a 100644 --- a/src/ip-address.c +++ b/src/ip-address.c @@ -418,7 +418,7 @@ int ip_address_del_ip (NetworkInadorHandle *handle, int index, IPAddr *ip_addr) ret |= nla_put (msg, IFA_LOCAL, family_size, &ip_addr->local_addr); } if (ip_addr->has_brd) { - ret |= nla_put (msg, IFA_LOCAL, family_size, &ip_addr->brd_addr); + ret |= nla_put (msg, IFA_BROADCAST, family_size, &ip_addr->brd_addr); } if (ret != 0) { diff --git a/src/main.c b/src/main.c index 8db3b16..8778447 100644 --- a/src/main.c +++ b/src/main.c @@ -36,6 +36,8 @@ #include #include +#include + #include "common.h" #include "manager.h" #include "interfaces.h" @@ -93,6 +95,8 @@ static void _main_setup_signal (void *loop) { } if (sigterm_pipe_fds[0] != -1) { + fcntl (sigterm_pipe_fds[0], F_SETFD, 1); + fcntl (sigterm_pipe_fds[1], F_SETFD, 1); GIOChannel *io; io = g_io_channel_unix_new (sigterm_pipe_fds[0]); diff --git a/src/manager.c b/src/manager.c index 235613e..73f2a88 100644 --- a/src/manager.c +++ b/src/manager.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -39,6 +40,7 @@ #include "common.h" #include "interfaces.h" #include "ip-address.h" +#include "bridge.h" #include "network-inador-manager.h" #define COMMAND_SOCKET_PATH "/tmp/network-inador.socket" @@ -550,6 +552,74 @@ void _manager_execute_delete_ip (ManagerClientInfo *manager_client, unsigned cha } } +void _manager_execute_create_bridge (ManagerClientInfo *manager_client, unsigned char *buffer, int buffer_len) { + int ret; + int name_len; + unsigned char name[IFNAMSIZ]; + + if (buffer_len < 3) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_CREATE_BRIDGE); + return; + } + + name_len = buffer[2]; + if (name_len == 0 || name_len >= IFNAMSIZ) { + _manager_send_error (manager_client, NET_INADOR_ERROR_BAD_STRING, NET_INADOR_COMMAND_CREATE_BRIDGE); + return; + } + + if (name_len + 3 < buffer_len) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_CREATE_BRIDGE); + return; + } + + memcpy (name, &buffer[3], name_len); + if (name[0] == 0) { + _manager_send_error (manager_client, NET_INADOR_ERROR_BAD_STRING, NET_INADOR_COMMAND_CREATE_BRIDGE); + return; + } + name[name_len] = 0; + + ret = interfaces_bridge_create (manager_client->manager->handle, name); + + if (ret == 0) { + /* OK */ + _manager_send_executed (manager_client); + } else { + _manager_send_error (manager_client, NET_INADOR_ERROR_NOT_EXECUTED, NET_INADOR_COMMAND_CREATE_BRIDGE); + } +} + +void _manager_execute_set_or_clear_master (ManagerClientInfo *manager_client, unsigned char *buffer, int buffer_len, gboolean is_clear) { + Interface *iface, *master; + int ret; + + if (buffer_len < 6 && is_clear) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_CLEAR_MASTER); + return; + } else if (buffer_len < 10 && is_clear == FALSE) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_SET_MASTER); + return; + } + + iface = _manager_fetch_interface (manager_client, &buffer[2], (is_clear ? NET_INADOR_COMMAND_CLEAR_MASTER : NET_INADOR_COMMAND_SET_MASTER)); + + if (is_clear == FALSE) { + master = _manager_fetch_interface (manager_client, &buffer[6], NET_INADOR_COMMAND_SET_MASTER); + + ret = interfaces_bridge_set_master_interface (manager_client->manager->handle, master->index, iface->index); + } else { + ret = interfaces_bridge_remove_master_interface (manager_client->manager->handle, iface->index); + } + + if (ret == 0) { + /* OK */ + _manager_send_executed (manager_client); + } else { + _manager_send_error (manager_client, NET_INADOR_ERROR_NOT_EXECUTED, (is_clear ? NET_INADOR_COMMAND_CLEAR_MASTER : NET_INADOR_COMMAND_SET_MASTER)); + } +} + static void _manager_handle_set_event_mask (ManagerClientInfo *manager_client, unsigned char *buffer, int buffer_len) { uint32_t events; @@ -641,6 +711,15 @@ static gboolean _manager_client_data (GIOChannel *source, GIOCondition condition case NET_INADOR_COMMAND_SET_EVENT_MASK: _manager_handle_set_event_mask (manager_client, buffer, bytes); break; + case NET_INADOR_COMMAND_CREATE_BRIDGE: + _manager_execute_create_bridge (manager_client, buffer, bytes); + break; + case NET_INADOR_COMMAND_CLEAR_MASTER: + _manager_execute_set_or_clear_master (manager_client, buffer, bytes, TRUE); + break; + case NET_INADOR_COMMAND_SET_MASTER: + _manager_execute_set_or_clear_master (manager_client, buffer, bytes, FALSE); + break; default: _manager_send_error (manager_client, NET_INADOR_ERROR_WRONG_COMMAND, command); } @@ -657,6 +736,7 @@ static gboolean _manager_client_connect (GIOChannel *source, GIOCondition condit fd = accept (manager->socket, NULL, NULL); printf ("___ MANAGER ___ Nueva conexión (%i)\n", fd); + fcntl (fd, F_SETFD, 1); manager_client = malloc (sizeof (ManagerClientInfo)); if (manager_client == NULL) { @@ -787,6 +867,8 @@ int manager_init (NetworkInadorHandle *handle) { return -1; } + fcntl (manager->socket, F_SETFD, 1); + memset (&socket_name, 0, sizeof (struct sockaddr_un)); socket_name.sun_family = AF_UNIX; @@ -827,3 +909,4 @@ int manager_init (NetworkInadorHandle *handle) { return 0; } + diff --git a/src/netlink-events.c b/src/netlink-events.c index 3e7ea25..aeea5ab 100644 --- a/src/netlink-events.c +++ b/src/netlink-events.c @@ -27,6 +27,8 @@ #include +#include + #include "common.h" #include "interfaces.h" #include "ip-address.h" @@ -80,6 +82,7 @@ void netlink_events_create_pair (NetlinkEventPair *pair, int family) { nl_socket_disable_seq_check (sock_req); fd = nl_socket_get_fd (sock_req); + fcntl (fd, F_SETFD, 1); channel = g_io_channel_unix_new (fd); diff --git a/src/network-inador-manager.h b/src/network-inador-manager.h index e8cb6b9..695fc19 100644 --- a/src/network-inador-manager.h +++ b/src/network-inador-manager.h @@ -23,6 +23,12 @@ enum { NET_INADOR_COMMAND_IFACE_CHANGE_NAME, + NET_INADOR_COMMAND_IFACE_CHANGE_MTU, + + NET_INADOR_COMMAND_CREATE_BRIDGE = 16, + NET_INADOR_COMMAND_CLEAR_MASTER, + NET_INADOR_COMMAND_SET_MASTER, + NET_INADOR_COMMAND_LIST_IP = 32, NET_INADOR_COMMAND_CLEAR_IP, NET_INADOR_COMMAND_ADD_IP,