Files
m241Neelima/components/ip_net2-6.9/ipnet2/src/ipnet_peer_info.c
T
2026-07-30 11:19:44 +05:30

750 lines
22 KiB
C

/*
******************************************************************************
* INTERPEAK SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_peer_info.c,v $ $Revision: 1.5.10.8 $
* $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_peer_info.c,v $
* $Author: jren $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Kenneth Jonsson <kjonsson@windriver.com>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*
* Implementation of IP peer information.
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#include "ipnet_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_clib.h>
#include <ipcom_cstyle.h>
#include <ipcom_list.h>
#include <ipcom_slab.h>
#include <ipcom_syslog.h>
#include <ipcom_type.h>
#include "ipnet_h.h"
#include "ipnet_peer_info.h"
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/*
* Number of buckets in the "ipnet_peer_info_buckets" list
*/
#define IPNET_PEER_INFO_BUCKET_CNT 64
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
* Array of list heads. This pointer points to shared memory if the
* system is built as a NAE
*/
static Ipcom_list *ipnet_peer_info_buckets;
#ifndef IPCOM_FORWARDER_NAE
/*
* Slab cache for Ipnet_peer_info objects.
*/
static Ipcom_memory_cache *ipnet_peer_info_slab;
#endif
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_peer_info_key_hash
*===========================================================================
* Description: Returns a hash value for the lookup key.
* Parameters: vr - virtual router index.
* domain - address domain, IP_AF_INET or IP_AF_INET6
* addr - pointer to the IP address
* Returns:
*
*/
IP_STATIC unsigned
ipnet_peer_info_key_hash(Ip_u16 vr, int domain, const void *addr)
{
return ipcom_hash_update(addr,
(int)ipnet_ipaddrlen(domain),
(vr << 16) + domain) % IPNET_PEER_INFO_BUCKET_CNT;
}
/*
*===========================================================================
* ipnet_peer_info_cmp_hash
*===========================================================================
* Description: Returns if the peer information object matches the
* search key.
* Parameters: vr - virtual router index.
* domain - address domain, IP_AF_INET or IP_AF_INET6
* addr - address of the peer
* peer_info - a peer information object
* Returns: IP_TRUE if the object matches the key
*
*/
IP_STATIC Ip_bool
ipnet_peer_info_cmp_hash(Ip_u16 vr,
int domain,
const void *addr,
const Ipnet_peer_info *peer_info)
{
return peer_info->vr == vr
&& peer_info->domain == domain
&& 0 == ipcom_memcmp(&peer_info->addr,
addr,
ipnet_ipaddrlen(domain));
}
/*
*===========================================================================
* ipnet_peer_info_malloc
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ipnet_peer_info *
ipnet_peer_info_malloc(void)
{
#ifdef IPCOM_FORWARDER_NAE
return ipcom_shm_malloc(sizeof(Ipnet_peer_info));
#else
return ipcom_slab_alloc(ipnet_peer_info_slab);
#endif
}
/*
*===========================================================================
* ipnet_peer_info_free
*===========================================================================
* Description:
* Parameters: peer_info -
* Returns:
*
*/
IP_STATIC void
ipnet_peer_info_free(Ipnet_peer_info *peer_info)
{
if (peer_info)
{
#ifdef IPCOM_FORWARDER_NAE
ipcom_shm_free(peer_info);
#else
ipcom_slab_free(ipnet_peer_info_slab, peer_info);
#endif
}
}
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
#if defined(IPCOM_FORWARDER_NAE)
#define IPNET_PEER_INFO_TIMEOUT_INFINITY (~(Ip_s64)0)
IP_GLOBAL void
ipnet_peer_info_timeout_schedule(Ipnet_peer_info *peer_info, Ip_s64 msec)
{
Ipcom_list *head = peer_info->list.head;
Ipnet_peer_info *p;
msec += ipnet->msec_now;
if (IP_UNLIKELY(IPNET_PEER_INFO_TIMEOUT_INFINITY == msec))
msec--;
peer_info->msec = msec;
/* Sort it into the list at its appropriate place */
ipcom_list_remove(&peer_info->list);
p = IPCOM_LIST_FIRST(head);
for (;;)
{
if (p == IP_NULL)
{
ipcom_list_insert_last(head, &peer_info->list);
break;
}
else if (peer_info->msec <= p->msec)
{
ipcom_list_insert_before(&p->list, &peer_info->list);
break;
}
p = IPCOM_LIST_NEXT(&p->list);
}
}
IP_GLOBAL void
ipnet_peer_info_timeout_cancel(Ipnet_peer_info *peer_info)
{
if (IPNET_PEER_INFO_TIMEOUT_INFINITY != peer_info->msec)
{
Ipcom_list *head = peer_info->list.head;
peer_info->msec = IPNET_PEER_INFO_TIMEOUT_INFINITY;
/* Move it last in the list; only active timer must be at the front */
ipcom_list_remove(&peer_info->list);
ipcom_list_insert_last(head, &peer_info->list);
}
}
IP_GLOBAL void
ipnet_peer_info_timeout_process(void)
{
Ipcom_list *head;
Ipnet_peer_info *peer_info;
unsigned idx;
ipcom_enter_critical_region();
for (idx = 0; idx < IPNET_PEER_INFO_BUCKET_CNT; idx++)
{
head = &ipnet_peer_info_buckets[idx];
for (;;)
{
peer_info = IPCOM_LIST_FIRST(head);
if (IP_NULL != peer_info
&& peer_info->msec != IPNET_PEER_INFO_TIMEOUT_INFINITY
&& IPCOM_IS_GEQ64(ipnet->msec_now, peer_info->msec))
{
Ip_s64 msec = peer_info->msec;
Ipcom_pkt *pkt = peer_info->reassembly_list;
Ip_u32 tag = IPCOM_PKT_GET_TAG(pkt);
/* Do not loose the peer info - We have a gap in the critsect - tag- critsect switch */
++peer_info->refcnt;
/* Exit the critical region */
ipcom_exit_critical_region();
/* Go atomic on the peer info tag */
ipcom_nae_switch_to_atomic(tag);
/* Re-enter the critical region */
ipcom_enter_critical_region();
/*
* Validate that we didnt loose the timeout during locking -
* 1 - We must have a reassembly list, with the same head
* 2 - We must have the same timeout tag
* 3 - We must have the same timeout value
* -
* If either fail; something was modified under us - Abort
* and retry. The peer_info structure is referenced, so we
* are safe that its still a valid pointer.
*/
if (peer_info->reassembly_list == pkt
&& tag == IPCOM_PKT_GET_TAG(pkt)
&& msec == peer_info->msec)
{
/* Unhook the reassembly list that cause the timeout */
peer_info->reassembly_list = pkt->next;
/* Cancel this timer */
ipnet_peer_info_timeout_cancel(peer_info);
/* Exit the critical region */
ipcom_exit_critical_region();
#ifdef IPCOM_USE_INET
/* Invoke */
if (peer_info->domain == IP_AF_INET)
ipnet_ip4_fragment_timeout_peer(peer_info, pkt);
#endif
#ifdef IPCOM_USE_INET6
/* Invoke */
if (peer_info->domain == IP_AF_INET6)
ipnet_ip6_fragment_timeout_peer(peer_info, pkt);
#endif
}
else
{
/* Exit the critical region */
ipcom_exit_critical_region();
}
/* Go ordered on the peer info tag */
ipcom_nae_switch_to_ordered(tag);
/* Kill the timer manager reference */
ipnet_peer_info_release(peer_info);
/* Re-acquire the lock */
ipcom_enter_critical_region();
}
else
{
/* Done with this bucket */
break;
}
}
}
/* Exit the critical region */
ipcom_exit_critical_region();
}
#else
#ifdef IPNET_DEBUG
IP_STATIC int
ipnet_peer_info_fragment_tmo_to_string(Ipcom_pkt *frag_head, char *buf, int buf_len)
{
Ipnet_peer_info *peer_info;
peer_info = ipnet_frag_to_peer_info(frag_head);
#ifdef IPCOM_USE_INET
if (peer_info->domain == IP_AF_INET)
{
Ipnet_pkt_ip *iphdr;
char dst[IP_INET_ADDRSTRLEN];
char src[IP_INET_ADDRSTRLEN];
iphdr = ipcom_pkt_get_iphdr(frag_head);
return ipcom_snprintf(buf,
buf_len,
"IPv4 fragment id %d src %s dst %s",
ip_ntohs(iphdr->id),
ipcom_inet_ntop(IP_AF_INET, iphdr->src, src, sizeof(src)),
ipcom_inet_ntop(IP_AF_INET, iphdr->dst, dst, sizeof(dst)));
}
#endif
#ifdef IPCOM_USE_INET6
if (peer_info->domain == IP_AF_INET6)
{
Ip_pkt_ip6_frag *frag_hdr;
Ipnet_pkt_ip6 *ip_hdr;
char dst[IP_INET6_ADDRSTRLEN];
char src[IP_INET6_ADDRSTRLEN];
ip_hdr = ipcom_pkt_get_iphdr(frag_head);
frag_hdr = ipcom_pkt_get_data(frag_head, -(int)IP_IP6F_SIZE);
return ipcom_snprintf(buf,
buf_len,
"IPv6 fragment id %u src %s dst %s",
(unsigned) ip_ntohl(frag_hdr->ip6f_ident),
ipcom_inet_ntop(IP_AF_INET6, &ip_hdr->src, src, sizeof(src)),
ipcom_inet_ntop(IP_AF_INET6, &ip_hdr->dst, dst, sizeof(dst)));
}
#endif
return ipcom_snprintf(buf,
buf_len,
"IPvX fragment domain unknown");
}
#endif /* IPNET_DEBUG */
IP_STATIC void
ipnet_peer_info_fragment_timeout(Ipcom_pkt *frag_head)
{
Ipnet_peer_info *peer_info;
/*
* Unhook the fragment list from the reassembly list
*
* THIS FUNCTION IS ONLY INVOKED IN NON-NAE MODE - DO NOT NEED
* TO PROTECT THE REASSEMBLY LIST
*/
peer_info = ipnet_frag_to_peer_info(frag_head);
ip_assert(peer_info->reassembly_list == frag_head);
peer_info->reassembly_list = frag_head->next;
#ifdef IPCOM_USE_INET
/* Invoke */
if (peer_info->domain == IP_AF_INET)
ipnet_ip4_fragment_timeout_peer(peer_info, frag_head);
#endif
#ifdef IPCOM_USE_INET6
/* Invoke */
if (peer_info->domain == IP_AF_INET6)
ipnet_ip6_fragment_timeout_peer(peer_info, frag_head);
#endif
}
IP_GLOBAL void
ipnet_peer_info_timeout_schedule(Ipnet_peer_info *peer_info, Ip_s64 msec)
{
/* Clear anything thats pending */
ipnet_peer_info_timeout_cancel(peer_info);
/* Schedule a new one */
(void) ipnet_timeout_schedule(msec,
(Ipnet_timeout_handler) ipnet_peer_info_fragment_timeout,
peer_info->reassembly_list,
&peer_info->ptmo);
}
IP_GLOBAL void
ipnet_peer_info_timeout_cancel(Ipnet_peer_info *peer_info)
{
ipnet_timeout_cancel(peer_info->ptmo);
}
#endif
/*
*===========================================================================
* ipnet_peer_info_get
*===========================================================================
* Description: Returns a peer information entry matching the domain
* and address. The entry will be created if it does not
* already exist.
* Parameters: vr - virtual router index
* domain - IP_AF_INET or IP_AF_INET6
* addr - pointer to an IP address in domain 'domain'
* Returns:
*
*/
IP_GLOBAL Ipnet_peer_info *
ipnet_peer_info_get(Ip_u16 vr, int domain, const void *addr)
{
Ipcom_list *head;
Ipnet_peer_info *peer_info;
Ipnet_peer_info *new_peer_info = IP_NULL;
unsigned idx;
int i;
idx = ipnet_peer_info_key_hash(vr, domain, addr);
recheck:
ipcom_enter_critical_region();
head = &ipnet_peer_info_buckets[idx];
/*
* Check if the entry already exist
*/
for (peer_info = IPCOM_LIST_FIRST(head);
peer_info != IP_NULL;
peer_info = IPCOM_LIST_NEXT(&peer_info->list))
{
if (ipnet_peer_info_cmp_hash(vr, domain, addr, peer_info))
{
++peer_info->refcnt;
ipcom_exit_critical_region();
ipnet_peer_info_free(new_peer_info);
return peer_info;
}
}
if (new_peer_info == IP_NULL)
{
/*
* Must allocate memory outside of the critical region to
* avoid deadlock. Must retry to find the entry since another
* NAE might have added while this NAE is running outside of
* the critical region.
*/
ipcom_exit_critical_region();
new_peer_info = ipnet_peer_info_malloc();
if (new_peer_info == IP_NULL)
return IP_NULL;
goto recheck;
}
/*
* No peer information found, create a new entry
*/
peer_info = new_peer_info;
peer_info->refcnt = 1;
peer_info->vr = vr;
peer_info->domain = domain;
peer_info->reassembly_list = IP_NULL;
peer_info->flags = 0;
ipcom_memcpy(&peer_info->addr, addr, ipnet_ipaddrlen(domain));
ipcom_atomic_set(&peer_info->id, ipcom_rand());
#ifdef IPCOM_USE_INET
if (ipnet->conf.inet.random_ip_id)
#endif
{
for (i = 0; i < IPNET_PEER_INFO_ID_CNT; i++)
ipcom_atomic_set(&peer_info->ids[i], ipcom_rand());
}
ipcom_list_insert_last(head, &peer_info->list);
#if defined(IPCOM_FORWARDER_NAE)
peer_info->msec = IPNET_PEER_INFO_TIMEOUT_INFINITY;
#else
peer_info->ptmo = IP_NULL;
#endif
ipcom_exit_critical_region();
return peer_info;
}
/*
*===========================================================================
* ipnet_peer_info_addref
*===========================================================================
* Description: Increases the reference count on a peer information entry
* Parameters: peer_info - a peer information entry
* Returns:
*
*/
IP_GLOBAL void
ipnet_peer_info_addref(Ipnet_peer_info *peer_info)
{
ipcom_enter_critical_region();
ip_assert(peer_info->refcnt > 0);
++peer_info->refcnt;
ipcom_exit_critical_region();
}
/*
*===========================================================================
* ipnet_peer_info_release
*===========================================================================
* Description: Releases a reference to a peer information entry
* Parameters: peer_info - a peer information entry.
* Returns:
*
*/
IP_GLOBAL void
ipnet_peer_info_release(Ipnet_peer_info *peer_info)
{
ipcom_enter_critical_region();
ip_assert(peer_info->refcnt > 0);
if (--peer_info->refcnt > 0)
ipcom_exit_critical_region();
else
{
Ipcom_pkt *pkt;
Ipcom_pkt *reassembly_list = peer_info->reassembly_list;
ipcom_list_remove(&peer_info->list);
ipcom_exit_critical_region();
/*
* Free all fragments of partially reassembled packets
*/
while (IP_NULL != (pkt = reassembly_list))
{
reassembly_list = pkt->next;
ipcom_pkt_free(pkt);
}
ipnet_peer_info_free(peer_info);
}
}
/*
*===========================================================================
* ipnet_peer_info_init
*===========================================================================
* Description: Initializes the peer information database.
* Parameters:
* Returns: 0 = success
* -IP_ERRNO_ENOMEM = out of memory
*
*/
IP_GLOBAL int
ipnet_peer_info_init(void)
{
unsigned i;
#ifdef IPCOM_FORWARDER_NAE
Ipnet_shm *shm = ipcom_shm_component(IPCOM_LOG_IPNET, sizeof(*shm));
if (!shm->is_initialized)
{
shm->peer_info_buckets
= ipcom_shm_reserve(IPNET_PEER_INFO_BUCKET_CNT
* sizeof(Ipcom_list));
for (i = 0; i < IPNET_PEER_INFO_BUCKET_CNT; i++)
ipcom_list_init(&shm->peer_info_buckets[i]);
}
/*
* else: another NAE has already initialized the shared memory
* region. This NAE should just point into the already initialized
* area.
*/
ipnet_peer_info_buckets = shm->peer_info_buckets;
#else
ipnet_peer_info_buckets = ipcom_calloc(IPNET_PEER_INFO_BUCKET_CNT,
sizeof(Ipcom_list));
ipnet_peer_info_slab = ipcom_slab_new("IPNET peer information",
0,
sizeof(Ipnet_peer_info),
0,
IP_NULL,
IP_NULL,
ipnet->memory_pool);
if (ipnet_peer_info_buckets == IP_NULL
|| ipnet_peer_info_slab == IP_NULL)
return -IP_ERRNO_ENOMEM;
for (i = 0; i < IPNET_PEER_INFO_BUCKET_CNT; i++)
ipcom_list_init(&ipnet_peer_info_buckets[i]);
#ifdef IPNET_DEBUG
ipnet_timeout_to_string((Ipnet_timeout_handler) ipnet_peer_info_fragment_timeout,
(Ipnet_timeout_to_string_f) ipnet_peer_info_fragment_tmo_to_string);
#endif /* !IPCOM_FORWARDER_NAE */
#endif
return 0;
}
#ifdef IPNET_USE_SHOW
/*
*===========================================================================
* ipnet_peer_info_show
*===========================================================================
* Description: Dumps a single peer info entry to the console.
* Parameters: peer_info - a peer info entry.
* unused - unused.
* Returns:
*
*/
IP_STATIC void
ipnet_peer_info_show(Ipnet_peer_info *peer_info, void *unused)
{
char addr_str[IP_INET6_ADDRSTRLEN];
IPCOM_UNUSED_ARG(unused);
ipcom_printf("%-6u %-30s %-10d %-4u\n",
peer_info->vr,
ipcom_inet_ntop(peer_info->domain,
&peer_info->addr,
addr_str,
sizeof(addr_str)),
(unsigned) ((peer_info->domain == IP_AF_INET
? 0xffff
: ~0)
& ipcom_atomic_get(&peer_info->id)),
peer_info->refcnt);
}
/*
*===========================================================================
* peerInfoShow
*===========================================================================
* Description: Dumps all peer info entries to the console
* Parameters:
* Returns: Number of entries dumped.
*
*/
int peerInfoShow(void);
int peerInfoShow(void)
{
unsigned i;
int cnt = 0;
Ipcom_list *node;
Ipnet_peer_info *peer_info;
ipcom_printf("%-6s %-30s %-10s %-4s\n", "VR", "Address", "ID", "RefCnt");
for (i = 0; i < IPNET_PEER_INFO_BUCKET_CNT; i++)
for (node = ipcom_list_first(&ipnet_peer_info_buckets[i]);
node != IP_NULL;
node = ipcom_list_next(node))
{
peer_info = ipcom_list_entry(node, Ipnet_peer_info, list);
ipnet_peer_info_show(peer_info, IP_NULL);
cnt++;
}
return cnt;
}
#endif /* IPNET_USE_SHOW */
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/