/*
Copyright (c) 2013-2017, 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_Netlink.cpp

	@brief
	This file implements the IPAM Netlink Socket Parer functionality.

	@Author
	Skylar Chang

*/
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include "IPACM_CmdQueue.h"
#include "IPACM_Defs.h"
#include "IPACM_Netlink.h"
#include "IPACM_EvtDispatcher.h"
#include "IPACM_Log.h"

int ipa_get_if_name(char *if_name, int if_index);
int find_mask(int ip_v4_last, int *mask_value);

#ifdef FEATURE_IPA_ANDROID

#define IPACM_NL_COPY_ADDR( event_info, element )                                        \
        memcpy( &event_info->attr_info.element.__data,                                   \
                RTA_DATA(rtah),                                                          \
                sizeof(event_info->attr_info.element.__data) );

#define IPACM_EVENT_COPY_ADDR_v6( event_data, element)                                   \
        memcpy( event_data, element.__data, sizeof(event_data));

#define IPACM_EVENT_COPY_ADDR_v4( event_data, element)                                   \
        memcpy( &event_data, element.__data, sizeof(event_data));

#define IPACM_NL_REPORT_ADDR( prefix, addr )                                             \
        if( AF_INET6 == (addr).ss_family ) {                                             \
          IPACM_LOG_IPV6_ADDR( prefix, addr.__data);                                    \
        } else {                                                                         \
          IPACM_LOG_IPV4_ADDR( prefix, (*(unsigned int*)&(addr).__data) );               \
        }

#else/* defined(FEATURE_IPA_ANDROID) */

#define IPACM_NL_COPY_ADDR( event_info, element )                                        \
        memcpy( &event_info->attr_info.element.__ss_padding,                             \
                RTA_DATA(rtah),                                                          \
                sizeof(event_info->attr_info.element.__ss_padding) );

#define IPACM_EVENT_COPY_ADDR_v6( event_data, element)                                   \
        memcpy( event_data, element.__ss_padding, sizeof(event_data));

#define IPACM_EVENT_COPY_ADDR_v4( event_data, element)                                   \
        memcpy( &event_data, element.__ss_padding, sizeof(event_data));

#define IPACM_NL_REPORT_ADDR( prefix, addr )                                             \
        if( AF_INET6 == (addr).ss_family ) {                                             \
          IPACM_LOG_IPV6_ADDR( prefix, addr.__ss_padding);                               \
        } else {                                                                         \
          IPACM_LOG_IPV4_ADDR( prefix, (*(unsigned int*)&(addr).__ss_padding) );         \
        }
#endif /* defined(FEATURE_IPA_ANDROID)*/

#define NDA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
#define IPACM_LOG_IPV6_ADDR(prefix, ip_addr)                            \
        IPACMDBG_H(prefix);                                               \
		IPACMDBG_H(" IPV6 Address %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", \
                  (int)ip_addr[0],  (int)ip_addr[1],                                                        \
                  (int)ip_addr[2],  (int)ip_addr[3],                                                        \
                  (int)ip_addr[4],  (int)ip_addr[5],                                                        \
                  (int)ip_addr[6],  (int)ip_addr[7],                                                        \
                  (int)ip_addr[8],  (int)ip_addr[9],                                                        \
                  (int)ip_addr[10], (int)ip_addr[11],                                                       \
                  (int)ip_addr[12], (int)ip_addr[13],                                                       \
                  (int)ip_addr[14], (int)ip_addr[15]);

#define IPACM_LOG_IPV4_ADDR(prefix, ip_addr)                            \
        IPACMDBG_H(prefix);                                               \
        IPACMDBG_H(" IPV4 Address %d.%d.%d.%d\n",                         \
                    (unsigned char)(ip_addr),                               \
                    (unsigned char)(ip_addr >> 8),                          \
                    (unsigned char)(ip_addr >> 16) ,                        \
                    (unsigned char)(ip_addr >> 24));

/* Opens a netlink socket*/
static int ipa_nl_open_socket
(
	 ipa_nl_sk_info_t *sk_info,
	 int protocol,
	 unsigned int grps
	 )
{
	int *p_sk_fd;
	int buf_size = 6669999, sendbuff=0, res;
	struct sockaddr_nl *p_sk_addr_loc;
	socklen_t optlen;

	p_sk_fd = &(sk_info->sk_fd);
	p_sk_addr_loc = &(sk_info->sk_addr_loc);

	/* Open netlink socket for specified protocol */
	if((*p_sk_fd = socket(AF_NETLINK, SOCK_RAW, protocol)) < 0)
	{
		IPACMERR("cannot open netlink socket\n");
		return IPACM_FAILURE;
	}

	optlen = sizeof(sendbuff);
	res = getsockopt(*p_sk_fd, SOL_SOCKET, SO_SNDBUF, &sendbuff, &optlen);

	if(res == -1) {
		IPACMDBG("Error getsockopt one");
	} else {
		IPACMDBG("orignal send buffer size = %d\n", sendbuff);
	}
	IPACMDBG("sets the send buffer to %d\n", buf_size);
	if (setsockopt(*p_sk_fd, SOL_SOCKET, SO_RCVBUF, &buf_size, sizeof(int)) == -1) {
    IPACMERR("Error setting socket opts\n");
	}

	/* Initialize socket addresses to null */
	memset(p_sk_addr_loc, 0, sizeof(struct sockaddr_nl));

	/* Populate local socket address using specified groups */
	p_sk_addr_loc->nl_family = AF_NETLINK;
	p_sk_addr_loc->nl_pid = getpid();
	p_sk_addr_loc->nl_groups = grps;

	/* Bind socket to the local address, i.e. specified groups. This ensures
	 that multicast messages for these groups are delivered over this
	 socket. */

	if(bind(*p_sk_fd,
					(struct sockaddr *)p_sk_addr_loc,
					sizeof(struct sockaddr_nl)) < 0)
	{
		IPACMERR("Socket bind failed\n");
		return IPACM_FAILURE;
	}

	return IPACM_SUCCESS;
}

/* Add fd to fdmap array and store read handler function ptr (up to MAX_NUM_OF_FD).*/
static int ipa_nl_addfd_map
(
	 ipa_nl_sk_fd_set_info_t *info,
	 int fd,
	 ipa_sock_thrd_fd_read_f read_f
	 )
{
	if(info->num_fd < MAX_NUM_OF_FD)
	{
		FD_SET(fd, &info->fdset);

		/* Add fd to fdmap array and store read handler function ptr */
		info->sk_fds[info->num_fd].sk_fd = fd;
		info->sk_fds[info->num_fd].read_func = read_f;

		/* Increment number of fds stored in fdmap */
		info->num_fd++;
		if(info->max_fd < fd)
			info->max_fd = fd;
	}
	else
	{
		return IPACM_FAILURE;
	}

	return IPACM_SUCCESS;
}

/*  start socket listener */
static int ipa_nl_sock_listener_start
(
	 ipa_nl_sk_fd_set_info_t *sk_fd_set
	 )
{
	int i, ret;

	while(true)
	{
	    for(i = 0; i < sk_fd_set->num_fd; i++ )
		{
			FD_SET(sk_fd_set->sk_fds[i].sk_fd, &(sk_fd_set->fdset));
		}

		if((ret = select(sk_fd_set->max_fd + 1, &(sk_fd_set->fdset), NULL, NULL, NULL)) < 0)
		{
			IPACMERR("ipa_nl select failed\n");
		}
		else
		{
			for(i = 0; i < sk_fd_set->num_fd; i++)
			{

				if(FD_ISSET(sk_fd_set->sk_fds[i].sk_fd, &(sk_fd_set->fdset)))
				{

					if(sk_fd_set->sk_fds[i].read_func)
					{
						if(IPACM_SUCCESS != ((sk_fd_set->sk_fds[i].read_func)(sk_fd_set->sk_fds[i].sk_fd)))
						{
							IPACMERR("Error on read callback[%d] fd=%d\n",
											 i,
											 sk_fd_set->sk_fds[i].sk_fd);
						}
						FD_CLR(sk_fd_set->sk_fds[i].sk_fd, &(sk_fd_set->fdset));
					}
					else
					{
						IPACMERR("No read function\n");
					}
				}

			} /* end of for loop*/
		} /* end of else */
	} /* end of while */

	return IPACM_SUCCESS;
}

