/* * main.c * This file is part of Network-inador * * Copyright (C) 2024 - 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 #include #include #include "network-inador-private.h" #include "netlink-events.h" #include "interfaces.h" #include "routes.h" #include "resolv_manager.h" struct _NetworkInadorWatchedPID { unsigned int source; pid_t child_pid; NetworkInadorChildWatchFunc func; void *data; }; /* Variables si nosotros hacemos vigilancia de procesos */ static FList *network_inador_watched_childs = NULL; static unsigned int network_inador_watched_child_source_counter = 1; static int network_inador_pipes_child_notify[2] = {-1, -1}; static int network_inador_child_source_watcher = 0; static unsigned int network_inador_add_watch_process (pid_t pid, NetworkInadorChildWatchFunc func, void *user_data) { struct _NetworkInadorWatchedPID *watcher; watcher = (struct _NetworkInadorWatchedPID *) malloc (sizeof (struct _NetworkInadorWatchedPID)); assert (watcher != NULL); watcher->source = network_inador_watched_child_source_counter; network_inador_watched_child_source_counter++; watcher->child_pid = pid; watcher->func = func; watcher->data = user_data; network_inador_watched_childs = f_list_append (network_inador_watched_childs, watcher); return watcher->source; } static void network_inador_remove_watch_process (unsigned int source) { /* Recorrer la lista para eliminar el watch, ignorar si no está en la lista */ struct _NetworkInadorWatchedPID *watcher; FList *g, *next; g = network_inador_watched_childs; while (g != NULL) { next = g->next; watcher = (struct _NetworkInadorWatchedPID *) g->data; if (watcher->source == source) { network_inador_watched_childs = f_list_delete_link (network_inador_watched_childs, g); free (watcher); break; } g = next; } } static void _network_inador_try_setup_route_events (NetworkInadorHandle *handle) { struct nl_sock * sock_req; sock_req = nl_socket_alloc (); int fd; if (nl_connect (sock_req, NETLINK_ROUTE) != 0) { nl_socket_free (sock_req); return; } nl_socket_set_nonblocking (sock_req); nl_socket_disable_seq_check (sock_req); fd = nl_socket_get_fd (sock_req); /* Set close-on-exec */ fcntl (fd, F_SETFD, FD_CLOEXEC); handle->route_events.nl_sock = sock_req; handle->has_route_events = 1; handle->route_events.source = handle->ops.input_add (fd, POLLIN, netlink_events_route_events_handle_read, handle); nl_socket_add_memberships (sock_req, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV6_IFINFO, RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, 0); nl_socket_modify_cb (sock_req, NL_CB_VALID, NL_CB_CUSTOM, netlink_events_route_dispatcher, handle); } static void _network_inador_try_pipe_routes (NetworkInadorHandle *handle) { int ret, flags; int pipe_fds[2]; ret = pipe (pipe_fds); if (ret < 0) { return; } handle->pipe_routes[0] = pipe_fds[0]; handle->pipe_routes[1] = pipe_fds[1]; flags = fcntl (pipe_fds[0], F_GETFL); fcntl (pipe_fds[0], F_SETFL, flags | O_NONBLOCK); handle->source_pipe_routes = handle->ops.input_add (pipe_fds[0], POLLIN, netlink_events_pipe_route_handle_read, handle); } static void network_inador_signal_handler_child (int signum) { //fprintf (stderr, "SIGCHLD session handler\n"); char byte = 1; /* Avisar que un proceso hijo murió */ if (network_inador_pipes_child_notify[1] >= 0) { write (network_inador_pipes_child_notify[1], &byte, 1); } } static void network_inador_child_pipe_read (void *data, int fd, int condition) { pid_t pid; int status; FList *g, *next; char byte; struct _NetworkInadorWatchedPID *watcher; int n; n = read (fd, &byte, 1); if (n <= 0) return; while ((pid = waitpid (-1, &status, WNOHANG)) > 0) { g = network_inador_watched_childs; fprintf (stderr, "El proceso hijo murió: %i\n", pid); while (g != NULL) { next = g->next; watcher = (struct _NetworkInadorWatchedPID *) g->data; if (watcher->child_pid == pid) { /* Este es la vigilancia del proceso que buscamos */ watcher->func (watcher->data, pid, status); /* Como ya murió el proceso, lo eliminamos de la lista de vigilancia */ network_inador_watched_childs = f_list_delete_link (network_inador_watched_childs, g); free (watcher); break; } g = next; } } } void network_inador_setup_child_handler (NetworkInadorHandle *handle) { struct sigaction act; sigset_t empty_mask; if (handle->ops.input_add == NULL) { /* No tenemos vigilancia de entrada, no podemos manejar las señales */ return; } if (pipe (network_inador_pipes_child_notify) != 0) { return; } /* Queremos que estos pipes se cierren automáticamente */ fcntl (network_inador_pipes_child_notify[0], F_SETFD, FD_CLOEXEC); fcntl (network_inador_pipes_child_notify[1], F_SETFD, FD_CLOEXEC); /* Establecer un manejador de SIGCHLD para los procesos hijos que se generen */ sigemptyset (&empty_mask); act.sa_mask = empty_mask; act.sa_flags = 0; act.sa_handler = &network_inador_signal_handler_child; if (sigaction (SIGCHLD, &act, NULL) < 0) { perror ("Failed to register SIGCHLD handler"); close (network_inador_pipes_child_notify[0]); network_inador_pipes_child_notify[0] = -1; close (network_inador_pipes_child_notify[1]); network_inador_pipes_child_notify[1] = -1; return; } network_inador_child_source_watcher = handle->ops.input_add (network_inador_pipes_child_notify[0], POLLIN, network_inador_child_pipe_read, handle); handle->ops.process_watch = network_inador_add_watch_process; handle->ops.process_unwatch = network_inador_remove_watch_process; } NetworkInadorHandle * network_inador_init_handle (struct NetworkInadorOps *network_inador_ops) { NetworkInadorHandle *handle; struct nl_sock * sock_req; handle = (NetworkInadorHandle *) malloc (sizeof (NetworkInadorHandle)); assert (handle != NULL); memset (handle, 0, sizeof (NetworkInadorHandle)); handle->pipe_routes[0] = -1; handle->pipe_routes[1] = -1; /* Copiar la información de eventos para vigilancia */ handle->ops = *network_inador_ops; /* Crear el socket de peticiones */ sock_req = nl_socket_alloc (); if (nl_connect (sock_req, NETLINK_ROUTE) != 0) { perror ("Falló conectar netlink socket\n"); free (handle); return NULL; } handle->nl_sock_route = sock_req; /* Si tenemos una ops de configuración de eventos, entonces, configurar el otro socket de eventos async */ if (handle->ops.input_add != NULL) { _network_inador_try_setup_route_events (handle); _network_inador_try_pipe_routes (handle); } /* Inicializar las interfaces (y las direcciones IP) */ interfaces_init (handle); /* Inicializar las rutas */ routes_init (handle); /* Inicializar el resolv.conf */ resolv_manager_init (handle); return handle; } void network_inador_destroy_handle (NetworkInadorHandle *handle) { if (network_inador_pipes_child_notify[0] >= 0) { if (handle->ops.input_remove != NULL) { handle->ops.input_remove (network_inador_child_source_watcher); network_inador_child_source_watcher = 0; } close (network_inador_pipes_child_notify[0]); network_inador_pipes_child_notify[0] = -1; } if (network_inador_pipes_child_notify[1] >= 0) { close (network_inador_pipes_child_notify[1]); network_inador_pipes_child_notify[1] = -1; } if (handle->has_route_events) { /* Quitar la vigilancia */ handle->ops.input_remove (handle->route_events.source); handle->route_events.source = 0; /* Cerrar el socket de eventos */ nl_socket_free (handle->route_events.nl_sock); handle->route_events.nl_sock = NULL; } if (handle->source_pipe_routes != 0) { handle->ops.input_remove (handle->source_pipe_routes); handle->source_pipe_routes = 0; } if (handle->pipe_routes[0] >= 0) { close (handle->pipe_routes[0]); handle->pipe_routes[0] = -1; } if (handle->pipe_routes[1] >= 0) { close (handle->pipe_routes[1]); handle->pipe_routes[1] = -1; } /* TODO: Liberar las listas ligadas aquí */ routes_clean_up (handle); interfaces_clean_up (handle); nl_socket_free (handle->nl_sock_route); handle->nl_sock_route = NULL; free (handle); }