From 476ab66ca835dd7570ddf0e4c73f71f4f3b5cf0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Arreola=20Rodr=C3=ADguez?= Date: Tue, 28 Dec 2021 23:21:31 -0600 Subject: [PATCH] Primera fase del cliente de dhcp. --- .gitignore | 2 + src/Makefile.am | 7 ++ src/common.h | 34 +++++- src/dhcp_client.c | 220 +++++++++++++++++++++++++++++++++++ src/dhcp_client.h | 33 ++++++ src/interfaces.c | 16 +++ src/interfaces.h | 1 + src/main.c | 1 + src/manager.c | 55 +++++++++ src/network-inador-manager.h | 11 +- src/ni-iface-helper.c | 203 ++++++++++++++++++++++++++++++++ 11 files changed, 581 insertions(+), 2 deletions(-) create mode 100644 src/dhcp_client.c create mode 100644 src/dhcp_client.h create mode 100644 src/ni-iface-helper.c diff --git a/.gitignore b/.gitignore index 63b80a3..b2853c2 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,5 @@ client-gtk/inador-gtk-client client-gtk/ni-marshal.c client-gtk/ni-marshal.h client-gtk/core + +src/ni-iface-helper diff --git a/src/Makefile.am b/src/Makefile.am index 11eb827..1dd1cdc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,9 +8,16 @@ network_inador_SOURCES = main.c \ ip-address.c ip-address.h \ bridge.c bridge.h \ manager.c manager.h \ + dhcp_client.c dhcp_client.h \ wireless_if.c wireless_if.h \ wireless_bss.c wireless_bss.h +libexec_PROGRAMS = ni-iface-helper + +ni_iface_helper_SOURCES = ni-iface-helper.c +ni_iface_helper_CPPFLAGS = $(AM_CPPFLAGS) +ni_iface_helper_CFLAGS = $(AM_CFLAGS) + #network_inador_CPPFLAGS = -DGAMEDATA_DIR=\"$(gamedatadir)/\" -DLOCALEDIR=\"$(localedir)\" $(AM_CPPFLAGS) network_inador_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" $(AM_CPPFLAGS) network_inador_CFLAGS = $(GLIB_CFLAGS) $(LIBNL3_CFLAGS) $(LIBNLGEN3_CFLAGS) $(AM_CFLAGS) diff --git a/src/common.h b/src/common.h index b836937..d5359ff 100644 --- a/src/common.h +++ b/src/common.h @@ -107,7 +107,39 @@ typedef struct _WirelessInfo { GList *aps; } WirelessInfo; +/* Información del proceso de DHCP */ +enum { + IFACE_NO_DHCP = 0, + IFACE_ISC_DHCLIENT +}; + +enum { + DHCP_CLIENT_INITING, + + DHCP_CLIENT_SELECTING, + DHCP_CLIENT_REQUESTING, + DHCP_CLIENT_BOUND, + DHCP_CLIENT_RENEWING, + + DHCP_CLIENT_KILLED, + DHCP_CLIENT_EXTERNAL_RUNNING +}; + +#define DHCP_CLIENT_FLAG_AUTO_RESTART 0x0001 + +typedef struct _InterfaceDHCPClientInfo { + int type; + + uint32_t flags; + int dhcp_state; + + /* Para vigilar el proceso */ + GPid process_pid; + guint process_watch; +} InterfaceDHCPClientInfo; + struct _Interface { + NetworkInadorHandle *handle; uint32_t index; char name[IFNAMSIZ]; uint32_t link_type; @@ -134,7 +166,7 @@ struct _Interface { GList *address; - //DHCPStateInfo dhcp_info; + InterfaceDHCPClientInfo dhcpc; /* Información wireless */ WirelessInfo *wireless; diff --git a/src/dhcp_client.c b/src/dhcp_client.c new file mode 100644 index 0000000..eba74cf --- /dev/null +++ b/src/dhcp_client.c @@ -0,0 +1,220 @@ +/* + * dhcp_client.c + * This file is part of Network Inador + * + * Copyright (C) 2021 - 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 "interfaces.h" +#include "common.h" + +#include "dhcp_client.h" +#include "network-inador-manager.h" + +void interfaces_dhcp_client_ignore_kill (GPid pid, gint status, gpointer data) { + g_spawn_check_exit_status (status, NULL); +} + +void interfaces_dhcp_prepare_args_for_dhclient (char **argv, int size, char *iface_name) { + gchar pid_file[256]; + + snprintf (pid_file, sizeof (pid_file), "/run/dhclient-%s.pid", iface_name); + /* Preparar los argumentos para el proceso */ + argv[0] = "/sbin/dhclient"; + argv[1] = "-d"; + argv[2] = "-q"; + argv[3] = "-pf"; + argv[4] = pid_file; + argv[5] = "-sf"; + argv[6] = "/home/gatuno/Proyectos/NetworkInador/src/ni-iface-helper"; + argv[7] = iface_name; + argv[8] = NULL; +} + +void interfaces_dhcp_client_killed_cb (GPid pid, gint status, gpointer data) { + Interface *iface = (Interface *) data; + gboolean ret; + GError *error = NULL; + char *argv[20]; + + /* Preparar los argumentos */ + interfaces_dhcp_prepare_args_for_dhclient (argv, 20, iface->name); + + if (g_spawn_check_exit_status (status, NULL)) { + /* Revisar si necesito algo */ + } + + if (iface->dhcpc.type == IFACE_ISC_DHCLIENT) { + if (iface->dhcpc.flags & DHCP_CLIENT_FLAG_AUTO_RESTART) { + /* Se cerró o mataron el proceso, reiniciar */ + ret = g_spawn_async_with_pipes ( + "/", + argv, + NULL, + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, + NULL, + &iface->dhcpc.process_pid, + NULL, + NULL, + NULL, + &error); + + if (ret == FALSE) { + printf ("Error dhcp: %s\n", error->message); + g_error_free (error); + error = NULL; + + iface->dhcpc.flags = DHCP_CLIENT_KILLED; + iface->dhcpc.process_watch = 0; + } else { + iface->dhcpc.dhcp_state = DHCP_CLIENT_EXTERNAL_RUNNING; + iface->dhcpc.process_watch = g_child_watch_add (iface->dhcpc.process_pid, interfaces_dhcp_client_killed_cb, iface); + } + } else { + /* En caso contrario, solo dejar la muerte escrita */ + iface->dhcpc.flags = DHCP_CLIENT_KILLED; + iface->dhcpc.process_watch = 0; + } + } +} + +int interfaces_dhcp_client_run (NetworkInadorHandle *handle, int index, int type, uint32_t flags) { + /* IFNAMSIZ */ + Interface *iface; + gboolean ret; + GError *error = NULL; + char *argv[20]; + + iface = _interfaces_locate_by_index (handle->interfaces, index); + + if (iface == NULL) { + printf ("Error, solicitaron operación sobre interfaz que no existe\n"); + + return -1; + } + + /* Preparar los argumentos */ + interfaces_dhcp_prepare_args_for_dhclient (argv, 20, iface->name); + + if (iface->dhcpc.type != IFACE_NO_DHCP) { + /* No puedo correr otro tipo de DHCP */ + return -1; + } + + if (iface->dhcpc.type == IFACE_ISC_DHCLIENT && iface->dhcpc.dhcp_state != DHCP_CLIENT_KILLED) { + /* El cliente de dhcp ya está corriendo, no hacer nada */ + return -1; + } + + ret = g_spawn_async_with_pipes ( + "/", + argv, + NULL, + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, + NULL, + &iface->dhcpc.process_pid, + NULL, + NULL, + NULL, + &error); + + if (ret == FALSE) { + + printf ("Error dhcp: %s\n", error->message); + g_error_free (error); + error = NULL; + + return -1; + } + + iface->dhcpc.dhcp_state = DHCP_CLIENT_EXTERNAL_RUNNING; + iface->dhcpc.type = IFACE_ISC_DHCLIENT; + iface->dhcpc.flags = flags; + + iface->dhcpc.process_watch = g_child_watch_add (iface->dhcpc.process_pid, interfaces_dhcp_client_killed_cb, iface); + + return 0; +} + +void interfaces_dhcp_client_stop (NetworkInadorHandle *handle, int index) { + Interface *iface; + + iface = _interfaces_locate_by_index (handle->interfaces, index); + + if (iface == NULL) { + printf ("Error, solicitaron operación sobre interfaz que no existe\n"); + + return; + } + + if (iface->dhcpc.type == IFACE_NO_DHCP) { + return; + } + + if (iface->dhcpc.type == IFACE_ISC_DHCLIENT) { + if (iface->dhcpc.dhcp_state == DHCP_CLIENT_KILLED) return; + + /* Proceso, matar y reiniciar estado */ + iface->dhcpc.type = IFACE_NO_DHCP; + iface->dhcpc.flags &= (~DHCP_CLIENT_FLAG_AUTO_RESTART); + + g_source_remove (iface->dhcpc.process_watch); + iface->dhcpc.process_watch = 0; + g_child_watch_add (iface->dhcpc.process_pid, interfaces_dhcp_client_ignore_kill, NULL); + + kill (iface->dhcpc.process_pid, SIGTERM); + } +} + +void interfaces_dhcp_client_internal_set_status (NetworkInadorHandle *handle, int index, int status) { + Interface *iface; + + iface = _interfaces_locate_by_index (handle->interfaces, index); + + if (iface == NULL) { + printf ("Error, solicitaron operación sobre interfaz que no existe\n"); + + return; + } + + if (iface->dhcpc.type == IFACE_NO_DHCP) { + return; + } + + if (iface->dhcpc.type == IFACE_ISC_DHCLIENT) { + /* Un proceso muerto no puede informar de cambios de estados */ + if (iface->dhcpc.dhcp_state == DHCP_CLIENT_KILLED) return; + + switch (status) { + case NET_INADOR_DHCP_STATUS_INITING: + iface->dhcpc.dhcp_state = DHCP_CLIENT_INITING; + break; + } + } +} + diff --git a/src/dhcp_client.h b/src/dhcp_client.h new file mode 100644 index 0000000..fee0318 --- /dev/null +++ b/src/dhcp_client.h @@ -0,0 +1,33 @@ +/* + * dhcp_client.h + * This file is part of Network Inador + * + * Copyright (C) 2021 - 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 __DHCP_CLIENT_H__ +#define __DHCP_CLIENT_H__ + +#include "common.h" + +int interfaces_dhcp_client_run (NetworkInadorHandle *handle, int index, int type, uint32_t flags); +void interfaces_dhcp_client_stop (NetworkInadorHandle *handle, int index); +void interfaces_dhcp_client_internal_set_status (NetworkInadorHandle *handle, int index, int status); + +#endif /* __DHCP_CLIENT_H__ */ + diff --git a/src/interfaces.c b/src/interfaces.c index 5029703..fd0bedf 100644 --- a/src/interfaces.c +++ b/src/interfaces.c @@ -256,6 +256,22 @@ Interface * _interfaces_locate_by_index (GList *list, int index) { return NULL; } +Interface * _interfaces_locate_by_name (GList *list, const char *name) { + Interface *iface; + + GList *g; + + for (g = list; g != NULL; g = g->next) { + iface = (Interface *) g->data; + + if (strcmp (iface->name, name) == 0) { + return iface; + } + } + + return NULL; +} + int _interfaces_wait_ack_or_error (struct nl_msg *msg, void *arg) { int *ret = (int *) arg; struct nlmsgerr *l_err; diff --git a/src/interfaces.h b/src/interfaces.h index 90f6a74..e1be4d2 100644 --- a/src/interfaces.h +++ b/src/interfaces.h @@ -36,6 +36,7 @@ int _interfaces_wait_ack_or_error (struct nl_msg *msg, void *arg); int _interfaces_wait_error (struct sockaddr_nl *nla, struct nlmsgerr *l_err, void *arg); Interface * _interfaces_locate_by_index (GList *list, int index); +Interface * _interfaces_locate_by_name (GList *list, const char *name); int interfaces_change_mac_address (NetworkInadorHandle *handle, int index, void *new_mac); int interfaces_change_mtu (NetworkInadorHandle *handle, int index, uint32_t new_mtu); diff --git a/src/main.c b/src/main.c index 8778447..cb5cf6e 100644 --- a/src/main.c +++ b/src/main.c @@ -44,6 +44,7 @@ #include "netlink-events.h" #include "ip-address.h" #include "bridge.h" +#include "dhcp_client.h" /* Usados para salir en caso de una señal */ static int sigterm_pipe_fds[2] = { -1, -1 }; diff --git a/src/manager.c b/src/manager.c index 73f2a88..3951c84 100644 --- a/src/manager.c +++ b/src/manager.c @@ -42,6 +42,7 @@ #include "ip-address.h" #include "bridge.h" #include "network-inador-manager.h" +#include "dhcp_client.h" #define COMMAND_SOCKET_PATH "/tmp/network-inador.socket" @@ -634,6 +635,57 @@ static void _manager_handle_set_event_mask (ManagerClientInfo *manager_client, u manager_client->wanted_events = events; } +static void _manager_execute_dhcp_set_status (ManagerClientInfo *manager_client, unsigned char *buffer, int buffer_len) { + int name_len; + unsigned char name[IFNAMSIZ]; + Interface *iface; + + if (buffer_len < 4) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_DHCP_SET_STATUS); + return; + } + + name_len = buffer[3]; + if (name_len == 0 || name_len >= IFNAMSIZ) { + _manager_send_error (manager_client, NET_INADOR_ERROR_BAD_STRING, NET_INADOR_COMMAND_DHCP_SET_STATUS); + return; + } + + if (name_len + 4 < buffer_len) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_DHCP_SET_STATUS); + return; + } + + if (buffer[2] < NET_INADOR_DHCP_STATUS_INITING || buffer[2] > NET_INADOR_DHCP_STATUS_RENEWING) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INVALID_VALUE, NET_INADOR_COMMAND_DHCP_SET_STATUS); + return; + } + + memcpy (name, &buffer[4], name_len); + name[name_len] = 0; + + iface = _interfaces_locate_by_name (manager_client->manager->handle->interfaces, name); + if (iface == NULL) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INVALID_IFACE_INDEX, NET_INADOR_COMMAND_DHCP_SET_STATUS); + return; + } + + if (iface->dhcpc.type != IFACE_ISC_DHCLIENT) { + _manager_send_error (manager_client, NET_INADOR_ERROR_NOT_EXECUTED, NET_INADOR_COMMAND_DHCP_SET_STATUS); + return; + } + + if (iface->dhcpc.dhcp_state == DHCP_CLIENT_KILLED) { + _manager_send_error (manager_client, NET_INADOR_ERROR_NOT_EXECUTED, NET_INADOR_COMMAND_DHCP_SET_STATUS); + return; + } + + interfaces_dhcp_client_internal_set_status (manager_client->manager->handle, iface->index, buffer[2]); + + /* OK */ + _manager_send_executed (manager_client); +} + static gboolean _manager_client_data (GIOChannel *source, GIOCondition condition, gpointer data) { ManagerClientInfo *manager_client = (ManagerClientInfo *) data; NetworkInadorManager *manager = manager_client->manager; @@ -720,6 +772,9 @@ static gboolean _manager_client_data (GIOChannel *source, GIOCondition condition case NET_INADOR_COMMAND_SET_MASTER: _manager_execute_set_or_clear_master (manager_client, buffer, bytes, FALSE); break; + case NET_INADOR_COMMAND_DHCP_SET_STATUS: + _manager_execute_dhcp_set_status (manager_client, buffer, bytes); + break; default: _manager_send_error (manager_client, NET_INADOR_ERROR_WRONG_COMMAND, command); } diff --git a/src/network-inador-manager.h b/src/network-inador-manager.h index 695fc19..d2fa8c2 100644 --- a/src/network-inador-manager.h +++ b/src/network-inador-manager.h @@ -35,6 +35,9 @@ enum { NET_INADOR_COMMAND_REMOVE_IP, NET_INADOR_COMMAND_SET_EVENT_MASK = 192, + + /* Los siguientes comandos son para uso interno */ + NET_INADOR_COMMAND_DHCP_SET_STATUS = 224, }; enum { @@ -61,9 +64,15 @@ enum { NET_INADOR_RESPONSE_IFACE = 2, NET_INADOR_RESPONSE_IPADDR, - }; +enum { + NET_INADOR_DHCP_STATUS_INITING = 1, + NET_INADOR_DHCP_STATUS_SELECTING, + NET_INADOR_DHCP_STATUS_REQUESTING, + NET_INADOR_DHCP_STATUS_BOUND, + NET_INADOR_DHCP_STATUS_RENEWING +}; #endif /* __NETWOR_INADOR_MANAGER_H__ */ diff --git a/src/ni-iface-helper.c b/src/ni-iface-helper.c new file mode 100644 index 0000000..5d824a9 --- /dev/null +++ b/src/ni-iface-helper.c @@ -0,0 +1,203 @@ +/* + * ni-iface-helper.c + * This file is part of Network Inador + * + * Copyright (C) 2021 - 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 "network-inador-manager.h" + +#define COMMAND_SOCKET_PATH "/tmp/network-inador.socket" + +extern char** environ; + +int debug (int argc, char *argv[]) { + char **item; + int fd; + + fd = open ("/tmp/var_dhcp.env", O_CREAT | O_WRONLY | O_TRUNC, 0644); + + if (fd < 0) { + return -1; + } + + for (item = environ; *item; item++) { + char *name, *val, **p; + + /* Split on the = */ + name = strdup (*item); + val = strchr (name, '='); + if (!val || val == name) { + free (name); + continue; + } + *val++ = '\0'; + + /* Ignore non-DCHP-related environment variables + for (p = (char **) ignore; *p; p++) { + if (strncmp (name, *p, strlen (*p)) == 0) + goto next; + }*/ + + write (fd, "Name: <", 7); + write (fd, name, strlen (name)); + write (fd, ">, Value: <", 11); + write (fd, val, strlen (val)); + write (fd, ">\n", 2); + } + + close (fd); + + return 0; +} + +char *retrieve_env (const char *name) { + char *bus, *dup, *get; + + get = getenv (name); + + if (get == NULL) { + return NULL; + } + + bus = strchr (get, '='); + + if (!bus || bus == get) { + return NULL; + } + + dup = strdup (&get[1]); + + if (dup == NULL) { + return NULL; + } + + return dup; +} + +int wait_for_ack_or_error (int s) { + unsigned char buffer[128]; + int ret; + + ret = recv (s, buffer, sizeof (buffer), 0); + + if (ret < 3) { + return -1; + } + + if (buffer[0] == NET_INADOR_TYPE_RESPONSE_ERROR) { + return -1; + } + + if (buffer[0] == NET_INADOR_TYPE_RESPONSE && buffer[1] == NET_INADOR_RESPONSE_EXECUTED) { + return 0; + } + + return -1; +} + +void send_preinit (int s, char *interface) { + unsigned char buffer[128]; + + buffer[0] = NET_INADOR_TYPE_COMMAND; + buffer[1] = NET_INADOR_COMMAND_DHCP_SET_STATUS; + + buffer[2] = NET_INADOR_DHCP_STATUS_INITING; + buffer[3] = strlen (interface); + strncpy (&buffer[4], interface, buffer[3]); + + send (s, buffer, 3 + buffer[3], 0); + + if (wait_for_ack_or_error (s) < 0) { + close (s); + return; + } + + close (s); +} + +int main (int argc, char *argv[]) { + char *reason, *interface; + int s, ret; + struct sockaddr_un path_dest; + + debug (argc, argv); + + reason = retrieve_env ("reason"); + + interface = retrieve_env ("interface"); + + if (reason == NULL || interface == NULL) { + return 1; + } + + /* Intentar abrir el socket a la escucha */ + s = socket (AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); + + if (s < 0) { + return 1; + } + + path_dest.sun_family = AF_UNIX; + strncpy (path_dest.sun_path, COMMAND_SOCKET_PATH, sizeof (path_dest.sun_path)); + ret = connect (s, (struct sockaddr *) &path_dest, sizeof (path_dest)); + + if (ret < 0) { + perror ("Connect"); + close (s); + return 1; + } + + if (strcmp (reason, "PREINIT") == 0 || strcmp (reason, "FAIL") == 0) { + send_preinit (s, interface); + + return 0; + } else if (strcmp (reason, "REBOOT") == 0 || strcmp (reason, "RENEW") == 0) { + /* + * Los valores más importantes recibidos: + * new_domain_name_servers -> los DNS + * new_expiry -> el timestamp de la hora + * new_domain_name -> DNS search name + * new_routers -> Lista de routers + * new_subnet_mask -> Máscara de red en formato largo + * new_broadcast_address -> Dirección broadcast + * new_ip_address -> Dirección IP + * new_dhcp_lease_time -> Tiempo en segundos + */ + } else if (strcmp (reason, "EXPIRE") == 0) { + /* Los mismos valores, pero con old_ */ + } else { + + + close (s); + } + + return 0; +}