/* allocate memory for ipa_nl__msg */
static struct msghdr* ipa_nl_alloc_msg
(
	 uint32_t msglen
	 )
{
	unsigned char *buf = NULL;
	struct sockaddr_nl *nladdr = NULL;
	struct iovec *iov = NULL;
	struct msghdr *msgh = NULL;

	if(IPA_NL_MSG_MAX_LEN < msglen)
	{
		IPACMERR("Netlink message exceeds maximum length\n");
		return NULL;
	}

	msgh = (struct msghdr *)malloc(sizeof(struct msghdr));
	if(msgh == NULL)
	{
		IPACMERR("Failed malloc for msghdr\n");
		return NULL;
	}

	nladdr = (struct sockaddr_nl *)malloc(sizeof(struct sockaddr_nl));
	if(nladdr == NULL)
	{
		IPACMERR("Failed malloc for sockaddr\n");
		free(msgh);
		return NULL;
	}

	iov = (struct iovec *)malloc(sizeof(struct iovec));
	if(iov == NULL)
	{
		PERROR("Failed malloc for iovec");
		free(nladdr);
		free(msgh);
		return NULL;
	}

	buf = (unsigned char *)malloc(msglen);
	if(buf == NULL)
	{
		IPACMERR("Failed malloc for mglen\n");
		free(iov);
		free(nladdr);
		free(msgh);
		return NULL;
	}

	memset(nladdr, 0, sizeof(struct sockaddr_nl));
	nladdr->nl_family = AF_NETLINK;

	memset(msgh, 0x0, sizeof(struct msghdr));
	msgh->msg_name = nladdr;
	msgh->msg_namelen = sizeof(struct sockaddr_nl);
	msgh->msg_iov = iov;
	msgh->msg_iovlen = 1;

	memset(iov, 0x0, sizeof(struct iovec));
	iov->iov_base = buf;
	iov->iov_len = msglen;

	return msgh;
}

/* release IPA message */
static void ipa_nl_release_msg
(
	 struct msghdr *msgh
	 )
{
	unsigned char *buf = NULL;
	struct sockaddr_nl *nladdr = NULL;
	struct iovec *iov = NULL;

	if(NULL == msgh)
	{
		return;
	}

	nladdr = (struct sockaddr_nl *)msgh->msg_name;
	iov = msgh->msg_iov;
	if(msgh->msg_iov)
	{
		buf = (unsigned char *)msgh->msg_iov->iov_base;
	}

	if(buf)
	{
	free(buf);
	}
	if(iov)
	{
	free(iov);
	}
	if(nladdr)
	{
	free(nladdr);
	}
	if(msgh)
	{
	free(msgh);
	}
	return;
}

/* receive and process nl message */
static int ipa_nl_recv
(
	 int              fd,
	 struct msghdr **msg_pptr,
	 unsigned int  *msglen_ptr
	 )
{
	struct msghdr *msgh = NULL;
	int rmsgl;

	msgh = ipa_nl_alloc_msg(IPA_NL_MSG_MAX_LEN);
	if(NULL == msgh)
	{
		IPACMERR("Failed to allocate NL message\n");
		goto error;
	}


	/* Receive message over the socket */
	rmsgl = recvmsg(fd, msgh, 0);

	/* Verify that something was read */
	if(rmsgl <= 0)
	{
		PERROR("NL recv error");
		goto error;
	}

	/* Verify that NL address length in the received message is expected value */
	if(sizeof(struct sockaddr_nl) != msgh->msg_namelen)
	{
		IPACMERR("rcvd msg with namelen != sizeof sockaddr_nl\n");
		goto error;
	}

	/* Verify that message was not truncated. This should not occur */
	if(msgh->msg_flags & MSG_TRUNC)
	{
		IPACMERR("Rcvd msg truncated!\n");
		goto error;
	}

	*msg_pptr    = msgh;
	*msglen_ptr = rmsgl;

	return IPACM_SUCCESS;

/* An error occurred while receiving the message. Free all memory before
				 returning. */
error:
	ipa_nl_release_msg(msgh);
	*msg_pptr    = NULL;
	*msglen_ptr  = 0;

	return IPACM_FAILURE;
}

/* decode the rtm netlink message */
static int ipa_nl_decode_rtm_link
(
	 const char              *buffer,
	 unsigned int             buflen,
	 ipa_nl_link_info_t      *link_info
)
{
	struct rtattr;
	/* NL message header */
	struct nlmsghdr *nlh = (struct nlmsghdr *)buffer;

	/* Extract the header data */
	link_info->metainfo = *(struct ifinfomsg *)NLMSG_DATA(nlh);
	buflen -= sizeof(struct nlmsghdr);

	return IPACM_SUCCESS;
}

/* Decode kernel address message parameters from Netlink attribute TLVs. */
static int ipa_nl_decode_rtm_addr
(
	 const char              *buffer,
	 unsigned int             buflen,
	 ipa_nl_addr_info_t   *addr_info
	 )
{
	struct nlmsghdr *nlh = (struct nlmsghdr *)buffer;  /* NL message header */
	struct rtattr *rtah = NULL;

	/* Extract the header data */
	addr_info->metainfo = *((struct ifaddrmsg *)NLMSG_DATA(nlh));
	buflen -= sizeof(struct nlmsghdr);

	/* Extract the available attributes */
	addr_info->attr_info.param_mask = IPA_NLA_PARAM_NONE;

	rtah = IFA_RTA(NLMSG_DATA(nlh));

	while(RTA_OK(rtah, buflen))
	{
		switch(rtah->rta_type)
		{

		case IFA_ADDRESS:
			addr_info->attr_info.prefix_addr.ss_family = addr_info->metainfo.ifa_family;
			IPACM_NL_COPY_ADDR( addr_info, prefix_addr );
			addr_info->attr_info.param_mask |= IPA_NLA_PARAM_PREFIXADDR;
			break;
		default:
			break;

		}
		/* Advance to next attribute */
		rtah = RTA_NEXT(rtah, buflen);
	}

	return IPACM_SUCCESS;
}

/* Decode kernel neighbor message parameters from Netlink attribute TLVs. */
static int ipa_nl_decode_rtm_neigh
(
	 const char              *buffer,
	 unsigned int             buflen,
	 ipa_nl_neigh_info_t   *neigh_info
	 )
{
	struct nlmsghdr *nlh = (struct nlmsghdr *)buffer;  /* NL message header */
	struct rtattr *rtah = NULL;

	/* Extract the header data */
	neigh_info->metainfo = *((struct ndmsg *)NLMSG_DATA(nlh));
	buflen -= sizeof(struct nlmsghdr);

	/* Extract the available attributes */
	neigh_info->attr_info.param_mask = IPA_NLA_PARAM_NONE;

	rtah = NDA_RTA(NLMSG_DATA(nlh));

	while(RTA_OK(rtah, buflen))
	{
		switch(rtah->rta_type)
		{

		case NDA_DST:
			neigh_info->attr_info.local_addr.ss_family = neigh_info->metainfo.ndm_family;
			IPACM_NL_COPY_ADDR( neigh_info, local_addr );
			break;

		case NDA_LLADDR:
			memcpy(neigh_info->attr_info.lladdr_hwaddr.sa_data,
						 RTA_DATA(rtah),
						 sizeof(neigh_info->attr_info.lladdr_hwaddr.sa_data));
			break;

		default:
			break;

		}

		/* Advance to next attribute */
		rtah = RTA_NEXT(rtah, buflen);
	}

	return IPACM_SUCCESS;
}

/* Decode kernel route message parameters from Netlink attribute TLVs. */
static int ipa_nl_decode_rtm_route
(
	 const char              *buffer,
	 unsigned int             buflen,
	 ipa_nl_route_info_t   *route_info
	 )
{
	struct nlmsghdr *nlh = (struct nlmsghdr *)buffer;  /* NL message header */
	struct rtattr *rtah = NULL;

	/* Extract the header data */
	route_info->metainfo = *((struct rtmsg *)NLMSG_DATA(nlh));
	buflen -= sizeof(struct nlmsghdr);

	route_info->attr_info.param_mask = IPA_RTA_PARAM_NONE;
	rtah = RTM_RTA(NLMSG_DATA(nlh));

	while(RTA_OK(rtah, buflen))
	{
		switch(rtah->rta_type)
		{

		case RTA_DST:
				route_info->attr_info.dst_addr.ss_family = route_info->metainfo.rtm_family;
				IPACM_NL_COPY_ADDR( route_info, dst_addr );
				route_info->attr_info.param_mask |= IPA_RTA_PARAM_DST;
			break;

		case RTA_SRC:
			route_info->attr_info.src_addr.ss_family = route_info->metainfo.rtm_family;
			IPACM_NL_COPY_ADDR( route_info, src_addr );
			route_info->attr_info.param_mask |= IPA_RTA_PARAM_SRC;
			break;

		case RTA_GATEWAY:
			route_info->attr_info.gateway_addr.ss_family = route_info->metainfo.rtm_family;
			IPACM_NL_COPY_ADDR( route_info, gateway_addr );
			route_info->attr_info.param_mask |= IPA_RTA_PARAM_GATEWAY;
			break;

		case RTA_IIF:
			memcpy(&route_info->attr_info.iif_index,
						 RTA_DATA(rtah),
						 sizeof(route_info->attr_info.iif_index));
			route_info->attr_info.param_mask |= IPA_RTA_PARAM_IIF;
			break;

		case RTA_OIF:
			memcpy(&route_info->attr_info.oif_index,
						 RTA_DATA(rtah),
						 sizeof(route_info->attr_info.oif_index));
			route_info->attr_info.param_mask |= IPA_RTA_PARAM_OIF;
			break;

		case RTA_PRIORITY:
			memcpy(&route_info->attr_info.priority,
						 RTA_DATA(rtah),
						 sizeof(route_info->attr_info.priority));
			route_info->attr_info.param_mask |= IPA_RTA_PARAM_PRIORITY;
			break;

		default:
			break;

		}

		/* Advance to next attribute */
		rtah = RTA_NEXT(rtah, buflen);
	}

	return IPACM_SUCCESS;
}

