ip_ftable.c revision 67c1caee8f1e9738d11e0824aa6f3645fbc57690
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file contains consumer routines of the IPv4 forwarding engine
*/
#include <inet/ip_ftable.h>
#include <inet/ipsec_info.h>
#include <inet/ipclassifier.h>
#define IS_DEFAULT_ROUTE(ire) \
/*
* structure for passing args between ire_ftable_lookup and ire_find_best_route
*/
typedef struct ire_ftable_args_s {
int ift_type;
const ts_label_t *ift_tsl;
int ift_flags;
ip_stack_t *);
static void ire_del_host_redir(ire_t *, char *);
/*
* Lookup a route in forwarding table. A specific lookup is indicated by
* passing the required parameters and indicating the match required in the
* flag field.
*
* Looking for default route can be done in three ways
* 1) pass mask as 0 and set MATCH_IRE_MASK in flags field
* along with other matches.
* 2) pass type as IRE_DEFAULT and set MATCH_IRE_TYPE in flags
* field along with other matches.
* 3) if the destination and mask are passed as zeros.
*
* A request to return a default route if no route
* is found, can be specified by setting MATCH_IRE_DEFAULT
* in flags.
*
* It does not support recursion more than one level. It
* will do recursive lookup only when the lookup maps to
* a prefix or default route and MATCH_IRE_RECURSIVE flag is passed.
*
* If the routing table is setup to allow more than one level
* of recursion, the cleaning up cache table will not work resulting
* in invalid routing.
*
*
* NOTE : When this function returns NULL, pire has already been released.
* pire is valid only when this function successfully returns an
* ire.
*/
ire_t *
{
/*
* When we return NULL from this function, we should make
* sure that *pire is NULL so that the callers will not
* wrongly REFRELE the pire.
*/
/*
* ire_match_args() will dereference ipif MATCH_IRE_SRC or
* MATCH_IRE_ILL is set.
*/
return (NULL);
/*
* The flags argument passed to ire_ftable_lookup may cause the
* search to return, not the longest matching prefix, but the
* "best matching prefix", i.e., the longest prefix that also
* satisfies constraints imposed via the permutation of flags
* passed in. To achieve this, we invoke ire_match_args() on
* each matching leaf in the radix tree. ire_match_args is
* invoked by the callback function ire_find_best_route()
* We hold the global tree lock in read mode when calling
* rn_match_args.Before dropping the global tree lock, ensure
* that the radix node can't be deleted by incrementing ire_refcnt.
*/
return (NULL);
} else {
}
if (!IS_DEFAULT_ROUTE(ire))
goto found_ire_held;
/*
* If default route is found, see if default matching criteria
* are satisfied.
*/
if (flags & MATCH_IRE_MASK) {
/*
* we were asked to match a 0 mask, and came back with
* a default route. Ok to return it.
*/
goto found_default_ire;
}
if ((flags & MATCH_IRE_TYPE) &&
/*
* we were asked to match a default ire type. Ok to return it.
*/
goto found_default_ire;
}
if (flags & MATCH_IRE_DEFAULT) {
goto found_default_ire;
}
/*
* we found a default route, but default matching criteria
* are not specified and we are not explicitly looking for
* default.
*/
return (NULL);
/*
* round-robin only if we have more than one route in the bucket.
*/
IS_DEFAULT_ROUTE(ire) &&
ipst);
} else {
/* no route */
return (NULL);
}
}
if ((flags & MATCH_IRE_RJ_BHOLE) &&
return (ire);
}
/*
* At this point, IRE that was found must be an IRE_FORWARDTABLE
* type. If this is a recursive lookup and an IRE_INTERFACE type was
* found, return that. If it was some other IRE_FORWARDTABLE type of
* IRE (one of the prefix types), then it is necessary to fill in the
* parent IRE pointed to by pire, and then lookup the gateway address of
* the parent. For backwards compatiblity, if this lookup returns an
* IRE other than a IRE_CACHETABLE or IRE_INTERFACE, then one more level
* of lookup is done.
*/
if (flags & MATCH_IRE_RECURSIVE) {
int match_flags = MATCH_IRE_DSTONLY;
return (ire);
/*
* If we can't find an IRE_INTERFACE or the caller has not
* asked for pire, we need to REFRELE the save_ire.
*/
/*
* Currently MATCH_IRE_ILL is never used with
* (MATCH_IRE_RECURSIVE | MATCH_IRE_DEFAULT) while
* sending out packets as MATCH_IRE_ILL is used only
* for communicating with on-link hosts. We can't assert
* that here as RTM_GET calls this function with
* MATCH_IRE_ILL | MATCH_IRE_DEFAULT | MATCH_IRE_RECURSIVE.
* We have already used the MATCH_IRE_ILL in determining
* the right prefix route at this point. To match the
* behavior of how we locate routes while sending out
* packets, we don't want to use MATCH_IRE_ILL below
* while locating the interface route.
*
* ire_ftable_lookup may end up with an incomplete IRE_CACHE
* entry for the gateway (i.e., one for which the
* ire_nce->nce_state is not yet ND_REACHABLE). If the caller
* has specified MATCH_IRE_COMPLETE, such entries will not
* be returned; instead, we return the IF_RESOLVER ire.
*/
(flags & MATCH_IRE_COMPLETE))) {
/*
* Do not release the parent ire if MATCH_IRE_PARENT
* is set. Also return it via ire.
*/
}
if (flags & MATCH_IRE_PARENT) {
/*
* Need an extra REFHOLD, if the parent
* ire is returned via both ire and
* pire.
*/
}
} else {
}
if (!found_incomplete)
return (ire);
}
/*
* If the caller did not ask for pire, release
* it now.
*/
}
return (ire);
}
(IRE_CACHETABLE | IRE_INTERFACE)),
(flags & MATCH_IRE_COMPLETE))) {
/*
* Do not release the parent ire if MATCH_IRE_PARENT
* is set. Also return it via ire.
*/
}
if (flags & MATCH_IRE_PARENT) {
/*
* Need an extra REFHOLD, if the
* parent ire is returned via both
* ire and pire.
*/
}
} else {
}
return (ire);
/*
* If the caller did not ask for pire, release
* it now.
*/
}
return (ire);
}
return (ire);
}
/*
* Find an IRE_OFFSUBNET IRE entry for the multicast address 'group'
* that goes through 'ipif'. As a fallback, a route that goes through
* ipif->ipif_ill can be returned.
*/
ire_t *
{
return (NULL);
continue;
}
case IRE_DEFAULT:
case IRE_PREFIX:
case IRE_HOST:
}
return (ire);
}
}
break;
case IRE_IF_NORESOLVER:
case IRE_IF_RESOLVER:
}
return (ire);
}
break;
}
}
return (save_ire);
}
/*
* Find an IRE_INTERFACE for the multicast group.
* Allows different routes for multicast addresses
* in the unicast routing table (akin to 224.0.0.0 but could be more specific)
* which point at different interfaces. This is used when IP_MULTICAST_IF
* isn't specified (when sending) and when IP_ADD_MEMBERSHIP doesn't
* specify the interface to join on.
*
*/
ire_t *
{
int match_flags = MATCH_IRE_TYPE;
/* We search a resolvable ire in case of multirouting. */
/*
* If the route is not resolvable, the looked up ire
* may be changed here. In that case, ire_multirt_lookup()
* IRE_REFRELE the original ire and change it.
*/
}
return (NULL);
/*
* Make sure we follow ire_ipif.
*
* We need to determine the interface route through
* which the gateway will be reached. We don't really
* care which interface is picked if the interface is
* part of a group.
*/
}
case IRE_DEFAULT:
case IRE_PREFIX:
case IRE_HOST:
return (ire);
case IRE_IF_NORESOLVER:
case IRE_IF_RESOLVER:
return (ire);
default:
return (NULL);
}
}
/*
* Delete the passed in ire if the gateway addr matches
*/
void
{
}
/*
* Search for all HOST REDIRECT routes that are
* pointing at the specified gateway and
* delete them. This routine is called only
* when a default gateway is going away.
*/
void
{
}
struct ihandle_arg {
};
static int
{
return (1);
}
}
return (0);
}
/*
* Locate the interface ire that is tied to the cache ire 'cire' via
* cire->ire_ihandle.
*
* We are trying to create the cache ire for an onlink destn. or
* gateway in 'cire'. We are called from ire_add_v4() in the IRE_IF_RESOLVER
* case, after the ire has come back from ARP.
*/
ire_t *
{
int match_flags;
struct ihandle_arg ih;
/*
* We don't need to specify the zoneid to ire_ftable_lookup() below
* because the ihandle refers to an ipif which can be in only one zone.
*/
/*
* We know that the mask of the interface ire equals cire->ire_cmask.
* (When ip_newroute() created 'cire' for an on-link destn. it set its
* cmask from the interface ire's mask)
*/
return (ire);
/*
* If we didn't find an interface ire above, we can't declare failure.
* For backwards compatibility, we need to support prefix routes
* pointing to next hop gateways that are not on-link.
*
* In the resolver/noresolver case, ip_newroute() thinks it is creating
* the cache ire for an onlink destination in 'cire'. But 'cire' is
* not actually onlink, because ire_ftable_lookup() cheated it, by
* doing ire_route_lookup() twice and returning an interface ire.
*
* Eg. default - gw1 (line 1)
* gw1 - gw2 (line 2)
* gw2 - hme0 (line 3)
*
* In the above example, ip_newroute() tried to create the cache ire
* 'cire' for gw1, based on the interface route in line 3. The
* ire_ftable_lookup() above fails, because there is no interface route
* to reach gw1. (it is gw2). We fall thru below.
*
* Do a brute force search based on the ihandle in a subset of the
* forwarding tables, corresponding to cire->ire_cmask. Otherwise
* things become very complex, since we don't have 'pire' in this
* case. (Also note that this method is not possible in the offlink
* case because we don't know the mask)
*/
}
/*
* IRE iterator used by ire_ftable_lookup[_v6]() to process multiple default
* routes. Given a starting point in the hash list (ire_origin), walk the IREs
* in the bucket skipping default interface routes and deleted entries.
* Returns the next IRE (unheld), or NULL when we're back to the starting point.
* Assumes that the caller holds a reference on the IRE bucket.
*/
ire_t *
{
do {
if (ire == ire_origin)
return (NULL);
return (ire);
}
static ipif_t *
{
/*
* Pick the best source address from dst_ill.
*
* 1) If it is part of a multipathing group, we would
* like to spread the inbound packets across different
* interfaces. ipif_select_source picks a random source
* across the different ills in the group.
*
* 2) If it is not part of a multipathing group, we try
* to pick the source address from the destination
* route. Clustering assumes that when we have multiple
* prefixes hosted on an interface, the prefix of the
* source address matches the prefix of the destination
* route. We do this only if the address is not
* DEPRECATED.
*
* 3) If the conn is in a different zone than the ire, we
* need to pick a source address from the right zone.
*
* NOTE : If we hit case (1) above, the prefix of the source
* address picked may not match the prefix of the
* destination routes prefix as ipif_select_source
* does not look at "dst" while picking a source
* address.
* If we want the same behavior as (2), we will need
* to change the behavior of ipif_select_source.
*/
/*
* The RTF_SETSRC flag is set in the parent ire (sire).
* Check that the ipif matching the requested source
* address still exists.
*/
return (src_ipif);
}
(dst_ill->ill_usesrc_ifindex != 0)) {
return (NULL);
} else {
/* hold src_ipif for uniformity */
}
return (src_ipif);
}
/*
* This function is called by ip_rput_noire() and ip_fast_forward()
* to resolve the route of incoming packet that needs to be forwarded.
* If the ire of the nexthop is not already in the cachetable, this
* routine will insert it to the table, but won't trigger ARP resolution yet.
* Thus unlike ip_newroute, this function adds incomplete ires to
* the cachetable. ARP resolution for these ires are delayed until
* after all of the packet processing is completed and its ready to
* be sent out on the wire, Eventually, the packet transmit routine
* ip_xmit_v4() attempts to send a packet to the driver. If it finds
* that there is no link layer information, it will do the arp
* resolution and queue the packet in ire->ire_nce->nce_qd_mp and
* then send it out once the arp resolution is over
* (see ip_xmit_v4()->ire_arpresolve()). This scheme is similar to
*
* In future, the insertion of incomplete ires in the cachetable should
* be implemented in hostpath as well, as doing so will greatly reduce
* the existing complexity for code paths that depend on the context of
* the sender (such as IPsec).
*
* Thus this scheme of adding incomplete ires in cachetable in forwarding
* path can be used as a template for simplifying the hostpath.
*/
ire_t *
{
int error;
if (supplied_ire != NULL) {
/* We have arrived here from ipfil_sendpkt */
ire = supplied_ire;
goto create_irecache;
}
goto icmp_err_ret;
}
/*
* If we encounter CGTP, we should have the caller use
* ip_newroute to resolve multirt instead of this function.
* CGTP specs explicitly state that it can't be used with routers.
* This essentially prevents insertion of incomplete RTF_MULTIRT
* ires in cachetable.
*/
if (ip_cgtp_filter &&
ip3dbg(("ire_forward: packet is to be multirouted- "
"handing it to ip_newroute\n"));
/*
* Inform caller about encountering of multirt so that
* ip_newroute() can be called.
*/
*check_multirt = B_TRUE;
return (NULL);
}
*check_multirt = B_FALSE;
/*
* Verify that the returned IRE does not have either
* the RTF_REJECT or RTF_BLACKHOLE flags set and that the IRE is
* either an IRE_CACHE, IRE_IF_NORESOLVER or IRE_IF_RESOLVER.
*/
ip3dbg(("ire 0x%p is not cache/resolver/noresolver\n",
(void *)ire));
goto icmp_err_ret;
}
/*
* If we already have a fully resolved IRE CACHE of the
* nexthop router, just hand over the cache entry
* and we are done.
*/
/*
* If we are using this ire cache entry as a
* gateway to forward packets, chances are we
* will be using it again. So turn off
* the temporary flag, thus reducing its
* chances of getting deleted frequently.
*/
irb->irb_tmp_ire_cnt--;
}
}
return (ire);
}
/*
* Increment the ire_ob_pkt_count field for ire if it is an
* INTERFACE (IF_RESOLVER or IF_NORESOLVER) IRE type, and
* increment the same for the parent IRE, sire, if it is some
* sort of prefix IRE (which includes DEFAULT, PREFIX, HOST
* and HOST_REDIRECT).
*/
}
/*
* sire must be either IRE_CACHETABLE OR IRE_INTERFACE type
*/
(IRE_CACHETABLE | IRE_INTERFACE)) == 0);
}
/* Obtain dst_ill */
ip2dbg(("ire_forward no dst ill; ire 0x%p\n",
(void *)ire));
goto icmp_err_ret;
}
/* Now obtain the src_ipif */
goto icmp_err_ret;
case IRE_IF_NORESOLVER:
/* create ire_cache for ire_addr endpoint */
case IRE_IF_RESOLVER:
/*
* We have the IRE_IF_RESOLVER of the nexthop gateway
* and now need to build a IRE_CACHE for it.
* In this case, we have the following :
*
* 1) src_ipif - used for getting a source address.
*
* means packets using the IRE_CACHE that we will build
* here will go out on dst_ill.
*
* 3) sire may or may not be NULL. But, the IRE_CACHE that is
* to be created will only be tied to the IRE_INTERFACE
* that was derived from the ire_ihandle field.
*
* If sire is non-NULL, it means the destination is
* off-link and we will first create the IRE_CACHE for the
* gateway.
*/
(!OK_RESOLVER_MP(res_mp))) {
goto out;
}
/*
* To be at this point in the code with a non-zero gw
* means that dst is reachable through a gateway that
* we have never resolved. By changing dst to the gw
* addr we resolve the gateway first.
*/
if (gw != INADDR_ANY) {
/*
* The source ipif that was determined above was
* relative to the destination address, not the
* gateway's. If src_ipif was not taken out of
* the IRE_IF_RESOLVER entry, we'll need to call
* ipif_select_source() again.
*/
goto icmp_err_ret;
}
gw = INADDR_ANY;
}
/*
* dst has been set to the address of the nexthop.
*
* TSol note: get security attributes of the nexthop;
* Note that the nexthop may either be a gateway, or the
* packet destination itself; Detailed explanation of
* issues involved is provided in the IRE_IF_NORESOLVER
* logic in ip_newroute().
*/
/*
* create an incomplete ire-cache with a null dlureq_mp.
* The dlureq_mp will be created in ire_arpresolve.
*/
ire = ire_create(
NULL,
&save_ire->ire_max_frag),
NULL,
IRE_CACHE, /* IRE type */
NULL,
NULL,
0,
0,
NULL,
ipst);
/* add the incomplete ire: */
ip1dbg(("setting max_frag to %d in ire 0x%p\n",
} else {
goto icmp_err_ret;
}
} else {
}
}
break;
default:
break;
}
out:
return (ire);
}
/* caller needs to send icmp error message */
return (NULL);
}
/*
* Obtain the rt_entry and rt_irb for the route to be added to
* the ips_ip_ftable.
* First attempt to add a node to the radix tree via rn_addroute. If the
* route already exists, return the bucket for the existing route.
*
* Locking notes: Need to hold the global radix tree lock in write mode to
* add a radix node. To prevent the node from being deleted, ire_get_bucket()
* returns with a ref'ed irb_t. The ire itself is added in ire_add_v4()
* while holding the irb_lock, but not the radix tree lock.
*/
irb_t *
{
struct radix_node *rn;
/* first try to see if route exists (based on rtalloc1) */
/*
* add the route. based on BSD's rtrequest1(RTM_ADD)
*/
/* found a non-root match */
}
}
}
return (irb);
}
/*
* This function is used when the caller wants to know the outbound
* interface for a packet given only the address.
* If this is a offlink IP address and there are multiple
* routes to this destination, this routine will utilise the
* first route it finds to IP address
* Return values:
* 0 - FAILURE
* nonzero - ifindex
*/
{
netstack_t *ns;
else
/*
* For exclusive stacks we set the zoneid to zero
* since IP uses the global zoneid in the exclusive stacks.
*/
}
return (ifindex);
}
/*
* Routine to find the route to a destination. If a ifindex is supplied
* it tries to match the the route to the corresponding ipif for the ifindex
*/
static ire_t *
{
int match_flags;
/* XXX pass NULL tsl for now */
} else {
}
return (ire);
}
/*
* This routine is called by IP Filter to send a packet out on the wire
* to a specified V4 dst (which may be onlink or offlink). The ifindex may or
* may not be 0. A non-null ifindex indicates IP Filter has stipulated
* an outgoing interface and requires the nexthop to be on that interface.
* IP WILL NOT DO the following to the data packet before sending it out:
* a. manipulate ttl
* b. checksuming
* c. ipsec work
* d. fragmentation
*
* Return values:
* 0: IP was able to send of the data pkt
* ECOMM: Could not send packet
* ENONET No route to dst. It is up to the caller
* to send icmp unreachable error message,
* EINPROGRESS The macaddr of the onlink dst or that
* of the offlink dst's nexthop needs to get
* resolved before packet can be sent to dst.
* Thus transmission is not guaranteed.
*
*/
int
{
int value;
int match_flags;
netstack_t *ns;
else
/*
* For exclusive stacks we set the zoneid to zero
* since IP uses the global zoneid in the exclusive stacks.
*/
} else {
/*
* We dont have support for V6 yet. It will be provided
* once RFE 6399103 has been delivered.
* Until then, for V6 dsts, IP Filter will not call
* this function. Instead the netinfo framework provides
* its own code path, in ip_inject_impl(), to achieve
* what it needs to do, for the time being.
*/
ip1dbg(("ipfil_sendpkt: no V6 support \n"));
goto discard;
}
/*
* Lets get the ire. We might get the ire cache entry,
* or the ire,sire pair needed to create the cache entry.
* XXX pass NULL tsl for now.
*/
if (ifindex == 0) {
/* There is no supplied index. So use the FIB info */
match_flags, ipst);
} else {
/*
* If supplied ifindex is non-null, the only valid
* nexthop is one off of the interface or group corresponding
* to the specified ifindex.
*/
} else {
/* Fallback to group names if hook_emulation set */
if (ipst->ips_ipmp_hook_emulation) {
}
ip1dbg(("ipfil_sendpkt: Could not find"
" route to dst\n"));
goto discard;
}
}
}
/*
* Verify that the returned IRE is non-null and does
* not have either the RTF_REJECT or RTF_BLACKHOLE
* flags set and that the IRE is either an IRE_CACHE,
* IRE_IF_NORESOLVER or IRE_IF_RESOLVER.
*/
/*
* Either ire could not be found or we got
* an invalid one
*/
ip1dbg(("ipfil_sendpkt: Could not find route to dst\n"));
goto discard;
}
/* IP Filter and CGTP dont mix. So bail out if CGTP is on */
if (ip_cgtp_filter &&
ip1dbg(("ipfil_sendpkt: IPFilter does not work with CGTP\n"));
goto discard;
}
/*
* If needed, we will create the ire cache entry for the
* nexthop, resolve its link-layer address and then send
* the packet out without ttl, checksumming, IPSec processing.
*/
case IRE_IF_NORESOLVER:
case IRE_CACHE:
}
break;
case IRE_IF_RESOLVER:
/*
* Call ire_forward(). This function
* will, create the ire cache entry of the
* the nexthop and adds this incomplete ire
* to the ire cache table
*/
ip1dbg(("ipfil_sendpkt: failed to create the"
" ire cache entry \n"));
goto discard;
}
break;
}
/*
* Now that we have the ire cache entry of the nexthop, call
* ip_xmit_v4() to trigger mac addr resolution
* if necessary and send it once ready.
*/
/*
* At this point, the reference for these have already been
* them to NULL to make sure we dont drop the references
* again in case ip_xmit_v4() returns with either SEND_FAILED
* or LLHDR_RESLV_FAILED
*/
switch (value) {
case SEND_FAILED:
ip1dbg(("ipfil_sendpkt: Send failed\n"));
break;
case LLHDR_RESLV_FAILED:
ip1dbg(("ipfil_sendpkt: Link-layer resolution"
" failed\n"));
break;
case LOOKUP_IN_PROGRESS:
return (EINPROGRESS);
case SEND_PASSED:
return (0);
}
} else {
}
return (value);
}
/* ire_walk routine invoked for ip_ire_report for each IRE. */
void
{
char buf1[16];
char buf2[16];
char buf3[16];
char buf4[16];
int ref;
return;
if (buf_len <= 0)
return;
/* Number of active references of this ire */
/* "inbound" to a non local address is a forward */
fo_pkt_count = 0;
ib_pkt_count = 0;
}
"%s %s %s %s %05d %05ld %06ld %08d %03d %06d %09d %09d %06d %08d "
"%04d %08d %08d %d/%d/%d %s\n",
(int)ire->ire_zoneid,
} else {
}
}
/*
* callback function provided by ire_ftable_lookup when calling
* rn_match_args(). Invoke ire_match_args on each matching leaf node in
* the radix tree.
*/
{
if (irb_ptr->irb_ire_cnt == 0)
return (B_FALSE);
continue;
else
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* ftable irb_t structures are dynamically allocated, and we need to
* check if the irb_t (and associated ftable tree attachment) needs to
* be cleaned up when the irb_refcnt goes to 0. The conditions that need
* be verified are:
* - no other walkers of the irebucket, i.e., quiescent irb_refcnt,
* - no other threads holding references to ire's in the bucket,
* i.e., irb_nire == 0
* - no active ire's in the bucket, i.e., irb_ire_cnt == 0
* - need to hold the global tree lock and irb_lock in write mode.
*/
void
{
for (;;) {
/*
* Someone has a reference to this radix node
* or there is some bucket walker.
*/
irb->irb_refcnt--;
return;
} else {
/*
* There is no other walker, nor is there any
* other thread that holds a direct ref to this
* radix node. Do the clean up if needed. Call
* to ire_unlink will clear the IRB_MARK_CONDEMNED flag
*/
/*
* more CONDEMNED entries could have
* been added while we dropped the lock,
* so we have to re-check.
*/
continue;
}
/*
* Now check if there are still any ires
* associated with this radix node.
*/
/*
* someone is still holding on
* to ires in this bucket
*/
irb->irb_refcnt--;
return;
} else {
/*
* Everything is clear. Zero walkers,
* Zero threads with a ref to this
* radix node, Zero ires associated with
* this radix node. Due to lock order,
* check the above conditions again
* after grabbing all locks in the right order
*/
if (irb_inactive(irb))
return;
/*
* irb_inactive could not free the irb.
* See if there are any walkers, if not
* try to clean up again.
*/
}
}
}
}
/*
* IRE iterator used by ire_ftable_lookup() to process multiple default
* routes. Given a starting point in the hash list (ire_origin), walk the IREs
* in the bucket skipping default interface routes and deleted entries.
* Returns the next IRE (unheld), or NULL when we're back to the starting point.
* Assumes that the caller holds a reference on the IRE bucket.
*
* In the absence of good IRE_DEFAULT routes, this function will return
* the first IRE_INTERFACE route found (if any).
*/
ire_t *
{
if (ire_origin != NULL) {
}
if (ire_origin == NULL) {
/*
* first time through routine, or we dropped off the end
* of list.
*/
}
(ire_t *), ire_origin);
/*
* Round-robin the routers list looking for a route that
* matches the passed in parameters.
* We start with the ire we found above and we walk the hash
* list until we're back where we started. It doesn't matter if
* routes are added or deleted by other threads - we know this
* ire will stay in the list because we hold a reference on the
* ire bucket.
*/
ire = ire_origin;
int match_flags = 0;
goto next_ire;
goto next_ire;
/*
* keep looking to see if there is a non-interface
* default ire, but save this one as a last resort.
*/
goto next_ire;
}
return (ire);
}
/*
* When we're in a local zone, we're only
* interested in routers that are
* reachable through ipifs within our zone.
*/
}
match_flags, ipst);
return (ire);
}
if (ire == ire_origin)
break;
}
return (maybe_ire);
}
void
{
}
void
{
}