NetworkInador/src/ni-dhcp-iface-helper.c

381 lines
8.0 KiB
C
Raw Normal View History

/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#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;
}
uint32_t utils_ip4_netmask_to_prefix (uint32_t netmask) {
uint32_t prefix;
uint8_t v;
const uint8_t *p = (uint8_t *) &netmask;
if (p[3]) {
prefix = 24;
v = p[3];
} else if (p[2]) {
prefix = 16;
v = p[2];
} else if (p[1]) {
prefix = 8;
v = p[1];
} else {
prefix = 0;
v = p[0];
}
while (v) {
prefix++;
v <<= 1;
}
return prefix;
}
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_CLIENT_FEED;
buffer[2] = AF_INET;
buffer[3] = NET_INADOR_DHCP_STATUS_SELECTING;
buffer[4] = strlen (interface);
memset (&buffer[5], 0, 11);
strncpy (&buffer[16], interface, buffer[4]);
send (s, buffer, 16 + buffer[4], 0);
if (wait_for_ack_or_error (s) < 0) {
return;
}
}
void send_bound_renew (int s, int is_bound, char *interface, struct in_addr *ip, int prefix, struct in_addr *gateway, struct in_addr *broadcast, struct in_addr *dhcp_server, uint32_t lease_time) {
unsigned char buffer[128];
struct in_addr empty;
int pos;
memset (&empty, 0, sizeof (empty));
buffer[0] = NET_INADOR_TYPE_COMMAND;
buffer[1] = NET_INADOR_COMMAND_DHCP_CLIENT_FEED;
buffer[2] = AF_INET;
if (is_bound) {
buffer[3] = NET_INADOR_DHCP_STATUS_BOUND;
} else {
buffer[3] = NET_INADOR_DHCP_STATUS_RENEWED;
}
buffer[4] = strlen (interface);
memset (&buffer[5], 0, 11);
buffer[6] = prefix;
pos = 16;
/* Copiar el nombre de la interfaz */
strncpy (&buffer[16], interface, buffer[4]);
pos = 16 + buffer[4];
if (memcmp (&empty, ip, sizeof (empty)) != 0) {
/* Tenemos una IP */
buffer[5] = 4;
memcpy (&buffer[pos], ip, 4);
pos += 4;
}
if (memcmp (&empty, gateway, sizeof (empty)) != 0) {
/* Tenemos una ruta */
buffer[7] = 4;
memcpy (&buffer[pos], gateway, 4);
pos += 4;
}
if (memcmp (&empty, broadcast, sizeof (empty)) != 0) {
/* Tenemos broadcast */
buffer[8] = 4;
memcpy (&buffer[pos], broadcast, 4);
pos += 4;
}
if (memcmp (&empty, dhcp_server, sizeof (empty)) != 0) {
/* Tenemos IP del servidor DHCP */
buffer[9] = 4;
memcpy (&buffer[pos], dhcp_server, 4);
pos += 4;
}
if (lease_time != 0) {
memcpy (&buffer[12], &lease_time, 4);
}
/* TODO: Faltan los DNS */
send (s, buffer, pos, 0);
if (wait_for_ack_or_error (s) < 0) {
return;
}
}
int parse_ip (char *var, struct in_addr *addr) {
int res;
if (var == NULL) {
return -1;
}
res = inet_pton (AF_INET, var, addr);
if (res <= 0) {
memset (addr, 0, sizeof (*addr));
return -1;
}
return 0;
}
int parse_int (char *var, int *entero) {
int res;
if (var == NULL) {
return -1;
}
res = sscanf (var, "%i", entero);
if (res <= 0) {
*entero = 0;
return -1;
}
return 0;
}
int main (int argc, char *argv[]) {
char *reason, *interface;
int s, ret;
struct sockaddr_un path_dest;
struct in_addr ip, netmask, gateway, broadcast, dhcp_server;
int prefix, lease_time;
debug (argc, argv);
reason = getenv ("reason");
interface = getenv ("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) {
send_preinit (s, interface);
} if (strcmp (reason, "FAIL") == 0) {
} else if (strcmp (reason, "BOUND") == 0 || strcmp (reason, "REBOOT") == 0 ||
strcmp (reason, "RENEW") == 0 || strcmp (reason, "REBIND") == 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
*/
/* Parsear y revisar las direcciones IP recibidas */
char *s_ip, *s_mask, *s_gateway, *s_broadcast, *s_lease_time, *s_dhcp_server;
s_ip = getenv ("new_ip_address");
s_mask = getenv ("new_subnet_mask");
s_gateway = getenv ("new_routers");
s_broadcast = getenv ("new_broadcast_address");
s_lease_time = getenv ("new_dhcp_lease_time");
s_dhcp_server = getenv ("new_dhcp_server_identifier");
/* TODO: Faltan los DNS */
if (parse_ip (s_ip, &ip) < 0) {
close (s);
return -1;
}
if (parse_ip (s_mask, &netmask) < 0) {
close (s);
return -1;
}
prefix = utils_ip4_netmask_to_prefix (netmask.s_addr);
if (parse_ip (s_gateway, &gateway) < 0) {
memset (&gateway, 0, sizeof (gateway));
}
if (parse_ip (s_broadcast, &broadcast) < 0) {
memset (&broadcast, 0, sizeof (broadcast));
}
if (parse_ip (s_dhcp_server, &dhcp_server) < 0) {
memset (&dhcp_server, 0, sizeof (dhcp_server));
}
if (parse_int (s_lease_time, &lease_time) < 0) {
lease_time = 0;
}
int is_bound = 0;
if (strcmp (reason, "BOUND") == 0 || strcmp (reason, "REBOOT") == 0) {
is_bound = 1;
}
send_bound_renew (s, is_bound, interface, &ip, prefix, &gateway, &broadcast, &dhcp_server, lease_time);
} else if (strcmp (reason, "EXPIRE") == 0 || strcmp (reason, "STOP") == 0 || strcmp (reason, "RELEASE") == 0) {
/* Los mismos valores, pero con old_ */
} else {
}
close (s);
return 0;
}