/* decode the ipa nl-message */
static int ipa_nl_decode_nlmsg
(
	 const char   *buffer,
	 unsigned int  buflen,
	 ipa_nl_msg_t  *msg_ptr
	 )
{
	char dev_name[IF_NAME_LEN]={0};
	int ret_val, mask_index, mask_value_v6;
	struct nlmsghdr *nlh = (struct nlmsghdr *)buffer;

	uint32_t if_ipv4_addr =0, if_ipipv4_addr_mask =0, temp =0, if_ipv4_addr_gw =0;

	ipacm_cmd_q_data evt_data;
	ipacm_event_data_all *data_all;
	ipacm_event_data_fid *data_fid;
	ipacm_event_data_addr *data_addr;


	while(NLMSG_OK(nlh, buflen))
	{
		memset(dev_name,0,IF_NAME_LEN);
		IPACMDBG("Received msg:%d from netlink\n", nlh->nlmsg_type)
		switch(nlh->nlmsg_type)
		{
		case RTM_NEWLINK:
			msg_ptr->type = nlh->nlmsg_type;
			msg_ptr->link_event = true;
			if(IPACM_SUCCESS != ipa_nl_decode_rtm_link(buffer, buflen, &(msg_ptr->nl_link_info)))
			{
				IPACMERR("Failed to decode rtm link message\n");
				return IPACM_FAILURE;
			}
			else
			{
				IPACMDBG("Got RTM_NEWLINK with below values\n");
				IPACMDBG("RTM_NEWLINK, ifi_change:%d\n", msg_ptr->nl_link_info.metainfo.ifi_change);
				IPACMDBG("RTM_NEWLINK, ifi_flags:%d\n", msg_ptr->nl_link_info.metainfo.ifi_flags);
				IPACMDBG("RTM_NEWLINK, ifi_index:%d\n", msg_ptr->nl_link_info.metainfo.ifi_index);
				IPACMDBG("RTM_NEWLINK, family:%d\n", msg_ptr->nl_link_info.metainfo.ifi_family);
				/* RTM_NEWLINK event with AF_BRIDGE family should be ignored in Android
				   but this should be processed in case of MDM for Ehernet interface.
				*/
#ifdef FEATURE_IPA_ANDROID
				if (msg_ptr->nl_link_info.metainfo.ifi_family == AF_BRIDGE)
				{
					IPACMERR(" ignore this RTM_NEWLINK msg \n");
					return IPACM_SUCCESS;
				}
#endif
				if(IFF_UP & msg_ptr->nl_link_info.metainfo.ifi_change)
				{
					IPACMDBG("GOT useful newlink event\n");
					ret_val = ipa_get_if_name(dev_name, msg_ptr->nl_link_info.metainfo.ifi_index);
					if(ret_val != IPACM_SUCCESS)
					{
						IPACMERR("Error while getting interface name\n");
						return IPACM_FAILURE;
					}

					data_fid = (ipacm_event_data_fid *)malloc(sizeof(ipacm_event_data_fid));
					if(data_fid == NULL)
					{
						IPACMERR("unable to allocate memory for event data_fid\n");
						return IPACM_FAILURE;
					}
					data_fid->if_index = msg_ptr->nl_link_info.metainfo.ifi_index;

					if(msg_ptr->nl_link_info.metainfo.ifi_flags & IFF_UP)
					{
						IPACMDBG_H("Interface %s bring up with IP-family: %d \n", dev_name, msg_ptr->nl_link_info.metainfo.ifi_family);
						/* post link up to command queue */
						evt_data.event = IPA_LINK_UP_EVENT;
						IPACMDBG_H("Posting IPA_LINK_UP_EVENT with if index: %d\n",
										 msg_ptr->nl_link_info.metainfo.ifi_index);
					}
					else
					{
						IPACMDBG_H("Interface %s bring down with IP-family: %d \n", dev_name, msg_ptr->nl_link_info.metainfo.ifi_family);
						/* post link down to command queue */
						evt_data.event = IPA_LINK_DOWN_EVENT;
						IPACMDBG_H("Posting IPA_LINK_DOWN_EVENT with if index: %d\n",
										 data_fid->if_index);
					}
					evt_data.evt_data = data_fid;
					IPACM_EvtDispatcher::PostEvt(&evt_data);
				}

				/* Add IPACM support for ECM plug-in/plug_out */
				/*--------------------------------------------------------------------------
                   Check if the interface is running.If its a RTM_NEWLINK and the interface
                    is running then it means that its a link up event
                ---------------------------------------------------------------------------*/
                if((msg_ptr->nl_link_info.metainfo.ifi_flags & IFF_RUNNING) &&
                   (msg_ptr->nl_link_info.metainfo.ifi_flags & IFF_LOWER_UP))
                {

					data_fid = (ipacm_event_data_fid *)malloc(sizeof(ipacm_event_data_fid));
					if(data_fid == NULL)
					{
						IPACMERR("unable to allocate memory for event data_fid\n");
						return IPACM_FAILURE;
					}
					data_fid->if_index = msg_ptr->nl_link_info.metainfo.ifi_index;

				        ret_val = ipa_get_if_name(dev_name, msg_ptr->nl_link_info.metainfo.ifi_index);
					if(ret_val != IPACM_SUCCESS)
					{
						IPACMERR("Error while getting interface name\n");
						free(data_fid);
						return IPACM_FAILURE;
					}
					IPACMDBG("Got a usb link_up event (Interface %s, %d) \n", dev_name, msg_ptr->nl_link_info.metainfo.ifi_index);

                    /*--------------------------------------------------------------------------
                       Post LAN iface (ECM) link up event
                     ---------------------------------------------------------------------------*/
                    evt_data.event = IPA_USB_LINK_UP_EVENT;
					evt_data.evt_data = data_fid;
					IPACMDBG_H("Posting usb IPA_LINK_UP_EVENT with if index: %d\n",
										 data_fid->if_index);
					IPACM_EvtDispatcher::PostEvt(&evt_data);
                }
                else if (!(msg_ptr->nl_link_info.metainfo.ifi_flags & IFF_LOWER_UP))
				{
					data_fid = (ipacm_event_data_fid *)malloc(sizeof(ipacm_event_data_fid));
					if(data_fid == NULL)
					{
						IPACMERR("unable to allocate memory for event data_fid\n");
						return IPACM_FAILURE;
					}
					data_fid->if_index = msg_ptr->nl_link_info.metainfo.ifi_index;

					ret_val = ipa_get_if_name(dev_name, msg_ptr->nl_link_info.metainfo.ifi_index);
					if(ret_val != IPACM_SUCCESS)
					{
						IPACMERR("Error while getting interface name\n");
						return IPACM_FAILURE;
					}
					IPACMDBG_H("Got a usb link_down event (Interface %s) \n", dev_name);

					/*--------------------------------------------------------------------------
						Post LAN iface (ECM) link down event
					---------------------------------------------------------------------------*/
					evt_data.event = IPA_LINK_DOWN_EVENT;
					evt_data.evt_data = data_fid;
					IPACMDBG_H("Posting usb IPA_LINK_DOWN_EVENT with if index: %d\n",
										 data_fid->if_index);
					IPACM_EvtDispatcher::PostEvt(&evt_data);
				}
			}
			break;

		case RTM_DELLINK:
			IPACMDBG("\n GOT dellink event\n");
			msg_ptr->type = nlh->nlmsg_type;
			msg_ptr->link_event = true;
			IPACMDBG("entering rtm decode\n");
			if(IPACM_SUCCESS != ipa_nl_decode_rtm_link(buffer, buflen, &(msg_ptr->nl_link_info)))
			{
				IPACMERR("Failed to decode rtm link message\n");
				return IPACM_FAILURE;
			}
			else
			{
				IPACMDBG("Got RTM_DELLINK with below values\n");
				IPACMDBG("RTM_DELLINK, ifi_change:%d\n", msg_ptr->nl_link_info.metainfo.ifi_change);
				IPACMDBG("RTM_DELLINK, ifi_flags:%d\n", msg_ptr->nl_link_info.metainfo.ifi_flags);
				IPACMDBG("RTM_DELLINK, ifi_index:%d\n", msg_ptr->nl_link_info.metainfo.ifi_index);
				IPACMDBG("RTM_DELLINK, family:%d\n", msg_ptr->nl_link_info.metainfo.ifi_family);
				/* RTM_NEWLINK event with AF_BRIDGE family should be ignored in Android
				   but this should be processed in case of MDM for Ehernet interface.
				*/
#ifdef FEATURE_IPA_ANDROID
				if (msg_ptr->nl_link_info.metainfo.ifi_family == AF_BRIDGE)
				{
					IPACMERR(" ignore this RTM_DELLINK msg \n");
					return IPACM_SUCCESS;
				}
#endif
				ret_val = ipa_get_if_name(dev_name, msg_ptr->nl_link_info.metainfo.ifi_index);
				if(ret_val != IPACM_SUCCESS)
				{
					IPACMERR("Error while getting interface name\n");
					return IPACM_FAILURE;
				}
				IPACMDBG("Interface %s bring down \n", dev_name);

				/* post link down to command queue */
				evt_data.event = IPA_LINK_DOWN_EVENT;
				data_fid = (ipacm_event_data_fid *)malloc(sizeof(ipacm_event_data_fid));
				if(data_fid == NULL)
				{
					IPACMERR("unable to allocate memory for event data_fid\n");
					return IPACM_FAILURE;
				}

				data_fid->if_index = msg_ptr->nl_link_info.metainfo.ifi_index;

				IPACMDBG_H("posting IPA_LINK_DOWN_EVENT with if idnex:%d\n",
								 data_fid->if_index);
				evt_data.evt_data = data_fid;
				IPACM_EvtDispatcher::PostEvt(&evt_data);
				/* finish command queue */
			}
			break;

		case RTM_NEWADDR:
			IPACMDBG("\n GOT RTM_NEWADDR event\n");
			if(IPACM_SUCCESS != ipa_nl_decode_rtm_addr(buffer, buflen, &(msg_ptr->nl_addr_info)))
			{
				IPACMERR("Failed to decode rtm addr message\n");
				return IPACM_FAILURE;
			}
			else
			{
				ret_val = ipa_get_if_name(dev_name, msg_ptr->nl_addr_info.metainfo.ifa_index);
				if(ret_val != IPACM_SUCCESS)
				{
					IPACMERR("Error while getting interface name\n");
				}
				IPACMDBG("Interface %s \n", dev_name);

				data_addr = (ipacm_event_data_addr *)malloc(sizeof(ipacm_event_data_addr));
				if(data_addr == NULL)
				{
					IPACMERR("unable to allocate memory for event data_addr\n");
					return IPACM_FAILURE;
				}

				if(AF_INET6 == msg_ptr->nl_addr_info.attr_info.prefix_addr.ss_family)
				{
					data_addr->iptype = IPA_IP_v6;
					IPACM_NL_REPORT_ADDR( "IFA_ADDRESS:", msg_ptr->nl_addr_info.attr_info.prefix_addr );
					IPACM_EVENT_COPY_ADDR_v6( data_addr->ipv6_addr, msg_ptr->nl_addr_info.attr_info.prefix_addr);
					data_addr->ipv6_addr[0] = ntohl(data_addr->ipv6_addr[0]);
					data_addr->ipv6_addr[1] = ntohl(data_addr->ipv6_addr[1]);
					data_addr->ipv6_addr[2] = ntohl(data_addr->ipv6_addr[2]);
					data_addr->ipv6_addr[3] = ntohl(data_addr->ipv6_addr[3]);
				}
				else
				{
					data_addr->iptype = IPA_IP_v4;
					IPACM_NL_REPORT_ADDR( "IFA_ADDRESS:", msg_ptr->nl_addr_info.attr_info.prefix_addr );
					IPACM_EVENT_COPY_ADDR_v4( data_addr->ipv4_addr, msg_ptr->nl_addr_info.attr_info.prefix_addr);
					data_addr->ipv4_addr = ntohl(data_addr->ipv4_addr);

				}

				evt_data.event = IPA_ADDR_ADD_EVENT;
				data_addr->if_index = msg_ptr->nl_addr_info.metainfo.ifa_index;
				strlcpy(data_addr->iface_name, dev_name, sizeof(data_addr->iface_name));
				if(AF_INET6 == msg_ptr->nl_addr_info.attr_info.prefix_addr.ss_family)
				{
				    IPACMDBG("Posting IPA_ADDR_ADD_EVENT with if index:%d, ipv6 addr:0x%x:%x:%x:%x\n",
								 data_addr->if_index,
								 data_addr->ipv6_addr[0],
								 data_addr->ipv6_addr[1],
								 data_addr->ipv6_addr[2],
								 data_addr->ipv6_addr[3]);
                }
				else
				{
				IPACMDBG("Posting IPA_ADDR_ADD_EVENT with if index:%d, ipv4 addr:0x%x\n",
								 data_addr->if_index,
								 data_addr->ipv4_addr);
				}
				evt_data.evt_data = data_addr;
				IPACM_EvtDispatcher::PostEvt(&evt_data);
			}
			break;

		case RTM_NEWROUTE:

			if(IPACM_SUCCESS != ipa_nl_decode_rtm_route(buffer, buflen, &(msg_ptr->nl_route_info)))
			{
				IPACMERR("Failed to decode rtm route message\n");
				return IPACM_FAILURE;
			}

			IPACMDBG("In case RTM_NEWROUTE\n");
			IPACMDBG("rtm_type: %d\n", msg_ptr->nl_route_info.metainfo.rtm_type);
			IPACMDBG("protocol: %d\n", msg_ptr->nl_route_info.metainfo.rtm_protocol);
			IPACMDBG("rtm_scope: %d\n", msg_ptr->nl_route_info.metainfo.rtm_scope);
			IPACMDBG("rtm_table: %d\n", msg_ptr->nl_route_info.metainfo.rtm_table);
			IPACMDBG("rtm_family: %d\n", msg_ptr->nl_route_info.metainfo.rtm_family);
			IPACMDBG("param_mask: 0x%x\n", msg_ptr->nl_route_info.attr_info.param_mask);

			/* take care of route add default route & uniroute */
			if((msg_ptr->nl_route_info.metainfo.rtm_type == RTN_UNICAST) &&
				 ((msg_ptr->nl_route_info.metainfo.rtm_protocol == RTPROT_BOOT) ||
				  (msg_ptr->nl_route_info.metainfo.rtm_protocol == RTPROT_RA)) &&
				 (msg_ptr->nl_route_info.metainfo.rtm_scope == RT_SCOPE_UNIVERSE) &&
				 (msg_ptr->nl_route_info.metainfo.rtm_table == RT_TABLE_MAIN))
			{
				IPACMDBG("\n GOT RTM_NEWROUTE event\n");

				if(msg_ptr->nl_route_info.attr_info.param_mask & IPA_RTA_PARAM_DST)
				{
					ret_val = ipa_get_if_name(dev_name, msg_ptr->nl_route_info.attr_info.oif_index);
					if(ret_val != IPACM_SUCCESS)
					{
						IPACMERR("Error while getting interface name\n");
						return IPACM_FAILURE;
					}

					IPACM_NL_REPORT_ADDR( "route add -host", msg_ptr->nl_route_info.attr_info.dst_addr );
					IPACM_NL_REPORT_ADDR( "gw", msg_ptr->nl_route_info.attr_info.gateway_addr );
					IPACMDBG("dev %s\n",dev_name );
					/* insert to command queue */
					IPACM_EVENT_COPY_ADDR_v4( if_ipv4_addr, msg_ptr->nl_route_info.attr_info.dst_addr);
					temp = (-1);

					evt_data.event = IPA_ROUTE_ADD_EVENT;
					data_addr = (ipacm_event_data_addr *)malloc(sizeof(ipacm_event_data_addr));
					if(data_addr == NULL)
					{
						IPACMERR("unable to allocate memory for event data_addr\n");
						return IPACM_FAILURE;
					}

					data_addr->if_index = msg_ptr->nl_route_info.attr_info.oif_index;
					data_addr->iptype = IPA_IP_v4;
					data_addr->ipv4_addr = ntohl(if_ipv4_addr);
					data_addr->ipv4_addr_mask = ntohl(if_ipipv4_addr_mask);

					IPACMDBG("Posting IPA_ROUTE_ADD_EVENT with if index:%d, ipv4 address 0x%x, mask:0x%x\n",
									 data_addr->if_index,
									 data_addr->ipv4_addr,
									 data_addr->ipv4_addr_mask);
					evt_data.evt_data = data_addr;
					IPACM_EvtDispatcher::PostEvt(&evt_data);
					/* finish command queue */

				}
				else
				{
					ret_val = ipa_get_if_name(dev_name, msg_ptr->nl_route_info.attr_info.oif_index);
					if(ret_val != IPACM_SUCCESS)
					{
						IPACMERR("Error while getting interface name\n");
						return IPACM_FAILURE;
					}

					if(AF_INET6 == msg_ptr->nl_route_info.metainfo.rtm_family)
					{
						/* insert to command queue */
						data_addr = (ipacm_event_data_addr *)malloc(sizeof(ipacm_event_data_addr));
						if(data_addr == NULL)
						{
							IPACMERR("unable to allocate memory for event data_addr\n");
							return IPACM_FAILURE;
						}

						if(msg_ptr->nl_route_info.attr_info.param_mask & IPA_RTA_PARAM_PRIORITY)
						{
							IPACMDBG_H("ip -6 route add default dev %s metric %d\n",
											 dev_name,
											 msg_ptr->nl_route_info.attr_info.priority);
						}
						else
						{
							IPACMDBG_H("ip -6 route add default dev %s\n", dev_name);
						}

						IPACM_EVENT_COPY_ADDR_v6( data_addr->ipv6_addr, msg_ptr->nl_route_info.attr_info.dst_addr);
						data_addr->ipv6_addr[0] = ntohl(data_addr->ipv6_addr[0]);
						data_addr->ipv6_addr[1] = ntohl(data_addr->ipv6_addr[1]);
						data_addr->ipv6_addr[2] = ntohl(data_addr->ipv6_addr[2]);
						data_addr->ipv6_addr[3] = ntohl(data_addr->ipv6_addr[3]);

						IPACM_EVENT_COPY_ADDR_v6( data_addr->ipv6_addr_mask, msg_ptr->nl_route_info.attr_info.dst_addr);
						data_addr->ipv6_addr_mask[0] = ntohl(data_addr->ipv6_addr_mask[0]);
						data_addr->ipv6_addr_mask[1] = ntohl(data_addr->ipv6_addr_mask[1]);
						data_addr->ipv6_addr_mask[2] = ntohl(data_addr->ipv6_addr_mask[2]);
						data_addr->ipv6_addr_mask[3] = ntohl(data_addr->ipv6_addr_mask[3]);

						IPACM_EVENT_COPY_ADDR_v6( data_addr->ipv6_addr_gw, msg_ptr->nl_route_info.attr_info.gateway_addr);
						data_addr->ipv6_addr_gw[0] = ntohl(data_addr->ipv6_addr_gw[0]);
						data_addr->ipv6_addr_gw[1] = ntohl(data_addr->ipv6_addr_gw[1]);
						data_addr->ipv6_addr_gw[2] = ntohl(data_addr->ipv6_addr_gw[2]);
						data_addr->ipv6_addr_gw[3] = ntohl(data_addr->ipv6_addr_gw[3]);
						IPACM_NL_REPORT_ADDR( " ", msg_ptr->nl_route_info.attr_info.gateway_addr);

						evt_data.event = IPA_ROUTE_ADD_EVENT;
						data_addr->if_index = msg_ptr->nl_route_info.attr_info.oif_index;
						data_addr->iptype = IPA_IP_v6;

						IPACMDBG("Posting IPA_ROUTE_ADD_EVENT with if index:%d, ipv6 address\n",
										 data_addr->if_index);
						evt_data.evt_data = data_addr;
						IPACM_EvtDispatcher::PostEvt(&evt_data);
						/* finish command queue */

					}
					else
					{
						IPACM_NL_REPORT_ADDR( "route add default gw \n", msg_ptr->nl_route_info.attr_info.gateway_addr );
						IPACMDBG_H("dev %s \n", dev_name);
						IPACM_NL_REPORT_ADDR( "dstIP:", msg_ptr->nl_route_info.attr_info.dst_addr );

						/* insert to command queue */
						data_addr = (ipacm_event_data_addr *)malloc(sizeof(ipacm_event_data_addr));
						if(data_addr == NULL)
						{
							IPACMERR("unable to allocate memory for event data_addr\n");
							return IPACM_FAILURE;
						}

						IPACM_EVENT_COPY_ADDR_v4( if_ipv4_addr, msg_ptr->nl_route_info.attr_info.dst_addr);
						IPACM_EVENT_COPY_ADDR_v4( if_ipipv4_addr_mask, msg_ptr->nl_route_info.attr_info.dst_addr);
						IPACM_EVENT_COPY_ADDR_v4( if_ipv4_addr_gw, msg_ptr->nl_route_info.attr_info.gateway_addr);

						evt_data.event = IPA_ROUTE_ADD_EVENT;
						data_addr->if_index = msg_ptr->nl_route_info.attr_info.oif_index;
						data_addr->iptype = IPA_IP_v4;
						data_addr->ipv4_addr = ntohl(if_ipv4_addr);
						data_addr->ipv4_addr_gw = ntohl(if_ipv4_addr_gw);
						data_addr->ipv4_addr_mask = ntohl(if_ipipv4_addr_mask);

            IPACMDBG_H("Posting IPA_ROUTE_ADD_EVENT with if index:%d, ipv4 addr:0x%x, mask: 0x%x and gw: 0x%x\n",
										 data_addr->if_index,
										 data_addr->ipv4_addr,
										 data_addr->ipv4_addr_mask,
										 data_addr->ipv4_addr_gw);
						evt_data.evt_data = data_addr;
						IPACM_EvtDispatcher::PostEvt(&evt_data);
						/* finish command queue */
					}
				}
			}

			/* ipv6 routing table */
			if((AF_INET6 == msg_ptr->nl_route_info.metainfo.rtm_family) &&
				 (msg_ptr->nl_route_info.metainfo.rtm_type == RTN_UNICAST) &&
				 (msg_ptr->nl_route_info.metainfo.rtm_protocol == RTPROT_KERNEL) &&
				 (msg_ptr->nl_route_info.metainfo.rtm_table == RT_TABLE_MAIN))
			{
				IPACMDBG("\n GOT valid v6-RTM_NEWROUTE event\n");
				ret_val = ipa_get_if_name(dev_name, msg_ptr->nl_route_info.attr_info.oif_index);
				if(ret_val != IPACM_SUCCESS)
				{
					IPACMERR("Error while getting interface name\n");
					return IPACM_FAILURE;
				}

				if(msg_ptr->nl_route_info.attr_info.param_mask & IPA_RTA_PARAM_DST)
				{
					IPACM_NL_REPORT_ADDR( "Route ADD DST:", msg_ptr->nl_route_info.attr_info.dst_addr );
					IPACMDBG("%d, metric %d, dev %s\n",
									 msg_ptr->nl_route_info.metainfo.rtm_dst_len,
									 msg_ptr->nl_route_info.attr_info.priority,
									 dev_name);

					/* insert to command queue */
					data_addr = (ipacm_event_data_addr *)malloc(sizeof(ipacm_event_data_addr));
					if(data_addr == NULL)
					{
						IPACMERR("unable to allocate memory for event data_addr\n");
						return IPACM_FAILURE;
					}

					 IPACM_EVENT_COPY_ADDR_v6( data_addr->ipv6_addr, msg_ptr->nl_route_info.attr_info.dst_addr);

					data_addr->ipv6_addr[0] = ntohl(data_addr->ipv6_addr[0]);
					data_addr->ipv6_addr[1] = ntohl(data_addr->ipv6_addr[1]);
					data_addr->ipv6_addr[2] = ntohl(data_addr->ipv6_addr[2]);
					data_addr->ipv6_addr[3] = ntohl(data_addr->ipv6_addr[3]);

					mask_value_v6 = msg_ptr->nl_route_info.metainfo.rtm_dst_len;
					for(mask_index = 0; mask_index < 4; mask_index++)
					{
						if(mask_value_v6 >= 32)
						{
							mask_v6(32, &data_addr->ipv6_addr_mask[mask_index]);
							mask_value_v6 -= 32;
						}
						else
						{
							mask_v6(mask_value_v6, &data_addr->ipv6_addr_mask[mask_index]);
							mask_value_v6 = 0;
						}
					}

					IPACMDBG("ADD IPV6 MASK %d: %08x:%08x:%08x:%08x \n",
									 msg_ptr->nl_route_info.metainfo.rtm_dst_len,
									 data_addr->ipv6_addr_mask[0],
									 data_addr->ipv6_addr_mask[1],
									 data_addr->ipv6_addr_mask[2],
									 data_addr->ipv6_addr_mask[3]);

					data_addr->ipv6_addr_mask[0] = ntohl(data_addr->ipv6_addr_mask[0]);
					data_addr->ipv6_addr_mask[1] = ntohl(data_addr->ipv6_addr_mask[1]);
					data_addr->ipv6_addr_mask[2] = ntohl(data_addr->ipv6_addr_mask[2]);
					data_addr->ipv6_addr_mask[3] = ntohl(data_addr->ipv6_addr_mask[3]);

					evt_data.event = IPA_ROUTE_ADD_EVENT;
					data_addr->if_index = msg_ptr->nl_route_info.attr_info.oif_index;
					data_addr->iptype = IPA_IP_v6;

					IPACMDBG("Posting IPA_ROUTE_ADD_EVENT with if index:%d, ipv6 addr\n",
									 data_addr->if_index);
					evt_data.evt_data = data_addr;
					IPACM_EvtDispatcher::PostEvt(&evt_data);
					/* finish command queue */
				}
				if(msg_ptr->nl_route_info.attr_info.param_mask & IPA_RTA_PARAM_GATEWAY)
				{
					IPACM_NL_REPORT_ADDR( "Route ADD ::/0  Next Hop:", msg_ptr->nl_route_info.attr_info.gateway_addr );
					IPACMDBG(" metric %d, dev %s\n",
									 msg_ptr->nl_route_info.attr_info.priority,
									 dev_name);

					/* insert to command queue */
					data_addr = (ipacm_event_data_addr *)malloc(sizeof(ipacm_event_data_addr));
					if(data_addr == NULL)
					{
						IPACMERR("unable to allocate memory for event data_addr\n");
						return IPACM_FAILURE;
					}

					IPACM_EVENT_COPY_ADDR_v6( data_addr->ipv6_addr, msg_ptr->nl_route_info.attr_info.dst_addr);

                    data_addr->ipv6_addr[0]=ntohl(data_addr->ipv6_addr[0]);
                    data_addr->ipv6_addr[1]=ntohl(data_addr->ipv6_addr[1]);
                    data_addr->ipv6_addr[2]=ntohl(data_addr->ipv6_addr[2]);
                    data_addr->ipv6_addr[3]=ntohl(data_addr->ipv6_addr[3]);

					IPACM_EVENT_COPY_ADDR_v6( data_addr->ipv6_addr_mask, msg_ptr->nl_route_info.attr_info.dst_addr);

					data_addr->ipv6_addr_mask[0]=ntohl(data_addr->ipv6_addr_mask[0]);
                    data_addr->ipv6_addr_mask[1]=ntohl(data_addr->ipv6_addr_mask[1]);
                    data_addr->ipv6_addr_mask[2]=ntohl(data_addr->ipv6_addr_mask[2]);
                    data_addr->ipv6_addr_mask[3]=ntohl(data_addr->ipv6_addr_mask[3]);

					evt_data.event = IPA_ROUTE_ADD_EVENT;
					data_addr->if_index = msg_ptr->nl_route_info.attr_info.oif_index;
					data_addr->iptype = IPA_IP_v6;

					IPACMDBG("posting IPA_ROUTE_ADD_EVENT with if index:%d, ipv6 address\n",
									 data_addr->if_index);
					evt_data.evt_data = data_addr;
					IPACM_EvtDispatcher::PostEvt(&evt_data);
					/* finish command queue */
				}
			}
			break;

		case RTM_DELROUTE:
			if(IPACM_SUCCESS != ipa_nl_decode_rtm_route(buffer, buflen, &(msg_ptr->nl_route_info)))
			{
				IPACMERR("Failed to decode rtm route message\n");
				return IPACM_FAILURE;
			}
			/* take care of route delete of default route & uniroute */
			if((msg_ptr->nl_route_info.metainfo.rtm_type == RTN_UNICAST) &&
				 ((msg_ptr->nl_route_info.metainfo.rtm_protocol == RTPROT_BOOT) ||
				  (msg_ptr->nl_route_info.metainfo.rtm_protocol == RTPROT_RA)) &&
				 (msg_ptr->nl_route_info.metainfo.rtm_scope == 0) &&
				 (msg_ptr->nl_route_info.metainfo.rtm_table == RT_TABLE_MAIN))
			{

				if(msg_ptr->nl_route_info.attr_info.param_mask & IPA_RTA_PARAM_DST)
				{
					ret_val = ipa_get_if_name(dev_name, msg_ptr->nl_route_info.attr_info.oif_index);
					if(ret_val != IPACM_SUCCESS)
					{
						IPACMERR("Error while getting interface name\n");
						return IPACM_FAILURE;
					}
					IPACM_NL_REPORT_ADDR( "route del -host ", msg_ptr->nl_route_info.attr_info.dst_addr);
					IPACM_NL_REPORT_ADDR( " gw ", msg_ptr->nl_route_info.attr_info.gateway_addr);
					IPACMDBG("dev %s\n", dev_name);

					/* insert to command queue */
					data_addr = (ipacm_event_data_addr *)malloc(sizeof(ipacm_event_data_addr));
					if(data_addr == NULL)
					{
						IPACMERR("unable to allocate memory for event data_addr\n");
						return IPACM_FAILURE;
					}
					IPACM_EVENT_COPY_ADDR_v4( if_ipv4_addr, msg_ptr->nl_route_info.attr_info.dst_addr);
					temp = (-1);
					if_ipipv4_addr_mask = ntohl(temp);

					evt_data.event = IPA_ROUTE_DEL_EVENT;
					data_addr->if_index = msg_ptr->nl_route_info.attr_info.oif_index;
					data_addr->iptype = IPA_IP_v4;
					data_addr->ipv4_addr = ntohl(if_ipv4_addr);
					data_addr->ipv4_addr_mask = ntohl(if_ipipv4_addr_mask);

					IPACMDBG_H("Posting event IPA_ROUTE_DEL_EVENT with if index:%d, ipv4 address 0x%x, mask:0x%x\n",
									 data_addr->if_index,
									 data_addr->ipv4_addr,
									 data_addr->ipv4_addr_mask);
					evt_data.evt_data = data_addr;
					IPACM_EvtDispatcher::PostEvt(&evt_data);
					/* finish command queue */
				}
				else
				{
					ret_val = ipa_get_if_name(dev_name, msg_ptr->nl_route_info.attr_info.oif_index);
					if(ret_val != IPACM_SUCCESS)
					{
						IPACMERR("Error while getting interface name\n");
						return IPACM_FAILURE;
					}

					/* insert to command queue */
					data_addr = (ipacm_event_data_addr *)malloc(sizeof(ipacm_event_data_addr));
					if(data_addr == NULL)
					{
						IPACMERR("unable to allocate memory for event data_addr\n");
						return IPACM_FAILURE;
					}

					if(AF_INET6 == msg_ptr->nl_route_info.metainfo.rtm_family)
					{
						if(msg_ptr->nl_route_info.attr_info.param_mask & IPA_RTA_PARAM_PRIORITY)
						{
							IPACMDBG("ip -6 route del default dev %s metric %d\n",
											 dev_name,
											 msg_ptr->nl_route_info.attr_info.priority);
						}
						else
						{
							IPACMDBG("ip -6 route del default dev %s\n", dev_name);
						}
						IPACM_EVENT_COPY_ADDR_v6( data_addr->ipv6_addr, msg_ptr->nl_route_info.attr_info.dst_addr);
						data_addr->ipv6_addr[0] = ntohl(data_addr->ipv6_addr[0]);
						data_addr->ipv6_addr[1] = ntohl(data_addr->ipv6_addr[1]);
						data_addr->ipv6_addr[2] = ntohl(data_addr->ipv6_addr[2]);
						data_addr->ipv6_addr[3] = ntohl(data_addr->ipv6_addr[3]);

						IPACM_EVENT_COPY_ADDR_v6( data_addr->ipv6_addr_mask, msg_ptr->nl_route_info.attr_info.dst_addr);
						data_addr->ipv6_addr_mask[0] = ntohl(data_addr->ipv6_addr_mask[0]);
						data_addr->ipv6_addr_mask[1] = ntohl(data_addr->ipv6_addr_mask[1]);
						data_addr->ipv6_addr_mask[2] = ntohl(data_addr->ipv6_addr_mask[2]);
						data_addr->ipv6_addr_mask[3] = ntohl(data_addr->ipv6_addr_mask[3]);

						IPACM_EVENT_COPY_ADDR_v6( data_addr->ipv6_addr_gw, msg_ptr->nl_route_info.attr_info.gateway_addr);
						data_addr->ipv6_addr_gw[0] = ntohl(data_addr->ipv6_addr_gw[0]);
						data_addr->ipv6_addr_gw[1] = ntohl(data_addr->ipv6_addr_gw[1]);
						data_addr->ipv6_addr_gw[2] = ntohl(data_addr->ipv6_addr_gw[2]);
						data_addr->ipv6_addr_gw[3] = ntohl(data_addr->ipv6_addr_gw[3]);
						IPACM_NL_REPORT_ADDR( " ", msg_ptr->nl_route_info.attr_info.gateway_addr);
						data_addr->iptype = IPA_IP_v6;
					}
					else
					{
						IPACM_NL_REPORT_ADDR( "route del default gw", msg_ptr->nl_route_info.attr_info.gateway_addr);
						IPACMDBG("dev %s\n", dev_name);

						IPACM_EVENT_COPY_ADDR_v4( data_addr->ipv4_addr, msg_ptr->nl_route_info.attr_info.dst_addr);
						data_addr->ipv4_addr = ntohl(data_addr->ipv4_addr);

						IPACM_EVENT_COPY_ADDR_v4( data_addr->ipv4_addr_mask, msg_ptr->nl_route_info.attr_info.dst_addr);
						data_addr->ipv4_addr_mask = ntohl(data_addr->ipv4_addr_mask);

						data_addr->iptype = IPA_IP_v4;
					}

					evt_data.event = IPA_ROUTE_DEL_EVENT;
					data_addr->if_index = msg_ptr->nl_route_info.attr_info.oif_index;

					IPACMDBG_H("Posting IPA_ROUTE_DEL_EVENT with if index:%d\n",
									 data_addr->if_index);
					evt_data.evt_data = data_addr;
					IPACM_EvtDispatcher::PostEvt(&evt_data);
					/* finish command queue */
				}
			}

			/* ipv6 routing table */
			if((AF_INET6 == msg_ptr->nl_route_info.metainfo.rtm_family) &&
				 (msg_ptr->nl_route_info.metainfo.rtm_type == RTN_UNICAST) &&
				 (msg_ptr->nl_route_info.metainfo.rtm_protocol == RTPROT_KERNEL) &&
				 (msg_ptr->nl_route_info.metainfo.rtm_table == RT_TABLE_MAIN))
			{
				IPACMDBG("\n GOT valid v6-RTM_DELROUTE event\n");
				ret_val = ipa_get_if_name(dev_name, msg_ptr->nl_route_info.attr_info.oif_index);
				if(ret_val != IPACM_SUCCESS)
				{
					IPACMERR("Error while getting interface name");
					return IPACM_FAILURE;
				}

				if(msg_ptr->nl_route_info.attr_info.param_mask & IPA_RTA_PARAM_DST)
				{
					IPACM_NL_REPORT_ADDR( "DEL", msg_ptr->nl_route_info.attr_info.dst_addr);
					IPACMDBG("/%d, metric %d, dev %s\n",
									 msg_ptr->nl_route_info.metainfo.rtm_dst_len,
									 msg_ptr->nl_route_info.attr_info.priority,
									 dev_name);

					/* insert to command queue */
					data_addr = (ipacm_event_data_addr *)malloc(sizeof(ipacm_event_data_addr));
					if(data_addr == NULL)
					{
						IPACMERR("unable to allocate memory for event data_addr\n");
						return IPACM_FAILURE;
					}

					IPACM_EVENT_COPY_ADDR_v6( data_addr->ipv6_addr, msg_ptr->nl_route_info.attr_info.dst_addr);

					data_addr->ipv6_addr[0] = ntohl(data_addr->ipv6_addr[0]);
					data_addr->ipv6_addr[1] = ntohl(data_addr->ipv6_addr[1]);
					data_addr->ipv6_addr[2] = ntohl(data_addr->ipv6_addr[2]);
					data_addr->ipv6_addr[3] = ntohl(data_addr->ipv6_addr[3]);

					mask_value_v6 = msg_ptr->nl_route_info.metainfo.rtm_dst_len;
					for(mask_index = 0; mask_index < 4; mask_index++)
					{
						IPACMDBG("%dst %d \n",
										 mask_index,
										 mask_value_v6);
						if(mask_value_v6 >= 32)
						{
							mask_v6(32, &data_addr->ipv6_addr_mask[mask_index]);
							mask_value_v6 -= 32;
							IPACMDBG("%dst: %08x \n",
											 mask_index,
											 data_addr->ipv6_addr_mask[mask_index]);
						}
						else
						{
							mask_v6(mask_value_v6, data_addr->ipv6_addr_mask);
							mask_value_v6 = 0;
							IPACMDBG("%dst: %08x \n",
											 mask_index,
											 data_addr->ipv6_addr_mask[mask_index]);
						}
					}

					IPACMDBG("DEL IPV6 MASK 0st: %08x ",
									 data_addr->ipv6_addr_mask[0]);
					IPACMDBG("1st: %08x ",
									 data_addr->ipv6_addr_mask[1]);
					IPACMDBG("2st: %08x ",
									 data_addr->ipv6_addr_mask[2]);
					IPACMDBG("3st: %08x \n",
									 data_addr->ipv6_addr_mask[3]);

					data_addr->ipv6_addr_mask[0] = ntohl(data_addr->ipv6_addr_mask[0]);
					data_addr->ipv6_addr_mask[1] = ntohl(data_addr->ipv6_addr_mask[1]);
					data_addr->ipv6_addr_mask[2] = ntohl(data_addr->ipv6_addr_mask[2]);
					data_addr->ipv6_addr_mask[3] = ntohl(data_addr->ipv6_addr_mask[3]);

					evt_data.event = IPA_ROUTE_DEL_EVENT;
					data_addr->if_index = msg_ptr->nl_route_info.attr_info.oif_index;
					data_addr->iptype = IPA_IP_v6;

					IPACMDBG_H("posting event IPA_ROUTE_DEL_EVENT with if index:%d, ipv4 address\n",
									 data_addr->if_index);
					evt_data.evt_data = data_addr;
					IPACM_EvtDispatcher::PostEvt(&evt_data);
					/* finish command queue */
				}
			}
			break;

		case RTM_NEWNEIGH:
			if(IPACM_SUCCESS != ipa_nl_decode_rtm_neigh(buffer, buflen, &(msg_ptr->nl_neigh_info)))
			{
				IPACMERR("Failed to decode rtm neighbor message\n");
				return IPACM_FAILURE;
			}

			ret_val = ipa_get_if_name(dev_name, msg_ptr->nl_neigh_info.metainfo.ndm_ifindex);
			if(ret_val != IPACM_SUCCESS)
			{
				IPACMERR("Error while getting interface index\n");
				return IPACM_FAILURE;
			}
			else
				{
				IPACMDBG("\n GOT RTM_NEWNEIGH event (%s) ip %d\n",dev_name,msg_ptr->nl_neigh_info.attr_info.local_addr.ss_family);
			}

			/* insert to command queue */
		    data_all = (ipacm_event_data_all *)malloc(sizeof(ipacm_event_data_all));
		    if(data_all == NULL)
			{
		    	IPACMERR("unable to allocate memory for event data_all\n");
						return IPACM_FAILURE;
			}

		    memset(data_all, 0, sizeof(ipacm_event_data_all));
		    if(msg_ptr->nl_neigh_info.attr_info.local_addr.ss_family == AF_INET6)
		    {
				IPACM_NL_REPORT_ADDR( " ", msg_ptr->nl_neigh_info.attr_info.local_addr);
				IPACM_EVENT_COPY_ADDR_v6( data_all->ipv6_addr, msg_ptr->nl_neigh_info.attr_info.local_addr);

				data_all->ipv6_addr[0]=ntohl(data_all->ipv6_addr[0]);
				data_all->ipv6_addr[1]=ntohl(data_all->ipv6_addr[1]);
				data_all->ipv6_addr[2]=ntohl(data_all->ipv6_addr[2]);
				data_all->ipv6_addr[3]=ntohl(data_all->ipv6_addr[3]);
				data_all->iptype = IPA_IP_v6;
		    }
		    else if (msg_ptr->nl_neigh_info.attr_info.local_addr.ss_family == AF_INET)
		    {
   				IPACM_NL_REPORT_ADDR( " ", msg_ptr->nl_neigh_info.attr_info.local_addr);
				IPACM_EVENT_COPY_ADDR_v4( data_all->ipv4_addr, msg_ptr->nl_neigh_info.attr_info.local_addr);
		    	data_all->ipv4_addr = ntohl(data_all->ipv4_addr);
		    	data_all->iptype = IPA_IP_v4;
		    }
		    else
		    {
		        data_all->iptype = IPA_IP_v6;
		    }

		    IPACMDBG("NDA_LLADDR:MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
		     (unsigned char)(msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr).sa_data[0],
		     (unsigned char)(msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr).sa_data[1],
		     (unsigned char)(msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr).sa_data[2],
		     (unsigned char)(msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr).sa_data[3],
		     (unsigned char)(msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr).sa_data[4],
		     (unsigned char)(msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr).sa_data[5]);


		    memcpy(data_all->mac_addr,
		    			 msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr.sa_data,
		    			 sizeof(data_all->mac_addr));
			data_all->if_index = msg_ptr->nl_neigh_info.metainfo.ndm_ifindex;
			strlcpy(data_all->iface_name, dev_name, sizeof(data_all->iface_name));
			/* Add support to replace src-mac as bridge0 mac */
			if((msg_ptr->nl_neigh_info.metainfo.ndm_family == AF_BRIDGE) &&
				(msg_ptr->nl_neigh_info.metainfo.ndm_state == NUD_PERMANENT))
		    {
				/* Posting IPA_BRIDGE_LINK_UP_EVENT event */
				evt_data.event = IPA_BRIDGE_LINK_UP_EVENT;
				IPACMDBG_H("posting IPA_BRIDGE_LINK_UP_EVENT (%s):index:%d \n",
                                 dev_name,
 		                    data_all->if_index);
			}
			else
		    {
				/* Posting new_neigh events for all LAN/WAN clients */
				evt_data.event = IPA_NEW_NEIGH_EVENT;
				IPACMDBG_H("posting IPA_NEW_NEIGH_EVENT (%s):index:%d ip-family: %d\n",
                                 dev_name,
 		                    data_all->if_index,
		    				 msg_ptr->nl_neigh_info.attr_info.local_addr.ss_family);
			}
		    evt_data.evt_data = data_all;
					IPACM_EvtDispatcher::PostEvt(&evt_data);
					/* finish command queue */
			break;

		case RTM_DELNEIGH:
			if(IPACM_SUCCESS != ipa_nl_decode_rtm_neigh(buffer, buflen, &(msg_ptr->nl_neigh_info)))
			{
				IPACMERR("Failed to decode rtm neighbor message\n");
				return IPACM_FAILURE;
			}

			ret_val = ipa_get_if_name(dev_name, msg_ptr->nl_neigh_info.metainfo.ndm_ifindex);
			if(ret_val != IPACM_SUCCESS)
			{
				IPACMERR("Error while getting interface index\n");
				return IPACM_FAILURE;
			}
			else
			{
				IPACMDBG("\n GOT RTM_DELNEIGH event (%s) ip %d\n",dev_name,msg_ptr->nl_neigh_info.attr_info.local_addr.ss_family);
			}

				/* insert to command queue */
				data_all = (ipacm_event_data_all *)malloc(sizeof(ipacm_event_data_all));
				if(data_all == NULL)
				{
					IPACMERR("unable to allocate memory for event data_all\n");
					return IPACM_FAILURE;
				}

		    memset(data_all, 0, sizeof(ipacm_event_data_all));
		    if(msg_ptr->nl_neigh_info.attr_info.local_addr.ss_family == AF_INET6)
				{
					IPACM_NL_REPORT_ADDR( " ", msg_ptr->nl_neigh_info.attr_info.local_addr);
					IPACM_EVENT_COPY_ADDR_v6( data_all->ipv6_addr, msg_ptr->nl_neigh_info.attr_info.local_addr);

					data_all->ipv6_addr[0] = ntohl(data_all->ipv6_addr[0]);
					data_all->ipv6_addr[1] = ntohl(data_all->ipv6_addr[1]);
					data_all->ipv6_addr[2] = ntohl(data_all->ipv6_addr[2]);
					data_all->ipv6_addr[3] = ntohl(data_all->ipv6_addr[3]);
					data_all->iptype = IPA_IP_v6;
				}
		    else if (msg_ptr->nl_neigh_info.attr_info.local_addr.ss_family == AF_INET)
				{
					IPACM_NL_REPORT_ADDR( " ", msg_ptr->nl_neigh_info.attr_info.local_addr);
					IPACM_EVENT_COPY_ADDR_v4( data_all->ipv4_addr, msg_ptr->nl_neigh_info.attr_info.local_addr);
					data_all->ipv4_addr = ntohl(data_all->ipv4_addr);
					data_all->iptype = IPA_IP_v4;
				}
		    else
		    {
		        data_all->iptype = IPA_IP_v6;
		    }

		    IPACMDBG("NDA_LLADDR:MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
		     (unsigned char)(msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr).sa_data[0],
		     (unsigned char)(msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr).sa_data[1],
		     (unsigned char)(msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr).sa_data[2],
		     (unsigned char)(msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr).sa_data[3],
		     (unsigned char)(msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr).sa_data[4],
		     (unsigned char)(msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr).sa_data[5]);

				memcpy(data_all->mac_addr,
							 msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr.sa_data,
							 sizeof(data_all->mac_addr));
		    evt_data.event = IPA_DEL_NEIGH_EVENT;
				data_all->if_index = msg_ptr->nl_neigh_info.metainfo.ndm_ifindex;

		    IPACMDBG_H("posting IPA_DEL_NEIGH_EVENT (%s):index:%d ip-family: %d\n",
                                 dev_name,
 		                    data_all->if_index,
		    				 msg_ptr->nl_neigh_info.attr_info.local_addr.ss_family);
				evt_data.evt_data = data_all;
				IPACM_EvtDispatcher::PostEvt(&evt_data);
				/* finish command queue */
			break;

		default:
			IPACMDBG(" ignore NL event %d!!!\n ", nlh->nlmsg_type);
			break;

		}
		nlh = NLMSG_NEXT(nlh, buflen);
	}

	return IPACM_SUCCESS;
}


