⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ip6_fib.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
				node_free(sfn);				goto st_failure;			}			/* Now link new subtree to main tree */			sfn->parent = fn;			fn->subtree = sfn;		} else {			sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,					sizeof(struct in6_addr), rt->rt6i_src.plen,					offsetof(struct rt6_info, rt6i_src));			if (sn == NULL)				goto st_failure;		}		if (fn->leaf == NULL) {			fn->leaf = rt;			atomic_inc(&rt->rt6i_ref);		}		fn = sn;	}#endif	err = fib6_add_rt2node(fn, rt, info);	if (err == 0) {		fib6_start_gc(rt);		if (!(rt->rt6i_flags&RTF_CACHE))			fib6_prune_clones(pn, rt);	}out:	if (err) {#ifdef CONFIG_IPV6_SUBTREES		/*		 * If fib6_add_1 has cleared the old leaf pointer in the		 * super-tree leaf node we have to find a new one for it.		 */		if (pn != fn && !pn->leaf && !(pn->fn_flags & RTN_RTINFO)) {			pn->leaf = fib6_find_prefix(pn);#if RT6_DEBUG >= 2			if (!pn->leaf) {				BUG_TRAP(pn->leaf != NULL);				pn->leaf = &ip6_null_entry;			}#endif			atomic_inc(&pn->leaf->rt6i_ref);		}#endif		dst_free(&rt->u.dst);	}	return err;#ifdef CONFIG_IPV6_SUBTREES	/* Subtree creation failed, probably main tree node	   is orphan. If it is, shoot it.	 */st_failure:	if (fn && !(fn->fn_flags & (RTN_RTINFO|RTN_ROOT)))		fib6_repair_tree(fn);	dst_free(&rt->u.dst);	return err;#endif}/* *	Routing tree lookup * */struct lookup_args {	int		offset;		/* key offset on rt6_info	*/	struct in6_addr	*addr;		/* search key			*/};static struct fib6_node * fib6_lookup_1(struct fib6_node *root,					struct lookup_args *args){	struct fib6_node *fn;	__be32 dir;	if (unlikely(args->offset == 0))		return NULL;	/*	 *	Descend on a tree	 */	fn = root;	for (;;) {		struct fib6_node *next;		dir = addr_bit_set(args->addr, fn->fn_bit);		next = dir ? fn->right : fn->left;		if (next) {			fn = next;			continue;		}		break;	}	while(fn) {		if (FIB6_SUBTREE(fn) || fn->fn_flags & RTN_RTINFO) {			struct rt6key *key;			key = (struct rt6key *) ((u8 *) fn->leaf +						 args->offset);			if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) {#ifdef CONFIG_IPV6_SUBTREES				if (fn->subtree)					fn = fib6_lookup_1(fn->subtree, args + 1);#endif				if (!fn || fn->fn_flags & RTN_RTINFO)					return fn;			}		}		if (fn->fn_flags & RTN_ROOT)			break;		fn = fn->parent;	}	return NULL;}struct fib6_node * fib6_lookup(struct fib6_node *root, struct in6_addr *daddr,			       struct in6_addr *saddr){	struct fib6_node *fn;	struct lookup_args args[] = {		{			.offset = offsetof(struct rt6_info, rt6i_dst),			.addr = daddr,		},#ifdef CONFIG_IPV6_SUBTREES		{			.offset = offsetof(struct rt6_info, rt6i_src),			.addr = saddr,		},#endif		{			.offset = 0,	/* sentinel */		}	};	fn = fib6_lookup_1(root, daddr ? args : args + 1);	if (fn == NULL || fn->fn_flags & RTN_TL_ROOT)		fn = root;	return fn;}/* *	Get node with specified destination prefix (and source prefix, *	if subtrees are used) */static struct fib6_node * fib6_locate_1(struct fib6_node *root,					struct in6_addr *addr,					int plen, int offset){	struct fib6_node *fn;	for (fn = root; fn ; ) {		struct rt6key *key = (struct rt6key *)((u8 *)fn->leaf + offset);		/*		 *	Prefix match		 */		if (plen < fn->fn_bit ||		    !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit))			return NULL;		if (plen == fn->fn_bit)			return fn;		/*		 *	We have more bits to go		 */		if (addr_bit_set(addr, fn->fn_bit))			fn = fn->right;		else			fn = fn->left;	}	return NULL;}struct fib6_node * fib6_locate(struct fib6_node *root,			       struct in6_addr *daddr, int dst_len,			       struct in6_addr *saddr, int src_len){	struct fib6_node *fn;	fn = fib6_locate_1(root, daddr, dst_len,			   offsetof(struct rt6_info, rt6i_dst));#ifdef CONFIG_IPV6_SUBTREES	if (src_len) {		BUG_TRAP(saddr!=NULL);		if (fn && fn->subtree)			fn = fib6_locate_1(fn->subtree, saddr, src_len,					   offsetof(struct rt6_info, rt6i_src));	}#endif	if (fn && fn->fn_flags&RTN_RTINFO)		return fn;	return NULL;}/* *	Deletion * */static struct rt6_info * fib6_find_prefix(struct fib6_node *fn){	if (fn->fn_flags&RTN_ROOT)		return &ip6_null_entry;	while(fn) {		if(fn->left)			return fn->left->leaf;		if(fn->right)			return fn->right->leaf;		fn = FIB6_SUBTREE(fn);	}	return NULL;}/* *	Called to trim the tree of intermediate nodes when possible. "fn" *	is the node we want to try and remove. */static struct fib6_node * fib6_repair_tree(struct fib6_node *fn){	int children;	int nstate;	struct fib6_node *child, *pn;	struct fib6_walker_t *w;	int iter = 0;	for (;;) {		RT6_TRACE("fixing tree: plen=%d iter=%d\n", fn->fn_bit, iter);		iter++;		BUG_TRAP(!(fn->fn_flags&RTN_RTINFO));		BUG_TRAP(!(fn->fn_flags&RTN_TL_ROOT));		BUG_TRAP(fn->leaf==NULL);		children = 0;		child = NULL;		if (fn->right) child = fn->right, children |= 1;		if (fn->left) child = fn->left, children |= 2;		if (children == 3 || FIB6_SUBTREE(fn)#ifdef CONFIG_IPV6_SUBTREES		    /* Subtree root (i.e. fn) may have one child */		    || (children && fn->fn_flags&RTN_ROOT)#endif		    ) {			fn->leaf = fib6_find_prefix(fn);#if RT6_DEBUG >= 2			if (fn->leaf==NULL) {				BUG_TRAP(fn->leaf);				fn->leaf = &ip6_null_entry;			}#endif			atomic_inc(&fn->leaf->rt6i_ref);			return fn->parent;		}		pn = fn->parent;#ifdef CONFIG_IPV6_SUBTREES		if (FIB6_SUBTREE(pn) == fn) {			BUG_TRAP(fn->fn_flags&RTN_ROOT);			FIB6_SUBTREE(pn) = NULL;			nstate = FWS_L;		} else {			BUG_TRAP(!(fn->fn_flags&RTN_ROOT));#endif			if (pn->right == fn) pn->right = child;			else if (pn->left == fn) pn->left = child;#if RT6_DEBUG >= 2			else BUG_TRAP(0);#endif			if (child)				child->parent = pn;			nstate = FWS_R;#ifdef CONFIG_IPV6_SUBTREES		}#endif		read_lock(&fib6_walker_lock);		FOR_WALKERS(w) {			if (child == NULL) {				if (w->root == fn) {					w->root = w->node = NULL;					RT6_TRACE("W %p adjusted by delroot 1\n", w);				} else if (w->node == fn) {					RT6_TRACE("W %p adjusted by delnode 1, s=%d/%d\n", w, w->state, nstate);					w->node = pn;					w->state = nstate;				}			} else {				if (w->root == fn) {					w->root = child;					RT6_TRACE("W %p adjusted by delroot 2\n", w);				}				if (w->node == fn) {					w->node = child;					if (children&2) {						RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);						w->state = w->state>=FWS_R ? FWS_U : FWS_INIT;					} else {						RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);						w->state = w->state>=FWS_C ? FWS_U : FWS_INIT;					}				}			}		}		read_unlock(&fib6_walker_lock);		node_free(fn);		if (pn->fn_flags&RTN_RTINFO || FIB6_SUBTREE(pn))			return pn;		rt6_release(pn->leaf);		pn->leaf = NULL;		fn = pn;	}}static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp,			   struct nl_info *info){	struct fib6_walker_t *w;	struct rt6_info *rt = *rtp;	RT6_TRACE("fib6_del_route\n");	/* Unlink it */	*rtp = rt->u.dst.rt6_next;	rt->rt6i_node = NULL;	rt6_stats.fib_rt_entries--;	rt6_stats.fib_discarded_routes++;	/* Reset round-robin state, if necessary */	if (fn->rr_ptr == rt)		fn->rr_ptr = NULL;	/* Adjust walkers */	read_lock(&fib6_walker_lock);	FOR_WALKERS(w) {		if (w->state == FWS_C && w->leaf == rt) {			RT6_TRACE("walker %p adjusted by delroute\n", w);			w->leaf = rt->u.dst.rt6_next;			if (w->leaf == NULL)				w->state = FWS_U;		}	}	read_unlock(&fib6_walker_lock);	rt->u.dst.rt6_next = NULL;	if (fn->leaf == NULL && fn->fn_flags&RTN_TL_ROOT)		fn->leaf = &ip6_null_entry;	/* If it was last route, expunge its radix tree node */	if (fn->leaf == NULL) {		fn->fn_flags &= ~RTN_RTINFO;		rt6_stats.fib_route_nodes--;		fn = fib6_repair_tree(fn);	}	if (atomic_read(&rt->rt6i_ref) != 1) {		/* This route is used as dummy address holder in some split		 * nodes. It is not leaked, but it still holds other resources,		 * which must be released in time. So, scan ascendant nodes		 * and replace dummy references to this route with references		 * to still alive ones.		 */		while (fn) {			if (!(fn->fn_flags&RTN_RTINFO) && fn->leaf == rt) {				fn->leaf = fib6_find_prefix(fn);				atomic_inc(&fn->leaf->rt6i_ref);				rt6_release(rt);			}			fn = fn->parent;		}		/* No more references are possible at this point. */		if (atomic_read(&rt->rt6i_ref) != 1) BUG();	}	inet6_rt_notify(RTM_DELROUTE, rt, info);	rt6_release(rt);}int fib6_del(struct rt6_info *rt, struct nl_info *info){	struct fib6_node *fn = rt->rt6i_node;	struct rt6_info **rtp;#if RT6_DEBUG >= 2	if (rt->u.dst.obsolete>0) {		BUG_TRAP(fn==NULL);		return -ENOENT;	}#endif	if (fn == NULL || rt == &ip6_null_entry)		return -ENOENT;	BUG_TRAP(fn->fn_flags&RTN_RTINFO);	if (!(rt->rt6i_flags&RTF_CACHE)) {		struct fib6_node *pn = fn;#ifdef CONFIG_IPV6_SUBTREES		/* clones of this route might be in another subtree */		if (rt->rt6i_src.plen) {			while (!(pn->fn_flags&RTN_ROOT))				pn = pn->parent;			pn = pn->parent;		}#endif		fib6_prune_clones(pn, rt);	}	/*	 *	Walk the leaf entries looking for ourself	 */	for (rtp = &fn->leaf; *rtp; rtp = &(*rtp)->u.dst.rt6_next) {		if (*rtp == rt) {			fib6_del_route(fn, rtp, info);			return 0;		}	}	return -ENOENT;}/* *	Tree traversal function. * *	Certainly, it is not interrupt safe. *	However, it is internally reenterable wrt itself and fib6_add/fib6_del. *	It means, that we can modify tree during walking *	and use this function for garbage collection, clone pruning, *	cleaning tree when a device goes down etc. etc. * *	It guarantees that every node will be traversed, *	and that it will be traversed only once. * *	Callback function w->func may return: *	0 -> continue walking. *	positive value -> walking is suspended (used by tree dumps, *	and probably by gc, if it will be split to several slices) *	negative value -> terminate walking. * *	The function itself returns: *	0   -> walk is complete. *	>0  -> walk is incomplete (i.e. suspended) *	<0  -> walk is terminated by an error. */static int fib6_walk_continue(struct fib6_walker_t *w){	struct fib6_node *fn, *pn;	for (;;) {		fn = w->node;		if (fn == NULL)			return 0;		if (w->prune && fn != w->root &&		    fn->fn_flags&RTN_RTINFO && w->state < FWS_C) {			w->state = FWS_C;			w->leaf = fn->leaf;		}		switch (w->state) {#ifdef CONFIG_IPV6_SUBTREES		case FWS_S:			if (FIB6_SUBTREE(fn)) {				w->node = FIB6_SUBTREE(fn);				continue;			}			w->state = FWS_L;#endif		case FWS_L:			if (fn->left) {				w->node = fn->left;				w->state = FWS_INIT;				continue;			}			w->state = FWS_R;		case FWS_R:			if (fn->right) {				w->node = fn->right;				w->state = FWS_INIT;				continue;			}			w->state = FWS_C;			w->leaf = fn->leaf;		case FWS_C:			if (w->leaf && fn->fn_flags&RTN_RTINFO) {				int err = w->func(w);				if (err)					return err;				continue;			}			w->state = FWS_U;		case FWS_U:			if (fn == w->root)				return 0;			pn = fn->parent;			w->node = pn;#ifdef CONFIG_IPV6_SUBTREES			if (FIB6_SUBTREE(pn) == fn) {				BUG_TRAP(fn->fn_flags&RTN_ROOT);				w->state = FWS_L;				continue;			}#endif			if (pn->left == fn) {				w->state = FWS_R;				continue;			}			if (pn->right == fn) {				w->state = FWS_C;				w->leaf = w->node->leaf;				continue;			}#if RT6_DEBUG >= 2			BUG_TRAP(0);#endif		}	}}static int fib6_walk(struct fib6_walker_t *w){	int res;	w->state = FWS_INIT;	w->node = w->root;	fib6_walker_link(w);	res = fib6_walk_continue(w);	if (res <= 0)		fib6_walker_unlink(w);	return res;}static int fib6_clean_node(struct fib6_walker_t *w){	int res;	struct rt6_info *rt;	struct fib6_cleaner_t *c = container_of(w, struct fib6_cleaner_t, w);	for (rt = w->leaf; rt; rt = rt->u.dst.rt6_next) {		res = c->func(rt, c->arg);		if (res < 0) {			w->leaf = rt;			res = fib6_del(rt, NULL);			if (res) {#if RT6_DEBUG >= 2				printk(KERN_DEBUG "fib6_clean_node: del failed: rt=%p@%p err=%d\n", rt, rt->rt6i_node, res);#endif				continue;			}			return 0;		}		BUG_TRAP(res==0);	}	w->leaf = rt;	return 0;}/* *	Convenient frontend to tree walker. * *	func is called on each route. *		It may return -1 -> delete this route. *		              0  -> continue walking * *	prune==1 -> only immediate children of node (certainly, *	ignoring pure split nodes) will be scanned. */static void fib6_clean_tree(struct fib6_node *root,			    int (*func)(struct rt6_info *, void *arg),			    int prune, void *arg){	struct fib6_cleaner_t c;	c.w.root = root;	c.w.func = fib6_clean_node;	c.w.prune = prune;	c.func = func;	c.arg = arg;	fib6_walk(&c.w);}void fib6_clean_all(int (*func)(struct rt6_info *, void *arg),		    int prune, void *arg){	struct fib6_table *table;	struct hlist_node *node;	unsigned int h;	rcu_read_lock();	for (h = 0; h < FIB_TABLE_HASHSZ; h++) {		hlist_for_each_entry_rcu(table, node, &fib_table_hash[h],					 tb6_hlist) {			write_lock_bh(&table->tb6_lock);			fib6_clean_tree(&table->tb6_root, func, prune, arg);			write_unlock_bh(&table->tb6_lock);		}	}	rcu_read_unlock();}static int fib6_prune_clone(struct rt6_info *rt, void *arg){	if (rt->rt6i_flags & RTF_CACHE) {		RT6_TRACE("pruning clone %p\n", rt);		return -1;	}	return 0;}static void fib6_prune_clones(struct fib6_node *fn, struct rt6_info *rt){	fib6_clean_tree(fn, fib6_prune_clone, 1, rt);}/* *	Garbage collection */static struct fib6_gc_args{	int			timeout;	int			more;} gc_args;static int fib6_age(struct rt6_info *rt, void *arg){	unsigned long now = jiffies;	/*	 *	check addrconf expiration here.	 *	Routes are expired even if they are in use.	 *	 *	Also age clones. Note, that clones are aged out	 *	only if they are not in use now.	 */	if (rt->rt6i_flags&RTF_EXPIRES && rt->rt6i_expires) {		if (time_after(now, rt->rt6i_expires)) {			RT6_TRACE("expiring %p\n", rt);			return -1;		}		gc_args.more++;	} else if (rt->rt6i_flags & RTF_CACHE) {		if (atomic_read(&rt->u.dst.__refcnt) == 0 &&		    time_after_eq(now, rt->u.dst.lastuse + gc_args.timeout)) {			RT6_TRACE("aging clone %p\n", rt);			return -1;		} else if ((rt->rt6i_flags & RTF_GATEWAY) &&			   (!(rt->rt6i_nexthop->flags & NTF_ROUTER))) {			RT6_TRACE("purging route %p via non-router but gateway\n",				  rt);			return -1;		}		gc_args.more++;	}	return 0;}static DEFINE_SPINLOCK(fib6_gc_lock);void fib6_run_gc(unsigned long dummy){	if (dummy != ~0UL) {		spin_lock_bh(&fib6_gc_lock);		gc_args.timeout = dummy ? (int)dummy : ip6_rt_gc_interval;	} else {		local_bh_disable();		if (!spin_trylock(&fib6_gc_lock)) {			mod_timer(&ip6_fib_timer, jiffies + HZ);			local_bh_enable();			return;		}		gc_args.timeout = ip6_rt_gc_interval;	}	gc_args.more = 0;	ndisc_dst_gc(&gc_args.more);	fib6_clean_all(fib6_age, 0, NULL);	if (gc_args.more)		mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval);	else {		del_timer(&ip6_fib_timer);		ip6_fib_timer.expires = 0;	}	spin_unlock_bh(&fib6_gc_lock);}void __init fib6_init(void){	fib6_node_kmem = kmem_cache_create("fib6_nodes",					   sizeof(struct fib6_node),					   0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,					   NULL);	fib6_tables_init();	__rtnl_register(PF_INET6, RTM_GETROUTE, NULL, inet6_dump_fib);}void fib6_gc_cleanup(void){	del_timer(&ip6_fib_timer);	kmem_cache_destroy(fib6_node_kmem);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -