905 lines
23 KiB
C
905 lines
23 KiB
C
/*
|
|
* routes.c
|
|
* This file is part of Network-inador
|
|
*
|
|
* Copyright (C) 2022 - Félix Arreola Rodríguez
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <stdint.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include <netlink/socket.h>
|
|
#include <netlink/msg.h>
|
|
|
|
#include "flist.h"
|
|
#include "network-inador-private.h"
|
|
#include "routes.h"
|
|
//#include "manager.h"
|
|
|
|
int _route_compare_nexthop_v4 (const void * a, const void * b);
|
|
int _route_compare_nexthop_v6 (const void * a, const void * b);
|
|
|
|
void route_ask_delayed_delroute (NetworkInadorHandle *handle) {
|
|
if (handle->pipe_routes[1] > 0) {
|
|
write (handle->pipe_routes[1], "", 1);
|
|
}
|
|
}
|
|
|
|
int _route_same_list_nexthops (int family, FList *nexthops_a, FList *nexthops_b) {
|
|
int count_a, count_b;
|
|
RouteNH *nha, *nhb;
|
|
|
|
FList *p_a, *p_b;
|
|
|
|
count_a = f_list_length (nexthops_a);
|
|
count_b = f_list_length (nexthops_b);
|
|
|
|
if (count_a != count_b) return 1;
|
|
|
|
p_a = nexthops_a;
|
|
p_b = nexthops_b;
|
|
|
|
while (p_a != NULL) {
|
|
nha = (RouteNH *) p_a->data;
|
|
nhb = (RouteNH *) p_b->data;
|
|
|
|
if (family == AF_INET) {
|
|
if (_route_compare_nexthop_v4 (nha, nhb) != 0) return 1;
|
|
} else if (family == AF_INET6) {
|
|
if (_route_compare_nexthop_v6 (nha, nhb) != 0) return 1;
|
|
}
|
|
p_a = p_a->next;
|
|
p_b = p_b->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
Route *_route_search_route (FList *list_routes, sa_family_t family, uint8_t tos, uint32_t table, void *dest, uint32_t prefix, uint32_t priority) {
|
|
FList *g;
|
|
Route *route;
|
|
int family_size = 0;
|
|
|
|
if (family == AF_INET) {
|
|
family_size = sizeof (struct in_addr);
|
|
} else if (family == AF_INET6) {
|
|
family_size = sizeof (struct in6_addr);
|
|
}
|
|
|
|
for (g = list_routes; g != NULL; g = g->next) {
|
|
route = (Route *) g->data;
|
|
|
|
if (route->family != family) continue;
|
|
|
|
/* Por tos si genera diferencia */
|
|
if (route->tos != tos) continue;
|
|
if (route->table != table) continue;
|
|
/* Por métrica */
|
|
if (route->priority != priority) continue;
|
|
|
|
if (memcmp (&route->dest, dest, family_size) == 0 && route->prefix == prefix) {
|
|
return route;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int _route_compare_nexthop_v4 (const void * a, const void * b) {
|
|
int ret;
|
|
RouteNH *nha = (RouteNH *) a, *nhb = (RouteNH *) b;
|
|
|
|
ret = memcmp (&nha->gw, &nhb->gw, 4);
|
|
|
|
if (ret != 0) return ret;
|
|
|
|
ret = nha->out_index - nhb->out_index;
|
|
if (ret != 0) return ret;
|
|
|
|
ret = nha->nh_weight - nhb->nh_weight;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int _route_compare_nexthop_v6 (const void * a, const void * b) {
|
|
int ret;
|
|
RouteNH *nha = (RouteNH *) a, *nhb = (RouteNH *) b;
|
|
|
|
ret = memcmp (&nha->gw, &nhb->gw, 16);
|
|
|
|
if (ret != 0) return ret;
|
|
|
|
ret = nha->out_index - nhb->out_index;
|
|
if (ret != 0) return ret;
|
|
|
|
ret = nha->nh_weight - nhb->nh_weight;
|
|
|
|
return ret;
|
|
}
|
|
|
|
FList * _route_sort_nexthops (int family, FList *nexthops) {
|
|
if (family == AF_INET) {
|
|
return f_list_sort (nexthops, _route_compare_nexthop_v4);
|
|
} else if (family == AF_INET6) {
|
|
return f_list_sort (nexthops, _route_compare_nexthop_v6);
|
|
}
|
|
|
|
return nexthops;
|
|
}
|
|
|
|
int routes_receive_message_newroute (struct nl_msg *msg, void *arg) {
|
|
NetworkInadorHandle *handle = (NetworkInadorHandle *) arg;
|
|
struct nlmsghdr *reply;
|
|
Route *route = NULL;
|
|
uint32_t table, prefix;
|
|
int remaining, remaining2;
|
|
int family, family_size = 0;
|
|
struct rtmsg *rtm_hdr;
|
|
struct nlattr *attr, *nest_attr_gw;
|
|
RouteNH *next_hop;
|
|
struct rtnexthop *nhptr;
|
|
int rtnhp_len;
|
|
int multipath_len;
|
|
unsigned char *p;
|
|
uint8_t route_type, tos;
|
|
struct_addr dest;
|
|
uint32_t priority = 0;
|
|
int was_new = 0, was_update = 0;
|
|
FList *route_list = NULL;
|
|
FList *old_next_hops = NULL;
|
|
|
|
reply = nlmsg_hdr (msg);
|
|
|
|
if (reply->nlmsg_type != RTM_NEWROUTE) return NL_SKIP;
|
|
|
|
/* Recuperar la tabla */
|
|
rtm_hdr = nlmsg_data (reply);
|
|
table = rtm_hdr->rtm_table;
|
|
|
|
/* Recuperar la familia */
|
|
family = rtm_hdr->rtm_family;
|
|
if (family == AF_INET) {
|
|
family_size = sizeof (struct in_addr);
|
|
route_list = handle->route_v4_tables;
|
|
} else if (family == AF_INET6) {
|
|
family_size = sizeof (struct in6_addr);
|
|
route_list = handle->route_v6_tables;
|
|
}
|
|
|
|
/* Recuperar el tos, porque marca diferencia entre rutas */
|
|
tos = rtm_hdr->rtm_tos;
|
|
|
|
route_type = rtm_hdr->rtm_type;
|
|
|
|
/* Recuperar el prefijo */
|
|
prefix = rtm_hdr->rtm_dst_len;
|
|
|
|
/* Recuperar solo el destino */
|
|
memset (&dest, 0, sizeof (dest));
|
|
nlmsg_for_each_attr(attr, reply, sizeof (struct rtmsg), remaining) {
|
|
switch (nla_type (attr)) {
|
|
case RTA_DST:
|
|
if (nla_len (attr) != family_size) {
|
|
/* Tamaño incorrecto para la IP */
|
|
continue;
|
|
}
|
|
memcpy (&dest, nla_data (attr), family_size);
|
|
break;
|
|
case RTA_PRIORITY:
|
|
if (nla_len (attr) != 4) {
|
|
/* Tamaño incorrecto para la prioridad */
|
|
continue;
|
|
}
|
|
priority = nla_get_u32 (attr);
|
|
break;
|
|
case RTA_TABLE:
|
|
if (nla_len (attr) != 4) {
|
|
/* Tamaño incorrecto para el id de la tabla */
|
|
continue;
|
|
}
|
|
table = nla_get_u32 (attr);
|
|
break;
|
|
}
|
|
}
|
|
/* TODO: Revisar si la ruta que procesamos ya existe */
|
|
route = _route_search_route (route_list, family, tos, table, &dest, prefix, priority);
|
|
|
|
if (route == NULL) {
|
|
/* Nueva ruta */
|
|
route = (Route *) malloc (sizeof (Route));
|
|
memset (route, 0, sizeof (Route));
|
|
|
|
route->family = family;
|
|
route->type = route_type;
|
|
route->table = table;
|
|
route->prefix = prefix;
|
|
route->tos = tos;
|
|
|
|
route->priority = priority;
|
|
route->for_delete = 0;
|
|
memcpy (&route->dest, &dest, family_size);
|
|
|
|
if (family == AF_INET) {
|
|
handle->route_v4_tables = f_list_append (handle->route_v4_tables, route);
|
|
} else if (family == AF_INET6) {
|
|
handle->route_v6_tables = f_list_append (handle->route_v6_tables, route);
|
|
}
|
|
was_new = 1;
|
|
} else {
|
|
/* Liberar los next-hops, puesto que volverán a ser creados */
|
|
//f_list_free_full (route->nexthops, free);
|
|
old_next_hops = route->nexthops;
|
|
route->nexthops = NULL;
|
|
route->for_delete = 0;
|
|
}
|
|
|
|
if (route->protocol != rtm_hdr->rtm_protocol) {
|
|
route->protocol = rtm_hdr->rtm_protocol;
|
|
was_update = 1;
|
|
}
|
|
|
|
if (route->scope != rtm_hdr->rtm_scope) {
|
|
route->scope = rtm_hdr->rtm_scope;
|
|
was_update = 1;
|
|
}
|
|
|
|
/* Pre-reservar el primer siguiente brinco y ligar */
|
|
next_hop = (RouteNH *) malloc (sizeof (RouteNH));
|
|
memset (next_hop, 0, sizeof (RouteNH));
|
|
|
|
nlmsg_for_each_attr(attr, reply, sizeof (struct rtmsg), remaining) {
|
|
switch (nla_type (attr)) {
|
|
case RTA_PREFSRC:
|
|
if (nla_len (attr) != family_size) {
|
|
continue;
|
|
}
|
|
memcpy (&route->prefsrc, nla_data (attr), family_size);
|
|
break;
|
|
case RTA_GATEWAY:
|
|
if (nla_len (attr) != family_size) {
|
|
continue;
|
|
}
|
|
memcpy (&next_hop->gw, nla_data (attr), family_size);
|
|
break;
|
|
case RTA_OIF:
|
|
if (nla_len (attr) != 4) {
|
|
/* Tamaño incorrecto para el índice de la interfaz */
|
|
continue;
|
|
}
|
|
next_hop->out_index = nla_get_u32 (attr);
|
|
break;
|
|
/* TODO: Revisar si RTA_PREF, RTA_CACHEINFO es útil */
|
|
case RTA_MULTIPATH:
|
|
nhptr = (struct rtnexthop*) nla_data (attr);
|
|
multipath_len = nla_len (attr);
|
|
|
|
while (multipath_len > 0) {
|
|
next_hop->nh_flags = nhptr->rtnh_flags;
|
|
next_hop->nh_weight = nhptr->rtnh_hops;
|
|
next_hop->out_index = nhptr->rtnh_ifindex;
|
|
|
|
/* Revisar en los atributos si tiene gw */
|
|
p = ((unsigned char *) nhptr) + sizeof (struct rtnexthop);
|
|
nest_attr_gw = nla_find ((const struct nlattr *) p, nhptr->rtnh_len - sizeof (struct rtnexthop), RTA_GATEWAY);
|
|
|
|
if (nest_attr_gw != NULL && nla_len (nest_attr_gw) == family_size) {
|
|
memcpy (&next_hop->gw, nla_data (nest_attr_gw), family_size);
|
|
}
|
|
|
|
multipath_len -= nhptr->rtnh_len;
|
|
nhptr = (struct rtnexthop *) (((unsigned char *) nhptr) + nhptr->rtnh_len);
|
|
|
|
/* Reservar el siguiente next-hop */
|
|
if (multipath_len > 0) {
|
|
route->nexthops = f_list_append (route->nexthops, next_hop);
|
|
|
|
next_hop = (RouteNH *) malloc (sizeof (RouteNH));
|
|
memset (next_hop, 0, sizeof (RouteNH));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
route->nexthops = f_list_append (route->nexthops, next_hop);
|
|
route->nexthops = _route_sort_nexthops (family, route->nexthops);
|
|
|
|
if (_route_same_list_nexthops (family, route->nexthops, old_next_hops) == 1) {
|
|
/* Son diferentes */
|
|
was_update = 1;
|
|
}
|
|
|
|
/* Liberar la lista vieja de next_hops */
|
|
f_list_free_full (old_next_hops, free);
|
|
|
|
if (was_new) {
|
|
/* Enviar aquí evento de ruta agregada */
|
|
//manager_send_event_route_add (handle, route);
|
|
} else if (was_update) {
|
|
/* Enviar actualización */
|
|
//manager_send_event_route_update (handle, route);
|
|
}
|
|
return NL_SKIP;
|
|
}
|
|
|
|
int routes_receive_message_delroute (struct nl_msg *msg, void *arg) {
|
|
NetworkInadorHandle *handle = (NetworkInadorHandle *) arg;
|
|
struct nlmsghdr *reply;
|
|
Route *route = NULL;
|
|
uint32_t table, prefix;
|
|
int remaining;
|
|
int family, family_size = 0;
|
|
struct rtmsg *rtm_hdr;
|
|
struct nlattr *attr;
|
|
uint8_t route_tos;
|
|
struct_addr dest;
|
|
uint32_t priority = 0;
|
|
FList *route_list = NULL;
|
|
|
|
reply = nlmsg_hdr (msg);
|
|
if (reply->nlmsg_type != RTM_DELROUTE) return NL_SKIP;
|
|
|
|
/* Recuperar la tabla */
|
|
rtm_hdr = nlmsg_data (reply);
|
|
table = rtm_hdr->rtm_table;
|
|
|
|
/* Recuperar la familia */
|
|
family = rtm_hdr->rtm_family;
|
|
if (family == AF_INET) {
|
|
family_size = sizeof (struct in_addr);
|
|
route_list = handle->route_v4_tables;
|
|
} else if (family == AF_INET6) {
|
|
family_size = sizeof (struct in6_addr);
|
|
route_list = handle->route_v6_tables;
|
|
}
|
|
|
|
/* Recuperar el tos de la ruta*/
|
|
route_tos = rtm_hdr->rtm_tos;
|
|
|
|
/* Recuperar el prefijo */
|
|
prefix = rtm_hdr->rtm_dst_len;
|
|
|
|
/* Recuperar solo el destino */
|
|
memset (&dest, 0, sizeof (dest));
|
|
nlmsg_for_each_attr(attr, reply, sizeof (struct rtmsg), remaining) {
|
|
switch (nla_type (attr)) {
|
|
case RTA_DST:
|
|
if (nla_len (attr) != family_size) {
|
|
/* Tamaño incorrecto para la IP */
|
|
continue;
|
|
}
|
|
memcpy (&dest, nla_data (attr), family_size);
|
|
break;
|
|
case RTA_PRIORITY:
|
|
if (nla_len (attr) != 4) {
|
|
/* Tamaño incorrecto para la prioridad */
|
|
continue;
|
|
}
|
|
priority = nla_get_u32 (attr);
|
|
break;
|
|
case RTA_TABLE:
|
|
if (nla_len (attr) != 4) {
|
|
/* Tamaño incorrecto para el id de la tabla */
|
|
continue;
|
|
}
|
|
table = nla_get_u32 (attr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
route = _route_search_route (route_list, family, route_tos, table, &dest, prefix, priority);
|
|
|
|
if (route == NULL) {
|
|
/* ¿Notificación de eliminar una ruta que no existe? Super raro */
|
|
return NL_SKIP;
|
|
}
|
|
|
|
if (family == AF_INET) {
|
|
/* Eliminar de la lista ligada */
|
|
handle->route_v4_tables = f_list_remove (handle->route_v4_tables, route);
|
|
} else if (family == AF_INET6) {
|
|
handle->route_v6_tables = f_list_remove (handle->route_v6_tables, route);
|
|
}
|
|
|
|
/* Notificar del evento */
|
|
//manager_send_event_route_del (handle, route);
|
|
|
|
/* Eliminar todos los next-hops primero */
|
|
f_list_free_full (route->nexthops, free);
|
|
|
|
free (route);
|
|
|
|
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;
|
|
FList *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 = f_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;
|
|
FList *g;
|
|
|
|
memset (&route_hdr, 0, sizeof (route_hdr));
|
|
|
|
route_hdr.rtm_family = route->family;
|
|
route_hdr.rtm_dst_len = route->prefix;
|
|
route_hdr.rtm_tos = route->tos;
|
|
route_hdr.rtm_table = route->table;
|
|
|
|
msg = nlmsg_alloc_simple (RTM_DELROUTE, NLM_F_REQUEST);
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void routes_ask (NetworkInadorHandle *handle) {
|
|
struct nl_msg * msg;
|
|
struct rtmsg rtm_hdr = {
|
|
.rtm_family = AF_UNSPEC,
|
|
};
|
|
int ret;
|
|
FList *g, *n;
|
|
Route *r;
|
|
|
|
/* Recorrer todas las rutas en la tabla de ruteo y marcarlas para eliminación */
|
|
for (g = handle->route_v4_tables; g != NULL; g = g->next) {
|
|
r = (Route *) g->data;
|
|
|
|
r->for_delete = 1;
|
|
}
|
|
|
|
for (g = handle->route_v6_tables; g != NULL; g = g->next) {
|
|
r = (Route *) g->data;
|
|
|
|
r->for_delete = 1;
|
|
}
|
|
|
|
/* Ahora sí, mandar la petición */
|
|
msg = nlmsg_alloc_simple (RTM_GETROUTE, NLM_F_REQUEST | NLM_F_DUMP);
|
|
if (msg == NULL) {
|
|
return;
|
|
}
|
|
ret = nlmsg_append (msg, &rtm_hdr, sizeof (rtm_hdr), NLMSG_ALIGNTO);
|
|
|
|
if (ret != 0) {
|
|
nlmsg_free (msg);
|
|
return;
|
|
}
|
|
|
|
nl_complete_msg (handle->nl_sock_route, msg);
|
|
|
|
ret = nl_send (handle->nl_sock_route, msg);
|
|
|
|
nlmsg_free (msg);
|
|
|
|
nl_socket_modify_cb (handle->nl_sock_route, NL_CB_VALID, NL_CB_CUSTOM, routes_receive_message_newroute, handle);
|
|
|
|
nl_recvmsgs_default (handle->nl_sock_route);
|
|
|
|
/* Ejecutar las eliminaciones */
|
|
for (g = handle->route_v4_tables; g != NULL; g = n) {
|
|
n = g->next;
|
|
|
|
r = (Route *) g->data;
|
|
if (r->for_delete == 0) continue;
|
|
|
|
handle->route_v4_tables = f_list_remove (handle->route_v4_tables, r);
|
|
|
|
/* Notificar del evento */
|
|
//manager_send_event_route_del (handle, r);
|
|
|
|
/* Eliminar todos los next-hops primero */
|
|
f_list_free_full (r->nexthops, free);
|
|
|
|
free (r);
|
|
}
|
|
|
|
for (g = handle->route_v6_tables; g != NULL; g = n) {
|
|
n = g->next;
|
|
|
|
r = (Route *) g->data;
|
|
if (r->for_delete == 0) continue;
|
|
|
|
handle->route_v6_tables = f_list_remove (handle->route_v6_tables, r);
|
|
|
|
/* Notificar del evento */
|
|
//manager_send_event_route_del (handle, r);
|
|
|
|
/* Eliminar todos los next-hops primero */
|
|
f_list_free_full (r->nexthops, free);
|
|
|
|
free (r);
|
|
}
|
|
}
|
|
|
|
int _routes_table_find_by_number (const void * left, const void * right) {
|
|
RouteTable *a, *b;
|
|
|
|
a = (RouteTable *) left;
|
|
b = (RouteTable *) right;
|
|
|
|
return a->table != b->table;
|
|
}
|
|
|
|
void _routes_table_parse_file (NetworkInadorHandle *handle, FILE *fd) {
|
|
FList *g;
|
|
int ret;
|
|
RouteTable *rtable, temp_table;
|
|
char buffer[2048];
|
|
|
|
while (fgets (buffer, sizeof (buffer), fd), feof (fd) == 0) {
|
|
if (buffer[0] == '#') continue; /* Ignorar las lineas con comentarios */
|
|
|
|
ret = sscanf (buffer, "%d %s", &(temp_table.table), temp_table.name);
|
|
|
|
if (ret < 2) {
|
|
continue;
|
|
}
|
|
|
|
g = f_list_find_custom (handle->route_tables_names, &temp_table, _routes_table_find_by_number);
|
|
|
|
if (g != NULL) {
|
|
/* El número de tabla ya existe, marcar y actualizar el nombre */
|
|
rtable = (RouteTable *) g->data;
|
|
|
|
rtable->for_delete = 0;
|
|
rtable->was_new = 0;
|
|
} else {
|
|
/* No existe, crear nueva */
|
|
rtable = (RouteTable *) malloc (sizeof (RouteTable));
|
|
rtable->table = temp_table.table;
|
|
rtable->for_delete = 0;
|
|
rtable->was_new = 1;
|
|
}
|
|
/* En cualquier caso actualizar el nombre */
|
|
strncpy (rtable->name, temp_table.name, sizeof (rtable->name));
|
|
|
|
handle->route_tables_names = f_list_append (handle->route_tables_names, rtable);
|
|
}
|
|
}
|
|
|
|
void routes_tables_read (NetworkInadorHandle *handle, int do_notify) {
|
|
FILE *fd;
|
|
FList *g, *h;
|
|
RouteTable *rtable;
|
|
DIR *dir;
|
|
struct dirent *direntry;
|
|
int len;
|
|
char buffer[4096];
|
|
|
|
g = handle->route_tables_names;
|
|
while (g != NULL) {
|
|
rtable = (RouteTable *) g->data;
|
|
|
|
rtable->for_delete = 1;
|
|
rtable->was_new = 0;
|
|
g = g->next;
|
|
}
|
|
|
|
//f_list_free_full (handle->route_tables_names, g_free);
|
|
//handle->route_tables_names = NULL;
|
|
|
|
/* Intentar abrir /etc/iproute2/rt_tables */
|
|
fd = fopen ("/etc/iproute2/rt_tables", "r");
|
|
if (fd != NULL) {
|
|
_routes_table_parse_file (handle, fd);
|
|
|
|
fclose (fd);
|
|
}
|
|
|
|
/* Ahora leer todo el directorio /etc/iproute2/rt_tables.d/ y buscar archivos *.conf */
|
|
dir = opendir ("/etc/iproute2/rt_tables.d");
|
|
if (dir != NULL) {
|
|
|
|
while (direntry = readdir (dir), direntry != NULL) {
|
|
len = strlen (direntry->d_name);
|
|
|
|
/* Buscar por archivos que terminen en .conf */
|
|
if (len > 5 && strcmp (&(direntry->d_name[len - 5]), ".conf") == 0) {
|
|
/* Intentar abrir este archivo y parsearlo */
|
|
snprintf (buffer, sizeof (buffer), "/etc/iproute2/rt_tables.d/%s", direntry->d_name);
|
|
fd = fopen (buffer, "r");
|
|
|
|
if (fd != NULL) {
|
|
_routes_table_parse_file (handle, fd);
|
|
|
|
fclose (fd);
|
|
}
|
|
}
|
|
}
|
|
|
|
closedir (dir);
|
|
}
|
|
|
|
/* Ahora, todas las tablas que están marcadas para eliminar, eliminarlas */
|
|
g = handle->route_tables_names;
|
|
|
|
while (g != NULL) {
|
|
h = g->next;
|
|
|
|
rtable = (RouteTable *) g->data;
|
|
|
|
if (rtable->for_delete) {
|
|
handle->route_tables_names = f_list_delete_link (handle->route_tables_names, g);
|
|
|
|
if (do_notify) {
|
|
//manager_send_event_route_table_del (handle, rtable);
|
|
}
|
|
|
|
free (rtable);
|
|
}
|
|
|
|
g = h;
|
|
}
|
|
|
|
g = handle->route_tables_names;
|
|
while (g != NULL) {
|
|
rtable = (RouteTable *) g->data;
|
|
|
|
if (rtable->was_new) {
|
|
if (do_notify) {
|
|
//manager_send_event_route_table_add (handle, rtable);
|
|
}
|
|
rtable->was_new = 0;
|
|
}
|
|
|
|
g = g->next;
|
|
}
|
|
}
|
|
|
|
void routes_init (NetworkInadorHandle *handle) {
|
|
/* Si es la primera vez que nos llaman, descargar una primera lista de interfaces */
|
|
routes_ask (handle);
|
|
|
|
/* Inicializar los nombres de las tablas de ruteo */
|
|
routes_tables_read (handle, FALSE);
|
|
}
|
|
|
|
void routes_clean_up (NetworkInadorHandle *handle) {
|
|
FList *g;
|
|
Route *route;
|
|
|
|
for (g = handle->route_v4_tables; g != NULL; g = g->next) {
|
|
route = (Route *) g->data;
|
|
|
|
f_list_free_full (route->nexthops, free);
|
|
route->nexthops = NULL;
|
|
}
|
|
|
|
f_list_free_full (handle->route_v4_tables, free);
|
|
handle->route_v4_tables = NULL;
|
|
|
|
for (g = handle->route_v6_tables; g != NULL; g = g->next) {
|
|
route = (Route *) g->data;
|
|
|
|
f_list_free_full (route->nexthops, free);
|
|
route->nexthops = NULL;
|
|
}
|
|
|
|
f_list_free_full (handle->route_v6_tables, free);
|
|
handle->route_v6_tables = NULL;
|
|
|
|
/* FIXME: Falta liberar el nombre de las tablas */
|
|
}
|
|
|