diff --git a/client-gtk/Makefile.am b/client-gtk/Makefile.am index 302a69d..643e560 100644 --- a/client-gtk/Makefile.am +++ b/client-gtk/Makefile.am @@ -15,6 +15,7 @@ inador_gtk_client_SOURCES = main.c \ ni-interface-chooser-dialog.c ni-interface-chooser-dialog.h \ ni-route.c ni-route.h \ ni-window-route.c ni-window-route.h \ + ni-add-route-dialog.c ni-add-route-dialog.h \ $(BUILT_SOURCES) #inador_gtk_client_CPPFLAGS = -DGAMEDATA_DIR=\"$(gamedatadir)/\" -DLOCALEDIR=\"$(localedir)\" $(AM_CPPFLAGS) diff --git a/client-gtk/ni-add-route-dialog.c b/client-gtk/ni-add-route-dialog.c new file mode 100644 index 0000000..10ea759 --- /dev/null +++ b/client-gtk/ni-add-route-dialog.c @@ -0,0 +1,687 @@ +/* + * ni-add-route-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-add-route-dialog.h" +#include "ni-route.h" + +struct _NIAddRouteGW { + NIAddRouteDialog *dialog; + GtkWidget *hbox; + + GtkWidget *hbox_weight; + GtkWidget *label_weight; + GtkWidget *gw, *weight; + + GtkWidget *button; + int pos; +}; + +struct _NIAddRouteDialogPrivate { + int family; + + GtkWidget *destination; + GtkWidget *button_add; + GtkWidget *metric; + + GList *gws; + GtkWidget *add_gw_button; + GtkSizeGroup *weight_sizer; + int gw_count; + + GtkListStore *tables_store; + GtkWidget *entry_tabla; + GtkWidget *entry_tipo; + gboolean valid_main_ip, valid_gw; + + gboolean valid_metric; + gboolean valid_route_table, valid_route_type; +}; + +enum { + PROP_ROUTE_FAMILY = 1, + + N_PROPERTIES +}; + +enum { + COL_TABLE_ID, + COL_TABLE_ID_STR, + COL_TABLE_NAME, + + NUM_TABLE_STORE_COLS +}; + +enum { + COL_ROUTE_TYPE_STR, + + COL_ROUTE_TYPE_DESC, + + NUM_ROUTE_TYPE_COLS +}; + +G_DEFINE_TYPE_WITH_PRIVATE (NIAddRouteDialog, ni_add_route_dialog, GTK_TYPE_DIALOG) + +static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; + +static void ni_add_route_dialog_add_more_gw_cb (GtkWidget *button, gpointer data); +static void ni_add_route_dialog_remove_gw_row_cb (GtkWidget *button, gpointer data); +static gboolean ni_add_route_dialog_get_ip (NIAddRouteDialog *dialog, struct_addr *addr); + +static void split_ip_mask (const char *texto, gchar *ip, int ip_size, gchar *mask, int mask_size) { + gchar *dup; + int g, has_slash = -1; + + mask[0] = 0; + dup = g_strdup (texto); + + for (g = 0; g < strlen (texto); g++) { + if (texto[g] == '/' && texto[g + 1] != 0) { + has_slash = g; + break; + } + } + + if (has_slash >= 0) { + strncpy (mask, &texto[has_slash + 1], mask_size); + strncpy (ip, texto, ip_size); + ip[has_slash] = 0; + } else { + strncpy (ip, texto, ip_size); + } +} + +static gboolean ip_valid_ipv4 (const char *ip) { + struct in_addr addr; + + if (inet_pton (AF_INET, ip, &addr) == 0) { + return FALSE; + } + + return TRUE; +} + +static gboolean ip_valid_ipv6 (const char *ip) { + struct in6_addr addr; + + if (inet_pton (AF_INET6, ip, &addr) == 0) { + return FALSE; + } + + return TRUE; +} + +static void ni_add_route_dialog_update_response (NIAddRouteDialog *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; + }*/ + gboolean res; + + res = dialog->priv->valid_main_ip && dialog->priv->valid_gw && dialog->priv->valid_metric && dialog->priv->valid_route_table && dialog->priv->valid_route_type; + + if (res == 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); +} + +static void ni_add_route_dialog_validate_main_ip (GtkEditable *editable, gpointer data) { + NIAddRouteDialog *dialog = NI_ADD_ROUTE_DIALOG (data); + struct_addr addr; + int prefix; + + dialog->priv->valid_main_ip = ni_add_route_dialog_get_destination (dialog, &addr, &prefix); + if (dialog->priv->valid_main_ip == FALSE) { + gtk_entry_set_icon_from_icon_name (GTK_ENTRY (editable), GTK_ENTRY_ICON_SECONDARY, "dialog-warning"); + } else { + gtk_entry_set_icon_from_icon_name (GTK_ENTRY (editable), GTK_ENTRY_ICON_SECONDARY, NULL); + } + ni_add_route_dialog_update_response (dialog); +} + +static void ni_add_route_dialog_validate_gws (GtkWidget *widget, gpointer data) { + NIAddRouteDialog *dialog = NI_ADD_ROUTE_DIALOG (data); + struct _NIAddRouteGW *gw_row; + GList *g; + const gchar *texto; + gboolean valid; + + dialog->priv->valid_gw = TRUE; + /* Recorrer cada caja de gateways */ + g = dialog->priv->gws; + while (g != NULL) { + gw_row = (struct _NIAddRouteGW *) g->data; + + valid = FALSE; + texto = gtk_entry_get_text (GTK_ENTRY (gw_row->gw)); + if (dialog->priv->family == AF_INET || dialog->priv->family == AF_UNSPEC) { + valid |= ip_valid_ipv4 (texto); + } + if (dialog->priv->family == AF_INET6 || dialog->priv->family == AF_UNSPEC) { + valid |= ip_valid_ipv6 (texto); + } + + dialog->priv->valid_gw = dialog->priv->valid_gw && valid; + + if (valid == FALSE) { + gtk_entry_set_icon_from_icon_name (GTK_ENTRY (gw_row->gw), GTK_ENTRY_ICON_SECONDARY, "dialog-warning"); + } else { + gtk_entry_set_icon_from_icon_name (GTK_ENTRY (gw_row->gw), GTK_ENTRY_ICON_SECONDARY, NULL); + } + + g = g->next; + } + ni_add_route_dialog_update_response (dialog); +} + +static void ni_add_route_dialog_validate_metric (GtkSpinButton *spin_button, GtkScrollType scroll, gpointer data) { + NIAddRouteDialog *dialog = NI_ADD_ROUTE_DIALOG (data); + gdouble value; + + value = gtk_spin_button_get_value (spin_button); + + if (value >= 0.0) { + dialog->priv->valid_metric = TRUE; + } else { + dialog->priv->valid_metric = FALSE; + } + + ni_add_route_dialog_update_response (dialog); +} + +static void ni_add_route_dialog_validate_route_table (GtkComboBox *combo, gpointer data) { + NIAddRouteDialog *dialog = NI_ADD_ROUTE_DIALOG (data); + const char *texto; + int n, g; + char endptr; + GtkWidget *entry; + + entry = gtk_bin_get_child (GTK_BIN (combo)); + texto = gtk_entry_get_text (GTK_ENTRY (entry)); + + n = sscanf (texto, "%i%c", &g, &endptr); + if (n != 1) { + dialog->priv->valid_route_table = FALSE; + gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry), GTK_ENTRY_ICON_SECONDARY, "dialog-warning"); + } else { + dialog->priv->valid_route_table = TRUE; + gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry), GTK_ENTRY_ICON_SECONDARY, NULL); + } + + ni_add_route_dialog_update_response (dialog); +} + +static void ni_add_route_dialog_validate_route_type (GtkComboBox *combo, gpointer data) { + NIAddRouteDialog *dialog = NI_ADD_ROUTE_DIALOG (data); + const char *texto; + int n, g; + char endptr; + GtkWidget *entry; + + entry = gtk_bin_get_child (GTK_BIN (combo)); + texto = gtk_entry_get_text (GTK_ENTRY (entry)); + + n = sscanf (texto, "%i%c", &g, &endptr); + if (n != 1 || g > 255) { + dialog->priv->valid_route_type = FALSE; + gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry), GTK_ENTRY_ICON_SECONDARY, "dialog-warning"); + } else { + dialog->priv->valid_route_type = TRUE; + gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry), GTK_ENTRY_ICON_SECONDARY, NULL); + } + + ni_add_route_dialog_update_response (dialog); +} + +/* Función para renderizar la tabla y el nombre con guion separado */ +void ni_add_route_dialog_cell_renderer_table_id (GtkCellLayout *cell_layout, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data) { + const gchar *name; + + gtk_tree_model_get (tree_model, iter, COL_TABLE_NAME, &name, -1); + + if (name == NULL) { + g_object_set (cell, "text", "(Sin nombre)", NULL); + } else { + g_object_set (cell, "text", name, NULL); + } +} + +static void ni_add_route_dialog_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { + NIAddRouteDialog *dialog = NI_ADD_ROUTE_DIALOG (object); + g_return_if_fail (NI_IS_ADD_ROUTE_DIALOG (object)); + + switch (prop_id) { + case PROP_ROUTE_FAMILY: + dialog->priv->family = g_value_get_uint (value); + if (dialog->priv->family == AF_UNSPEC) { + gtk_entry_set_placeholder_text (GTK_ENTRY (dialog->priv->destination), "192.0.2.1/24 or 2001:db8::1/64"); + } else if (dialog->priv->family == AF_INET) { + gtk_entry_set_placeholder_text (GTK_ENTRY (dialog->priv->destination), "192.0.2.1/24"); + } else if (dialog->priv->family == AF_INET6) { + gtk_entry_set_placeholder_text (GTK_ENTRY (dialog->priv->destination), "2001:db8::1/64"); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void ni_add_route_dialog_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { + NIAddRouteDialog *dialog = NI_ADD_ROUTE_DIALOG (object); + g_return_if_fail (NI_IS_ADD_ROUTE_DIALOG (object)); + + switch (prop_id) { + case PROP_ROUTE_FAMILY: + g_value_set_uint (value, dialog->priv->family); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void ni_add_route_dialog_class_init (NIAddRouteDialogClass *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_add_route_dialog_set_property; + object_class->get_property = ni_add_route_dialog_get_property; + + obj_properties[PROP_ROUTE_FAMILY] = g_param_spec_uint ( + "family", + "Network family", + "Network family to allow", + 0, 10, AF_UNSPEC, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + + g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties); +} + +static struct _NIAddRouteGW *ni_add_route_dialog_create_gw_row (NIAddRouteDialog *dialog) { + struct _NIAddRouteGW *gw_row; + GtkWidget *label, *button; + + gw_row = (struct _NIAddRouteGW *) malloc (sizeof (struct _NIAddRouteGW)); + if (gw_row == NULL) return NULL; + + gw_row->dialog = dialog; + + gw_row->hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + //gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + if (dialog->priv->gw_count > 0) { + button = gtk_button_new_from_icon_name ("list-remove", GTK_ICON_SIZE_BUTTON); + g_signal_connect (button, "clicked", G_CALLBACK (ni_add_route_dialog_remove_gw_row_cb), gw_row); + gtk_box_pack_start (GTK_BOX (gw_row->hbox), button, FALSE, FALSE, 0); + } + + label = gtk_label_new ("Puerta de enlace:"); + gtk_box_pack_start (GTK_BOX (gw_row->hbox), label, FALSE, FALSE, 0); + + gw_row->gw = gtk_entry_new (); + g_signal_connect (gw_row->gw, "changed", G_CALLBACK (ni_add_route_dialog_validate_gws), dialog); + gtk_box_pack_start (GTK_BOX (gw_row->hbox), gw_row->gw, TRUE, TRUE, 0); + + gw_row->hbox_weight = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3); + gtk_box_pack_start (GTK_BOX (gw_row->hbox), gw_row->hbox_weight, FALSE, FALSE, 5); + gtk_size_group_add_widget (dialog->priv->weight_sizer, gw_row->hbox_weight); + + gw_row->label_weight = gtk_label_new ("Peso:"); + gtk_widget_hide (gw_row->label_weight); + gtk_box_pack_start (GTK_BOX (gw_row->hbox_weight), gw_row->label_weight, FALSE, FALSE, 0); + + gw_row->weight = gtk_spin_button_new_with_range (0.0, 9000.0, 10.0); + gtk_widget_hide (gw_row->weight); + gtk_box_pack_start (GTK_BOX (gw_row->hbox_weight), gw_row->weight, TRUE, TRUE, 0); + + return gw_row; +} + +static void ni_add_route_dialog_remove_gw_row_cb (GtkWidget *button, gpointer data) { + struct _NIAddRouteGW *gw_row = (struct _NIAddRouteGW *) data; + NIAddRouteDialog *dialog = gw_row->dialog; + NIAddRouteDialogPrivate *priv = dialog->priv; + + gtk_size_group_remove_widget (priv->weight_sizer, gw_row->hbox_weight); + + priv->gws = g_list_remove (priv->gws, gw_row); + priv->gw_count--; + gtk_widget_destroy (gw_row->hbox); + free (gw_row); + + if (priv->gw_count == 1) { + /* Volver a ocultar la caja de peso de la única fila */ + gw_row = (struct _NIAddRouteGW *) priv->gws->data; + gtk_widget_hide (gw_row->hbox_weight); + } +} + +static void ni_add_route_dialog_add_more_gw_cb (GtkWidget *button, gpointer data) { + struct _NIAddRouteGW *gw_row; + NIAddRouteDialog *dialog = NI_ADD_ROUTE_DIALOG (data); + NIAddRouteDialogPrivate *priv = ni_add_route_dialog_get_instance_private (dialog); + GValue gval = G_VALUE_INIT; + gint pos; + GtkWidget *vbox_parent, *image; + + /* Si es la única fila, mostrar la caja de peso */ + if (priv->gw_count == 1) { + gw_row = (struct _NIAddRouteGW *) priv->gws->data; + gtk_widget_show (gw_row->hbox_weight); + } + + /* Desconectar esta señal y conectarla a eliminar */ + vbox_parent = gtk_widget_get_parent (priv->add_gw_button); + g_value_init (&gval, G_TYPE_INT); + gtk_container_child_get_property (GTK_CONTAINER (vbox_parent), priv->add_gw_button, "position", &gval); + pos = g_value_get_int (&gval); + + /* Ahora, crear una nueva fila */ + gw_row = ni_add_route_dialog_create_gw_row (dialog); + if (gw_row != NULL) { + gtk_box_pack_start (GTK_BOX (vbox_parent), gw_row->hbox, FALSE, FALSE, 0); + gtk_box_reorder_child (GTK_BOX (vbox_parent), gw_row->hbox, pos); + gtk_widget_show_all (gw_row->hbox); + priv->gws = g_list_append (priv->gws, gw_row); + priv->gw_count++; + gtk_window_set_focus (GTK_WINDOW (dialog), gw_row->gw); + } +} + +static void ni_add_route_dialog_init (NIAddRouteDialog *dialog) { + NIAddRouteDialogPrivate *priv = ni_add_route_dialog_get_instance_private (dialog); + GtkWidget *image, *entry; + GtkWidget *vbox, *expander; + GtkWidget *container, *label, *hbox; + GtkTreeIter iter; + GtkCellRenderer *renderer; + struct _NIAddRouteGW *gw_row; + GtkListStore *store; + + dialog->priv = priv; + + priv->family = AF_UNSPEC; + + gtk_window_set_title (GTK_WINDOW (dialog), "Add new Route"); + + gtk_dialog_add_button (GTK_DIALOG (dialog), "Cancelar", GTK_RESPONSE_CANCEL); + + priv->button_add = gtk_dialog_add_button (GTK_DIALOG (dialog), "Agregar", GTK_RESPONSE_OK); + image = gtk_image_new_from_icon_name ("list-add", GTK_ICON_SIZE_BUTTON); + gtk_button_set_image (GTK_BUTTON (priv->button_add), image); + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, FALSE); + + 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 ("Ingrese los detalles sobre la ruta:"); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5); + + /* La caja de la ruta */ + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new ("Destino:"); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + priv->destination = gtk_entry_new (); + gtk_entry_set_placeholder_text (GTK_ENTRY (priv->destination), "192.0.2.1/24 or 2001:db8::1/64"); + g_signal_connect (priv->destination, "changed", G_CALLBACK (ni_add_route_dialog_validate_main_ip), dialog); + gtk_box_pack_start (GTK_BOX (hbox), priv->destination, TRUE, TRUE, 0); + + priv->weight_sizer = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + /* Primero crear una sola fila de gateway */ + priv->gw_count = 0; + priv->gws = NULL; + gw_row = ni_add_route_dialog_create_gw_row (dialog); + + if (gw_row != NULL) { + gtk_box_pack_start (GTK_BOX (vbox), gw_row->hbox, FALSE, FALSE, 0); + priv->gws = g_list_append (NULL, gw_row); + priv->gw_count = 1; + } + + priv->add_gw_button = gtk_button_new_with_label ("Agregar un siguiente brinco"); + g_signal_connect (priv->add_gw_button, "clicked", G_CALLBACK (ni_add_route_dialog_add_more_gw_cb), dialog); + image = gtk_image_new_from_icon_name ("list-add", GTK_ICON_SIZE_BUTTON); + gtk_button_set_image (GTK_BUTTON (priv->add_gw_button), image); + gtk_box_pack_start (GTK_BOX (vbox), priv->add_gw_button, FALSE, FALSE, 0); + + /* La métrica */ + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new ("Metrica:"); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + priv->metric = gtk_spin_button_new_with_range (0.0, 9000.0, 10.0); + g_signal_connect (priv->metric, "change-value", G_CALLBACK (ni_add_route_dialog_validate_metric), dialog); + gtk_spin_button_set_digits (GTK_SPIN_BUTTON (priv->metric), 0); + // gtk_spin_button_get_value_as_int + gtk_box_pack_start (GTK_BOX (hbox), priv->metric, TRUE, TRUE, 0); + + + // Las opciones avanzadas + expander = gtk_expander_new_with_mnemonic ("_Opciones avanzadas"); + gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 0); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3); + gtk_container_add (GTK_CONTAINER (expander), vbox); + + /* La tabla de ruteo */ + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 3); + + label = gtk_label_new ("Tabla de ruteo:"); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + priv->tables_store = gtk_list_store_new (NUM_TABLE_STORE_COLS, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING); + /* TODO: Conseguir esta información desde el NetworkInador */ + gtk_list_store_insert_with_values (priv->tables_store, &iter, -1, COL_TABLE_ID, 0, COL_TABLE_ID_STR, "0", COL_TABLE_NAME, "Por defecto", -1); + gtk_list_store_insert_with_values (priv->tables_store, NULL, -1, COL_TABLE_ID, 254, COL_TABLE_ID_STR, "254", COL_TABLE_NAME, "Main", -1); + gtk_list_store_insert_with_values (priv->tables_store, NULL, -1, COL_TABLE_ID, 255, COL_TABLE_ID_STR, "255", COL_TABLE_NAME, "Local", -1); + + priv->entry_tabla = gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL (priv->tables_store)); + g_signal_connect (priv->entry_tabla, "changed", G_CALLBACK (ni_add_route_dialog_validate_route_table), dialog); + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->entry_tabla), renderer, TRUE); + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->entry_tabla), renderer, ni_add_route_dialog_cell_renderer_table_id, NULL, NULL); + gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (priv->entry_tabla), COL_TABLE_ID_STR); + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->entry_tabla), &iter); + + g_object_unref (priv->tables_store); + gtk_box_pack_start (GTK_BOX (hbox), priv->entry_tabla, TRUE, TRUE, 0); + + /* La opción avanzada de tipo de tipo de ruta */ + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 3); + + label = gtk_label_new ("Tipo de ruta:"); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + store = gtk_list_store_new (NUM_ROUTE_TYPE_COLS, G_TYPE_STRING, G_TYPE_STRING); + gtk_list_store_insert_with_values (store, NULL, -1, COL_ROUTE_TYPE_STR, "2", COL_ROUTE_TYPE_DESC, "Kernel", -1); + gtk_list_store_insert_with_values (store, NULL, -1, COL_ROUTE_TYPE_STR, "3", COL_ROUTE_TYPE_DESC, "Arranque (Boot)", -1); + gtk_list_store_insert_with_values (store, &iter, -1, COL_ROUTE_TYPE_STR, "4", COL_ROUTE_TYPE_DESC, "Estática", -1); + gtk_list_store_insert_with_values (store, NULL, -1, COL_ROUTE_TYPE_STR, "16", COL_ROUTE_TYPE_DESC, "Por DHCP", -1); + + priv->entry_tipo = gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL (store)); + g_signal_connect (priv->entry_tipo, "changed", G_CALLBACK (ni_add_route_dialog_validate_route_type), dialog); + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->entry_tipo), renderer, TRUE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->entry_tipo), renderer, "text", COL_ROUTE_TYPE_DESC); + gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (priv->entry_tipo), COL_ROUTE_TYPE_STR); + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->entry_tipo), &iter); + + g_object_unref (store); + gtk_box_pack_start (GTK_BOX (hbox), priv->entry_tipo, TRUE, TRUE, 0); + + /* TODO: Agregar pref-src en las opciones avanzadas */ + + dialog->priv->valid_main_ip = FALSE; + dialog->priv->valid_gw = FALSE; + dialog->priv->valid_metric = TRUE; + dialog->priv->valid_route_table = TRUE; + dialog->priv->valid_route_type = TRUE; + + gtk_widget_show_all (container); + + if (gw_row != NULL) { + gtk_widget_hide (gw_row->hbox_weight); + } +} + +gboolean ni_add_route_dialog_get_destination (NIAddRouteDialog *dialog, struct_addr *addr, int *prefix) { + const gchar *texto; + gchar *rest, ip[128], mask[32]; + int lprefix; + gboolean valid; + + texto = gtk_entry_get_text (GTK_ENTRY (dialog->priv->destination)); + + split_ip_mask (texto, ip, sizeof (ip), mask, sizeof (mask)); + + if (mask[0] != 0) { + lprefix = g_ascii_strtoll (mask, &rest, 10); + + if (rest[0] != 0) { + return FALSE; + } + } + + if (dialog->priv->family == AF_INET || dialog->priv->family == AF_UNSPEC) { + valid = ip_valid_ipv4 (ip); + if (valid == FALSE || + (mask[0] != 0 && (lprefix <= 0 || lprefix > 32))) { + if (dialog->priv->family == AF_INET) { + return FALSE; + } + } else { + *prefix = 32; + if (mask[0] != 0) { + *prefix = lprefix; + } + inet_pton (AF_INET, ip, addr); + + return TRUE; + } + } + + if (dialog->priv->family == AF_INET6 || dialog->priv->family == AF_UNSPEC) { + valid = ip_valid_ipv6 (ip); + if (valid == FALSE || (mask[0] != 0 && (lprefix <= 0 || lprefix > 128))) { + return FALSE; + } else { + *prefix = 128; + if (mask[0] != 0) { + *prefix = lprefix; + } + inet_pton (AF_INET6, ip, addr); + } + } + + return FALSE; +} + +GList *ni_add_route_dialog_get_gateways (NIAddRouteDialog *dialog) { + GList *result = NULL, *h; + int g, ret; + struct NIAddRouteDialogGW *gw; + struct _NIAddRouteGW *gw_row; + const char *gw_texto; + + for (g = 0; g < dialog->priv->gw_count; g++) { + gw = (struct NIAddRouteDialogGW *) g_malloc (sizeof (struct NIAddRouteDialogGW)); + + h = g_list_nth (dialog->priv->gws, g); + gw_row = (struct _NIAddRouteGW *) h->data; + + gw_texto = gtk_entry_get_text (GTK_ENTRY (gw_row->gw)); + + if (dialog->priv->family == AF_INET) { + inet_pton (AF_INET, gw_texto, &gw->addr); + } else if (dialog->priv->family == AF_INET6) { + inet_pton (AF_INET6, gw_texto, &gw->addr); + } else { + /* UNSPEC */ + ret = inet_pton (AF_INET, gw_texto, &gw->addr); + if (ret == 0) { + inet_pton (AF_INET6, gw_texto, &gw->addr); + } + } + + gw_texto = gtk_entry_get_text (GTK_ENTRY (gw_row->weight)); + + gw->metric = g_ascii_strtoll (gw_texto, NULL, 10); + + result = g_list_append (result, gw); + } + + return result; +} + +int ni_add_route_dialog_get_metric (NIAddRouteDialog *dialog) { + gdouble val; + + val = gtk_spin_button_get_value (GTK_SPIN_BUTTON (dialog->priv->metric)); + + return (int) val; +} + +int ni_add_route_dialog_get_route_table (NIAddRouteDialog *dialog) { + const char *texto; + + texto = gtk_entry_get_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (dialog->priv->entry_tabla)))); + + return g_ascii_strtoll (texto, NULL, 10); +} + +int ni_add_route_dialog_get_route_type (NIAddRouteDialog *dialog) { + const char *texto; + + texto = gtk_entry_get_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (dialog->priv->entry_tipo)))); + + return g_ascii_strtoll (texto, NULL, 10); +} + +GtkWidget* ni_add_route_dialog_new (int family) { + NIAddRouteDialog *dialog; + + dialog = g_object_new (NI_TYPE_ADD_ROUTE_DIALOG, "family", family, NULL); + + return GTK_WIDGET (dialog); +} + diff --git a/client-gtk/ni-add-route-dialog.h b/client-gtk/ni-add-route-dialog.h new file mode 100644 index 0000000..bb8c98a --- /dev/null +++ b/client-gtk/ni-add-route-dialog.h @@ -0,0 +1,79 @@ +/* + * ni-add-route-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_ADD_ROUTE_DIALOG_H__ +#define __NI_ADD_ROUTE_DIALOG_H__ + +#include + +#include "ni-route.h" +#include "ni-ip.h" + +G_BEGIN_DECLS + +typedef struct _NIAddRouteDialogClass NIAddRouteDialogClass; +typedef struct _NIAddRouteDialog NIAddRouteDialog; +typedef struct _NIAddRouteDialogPrivate NIAddRouteDialogPrivate; + +/* + * Type declaration. + */ +#define NI_TYPE_ADD_ROUTE_DIALOG (ni_add_route_dialog_get_type ()) +#define NI_ADD_ROUTE_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NI_TYPE_ADD_ROUTE_DIALOG, NIAddRouteDialog)) +#define NI_ADD_ROUTE_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NI_TYPE_ADD_ROUTE_DIALOG, NIAddRouteDialogClass)) +#define NI_IS_ADD_ROUTE_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NI_TYPE_ADD_ROUTE_DIALOG)) +#define NI_IS_ADD_ROUTE_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NI_TYPE_ADD_ROUTE_DIALOG)) +#define NI_ADD_ROUTE_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NI_TYPE_ADD_ROUTE_DIALOG, NIAddRouteDialogClass)) + +GType ni_add_route_dialog_get_type (void); + +struct _NIAddRouteDialogClass { + GtkDialogClass parent_class; + +}; + +struct _NIAddRouteDialog { + GtkDialog parent_instance; + + NIAddRouteDialogPrivate *priv; +}; + +struct NIAddRouteDialogGW { + struct_addr addr; + int metric; +}; + +/* + * Method definitions. + */ +GtkWidget* ni_add_route_dialog_new (int family); + +gboolean ni_add_route_dialog_get_destination (NIAddRouteDialog *dialog, struct_addr *addr, int *prefix); +GList *ni_add_route_dialog_get_gateways (NIAddRouteDialog *dialog); +int ni_add_route_dialog_get_metric (NIAddRouteDialog *dialog); +int ni_add_route_dialog_get_route_table (NIAddRouteDialog *dialog); +int ni_add_route_dialog_get_route_type (NIAddRouteDialog *dialog); + +G_END_DECLS + +#endif /* __NI_ADD_ROUTE_DIALOG_H__ */ + diff --git a/client-gtk/ni-route.h b/client-gtk/ni-route.h index 6e58cbc..b4bc893 100644 --- a/client-gtk/ni-route.h +++ b/client-gtk/ni-route.h @@ -31,6 +31,9 @@ #include #include +#include "ni-ip.h" +#include "ni-client.h" + G_BEGIN_DECLS typedef struct _NIRouteClass NIRouteClass; diff --git a/client-gtk/ni-window-route.c b/client-gtk/ni-window-route.c index 4b8a734..8af24bc 100644 --- a/client-gtk/ni-window-route.c +++ b/client-gtk/ni-window-route.c @@ -37,6 +37,8 @@ #include "ni-client.h" #include "ni-route.h" #include "ni-interface.h" +#include "ni-add-route-dialog.h" + struct _NIWindowRoutePrivate { NIClient *ni_client; @@ -104,8 +106,8 @@ static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; static gboolean ni_window_route_foreach_tables_search_id (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); static gboolean ni_window_route_foreach_routes_search_route (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); -static void ni_window_interface_new_ip_cb (NIClient *ni_client, NIRoute *ni_route, gpointer data); -static void ni_window_interface_delete_ip_cb (NIClient *ni_client, NIRoute *ni_route, gpointer data); +static void ni_window_interface_new_route_cb (NIClient *ni_client, NIRoute *ni_route, gpointer data); +static void ni_window_interface_delete_route_cb (NIClient *ni_client, NIRoute *ni_route, gpointer data); static void ni_window_route_updated_route_cb (NIRoute *ni_route, gpointer data) { NIWindowRoute *window_route = NI_WINDOW_ROUTE (data); @@ -273,8 +275,8 @@ static void ni_window_route_constructed (GObject *obj) { g_free (name);*/ /* Conectar la señal de ruta agregada y ruta eliminada */ - g_signal_connect (window_route->priv->ni_client, "new-route", G_CALLBACK (ni_window_interface_new_ip_cb), window_route); - g_signal_connect (window_route->priv->ni_client, "delete-route", G_CALLBACK (ni_window_interface_delete_ip_cb), window_route); + g_signal_connect (window_route->priv->ni_client, "new-route", G_CALLBACK (ni_window_interface_new_route_cb), window_route); + g_signal_connect (window_route->priv->ni_client, "delete-route", G_CALLBACK (ni_window_interface_delete_route_cb), window_route); /* Recorrer las rutas, y procesar la información */ if (window_route->priv->family == AF_INET) { @@ -405,13 +407,13 @@ gboolean ni_window_route_tree_filter_routes_func (GtkTreeModel *tree_model, GtkT return FALSE; } -static void ni_window_interface_new_ip_cb (NIClient *ni_client, NIRoute *ni_route, gpointer data) { +static void ni_window_interface_new_route_cb (NIClient *ni_client, NIRoute *ni_route, gpointer data) { NIWindowRoute *window_route = NI_WINDOW_ROUTE (data); ni_window_route_route_added_cb (window_route, ni_route); } -static void ni_window_interface_delete_ip_cb (NIClient *ni_client, NIRoute *ni_route, gpointer data) { +static void ni_window_interface_delete_route_cb (NIClient *ni_client, NIRoute *ni_route, gpointer data) { NIWindowRoute *window_route = NI_WINDOW_ROUTE (data); struct _NIWindowRouteSearchRoute for_search; @@ -426,6 +428,59 @@ static void ni_window_interface_delete_ip_cb (NIClient *ni_client, NIRoute *ni_r } } +static void ni_window_route_add_route_button_add_cb (GtkWidget *button, gpointer data) { + NIWindowRoute *window_route = NI_WINDOW_ROUTE (data); + GtkWidget *dialog; + gint response; + NIClient *ni_client; + GList *gws; + int metrica, tabla, tipo; + struct_addr destino; + gboolean has_ + dialog = ni_add_route_dialog_new (window_route->priv->family); + + gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window_route)); + + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (response == GTK_RESPONSE_OK) { + /* Mandar la ip al network-inador */ + ni_client = ni_interface_get_client (window_route->priv->ni_interface); + + gws = ni_add_route_dialog_get_gateways (NI_ADD_ROUTE_DIALOG (dialog)); + + if (gws != NULL) { + + } + + g_list_free_full (gws, g_free); + #if 0 + ni_ip_add_dialog_get_address (NI_IP_ADD_DIALOG (dialog), &addr, &prefix); + has_p2p = ni_ip_add_dialog_has_p2p_address (NI_IP_ADD_DIALOG (dialog)); + if (has_p2p) { + memcpy (&p2p_addr, &addr, sizeof (p2p_addr)); + ni_ip_add_dialog_get_p2p_address (NI_IP_ADD_DIALOG (dialog), &addr); + } + + flags |= IFA_F_PERMANENT; + if (ni_ip_add_dialog_get_noprefix (NI_IP_ADD_DIALOG (dialog))) { + flags |= IFA_F_NOPREFIXROUTE; + } + //void ni_client_ask_ip_new (NIClient *ni_client, NIInterface *ni_interface, int family, int prefix, struct_addr *addr, uint32_t flags, unsigned char scope, int has_local, struct_addr *local_addr, int has_brd, struct_addr *brd_addr) { + + /* TODO: Pedir los tiempos de validez de la interfaz */ + guint32 cacheinfo[2]; + cacheinfo[0] = 60; + cacheinfo[1] = 60; + ni_client_ask_ip_new (ni_client, window_iface->priv->ni_interface, family, prefix, &addr, flags, 0, has_p2p, (has_p2p ? &p2p_addr : NULL), FALSE, NULL, cacheinfo); + #endif + } + + gtk_widget_destroy (dialog); +} + static void ni_window_route_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NIWindowRoute *window_route = NI_WINDOW_ROUTE (object); g_return_if_fail (NI_IS_WINDOW_ROUTE (object)); @@ -577,6 +632,7 @@ static void ni_window_route_init (NIWindowRoute *window_route) { gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0); priv->tables_store = gtk_list_store_new (NUM_TABLE_STORE_COLS, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_BOOLEAN); + /* TODO: Recuperar esta informacion de las tablas de ruteo desde el NetworkInador */ gtk_list_store_insert_with_values (priv->tables_store, &iter, -1, TABLE_STORE_TABLE, 0, TABLE_STORE_NAME, "Todas", TABLE_STORE_IS_NEW, FALSE, -1); gtk_list_store_insert_with_values (priv->tables_store, NULL, -1, TABLE_STORE_TABLE, 254, TABLE_STORE_NAME, "Main", TABLE_STORE_IS_NEW, FALSE, -1); gtk_list_store_insert_with_values (priv->tables_store, NULL, -1, TABLE_STORE_TABLE, 255, TABLE_STORE_NAME, "Local", TABLE_STORE_IS_NEW, FALSE, -1); @@ -615,7 +671,7 @@ static void ni_window_route_init (NIWindowRoute *window_route) { 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_addr_v6_button_add_cb), window_iface); + g_signal_connect (button, "clicked", G_CALLBACK (ni_window_route_add_route_button_add_cb), window_route); priv->del_route_button = gtk_button_new_from_icon_name ("list-remove", GTK_ICON_SIZE_LARGE_TOOLBAR); gtk_box_pack_start (GTK_BOX (vbox2), priv->del_route_button, FALSE, FALSE, 0); diff --git a/src/manager.c b/src/manager.c index ab25980..5cc14fa 100644 --- a/src/manager.c +++ b/src/manager.c @@ -42,6 +42,7 @@ #include "common.h" #include "interfaces.h" #include "ip-address.h" +#include "routes.h" #include "bridge.h" #include "network-inador-manager.h" #include "dhcp_client.h" @@ -1160,6 +1161,120 @@ void _manager_send_list_routes (ManagerClientInfo *manager_client, unsigned char _manager_send_end_command (manager_client, NET_INADOR_COMMAND_LIST_ROUTES); } +void _manager_execute_add_route (ManagerClientInfo *manager_client, unsigned char *buffer, int buffer_len) { + Route route; + int ret, has_prefsrc; + int family_size, wanted_size, family, nexthops_needed; + int g, rem, pos; + RouteNH *nh; + + if (buffer_len < 18) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_ADD_ROUTE); + return; + } + + family = buffer[2]; + + if (family != AF_INET && family != AF_INET6) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INVALID_FAMILY, NET_INADOR_COMMAND_ADD_ROUTE); + + return; + } + + if (family == AF_INET) { + family_size = sizeof (struct in_addr); + } else if (family == AF_INET6) { + family_size = sizeof (struct in6_addr); + } + + has_prefsrc = 0; + if (buffer[12] & 0x01) { + has_prefsrc = 1; + } + /* Ya puedo revisar el resto de la longitud */ + wanted_size = 18 + family_size + (family_size * has_prefsrc); + if (buffer_len < wanted_size) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_ADD_ROUTE); + return; + } + + /* Revisar los next hops */ + g = buffer[13]; + rem = buffer_len - wanted_size; + pos = wanted_size; + while (g > 0) { + if (rem < 8) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_ADD_ROUTE); + return; + } + + if (buffer[pos] & 0x01 && rem < 12) { + /* Tiene GW */ + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_ADD_ROUTE); + return; + } + + g--; + if (buffer[pos] & 0x01) { + rem -= 12; + pos += 12; + } else { + rem -= 8; + pos += 8; + } + } + + memset (&route, 0, sizeof (route)); + + /* Ejecutar el parsing */ + route.family = family; + route.type = buffer[3]; + memcpy (&route.table, &buffer[4], 4); + route.prefix = buffer[8]; + route.protocol = buffer[9]; + route.tos = buffer[10]; + route.scope = buffer[11]; + memcpy (&route.priority, &buffer[14], 4); + + memcpy (&route.dest, &buffer[18], family_size); + pos = 18 + family_size; + if (has_prefsrc) { + memcpy (&route.prefsrc, &buffer[pos], family_size); + pos += family_size; + } + + /* Recorrer cada next-hop */ + g = buffer[13]; + route.nexthops = NULL; + while (g > 0) { + nh = (RouteNH *) malloc (sizeof (RouteNH)); + + memset (nh, 0, sizeof (RouteNH)); + nh->nh_flags = buffer[pos + 1]; + nh->nh_weight = buffer[pos + 2]; + memcpy (&nh->out_index, &buffer[pos + 4], 4); + + pos += 8; + + if (buffer[pos] & 0x01) { + /* Tiene GW */ + memcpy (&nh->gw, &buffer[pos], family_size); + pos += family_size; + } + route.nexthops = g_list_append (route.nexthops, nh); + g--; + } + + ret = routes_add (manager_client->manager->handle, &route); + + if (ret == 0) { + /* OK */ + _manager_send_executed (manager_client); + } else { + _manager_send_error (manager_client, NET_INADOR_ERROR_NOT_EXECUTED, NET_INADOR_COMMAND_ADD_ROUTE); + } +} + static gboolean _manager_client_data (GIOChannel *source, GIOCondition condition, gpointer data) { ManagerClientInfo *manager_client = (ManagerClientInfo *) data; NetworkInadorManager *manager = manager_client->manager; @@ -1260,6 +1375,12 @@ static gboolean _manager_client_data (GIOChannel *source, GIOCondition condition break; case NET_INADOR_COMMAND_LIST_ROUTES: _manager_send_list_routes (manager_client, buffer, bytes); + break; + case NET_INADOR_COMMAND_ADD_ROUTE: + _manager_execute_add_route (manager_client, buffer, bytes); + break; + case NET_INADOR_COMMAND_REMOVE_ROUTE: + break; default: _manager_send_error (manager_client, NET_INADOR_ERROR_WRONG_COMMAND, command); diff --git a/src/network-inador-manager.h b/src/network-inador-manager.h index dd58b2c..844742a 100644 --- a/src/network-inador-manager.h +++ b/src/network-inador-manager.h @@ -41,6 +41,8 @@ enum { NET_INADOR_COMMAND_GET_DHCP_STATUS, NET_INADOR_COMMAND_LIST_ROUTES = 64, + NET_INADOR_COMMAND_ADD_ROUTE, + NET_INADOR_COMMAND_REMOVE_ROUTE, NET_INADOR_COMMAND_SET_EVENT_MASK = 192, diff --git a/src/routes.c b/src/routes.c index 72b8316..fde6f42 100644 --- a/src/routes.c +++ b/src/routes.c @@ -431,6 +431,169 @@ int routes_receive_message_delroute (struct nl_msg *msg, void *arg) { return NL_SKIP; } +static int _route_wait_ack_or_error (struct nl_msg *msg, void *arg) { + int *ret = (int *) arg; + struct nlmsgerr *l_err; + struct nlmsghdr *reply; + + reply = nlmsg_hdr (msg); + + if (reply->nlmsg_type == NLMSG_ERROR) { + l_err = nlmsg_data (reply); + + *ret = l_err->error; + } + + return NL_SKIP; +} + +static int _route_wait_error (struct sockaddr_nl *nla, struct nlmsgerr *l_err, void *arg) { + int *ret = (int *) arg; + + *ret = l_err->error; + + return NL_SKIP; +} + +int routes_add (NetworkInadorHandle *handle, Route *route) { + struct nl_msg * msg, *msg_nh; + struct rtmsg route_hdr; + int ret, error; + int family_size = 0; + struct_addr empty; + int hop_count; + RouteNH *nh; + GList *g; + struct rtnexthop nexthop_hdr, *nhptr; + char buffer_nexthops[8192]; + int size_nexthops; + + route_hdr.rtm_family = route->family; + route_hdr.rtm_dst_len = route->prefix; + route_hdr.rtm_src_len = 0; + route_hdr.rtm_tos = route->tos; + route_hdr.rtm_table = route->table; + route_hdr.rtm_protocol = route->protocol; + route_hdr.rtm_scope = route->scope; + route_hdr.rtm_type = route->type; + route_hdr.rtm_flags = 0; + + msg = nlmsg_alloc_simple (RTM_NEWROUTE, NLM_F_REQUEST | NLM_F_CREATE); + ret = nlmsg_append (msg, &route_hdr, sizeof (route_hdr), NLMSG_ALIGNTO); + + if (ret != 0) { + nlmsg_free (msg); + + return -1; + } + + if (route->family == AF_INET) { + family_size = sizeof (struct in_addr); + } else if (route->family == AF_INET6) { + family_size = sizeof (struct in6_addr); + } + + ret = nla_put (msg, RTA_DST, family_size, &route->dest); + if (route->priority != 0) { + ret |= nla_put (msg, RTA_PRIORITY, 4, &route->priority); + } + + memset (&empty, 0, sizeof (empty)); + if (memcmp (&empty, &route->prefsrc, family_size) != 0) { + ret |= nla_put (msg, RTA_PREFSRC, family_size, &route->prefsrc); + } + + hop_count = g_list_length (route->nexthops); + if (hop_count <= 1) { + /* Agregar por el método 1 */ + nh = (RouteNH *) route->nexthops->data; + + ret |= nla_put (msg, RTA_OIF, 4, &nh->out_index); + + if (memcmp (&empty, &nh->gw, family_size) != 0) { + ret |= nla_put (msg, RTA_GATEWAY, family_size, &nh->gw); + } + } else { + /* Tenemos múltiples next-hops */ + //nest = nla_nest_start (msg, RTA_MULTIPATH); + size_nexthops = 0; + + g = route->nexthops; + while (g != NULL) { + nh = (RouteNH *) g->data; + + msg_nh = nlmsg_alloc (); + if (msg_nh == NULL) break; + nexthop_hdr.rtnh_len = 0; + nexthop_hdr.rtnh_flags = nh->nh_flags; + nexthop_hdr.rtnh_hops = nh->nh_weight; + nexthop_hdr.rtnh_ifindex = nh->out_index; + + nlmsg_append (msg_nh, &nexthop_hdr, sizeof (nexthop_hdr), 0); + + if (memcmp (&empty, &nh->gw, family_size) != 0) { + /* Tiene un gw el next-hop */ + nla_put (msg_nh, RTA_GATEWAY, family_size, &nh->gw); + } + + /* Corregir la longitud */ + nhptr = nlmsg_data (nlmsg_hdr (msg_nh)); + nhptr->rtnh_len = (nlmsg_hdr (msg_nh))->nlmsg_len - NLMSG_HDRLEN; + + /* Copiar al buffer de los next-hops */ + memcpy (&buffer_nexthops[size_nexthops], nhptr, nhptr->rtnh_len); + size_nexthops += nhptr->rtnh_len; + + nlmsg_free (msg_nh); + g = g->next; + } + + ret |= nla_put (msg, RTA_MULTIPATH, size_nexthops, buffer_nexthops); + } + + if (ret != 0) { + nlmsg_free (msg); + + return -1; + } + + nl_complete_msg (handle->nl_sock_route, msg); + + ret = nl_send (handle->nl_sock_route, msg); + + nlmsg_free (msg); + if (ret <= 0) { + return -1; + } + + error = 0; + nl_socket_modify_cb (handle->nl_sock_route, NL_CB_VALID, NL_CB_CUSTOM, _route_wait_ack_or_error, &error); + nl_socket_modify_cb (handle->nl_sock_route, NL_CB_INVALID, NL_CB_CUSTOM, _route_wait_ack_or_error, &error); + nl_socket_modify_cb (handle->nl_sock_route, NL_CB_ACK, NL_CB_CUSTOM, _route_wait_ack_or_error, &error); + nl_socket_modify_err_cb (handle->nl_sock_route, NL_CB_CUSTOM, _route_wait_error, &error); + + ret = nl_recvmsgs_default (handle->nl_sock_route); + + if (ret < 0 || error < 0) { + return -1; + } + + return 0; +} + +int routes_del (NetworkInadorHandle *handle, Route *route) { + struct nl_msg * msg; + struct rtmsg route_hdr; + int ret, error; + int family_size = 0; + struct_addr empty; + int hop_count; + RouteNH *nh; + GList *g; + + /* TODO: Pendiente escribir esto */ +} + void routes_ask (NetworkInadorHandle *handle) { struct nl_msg * msg; struct rtmsg rtm_hdr = { diff --git a/src/routes.h b/src/routes.h index ce8db15..b90aaea 100644 --- a/src/routes.h +++ b/src/routes.h @@ -32,6 +32,7 @@ void routes_init (NetworkInadorHandle *handle); void routes_ask (NetworkInadorHandle *handle); int routes_receive_message_newroute (struct nl_msg *msg, void *arg); int routes_receive_message_delroute (struct nl_msg *msg, void *arg); +int routes_add (NetworkInadorHandle *handle, Route *route); void route_ask_delayed_delroute (NetworkInadorHandle *handle);