blob: b96cebbbb246ace21accc23bf9689a2f22f4b196 [file] [log] [blame]
/*
Copyright (c) 2013, The Linux Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of The Linux Foundation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*!
@file
IPACM_Neighbor.cpp
@brief
This file implements the functionality of handling IPACM Neighbor events.
@Author
Skylar Chang
*/
#include <sys/ioctl.h>
#include <IPACM_Neighbor.h>
#include <IPACM_EvtDispatcher.h>
#include "IPACM_Defs.h"
#include "IPACM_Log.h"
IPACM_Neighbor::IPACM_Neighbor()
{
num_neighbor_client = 0;
circular_index = 0;
memset(neighbor_client, 0, IPA_MAX_NUM_NEIGHBOR_CLIENTS * sizeof(ipa_neighbor_client));
IPACM_EvtDispatcher::registr(IPA_WLAN_CLIENT_ADD_EVENT_EX, this);
IPACM_EvtDispatcher::registr(IPA_NEW_NEIGH_EVENT, this);
IPACM_EvtDispatcher::registr(IPA_DEL_NEIGH_EVENT, this);
return;
}
void IPACM_Neighbor::event_callback(ipa_cm_event_id event, void *param)
{
ipacm_event_data_all *data_all = NULL;
int i, ipa_interface_index;
ipacm_cmd_q_data evt_data;
int num_neighbor_client_temp = num_neighbor_client;
IPACMDBG("Recieved event %d\n", event);
switch (event)
{
case IPA_WLAN_CLIENT_ADD_EVENT_EX:
{
ipacm_event_data_wlan_ex *data = (ipacm_event_data_wlan_ex *)param;
ipa_interface_index = IPACM_Iface::iface_ipa_index_query(data->if_index);
/* check for failure return */
if (IPACM_FAILURE == ipa_interface_index) {
IPACMERR("IPA_WLAN_CLIENT_ADD_EVENT_EX: not supported iface id: %d\n", data->if_index);
break;
}
uint8_t client_mac_addr[6] = {0};
IPACMDBG_H("Received IPA_WLAN_CLIENT_ADD_EVENT\n");
for(i = 0; i < data->num_of_attribs; i++)
{
if(data->attribs[i].attrib_type == WLAN_HDR_ATTRIB_MAC_ADDR)
{
memcpy(client_mac_addr,
data->attribs[i].u.mac_addr,
sizeof(client_mac_addr));
IPACMDBG_H("AP Mac Address %02x:%02x:%02x:%02x:%02x:%02x\n",
client_mac_addr[0], client_mac_addr[1], client_mac_addr[2],
client_mac_addr[3], client_mac_addr[4], client_mac_addr[5]);
}
else
{
IPACMDBG_H("The attribute type is not expected!\n");
}
}
for (i = 0; i < num_neighbor_client_temp; i++)
{
/* find the client */
if (memcmp(neighbor_client[i].mac_addr, client_mac_addr, sizeof(neighbor_client[i].mac_addr)) == 0)
{
/* check if iface is not bridge interface*/
if (strcmp(IPACM_Iface::ipacmcfg->ipa_virtual_iface_name, IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name) != 0)
{
/* use previous ipv4 first */
if(data->if_index != neighbor_client[i].iface_index)
{
IPACMERR("update new kernel iface index \n");
neighbor_client[i].iface_index = data->if_index;
}
/* check if client associated with previous network interface */
if(ipa_interface_index != neighbor_client[i].ipa_if_num)
{
IPACMERR("client associate to different AP \n");
return;
}
if (neighbor_client[i].v4_addr != 0) /* not 0.0.0.0 */
{
evt_data.event = IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT;
data_all = (ipacm_event_data_all *)malloc(sizeof(ipacm_event_data_all));
if (data_all == NULL)
{
IPACMERR("Unable to allocate memory\n");
return;
}
data_all->iptype = IPA_IP_v4;
data_all->if_index = neighbor_client[i].iface_index;
data_all->ipv4_addr = neighbor_client[i].v4_addr; //use previous ipv4 address
memcpy(data_all->mac_addr,
neighbor_client[i].mac_addr,
sizeof(data_all->mac_addr));
evt_data.evt_data = (void *)data_all;
IPACM_EvtDispatcher::PostEvt(&evt_data);
/* ask for replaced iface name*/
ipa_interface_index = IPACM_Iface::iface_ipa_index_query(data_all->if_index);
/* check for failure return */
if (IPACM_FAILURE == ipa_interface_index) {
IPACMERR("not supported iface id: %d\n", data_all->if_index);
} else {
IPACMDBG_H("Posted event %d, with %s for ipv4 client re-connect\n",
evt_data.event,
IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name);
}
}
}
break;
}
}
}
break;
default:
{
if (event == IPA_NEW_NEIGH_EVENT)
{
IPACMDBG_H("Received IPA_NEW_NEIGH_EVENT\n");
}
else
{
IPACMDBG_H("Received IPA_DEL_NEIGH_EVENT\n");
}
ipacm_event_data_all *data = (ipacm_event_data_all *)param;
ipa_interface_index = IPACM_Iface::iface_ipa_index_query(data->if_index);
/* check for failure return */
if (IPACM_FAILURE == ipa_interface_index) {
IPACMERR("not supported iface id: %d\n", data->if_index);
break;
}
if (data->iptype == IPA_IP_v4)
{
if (data->ipv4_addr != 0) /* not 0.0.0.0 */
{
IPACMDBG("Got Neighbor event with ipv4 address: 0x%x \n", data->ipv4_addr);
/* check if ipv4 address is link local(169.254.xxx.xxx) */
if ((data->ipv4_addr & IPV4_ADDR_LINKLOCAL_MASK) == IPV4_ADDR_LINKLOCAL)
{
IPACMDBG_H("This is link local ipv4 address: 0x%x : ignore this NEIGH_EVENT\n", data->ipv4_addr);
return;
}
/* check if iface is bridge interface*/
if (strcmp(IPACM_Iface::ipacmcfg->ipa_virtual_iface_name, IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name) == 0)
{
/* searh if seen this client or not*/
for (i = 0; i < num_neighbor_client_temp; i++)
{
if (memcmp(neighbor_client[i].mac_addr, data->mac_addr, sizeof(neighbor_client[i].mac_addr)) == 0)
{
data->if_index = neighbor_client[i].iface_index;
neighbor_client[i].v4_addr = data->ipv4_addr; // cache client's previous ipv4 address
/* construct IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT command and insert to command-queue */
if (event == IPA_NEW_NEIGH_EVENT)
evt_data.event = IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT;
else
/* not to clean-up the client mac cache on bridge0 delneigh */
evt_data.event = IPA_NEIGH_CLIENT_IP_ADDR_DEL_EVENT;
data_all = (ipacm_event_data_all *)malloc(sizeof(ipacm_event_data_all));
if (data_all == NULL)
{
IPACMERR("Unable to allocate memory\n");
return;
}
memcpy(data_all, data, sizeof(ipacm_event_data_all));
evt_data.evt_data = (void *)data_all;
IPACM_EvtDispatcher::PostEvt(&evt_data);
/* ask for replaced iface name*/
ipa_interface_index = IPACM_Iface::iface_ipa_index_query(data_all->if_index);
/* check for failure return */
if (IPACM_FAILURE == ipa_interface_index) {
IPACMERR("not supported iface id: %d\n", data_all->if_index);
} else {
IPACMDBG_H("Posted event %d,\
with %s for ipv4\n",
evt_data.event,
IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name);
}
break;
}
}
}
else
{
/* construct IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT command and insert to command-queue */
if (event == IPA_NEW_NEIGH_EVENT)
{
evt_data.event = IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT;
/* Also save to cache for ipv4 */
/*searh if seen this client or not*/
for (i = 0; i < num_neighbor_client_temp; i++)
{
/* find the client */
if (memcmp(neighbor_client[i].mac_addr, data->mac_addr, sizeof(neighbor_client[i].mac_addr)) == 0)
{
/* update the network interface client associated */
neighbor_client[i].iface_index = data->if_index;
neighbor_client[i].ipa_if_num = ipa_interface_index;
neighbor_client[i].v4_addr = data->ipv4_addr; // cache client's previous ipv4 address
IPACMDBG_H("update cache %d-entry, with %s iface, ipv4 address: 0x%x\n",
i,
IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,
data->ipv4_addr);
break;
}
}
/* not find client */
if (i == num_neighbor_client_temp)
{
if (num_neighbor_client_temp < IPA_MAX_NUM_NEIGHBOR_CLIENTS)
{
memcpy(neighbor_client[num_neighbor_client_temp].mac_addr,
data->mac_addr,
sizeof(data->mac_addr));
neighbor_client[num_neighbor_client_temp].iface_index = data->if_index;
/* cache the network interface client associated */
neighbor_client[num_neighbor_client_temp].ipa_if_num = ipa_interface_index;
neighbor_client[num_neighbor_client_temp].v4_addr = data->ipv4_addr;
num_neighbor_client++;
IPACMDBG_H("Cache client MAC %02x:%02x:%02x:%02x:%02x:%02x\n, total client: %d\n",
neighbor_client[num_neighbor_client_temp].mac_addr[0],
neighbor_client[num_neighbor_client_temp].mac_addr[1],
neighbor_client[num_neighbor_client_temp].mac_addr[2],
neighbor_client[num_neighbor_client_temp].mac_addr[3],
neighbor_client[num_neighbor_client_temp].mac_addr[4],
neighbor_client[num_neighbor_client_temp].mac_addr[5],
num_neighbor_client);
}
else
{
IPACMERR("error: neighbor client oversize! recycle %d-st entry ! \n", circular_index);
memcpy(neighbor_client[circular_index].mac_addr,
data->mac_addr,
sizeof(data->mac_addr));
neighbor_client[circular_index].iface_index = data->if_index;
/* cache the network interface client associated */
neighbor_client[circular_index].ipa_if_num = ipa_interface_index;
neighbor_client[circular_index].v4_addr = 0;
IPACMDBG_H("Copy wlan-iface client MAC %02x:%02x:%02x:%02x:%02x:%02x\n, total client: %d, circular %d\n",
neighbor_client[circular_index].mac_addr[0],
neighbor_client[circular_index].mac_addr[1],
neighbor_client[circular_index].mac_addr[2],
neighbor_client[circular_index].mac_addr[3],
neighbor_client[circular_index].mac_addr[4],
neighbor_client[circular_index].mac_addr[5],
num_neighbor_client,
circular_index);
circular_index++;
}
}
}
else
{
evt_data.event = IPA_NEIGH_CLIENT_IP_ADDR_DEL_EVENT;
/*searh if seen this client or not*/
for (i = 0; i < num_neighbor_client_temp; i++)
{
/* find the client */
if (memcmp(neighbor_client[i].mac_addr, data->mac_addr, sizeof(neighbor_client[i].mac_addr)) == 0)
{
IPACMDBG_H("Clean %d-st Cached client-MAC %02x:%02x:%02x:%02x:%02x:%02x\n, total client: %d\n",
i,
neighbor_client[i].mac_addr[0],
neighbor_client[i].mac_addr[1],
neighbor_client[i].mac_addr[2],
neighbor_client[i].mac_addr[3],
neighbor_client[i].mac_addr[4],
neighbor_client[i].mac_addr[5],
num_neighbor_client);
memset(neighbor_client[i].mac_addr, 0, sizeof(neighbor_client[i].mac_addr));
neighbor_client[i].iface_index = 0;
neighbor_client[i].v4_addr = 0;
neighbor_client[i].ipa_if_num = 0;
for (; i < num_neighbor_client_temp - 1; i++)
{
memcpy(neighbor_client[i].mac_addr,
neighbor_client[i+1].mac_addr,
sizeof(neighbor_client[i].mac_addr));
neighbor_client[i].iface_index = neighbor_client[i+1].iface_index;
neighbor_client[i].v4_addr = neighbor_client[i+1].v4_addr;
neighbor_client[i].ipa_if_num = neighbor_client[i+1].ipa_if_num;
}
num_neighbor_client--;
IPACMDBG_H(" total number of left cased clients: %d\n", num_neighbor_client);
break;
}
}
/* not find client, no need clean-up */
}
data_all = (ipacm_event_data_all *)malloc(sizeof(ipacm_event_data_all));
if (data_all == NULL)
{
IPACMERR("Unable to allocate memory\n");
return;
}
memcpy(data_all, data, sizeof(ipacm_event_data_all));
evt_data.evt_data = (void *)data_all;
IPACM_EvtDispatcher::PostEvt(&evt_data);
IPACMDBG_H("Posted event %d with %s for ipv4\n",
evt_data.event,
IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name);
}
}
}
else
{ //ipv6 starts
if ((data->ipv6_addr[0]) || (data->ipv6_addr[1]) || (data->ipv6_addr[2]) || (data->ipv6_addr[3]))
{
IPACMDBG("Got New_Neighbor event with ipv6 address \n");
/* check if iface is bridge interface*/
if (strcmp(IPACM_Iface::ipacmcfg->ipa_virtual_iface_name, IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name) == 0)
{
/* searh if seen this client or not*/
for (i = 0; i < num_neighbor_client_temp; i++)
{
if (memcmp(neighbor_client[i].mac_addr, data->mac_addr, sizeof(neighbor_client[i].mac_addr)) == 0)
{
data->if_index = neighbor_client[i].iface_index;
/* construct IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT command and insert to command-queue */
if (event == IPA_NEW_NEIGH_EVENT) evt_data.event = IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT;
else evt_data.event = IPA_NEIGH_CLIENT_IP_ADDR_DEL_EVENT;
data_all = (ipacm_event_data_all *)malloc(sizeof(ipacm_event_data_all));
if (data_all == NULL)
{
IPACMERR("Unable to allocate memory\n");
return;
}
memcpy(data_all, data, sizeof(ipacm_event_data_all));
evt_data.evt_data = (void *)data_all;
IPACM_EvtDispatcher::PostEvt(&evt_data);
/* ask for replaced iface name*/
ipa_interface_index = IPACM_Iface::iface_ipa_index_query(data_all->if_index);
/* check for failure return */
if (IPACM_FAILURE == ipa_interface_index) {
IPACMERR("not supported iface id: %d\n", data_all->if_index);
} else {
IPACMDBG_H("Posted event %d,\
with %s for ipv6\n",
evt_data.event,
IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name);
}
break;
};
}
}
else
{
/* construct IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT command and insert to command-queue */
if (event == IPA_NEW_NEIGH_EVENT)
evt_data.event = IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT;
else
evt_data.event = IPA_NEIGH_CLIENT_IP_ADDR_DEL_EVENT;
data_all = (ipacm_event_data_all *)malloc(sizeof(ipacm_event_data_all));
if (data_all == NULL)
{
IPACMERR("Unable to allocate memory\n");
return;
}
memcpy(data_all, data, sizeof(ipacm_event_data_all));
evt_data.evt_data = (void *)data_all;
IPACM_EvtDispatcher::PostEvt(&evt_data);
IPACMDBG_H("Posted event %d with %s for ipv6\n",
evt_data.event,
IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name);
}
}
else
{
IPACMDBG(" Got Neighbor event with no ipv6/ipv4 address \n");
/*no ipv6 in data searh if seen this client or not*/
for (i = 0; i < num_neighbor_client_temp; i++)
{
/* find the client */
if (memcmp(neighbor_client[i].mac_addr, data->mac_addr, sizeof(neighbor_client[i].mac_addr)) == 0)
{
IPACMDBG_H(" find %d-st client, MAC %02x:%02x:%02x:%02x:%02x:%02x\n, total client: %d\n",
i,
neighbor_client[i].mac_addr[0],
neighbor_client[i].mac_addr[1],
neighbor_client[i].mac_addr[2],
neighbor_client[i].mac_addr[3],
neighbor_client[i].mac_addr[4],
neighbor_client[i].mac_addr[5],
num_neighbor_client);
/* check if iface is not bridge interface*/
if (strcmp(IPACM_Iface::ipacmcfg->ipa_virtual_iface_name, IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name) != 0)
{
/* use previous ipv4 first */
if(data->if_index != neighbor_client[i].iface_index)
{
IPACMDBG_H("update new kernel iface index \n");
neighbor_client[i].iface_index = data->if_index;
}
/* check if client associated with previous network interface */
if(ipa_interface_index != neighbor_client[i].ipa_if_num)
{
IPACMDBG_H("client associate to different AP \n");
}
if (neighbor_client[i].v4_addr != 0) /* not 0.0.0.0 */
{
/* construct IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT command and insert to command-queue */
if (event == IPA_NEW_NEIGH_EVENT)
evt_data.event = IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT;
else
evt_data.event = IPA_NEIGH_CLIENT_IP_ADDR_DEL_EVENT;
data_all = (ipacm_event_data_all *)malloc(sizeof(ipacm_event_data_all));
if (data_all == NULL)
{
IPACMERR("Unable to allocate memory\n");
return;
}
data_all->iptype = IPA_IP_v4;
data_all->if_index = neighbor_client[i].iface_index;
data_all->ipv4_addr = neighbor_client[i].v4_addr; //use previous ipv4 address
memcpy(data_all->mac_addr,
neighbor_client[i].mac_addr,
sizeof(data_all->mac_addr));
evt_data.evt_data = (void *)data_all;
IPACM_EvtDispatcher::PostEvt(&evt_data);
/* ask for replaced iface name*/
ipa_interface_index = IPACM_Iface::iface_ipa_index_query(data_all->if_index);
/* check for failure return */
if (IPACM_FAILURE == ipa_interface_index) {
IPACMERR("not supported iface id: %d\n", data_all->if_index);
} else {
IPACMDBG_H("Posted event %d,\
with %s for ipv4 client re-connect\n",
evt_data.event,
IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name);
}
}
}
/* delete cache neighbor entry */
if (event == IPA_DEL_NEIGH_EVENT)
{
IPACMDBG_H("Clean %d-st Cached client-MAC %02x:%02x:%02x:%02x:%02x:%02x\n, total client: %d\n",
i,
neighbor_client[i].mac_addr[0],
neighbor_client[i].mac_addr[1],
neighbor_client[i].mac_addr[2],
neighbor_client[i].mac_addr[3],
neighbor_client[i].mac_addr[4],
neighbor_client[i].mac_addr[5],
num_neighbor_client);
memset(neighbor_client[i].mac_addr, 0, sizeof(neighbor_client[i].mac_addr));
neighbor_client[i].iface_index = 0;
neighbor_client[i].v4_addr = 0;
neighbor_client[i].ipa_if_num = 0;
for (; i < num_neighbor_client_temp - 1; i++)
{
memcpy(neighbor_client[i].mac_addr,
neighbor_client[i+1].mac_addr,
sizeof(neighbor_client[i].mac_addr));
neighbor_client[i].iface_index = neighbor_client[i+1].iface_index;
neighbor_client[i].v4_addr = neighbor_client[i+1].v4_addr;
neighbor_client[i].ipa_if_num = neighbor_client[i+1].ipa_if_num;
}
num_neighbor_client--;
IPACMDBG_H(" total number of left cased clients: %d\n", num_neighbor_client);
}
break;
}
}
/* not find client */
if ((i == num_neighbor_client_temp) && (event == IPA_NEW_NEIGH_EVENT))
{
/* check if iface is not bridge interface*/
if (strcmp(IPACM_Iface::ipacmcfg->ipa_virtual_iface_name, IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name) != 0)
{
if (num_neighbor_client_temp < IPA_MAX_NUM_NEIGHBOR_CLIENTS)
{
memcpy(neighbor_client[num_neighbor_client_temp].mac_addr,
data->mac_addr,
sizeof(data->mac_addr));
neighbor_client[num_neighbor_client_temp].iface_index = data->if_index;
/* cache the network interface client associated */
neighbor_client[num_neighbor_client_temp].ipa_if_num = ipa_interface_index;
neighbor_client[num_neighbor_client_temp].v4_addr = 0;
num_neighbor_client++;
IPACMDBG_H("Copy client MAC %02x:%02x:%02x:%02x:%02x:%02x\n, total client: %d\n",
neighbor_client[num_neighbor_client_temp].mac_addr[0],
neighbor_client[num_neighbor_client_temp].mac_addr[1],
neighbor_client[num_neighbor_client_temp].mac_addr[2],
neighbor_client[num_neighbor_client_temp].mac_addr[3],
neighbor_client[num_neighbor_client_temp].mac_addr[4],
neighbor_client[num_neighbor_client_temp].mac_addr[5],
num_neighbor_client);
return;
}
else
{
IPACMERR("error: neighbor client oversize! recycle %d-st entry ! \n", circular_index);
memcpy(neighbor_client[circular_index].mac_addr,
data->mac_addr,
sizeof(data->mac_addr));
neighbor_client[circular_index].iface_index = data->if_index;
/* cache the network interface client associated */
neighbor_client[circular_index].ipa_if_num = ipa_interface_index;
neighbor_client[circular_index].v4_addr = 0;
IPACMDBG_H("Copy wlan-iface client MAC %02x:%02x:%02x:%02x:%02x:%02x\n, total client: %d, circular %d\n",
neighbor_client[circular_index].mac_addr[0],
neighbor_client[circular_index].mac_addr[1],
neighbor_client[circular_index].mac_addr[2],
neighbor_client[circular_index].mac_addr[3],
neighbor_client[circular_index].mac_addr[4],
neighbor_client[circular_index].mac_addr[5],
num_neighbor_client,
circular_index);
circular_index++;
return;
}
}
}
}
} //ipv6 ends
}
break;
}
return;
}