diff --git a/src/Makefile.am b/src/Makefile.am index 9703ac1..91ccb71 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,6 +9,7 @@ network_inador_SOURCES = main.c \ bridge.c bridge.h \ manager.c manager.h \ dhcp_client.c dhcp_client.h \ + routes.c routes.h \ wireless_if.c wireless_if.h \ wireless_bss.c wireless_bss.h diff --git a/src/common.h b/src/common.h index 2fee78f..ddc3928 100644 --- a/src/common.h +++ b/src/common.h @@ -61,7 +61,7 @@ typedef union { typedef struct _IPAddr { sa_family_t family; - int prefix; + uint8_t prefix; struct_addr local_addr; struct_addr addr; struct_addr brd_addr; @@ -69,12 +69,12 @@ typedef struct _IPAddr { char label[256]; struct ifa_cacheinfo cacheinfo; - int is_p2p; - int has_brd; - int has_local; + uint8_t is_p2p; + uint8_t has_brd; + uint8_t has_local; uint32_t flags; - unsigned char scope; + uint8_t scope; Interface *iface; } IPAddr; @@ -158,8 +158,6 @@ typedef struct _InterfaceDHCPClientInfo { struct_addr dhcp_server_ip; uint32_t lease_time; - - /* TODO: Falta almacenar la información de DNS */ } InterfaceDHCPClientInfo; struct _Interface { @@ -220,9 +218,37 @@ typedef struct _netlink_event_pair { guint source; } NetlinkEventPair; +typedef struct _RouteNH { + struct_addr gw; + uint32_t out_index; + uint8_t nh_weight; + uint8_t nh_flags; +} RouteNH; + +/* La tabla de ruteo */ +typedef struct _Route { + sa_family_t family; /* AF_INET, AF_INET6 */ + + uint8_t type; /* Unicast, local, broadcast, etc... */ + uint32_t table; + struct_addr dest; + uint8_t prefix; + + uint8_t protocol; + uint8_t tos; + uint8_t scope; + + struct_addr prefsrc; + uint32_t priority; + + /* Los brincos */ + GList *nexthops; +} Route; + struct _NetworkInadorHandle { GList *interfaces; - //Routev4 *rtable_v4; + GList *route_v4_tables; + GList *route_v6_tables; NetworkInadorManager *manager; diff --git a/src/interfaces.c b/src/interfaces.c index 6015a4f..3ddf28a 100644 --- a/src/interfaces.c +++ b/src/interfaces.c @@ -643,3 +643,18 @@ void interfaces_init (NetworkInadorHandle *handle) { ip_address_init (handle); } +void interfaces_clean_up (NetworkInadorHandle *handle) { + GList *g; + Interface *iface; + + for (g = handle->interfaces; g != NULL; g = g->next) { + iface = (Interface *) g->data; + /* Antes de eliminar la interfaz, eliminar la lista ligada de todas las direcciones IP */ + g_list_free_full (iface->address, free); + + iface->address = NULL; + } + + g_list_free_full (handle->interfaces, free); + handle->interfaces = NULL; +} diff --git a/src/interfaces.h b/src/interfaces.h index e1be4d2..c7839df 100644 --- a/src/interfaces.h +++ b/src/interfaces.h @@ -44,5 +44,7 @@ int interfaces_change_set_up (NetworkInadorHandle *handle, int index); int interfaces_change_set_down (NetworkInadorHandle *handle, int index); int interfaces_change_name (NetworkInadorHandle *handle, int index, char * new_name); +void interfaces_clean_up (NetworkInadorHandle *handle); + #endif diff --git a/src/main.c b/src/main.c index cb5cf6e..e76db6f 100644 --- a/src/main.c +++ b/src/main.c @@ -45,6 +45,7 @@ #include "ip-address.h" #include "bridge.h" #include "dhcp_client.h" +#include "routes.h" /* Usados para salir en caso de una señal */ static int sigterm_pipe_fds[2] = { -1, -1 }; @@ -70,6 +71,8 @@ static void _sigterm_handler (int signum) { static gboolean _main_quit_handler (GIOChannel *source, GIOCondition cond, gpointer data) { GMainLoop *loop = (GMainLoop *) data; g_main_loop_quit (loop); + + return FALSE; } static void _main_setup_signal (void *loop) { @@ -104,6 +107,7 @@ static void _main_setup_signal (void *loop) { g_io_channel_set_close_on_unref (io, TRUE); g_io_add_watch (io, G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR, _main_quit_handler, loop); + g_io_channel_unref (io); } } @@ -136,8 +140,12 @@ int main (int argc, char *argv[]) { _main_setup_signal (loop); + /* Inicializar las interfaces (y las direcciones IP) */ interfaces_init (&handle); + /* Inicializar las rutas */ + routes_init (&handle); + manager_init (&handle); g_main_loop_run (loop); @@ -145,6 +153,8 @@ int main (int argc, char *argv[]) { /* Detener la llegada de eventos */ netlink_events_clear (&handle); + routes_clean_up (&handle); + interfaces_clean_up (&handle); // nl_socket_free??? return 0; diff --git a/src/manager.c b/src/manager.c index 9eeb25c..df88c47 100644 --- a/src/manager.c +++ b/src/manager.c @@ -1004,6 +1004,160 @@ void _manager_send_dhcp_status (ManagerClientInfo *manager_client, InterfaceDHCP send (manager_client->fd, buffer, pos, 0); } +void _manager_send_route (ManagerClientInfo *manager_client, Route *route, gboolean is_event) { + unsigned char buffer[80]; + int family_size = 0; + int pos, has_gw; + struct_addr empty; + GList *g; + RouteNH *nexthop; + + memset (&empty, 0, sizeof (empty)); + + if (is_event) { + buffer[0] = NET_INADOR_TYPE_EVENT; + buffer[1] = NET_INADOR_EVENT_ROUTE_ADDED; + } else { + buffer[0] = NET_INADOR_TYPE_RESPONSE; + buffer[1] = NET_INADOR_RESPONSE_ROUTE; + } + + /* Familia y máscara */ + if (route->family == AF_INET) { + family_size = sizeof (struct in_addr); + } else if (route->family == AF_INET6) { + family_size = sizeof (struct in6_addr); + } + buffer[2] = route->family; + buffer[3] = route->type; + memcpy (&buffer[4], &route->table, 4); + buffer[8] = route->prefix; + buffer[9] = route->protocol; + buffer[10] = route->tos; + buffer[11] = route->scope; + buffer[12] = 0; + if (memcmp (&empty, &route->prefsrc, family_size) != 0) { + /* Tiene pref-src */ + buffer[12] |= 0x01; + } + + buffer[13] = 0; /* Los next-hop se incrementarán conforme los recorra */ + memcpy (&buffer[14], &route->priority, 4); + + /* Copiar la Ruta de destino */ + memcpy (&buffer[18], &route->dest, family_size); + pos = 18 + family_size; + + /* Copiar el pref-src, si lo tiene */ + if (buffer[12] & 0x01) { + memcpy (&buffer[pos], &route->prefsrc, family_size); + pos += family_size; + } + + /* TODO: Recorrer todos los nexthops */ + g = route->nexthops; + while (g != NULL) { + nexthop = (RouteNH *) g->data; + + buffer[pos] = 0; + has_gw = 0; + if (memcmp (&nexthop->gw, &empty, family_size) != 0) { + has_gw = 1; + buffer[pos] |= 0x01; + } + pos++; + buffer[pos++] = nexthop->nh_flags; /* Flags next-hop */ + buffer[pos++] = nexthop->nh_weight; /* Weight next-hop */ + + buffer[pos++] = 0; /* Omitimos un byte faltante */ + + /* La interfaz de salida */ + memcpy (&buffer[pos], &nexthop->out_index, 4); + pos += 4; + + if (has_gw) { + memcpy (&buffer[pos], &nexthop->gw, family_size); + pos += family_size; + } + + /* El contador de next-hops */ + buffer[13]++; + + g = g->next; + } + + send (manager_client->fd, buffer, pos, 0); +} + +void _manager_send_route_del (ManagerClientInfo *manager_client, Route *route) { + unsigned char buffer[80]; + int family_size = 0; + + buffer[0] = NET_INADOR_TYPE_EVENT; + buffer[1] = NET_INADOR_EVENT_ROUTE_REMOVED; + + /* Familia y máscara */ + if (route->family == AF_INET) { + family_size = sizeof (struct in_addr); + } else if (route->family == AF_INET6) { + family_size = sizeof (struct in6_addr); + } + buffer[2] = route->family; + buffer[3] = route->type; + memcpy (&buffer[4], &route->table, 4); + buffer[8] = route->prefix; + memcpy (&buffer[9], &route->priority, 4); + + /* Copiar la Ruta de destino */ + memcpy (&buffer[13], &route->dest, family_size); + + send (manager_client->fd, buffer, 13 + family_size, 0); +} + +void _manager_send_list_routes (ManagerClientInfo *manager_client, unsigned char *buffer, int buffer_len) { + int family; + GList *g; + Route *route; + uint32_t table; + + if (buffer_len < 7) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_LIST_ROUTES); + return; + } + + family = buffer[6]; + + if (family != AF_UNSPEC && family != AF_INET && family != AF_INET6) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INVALID_FAMILY, NET_INADOR_COMMAND_LIST_ROUTES); + + return; + } + + memcpy (&table, &buffer[2], 4); + + if (family == AF_UNSPEC || family == AF_INET) { + for (g = manager_client->manager->handle->route_v4_tables; g != NULL; g = g->next) { + route = (Route *) g->data; + + if (table != 0 && route->table != table) continue; + + _manager_send_route (manager_client, route, FALSE); + } + } + + if (family == AF_UNSPEC || family == AF_INET6) { + for (g = manager_client->manager->handle->route_v6_tables; g != NULL; g = g->next) { + route = (Route *) g->data; + + if (table != 0 && route->table != table) continue; + + _manager_send_route (manager_client, route, FALSE); + } + } + + _manager_send_end_command (manager_client, NET_INADOR_COMMAND_LIST_ROUTES); +} + static gboolean _manager_client_data (GIOChannel *source, GIOCondition condition, gpointer data) { ManagerClientInfo *manager_client = (ManagerClientInfo *) data; NetworkInadorManager *manager = manager_client->manager; @@ -1102,6 +1256,9 @@ static gboolean _manager_client_data (GIOChannel *source, GIOCondition condition case NET_INADOR_COMMAND_GET_DHCP_STATUS: _manager_execute_dhcp_get_status (manager_client, buffer, bytes); break; + case NET_INADOR_COMMAND_LIST_ROUTES: + _manager_send_list_routes (manager_client, buffer, bytes); + break; default: _manager_send_error (manager_client, NET_INADOR_ERROR_WRONG_COMMAND, command); } @@ -1246,6 +1403,48 @@ void manager_send_event_dhcp_change (NetworkInadorHandle *handle, InterfaceDHCPC } } +void manager_send_event_route_add (NetworkInadorHandle *handle, Route *route) { + char buffer_ip[1024]; + inet_ntop (route->family, &route->dest, buffer_ip, sizeof (buffer_ip)); + printf ("___ MANAGER ___ Informando ruta agregada: %s/%i [Tabla %i]\n", buffer_ip, route->prefix, route->table); + GList *g; + ManagerClientInfo *manager_client; + + if (handle->manager == NULL) return; + + for (g = handle->manager->connected_client_list; g != NULL; g = g->next) { + manager_client = (ManagerClientInfo *) g->data; + + if (manager_client->wanted_events & NET_INADOR_EVENT_MASK_ROUTES) { + printf ("___ MANAGER ___ Informando a la conexión (%i)\n", manager_client->fd); + _manager_send_route (manager_client, route, TRUE); + } + } +} + +void manager_send_event_route_del (NetworkInadorHandle *handle, Route *route) { + char buffer_ip[1024]; + inet_ntop (route->family, &route->dest, buffer_ip, sizeof (buffer_ip)); + printf ("___ MANAGER ___ Informando ruta eliminada: %s/%i [Tabla %i]\n", buffer_ip, route->prefix, route->table); + GList *g; + ManagerClientInfo *manager_client; + + if (handle->manager == NULL) return; + + for (g = handle->manager->connected_client_list; g != NULL; g = g->next) { + manager_client = (ManagerClientInfo *) g->data; + + if (manager_client->wanted_events & NET_INADOR_EVENT_MASK_ROUTES) { + printf ("___ MANAGER ___ Informando a la conexión (%i)\n", manager_client->fd); + _manager_send_route_del (manager_client, route); + } + } +} + +void manager_send_event_route_update (NetworkInadorHandle *handle, Route *route) { + manager_send_event_route_add (handle, route); +} + int manager_init (NetworkInadorHandle *handle) { NetworkInadorManager *manager; struct sockaddr_un socket_name; diff --git a/src/manager.h b/src/manager.h index 382e000..1759da7 100644 --- a/src/manager.h +++ b/src/manager.h @@ -34,6 +34,9 @@ void manager_send_event_interface_update (NetworkInadorHandle *handle, Interface void manager_send_event_ip_add (NetworkInadorHandle *handle, IPAddr *ip_addr); void manager_send_event_ip_del (NetworkInadorHandle *handle, IPAddr *ip_addr); void manager_send_event_dhcp_change (NetworkInadorHandle *handle, InterfaceDHCPClientInfo *dhcpc); +void manager_send_event_route_add (NetworkInadorHandle *handle, Route *route); +void manager_send_event_route_del (NetworkInadorHandle *handle, Route *route); +void manager_send_event_route_update (NetworkInadorHandle *handle, Route *route); #endif /* __MANAGER_H__ */ diff --git a/src/netlink-events.c b/src/netlink-events.c index aeea5ab..e173efd 100644 --- a/src/netlink-events.c +++ b/src/netlink-events.c @@ -32,6 +32,7 @@ #include "common.h" #include "interfaces.h" #include "ip-address.h" +#include "routes.h" static int _netlink_events_route_dispatcher (struct nl_msg *msg, void *arg) { struct nlmsghdr *reply; @@ -51,6 +52,12 @@ static int _netlink_events_route_dispatcher (struct nl_msg *msg, void *arg) { case RTM_DELADDR: return ip_address_receive_message_deladdr (msg, arg); break; + case RTM_NEWROUTE: + return routes_receive_message_newroute (msg, arg); + break; + case RTM_DELROUTE: + return routes_receive_message_delroute (msg, arg); + break; } return NL_SKIP; @@ -104,7 +111,7 @@ void netlink_events_clear_pair (NetlinkEventPair *pair) { void netlink_events_setup (NetworkInadorHandle *handle) { netlink_events_create_pair (&handle->route_events, NETLINK_ROUTE); - nl_socket_add_memberships (handle->route_events.nl_sock, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV6_IFINFO, 0); + nl_socket_add_memberships (handle->route_events.nl_sock, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV6_IFINFO, RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, 0); nl_socket_modify_cb (handle->route_events.nl_sock, NL_CB_VALID, NL_CB_CUSTOM, _netlink_events_route_dispatcher, handle); } diff --git a/src/network-inador-manager.h b/src/network-inador-manager.h index aba457b..dd58b2c 100644 --- a/src/network-inador-manager.h +++ b/src/network-inador-manager.h @@ -13,6 +13,7 @@ enum { #define NET_INADOR_EVENT_MASK_INTERFACES 0x01 #define NET_INADOR_EVENT_MASK_IP 0x02 #define NET_INADOR_EVENT_MASK_DHCP_STATUS 0x04 +#define NET_INADOR_EVENT_MASK_ROUTES 0x08 enum { NET_INADOR_COMMAND_LIST_IFACES = 1, @@ -39,6 +40,8 @@ enum { NET_INADOR_COMMAND_STOP_DHCP, NET_INADOR_COMMAND_GET_DHCP_STATUS, + NET_INADOR_COMMAND_LIST_ROUTES = 64, + NET_INADOR_COMMAND_SET_EVENT_MASK = 192, /* Los siguientes comandos son para uso interno */ @@ -63,7 +66,10 @@ enum { NET_INADOR_EVENT_IFACE_REMOVED, NET_INADOR_EVENT_IPADDR_REMOVED, - NET_INADOR_EVENT_DHCP_STATUS = 6 + NET_INADOR_EVENT_DHCP_STATUS = 6, + + NET_INADOR_EVENT_ROUTE_ADDED = 10, + NET_INADOR_EVENT_ROUTE_REMOVED, }; enum { @@ -72,7 +78,9 @@ enum { NET_INADOR_RESPONSE_IFACE = 2, NET_INADOR_RESPONSE_IPADDR, - NET_INADOR_RESPONSE_DHCP_STATUS = 6 + NET_INADOR_RESPONSE_DHCP_STATUS = 6, + + NET_INADOR_RESPONSE_ROUTE = 10, }; enum { diff --git a/src/routes.c b/src/routes.c new file mode 100644 index 0000000..43fa1dc --- /dev/null +++ b/src/routes.c @@ -0,0 +1,382 @@ +/* + * 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 +#include +#include + +#include + +#include + +#include +#include + +#include "common.h" +#include "routes.h" +#include "manager.h" + +Route *_route_search_route (GList *list_routes, sa_family_t family, uint8_t type, uint32_t table, void *dest, uint32_t prefix, uint32_t priority) { + GList *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; + if (route->type != type) continue; + if (route->table != table) continue; + if (route->priority != priority) continue; + + if (memcmp (&route->dest, dest, family_size) == 0 && route->prefix == prefix) { + return route; + } + } + + return NULL; +} + +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; + struct_addr dest; + uint32_t priority = 0; + int was_new = 0; + GList *route_list = 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 tipo de ruta */ + 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, route_type, 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->priority = priority; + memcpy (&route->dest, &dest, family_size); + + if (family == AF_INET) { + handle->route_v4_tables = g_list_append (handle->route_v4_tables, route); + } else if (family == AF_INET6) { + handle->route_v6_tables = g_list_append (handle->route_v6_tables, route); + } + was_new = 1; + } else { + /* Liberar los next-hops, puesto que volverán a ser creados */ + g_list_free_full (route->nexthops, free); + route->nexthops = NULL; + } + + route->protocol = rtm_hdr->rtm_protocol; + route->tos = rtm_hdr->rtm_tos; + route->scope = rtm_hdr->rtm_scope; + + /* 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 = g_list_append (route->nexthops, next_hop); + + next_hop = (RouteNH *) malloc (sizeof (RouteNH)); + memset (next_hop, 0, sizeof (RouteNH)); + } + } + break; + } + } + + route->nexthops = g_list_append (route->nexthops, next_hop); + + if (was_new) { + /* Enviar aquí evento de ruta agregada */ + manager_send_event_route_add (handle, route); + } else { + /* 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_type; + struct_addr dest; + uint32_t priority = 0; + GList *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 tipo de ruta */ + 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; + } + } + + route = _route_search_route (route_list, family, route_type, 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 = g_list_remove (handle->route_v4_tables, route); + } else if (family == AF_INET6) { + handle->route_v6_tables = g_list_remove (handle->route_v6_tables, route); + } + + /* Notificar del evento */ + manager_send_event_route_del (handle, route); + + /* Eliminar todos los next-hops primero */ + g_list_free_full (route->nexthops, free); + + free (route); + + return NL_SKIP; +} + +void routes_init (NetworkInadorHandle *handle) { + /* Si es la primera vez que nos llaman, descargar una primera lista de interfaces */ + struct nl_msg * msg; + struct rtmsg rtm_hdr = { + .rtm_family = AF_UNSPEC, + }; + int ret; + + 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); +} + +void routes_clean_up (NetworkInadorHandle *handle) { + GList *g; + Route *route; + + for (g = handle->route_v4_tables; g != NULL; g = g->next) { + route = (Route *) g->data; + + g_list_free_full (route->nexthops, free); + route->nexthops = NULL; + } + + g_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; + + g_list_free_full (route->nexthops, free); + route->nexthops = NULL; + } + + g_list_free_full (handle->route_v6_tables, free); + handle->route_v6_tables = NULL; +} + diff --git a/src/routes.h b/src/routes.h new file mode 100644 index 0000000..72ba0cd --- /dev/null +++ b/src/routes.h @@ -0,0 +1,38 @@ +/* + * routes.h + * 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 + */ + +#ifndef __ROUTES_H__ +#define __ROUTES_H__ + +#include +#include + +#include "common.h" + +void routes_init (NetworkInadorHandle *handle); +int routes_receive_message_newroute (struct nl_msg *msg, void *arg); +int routes_receive_message_delroute (struct nl_msg *msg, void *arg); + +void routes_clean_up (NetworkInadorHandle *handle); + +#endif /* __ROUTES_H__ */ +