/*  Virtual function registered to receive incoming messages over the NETLINK routing socket*/
int ipa_nl_recv_msg(int fd)
{
	struct msghdr *msghdr = NULL;
	struct iovec *iov = NULL;
	unsigned int msglen = 0;
	ipa_nl_msg_t *nlmsg = NULL;

	nlmsg = (ipa_nl_msg_t *)malloc(sizeof(ipa_nl_msg_t));
	if(NULL == nlmsg)
	{
		IPACMERR("Failed alloc of nlmsg \n");
		goto error;
	}
	else
	{
		if(IPACM_SUCCESS != ipa_nl_recv(fd, &msghdr, &msglen))
		{
			IPACMERR("Failed to receive nl message \n");
			goto error;
		}

		if(msghdr== NULL)
		{
			IPACMERR(" failed to get msghdr\n");
			goto error;
		}

		iov = msghdr->msg_iov;

		memset(nlmsg, 0, sizeof(ipa_nl_msg_t));
		if(IPACM_SUCCESS != ipa_nl_decode_nlmsg((char *)iov->iov_base, msglen, nlmsg))
		{
			IPACMERR("Failed to decode nl message \n");
			goto error;
		}
		/* Release NetLink message buffer */
		if(msghdr)
		{
			ipa_nl_release_msg(msghdr);
		}
		if(nlmsg)
		{
			free(nlmsg);
		}
	}

	return IPACM_SUCCESS;

error:
	if(msghdr)
	{
		ipa_nl_release_msg(msghdr);
	}
	if(nlmsg)
	{
		free(nlmsg);
	}

	return IPACM_FAILURE;
}

