315 lines
8.9 KiB
C
315 lines
8.9 KiB
C
/*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <signal.h>
|
|
#include <sys/wait.h>
|
|
|
|
#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);
|
|
}
|
|
|