/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#include <inet/ip_multi.h>
#include <inet/ip_ftable.h>
#include <inet/ipclassifier.h>
#include <inet/ip_listutils.h>
/*
* Routines for handling destination cache entries.
* There is always one DCEF_DEFAULT for each ip_stack_t created at init time.
* That entry holds both the IP ident value and the dce generation number.
*
* Any time a DCE is changed significantly (different path MTU, but NOT
* different ULP info!), the dce_generation number is increased.
* Also, when a new DCE is created, the dce_generation number in the default
* DCE is bumped. That allows the dce_t information to be cached efficiently
* as long as the entity caching the dce_t also caches the dce_generation,
* and compares the cached generation to detect any changes.
* Furthermore, when a DCE is deleted, if there are any outstanding references
* to the DCE it will be marked as condemned. The condemned mark is
* a designated generation number which is never otherwise used, hence
* the single comparison with the generation number captures that as well.
*
* An example of code which caches is as follows:
*
* if (mystruct->my_dce_generation != mystruct->my_dce->dce_generation) {
* The DCE has changed
* mystruct->my_dce = dce_lookup_pkt(mp, ixa,
* &mystruct->my_dce_generation);
* Not needed in practice, since we have the default DCE:
* if (DCE_IS_CONDEMNED(mystruct->my_dce))
* return failure;
* }
*
* Note that for IPv6 link-local addresses we record the ifindex since the
* link-locals are not globally unique.
*/
/*
* Hash bucket structure for DCEs
*/
typedef struct dcb_s {
} dcb_t;
static void dce_make_condemned(dce_t *);
static int dce_reclaim_shutdown;
/* The time in seconds between executions of the IP DCE reclaim worker. */
/* The factor of the DCE threshold at which to start hard reclaims */
/* Operates on a uint64_t */
/*
* Reclaim a fraction of dce's in the dcb.
* For now we have a higher probability to delete DCEs without DCE_PMTU.
*/
static void
{
/* Clear DCEF_PMTU if the pmtu is too old */
} else {
}
if (hash % fraction_pmtu != 0) {
retained++;
continue;
}
} else {
retained++;
continue;
}
}
}
}
}
/*
* kmem_cache callback to free up memory.
*
*/
static void
{
int i;
for (i = 0; i < ipst->ips_dce_hashsize; i++) {
}
/*
* Walk all CONNs that can have a reference on an ire, nce or dce.
* Get them to update any stale references to drop any refholds they
* have.
*/
}
/*
* Called by dce_reclaim_worker() below, and no one else. Typically this will
* mean that the number of entries in the hash buckets has exceeded a tunable
* threshold.
*/
static void
ip_dce_reclaim(void)
{
/*
* netstack_next() can return a netstack_t with a NULL
* netstack_ip at boot time.
*/
continue;
}
}
}
/* ARGSUSED */
static void
{
"dce_reclaim_worker");
while (!dce_reclaim_shutdown) {
if (dce_reclaim_shutdown)
break;
}
dce_reclaim_shutdown = 0;
thread_exit();
}
void
dce_g_init(void)
{
}
void
dce_g_destroy(void)
{
dce_reclaim_shutdown = 1;
while (dce_reclaim_thread != NULL)
}
/*
* Allocate a default DCE and a hash table for per-IP address DCEs
*/
void
{
int i;
/* This must be a power of two since we are using IRE_ADDR_HASH macro */
for (i = 0; i < ipst->ips_dce_hashsize; i++) {
NULL);
NULL);
}
}
void
{
int i;
for (i = 0; i < ipst->ips_dce_hashsize; i++) {
}
ipst->ips_dce_hashsize = 0;
}
/* When any DCE is good enough */
dce_t *
{
return (dce);
}
/*
* Generic for IPv4 and IPv6.
*
* Used by callers that need to cache e.g., the datapath
* Returns the generation number in the last argument.
*/
dce_t *
{
/*
* If we have a source route we need to look for the final
* destination in the source route option.
*/
} else {
/*
* If we have a routing header we need to look for the final
* destination in the routing extension header.
*/
ifindex = 0;
}
generationp));
}
}
/*
* Used by callers that need to cache e.g., the datapath
* Returns the generation number in the last argument.
*/
dce_t *
{
/* Set *generationp before dropping the lock(s) that allow additions */
if (generationp != NULL)
if (!DCE_IS_CONDEMNED(dce)) {
if (generationp != NULL)
return (dce);
}
}
}
/* Not found */
return (dce);
}
/*
* Used by callers that need to cache e.g., the datapath
* Returns the generation number in the last argument.
* ifindex should only be set for link-locals
*/
dce_t *
{
/* Set *generationp before dropping the lock(s) that allow additions */
if (generationp != NULL)
if (!DCE_IS_CONDEMNED(dce)) {
if (generationp != NULL)
return (dce);
}
}
}
/* Not found */
return (dce);
}
/*
* Atomically looks for a non-default DCE, and if not found tries to create one.
* If there is no memory it returns NULL.
* When an entry is created we increase the generation number on
* the default DCE so that conn_ip_output will detect there is a new DCE.
*/
dce_t *
{
/*
* Assuming that we get fairly even distribution across all of the
* buckets, once one bucket is overly full, prune the whole cache.
*/
if (!DCE_IS_CONDEMNED(dce)) {
return (dce);
}
}
}
return (NULL);
}
/* Link into list */
/* Initialize dce_ident to be different than for the last packet */
return (dce);
}
/*
* Atomically looks for a non-default DCE, and if not found tries to create one.
* If there is no memory it returns NULL.
* When an entry is created we increase the generation number on
* the default DCE so that conn_ip_output will detect there is a new DCE.
* ifindex should only be used with link-local addresses.
*/
dce_t *
{
/* We should not create entries for link-locals w/o an ifindex */
/*
* Assuming that we get fairly even distribution across all of the
* buckets, once one bucket is overly full, prune the whole cache.
*/
if (!DCE_IS_CONDEMNED(dce)) {
return (dce);
}
}
}
return (NULL);
}
/* Link into list */
/* Initialize dce_ident to be different than for the last packet */
return (dce);
}
/*
*
* Note that we do not bump the generation number here.
* New connections will find the new uinfo.
*
* The only use of this (tcp, sctp using iulp_t) is to set rtt+rtt_sd.
*/
static void
{
/*
*
* We serialize multiple advises using dce_lock.
*/
/* Gard against setting to zero */
/*
* If there is no old cached values, initialize them
* conservatively. Set them to be (1.5 * new value).
*/
} else {
}
} else {
}
}
} else {
}
}
if (uinfo->iulp_ssthresh != 0) {
(uinfo->iulp_ssthresh +
else
}
/* We have uinfo for sure */
}
int
{
return (ENOMEM);
return (0);
}
int
{
return (ENOMEM);
return (0);
}
/* Common routine for IPv4 and IPv6 */
int
{
if (IN6_IS_ADDR_V4MAPPED_ANY(dst)) {
} else {
}
}
static void
{
/* Count how many condemned dces for kmem_cache callback */
}
/*
* Increment the generation avoiding the special condemned value
*/
void
{
if (!DCE_IS_CONDEMNED(dce)) {
if (generation == DCE_GENERATION_CONDEMNED)
}
}
/*
* Increment the generation number on all dces that have a path MTU and
* the default DCE. Used when ill_mtu or ill_mc_mtu changes.
*/
void
{
int i;
for (i = 0; i < ipst->ips_dce_hashsize; i++) {
if (isv6)
else
if (DCE_IS_CONDEMNED(dce))
continue;
}
}
}
/*
* Caller needs to do a dce_refrele since we can't do the
* dce_refrele under dcb_lock.
*/
static void
{
}
static void
{
/* Count how many condemned dces for kmem_cache callback */
if (DCE_IS_CONDEMNED(dce))
}
void
{
}
void
{
}
/* No tracing support yet hence the same as the above functions */
void
{
}
void
{
}
/* Report both the IPv4 and IPv6 DCEs. */
mblk_t *
{
int i;
/*
* make a copy of the original message
*/
/* First we do IPv4 entries */
sizeof (struct T_optmgmt_ack)];
for (i = 0; i < ipst->ips_dce_hashsize; i++) {
else
dest_cache.DestPmtu = 0;
dest_cache.DestIfindex = 0;
(char *)&dest_cache, (int)sizeof (dest_cache))) {
ip1dbg(("ip_snmp_get_mib2_ip_dce: "
"failed to allocate %u bytes\n",
(uint_t)sizeof (dest_cache)));
}
}
}
ip3dbg(("ip_snmp_get: level %d, name %d, len %d\n",
/* Copymsg failed above */
return (NULL);
}
/* Now for IPv6 */
sizeof (struct T_optmgmt_ack)];
for (i = 0; i < ipst->ips_dce_hashsize; i++) {
else
dest_cache.DestPmtu = 0;
else
dest_cache.DestIfindex = 0;
(char *)&dest_cache, (int)sizeof (dest_cache))) {
ip1dbg(("ip_snmp_get_mib2_ip_dce: "
"failed to allocate %u bytes\n",
(uint_t)sizeof (dest_cache)));
}
}
}
ip3dbg(("ip_snmp_get: level %d, name %d, len %d\n",
return (mp2ctl);
}
/*
* Remove IPv6 DCEs which refer to an ifindex that is going away.
* This is not required for correctness, but it avoids netstat -d
* showing stale stuff that will never be used.
*/
void
{
uint_t i;
for (i = 0; i < ipst->ips_dce_hashsize; i++) {
}
}
}
}