2059 lines
64 KiB
C
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
|
|
****************************************************************************
|
|
*/
|
|
|