/*  get ipa interface name */
int ipa_get_if_name
(
	 char *if_name,
	 int if_index
	 )
{
	int fd;
	struct ifreq ifr;

	if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		IPACMERR("get interface name socket create failed \n");
		return IPACM_FAILURE;
	}

	memset(&ifr, 0, sizeof(struct ifreq));
	ifr.ifr_ifindex = if_index;
	IPACMDBG("Interface index %d\n", if_index);

	if(ioctl(fd, SIOCGIFNAME, &ifr) < 0)
	{
		IPACMERR("call_ioctl_on_dev: ioctl failed:\n");
		close(fd);
		return IPACM_FAILURE;
	}

	(void)strlcpy(if_name, ifr.ifr_name, sizeof(ifr.ifr_name));
	IPACMDBG("interface name %s\n", ifr.ifr_name);
	close(fd);

	return IPACM_SUCCESS;
}

/* Initialization routine for listener on NetLink sockets interface */
int ipa_nl_listener_init
(
	 unsigned int nl_type,
	 unsigned int nl_groups,
	 ipa_nl_sk_fd_set_info_t *sk_fdset,
	 ipa_sock_thrd_fd_read_f read_f
	 )
{
	ipa_nl_sk_info_t sk_info;
	int ret_val;

	memset(&sk_info, 0, sizeof(ipa_nl_sk_info_t));
	IPACMDBG_H("Entering IPA NL listener init\n");

	if(ipa_nl_open_socket(&sk_info, nl_type, nl_groups) == IPACM_SUCCESS)
	{
		IPACMDBG_H("IPA Open netlink socket succeeds\n");
	}
	else
	{
		IPACMERR("Netlink socket open failed\n");
		return IPACM_FAILURE;
	}

	/* Add NETLINK socket to the list of sockets that the listener
					 thread should listen on. */

	if(ipa_nl_addfd_map(sk_fdset, sk_info.sk_fd, read_f) != IPACM_SUCCESS)
	{
		IPACMERR("cannot add nl routing sock for reading\n");
		close(sk_info.sk_fd);
		return IPACM_FAILURE;
	}

	/* Start the socket listener thread */
	ret_val = ipa_nl_sock_listener_start(sk_fdset);

	if(ret_val != IPACM_SUCCESS)
	{
		IPACMERR("Failed to start NL listener\n");
	}

	return IPACM_SUCCESS;
}

