972 lines
28 KiB
C
972 lines
28 KiB
C
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_RCPL8_2024030303 $ $RCSfile: ipnet_rtnetlink_neigh.c,v $ $Revision: 1.16.10.6 $
|
|
* $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_rtnetlink_neigh.c,v $
|
|
* $Author: lwang7 $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation Jonas Rendel <Jonas.Rendel@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
* Contains netlink handlers for managing Ipv4 and Ipv6 addresses.
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifndef IPCOM_KERNEL
|
|
#define IPCOM_KERNEL
|
|
#endif
|
|
#include "ipnet_config.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifdef IPNET_USE_NETLINK
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom.h>
|
|
#include <ipcom_type.h>
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_sock6.h>
|
|
#include <ipcom_sock2.h>
|
|
#include <ipcom_route.h>
|
|
#include <ipcom_errno.h>
|
|
|
|
#include "ipnet_h.h"
|
|
#include "ipnet_ctrl.h"
|
|
#include "ipnet_route.h"
|
|
#include "ipnet_netlink_h.h"
|
|
#include "ipnet_rtnetlink_h.h"
|
|
#include "ipcom_sock.h"
|
|
#ifdef IPCOM_USE_INET6
|
|
#include "ipcom_sock6.h"
|
|
#include "ipnet_ip6.h"
|
|
#endif
|
|
|
|
#ifdef IP_PORT_LKM
|
|
#include "ipcom_lkm.h" /* Needed for Linux PPP interface config */
|
|
#endif /* IP_PORT_LKM */
|
|
|
|
#include "ipnet_eth.h"
|
|
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
#include "ipcom_forwarder.h"
|
|
#endif
|
|
|
|
#ifdef IPNET_USE_VRRP
|
|
#include "ipnet_vrrp.h"
|
|
#endif
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
typedef struct Ipnet_rtnetlink_neigh_family
|
|
{
|
|
struct
|
|
{
|
|
Ip_ptrdiff_t *vr;
|
|
Ip_ptrdiff_t *neigh;
|
|
Ip_ptrdiff_t *done;
|
|
Ip_ptrdiff_t aborted;
|
|
Ip_ptrdiff_t cont;
|
|
} state;
|
|
|
|
/* Requested */
|
|
struct Ip_nlmsghdr *nlmsg;
|
|
int family;
|
|
|
|
/* Buffering */
|
|
Ipnet_netlink_mem_t *mem;
|
|
}
|
|
Ipnet_rtnetlink_neigh_family_t;
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifdef IPNET_USE_VRRP
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_eth_vrrp_dtor
|
|
*===========================================================================
|
|
* Description: Destructor for VRRP neighbors.
|
|
* Parameters: neigh - neighbor entry that is used to respond to ARP
|
|
* requests for the VRRP MAC.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_eth_vrrp_dtor(const Ipnet_neigh *neigh)
|
|
{
|
|
Ipnet_netif *netif = neigh->netif;
|
|
|
|
switch(neigh->domain)
|
|
{
|
|
case IP_AF_INET:
|
|
#ifdef IPCOM_USE_INET
|
|
if (ipnet_ip4_get_addr_type2(neigh->addr.in.s_addr, netif)
|
|
== IPNET_ADDR_TYPE_UNICAST)
|
|
/*
|
|
* Owner address, inform link local nodes about the MAC
|
|
* change
|
|
*/
|
|
ipnet_arp_request(netif,
|
|
0,
|
|
neigh->addr.in.s_addr,
|
|
IP_TRUE,
|
|
IP_NULL,
|
|
IP_NULL);
|
|
return;
|
|
#endif
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_eth_vrrp_add_addr
|
|
*===========================================================================
|
|
* Description: Checks if the neighbor should be used for VRRP and
|
|
* adds the VRRP address in that case.
|
|
* Parameters: neigh - a neighbor entry.
|
|
* Returns: 0 = success
|
|
* <0 = error code
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_eth_vrrp_add_addr(Ipnet_neigh *neigh)
|
|
{
|
|
Ip_u8 vrid = neigh->lladdr[5];
|
|
|
|
if ((ipnet_eth_get_vrrp_mac_addr_type(neigh->lladdr) == IPNET_NOT_VRRP_MAC_ADDRESS)
|
|
|| vrid == 0
|
|
|| ipnet_neigh_is_blackhole(neigh))
|
|
/*
|
|
* Should not be added as a VRRP address.
|
|
*/
|
|
return 0;
|
|
/* set VRRP address to be publish */
|
|
ipnet_neigh_set_publ(neigh, IP_TRUE);
|
|
return ipnet_vrrp_add_addr(neigh, vrid);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_eth_vrrp_del_addr
|
|
*===========================================================================
|
|
* Description: Removes a VRRP address
|
|
* Parameters: neigh - neighbor used to respond to ARP/ND request on the
|
|
* VRRP address.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_eth_vrrp_del_addr(Ipnet_neigh *neigh)
|
|
{
|
|
if (ipnet_eth_get_vrrp_mac_addr_type(neigh->lladdr) == IPNET_NOT_VRRP_MAC_ADDRESS)
|
|
return -IP_ERRNO_EINVAL;
|
|
|
|
return ipnet_vrrp_del_addr(neigh, neigh->lladdr[5], ipnet_eth_vrrp_dtor);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_rtnetlink_neigh_foreach
|
|
*===========================================================================
|
|
* Description: Called for each neighbor entry during dumping of
|
|
* neighbors via netlink.
|
|
* Parameters: neigh - a neighbor entry
|
|
* user_arg - pointer the the netlink dump parameters.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_rtnetlink_neigh_foreach(Ipnet_neigh *neigh, void *user_arg)
|
|
{
|
|
Ipnet_rtnetlink_neigh_family_t *param = user_arg;
|
|
Ip_u16 vr = neigh->netif->vr_index;
|
|
int ret = 0;
|
|
|
|
/*
|
|
* This traversal is already aborted
|
|
*/
|
|
if (param->state.aborted)
|
|
return;
|
|
|
|
/*
|
|
* Is this the right domain? AF_UNSPEC means we should dump all
|
|
* domain
|
|
*/
|
|
if (param->family != IP_AF_UNSPEC
|
|
&& neigh->key_domain != param->family)
|
|
return;
|
|
|
|
/*
|
|
* Check if specific VR shall be dumped.
|
|
*/
|
|
if (IP_BIT_ISFALSE(param->nlmsg->nlmsg_flags, IP_NLM_F_VR_UNSPEC)
|
|
&& (param->mem->vr != IPCOM_VR_ANY)
|
|
&& (param->mem->vr != vr))
|
|
return;
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if (neigh->domain == IP_AF_INET6
|
|
&& ipnet_rtnetlink_ip6_neigh_not_local(neigh) == IP_FALSE)
|
|
return;
|
|
#endif
|
|
/*
|
|
* If this is a trailer message buffer, make sure it
|
|
* starts dumping where the last message ended
|
|
*/
|
|
if (param->state.cont)
|
|
{
|
|
Ipnet_neigh *prev_neigh = (Ipnet_neigh*)(*param->state.neigh);
|
|
|
|
/*
|
|
* Verify current state
|
|
*/
|
|
if (*param->state.vr != vr)
|
|
return;
|
|
|
|
if (prev_neigh != neigh)
|
|
/*
|
|
* Skip all entries until we get back to the entry we
|
|
* should continue from.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Reset the continue variable; we're on the move again
|
|
*/
|
|
param->state.cont = 0;
|
|
|
|
/*
|
|
* Verify this particular NC entry
|
|
*/
|
|
#ifdef IPCOM_USE_INET
|
|
if (neigh->domain == IP_AF_INET)
|
|
ret = ipnet_rtnetlink_ip4_neigh_fill_info(param->mem,
|
|
neigh,
|
|
param->nlmsg->nlmsg_seq,
|
|
IP_RTM_NEWNEIGH,
|
|
IP_NLM_F_MULTI);
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
if (neigh->domain == IP_AF_INET6)
|
|
ret = ipnet_rtnetlink_ip6_neigh_fill_info(param->mem,
|
|
neigh,
|
|
param->nlmsg->nlmsg_seq,
|
|
IP_RTM_NEWNEIGH,
|
|
IP_NLM_F_MULTI);
|
|
#endif
|
|
if (ret < 0)
|
|
goto aborted;
|
|
|
|
return;
|
|
|
|
aborted:
|
|
param->state.aborted = 1;
|
|
*param->state.neigh = (Ip_ptrdiff_t)neigh;
|
|
*param->state.vr = (Ip_ptrdiff_t)vr;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_rtnetlink_neigh_event
|
|
*===========================================================================
|
|
* Description: Status of a neighbor has changed create a netlink
|
|
* event to inform any listeners about it
|
|
* Parameters: neigh - neighbor entry that generated the event
|
|
* event - RTM_NEWNEIGH or RTM_DELNEIGH
|
|
* Returns: -
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_rtnetlink_neigh_event(Ipnet_neigh *neigh, int event)
|
|
{
|
|
Ipcom_pkt *pkt = IP_NULL;
|
|
Ipnet_netlink_mem_t mem;
|
|
int ret = -1;
|
|
|
|
if (!ipnet_netlink_pkt_alloc(&pkt, IP_NULL, IP_TRUE, &mem, 512))
|
|
return;
|
|
|
|
/*
|
|
* Set the VR-index
|
|
*/
|
|
pkt->vr_index = mem.vr = neigh->netif->vr_index;
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if (neigh->domain == IP_AF_INET)
|
|
{
|
|
ret = ipnet_rtnetlink_ip4_neigh_fill_info (&mem,
|
|
neigh,
|
|
0,
|
|
event,
|
|
0);
|
|
}
|
|
#endif
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if (neigh->domain == IP_AF_INET6)
|
|
{
|
|
if (ipnet_rtnetlink_ip6_neigh_not_local(neigh))
|
|
ret = ipnet_rtnetlink_ip6_neigh_fill_info (&mem,
|
|
neigh,
|
|
0,
|
|
event,
|
|
0);
|
|
}
|
|
#endif
|
|
|
|
if (ret < 0)
|
|
ipcom_pkt_free(pkt);
|
|
else
|
|
{
|
|
pkt->end += ret;
|
|
|
|
/*
|
|
* We're done; do report this pkt
|
|
*/
|
|
ipnet_rtnetlink_broadcast(pkt, IP_RTNLGRP_NEIGH);
|
|
}
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*==========================================================================
|
|
* ipnet_rtnetlink_getroute_family
|
|
*===========================================================================
|
|
* Description: Get interface info via NETLINK
|
|
* Parameters: data - Message payload.
|
|
* len - Length of message.
|
|
* cb - NETLINK callback entry
|
|
*
|
|
* Returns: 0 - Success
|
|
* <0 - Failure
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipnet_rtnetlink_neigh_get_family(Ipnet_netlink_mem_t *mem,
|
|
struct Ip_nlmsghdr *nlh,
|
|
struct Ip_rtattr **rta,
|
|
int family)
|
|
{
|
|
struct Ip_ndmsg *ndm = IP_NLMSG_DATA(nlh);
|
|
int ret = -IP_ERRNO_EAFNOSUPPORT;
|
|
Ip_u16 vr;
|
|
Ipnet_netif *netif;
|
|
Ipnet_neigh *neigh;
|
|
|
|
/*
|
|
* Destination address required
|
|
*/
|
|
if (rta[IP_NDA_DST-1] == IP_NULL)
|
|
return -IP_ERRNO_EINVAL;
|
|
|
|
if (mem->vr == IPCOM_VR_ANY)
|
|
return -IP_ERRNO_EINVAL;
|
|
|
|
vr = ipnet_rtnetlink_vr(rta[IP_NDA_VR - 1], mem->vr);
|
|
|
|
netif = ipnet_if_indextonetif(vr, (Ip_u32) ndm->ndm_ifindex);
|
|
if (netif == IP_NULL)
|
|
/*
|
|
* No network interface with the specified index
|
|
*/
|
|
return -IP_ERRNO_ENXIO;
|
|
|
|
neigh = ipnet_neigh_get(family,
|
|
IP_RTA_DATA(rta[IP_NDA_DST-1]),
|
|
netif,
|
|
IPNET_NEIGH_NO_CREATE);
|
|
if (IP_NULL == neigh)
|
|
return -IP_ERRNO_ENOENT;
|
|
|
|
switch (family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
ret = ipnet_rtnetlink_ip4_neigh_fill_info(mem,
|
|
neigh,
|
|
nlh->nlmsg_seq,
|
|
IP_RTM_NEWNEIGH,
|
|
0);
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
ret = ipnet_rtnetlink_ip6_neigh_fill_info(mem,
|
|
neigh,
|
|
nlh->nlmsg_seq,
|
|
IP_RTM_NEWNEIGH,
|
|
0);
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
ipnet_neigh_release(neigh);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*==========================================================================
|
|
* ipnet_rtnetlink_neigh_dump_family
|
|
*===========================================================================
|
|
* Description: Get interface info via NETLINK
|
|
* Parameters: data - Message payload.
|
|
* len - Length of message.
|
|
* cb - NETLINK callback entry
|
|
*
|
|
* Returns: 0 - Success
|
|
* <0 - Failure
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipnet_rtnetlink_neigh_dump_family(Ipnet_netlink_mem_t *mem,
|
|
struct Ip_nlmsghdr *nlmsg,
|
|
int family,
|
|
Ip_ptrdiff_t *args)
|
|
{
|
|
Ipnet_rtnetlink_neigh_family_t param;
|
|
Ip_ptrdiff_t *done = &args[0];
|
|
Ip_ptrdiff_t *s_vr = &args[1];
|
|
Ip_ptrdiff_t *s_neigh = &args[2];
|
|
Ip_ptrdiff_t *s_continue = &args[3];
|
|
|
|
param.nlmsg = nlmsg;
|
|
param.family = family;
|
|
param.mem = mem;
|
|
|
|
if (*done)
|
|
goto exit_out;
|
|
|
|
param.state.aborted = 0;
|
|
param.state.done = done;
|
|
param.state.neigh = s_neigh;
|
|
param.state.vr = s_vr;
|
|
param.state.cont = *s_continue;
|
|
|
|
ipnet_neigh_foreach(ipnet_rtnetlink_neigh_foreach,
|
|
¶m);
|
|
|
|
/*
|
|
* Are we aborted?
|
|
*/
|
|
*s_continue = param.state.aborted;
|
|
|
|
/*
|
|
* If not aborted; we're done
|
|
*/
|
|
if (!param.state.aborted)
|
|
*done = 1;
|
|
|
|
exit_out:
|
|
return (int) param.mem->used;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_rtnetlink_neigh_delneigh_family
|
|
*===========================================================================
|
|
* Description: Marks a specific neighbor as dead. Dead neighbors will
|
|
* be deleted as soon as their reference count reaches zero.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipnet_rtnetlink_neigh_delneigh_family(Ipnet_netlink_mem_t *mem,
|
|
struct Ip_nlmsghdr *nlmsg,
|
|
struct Ip_rtattr **rta,
|
|
int family)
|
|
{
|
|
Ip_u16 vr;
|
|
Ipnet_netif *netif;
|
|
struct Ip_ndmsg *ndm = IP_NLMSG_DATA(nlmsg);
|
|
Ipnet_neigh *neigh;
|
|
|
|
/*
|
|
* Destination address required
|
|
*/
|
|
if (rta[IP_NDA_DST-1] == IP_NULL)
|
|
return -IP_ERRNO_EINVAL;
|
|
|
|
if (mem->vr == IPCOM_VR_ANY)
|
|
return -IP_ERRNO_EINVAL;
|
|
|
|
vr = ipnet_rtnetlink_vr(rta[IP_NDA_VR - 1], mem->vr);
|
|
netif = ipnet_if_indextonetif(vr, (Ip_u32) ndm->ndm_ifindex);
|
|
|
|
if (netif == IP_NULL)
|
|
/*
|
|
* No network interface with the specified index
|
|
*/
|
|
return -IP_ERRNO_ENXIO;
|
|
|
|
if (netif->vr_index != vr)
|
|
/*
|
|
* Wrong VR
|
|
*/
|
|
return -IP_ERRNO_EINVAL;
|
|
|
|
/*
|
|
* Get the matching neighbor entry, create it if necessary.
|
|
*/
|
|
neigh = ipnet_neigh_get(family,
|
|
IP_RTA_DATA(rta[IP_NDA_DST-1]),
|
|
netif,
|
|
IPNET_NEIGH_NO_CREATE);
|
|
if (neigh == IP_NULL)
|
|
/*
|
|
* No matching neighbor present
|
|
*/
|
|
return -IP_ERRNO_ESRCH;
|
|
|
|
#ifdef IPNET_USE_VRRP
|
|
(void)ipnet_eth_vrrp_del_addr(neigh);
|
|
#endif
|
|
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
if (neigh->state == IPNET_ND_REACHABLE && ipnet_neigh_is_external(neigh) && ipcom_nae_is_control())
|
|
{
|
|
/*
|
|
* Go STALE; were deleting an external REACHABLE neighbour on the mgmt plane.
|
|
*/
|
|
ipnet_neigh_set_external(neigh, IP_FALSE);
|
|
if (!ipnet_neigh_nae_verify_use(neigh, IPNET_ND_DEAD))
|
|
{
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
ipcom_forwarder_fw_neigh_external_delete++;
|
|
#endif
|
|
ipnet_neigh_set_state(neigh, IPNET_ND_DEAD);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
ipcom_forwarder_fw_neigh_external_delete++;
|
|
#endif
|
|
ipnet_neigh_set_state(neigh, IPNET_ND_DEAD);
|
|
}
|
|
#else
|
|
ipnet_neigh_set_state(neigh, IPNET_ND_DEAD);
|
|
#endif
|
|
|
|
ipnet_neigh_release(neigh);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_rtnetlink_neigh_newneigh_family
|
|
*===========================================================================
|
|
* Description: Creates a new neighbor entry as a response to a
|
|
* netlink RTM_NEWNEIGH message
|
|
*
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipnet_rtnetlink_neigh_newneigh_family(Ipnet_netlink_mem_t *mem,
|
|
struct Ip_nlmsghdr *nlmsg,
|
|
struct Ip_rtattr **rta,
|
|
int family)
|
|
{
|
|
Ipnet_netif *netif;
|
|
Ipnet_neigh *neigh;
|
|
int nd_state;
|
|
int ret = 0;
|
|
Ip_u16 vr;
|
|
struct Ip_ndmsg *ndm = IP_NLMSG_DATA(nlmsg);
|
|
void *link_addr = IP_NULL;
|
|
/*
|
|
* Destination address required
|
|
*/
|
|
if (rta[IP_NDA_DST-1] == IP_NULL)
|
|
return -IP_ERRNO_EINVAL;
|
|
|
|
if (mem->vr == IPCOM_VR_ANY)
|
|
return -IP_ERRNO_EINVAL;
|
|
|
|
vr = ipnet_rtnetlink_vr(rta[IP_NDA_VR - 1], mem->vr);
|
|
|
|
netif = ipnet_if_indextonetif(vr, (Ip_u32) ndm->ndm_ifindex);
|
|
|
|
if (netif == IP_NULL)
|
|
/*
|
|
* No network interface with the specified index
|
|
*/
|
|
return -IP_ERRNO_ENXIO;
|
|
|
|
#ifndef IPCOM_FORWARDER_NAE
|
|
neigh = ipnet_neigh_get(family,
|
|
IP_RTA_DATA(rta[IP_NDA_DST-1]),
|
|
netif,
|
|
IPNET_NEIGH_NO_CREATE);
|
|
if (IP_NULL == neigh)
|
|
{
|
|
if (IP_BIT_ISFALSE(nlmsg->nlmsg_flags, IP_NLM_F_CREATE))
|
|
return -IP_ERRNO_ENOENT;
|
|
}
|
|
else
|
|
{
|
|
if (IP_BIT_ISSET(nlmsg->nlmsg_flags, IP_NLM_F_EXCL))
|
|
{
|
|
ipnet_neigh_release(neigh);
|
|
return -IP_ERRNO_EEXIST;
|
|
}
|
|
|
|
if (IP_BIT_ISSET(nlmsg->nlmsg_flags, IP_NLM_F_REPLACE) && IP_BIT_ISFALSE(ndm->ndm_flags, IP_NTF_USE))
|
|
{
|
|
ipnet_neigh_set_state(neigh, IPNET_ND_DEAD);
|
|
ipnet_neigh_release(neigh);
|
|
neigh = IP_NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the matching neighbor entry, create it if necessary.
|
|
*/
|
|
if (IP_NULL == neigh)
|
|
neigh = ipnet_neigh_get(family,
|
|
IP_RTA_DATA(rta[IP_NDA_DST-1]),
|
|
netif,
|
|
IPNET_NEIGH_CAN_CREATE);
|
|
#else
|
|
neigh = ipnet_neigh_get(family,
|
|
IP_RTA_DATA(rta[IP_NDA_DST-1]),
|
|
netif,
|
|
IPNET_NEIGH_CAN_CREATE);
|
|
#endif
|
|
|
|
if (neigh == IP_NULL)
|
|
return -IP_ERRNO_ENOMEM;
|
|
|
|
if (IP_BIT_ISFALSE(ndm->ndm_flags, IP_NTF_USE))
|
|
{
|
|
if (rta[IP_NDA_LLADDR-1])
|
|
/*
|
|
* Neighbors link layer address
|
|
*/
|
|
link_addr = IP_RTA_DATA(rta[IP_NDA_LLADDR-1]);
|
|
else if (IP_BIT_ISSET(ndm->ndm_flags, IP_NTF_PROXY))
|
|
{
|
|
/*
|
|
* Proxy ARP entry. Use Link layer address of local netif
|
|
*/
|
|
link_addr = netif->ipcom.link_addr;
|
|
ipnet_neigh_set_publ(neigh, IP_TRUE);
|
|
}
|
|
|
|
nd_state = ipnet_rtnetlink_neigh_nud2nc(ndm->ndm_state);
|
|
|
|
if (nd_state == IPNET_ND_INCOMPLETE && ipnet_neigh_is_complete(neigh))
|
|
{
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
if (!ipcom_nae_is_control() || ipnet_neigh_is_external(neigh))
|
|
{
|
|
/*
|
|
* This state will end up being incomplete - which is
|
|
* allowed under NAE, since we do not drive our own
|
|
* states
|
|
*/
|
|
ipcom_forwarder_fw_invalid_incomplete++;
|
|
ipnet_neigh_invalidate(neigh);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/*
|
|
* Don't allow the entry to be pushed back to invalid
|
|
* state.
|
|
*/
|
|
ret = -IP_ERRNO_EINVAL;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (link_addr && ipcom_memcmp(neigh->lladdr, link_addr, neigh->netif->ipcom.link_addr_size) != 0)
|
|
{
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
if (ipnet_neigh_is_complete(neigh))
|
|
{
|
|
ipcom_forwarder_fw_invalid_lladdr++;
|
|
ipnet_neigh_invalidate(neigh);
|
|
}
|
|
#endif
|
|
|
|
/* Must update */
|
|
ipnet_neigh_set_lladdr(neigh, link_addr);
|
|
}
|
|
|
|
/*
|
|
* No link layer address specified, the neighbour is incomplete,
|
|
* and we're setting it to something that indicates reachability
|
|
*/
|
|
if (IP_NULL == link_addr && !ipnet_neigh_is_complete(neigh) && nd_state != IPNET_ND_INCOMPLETE)
|
|
{
|
|
ret = -IP_ERRNO_EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
if (IP_BIT_ISSET(ndm->ndm_flags, IP_NTF_EXTERNAL))
|
|
ipnet_neigh_set_external(neigh, IP_TRUE);
|
|
|
|
if (IP_BIT_ISTRUE(ndm->ndm_state, IP_NUD_PERMANENT))
|
|
{
|
|
ipnet_neigh_set_report(neigh);
|
|
ipnet_neigh_set_perm(neigh, IP_TRUE);
|
|
ipnet_neigh_set_state(neigh, IPNET_ND_REACHABLE);
|
|
}
|
|
else
|
|
{
|
|
ipnet_neigh_set_perm(neigh, IP_FALSE);
|
|
|
|
/* Change State */
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
if (ipcom_nae_is_control() && ipnet_neigh_is_external(neigh) && nd_state == IPNET_ND_STALE && neigh->state == IPNET_ND_REACHABLE)
|
|
{
|
|
if (!ipnet_neigh_nae_verify_use(neigh, IPNET_ND_STALE))
|
|
ipnet_neigh_set_state(neigh, IPNET_ND_STALE);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (nd_state != (int) neigh->state)
|
|
{
|
|
ipnet_neigh_set_report(neigh);
|
|
ipnet_neigh_set_state(neigh, nd_state);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef IPCOM_FORWARDER_NAE
|
|
if (link_addr != IP_NULL
|
|
&& IP_BIT_ISSET(ndm->ndm_flags, IP_NTF_GRAT))
|
|
/*
|
|
* Gratuitous ARP/NDP was requested
|
|
*/
|
|
ipnet_neigh_dad(neigh);
|
|
#endif
|
|
#ifdef IPNET_USE_VRRP
|
|
(void)ipnet_eth_vrrp_add_addr(neigh);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* Have this entry be 'updated' */
|
|
switch (neigh->state)
|
|
{
|
|
default:
|
|
case IPNET_ND_REACHABLE:
|
|
case IPNET_ND_PROBE:
|
|
case IPNET_ND_INCOMPLETE:
|
|
case IPNET_ND_DELAY:
|
|
break;
|
|
|
|
case IPNET_ND_STALE:
|
|
ipnet_neigh_set_state(neigh, IPNET_ND_PROBE);
|
|
break;
|
|
|
|
case IPNET_ND_PENDING:
|
|
ipnet_neigh_set_state(neigh, IPNET_ND_INCOMPLETE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
done:
|
|
ipnet_neigh_release(neigh);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_rtnetlink_neigh_nc2nud
|
|
*===========================================================================
|
|
* Description: Converts neighbor states from IPNET to NETLINK standard.
|
|
* Parameters: neigh - a IPNET neighbor entry.
|
|
* Returns: NETLINK neighbor state
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_u16
|
|
ipnet_rtnetlink_neigh_nc2nud(Ipnet_neigh *neigh)
|
|
{
|
|
Ip_u16 flags = 0;
|
|
|
|
if (ipnet_neigh_is_perm(neigh))
|
|
{
|
|
flags |= IP_NUD_PERMANENT;
|
|
return flags;
|
|
}
|
|
|
|
switch (neigh->state)
|
|
{
|
|
case IPNET_ND_PENDING:
|
|
case IPNET_ND_INCOMPLETE:
|
|
if (neigh->remaining_ucast_probes == 0
|
|
&& neigh->remaining_mcast_probes == 0)
|
|
flags |= IP_NUD_FAILED;
|
|
else
|
|
flags |= IP_NUD_INCOMPLETE;
|
|
break;
|
|
case IPNET_ND_REACHABLE:
|
|
flags |= IP_NUD_REACHABLE;
|
|
break;
|
|
case IPNET_ND_STALE:
|
|
flags |= IP_NUD_STALE;
|
|
break;
|
|
case IPNET_ND_DELAY:
|
|
flags |= IP_NUD_DELAY;
|
|
break;
|
|
case IPNET_ND_PROBE:
|
|
flags |= IP_NUD_PROBE;
|
|
break;
|
|
default:
|
|
/*
|
|
* Set all other states to the NUD_NONE dummy state.
|
|
*/
|
|
flags = IP_NUD_NONE;
|
|
break;
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_rtnetlink_neigh_nud2nc
|
|
*===========================================================================
|
|
* Description: Converts neighbor states from NETLINK standard to IPNET
|
|
* Parameters: ndm_state - NETLINK neighbor state
|
|
* Returns: IPNET neighbor state.
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipnet_rtnetlink_neigh_nud2nc(Ip_u16 ndm_state)
|
|
{
|
|
if (IP_BIT_ISSET(ndm_state, IP_NUD_PERMANENT|IP_NUD_REACHABLE))
|
|
return IPNET_ND_REACHABLE;
|
|
if (IP_BIT_ISSET(ndm_state, IP_NUD_STALE))
|
|
return IPNET_ND_STALE;
|
|
if (IP_BIT_ISSET(ndm_state, IP_NUD_PROBE))
|
|
return IPNET_ND_PROBE;
|
|
if (IP_BIT_ISSET(ndm_state, IP_NUD_DELAY))
|
|
return IPNET_ND_DELAY;
|
|
return IPNET_ND_INCOMPLETE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_rtnetlink_neigh_change
|
|
*===========================================================================
|
|
* Description: Called for any kind of change in the neighbor state machine.
|
|
* There is no need for a specific neigh_add() function
|
|
* anymore. This function is called at creation time with
|
|
* state set to INCOMPLETE
|
|
* Parameters: neigh - a neighbor entry
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipnet_rtnetlink_neigh_change(Ipnet_neigh *neigh)
|
|
{
|
|
ipnet_rtnetlink_neigh_event(neigh, IP_RTM_NEWNEIGH);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_rtnetlink_neigh_del
|
|
*===========================================================================
|
|
* Description: Called when a neighbor is about to be removed from the stack.
|
|
* Parameters: neigh - neighbor entry that is about to be deleted.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipnet_rtnetlink_neigh_del(Ipnet_neigh *neigh)
|
|
{
|
|
ipnet_rtnetlink_neigh_event(neigh, IP_RTM_DELNEIGH);
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
#endif /* IPNET_USE_NETLINK */
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|