/* * 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 #include #include #include "interfaces.h" #include "network-inador-private.h" //#include "manager.h" #include "dhcp_client.h" #include "resolv_manager.h" #include "launch_process.h" #include "network-inador-manager.h" static void interfaces_dhcp_clear_info (InterfaceDHCPClientInfo *dhcpc); /*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_isc_dhclient (char **argv, int size, char *iface_name, char *pid_file, size_t pid_file_len) { snprintf (pid_file, pid_file_len, "/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-dhcp-helper"; argv[7] = iface_name; argv[8] = NULL; } void interfaces_dhcp_prepare_args_for_busybox_udhcpc (char **argv, int size, char *iface_name, char *pid_file, size_t pid_file_len) { snprintf (pid_file, pid_file_len, "/run/dhclient-%s.pid", iface_name); /* Preparar los argumentos para el proceso */ argv[0] = "/bin/busybox"; argv[1] = "udhcpc"; argv[2] = "-i"; argv[3] = iface_name; argv[4] = "-p"; argv[5] = pid_file; argv[6] = "-s"; argv[7] = "/home/gatuno/Proyectos/NetworkInador/src/ni-dhcp-helper"; argv[8] = "-f"; argv[9] = NULL; } void interfaces_dhcp_client_killed_cb (void *data, pid_t pid, int status) { Interface *iface = (Interface *) data; NetworkInadorHandle *handle = iface->handle; int ret; int error = 0; char *argv[20]; char pid_file[256]; /* Preparar los argumentos */ if (iface->dhcpc.type == IFACE_ISC_DHCLIENT) { interfaces_dhcp_prepare_args_for_isc_dhclient (argv, 20, iface->name, pid_file, sizeof (pid_file)); } else if (iface->dhcpc.type == IFACE_BUSYBOX_UDHCPC) { interfaces_dhcp_prepare_args_for_busybox_udhcpc (argv, 20, iface->name, pid_file, sizeof (pid_file)); } if (WIFEXITED (status)) { /* Revisar si necesito algo */ } /* Por limpieza, quitar la vigilancia del proceso */ if (handle->ops.process_unwatch != NULL) { handle->ops.process_unwatch (iface->dhcpc.process_watch); } iface->dhcpc.process_watch = 0; interfaces_dhcp_clear_info (&iface->dhcpc); /* FIXME: ¿Debemos limpiar la información de DNS si no la movimos? */ resolv_manager_clear_dhcp_nameservers (iface->handle, iface); /* Enviar actualización de estado aquí */ iface->dhcpc.dhcp_state = DHCP_CLIENT_KILLED; //manager_send_event_dhcp_change (iface->handle, &iface->dhcpc); if (iface->dhcpc.type == IFACE_ISC_DHCLIENT || iface->dhcpc.type == IFACE_BUSYBOX_UDHCPC) { if (iface->dhcpc.flags & DHCP_CLIENT_FLAG_AUTO_RESTART) { /* Se cerró o mataron el proceso, reiniciar */ ret = launch_process ( "/", argv, NULL, &iface->dhcpc.process_pid, NULL, NULL, NULL, &error); /*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", strerror (error)); iface->dhcpc.dhcp_state = DHCP_CLIENT_KILLED; iface->dhcpc.process_watch = 0; } else { iface->dhcpc.dhcp_state = DHCP_CLIENT_SELECTING; if (handle->ops.process_watch != NULL) { iface->dhcpc.process_watch = handle->ops.process_watch (iface->dhcpc.process_pid, interfaces_dhcp_client_killed_cb, iface); } } } else { /* En caso contrario, solo dejar la muerte escrita */ iface->dhcpc.dhcp_state = DHCP_CLIENT_KILLED; if (handle->ops.process_unwatch != NULL) { handle->ops.process_unwatch (iface->dhcpc.process_watch); } iface->dhcpc.process_watch = 0; } } } static void interfaces_dhcp_clear_info (InterfaceDHCPClientInfo *dhcpc) { memset (&dhcpc->ip, 0, sizeof (dhcpc->ip)); dhcpc->gw_c = 0; dhcpc->dns_c = 0; memset (&dhcpc->broadcast, 0, sizeof (dhcpc->broadcast)); memset (&dhcpc->dhcp_server_ip, 0, sizeof (dhcpc->dhcp_server_ip)); dhcpc->lease_time = 0; dhcpc->prefix = 0; dhcpc->domain_name[0] = 0; } int interfaces_dhcp_client_run (NetworkInadorHandle *handle, int index, int type, uint32_t flags) { /* IFNAMSIZ */ Interface *iface; int ret; int error; char *argv[20]; char pid_file[256]; iface = _interfaces_locate_by_index (handle->interfaces, index); if (iface == NULL) { printf ("Error, solicitaron operación sobre interfaz que no existe\n"); return -1; } /* Revisar que tenga la función vigiladora de hijos muertos */ /* Preparar los argumentos */ if (type == IFACE_ISC_DHCLIENT) { interfaces_dhcp_prepare_args_for_isc_dhclient (argv, 20, iface->name, pid_file, sizeof (pid_file)); } else if (type == IFACE_BUSYBOX_UDHCPC) { interfaces_dhcp_prepare_args_for_busybox_udhcpc (argv, 20, iface->name, pid_file, sizeof (pid_file)); } 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.type == IFACE_BUSYBOX_UDHCPC) && iface->dhcpc.dhcp_state != DHCP_CLIENT_KILLED) { /* El cliente de dhcp ya está corriendo, no hacer nada */ return -1; } interfaces_dhcp_clear_info (&iface->dhcpc); ret = launch_process ( "/", argv, NULL, &iface->dhcpc.process_pid, NULL, NULL, NULL, &error); /* ret = g_spawn_async_with_pipes ( "/", argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &iface->dhcpc.process_pid, NULL, NULL, NULL, &error);*/ /*printf ("Por favor corre el siguiente comando DHCP:\n"); int g = 0; while (argv[g] != NULL) { printf ("%s ", argv[g]); g++; } printf ("\n"); ret = TRUE;*/ if (ret == FALSE) { printf ("Error dhcp: %s\n", strerror (error)); return -1; } iface->dhcpc.dhcp_state = DHCP_CLIENT_SELECTING; iface->dhcpc.type = type; iface->dhcpc.flags = flags; if (handle->ops.process_watch != NULL) { iface->dhcpc.process_watch = handle->ops.process_watch (iface->dhcpc.process_pid, interfaces_dhcp_client_killed_cb, iface); } //manager_send_event_dhcp_change (handle, &iface->dhcpc); return 0; } int 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 -1; } if (iface->dhcpc.type == IFACE_NO_DHCP) { return -1; } if (iface->dhcpc.type == IFACE_ISC_DHCLIENT || iface->dhcpc.type == IFACE_BUSYBOX_UDHCPC) { if (iface->dhcpc.dhcp_state != DHCP_CLIENT_KILLED) { /* Proceso, matar y reiniciar estado */ iface->dhcpc.flags &= (~DHCP_CLIENT_FLAG_AUTO_RESTART); if (handle->ops.process_unwatch != NULL) { handle->ops.process_unwatch (iface->dhcpc.process_watch); } iface->dhcpc.process_watch = 0; // Si el proces muere, realmente no importa kill (iface->dhcpc.process_pid, SIGTERM); iface->dhcpc.dhcp_state = DHCP_CLIENT_KILLED; } iface->dhcpc.type = IFACE_NO_DHCP; interfaces_dhcp_clear_info (&iface->dhcpc); /* TODO: Revisar aquí si es pertinente desconfigurar la interfaz y borrar la información obtenida */ resolv_manager_clear_dhcp_nameservers (handle, iface); /* Enviar actualización de estado aquí */ //manager_send_event_dhcp_change (handle, &iface->dhcpc); } return 0; } void interfaces_dhcp_client_internal_feed_from_client (NetworkInadorHandle *handle, int index, int status, struct_addr *ip, int prefix, struct_addr *gateways, int gw_c, struct_addr *broadcast, struct_addr *dhcp_server, uint32_t lease_time, struct_addr *dns_list, int dns_count, char *domain_name) { Interface *iface; int g; struct_addr old_dns[7]; int old_dns_c; int dns_changed; 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; } // TODO: Cuando entre a estado Selecting, previo otro estado diferente, borrar la IP, si es que tenemos if (iface->dhcpc.type == IFACE_ISC_DHCLIENT || iface->dhcpc.type == IFACE_BUSYBOX_UDHCPC) { /* 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_SELECTING: iface->dhcpc.dhcp_state = DHCP_CLIENT_SELECTING; break; case NET_INADOR_DHCP_STATUS_BOUND: iface->dhcpc.dhcp_state = DHCP_CLIENT_BOUND; break; case NET_INADOR_DHCP_STATUS_RENEWED: iface->dhcpc.dhcp_state = DHCP_CLIENT_RENEWED; break; case NET_INADOR_DHCP_STATUS_EXPIRED: iface->dhcpc.dhcp_state = DHCP_CLIENT_EXPIRED; /* Borrar la IP anterior, si es que está puesta */ break; case NET_INADOR_DHCP_STATUS_FAILED: iface->dhcpc.dhcp_state = DHCP_CLIENT_FAILED; break; } } printf ("----> DHCP status: %i\n", iface->dhcpc.dhcp_state); if (status == NET_INADOR_DHCP_STATUS_BOUND || status == NET_INADOR_DHCP_STATUS_RENEWED) { dns_changed = 0; /* Copiar la información de DNS para saber si hubo cambios */ old_dns_c = iface->dhcpc.dns_c; if (old_dns_c != dns_count) { /* Copiamos los dns viejos solo si la cantidad es diferente */ memcpy (old_dns, iface->dhcpc.dns, old_dns_c * (sizeof (iface->dhcpc.dns[0]))); } /* Copiar las variables de estado */ memcpy (&iface->dhcpc.ip, ip, sizeof (iface->dhcpc.ip)); iface->dhcpc.prefix = prefix; iface->dhcpc.gw_c = gw_c; for (g = 0; g < gw_c && g < 7; g++) { memcpy (&iface->dhcpc.gateways[g], &gateways[g], 4); } memcpy (&iface->dhcpc.broadcast, broadcast, sizeof (iface->dhcpc.broadcast)); memcpy (&iface->dhcpc.dhcp_server_ip, dhcp_server, sizeof (iface->dhcpc.dhcp_server_ip)); iface->dhcpc.lease_time = lease_time; iface->dhcpc.dns_c = dns_count; for (g = 0; g < dns_count && g < 7; g++) { memcpy (&iface->dhcpc.dns[g], &dns_list[g], 4); } if (strcmp (iface->dhcpc.domain_name, domain_name) != 0) { /* El nombre de dominio cambió */ dns_changed = 1; } strncpy (iface->dhcpc.domain_name, domain_name, sizeof (iface->dhcpc.domain_name)); if (iface->dhcpc.dns_c != old_dns_c) { /* Tenemos cambio en los DNS, actualizar el resolv.conf */ dns_changed = 1; } else if (memcmp (iface->dhcpc.dns, old_dns, dns_count * (sizeof (iface->dhcpc.dns[0]))) != 0) { /* Tenemos cambio en los DNS, actualizar el resolv.conf */ dns_changed = 1; } if (dns_changed && (iface->dhcpc.flags & DHCP_CLIENT_FLAG_DONT_ADD_DNS_INFO) == 0) { resolv_manager_process_dhcp_nameservers (handle, iface); } /* Esto solo es para impresión */ char buf_a[256], buf_b[256], buf_c[256], buf_d[256]; inet_ntop (AF_INET, &iface->dhcpc.ip, buf_a, sizeof (buf_a)); inet_ntop (AF_INET, &iface->dhcpc.broadcast, buf_c, sizeof (buf_c)); inet_ntop (AF_INET, &iface->dhcpc.dhcp_server_ip, buf_d, sizeof (buf_d)); printf ("----> (%i) DHCP Server (%s), IP obtenida: %s/%i, GW: ", (unsigned int) time (NULL), buf_d, buf_a, prefix); for (g = 0; g < gw_c; g++) { inet_ntop (AF_INET, &iface->dhcpc.gateways[g], buf_b, sizeof (buf_b)); printf ("%s, ", buf_b); } printf ("bcast: %s, lease: %i, DNS: ", buf_c, lease_time); for (g = 0; g < dns_count; g++) { inet_ntop (AF_INET, &iface->dhcpc.dns[g], buf_b, sizeof (buf_b)); printf ("%s, ", buf_b); } printf (" y domain name <%s>\n", domain_name); } else if (status == NET_INADOR_DHCP_STATUS_SELECTING || status == NET_INADOR_DHCP_STATUS_EXPIRED || status == NET_INADOR_DHCP_STATUS_FAILED) { /* Cuando entramos a SELECTING, debemos limpiar los DNS del resolv.conf */ interfaces_dhcp_clear_info (&iface->dhcpc); /* FIXME: ¿Debemos limpiar la información DNS si no la movimos? */ resolv_manager_clear_dhcp_nameservers (handle, iface); } //manager_send_event_dhcp_change (handle, &iface->dhcpc); }