NetworkInador/client-gtk/ni-route.c

473 lines
12 KiB
C

/*
* 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#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->tos != tos) 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_route_tos (NIRoute *ni_route) {
g_return_val_if_fail (NI_IS_ROUTE (ni_route), 0);
return ni_route->priv->tos;
}
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;
}