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

2059 lines
64 KiB
C

/*
******************************************************************************
* INTERPEAK SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_dst_cache.c,v $ $Revision: 1.16.2.28 $
* $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_dst_cache.c,v $
* $Author: jren $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Kenneth Jonsson <kjonsson@windriver.com>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*
* Implementation of the destination cache for IPNET. The hash table
* and all the elements in it stored in memory only visable to the
* local CPU core when the system is running as a NAE. Hence, there is
* no requirement for locking.
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#include "ipnet_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_clib.h>
#include <ipcom_cstyle.h>
#include <ipcom_hash.h>
#include <ipcom_pkt.h>
#include <ipcom_sock.h>
#include <ipcom_type.h>
#include <ipcom_syslog.h>
#include "ipnet_dst_cache.h"
#include "ipnet_h.h"
#include "ipnet_neigh.h"
#include "ipnet_peer_info.h"
#include "ipnet_route.h"
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
#define IPNET_FLOW_SPEC_ZERO(fs) (fs = ipnet_flow_spec_zero)
#ifdef IPCOM_USE_INET6
#define IPNET_FLOW_SPEC_DOMAIN(fs) \
(IP_BIT_ISSET((fs)->flags, IPNET_FSF_IPV6) ? IP_AF_INET6 : IP_AF_INET)
#else
#define IPNET_FLOW_SPEC_DOMAIN(fs) IP_AF_INET
#endif
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/*
* Context data when travering all destination cache entries in order
* to locate the true path MTU to a specific node.
*/
struct Ipnet_dst_cache_pmtu {
int domain;
union Ip_in_addr_union to;
Ip_u32 path_mtu;
};
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
IP_STATIC void
ipnet_dst_cache_delete(Ipnet_dst_cache *dst);
IP_STATIC int
ipnet_dst_cache_check_limit(void);
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
* Hash table where all the destination cache entries are stored.
*/
Ipcom_hash *ipnet_dst_cache;
/*
* Memory cache where all the destination cache entries are allocated
* from.
*/
static Ipcom_memory_cache *ipnet_dst_slab;
/*
* Destination cache entry that will discard any packets transmitted
* or received using that flow.
*/
static Ipnet_dst_cache ipnet_dst_cache_blackhole_singleton;
/*
* A destination cache entry with all zeros. It is used to quickly
* zero out all fields when creating a new flow specification.
*/
static const Ipnet_flow_spec ipnet_flow_spec_zero;
static Ipcom_hash *ipnet_pmtus;
static Ipcom_memory_cache *ipnet_pmtu_slab;
unsigned long ipnet_pmtu_limit = 0;
#define INET_DEFAULT_PATHMTU_LIVE_TIME 600
unsigned long ipnet_pmtu_live_time = 0;
union Ipnet_pmtu_addr_union
{
struct Ip_in_addr in;
#ifdef IPCOM_USE_INET6
struct Ip_in6_addr in6;
#endif
};
typedef struct Ipnet_pmtu_lookup_key_struct
{
/* The ID of the virtual router this
interface is assigned to */
Ip_u16 vr_index;
/*
* IP_AF_INET or IP_AF_INET6
*/
int domain;
/*
* IP address of the peer. Points to a struct Ip_in_addr if
* domain==IP_AF_INET and to a struct Ip_in6_addr if
* domain==IP_AF_INET6.
*/
union Ipnet_pmtu_addr_union addr;
} Ipnet_pmtu_lookup_key;
typedef struct Ipnet_pmtu_struct
{
Ipnet_pmtu_lookup_key key; /* The key for this entry */
Ip_u32 path_mtu; /* The path-MTU of this flow */
Ipnet_timeout *tmo; /* Timer for this entry */
} Ipnet_pmtu;
IP_STATIC unsigned ipnet_pmtu_key_hash(Ipnet_pmtu_lookup_key *key);
IP_STATIC unsigned ipnet_pmtu_obj_hash(Ipnet_pmtu *pmtu);
IP_STATIC unsigned ipnet_pmtu_cmp_hash(Ipnet_pmtu *pmtu, Ipnet_pmtu_lookup_key *key);
IP_STATIC void ipnet_pmtu_update(Ipnet_pmtu *pmtu, Ip_u32 new_path_mtu);
IP_STATIC void ipnet_pmtu_timeout(Ipnet_pmtu *pmtu);
IP_STATIC void ipnet_pmtu_delete(Ipnet_pmtu *pmtu);
IP_GLOBAL Ipnet_pmtu *ipnet_pmtu_get(int vr, int domain, IP_CONST void *addr);
IP_STATIC Ipnet_pmtu *ipnet_pmtu_new(int vr, int domain, IP_CONST void *addr, int pmtu_value);
#ifdef IPCOM_USE_INET
IP_STATIC void ipnet_pmtu_proc_inet(Ipcom_pkt *pkt);
#endif
#ifdef IPCOM_USE_INET6
IP_STATIC void ipnet_pmtu_proc_inet6(Ipcom_pkt *pkt);
#endif
typedef void (*Ipnet_pmtu_foreach_func)(struct Ipnet_pmtu_struct *pmtu,
void *user_data);
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_dst_cache_hash_key
*===========================================================================
* Description: Returns a hash value for a flow specification.
* Parameters: flow_spec - a flow specification.
* Returns:
*
*/
IP_STATIC unsigned
ipnet_dst_cache_hash_key(const Ipnet_flow_spec *flow_spec)
{
unsigned h;
int domain = IPNET_FLOW_SPEC_DOMAIN(flow_spec);
h = (((Ip_u32)flow_spec->ds << 22)
+ ((Ip_u32)flow_spec->vr << 13)
+ (flow_spec->egress_ifindex << 8)
+ flow_spec->ingress_ifindex
+ (flow_spec->zone_id << 15));
#ifdef IPCOM_USE_INET
if (domain == IP_AF_INET)
h = ipcom_hash_update(&flow_spec->to.in,
sizeof(flow_spec->to.in),
h ^ flow_spec->from.in.s_addr);
#endif
#ifdef IPCOM_USE_INET6
if (domain == IP_AF_INET6)
h = ipcom_hash_update(&flow_spec->to.in6,
sizeof(flow_spec->from.in6) * 2,
h);
#endif
return h;
}
/*
*===========================================================================
* ipnet_dst_cache_hash_obj
*===========================================================================
* Description: Returns a hash value for the destination cache entry
* Parameters: dst - a destination cache entry
* Returns:
*
*/
IP_STATIC unsigned
ipnet_dst_cache_hash_obj(const Ipnet_dst_cache *dst)
{
return ipnet_dst_cache_hash_key(&dst->flow_spec);
}
/*
*===========================================================================
* ipnet_dst_cache_hash_cmp
*===========================================================================
* Description: Checks if a destination cache matches a specific flow
* specification
* Parameters: dst - a destination cache entry
* flow_spec - a flow specification
* Returns: IP_TRUE if the flow specification matches the
* destination cache entry
* IP_FALSE otherwise
*
*/
IP_STATIC Ip_bool
ipnet_dst_cache_hash_cmp(const Ipnet_dst_cache *dst,
const Ipnet_flow_spec *flow_spec)
{
Ip_size_t alen;
/*
* memcmp() over the Ipnet_flow_spec structure would work
* here. However, that function is implemented as a slow
* byte-to-byte comparison on serveral platforms.
*/
if (dst->flow_spec.flags != flow_spec->flags
|| dst->flow_spec.ds != flow_spec->ds
|| dst->flow_spec.vr != flow_spec->vr
|| dst->flow_spec.egress_ifindex != flow_spec->egress_ifindex
|| dst->flow_spec.ingress_ifindex != flow_spec->ingress_ifindex
|| dst->flow_spec.zone_id != flow_spec->zone_id
#ifdef IPMIP
|| (dst->flow_spec.mip.mask.u64[0] != 0
&& dst->flow_spec.mip.key.u64[0] != (flow_spec->mip.key.u64[0] & dst->flow_spec.mip.mask.u64[0]))
|| (dst->flow_spec.mip.mask.u64[1] != 0
&& dst->flow_spec.mip.key.u64[1] != (flow_spec->mip.key.u64[1] & dst->flow_spec.mip.mask.u64[1]))
|| (dst->flow_spec.mip.mask.u64[2] != 0
&& dst->flow_spec.mip.key.u64[2] != (flow_spec->mip.key.u64[2] & dst->flow_spec.mip.mask.u64[2]))
#endif
)
return IP_FALSE;
alen = ipnet_ipaddrlen(IPNET_FLOW_SPEC_DOMAIN(flow_spec));
return ipcom_memcmp(&dst->flow_spec.to, &flow_spec->to, alen) == 0
&& ipcom_memcmp(&dst->flow_spec.from, &flow_spec->from, alen) == 0;
}
/*
*===========================================================================
* ipnet_dst_cache_flush_cb
*===========================================================================
* Description: Deletes the flow cache entry if it matches the flush
* filter.
* Parameters: dst - a destination cache entry
* filter - only entries matching this filter will be
* deleted
* Returns:
*
*/
IP_STATIC void
ipnet_dst_cache_flush_cb(Ipnet_dst_cache *dst,
const Ipnet_flow_spec *filter)
{
if ((filter->vr == IPCOM_VR_ANY || filter->vr == dst->flow_spec.vr)
&& (IPNET_FLOW_SPEC_DOMAIN(filter) == IPNET_FLOW_SPEC_DOMAIN(&dst->flow_spec)))
{
/*
* Delete this entry.
*/
ipnet_dst_cache_delete(dst);
}
/* else: keep this entry since it didn't match the flush filter */
}
/*
*===========================================================================
* ipnet_dst_cache_flush_where_neigh_is_cb
*===========================================================================
* Description: Visitor callback for each destination cache entry,
* will delete all destination cache entries using
* 'neigh' as their next-hop.
* Parameters: dst - a destination cache entry
* neigh - neighbor entry to match
* Returns:
*
*/
IP_STATIC void
ipnet_dst_cache_flush_where_neigh_is_cb(Ipnet_dst_cache *dst,
const Ipnet_neigh *neigh)
{
if (dst->neigh == neigh)
/*
* Delete this entry.
*/
ipnet_dst_cache_delete(dst);
}
/*
*===========================================================================
* ipnet_dst_cache_reset_timer
*===========================================================================
* Description: Starts a timer on a destination cache entry
* Parameters: dst - a destination cache entry.
* transient - IP_TRUE if this entry should be considered
* short lived for now
* Returns:
*
*/
IP_STATIC void
ipnet_dst_cache_reset_timer(Ipnet_dst_cache *dst, Ip_bool transient)
{
Ip_s64 msec;
Ipnet_netif *netif = dst->neigh->netif;
#if defined(IPCOM_USE_INET) && defined(IPCOM_USE_INET6)
int domain = IPNET_FLOW_SPEC_DOMAIN(&dst->flow_spec);
#endif
int transient_cnt = IPNET_NETIF_CONF(domain, netif, dst_cache_transient_cnt);
/*
* The 'dst_cache_to_live' parameter specified in seconds
*/
if (transient && transient_cnt > 0)
{
dst->transient_cnt = transient_cnt;
msec = IPNET_NETIF_CONF(domain, netif, dst_cache_transient_to_live_msec);
}
else
{
dst->transient_cnt = 0;
msec = IPNET_NETIF_CONF(domain, netif, dst_cache_to_live) * 1000;
}
ipnet_dst_cache_set_tx_handler(dst, dst->real_tx);
ipnet_dst_cache_set_rx_handler(dst, dst->real_rx);
ipnet_timeout_cancel(dst->tmo);
ipnet_timeout_schedule(msec,
(Ipnet_timeout_handler) ipnet_dst_cache_delete,
dst,
&dst->tmo);
}
/*
*===========================================================================
* ipnet_dst_cache_blackhole_action
*===========================================================================
* Description: Discards all packets.
* Parameters: dst - not used.
* pkt - packet to discard
* Returns: 0 = success
*
*/
IP_STATIC int
ipnet_dst_cache_blackhole_action(Ipnet_dst_cache *dst, Ipcom_pkt *pkt)
{
IPCOM_UNUSED_ARG(dst);
ipcom_pkt_free(pkt);
return 0;
}
/*
*===========================================================================
* ipnet_dst_cache_delete
*===========================================================================
* Description: Timeout handler for a destination cache entry.
* Parameters: dst - the destination cache entry that had its timer
* expired.
* Returns:
*
*/
IP_STATIC void
ipnet_dst_cache_delete(Ipnet_dst_cache *dst)
{
/*
* This entry has expired, lets delete it. It is not allowed
* to keep references to a destination cache entry between
* processing of two packets. The timeout handler is run on
* the same CPU core as packet processing is run, so it is
* safe to free this entry directly here.
*/
(void)ipcom_hash_remove(ipnet_dst_cache, dst);
#ifdef IPMIP
if (IP_UNLIKELY(dst->mip.flow_handle != IP_NULL))
(*dst->mip.flow_handle) (IP_FALSE, dst);
#endif
if (dst->peer_info)
ipnet_peer_info_release(dst->peer_info);
ipnet_neigh_release(dst->neigh);
ipnet_timeout_cancel(dst->tmo);
ipcom_slab_free(ipnet_dst_slab, dst);
}
/*
*===========================================================================
* ipnet_dst_cache_check_limit
*===========================================================================
* Description: check the dst cache num reach the limit
* Parameters:
* Returns: IP_TRUE check pass
IP_FALSE check failed
*
*/
IP_STATIC int
ipnet_dst_cache_check_limit(void)
{
if(0 == ipnet_dst_cache_number_limit)
return IP_TRUE;
if(ipnet_dst_cache_number_limit > ipnet_dst_cache->elem)
return IP_TRUE;
return IP_FALSE;
}
/*
*===========================================================================
* ipnet_dst_cache_tmo_to_string
*===========================================================================
* Description: Creates a string representation of a destination cache entry
* Parameters: dst - a destination cache entry
* buf - buffer where the string representation should be
* stored
* buf_len - lenth of 'buf'
* Returns: Number of characters written to 'buf'
*
*/
#if defined(IPNET_DEBUG)
IP_STATIC int
ipnet_dst_cache_tmo_to_string(const Ipnet_dst_cache *dst, char *buf, Ip_size_t buf_len)
{
char from[IP_INET6_ADDRSTRLEN];
char to[IP_INET6_ADDRSTRLEN];
int domain = IPNET_FLOW_SPEC_DOMAIN(&dst->flow_spec);
return ipcom_snprintf(buf,
buf_len,
"Dst cache %c%16s -> %-16s VR=%d, DS=%d, zone_id=%d, egress if=%u, ingress if=%u",
IP_BIT_ISSET(dst->flow_spec.flags, IPNET_FSF_DONTROUTE) ? '*' : ' ',
ipcom_inet_ntop(domain,
&dst->flow_spec.from,
from,
sizeof(from)),
ipcom_inet_ntop(domain,
&dst->flow_spec.to,
to,
sizeof(to)),
dst->flow_spec.vr,
dst->flow_spec.ds,
dst->flow_spec.zone_id,
dst->flow_spec.egress_ifindex,
dst->flow_spec.ingress_ifindex);
}
#endif /* IPNET_DEBUG */
/*
*===========================================================================
* ipnet_dst_cache_ip4_dont_route_netif
*===========================================================================
* Description: Tries to find the egress interface to use for a don't route
* transmission.
* Parameters: flow_spec - flow specification.
* Returns:
*
*/
#ifdef IPCOM_USE_INET
IP_STATIC Ipnet_netif *
ipnet_dst_cache_ip4_dont_route_netif(const Ipnet_flow_spec *flow_spec)
{
Ipnet_netif *netif;
Ipnet_netif *best_netif = IP_NULL;
int best_prefixlen = 0;
unsigned i;
/*
* IPNET_NETIF_FOR_EACH_OR_SPECIFIC_ON_VR sets netif to NULL after the for loop,
* this is a performance optimized defination
*/
/* coverity[result_independent_of_operands] */
IPNET_NETIF_FOR_EACH_OR_SPECIFIC_ON_VR(netif,
flow_spec->vr,
flow_spec->egress_ifindex,
i)
{
Ipnet_ip4_addr_entry *addr;
if (IP_BIT_ISFALSE(netif->ipcom.flags, IP_IFF_UP))
continue;
for (addr = netif->inet4_addr_list;
addr != IP_NULL && addr->type == IPNET_ADDR_TYPE_UNICAST;
addr = addr->next)
{
if (ipcom_route_key_cmp(32,
&flow_spec->to.in,
&addr->ipaddr_n,
&addr->netmask_n)
!= IPCOM_ROUTE_KEY_CMP_NO_MATCH)
{
int this_prefixlen = ipcom_mask_to_prefixlen(&addr->netmask_n, 32);
/*
* Select the network interface that is attached to a
* subnet that closest matches the 'to' address.
*/
if (best_prefixlen < this_prefixlen)
{
best_prefixlen = this_prefixlen;
best_netif = netif;
}
}
}
}
return best_netif;
}
#endif /* IPCOM_USE_INET */
/*
*===========================================================================
* ipnet_dst_cache_ip6_dont_route_netif
*===========================================================================
* Description: Tries to find the egress interface to use for a don't route
* transmission.
* Parameters: flow_spec - flow specification.
* Returns:
*
*/
#ifdef IPCOM_USE_INET6
IP_STATIC Ipnet_netif *
ipnet_dst_cache_ip6_dont_route_netif(const Ipnet_flow_spec *flow_spec)
{
Ipnet_netif *netif;
Ipnet_netif *best_netif = IP_NULL;
unsigned best_prefixlen = 0;
unsigned i;
/*
* IPNET_NETIF_FOR_EACH_OR_SPECIFIC_ON_VR sets netif to NULL after the for loop,
* this is a performance optimized defination
*/
/* coverity[result_independent_of_operands] */
IPNET_NETIF_FOR_EACH_OR_SPECIFIC_ON_VR(netif,
flow_spec->vr,
flow_spec->egress_ifindex,
i)
{
Ipnet_ip6_addr_entry *addr;
struct Ip_in6_addr mask;
if (IP_BIT_ISFALSE(netif->ipcom.flags, IP_IFF_UP))
continue;
for (addr = netif->inet6_addr_list;
addr != IP_NULL && addr->type == IPNET_ADDR_TYPE_UNICAST;
addr = addr->next)
{
if (addr->prefixlen == 0)
continue;
ipcom_memset(&mask, 0, sizeof(mask));
ipnet_route_create_mask(&mask, addr->prefixlen);
if (ipcom_route_key_cmp(128, &flow_spec->to.in6, &addr->addr, &mask)
!= IPCOM_ROUTE_KEY_CMP_NO_MATCH)
{
if (best_prefixlen < addr->prefixlen)
{
best_prefixlen = addr->prefixlen;
best_netif = netif;
}
}
}
}
return best_netif;
}
#endif /* IPCOM_USE_INET6 */
/*
*===========================================================================
* ipnet_dst_cache_select_best_rt
*===========================================================================
* Description: Selects routes with next-hop that is known to be reachable
* over routes where the connectivity of the next-hop is
* unknown.
* Parameters: domain - IP_AF_INET or IP_AF_INET6
* flow_spec - flow specification that will use the route
* rt - route returned from the FIB, must not be IP_NULL
* Returns: Best route, will never be IP_NULL
*
*/
IP_GLOBAL Ipnet_route_entry *
ipnet_dst_cache_select_best_rt(int domain,
const Ipnet_flow_spec *flow_spec,
Ipnet_route_entry *rt)
{
Ipnet_route_entry *re = rt;
Ipnet_neigh *neigh;
if (IP_BIT_ISFALSE(rt->hdr.flags, IPNET_RTF_GATEWAY)
|| (rt->prev == IP_NULL && rt->next == IP_NULL))
return rt;
/*
* RFC 1122 section 3.3.1.2 - WIND00314264
* The IP layer should choose the next "default" route if the
* first one is not unreachable.
*
* RFC 4861 - default router switch
*/
/*
* Prefer next-hops that are known to be reachable.
*/
do {
Ipnet_netif *netif = re->netif;
neigh = netif->neigh_create(domain, netif, flow_spec, re);
if (neigh)
{
if (ipnet_neigh_is_complete(neigh))
{
/* This next-hop is reachable */
ipnet_neigh_release(neigh);
break;
}
ipnet_neigh_release(neigh);
}
re = re->next;
if (re == IP_NULL)
{
re = rt;
while (re->prev)
re = re->prev;
}
} while (re != rt);
return re;
}
/*
*===========================================================================
* ipnet_dst_cache_pmtu_cb
*===========================================================================
* Description: Callback that checks if the destination cache entry matches
* the specified domain and 'to' address and if so, adjust
* the path MTU.
* Parameters: dst - a destination cache entry
* p - matching domain and 'to' address
* Returns:
*
*/
IP_STATIC void
ipnet_dst_cache_pmtu_cb(Ipnet_dst_cache *dst, struct Ipnet_dst_cache_pmtu *p)
{
if (p->path_mtu > dst->path_mtu
&& IPNET_FLOW_SPEC_DOMAIN(&dst->flow_spec) == p->domain
&& ipcom_memcmp(&dst->flow_spec.to,
&p->to,
ipnet_ipaddrlen(p->domain)) == 0)
{
p->path_mtu = dst->path_mtu;
}
}
/*
*===========================================================================
* ipnet_dst_cache_get_pmtu
*===========================================================================
* Description: Checks all entries that ends at on the same remote node
* and returns the smallest path MTU used to that host.
* Parameters: dst - the destination entry to match.
* Returns: Path MTU
*
*/
IP_STATIC Ip_u32
ipnet_dst_cache_get_pmtu(const Ipnet_dst_cache *dst)
{
struct Ipnet_dst_cache_pmtu p;
p.domain = IPNET_FLOW_SPEC_DOMAIN(&dst->flow_spec);
p.to = dst->flow_spec.to;
p.path_mtu = dst->path_mtu;
ipcom_hash_for_each(ipnet_dst_cache,
(Ipcom_hash_foreach_cb_func) ipnet_dst_cache_pmtu_cb,
&p);
return p.path_mtu;
}
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_dst_cache_new
*===========================================================================
* Description: Creates a new destination cache entry.
* Parameters: flow_spec - fields that uniquely indentify a flow of
* packets
* domain_ctor - domain specific constructor for this
* entry.
* pdst - pointer to where the new entry should be stored
* Returns: 0 = success
* <0 = error code
*
*/
IP_GLOBAL int
ipnet_dst_cache_new(const Ipnet_flow_spec *flow_spec,
Ipnet_dst_cache_domain_ctor domain_ctor,
Ipnet_dst_cache **pdst)
{
Ipnet_route_entry *rt;
Ipnet_route_entry rt_buf;
Ipnet_netif *netif = IP_NULL;
int ret = 0;
int rt_ret = 0;
Ipnet_dst_cache *dst;
int domain;
#if defined(IPCOM_USE_INET) && defined(IPCOM_USE_INET6)
Ipnet_flow_spec v4mapped_flow_spec;
#endif
Ipnet_pmtu *store_pathmtu;
domain = IPNET_FLOW_SPEC_DOMAIN(flow_spec);
ip_assert(ipnet_dst_cache_get(flow_spec) == IP_NULL);
ip_assert(domain == IP_AF_INET || domain == IP_AF_INET6);
if(IP_TRUE != ipnet_dst_cache_check_limit())
return -IP_ERRNO_ENOMEM;
dst = ipcom_slab_alloc(ipnet_dst_slab);
if (dst == IP_NULL)
{
/*
* This is kind of bad, out of memory.
*/
ipnet_try_free_mem(IP_FALSE);
dst = ipcom_slab_alloc(ipnet_dst_slab);
if (dst == IP_NULL)
return -IP_ERRNO_ENOMEM;
}
/*
* Copy the flow specification
*/
ipcom_memcpy(&dst->flow_spec, flow_spec, sizeof(*flow_spec));
/*
* Fetch the pointer to the ingress interface if an ingress
* interface is specified.
*/
if (flow_spec->ingress_ifindex)
dst->ingress_netif = ipnet_if_indextonetif(flow_spec->vr,
flow_spec->ingress_ifindex);
if (IP_BIT_ISSET(flow_spec->flags, IPNET_FSF_DONTROUTE))
{
/*
* Don't route. Create a route entry with the bare minimum of
* fields initialized.
*/
ipcom_memset(&rt_buf, 0, sizeof(rt_buf));
rt = &rt_buf;
rt->hdr.key = (void*) &flow_spec->to;
if (flow_spec->egress_ifindex == 0)
{
switch (domain)
{
#ifdef IPCOM_USE_INET
case IP_AF_INET:
rt->netif = ipnet_dst_cache_ip4_dont_route_netif(flow_spec);
break;
#endif
#ifdef IPCOM_USE_INET6
case IP_AF_INET6:
rt->netif = ipnet_dst_cache_ip6_dont_route_netif(flow_spec);
break;
#endif
default:
IP_PANIC();
break;
}
if (rt->netif == IP_NULL)
{
ret = -IP_ERRNO_EHOSTUNREACH;
goto errout;
}
}
/*
* else: use the specified egress interface
*/
}
else
{
/*
* Locate the FIB entry to the destination
*/
rt_ret = ipnet_route_lookup_ecmp(domain,
flow_spec->vr,
0,
&flow_spec->to,
flow_spec->zone_id,
flow_spec->egress_ifindex,
dst,
&dst->flow_spec,
&rt);
if (IP_UNLIKELY(0 > rt_ret || IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_BLACKHOLE)))
{
if (IP_BIT_ISSET(flow_spec->flags, IPNET_FSF_DONTDISCARD))
{
/* The flow spec does not allow discard management */
ret = rt_ret < 0? rt_ret : -IP_ERRNO_EHOSTUNREACH;
goto errout;
}
/* Setup an 'error' neigh using the discard netif */
ipcom_memset(&rt_buf, 0, sizeof(rt_buf));
rt = &rt_buf;
rt->hdr.key = (void*) &flow_spec->to;
rt->netif = netif = ipnet_neigh_netif_discard();
rt->data = &rt_ret;
}
#if defined(IPCOM_USE_INET) && defined(IPCOM_USE_INET6)
if (rt_ret >= 0
&& domain == IP_AF_INET6
&& IP_IN6_IS_ADDR_V4MAPPED(&flow_spec->to.in6))
{
/* 'rt' is an IPv4 route in this case */
domain = IP_AF_INET;
ipcom_memcpy(&v4mapped_flow_spec, flow_spec, sizeof(Ipnet_flow_spec));
IP_BIT_CLR(v4mapped_flow_spec.flags, IPNET_FSF_IPV6);
v4mapped_flow_spec.to.in.s_addr = flow_spec->to.in6.in6.addr32[3];
v4mapped_flow_spec.to.in6.in6.addr32[1] = 0;
v4mapped_flow_spec.to.in6.in6.addr32[2] = 0;
v4mapped_flow_spec.to.in6.in6.addr32[3] = 0;
flow_spec = &v4mapped_flow_spec;
}
#endif /* IPCOM_USE_INET && IPCOM_USE_INET6 */
if (rt_ret >= 0)
rt = ipnet_dst_cache_select_best_rt(domain, flow_spec, rt);
}
/*
* Get the peer information entry.
*/
dst->peer_info = ipnet_peer_info_get(flow_spec->vr,
domain,
&flow_spec->to);
if (dst->peer_info == IP_NULL)
{
/* out of memory */
ret = -IP_ERRNO_ENOMEM;
goto errout;
}
/*
* Do the domain specific initialization of the entry. The most
* important part of this is the initialization of the
* transmission/reception handlers and selection of local
* address (dst->laddr).
*/
ret = domain_ctor(dst, rt);
if (ret < 0)
goto errout;
if (IP_LIKELY(netif == IP_NULL))
{
if (flow_spec->egress_ifindex)
{
/*
* Multicast routes might have
* rt->netif->ipcom.ifindex!=flow_spec->egress_ifindex.
*/
netif = ipnet_if_indextonetif(flow_spec->vr,
flow_spec->egress_ifindex);
}
else
netif = rt->netif;
if (IP_UNLIKELY(IP_NULL == netif))
{
ret = -IP_ERRNO_ENXIO;
goto errout;
}
}
/*
* Set the initial path-MTU for this flow. Might be lowered by
* ICMP need frag. or ICMPv6 packet too big messages later on.
*/
dst->path_mtu = netif->ipcom.mtu;
if (dst->peer_info != IP_NULL
&& IP_BIT_ISSET(dst->peer_info->flags, IPNET_PEER_INFO_CHECKPMTU))
{
/*
* Has already received need frag / too big message when
* communicating with this host, figure out the PMTU to use.
*/
dst->path_mtu = ipnet_dst_cache_get_pmtu(dst);
}
store_pathmtu = ipnet_pmtu_get(flow_spec->vr, domain, &flow_spec->to);
if(IP_NULL != store_pathmtu)
{
if(store_pathmtu->path_mtu < dst->path_mtu)
dst->path_mtu = store_pathmtu->path_mtu;
#ifdef IPCOM_USE_INET
if ((domain == IP_AF_INET) && (dst->path_mtu < ipnet_conf_ip4_min_mtu))
{
dst->path_mtu = ipnet_conf_ip4_min_mtu;
}
#endif
#ifdef IPCOM_USE_INET6
if ((domain == IP_AF_INET6) && (dst->path_mtu < ipnet_conf_ip6_min_mtu))
{
dst->path_mtu = ipnet_conf_ip6_min_mtu;
}
#endif
}
ret = -IP_ERRNO_ENOMEM;
if (dst->real_rx == ipnet_dst_cache_blackhole()->rx)
{
#ifdef IPNET_IS_ROUTER
enum Ipnet_addr_type addr_type;
#ifdef IPCOM_USE_INET
if (domain == IP_AF_INET && (IP_BIT_ISFALSE(ipnet_flags, IPNET_FLAG_IPV4_FORWARD_PKT)
|| IP_BIT_ISSET(netif->flags2, IPNET_IFF2_NO_IPV4_FORWARD)
|| IP_BIT_ISTRUE(flow_spec->flags, IPNET_FSF_LINK_BROADCAST)))
{
addr_type = ipnet_ip4_get_addr_type(flow_spec->from.in.s_addr, flow_spec->vr, netif);
if (addr_type != IPNET_ADDR_TYPE_ANY && addr_type != IPNET_ADDR_TYPE_UNICAST)
{
ret = -IP_ERRNO_EHOSTUNREACH;
goto errout;
}
}
#endif
#ifdef IPCOM_USE_INET6
if (domain == IP_AF_INET6 && (IP_BIT_ISFALSE(ipnet_flags, IPNET_FLAG_IPV6_FORWARD_PKT)
|| IP_BIT_ISSET(netif->flags2, IPNET_IFF2_NO_IPV6_FORWARD)
|| IP_BIT_ISTRUE(flow_spec->flags, IPNET_FSF_LINK_BROADCAST)))
{
addr_type = ipnet_ip6_get_addr_type(&flow_spec->from.in6, flow_spec->vr, netif);
if (addr_type != IPNET_ADDR_TYPE_ANY && addr_type != IPNET_ADDR_TYPE_UNICAST)
{
ret = -IP_ERRNO_EHOSTUNREACH;
goto errout;
}
}
#endif
#endif
}
/* Create the neighbor */
dst->neigh = netif->neigh_create(domain, netif, flow_spec, rt);
if (dst->neigh == IP_NULL)
{
/* out of memory */
goto errout;
}
#ifdef IPMIP
if (IP_UNLIKELY(dst->mip.flow_handle != IP_NULL))
(*dst->mip.flow_handle) (IP_TRUE, dst);
#endif
/*
* Finally add this entry into the hash destination cache table
*/
if (ipcom_hash_add(ipnet_dst_cache, dst) != IPCOM_SUCCESS)
/* out of memory */
goto errout;
ipnet_dst_cache_reset_timer(dst, IP_TRUE);
++rt->use;
*pdst = dst;
return 0;
errout:
ipnet_dst_cache_delete(dst);
return ret;
}
/*
*===========================================================================
* ipnet_dst_cache_transient_rx
*===========================================================================
* Description: Reception handler during the transient state
* Parameters: dst - ingress destination cache entry
* pkt - a packet
* Returns: The return value of the real RX handler.
*
*/
IP_STATIC int
ipnet_dst_cache_transient_rx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt)
{
if (dst->transient_cnt-- <= 0)
{
dst->rx = dst->real_rx;
ipnet_dst_cache_reset_timer(dst, IP_FALSE);
}
return dst->real_rx(dst, pkt);
}
/*
*===========================================================================
* ipnet_dst_cache_set_rx_handler
*===========================================================================
* Description: Sets the reception handler.
* Parameters: dst - a destination cache entry
* rx_func - reception handler to use
* Returns:
*
*/
IP_GLOBAL void
ipnet_dst_cache_set_rx_handler(Ipnet_dst_cache *dst,
Ipnet_dst_cache_rx_func rx_func)
{
if (dst->transient_cnt > 0)
dst->rx = ipnet_dst_cache_transient_rx;
else
dst->rx = rx_func;
dst->real_rx = rx_func;
}
/*
*===========================================================================
* ipnet_dst_cache_transient_tx
*===========================================================================
* Description: Transmission handler during the transient state
* Parameters: dst - egress destination cache entry
* pkt - a packet
* Returns: The return value of the real TX handler.
*
*/
IP_STATIC int
ipnet_dst_cache_transient_tx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt)
{
if (dst->transient_cnt-- <= 0)
{
dst->tx = dst->real_tx;
ipnet_dst_cache_reset_timer(dst, IP_FALSE);
}
return dst->real_tx(dst, pkt);
}
/*
*===========================================================================
* ipnet_dst_cache_set_tx_handler
*===========================================================================
* Description: Sets the transmission handler.
* Parameters: dst - a destination cache entry
* tx_func - transmission handler to use
* Returns:
*
*/
IP_GLOBAL void
ipnet_dst_cache_set_tx_handler(Ipnet_dst_cache *dst,
Ipnet_dst_cache_tx_func tx_func)
{
if (dst->transient_cnt > 0)
dst->tx = ipnet_dst_cache_transient_tx;
else
dst->tx = tx_func;
dst->real_tx = tx_func;
}
/*
*===========================================================================
* ipnet_dst_cache_set_path_mtu
*===========================================================================
* Description: Sets the path MTU for this entry.
* Parameters: dst - a destination cache entry
* mtu - new path mtu
* Returns:
*
*/
IP_GLOBAL void
ipnet_dst_cache_set_path_mtu(Ipnet_dst_cache *dst, Ip_u32 mtu)
{
Ip_u32 path_mtu_min = 0;
#ifdef IPCOM_USE_INET
if (ipnet_flow_spec_domain(&dst->flow_spec) == IP_AF_INET)
{
path_mtu_min = ipnet_conf_ip4_min_mtu;
if (mtu < path_mtu_min)
{
if (mtu == 0)
IP_BIT_SET(dst->peer_info->flags, IPNET_PEER_INFO_ZEROPMTU);
mtu = path_mtu_min;
}
}
#endif
#ifdef IPCOM_USE_INET6
if (ipnet_flow_spec_domain(&dst->flow_spec) == IP_AF_INET6)
{
path_mtu_min = ipnet_conf_ip6_min_mtu;
}
#endif
if (mtu < dst->path_mtu && mtu >= path_mtu_min)
{
dst->path_mtu = mtu;
IP_BIT_SET(dst->peer_info->flags, IPNET_PEER_INFO_CHECKPMTU);
ipnet_dst_cache_reset_timer(dst, IP_FALSE);
}
}
IP_GLOBAL Ipnet_dst_cache *
ipnet_dst_cache_blackhole_flow_spec(const Ipnet_flow_spec *flow_spec)
{
ipnet_dst_cache_blackhole_singleton.flow_spec = *flow_spec;
return &ipnet_dst_cache_blackhole_singleton;
}
/*
*===========================================================================
* ipnet_dst_cache_blackhole
*===========================================================================
* Description: Returns a destination cache entry that will discard
* any packets transmitted or received.
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ipnet_dst_cache *
ipnet_dst_cache_blackhole(void)
{
return &ipnet_dst_cache_blackhole_singleton;
}
/*
*===========================================================================
* ipnet_dst_cache_flush
*===========================================================================
* Description: Flushes all matching destination cache entries
* Parameters: vr - index of virtual router to flush or IP_VR_ANY for all
* domain - IP_AF_INET/IP_AF_INET6 or 0 to flush all domains
* Returns:
*
*/
IP_GLOBAL void
ipnet_dst_cache_flush(Ip_u16 vr, int domain)
{
Ipnet_flow_spec flush_filter;
flush_filter.vr = vr;
#ifdef IPCOM_USE_INET
if (domain == 0 || domain == IP_AF_INET)
{
flush_filter.flags = 0;
ipcom_hash_for_each(ipnet_dst_cache,
(Ipcom_hash_foreach_cb_func) ipnet_dst_cache_flush_cb,
&flush_filter);
}
#endif
#ifdef IPCOM_USE_INET6
if (domain == 0 || domain == IP_AF_INET6)
{
flush_filter.flags = IPNET_FSF_IPV6;
ipcom_hash_for_each(ipnet_dst_cache,
(Ipcom_hash_foreach_cb_func) ipnet_dst_cache_flush_cb,
&flush_filter);
}
#endif
ipnet_route_cache_invalidate();
}
/*
*===========================================================================
* ipnet_dst_cache_flush_where_neigh_is
*===========================================================================
* Description: Flush all entries using a specific neighbor as first hop.
* Parameters: neigh - the neighbor to match
* Returns:
*
*/
IP_GLOBAL void
ipnet_dst_cache_flush_where_neigh_is(Ipnet_neigh *neigh)
{
ipcom_hash_for_each(ipnet_dst_cache,
(Ipcom_hash_foreach_cb_func) ipnet_dst_cache_flush_where_neigh_is_cb,
neigh);
}
/*
*===========================================================================
* ipnet_dst_cache_foreach
*===========================================================================
* Description: Calls a user defined function for every destination
* cache entry.
* Parameters: foreach_func -
* user_data -
* Returns:
*
*/
IP_GLOBAL void
ipnet_dst_cache_foreach(Ipnet_dst_cache_foreach_func foreach_func,
void *user_data)
{
ipcom_hash_for_each(ipnet_dst_cache,
(Ipcom_hash_foreach_cb_func) foreach_func,
user_data);
}
/*
*===========================================================================
* ipnet_dst_cache_init
*===========================================================================
* Description: Initializes the destination cache module.
* Parameters:
* Returns: 0 = success
* -IP_ERRNO_ENOMEM = out of memory
*
*/
IP_GLOBAL int
ipnet_dst_cache_init(void)
{
ipnet_dst_slab = ipcom_slab_new("IPNET destination cache",
IPCOM_SLAB_F_ZERO,
sizeof(Ipnet_dst_cache),
0,
IP_NULL,
IP_NULL,
ipnet->memory_pool);
ipnet_dst_cache
= ipcom_hash_new((Ipcom_hash_obj_func) ipnet_dst_cache_hash_obj,
(Ipcom_hash_key_func) ipnet_dst_cache_hash_key,
(Ipcom_hash_cmp_func) ipnet_dst_cache_hash_cmp);
if (ipnet_dst_slab == IP_NULL
|| ipnet_dst_cache == IP_NULL)
return -IP_ERRNO_ENOMEM;
/**/
ipnet_dst_cache_blackhole_singleton.tx = ipnet_dst_cache_blackhole_action;
ipnet_dst_cache_blackhole_singleton.rx = ipnet_dst_cache_blackhole_action;
ipnet_dst_cache_blackhole_singleton.path_mtu = 0x7ffffff;
#ifdef IPNET_DEBUG
ipnet_timeout_to_string((Ipnet_timeout_handler) ipnet_dst_cache_delete,
(Ipnet_timeout_to_string_f) ipnet_dst_cache_tmo_to_string);
#endif
return 0;
}
/*
*===========================================================================
* ipnet_flow_spec_domain
*===========================================================================
* Description: Returns the address domain for a flow specification
* Parameters: flow_spec - a neighbor entry.
* Returns: A domain, IP_AF_INET or IP_AF_INET6
*
*/
IP_GLOBAL int
ipnet_flow_spec_domain(const Ipnet_flow_spec *flow_spec)
{
/*
* Destination cache entries created for "IP_MSG_DONTROUTE"
* transmissions has their domain value multiplied by -1.
*/
return IPNET_FLOW_SPEC_DOMAIN(flow_spec);
}
#ifdef IPMIP
IP_GLOBAL void
ipnet_flow_spec_mobility_from_pkt(Ipnet_flow_spec *flow_spec,
const Ipcom_pkt *pkt,
Ip_u8 proto)
{
Ip_u8 *encap;
Ip_size_t size;
ipcom_memset(&flow_spec->mip, 0, sizeof(flow_spec->mip));
/* Store protocol */
flow_spec->mip.key.u8[1] = proto;
/* */
if ((encap = ipcom_pkt_get_info_and_size(pkt, IPNET_PKT_INFO_ENCAP, &size)) != IP_NULL)
{
/* ENCAP */
ipcom_memcpy(&flow_spec->mip.key.u32[1], encap, size);
/* GRE KEY */
flow_spec->mip.key.u32[5] = (Ip_u32)pkt->link_cookie;
/* ENCAP SIZE */
flow_spec->mip.key.u8[0] = (Ip_u8)size;
}
else
{
/* ENCAP SIZE */
flow_spec->mip.key.u8[0] = (Ip_u8)pkt->lla_size;
/* ENCAP */
ipcom_memcpy(&flow_spec->mip.key.u32[1], &pkt->data[pkt->lla_off], pkt->lla_size);
}
}
IP_GLOBAL void
ipnet_flow_spec_mobility_from_sock(Ipnet_flow_spec *flow_spec,
Ipnet_socket *sock)
{
#ifdef IPNET_USE_RFC2890
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_GRE_KEY))
flow_spec->mip.key.u32[5] = (Ip_u32)sock->gre_key;
#endif
/* PROTOCOL */
flow_spec->mip.key.u8[1] = (Ip_u8) sock->proto;
}
#endif
/*
*===========================================================================
* ipnet_flow_spec_from_sock
*===========================================================================
* Description: Returns a flow specification created from a socket.
* Parameters: flow_spec - pointer to where the flow information will
* be stored.
* sock - an IP_AF_INET or IP_AF_INET6 socket.
* domain_data - domain specific data.
* domain_func - function that will initialize the domain
* specific part of the flow specification.
* Returns: 0 = success
* <0 = error code
*
*/
IP_GLOBAL int
ipnet_flow_spec_from_sock(Ipnet_flow_spec *flow_spec,
Ipnet_socket *sock,
const void *domain_data,
Ipnet_flow_spec_from_sock_domain_func domain_func)
{
int ret;
IPNET_FLOW_SPEC_ZERO(*flow_spec);
flow_spec->vr = sock->vr_index;
if (sock->ipcom.domain == IP_AF_INET6)
flow_spec->flags = IPNET_FSF_IPV6;
if (IP_LIKELY((ret = domain_func(flow_spec, sock, domain_data)) == 0))
{
#ifdef IPMIP
ipnet_flow_spec_mobility_from_sock(flow_spec, sock);
#endif
}
return ret;
}
IP_GLOBAL int
ipnet_flow_spec_from_info(Ipnet_flow_spec *flow_spec,
Ip_u16 vr,
const void *src,
const void *dst,
Ip_u32 zone_id,
Ip_u8 tos_tclass,
Ipnet_netif *iif,
Ipnet_netif *oif,
Ipnet_flow_spec_from_info_domain_func domain_func)
{
IPNET_FLOW_SPEC_ZERO(*flow_spec);
flow_spec->vr = vr;
flow_spec->ds = tos_tclass;
flow_spec->ingress_ifindex = iif? iif->ipcom.ifindex : 0;
flow_spec->egress_ifindex = oif? oif->ipcom.ifindex : 0;
return domain_func(flow_spec, src, dst);
}
#ifdef IPNET_USE_SHOW
/*
*===========================================================================
* ipnet_dst_cache_show
*===========================================================================
* Description:
* Parameters: dst -
* bucket -
* ideal_bucket -
* Returns:
*
*/
IP_STATIC void
ipnet_dst_cache_show(Ipnet_dst_cache *dst)
{
char addr_str[IP_INET6_ADDRSTRLEN];
Ipnet_flow_spec *flow_spec = &dst->flow_spec;
ipcom_printf("%-20s ",
ipcom_inet_ntop(IPNET_FLOW_SPEC_DOMAIN(flow_spec),
&flow_spec->to,
addr_str,
sizeof(addr_str)));
ipcom_printf("%-20s ",
ipcom_inet_ntop(IPNET_FLOW_SPEC_DOMAIN(flow_spec),
&flow_spec->from,
addr_str,
sizeof(addr_str)));
ipcom_printf("%-4u %-4u %-3u %-4u %-4u %-6u %-6s %-20s\n",
flow_spec->ingress_ifindex,
flow_spec->egress_ifindex,
flow_spec->ds,
flow_spec->zone_id,
flow_spec->vr,
dst->path_mtu,
IP_BIT_ISSET(flow_spec->flags, IPNET_FSF_DONTROUTE) ? "Yes" : "",
ipcom_inet_ntop(IPNET_FLOW_SPEC_DOMAIN(flow_spec),
&dst->laddr,
addr_str,
sizeof(addr_str)));
}
/*
*===========================================================================
* dstShow
*===========================================================================
* Description: Dumps all destination cache entries to the console.
* Parameters:
* Returns:
*
*/
unsigned dstShow(void);
unsigned dstShow(void)
{
unsigned i;
ipcom_printf("%-20s %-20s %-4s %-4s %-3s %-4s %-4s %-6s %-6s %-20s\n",
"Dst", "Src", "I-If", "E-If", "DS", "Zone", "VR", "PMTU", "DontRt", "Laddr");
for (i = 0; i < ipnet_dst_cache->size; i++)
if (ipnet_dst_cache->table[i])
ipnet_dst_cache_show(ipnet_dst_cache->table[i]);
/*
* Return the number of elements printed
*/
return ipnet_dst_cache->elem;
}
#endif /* IPNET_USE_SHOW */
IP_INLINE Ip_size_t
ipnet_pmtu_keylen(int domain)
{
#ifdef IPCOM_USE_INET6
if (domain == IP_AF_INET6)
return sizeof(struct Ip_in6_addr);
#endif
return sizeof(struct Ip_in_addr);
}
/*
*===========================================================================
* ipnet_pmtu_key_hash
*===========================================================================
* Description: Generates a hash value for the pmtu
* Parameters: neigh - a pmtu key.
* Returns: Hash value. Will always be the same for the same key content.
*
*/
IP_STATIC unsigned
ipnet_pmtu_key_hash(Ipnet_pmtu_lookup_key *key)
{
return ipcom_hash_update(&(key->addr),
(int)ipnet_pmtu_keylen(key->domain),
key->domain);
}
/*
*===========================================================================
* ipnet_pmtu_obj_hash
*===========================================================================
* Description: Generates a hash value for the pmtu.
* Parameters: neigh - a neighbor entry.
* Returns: Hash value. Will always be the same for the same pmtu.
*
*/
IP_STATIC unsigned
ipnet_pmtu_obj_hash(Ipnet_pmtu *pmtu)
{
Ipnet_pmtu_lookup_key key;
(void)ipcom_memcpy(&key, &(pmtu->key), (Ip_size_t) sizeof(Ipnet_pmtu_lookup_key));
return ipnet_pmtu_key_hash(&key);
}
/*
*===========================================================================
* ipnet_pmtu_cmp_hash
*===========================================================================
* Description: Checks if the lookup key matches the pmtu entry.
* Parameters: pmtu - a pmtu node
* key - a lookup key
* Returns: IP_TRUE if the key matches the node, IP_FALSE otherwise
*
*/
IP_STATIC unsigned
ipnet_pmtu_cmp_hash(Ipnet_pmtu *pmtu,
Ipnet_pmtu_lookup_key *key)
{
return (key->vr_index== pmtu->key.vr_index)
&& (key->domain == pmtu->key.domain)
&& (ipcom_memcmp(&(key->addr),
&(pmtu->key.addr),
ipnet_pmtu_keylen(key->domain)) == 0);
}
IP_GLOBAL int
ipnet_pmtu_init(void)
{
ipnet_pmtu_slab = ipcom_slab_new("IPNET pmtus",
IPCOM_SLAB_F_ZERO,
sizeof(Ipnet_pmtu),
0,
IP_NULL,
IP_NULL,
ipnet->memory_pool);
ipnet_pmtus = ipcom_hash_new((Ipcom_hash_obj_func) ipnet_pmtu_obj_hash,
(Ipcom_hash_key_func) ipnet_pmtu_key_hash,
(Ipcom_hash_cmp_func) ipnet_pmtu_cmp_hash);
ipnet_pmtu_live_time = INET_DEFAULT_PATHMTU_LIVE_TIME;
if (ipnet_pmtu_slab == IP_NULL
|| ipnet_pmtus == IP_NULL)
{
/*
* Boot error, this system is too memory contrained to support
* networking
*/
IP_PANIC();
return -IP_ERRNO_ENOMEM;
}
return 0;
}
/*
*===========================================================================
* ipnet_dstcache_delete_by_pmtu
*===========================================================================
* Description: Called for each destination cache entry. Entries that matches
* the key.
* Parameters: dst - a destination cache entry
* pmtu - the pmtu node
* Returns:
*
*/
IP_STATIC void
ipnet_dstcache_delete_by_pmtu(Ipnet_dst_cache *dst,
void *pmtu_ptr)
{
Ipnet_pmtu *pmtu = (Ipnet_pmtu *)pmtu_ptr;
if (dst->flow_spec.vr == pmtu->key.vr_index)
{
if((IP_AF_INET == pmtu->key.domain)
&& (pmtu->key.addr.in.s_addr == dst->flow_spec.to.in.s_addr))
{
ipnet_dst_cache_delete(dst);
}
#ifdef IPCOM_USE_INET6
else if((IP_AF_INET6 == pmtu->key.domain)
&& IP_IN6_ARE_ADDR_EQUAL(&pmtu->key.addr.in6, &dst->flow_spec.to.in6))
{
ipnet_dst_cache_delete(dst);
}
#endif
}
}
/*
*===========================================================================
* ipnet_pmtu_update
*===========================================================================
* Description: update a pmtu entry.
* Parameters: pmtu - the pmtu that will be deleted
* Returns:
*
*/
IP_STATIC void
ipnet_pmtu_update(Ipnet_pmtu *pmtu, Ip_u32 new_path_mtu)
{
Ip_u32 path_mtu_min = 0;
Ip_s64 msec;
#ifdef IPCOM_USE_INET
if (pmtu->key.domain == IP_AF_INET)
{
path_mtu_min = ipnet_conf_ip4_min_mtu;
if (new_path_mtu < path_mtu_min)
{
new_path_mtu = path_mtu_min;
}
}
#endif
#ifdef IPCOM_USE_INET6
if (pmtu->key.domain == IP_AF_INET6)
{
path_mtu_min = ipnet_conf_ip6_min_mtu;
if (new_path_mtu < path_mtu_min)
{
new_path_mtu = path_mtu_min;
}
}
#endif
if (pmtu->path_mtu > new_path_mtu)
{
pmtu->path_mtu = new_path_mtu;
msec = ipnet_pmtu_live_time * 1000;
ipnet_timeout_cancel(pmtu->tmo);
(void)ipnet_timeout_schedule(msec,
(Ipnet_timeout_handler) ipnet_pmtu_timeout,
pmtu,
&pmtu->tmo);
}
}
/*
*===========================================================================
* ipnet_pmtu_timeout
*===========================================================================
* Description: Timeout handler for a pmtu entry.
* Parameters: pmtu - the pmtu entry that had its timer
* expired.
* Returns:
*
*/
IP_STATIC void
ipnet_pmtu_timeout(Ipnet_pmtu *pmtu)
{
ipnet_pmtu_delete(pmtu);
}
/*
*===========================================================================
* ipnet_pmtu_delete
*===========================================================================
* Description: delete a pmtu entry.
* Parameters: pmtu - the pmtu entry that will be deleted
*
* Returns:
*
*/
IP_STATIC void
ipnet_pmtu_delete(Ipnet_pmtu *pmtu)
{
ip_assert(pmtu->tmo == IP_NULL);
/*ipnet_flow_spec_from_pmtu(&flow_spec, pmtu);*/
/* Delete relevant DST cache */
ipnet_dst_cache_foreach(ipnet_dstcache_delete_by_pmtu, (void *)pmtu);
(void)ipcom_hash_remove(ipnet_pmtus, pmtu);
ipnet_timeout_cancel(pmtu->tmo);
ipcom_slab_free(ipnet_pmtu_slab, pmtu);
}
/*
*===========================================================================
* ipnet_pmtu_new
*===========================================================================
* Description: Allocates and initializes a new pmtu entry.
* Parameters: vr - virtual router
* domain - IP_AF_INET or IP_AF_INET6 .
* addr - address of the peer
* Returns:
*
*/
IP_STATIC Ipnet_pmtu *
ipnet_pmtu_new(int vr,
int domain,
IP_CONST void *addr,
int pmtu_value)
{
Ipnet_pmtu *pmtu = IP_NULL;
Ip_s64 msec;
if((0 != ipnet_pmtu_limit)
&&(ipnet_pmtus->elem >= ipnet_pmtu_limit))
{
IPCOM_LOG2(WARNING,
"Pmtu node number exceed limit(peer %s; limit %u)",
ipcom_inet_ntop(domain,
addr,
ipnet->log_buf,
sizeof(ipnet->log_buf)),
ipnet_pmtu_limit);
return IP_NULL;
}
pmtu = ipcom_slab_alloc(ipnet_pmtu_slab);
if (pmtu == IP_NULL)
{
IPCOM_LOG1(WARNING,
"Failed to allocate pmtu memory for peer %s",
ipcom_inet_ntop(domain,
addr,
ipnet->log_buf,
sizeof(ipnet->log_buf)));
ipnet_try_free_mem(IP_TRUE);
return IP_NULL;
}
(void)ipcom_memset(pmtu, 0, sizeof(Ipnet_pmtu));
pmtu->key.vr_index = vr;
pmtu->key.domain = domain;
(void)ipcom_memcpy(&(pmtu->key.addr), addr, ipnet_pmtu_keylen(domain));
pmtu->path_mtu = pmtu_value;
msec = ipnet_pmtu_live_time * 1000;
ipnet_timeout_cancel(pmtu->tmo);
(void)ipnet_timeout_schedule(msec,
(Ipnet_timeout_handler) ipnet_pmtu_timeout,
pmtu,
&pmtu->tmo);
if (ipcom_hash_add(ipnet_pmtus, pmtu) != IPCOM_SUCCESS)
{
ipnet_pmtu_delete(pmtu);
return IP_NULL;
}
return pmtu;
}
/*
*===========================================================================
* ipnet_pmtu_get
*===========================================================================
* Description: Retrieves pmtu entry.
* Parameters: vr - virtual router
* domain - IP_AF_INET or IP_AF_INET6 .
* addr - address of the peer
* Returns:
*
*/
IP_GLOBAL Ipnet_pmtu *
ipnet_pmtu_get(int vr,
int domain,
IP_CONST void *addr)
{
Ipnet_pmtu_lookup_key key;
Ipnet_pmtu *pmtu = IP_NULL;
key.vr_index= vr;
key.domain= domain;
(void)ipcom_memcpy(&(key.addr), addr, (Ip_size_t)ipnet_pmtu_keylen(domain));
pmtu = ipcom_hash_get(ipnet_pmtus, &key);
if (pmtu == IP_NULL)
{
return IP_NULL;
}
return pmtu;
}
#ifdef IPCOM_USE_INET
IP_STATIC void
ipnet_pmtu_proc_inet(Ipcom_pkt *pkt)
{
Ipnet_pkt_icmp *icmp;
Ipnet_pkt_ip *exceeding_ip;
Ipnet_pmtu *pmtu;
Ip_u32 new_path_mtu;
icmp = ipcom_pkt_get_data(pkt, 0);
exceeding_ip = (Ipnet_pkt_ip *) icmp->data.failing_pkt.ip;
new_path_mtu = IP_GET_NTOHL(icmp->data.failing_pkt.next_hop_mtu);
if (new_path_mtu < ipnet_conf_ip4_min_mtu)
{
IPCOM_LOG2(WARNING,
"Pmtu value less than minimum MTU value(pmtu %u; min %u)",
new_path_mtu,
ipnet_conf_ip4_min_mtu);
}
pmtu = ipnet_pmtu_get(pkt->vr_index, IP_AF_INET, &(exceeding_ip->dst));
if(IP_NULL == pmtu)
{
pmtu = ipnet_pmtu_new(pkt->vr_index, IP_AF_INET, &(exceeding_ip->dst), new_path_mtu);
return;
}
ipnet_pmtu_update(pmtu, new_path_mtu);
return;
}
#endif
#ifdef IPCOM_USE_INET6
IP_STATIC void
ipnet_pmtu_proc_inet6(Ipcom_pkt *pkt)
{
Ipnet_pkt_ip6 *exceeding_ip6;
Ipnet_pkt_icmp6 *icmp6;
struct Ip_in6_addr in6;
Ipnet_pmtu *pmtu;
Ip_u32 new_path_mtu;
exceeding_ip6 = ipcom_pkt_get_data(pkt, IPNET_ICMPV6_HDR_SIZE);
icmp6 = ipcom_pkt_get_data(pkt, 0);
new_path_mtu = IP_GET_NTOHL(&icmp6->ipnet_icmp6_mtu);
(void)ipcom_memcpy(&in6, &(exceeding_ip6->dst), (Ip_size_t) sizeof(struct Ip_in6_addr));
if (new_path_mtu < ipnet_conf_ip6_min_mtu)
{
IPCOM_LOG2(WARNING,
"Pmtu value less than minimum MTU value(pmtu %u; min %u)",
new_path_mtu,
ipnet_conf_ip6_min_mtu);
}
pmtu = ipnet_pmtu_get(pkt->vr_index, IP_AF_INET6, &in6);
if(IP_NULL == pmtu)
{
pmtu = ipnet_pmtu_new(pkt->vr_index, IP_AF_INET6, &in6, new_path_mtu);
return;
}
ipnet_pmtu_update(pmtu, new_path_mtu);
return;
}
#endif
/*
*===========================================================================
* ipnet_pmtu_proc
*===========================================================================
* Description: try proc pmtu.
* Parameters: vr - virtual router
* domain - IP_AF_INET or IP_AF_INET6 .
* addr - address of the peer
* Returns:
*
*/
IP_GLOBAL void
ipnet_pmtu_proc(Ipcom_pkt *pkt, int domain)
{
if(IP_AF_INET == domain)
{
#ifdef IPCOM_USE_INET
ipnet_pmtu_proc_inet(pkt);
IPCOM_LOG0(DEBUG, "Process INET PMTU");
#endif
}
#ifdef IPCOM_USE_INET6
else if(IP_AF_INET6 == domain)
{
ipnet_pmtu_proc_inet6(pkt);
IPCOM_LOG0(DEBUG, "Process INET6 PMTU");
}
#endif
else
IPCOM_LOG1(WARNING,
"Domain %d is illegal.", domain);
return;
}
/*
*===========================================================================
* ipnet_pmtu_foreach
*===========================================================================
* Description: Runs a function for each pmtu entry currently in
* the pmtu cache.
* Parameters: pmtu_foreach - function to call for each entry
* user_data - user defined data passed to pmtu_foreach
* as 2:nd argument
* Returns:
*
*/
IP_GLOBAL void
ipnet_pmtu_foreach(Ipnet_pmtu_foreach_func pmtu_foreach, void *user_data)
{
ipcom_hash_for_each(ipnet_pmtus,
(Ipcom_hash_foreach_cb_func)pmtu_foreach,
user_data);
}
/*
*===========================================================================
* ipnet_pmtu_show
*===========================================================================
* Description: Dumps a single pmtu entry to the console.
* Parameters: pmtu - a pmtu entry.
* unused - unused.
* Returns:
*
*/
IP_STATIC void
ipnet_pmtu_show(Ipnet_pmtu *pmtu, void *domain)
{
char addr_str[IP_INET6_ADDRSTRLEN];
int show_domain;
show_domain = (int)domain;
if(show_domain == pmtu->key.domain)
(void)ipcom_printf("%-48s %-10u %-10u %-10u\n",
ipcom_inet_ntop(show_domain,
&pmtu->key.addr,
addr_str,
sizeof(addr_str)),
pmtu->key.vr_index,
show_domain,
pmtu->path_mtu);
}
/*
*===========================================================================
* int pmtuShow
*===========================================================================
* Description: Dumps all pmtu cache entries to the console
* Parameters:
* Returns: Number of entries dumped.
*
*/
int pmtuShow(void)
{
#ifdef IPCOM_USE_INET
(void)ipcom_printf("%-48s %-10s %-10s %-10s\n",
"Address", "VR", "Domain", "PMTU");
(void)ipcom_printf("\nINET:\n");
ipnet_pmtu_foreach(ipnet_pmtu_show, (void *)IP_AF_INET);
#endif
#ifdef IPCOM_USE_INET6
(void)ipcom_printf("\nINET6:\n");
ipnet_pmtu_foreach(ipnet_pmtu_show, (void *)IP_AF_INET6);
#endif
return ipnet_pmtus->elem;
}
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/