/* * 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 #include #include #include #include #include #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); }