517 lines
14 KiB
C
517 lines
14 KiB
C
|
/*
|
||
|
* ip-address.c
|
||
|
* This file is part of Network-inador
|
||
|
*
|
||
|
* Copyright (C) 2019, 2020 - 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 <string.h>
|
||
|
|
||
|
#include <netlink/socket.h>
|
||
|
#include <netlink/msg.h>
|
||
|
|
||
|
#include <net/if_arp.h>
|
||
|
#include <linux/if_addr.h>
|
||
|
#include <arpa/inet.h>
|
||
|
|
||
|
#include "network-inador-private.h"
|
||
|
#include "ip-address.h"
|
||
|
#include "interfaces.h"
|
||
|
//#include "manager.h"
|
||
|
#include "routes.h"
|
||
|
|
||
|
IPAddr *_ip_address_search_addr (Interface *iface, sa_family_t family, void *addr, uint32_t prefix, void *local_addr) {
|
||
|
FList *g;
|
||
|
IPAddr *ip_addr;
|
||
|
int family_size = 0;
|
||
|
|
||
|
if (family == AF_INET) {
|
||
|
family_size = sizeof (struct in_addr);
|
||
|
} else if (family == AF_INET6) {
|
||
|
family_size = sizeof (struct in6_addr);
|
||
|
}
|
||
|
|
||
|
for (g = iface->address; g != NULL; g = g->next) {
|
||
|
ip_addr = (IPAddr *) g->data;
|
||
|
|
||
|
if (ip_addr->family != family) continue;
|
||
|
|
||
|
if (memcmp (&ip_addr->addr, addr, family_size) == 0 && ip_addr->prefix == prefix) {
|
||
|
if (local_addr != NULL) {
|
||
|
if (memcmp (&ip_addr->local_addr, local_addr, family_size) == 0) {
|
||
|
return ip_addr;
|
||
|
}
|
||
|
} else {
|
||
|
return ip_addr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
int ip_address_receive_message_newaddr (struct nl_msg *msg, void *arg) {
|
||
|
NetworkInadorHandle *handle = (NetworkInadorHandle *) arg;
|
||
|
Interface *iface;
|
||
|
IPAddr *ip_addr;
|
||
|
|
||
|
struct nlmsghdr *reply;
|
||
|
struct ifaddrmsg *addr_msg;
|
||
|
int remaining;
|
||
|
struct nlattr *attr;
|
||
|
struct ifa_cacheinfo cacheinfo;
|
||
|
char label[256];
|
||
|
int label_len;
|
||
|
|
||
|
struct_addr addr;
|
||
|
struct_addr local_addr;
|
||
|
struct_addr brd_addr;
|
||
|
uint32_t new_flags;
|
||
|
|
||
|
/* TODO: Decidir si guardamos también la IP anycast */
|
||
|
|
||
|
int family;
|
||
|
int has_brd = 0, has_local = 0, has_cacheinfo = 0;
|
||
|
int family_size = 0;
|
||
|
int was_new = 0;
|
||
|
|
||
|
reply = nlmsg_hdr (msg);
|
||
|
|
||
|
if (reply->nlmsg_type != RTM_NEWADDR) return NL_SKIP;
|
||
|
|
||
|
addr_msg = nlmsg_data (reply);
|
||
|
|
||
|
iface = _interfaces_locate_by_index (handle->interfaces, addr_msg->ifa_index);
|
||
|
|
||
|
if (iface == NULL) {
|
||
|
printf ("IP para una interfaz desconocida\n");
|
||
|
return NL_SKIP;
|
||
|
}
|
||
|
|
||
|
//printf ("-> New addr\n");
|
||
|
|
||
|
new_flags = addr_msg->ifa_flags;
|
||
|
|
||
|
family = addr_msg->ifa_family;
|
||
|
if (family == AF_INET) {
|
||
|
family_size = sizeof (struct in_addr);
|
||
|
} else if (family == AF_INET6) {
|
||
|
family_size = sizeof (struct in6_addr);
|
||
|
}
|
||
|
|
||
|
label[0] = 0;
|
||
|
label_len = 0;
|
||
|
|
||
|
/* Prevaciar el cacheinfo */
|
||
|
cacheinfo.ifa_prefered = INFINITY_LIFE_TIME;
|
||
|
cacheinfo.ifa_valid = INFINITY_LIFE_TIME;
|
||
|
cacheinfo.cstamp = 0;
|
||
|
cacheinfo.tstamp = 0;
|
||
|
|
||
|
nlmsg_for_each_attr(attr, reply, sizeof (struct ifaddrmsg), remaining) {
|
||
|
//printf ("-> new addr type attr: %i\n", nla_type (attr));
|
||
|
switch (nla_type (attr)) {
|
||
|
case IFA_FLAGS:
|
||
|
new_flags = nla_get_u32 (attr);
|
||
|
break;
|
||
|
case IFA_ADDRESS:
|
||
|
memcpy (&addr, nla_data (attr), nla_len (attr));
|
||
|
break;
|
||
|
case IFA_LOCAL:
|
||
|
memcpy (&local_addr, nla_data (attr), nla_len (attr));
|
||
|
has_local = 1;
|
||
|
break;
|
||
|
case IFA_BROADCAST:
|
||
|
memcpy (&brd_addr, nla_data (attr), nla_len (attr));
|
||
|
has_brd = 1;
|
||
|
break;
|
||
|
case IFA_CACHEINFO:
|
||
|
memcpy (&cacheinfo, nla_data (attr), nla_len (attr));
|
||
|
printf ("Cache info timestamp: preferred: %u, valid: %u, created: %u, updated: %u\n", cacheinfo.ifa_prefered, cacheinfo.ifa_valid, cacheinfo.cstamp, cacheinfo.tstamp);
|
||
|
has_cacheinfo = 1;
|
||
|
break;
|
||
|
case IFA_LABEL:
|
||
|
label_len = nla_len (attr);
|
||
|
memcpy (label, nla_data(attr), label_len);
|
||
|
label[label_len] = 0;
|
||
|
printf ("-> La IP tiene label: %s\n", label);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ip_addr = _ip_address_search_addr (iface, family, &addr, addr_msg->ifa_prefixlen, (has_local ? &local_addr : NULL));
|
||
|
|
||
|
/* Agregar los datos al objeto */
|
||
|
if (ip_addr == NULL) {
|
||
|
ip_addr = malloc (sizeof (IPAddr));
|
||
|
memset (ip_addr, 0, sizeof (*ip_addr));
|
||
|
|
||
|
iface->address = f_list_append (iface->address, ip_addr);
|
||
|
|
||
|
ip_addr->family = addr_msg->ifa_family;
|
||
|
memcpy (&ip_addr->addr, &addr, family_size);
|
||
|
|
||
|
ip_addr->prefix = addr_msg->ifa_prefixlen;
|
||
|
ip_addr->iface = iface;
|
||
|
|
||
|
was_new = 1;
|
||
|
}
|
||
|
|
||
|
ip_addr->flags = new_flags;
|
||
|
ip_addr->scope = addr_msg->ifa_scope;
|
||
|
|
||
|
/* Actualizar la local o la broadcast */
|
||
|
ip_addr->is_p2p = 0;
|
||
|
|
||
|
if (has_local) {
|
||
|
ip_addr->has_local = 1;
|
||
|
memcpy (&ip_addr->local_addr, &local_addr, family_size);
|
||
|
/* Revisar si las direcciones son P2P */
|
||
|
if (memcmp (&local_addr, &addr, family_size) != 0) {
|
||
|
ip_addr->is_p2p = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ip_addr->label[0] = 0;
|
||
|
if (label[0] != 0) {
|
||
|
memcpy (ip_addr->label, label, label_len);
|
||
|
ip_addr->label[label_len] = 0;
|
||
|
}
|
||
|
|
||
|
if (has_brd) {
|
||
|
ip_addr->has_brd = 1;
|
||
|
memcpy (&ip_addr->brd_addr, &brd_addr, family_size);
|
||
|
}
|
||
|
|
||
|
if (has_cacheinfo) {
|
||
|
memcpy (&ip_addr->cacheinfo, &cacheinfo, sizeof (cacheinfo));
|
||
|
}
|
||
|
|
||
|
if (was_new) {
|
||
|
//manager_send_event_ip_add (handle, ip_addr);
|
||
|
} else {
|
||
|
/* En caso contrario, enviar una actualización de IP */
|
||
|
}
|
||
|
|
||
|
char buffer[2048];
|
||
|
char buffer_2[2048];
|
||
|
if (ip_addr->is_p2p) {
|
||
|
inet_ntop (ip_addr->family, &ip_addr->addr, buffer_2, sizeof (buffer_2));
|
||
|
inet_ntop (ip_addr->family, &ip_addr->local_addr, buffer, sizeof (buffer));
|
||
|
|
||
|
printf ("Dirección IP + %s/%d peer %s sobre interfaz: %d, scope: %d, flags: %u\n", buffer, ip_addr->prefix, buffer_2, iface->index, (unsigned int) ip_addr->scope, ip_addr->flags);
|
||
|
} else {
|
||
|
inet_ntop (ip_addr->family, &ip_addr->addr, buffer, sizeof (buffer));
|
||
|
printf ("Dirección IP + %s/%d sobre interfaz: %d, scope: %d, flags: %u\n", buffer, ip_addr->prefix, iface->index, (unsigned int) ip_addr->scope, ip_addr->flags);
|
||
|
}
|
||
|
|
||
|
return NL_SKIP;
|
||
|
}
|
||
|
|
||
|
int ip_address_receive_message_deladdr (struct nl_msg *msg, void *arg) {
|
||
|
NetworkInadorHandle *handle = (NetworkInadorHandle *) arg;
|
||
|
Interface *iface;
|
||
|
IPAddr *ip_addr = NULL;
|
||
|
|
||
|
struct nlmsghdr *reply;
|
||
|
struct ifaddrmsg *addr_msg;
|
||
|
int remaining;
|
||
|
struct nlattr *attr;
|
||
|
|
||
|
struct_addr addr;
|
||
|
struct_addr local_addr;
|
||
|
int family, family_size = 0;
|
||
|
int has_local = 0;
|
||
|
|
||
|
reply = nlmsg_hdr (msg);
|
||
|
|
||
|
if (reply->nlmsg_type != RTM_DELADDR) return NL_SKIP;
|
||
|
|
||
|
addr_msg = nlmsg_data (reply);
|
||
|
|
||
|
iface = _interfaces_locate_by_index (handle->interfaces, addr_msg->ifa_index);
|
||
|
|
||
|
if (iface == NULL) {
|
||
|
printf ("IP para una interfaz desconocida\n");
|
||
|
return NL_SKIP;
|
||
|
}
|
||
|
|
||
|
family = addr_msg->ifa_family;
|
||
|
if (family == AF_INET) {
|
||
|
family_size = sizeof (struct in_addr);
|
||
|
} else if (family == AF_INET6) {
|
||
|
family_size = sizeof (struct in6_addr);
|
||
|
}
|
||
|
|
||
|
//printf ("DEL MSG\n");
|
||
|
nlmsg_for_each_attr(attr, reply, sizeof (struct ifaddrmsg), remaining) {
|
||
|
//printf ("-> del addr type attr: %i\n", nla_type (attr));
|
||
|
switch (nla_type (attr)) {
|
||
|
case IFA_ADDRESS:
|
||
|
memcpy (&addr, nla_data (attr), nla_len (attr));
|
||
|
break;
|
||
|
case IFA_LOCAL:
|
||
|
memcpy (&local_addr, nla_data (attr), nla_len (attr));
|
||
|
has_local = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
route_ask_delayed_delroute (handle);
|
||
|
|
||
|
ip_addr = _ip_address_search_addr (iface, family, &addr, addr_msg->ifa_prefixlen, (has_local ? &local_addr : NULL));
|
||
|
|
||
|
if (ip_addr == NULL) {
|
||
|
//printf ("IP no encontrada\n");
|
||
|
return NL_SKIP;
|
||
|
}
|
||
|
|
||
|
char buffer[2048];
|
||
|
inet_ntop (ip_addr->family, &ip_addr->local_addr, buffer, sizeof (buffer));
|
||
|
printf ("Dirección IP - %s/%d sobre interfaz: %d, scope: %d, flags: %u\n", buffer, ip_addr->prefix, iface->index, (unsigned int) ip_addr->scope, ip_addr->flags);
|
||
|
|
||
|
/* Eliminar de la lista ligada */
|
||
|
iface->address = f_list_remove (iface->address, ip_addr);
|
||
|
|
||
|
/* Notificar del evento */
|
||
|
//manager_send_event_ip_del (handle, ip_addr);
|
||
|
|
||
|
free (ip_addr);
|
||
|
|
||
|
return NL_SKIP;
|
||
|
}
|
||
|
|
||
|
static int _ip_address_wait_ack_or_error (struct nl_msg *msg, void *arg) {
|
||
|
int *ret = (int *) arg;
|
||
|
struct nlmsgerr *l_err;
|
||
|
struct nlmsghdr *reply;
|
||
|
|
||
|
reply = nlmsg_hdr (msg);
|
||
|
|
||
|
if (reply->nlmsg_type == NLMSG_ERROR) {
|
||
|
l_err = nlmsg_data (reply);
|
||
|
|
||
|
*ret = l_err->error;
|
||
|
}
|
||
|
|
||
|
return NL_SKIP;
|
||
|
}
|
||
|
|
||
|
static int _ip_address_wait_error (struct sockaddr_nl *nla, struct nlmsgerr *l_err, void *arg) {
|
||
|
int *ret = (int *) arg;
|
||
|
|
||
|
*ret = l_err->error;
|
||
|
|
||
|
return NL_SKIP;
|
||
|
}
|
||
|
|
||
|
int ip_address_add_ip (NetworkInadorHandle *handle, int index, IPAddr *ip_addr) {
|
||
|
struct nl_msg * msg;
|
||
|
struct ifaddrmsg addr_hdr;
|
||
|
int ret, error;
|
||
|
Interface *iface;
|
||
|
int family_size = 0;
|
||
|
|
||
|
/* TODO: Si no tenemos los route events, podríamos ejecutar una lectura de interfaces para refrescar la lista */
|
||
|
iface = _interfaces_locate_by_index (handle->interfaces, index);
|
||
|
|
||
|
if (iface == NULL) {
|
||
|
printf ("Error, solicitaron operación sobre interfaz que no existe\n");
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
addr_hdr.ifa_family = ip_addr->family;
|
||
|
addr_hdr.ifa_prefixlen = ip_addr->prefix;
|
||
|
addr_hdr.ifa_flags = ip_addr->flags;
|
||
|
addr_hdr.ifa_scope = ip_addr->scope;
|
||
|
addr_hdr.ifa_index = index;
|
||
|
|
||
|
msg = nlmsg_alloc_simple (RTM_NEWADDR, NLM_F_REQUEST);
|
||
|
ret = nlmsg_append (msg, &addr_hdr, sizeof (addr_hdr), NLMSG_ALIGNTO);
|
||
|
|
||
|
if (ret != 0) {
|
||
|
nlmsg_free (msg);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (ip_addr->family == AF_INET) {
|
||
|
family_size = sizeof (struct in_addr);
|
||
|
} else if (ip_addr->family == AF_INET6) {
|
||
|
family_size = sizeof (struct in6_addr);
|
||
|
}
|
||
|
|
||
|
ret = nla_put (msg, IFA_ADDRESS, family_size, &ip_addr->addr);
|
||
|
if (ip_addr->has_local) {
|
||
|
ret |= nla_put (msg, IFA_LOCAL, family_size, &ip_addr->local_addr);
|
||
|
} else {
|
||
|
ret |= nla_put (msg, IFA_LOCAL, family_size, &ip_addr->addr);
|
||
|
}
|
||
|
if (ip_addr->has_brd) {
|
||
|
ret |= nla_put (msg, IFA_BROADCAST, family_size, &ip_addr->brd_addr);
|
||
|
}
|
||
|
|
||
|
/* El tiempo preferido no puede ser mayor que el tiempo válido */
|
||
|
if (ip_addr->cacheinfo.ifa_prefered > ip_addr->cacheinfo.ifa_valid) {
|
||
|
ip_addr->cacheinfo.ifa_prefered = ip_addr->cacheinfo.ifa_valid;
|
||
|
}
|
||
|
|
||
|
ret |= nla_put (msg, IFA_CACHEINFO, 16, &ip_addr->cacheinfo);
|
||
|
|
||
|
if (ret != 0) {
|
||
|
nlmsg_free (msg);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
nl_complete_msg (handle->nl_sock_route, msg);
|
||
|
|
||
|
ret = nl_send (handle->nl_sock_route, msg);
|
||
|
|
||
|
nlmsg_free (msg);
|
||
|
if (ret <= 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
error = 0;
|
||
|
nl_socket_modify_cb (handle->nl_sock_route, NL_CB_VALID, NL_CB_CUSTOM, _ip_address_wait_ack_or_error, &error);
|
||
|
nl_socket_modify_cb (handle->nl_sock_route, NL_CB_INVALID, NL_CB_CUSTOM, _ip_address_wait_ack_or_error, &error);
|
||
|
nl_socket_modify_cb (handle->nl_sock_route, NL_CB_ACK, NL_CB_CUSTOM, _ip_address_wait_ack_or_error, &error);
|
||
|
nl_socket_modify_err_cb (handle->nl_sock_route, NL_CB_CUSTOM, _ip_address_wait_error, &error);
|
||
|
|
||
|
ret = nl_recvmsgs_default (handle->nl_sock_route);
|
||
|
|
||
|
if (ret < 0 || error < 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int ip_address_del_ip (NetworkInadorHandle *handle, int index, IPAddr *ip_addr) {
|
||
|
struct nl_msg * msg;
|
||
|
struct ifaddrmsg addr_hdr;
|
||
|
int ret, error;
|
||
|
Interface *iface;
|
||
|
FList *addr_pos;
|
||
|
int family_size = 0;
|
||
|
|
||
|
/* TODO: Si no tenemos los eventos, podemos ejecutar una lectura de interfaces para refrescar la información */
|
||
|
iface = _interfaces_locate_by_index (handle->interfaces, index);
|
||
|
|
||
|
if (iface == NULL) {
|
||
|
printf ("Error, solicitaron operación sobre interfaz que no existe\n");
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
addr_pos = f_list_find (iface->address, ip_addr);
|
||
|
|
||
|
if (addr_pos == NULL) {
|
||
|
printf ("Error, la dirección solicitada no pertenece a la interfaz\n");
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
addr_hdr.ifa_family = ip_addr->family;
|
||
|
addr_hdr.ifa_prefixlen = ip_addr->prefix;
|
||
|
addr_hdr.ifa_flags = ip_addr->flags;
|
||
|
addr_hdr.ifa_scope = ip_addr->scope;
|
||
|
addr_hdr.ifa_index = index;
|
||
|
|
||
|
msg = nlmsg_alloc_simple (RTM_DELADDR, NLM_F_REQUEST);
|
||
|
ret = nlmsg_append (msg, &addr_hdr, sizeof (addr_hdr), NLMSG_ALIGNTO);
|
||
|
|
||
|
if (ret != 0) {
|
||
|
nlmsg_free (msg);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (ip_addr->family == AF_INET) {
|
||
|
family_size = sizeof (struct in_addr);
|
||
|
} else if (ip_addr->family == AF_INET6) {
|
||
|
family_size = sizeof (struct in6_addr);
|
||
|
}
|
||
|
|
||
|
ret = nla_put (msg, IFA_ADDRESS, family_size, &ip_addr->addr);
|
||
|
if (ip_addr->has_local) {
|
||
|
ret |= nla_put (msg, IFA_LOCAL, family_size, &ip_addr->local_addr);
|
||
|
}
|
||
|
if (ip_addr->has_brd) {
|
||
|
ret |= nla_put (msg, IFA_BROADCAST, family_size, &ip_addr->brd_addr);
|
||
|
}
|
||
|
|
||
|
if (ret != 0) {
|
||
|
nlmsg_free (msg);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
nl_complete_msg (handle->nl_sock_route, msg);
|
||
|
|
||
|
ret = nl_send (handle->nl_sock_route, msg);
|
||
|
|
||
|
nlmsg_free (msg);
|
||
|
if (ret <= 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
error = 0;
|
||
|
nl_socket_modify_cb (handle->nl_sock_route, NL_CB_VALID, NL_CB_CUSTOM, _ip_address_wait_ack_or_error, &error);
|
||
|
nl_socket_modify_cb (handle->nl_sock_route, NL_CB_INVALID, NL_CB_CUSTOM, _ip_address_wait_ack_or_error, &error);
|
||
|
nl_socket_modify_cb (handle->nl_sock_route, NL_CB_ACK, NL_CB_CUSTOM, _ip_address_wait_ack_or_error, &error);
|
||
|
nl_socket_modify_err_cb (handle->nl_sock_route, NL_CB_CUSTOM, _ip_address_wait_error, &error);
|
||
|
|
||
|
ret = nl_recvmsgs_default (handle->nl_sock_route);
|
||
|
|
||
|
if (ret < 0 || error < 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void ip_address_init (NetworkInadorHandle *handle) {
|
||
|
/* Si es la primera vez que nos llaman, descargar una primera lista de direcciones en todas las interfaces */
|
||
|
struct nl_msg * msg;
|
||
|
struct ifaddrmsg addr_hdr = {
|
||
|
.ifa_family = AF_UNSPEC,
|
||
|
};
|
||
|
int ret;
|
||
|
|
||
|
msg = nlmsg_alloc_simple (RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP);
|
||
|
ret = nlmsg_append (msg, &addr_hdr, sizeof (addr_hdr), NLMSG_ALIGNTO);
|
||
|
|
||
|
if (ret != 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
nl_complete_msg (handle->nl_sock_route, msg);
|
||
|
|
||
|
ret = nl_send (handle->nl_sock_route, msg);
|
||
|
|
||
|
nlmsg_free (msg);
|
||
|
|
||
|
nl_socket_modify_cb (handle->nl_sock_route, NL_CB_VALID, NL_CB_CUSTOM, ip_address_receive_message_newaddr, handle);
|
||
|
|
||
|
nl_recvmsgs_default (handle->nl_sock_route);
|
||
|
}
|
||
|
|