/* find the newroute subnet mask */
int find_mask(int ip_v4_last, int *mask_value)
{

	switch(ip_v4_last)
	{

	case 3:
		*mask_value = 252;
		return IPACM_SUCCESS;
		break;

	case 7:
		*mask_value = 248;
		return IPACM_SUCCESS;
		break;

	case 15:
		*mask_value = 240;
		return IPACM_SUCCESS;
		break;

	case 31:
		*mask_value = 224;
		return IPACM_SUCCESS;
		break;

	case 63:
		*mask_value = 192;
		return IPACM_SUCCESS;
		break;

	case 127:
		*mask_value = 128;
		return IPACM_SUCCESS;
		break;

	case 255:
		*mask_value = 0;
		return IPACM_SUCCESS;
		break;

	default:
		return IPACM_FAILURE;
		break;

	}
}

/* map mask value for ipv6 */
int mask_v6(int index, uint32_t *mask)
{
	switch(index)
	{

	case 0:
		*mask = 0x00000000;
		return IPACM_SUCCESS;
		break;
	case 4:
		*mask = 0xf0000000;
		return IPACM_SUCCESS;
		break;
	case 8:
		*mask = 0xff000000;
		return IPACM_SUCCESS;
		break;
	case 12:
		*mask = 0xfff00000;
		return IPACM_SUCCESS;
		break;
	case 16:
		*mask = 0xffff0000;
		return IPACM_SUCCESS;
		break;
	case 20:
		*mask = 0xfffff000;
		return IPACM_SUCCESS;
		break;
	case 24:
		*mask = 0xffffff00;
		return IPACM_SUCCESS;
		break;
	case 28:
		*mask = 0xfffffff0;
		return IPACM_SUCCESS;
		break;
	case 32:
		*mask = 0xffffffff;
		return IPACM_SUCCESS;
		break;
	default:
		return IPACM_FAILURE;
		break;

	}
}


