423 lines
13 KiB
C
423 lines
13 KiB
C
|
/*
|
||
|
* 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 <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <time.h>
|
||
|
|
||
|
#include <fcntl.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <arpa/inet.h>
|
||
|
|
||
|
#include <signal.h>
|
||
|
|
||
|
#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);
|
||
|
}
|
||
|
|