blob: d6d57b85af08b545744b56fad6d310783143d8c1 [file] [log] [blame]
/*
*
* Copyright (C) 2007 Mindspeed Technologies, Inc.
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack_tcp.h>
#undef NLMSG_TAIL
#include <net/if.h>
#include <net/if_arp.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <netinet/icmp6.h>
#include "cmm.h"
#include "itf.h"
#include "forward_engine.h"
#include "neighbor_resolution.h"
extern pthread_mutex_t tnlMutex;
struct list_head neigh_table[2 * NEIGHBOR_HASH_TABLE_SIZE];
struct list_head neigh_table_by_mac[NEIGHBOR_HASH_TABLE_SIZE];
struct list_head neigh_state_table;
pthread_mutex_t neighMutex = PTHREAD_MUTEX_INITIALIZER;
static struct neighReq *neighReqHead = NULL;
static int cmmNeighborNetlinkLookupAllFilter(const struct sockaddr_nl *nladdr, struct nlmsghdr *nlh, void *arg)
{
int ifindex = *(int *)arg;
struct NeighborEntry *neigh;
struct ndmsg *ndm;
struct rtattr *attr;
if (nlh->nlmsg_type != RTM_NEWNEIGH) {
cmm_print(DEBUG_ERROR, "%s::%d: unexpected netlink message(%d)\n",
__func__, __LINE__, nlh->nlmsg_type);
goto out;
}
ndm = NLMSG_DATA(nlh);
if (ndm->ndm_state & NUD_NOARP)
goto out;
if (ndm->ndm_ifindex != ifindex)
goto out;
attr = cmm_get_rtattr(NDA_RTA(ndm), NDA_PAYLOAD(nlh), NDA_DST);
if (!attr) {
cmm_print(DEBUG_ERROR, "%s::%d: rtnetlink message missing daddr\n", __func__, __LINE__);
goto out;
}
/* If an entry already exists then skip update,
it will be done through the listner thread. This is to avoid calling the ARP/Neighbor state machine from here */
neigh = __cmmNeighFind(ndm->ndm_family, RTA_DATA(attr), ifindex);
if (!neigh) {
neigh = __cmmNeighAdd(ndm->ndm_family, RTA_DATA(attr), ifindex);
if (!neigh) {
cmm_print(DEBUG_ERROR, "%s::%d: __cmmNeighAdd() failed\n", __func__, __LINE__);
goto out;
}
}
else
goto out;
/* Putting data in ArpEntry */
neigh->state = ndm->ndm_state;
if (neigh->state & NUD_VALID) {
int key;
attr = cmm_get_rtattr(NDA_RTA(ndm), NDA_PAYLOAD(nlh), NDA_LLADDR);
memcpy(neigh->macAddr, RTA_DATA(attr), RTA_PAYLOAD(attr));
key = HASH_MAC(neigh->macAddr);
list_add(&neigh_table_by_mac[key], &neigh->list_by_mac);
}
else
memset(neigh->macAddr, 0, ETH_ALEN);
out:
return RTNL_CB_CONTINUE;
}
static int cmmNeighborNetlinkLookupAll(int family, int ifindex)
{
struct rtnl_handle rth;
struct ndmsg ndm = {
.ndm_family = family,
.ndm_ifindex = 0,
.ndm_state = 0,
.ndm_flags = 0,
.ndm_type = 0,
};
cmm_print(DEBUG_INFO, "%s\n", __func__);
if (cmm_rtnl_open(&rth, 0) < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: netlink socket() %s\n", __func__, __LINE__, strerror(errno));
goto err0;
}
if (cmm_rtnl_dump_request(&rth, RTM_GETNEIGH, &ndm, sizeof(struct ndmsg)) < 0)
goto err1;
cmm_rtnl_listen(&rth, cmmNeighborNetlinkLookupAllFilter, &ifindex);
cmm_rtnl_close(&rth);
return 0;
err1:
cmm_rtnl_close(&rth);
err0:
return -1;
}
int cmmNeighAddSolicitQ(int family, int ifindex, unsigned int *dst_ip, unsigned char *dst_mac)
{
struct neighReq *n;
n = (struct neighReq *)malloc(sizeof(struct neighReq));
if (!n) {
return -1;
}
n->family = family;
n->ifindex = ifindex;
if (family == AF_INET)
n->dst_ip[0] = *dst_ip;
else
memcpy((unsigned char *)n->dst_ip, (unsigned char *)dst_ip, 16);
if (dst_mac != NULL) {
memcpy(n->dst_mac, dst_mac, 6);
n->dst_mac_null=0;
}
else {
n->dst_mac_null=1;
}
if (neighReqHead != NULL)
n->next = neighReqHead;
else
n->next = NULL;
neighReqHead = n;
return 0;
}
void cmmNeighSendSolicitQ(void)
{
struct neighReq *n, *next;
if (neighReqHead == NULL)
return;
for(n = neighReqHead; n != NULL; n = next) {
if (n->family == AF_INET) {
cmmArpRequest(n->ifindex, n->dst_ip[0],
n->dst_mac_null ? NULL : n->dst_mac);
}
else {
cmmNeighborSolicitation(n->ifindex, n->dst_ip,
n->dst_mac_null ? NULL : n->dst_mac);
}
next = n->next;
free(n);
}
neighReqHead = NULL;
return;
}
#define MULTICAST_SOLICITED_NODE "FF02::1:FF00:0000"
int cmmNeighborSolicitation(int ifindex, unsigned int *dst_ip, unsigned char *dst_mac)
{
struct sockaddr_in6 sockaddr;
struct __attribute__((packed)) {
struct nd_neighbor_solicit hdr;
uint8_t nd_opt_type;
uint8_t nd_opt_len;
unsigned char src_mac[ETH_ALEN];
} neighbor;
int sockopt;
unsigned int multi_addr[4];
int fd;
int len;
int rc = -1;
/* dst_mac is NULL for multicast solicitations */
if(dst_mac == NULL)
inet_pton(AF_INET6, MULTICAST_SOLICITED_NODE, multi_addr);
fd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if (fd < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: socket() %s\n", __func__, __LINE__, strerror(errno));
goto out;
}
memset(&sockaddr, 0, sizeof(struct sockaddr_in6));
sockaddr.sin6_family = AF_INET6;
sockaddr.sin6_scope_id = ifindex;
if (itf_get_ipaddr(ifindex, AF_INET6, RT_SCOPE_LINK, (unsigned int *)&sockaddr.sin6_addr.s6_addr32, dst_ip) < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: itf_get_ipaddr(%d) failed\n", __func__, __LINE__, ifindex);
goto close;
}
if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: bind() %s\n", __func__, __LINE__, strerror(errno));
goto close;
}
memset(&sockaddr, 0, sizeof(struct sockaddr_in6));
sockaddr.sin6_family = AF_INET6;
if(dst_mac == NULL) {
memcpy(sockaddr.sin6_addr.s6_addr, multi_addr, sizeof(multi_addr));
memcpy(sockaddr.sin6_addr.s6_addr + 13, (unsigned char *)dst_ip + 13, 3);
}
else {
memcpy(sockaddr.sin6_addr.s6_addr, (unsigned char *)dst_ip, 16);
}
memset(&neighbor.hdr, 0, sizeof(struct nd_neighbor_solicit));
neighbor.hdr.nd_ns_type = ND_NEIGHBOR_SOLICIT;
memcpy(&neighbor.hdr.nd_ns_target, dst_ip, 16);
neighbor.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
neighbor.nd_opt_len = 1; /* 8 bytes */
if (itf_get_macaddr(ifindex, neighbor.src_mac) < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: itf_get_macaddr(%d) failed\n", __func__, __LINE__, ifindex);
goto close;
}
sockopt = 255;
if(dst_mac == NULL) {
if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_HOPS, (char *) &sockopt, sizeof(sockopt)) < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: setsockopt() %s\n", __func__, __LINE__, strerror(errno));
goto close;
}
}
else {
if (setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, (char *) &sockopt, sizeof(sockopt)) < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: setsockopt() %s\n", __func__, __LINE__, strerror(errno));
goto close;
}
}
len = sizeof(neighbor);
if (sendto(fd, &neighbor, len, 0, (struct sockaddr *) &sockaddr, sizeof(sockaddr)) < len) {
cmm_print(DEBUG_ERROR, "%s::%d: sendto() %s\n", __func__, __LINE__, strerror(errno));
goto close;
}
rc = 0;
close:
close(fd);
out:
return rc;
}
int cmmArpRequest(int ifindex, unsigned int dst_ip, unsigned char *dst_mac)
{
struct sockaddr_ll sockaddr;
struct __attribute__((packed)) {
struct arphdr ah;
unsigned char src_mac[ETH_ALEN];
unsigned int src_ip;
unsigned char dst_mac[ETH_ALEN];
unsigned int dst_ip;
} arp;
unsigned int src_ip;
unsigned char src_mac[ETH_ALEN];
int fd;
int len;
int rc = -1;
fd = socket(PF_PACKET, SOCK_DGRAM, 0);
if (fd < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: socket() %s\n", __func__, __LINE__, strerror(errno));
goto out;
}
memset(&sockaddr, 0, sizeof(struct sockaddr_ll));
sockaddr.sll_family = AF_PACKET;
sockaddr.sll_ifindex = ifindex;
sockaddr.sll_protocol = htons(ETH_P_ARP);
if (bind(fd, (struct sockaddr *) &sockaddr, sizeof(struct sockaddr_ll)) < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: bind() %s\n", __func__, __LINE__, strerror(errno));
goto close;
}
memset(&sockaddr, 0, sizeof(struct sockaddr_ll));
sockaddr.sll_family = AF_PACKET;
sockaddr.sll_ifindex = ifindex;
sockaddr.sll_protocol = htons(ETH_P_ARP);
sockaddr.sll_halen = ETH_ALEN;
if (itf_get_ipaddr(ifindex, AF_INET, RT_SCOPE_UNIVERSE, &src_ip, &dst_ip) < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: itf_get_ipaddr(%d) failed\n", __func__, __LINE__, ifindex);
goto close;
}
if (itf_get_macaddr(ifindex, src_mac) < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: itf_get_macaddr(%d) failed\n", __func__, __LINE__, ifindex);
goto close;
}
if (!dst_mac)
/* broadcast */
memset(sockaddr.sll_addr, 0xFF, ETH_ALEN);
else
/* unicast */
memcpy(sockaddr.sll_addr, dst_mac, ETH_ALEN);
arp.ah.ar_hrd = htons(ARPHRD_ETHER);
arp.ah.ar_pro = htons(ETH_P_IP);
arp.ah.ar_hln = ETH_ALEN;
arp.ah.ar_pln = 4;
arp.ah.ar_op = htons(ARPOP_REQUEST);
memcpy(arp.src_mac, src_mac, ETH_ALEN);
memcpy(&arp.src_ip, &src_ip, 4);
if (!dst_mac)
memset(arp.dst_mac, 0, ETH_ALEN);
else
memcpy(arp.dst_mac, sockaddr.sll_addr, ETH_ALEN);
memcpy(&arp.dst_ip, &dst_ip, 4);
len = sizeof(arp);
if (sendto(fd, &arp, len, 0, (struct sockaddr *) &sockaddr, sizeof(struct sockaddr_ll)) < len) {
cmm_print(DEBUG_ERROR, "%s::%d: sendto() %s\n", __func__, __LINE__, strerror(errno));
goto close;
}
rc = 0;
close:
close(fd);
out:
return rc;
}
/*****************************************************************
* __cmmNeighUpdateAllMacs
*
*
******************************************************************/
void __cmmNeighUpdateAllMacs(int ifindex, unsigned char *macAddr, int port)
{
struct NeighborEntry *neigh;
struct list_head *entry;
int key;
key = HASH_MAC(macAddr);
entry = list_first(&neigh_table_by_mac[key]);
while (entry != &neigh_table_by_mac[key])
{
neigh = container_of(entry, struct NeighborEntry, list_by_mac);
if ((neigh->ifindex == ifindex) &&
!memcmp(neigh->macAddr, macAddr, ETH_ALEN))
neigh->port = port;
entry = list_next(entry);
}
}
/*****************************************************************
* __cmmNeighFind
*
*
******************************************************************/
struct NeighborEntry *__cmmNeighFind(int family, const unsigned int *ipAddr, int ifindex)
{
struct NeighborEntry *neigh;
struct list_head *entry;
char buf[INET6_ADDRSTRLEN];
int key;
int ipAddrLen = IPADDRLEN(family);
cmm_print(DEBUG_INFO, "%s: Neighbor(%d, %s)\n", __func__, ifindex, inet_ntop(family, ipAddr, buf, sizeof(buf)));
key = HASH_NEIGHBOR(family, ipAddr);
entry = list_first(&neigh_table[key]);
while (entry != &neigh_table[key])
{
neigh = container_of(entry, struct NeighborEntry, list);
if (!memcmp(neigh->ipAddr, ipAddr, ipAddrLen) && neigh->ifindex == ifindex)
goto found;
entry = list_next(entry);
}
neigh = NULL;
found:
return neigh;
}
/*****************************************************************
* __cmmNeighRemove
*
*
******************************************************************/
/* NOTE: The neighMutex must be locked by the caller of this routine. */
void __cmmNeighRemove(struct NeighborEntry *neigh)
{
char buf[INET6_ADDRSTRLEN];
list_del(&neigh->list);
if (neigh->state & NUD_VALID)
list_del(&neigh->list_by_mac);
if (neigh->flags & NEEDS_SOLICIT)
list_del(&neigh->list_by_state);
cmm_print(DEBUG_INFO, "%s: Neighbor(%d, %s) removed\n", __func__, neigh->ifindex, inet_ntop(neigh->family, neigh->ipAddr, buf, sizeof(buf)));
free(neigh);
}
/*****************************************************************
* __cmmNeighPut
*
*
******************************************************************/
void __cmmNeighPut(struct NeighborEntry *neigh)
{
char buf[INET6_ADDRSTRLEN];
cmm_print(DEBUG_INFO, "%s: Neighbor(%d, %s) put\n", __func__, neigh->ifindex, inet_ntop(neigh->family, neigh->ipAddr, buf, sizeof(buf)));
neigh->count--;
if (neigh->count <= 0)
{
__cmmNeighRemove(neigh);
}
}
/*****************************************************************
* __cmmNeighAdd
*
*
******************************************************************/
struct NeighborEntry *__cmmNeighAdd(int family, const unsigned int *ipAddr, int ifindex)
{
struct NeighborEntry *neigh;
char buf[INET6_ADDRSTRLEN];
int key;
neigh = malloc(sizeof(struct NeighborEntry));
if (!neigh)
{
cmm_print(DEBUG_ERROR, "%s::%d: malloc() failed\n", __func__, __LINE__);
goto err0;
}
memset(neigh, 0, sizeof(struct NeighborEntry));
neigh->count = 0;
neigh->port = -1;
neigh->nr_probes = 0;
neigh->ifindex = ifindex;
neigh->ipAddrLen = IPADDRLEN(family);
neigh->family = family;
memcpy(neigh->ipAddr, ipAddr, neigh->ipAddrLen);
key = HASH_NEIGHBOR(family, ipAddr);
list_add(&neigh_table[key], &neigh->list);
cmm_print(DEBUG_INFO, "%s: Neighbor(%d, %s) added\n", __func__, ifindex, inet_ntop(family, ipAddr, buf, sizeof(buf)));
return neigh;
err0:
return NULL;
}
/*****************************************************************
* __cmmNeighGet
*
*
******************************************************************/
struct NeighborEntry *__cmmNeighGet(int family, const unsigned int *ipAddr, int ifindex)
{
struct NeighborEntry *neigh;
neigh = __cmmNeighFind(family, ipAddr, ifindex);
if (!neigh)
{
if (cmmNeighborNetlinkLookupAll(family, ifindex) < 0)
{
cmm_print(DEBUG_ERROR, "%s::%d: cmmNeighborNetlinkLookupAll() failed\n", __func__, __LINE__);
goto err;
}
neigh = __cmmNeighFind(family, ipAddr, ifindex);
if (!neigh)
goto err;
}
neigh->count++;
return neigh;
err:
return NULL;
}
/*****************************************************************
* cmmNeighShow
*
*
******************************************************************/
int cmmNeighShow(struct cli_def * cli, char *command, char *argv[], int argc)
{
int i;
struct NeighborEntry *temp;
struct list_head *entry;
char addr_buf[INET6_ADDRSTRLEN];
char mac_buf[MAC_ADDRSTRLEN];
char ifname[IFNAMSIZ];
int count;
cli_print(cli, "IPv4 ARP:");
count = 0;
for (i = 0; i < NEIGHBOR_HASH_TABLE_SIZE; i++)
{
__pthread_mutex_lock(&neighMutex);
for (entry = list_first(&neigh_table[i]); entry != &neigh_table[i]; entry = list_next(entry))
{
temp = container_of(entry, struct NeighborEntry, list);
cli_print(cli, "IP addr: %s --> MAC addr: %s If: %s, Port:%d, state: %x, Count: %d",
inet_ntop(AF_INET, temp->ipAddr, addr_buf, sizeof(addr_buf)),
mac_ntop(temp->macAddr, mac_buf, sizeof(mac_buf)),
if_indextoname(temp->ifindex, ifname),
temp->port,
temp->state,
temp->count);
count++;
}
__pthread_mutex_unlock(&neighMutex);
}
cli_print(cli, "Total ARP Entries: %d\n", count);
cli_print(cli, "IPv6 Neighbor:");
count = 0;
for (i = NEIGHBOR_HASH_TABLE_SIZE; i < 2 * NEIGHBOR_HASH_TABLE_SIZE; i++)
{
__pthread_mutex_lock(&neighMutex);
for (entry = list_first(&neigh_table[i]); entry != &neigh_table[i]; entry = list_next(entry))
{
temp = container_of(entry, struct NeighborEntry, list);
cli_print(cli, "IP addr: %s --> MAC addr: %s If: %s, Port: %d, state: %x, Count: %d",
inet_ntop(AF_INET6, temp->ipAddr, addr_buf, sizeof(addr_buf)),
mac_ntop(temp->macAddr, mac_buf, sizeof(mac_buf)),
if_indextoname(temp->ifindex, ifname),
temp->port,
temp->state,
temp->count);
count++;
}
__pthread_mutex_unlock(&neighMutex);
}
cli_print(cli, "Total Neighbor Entries: %d\n", count);
return CLI_OK;
}
/*****************************************************************
* cmmNeighborResolved
*
*
******************************************************************/
static void __cmmNeighborResolved(FCI_CLIENT *fci_handle, struct NeighborEntry *neigh)
{
/* Process conntrack entries that use this neighbor */
struct RtEntry *route;
struct list_head *entry;
int key;
/* Force lookup of bridge port */
neigh->port = -1;
key = HASH_MAC(neigh->macAddr);
list_add(&neigh_table_by_mac[key], &neigh->list_by_mac);
key = HASH_NEIGHBOR(neigh->family, neigh->ipAddr);
entry = list_first(&rt_table_by_gw_ip[key]);
while (entry != &rt_table_by_gw_ip[key])
{
route = container_of(entry, struct RtEntry, list_by_gw_ip);
entry = list_next(entry);
if (route->neighEntry != neigh)
continue;
/* Force lookup of bridge port */
route->flags |= CHECK_BRIDGE_PORT;
__cmmCtUpdateWithRoute(fci_handle, route);
__cmmTunnelUpdateWithRoute(fci_handle, route);
__cmmSocketUpdateWithRoute(fci_handle, route);
}
}
/*****************************************************************
* cmmNeighborUnresolved
*
*
******************************************************************/
static void __cmmNeighborUnresolved(FCI_CLIENT *fci_handle, struct NeighborEntry *neigh)
{
/* Process conntrack entries that use this neighbor */
struct RtEntry *route;
struct list_head *entry;
int key;
cmm_print(DEBUG_INFO, "%s: Remove ARP/Neighbor entry\n", __func__);
list_del(&neigh->list_by_mac);
/* Reset bridge port information, if any */
neigh->port = -1;
key = HASH_NEIGHBOR(neigh->family, neigh->ipAddr);
entry = list_first(&rt_table_by_gw_ip[key]);
while (entry != &rt_table_by_gw_ip[key])
{
route = container_of(entry, struct RtEntry, list_by_gw_ip);
entry = list_next(entry);
if (route->neighEntry != neigh)
continue;
/* Force lookup of bridge port */
route->flags |= CHECK_BRIDGE_PORT;
__cmmCtUpdateWithRoute(fci_handle, route);
__cmmTunnelUpdateWithRoute(fci_handle, route);
__cmmSocketUpdateWithRoute(fci_handle, route);
}
}
/*****************************************************************
* cmmNeighborUpdate
*
*
******************************************************************/
static int cmmNeighborUpdate(struct cmm_ct *ctx, const struct sockaddr_nl *who, struct nlmsghdr *n)
{
char buf[INET6_ADDRSTRLEN];
struct ndmsg *r = NLMSG_DATA(n);
struct rtattr * tb [NDA_MAX + 1];
struct NeighborEntry *neigh;
unsigned int *ipAddr;
unsigned char *macAddr;
unsigned int macAddrLen;
unsigned short old_state;
/*Parse the message*/
cmm_parse_rtattr(tb, NDA_MAX, NDA_RTA(r), NDA_PAYLOAD(n));
/* Check if the event can interrest us*/
if (!tb[NDA_DST])
return 0;
if (r->ndm_state & NUD_NOARP)
return 0;
ipAddr = RTA_DATA(tb[NDA_DST]);
if (tb[NDA_LLADDR])
{
macAddr = RTA_DATA(tb[NDA_LLADDR]);
macAddrLen = RTA_PAYLOAD(tb[NDA_LLADDR]);
}
else
{
macAddr = NULL;
macAddrLen = 0;
}
/*Get the IP address in ascii*/
inet_ntop(r->ndm_family, ipAddr, buf, INET6_ADDRSTRLEN);
cmm_print(DEBUG_INFO, "%s: %s, state=%x\n", __func__, buf, r->ndm_state);
/*Try to find a corresponding entry in the ARP table*/
__pthread_mutex_lock(&itf_table.lock);
__pthread_mutex_lock(&ctMutex);
__pthread_mutex_lock(&rtMutex);
__pthread_mutex_lock(&neighMutex);
__pthread_mutex_lock(&flowMutex);
neigh = __cmmNeighFind(r->ndm_family, ipAddr, r->ndm_ifindex);
if (!neigh)
goto out;
/* Update local entry */
old_state = neigh->state;
neigh->state = r->ndm_state;
cmm_print(DEBUG_INFO, "%s: old state = 0x%0x new state = 0x%0x\n", __func__, old_state, neigh->state);
if((r->ndm_state & NUD_REACHABLE) && (neigh->flags & NEEDS_SOLICIT))
{
neigh->flags &= ~NEEDS_SOLICIT;
neigh->nr_probes = 0;
list_del(&neigh->list_by_state);
cmm_print(DEBUG_INFO, "%s: Deleted Entry in Neighbor by state list \n", __func__);
}
if ((r->ndm_state & NUD_STALE) && (!(neigh->flags & NEEDS_SOLICIT)))
{
neigh->flags |= NEEDS_SOLICIT;
list_add(&neigh_state_table, &neigh->list_by_state);
cmm_print(DEBUG_INFO, "%s: Added Entry in Neighbor by state list \n", __func__);
}
/*In those states, the MAC address is usable*/
if (r->ndm_state & NUD_VALID)
{
cmm_print(DEBUG_INFO, "%s: Update ARP/Neighbor entry\n", __func__);
if (!(old_state & NUD_VALID))
{
memcpy(neigh->macAddr, macAddr, macAddrLen);
__cmmNeighborResolved(ctx->fci_handle, neigh);
}
else
{
if (memcmp(neigh->macAddr, macAddr, macAddrLen))
{
memcpy(neigh->macAddr, macAddr, macAddrLen);
list_del(&neigh->list_by_mac);
__cmmNeighborResolved(ctx->fci_handle, neigh);
}
}
}
else
{
memset(neigh->macAddr, 0, ETH_ALEN);
if (old_state & NUD_VALID)
__cmmNeighborUnresolved(ctx->fci_handle, neigh);
}
out:
__pthread_mutex_unlock(&flowMutex);
__pthread_mutex_unlock(&neighMutex);
__pthread_mutex_unlock(&rtMutex);
__pthread_mutex_unlock(&ctMutex);
__pthread_mutex_unlock(&itf_table.lock);
return 0;
}
/*****************************************************************
* cmmNeighThread
*
* Function that sends neighbor soliciations
*
******************************************************************/
int cmmNeighSendSolicit(void)
{
struct NeighborEntry *neigh;
struct list_head *entry;
struct list_head *next;
__pthread_mutex_lock(&neighMutex);
for (entry = list_first(&neigh_state_table); entry != &neigh_state_table; entry = next)
{
next = list_next(entry);
neigh = container_of(entry, struct NeighborEntry, list_by_state);
if(neigh->nr_probes < MAX_UCAST_SOLICIT )
{
if(cmmNeighAddSolicitQ(neigh->family, neigh->ifindex,
neigh->ipAddr, neigh->macAddr) == 0)
neigh->nr_probes++;
}
else if ((neigh->nr_probes >= MAX_UCAST_SOLICIT) &&
(neigh->nr_probes < (MAX_UCAST_SOLICIT + MAX_MCAST_SOLICIT)))
{
if(cmmNeighAddSolicitQ(neigh->family, neigh->ifindex,
neigh->ipAddr, NULL) == 0)
neigh->nr_probes++;
}
else
{
neigh->flags &= ~NEEDS_SOLICIT;
neigh->nr_probes = 0;
list_del(&neigh->list_by_state);
cmm_print(DEBUG_INFO, "%s: Deleted Entry in Neighbor by state list \n", __func__);
}
}
__pthread_mutex_unlock(&neighMutex);
cmmNeighSendSolicitQ();
return 0;
}
/*****************************************************************
* cmmNeighborGet
*
*
******************************************************************/
static int cmmNeighborGet(struct cmm_ct *ctx, const struct sockaddr_nl *who, struct nlmsghdr *n)
{
struct ndmsg *r = NLMSG_DATA(n);
char tab6[INET6_ADDRSTRLEN];
char ifname[IFNAMSIZ];
struct rtattr * tb [NDA_MAX + 1];
/* ARP request immediately done by CMM as Kernel waits 1 seconds before sending it */
/*Parse the message*/
cmm_parse_rtattr(tb, NDA_MAX, NDA_RTA(r), NDA_PAYLOAD(n));
inet_ntop(r->ndm_family, RTA_DATA(tb[NDA_DST]), tab6, INET6_ADDRSTRLEN);
cmm_print(DEBUG_INFO, "%s: %s(%s), state=%x\n", __func__, tab6, if_indextoname(r->ndm_ifindex, ifname), r->ndm_state);
/*IPv4 Neighbor*/
if (r->ndm_family == AF_INET)
{
cmmArpRequest(r->ndm_ifindex, *(unsigned int *)(RTA_DATA(tb[NDA_DST])), NULL);
}
else if(r->ndm_family == AF_INET6)
{
cmmNeighborSolicitation(r->ndm_ifindex, (unsigned int *)(RTA_DATA(tb[NDA_DST])), NULL);
}
// Return,
return RTNL_CB_CONTINUE;
}
/*****************************************************************
* cmmRtnlNeigh
*
*
******************************************************************/
int cmmRtnlNeigh(const struct sockaddr_nl *who, struct nlmsghdr *nlh, void *arg)
{
struct cmm_ct *ctx = arg;
switch (nlh->nlmsg_type) {
case RTM_GETNEIGH:
cmmNeighborGet(ctx, who, nlh);
break;
case RTM_NEWNEIGH:
case RTM_DELNEIGH:
cmmNeighborUpdate(ctx, who, nlh);
break;
default:
cmm_print(DEBUG_ERROR, "%s: unsupported NEIGH netlink message %x\n", __func__, nlh->nlmsg_type);
break;
}
return RTNL_CB_CONTINUE;
}