473 lines
12 KiB
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;
|
|
}
|
|
|