/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * * Implementation of IP peer information. */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipnet_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #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 **************************************************************************** */