/* * ni-route.c * This file is part of NetworkInador * * Copyright (C) 2021 - Félix Arreola Rodríguez * * NetworkInador 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. * * NetworkInador 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 NetworkInador; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #include #include #include #include "ni-client.h" #include "ni-route.h" #include "ni-ip.h" #include "../src/network-inador-manager.h" struct _NIRoutePrivate { NIClient *ni_client; guint family; gint family_size; guint type; uint32_t table; struct_addr dest; guint prefix; guint protocol, tos, scope; gboolean has_prefsrc; struct_addr prefsrc; guint priority; /* Múltiples GW */ guint nh; NIRouteNH *nexthops; }; enum { PROP_NI_CLIENT = 1, N_PROPERTIES }; G_DEFINE_TYPE_WITH_PRIVATE (NIRoute, ni_route, G_TYPE_OBJECT) static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; enum { ROUTE_UPDATED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; static gboolean ni_route_process_route_response (NIClient *ni_client, unsigned char *buffer, guint size, gpointer data); static void ni_route_constructed (GObject *obj) { NIRoute *ni_route; GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT); parent_class->constructed (obj); ni_route = NI_ROUTE (obj); if (ni_route->priv->ni_client != NULL) { /* Conectar la señal de datos */ g_signal_connect (ni_route->priv->ni_client, "packet-read", G_CALLBACK (ni_route_process_route_response), ni_route); } } static void ni_route_dispose (GObject *obj) { NIRoute *ni_route; GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT); ni_route = NI_ROUTE (obj); g_signal_handlers_disconnect_by_func (ni_route->priv->ni_client, G_CALLBACK (ni_route_process_route_response), ni_route); parent_class->dispose (obj); } static void ni_route_finalize (GObject *obj) { NIRoute *ni_route; GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT); ni_route = NI_ROUTE (obj); /* Nada que hacer, por el momento */ parent_class->finalize (obj); } static void ni_route_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NIRoute *ni_route = NI_ROUTE (object); g_return_if_fail (NI_IS_ROUTE (object)); switch (prop_id) { case PROP_NI_CLIENT: ni_route->priv->ni_client = NI_CLIENT (g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void ni_route_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NIRoute *ni_route = NI_ROUTE (object); g_return_if_fail (NI_IS_ROUTE (object)); switch (prop_id) { case PROP_NI_CLIENT: g_value_set_object (value, ni_route->priv->ni_client); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void ni_route_class_init (NIRouteClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = ni_route_set_property; object_class->get_property = ni_route_get_property; object_class->constructed = ni_route_constructed; object_class->dispose = ni_route_dispose; object_class->finalize = ni_route_finalize; 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); signals[ROUTE_UPDATED] = g_signal_new ("updated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void ni_route_init (NIRoute *ni_route) { NIRoutePrivate *priv = ni_route_get_instance_private (ni_route); ni_route->priv = priv; /* initialize all public and private members to reasonable default values. * They are all automatically initialized to 0 to begin with. */ priv->ni_client = NULL; priv->family = AF_UNSPEC; priv->type = 0; priv->table = 0; priv->prefix = 0; priv->protocol = 0; priv->tos = 0; priv->scope = 0; priv->priority = 0; priv->has_prefsrc = FALSE; priv->nh = 0; priv->nexthops = NULL; memset (&priv->dest, 0, sizeof (priv->dest)); memset (&priv->prefsrc, 0, sizeof (priv->prefsrc)); } static gboolean ni_route_process_route_response (NIClient *ni_client, unsigned char *buffer, guint size, gpointer user_data) { /* Tengo que revisar si los datos son de mi interés, para absorber el evento yo */ NIRoute *ni_route = (NIRoute *) user_data; uint8_t family, family_size; uint8_t type, protocol, tos, scope, prefix; uint32_t tabla, prioridad; uint8_t num_nexthops; gboolean has_prefsrc; int pos, g; NIRouteNH nexthops[16]; struct_addr dest, prefsrc; int was_update = 0; memset (nexthops, 0, sizeof (nexthops)); family = buffer[2]; if (buffer[0] != NET_INADOR_TYPE_EVENT && buffer[1] != NET_INADOR_EVENT_ROUTE_ADDED) { return FALSE; /* No es para mí */ } if (family == AF_INET) { family_size = sizeof (struct in_addr); } else if (family == AF_INET6) { family_size = sizeof (struct in6_addr); } type = buffer[3]; memcpy (&tabla, &buffer[4], 4); prefix = buffer[8]; protocol = buffer[9]; tos = buffer[10]; scope = buffer[11]; has_prefsrc = FALSE; if (buffer[12] & 0x01) { has_prefsrc = TRUE; } num_nexthops = buffer[13]; memcpy (&prioridad, &buffer[14], 4); memcpy (&dest, &buffer[18], family_size); pos = 18 + family_size; if (has_prefsrc) { memcpy (&prefsrc, &buffer[pos], family_size); pos += family_size; } /* Tengo suficientes datos, revisar si es mi ruta */ if (ni_route->priv->family != family) return FALSE; if (ni_route->priv->type != type) return FALSE; if (ni_route->priv->table != tabla) return FALSE; if (ni_route->priv->priority != prioridad) return FALSE; if (memcmp (&ni_route->priv->dest, &dest, family_size) != 0 || ni_route->priv->prefix != prefix) { return FALSE; } /* Terminar de leer los otros datos */ if (num_nexthops > 16) { num_nexthops = 16; } for (g = 0; g < num_nexthops; g++) { nexthops[g].has_gw = FALSE; if (buffer[pos] & 0x01) { /* Tiene GW */ nexthops[g].has_gw = TRUE; } pos++; nexthops[g].flags = buffer[pos]; pos++; nexthops[g].weight = buffer[pos]; pos++; pos++; /* El byte ignorado */ memcpy (&nexthops[g].out_index, &buffer[pos], 4); pos +=4; if (nexthops[g].has_gw) { memcpy (&nexthops[g].gw, &buffer[pos], family_size); pos += family_size; } } /* Es mi ruta, procesar como actualización */ if (ni_route->priv->protocol != protocol) { was_update = 1; ni_route->priv->protocol = protocol; } if (ni_route->priv->tos != tos) { was_update = 1; ni_route->priv->tos = tos; } if (ni_route->priv->scope != scope) { was_update = 1; ni_route->priv->scope = scope; } if (ni_route->priv->has_prefsrc != has_prefsrc) { ni_route->priv->has_prefsrc = has_prefsrc; was_update = 1; if (has_prefsrc == FALSE) { memset (&ni_route->priv->prefsrc, 0, family_size); } else { memcpy (&ni_route->priv->prefsrc, &prefsrc, ni_route->priv->family_size); } } else if (ni_route->priv->has_prefsrc) { was_update = 1; memcpy (&ni_route->priv->prefsrc, &prefsrc, ni_route->priv->family_size); } if (ni_route->priv->nh != num_nexthops) { /* Liberar los next-hops anteriores y re-crear los nuevos */ ni_route->priv->nh = num_nexthops; free (ni_route->priv->nexthops); ni_route->priv->nexthops = (NIRouteNH *) malloc (sizeof (NIRouteNH) * num_nexthops); for (g = 0; g < num_nexthops; g++) { /* Copiar cada next-hop */ memcpy (&ni_route->priv->nexthops[g], &nexthops[g], sizeof (NIRouteNH)); } was_update = 1; } else { for (g = 0; g < num_nexthops; g++) { /* Comparar brincos */ if (memcmp (&ni_route->priv->nexthops[g], &nexthops[g], sizeof (NIRouteNH)) != 0) { was_update = 1; memcpy (&ni_route->priv->nexthops[g], &nexthops[g], sizeof (NIRouteNH)); } } } /* Emitir mi señal de update */ if (was_update) { g_signal_emit (ni_route, signals[ROUTE_UPDATED], 0); } /* Mi evento, ya lo procesé */ return TRUE; } /* Los métodos getters */ NIClient *ni_route_get_client (NIRoute *ni_route) { g_return_val_if_fail (NI_IS_ROUTE (ni_route), NULL); return ni_route->priv->ni_client; } guint ni_route_get_route_type (NIRoute *ni_route) { g_return_val_if_fail (NI_IS_ROUTE (ni_route), 0); return ni_route->priv->type; } guint ni_route_get_family (NIRoute *ni_route) { g_return_val_if_fail (NI_IS_ROUTE (ni_route), 0); return ni_route->priv->family; } const struct_addr *ni_route_get_dest (NIRoute *ni_route) { g_return_val_if_fail (NI_IS_ROUTE (ni_route), NULL); return &ni_route->priv->dest; } guint ni_route_get_prefix (NIRoute *ni_route) { g_return_val_if_fail (NI_IS_ROUTE (ni_route), 0); return ni_route->priv->prefix; } uint32_t ni_route_get_table (NIRoute *ni_route) { g_return_val_if_fail (NI_IS_ROUTE (ni_route), 0); return ni_route->priv->table; } guint ni_route_get_num_nexthops (NIRoute *ni_route) { g_return_val_if_fail (NI_IS_ROUTE (ni_route), 0); return ni_route->priv->nh; } const NIRouteNH *ni_route_get_nexthops (NIRoute *ni_route) { g_return_val_if_fail (NI_IS_ROUTE (ni_route), NULL); return ni_route->priv->nexthops; } gboolean ni_route_has_prefsrc (NIRoute *ni_route) { g_return_val_if_fail (NI_IS_ROUTE (ni_route), FALSE); return ni_route->priv->has_prefsrc; } const struct_addr *ni_route_get_prefsrc (NIRoute *ni_route) { g_return_val_if_fail (NI_IS_ROUTE (ni_route), NULL); return &ni_route->priv->prefsrc; } guint ni_route_get_priority (NIRoute *ni_route) { g_return_val_if_fail (NI_IS_ROUTE (ni_route), 0); return ni_route->priv->priority; } /* guint family; guint type; guint table; struct_addr dest; guint prefix; guint protocol, tos, scope; gboolean has_prefsrc; struct_addr prefsrc; guint priority; */ NIRoute *ni_route_new (NIClient *ni_client, int family, int type, uint32_t table, struct_addr *dest, int prefix, unsigned char protocol, unsigned char tos, unsigned char scope, struct_addr *prefsrc, int priority, int num_nexthops, NIRouteNH *nexthops) { NIRoute *ni_route; int g; g_return_val_if_fail (NI_IS_CLIENT (ni_client), NULL); g_return_val_if_fail (dest != NULL, NULL); g_return_val_if_fail (num_nexthops > 0, NULL); ni_route = g_object_new (NI_TYPE_ROUTE, "ni-client", ni_client, NULL); if (family == AF_INET) { ni_route->priv->family_size = sizeof (struct in_addr); } else if (family == AF_INET6) { ni_route->priv->family_size = sizeof (struct in6_addr); } /* Establecer y copiar los valores */ ni_route->priv->family = family; ni_route->priv->type = type; ni_route->priv->table = table; ni_route->priv->prefix = prefix; memcpy (&ni_route->priv->dest, dest, ni_route->priv->family_size); ni_route->priv->protocol = protocol; ni_route->priv->tos = tos; ni_route->priv->scope = scope; ni_route->priv->priority = priority; if (prefsrc != NULL) { ni_route->priv->has_prefsrc = TRUE; memcpy (&ni_route->priv->prefsrc, prefsrc, ni_route->priv->family_size); } ni_route->priv->nh = num_nexthops; ni_route->priv->nexthops = (NIRouteNH *) malloc (sizeof (NIRouteNH) * num_nexthops); for (g = 0; g < num_nexthops; g++) { /* Copiar cada next-hop */ memcpy (&ni_route->priv->nexthops[g], &nexthops[g], sizeof (NIRouteNH)); } return ni_route; }