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

📄 fib_semantics.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
		return kmalloc(bytes, GFP_KERNEL);	else		return (struct hlist_head *)			__get_free_pages(GFP_KERNEL, get_order(bytes));}static void fib_hash_free(struct hlist_head *hash, int bytes){	if (!hash)		return;	if (bytes <= PAGE_SIZE)		kfree(hash);	else		free_pages((unsigned long) hash, get_order(bytes));}static void fib_hash_move(struct hlist_head *new_info_hash,			  struct hlist_head *new_laddrhash,			  unsigned int new_size){	struct hlist_head *old_info_hash, *old_laddrhash;	unsigned int old_size = fib_hash_size;	unsigned int i, bytes;	spin_lock_bh(&fib_info_lock);	old_info_hash = fib_info_hash;	old_laddrhash = fib_info_laddrhash;	fib_hash_size = new_size;	for (i = 0; i < old_size; i++) {		struct hlist_head *head = &fib_info_hash[i];		struct hlist_node *node, *n;		struct fib_info *fi;		hlist_for_each_entry_safe(fi, node, n, head, fib_hash) {			struct hlist_head *dest;			unsigned int new_hash;			hlist_del(&fi->fib_hash);			new_hash = fib_info_hashfn(fi);			dest = &new_info_hash[new_hash];			hlist_add_head(&fi->fib_hash, dest);		}	}	fib_info_hash = new_info_hash;	for (i = 0; i < old_size; i++) {		struct hlist_head *lhead = &fib_info_laddrhash[i];		struct hlist_node *node, *n;		struct fib_info *fi;		hlist_for_each_entry_safe(fi, node, n, lhead, fib_lhash) {			struct hlist_head *ldest;			unsigned int new_hash;			hlist_del(&fi->fib_lhash);			new_hash = fib_laddr_hashfn(fi->fib_prefsrc);			ldest = &new_laddrhash[new_hash];			hlist_add_head(&fi->fib_lhash, ldest);		}	}	fib_info_laddrhash = new_laddrhash;	spin_unlock_bh(&fib_info_lock);	bytes = old_size * sizeof(struct hlist_head *);	fib_hash_free(old_info_hash, bytes);	fib_hash_free(old_laddrhash, bytes);}struct fib_info *fib_create_info(struct fib_config *cfg){	int err;	struct fib_info *fi = NULL;	struct fib_info *ofi;	int nhs = 1;	/* Fast check to catch the most weird cases */	if (fib_props[cfg->fc_type].scope > cfg->fc_scope)		goto err_inval;#ifdef CONFIG_IP_ROUTE_MULTIPATH	if (cfg->fc_mp) {		nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len);		if (nhs == 0)			goto err_inval;	}#endif	err = -ENOBUFS;	if (fib_info_cnt >= fib_hash_size) {		unsigned int new_size = fib_hash_size << 1;		struct hlist_head *new_info_hash;		struct hlist_head *new_laddrhash;		unsigned int bytes;		if (!new_size)			new_size = 1;		bytes = new_size * sizeof(struct hlist_head *);		new_info_hash = fib_hash_alloc(bytes);		new_laddrhash = fib_hash_alloc(bytes);		if (!new_info_hash || !new_laddrhash) {			fib_hash_free(new_info_hash, bytes);			fib_hash_free(new_laddrhash, bytes);		} else {			memset(new_info_hash, 0, bytes);			memset(new_laddrhash, 0, bytes);			fib_hash_move(new_info_hash, new_laddrhash, new_size);		}		if (!fib_hash_size)			goto failure;	}	fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);	if (fi == NULL)		goto failure;	fib_info_cnt++;	fi->fib_protocol = cfg->fc_protocol;	fi->fib_flags = cfg->fc_flags;	fi->fib_priority = cfg->fc_priority;	fi->fib_prefsrc = cfg->fc_prefsrc;	fi->fib_nhs = nhs;	change_nexthops(fi) {		nh->nh_parent = fi;	} endfor_nexthops(fi)	if (cfg->fc_mx) {		struct nlattr *nla;		int remaining;		nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {			int type = nla_type(nla);			if (type) {				if (type > RTAX_MAX)					goto err_inval;				fi->fib_metrics[type - 1] = nla_get_u32(nla);			}		}	}	if (cfg->fc_mp) {#ifdef CONFIG_IP_ROUTE_MULTIPATH		err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg);		if (err != 0)			goto failure;		if (cfg->fc_oif && fi->fib_nh->nh_oif != cfg->fc_oif)			goto err_inval;		if (cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw)			goto err_inval;#ifdef CONFIG_NET_CLS_ROUTE		if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow)			goto err_inval;#endif#else		goto err_inval;#endif	} else {		struct fib_nh *nh = fi->fib_nh;		nh->nh_oif = cfg->fc_oif;		nh->nh_gw = cfg->fc_gw;		nh->nh_flags = cfg->fc_flags;#ifdef CONFIG_NET_CLS_ROUTE		nh->nh_tclassid = cfg->fc_flow;#endif#ifdef CONFIG_IP_ROUTE_MULTIPATH		nh->nh_weight = 1;#endif	}	if (fib_props[cfg->fc_type].error) {		if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp)			goto err_inval;		goto link_it;	}	if (cfg->fc_scope > RT_SCOPE_HOST)		goto err_inval;	if (cfg->fc_scope == RT_SCOPE_HOST) {		struct fib_nh *nh = fi->fib_nh;		/* Local address is added. */		if (nhs != 1 || nh->nh_gw)			goto err_inval;		nh->nh_scope = RT_SCOPE_NOWHERE;		nh->nh_dev = dev_get_by_index(&init_net, fi->fib_nh->nh_oif);		err = -ENODEV;		if (nh->nh_dev == NULL)			goto failure;	} else {		change_nexthops(fi) {			if ((err = fib_check_nh(cfg, fi, nh)) != 0)				goto failure;		} endfor_nexthops(fi)	}	if (fi->fib_prefsrc) {		if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst ||		    fi->fib_prefsrc != cfg->fc_dst)			if (inet_addr_type(fi->fib_prefsrc) != RTN_LOCAL)				goto err_inval;	}link_it:	if ((ofi = fib_find_info(fi)) != NULL) {		fi->fib_dead = 1;		free_fib_info(fi);		ofi->fib_treeref++;		return ofi;	}	fi->fib_treeref++;	atomic_inc(&fi->fib_clntref);	spin_lock_bh(&fib_info_lock);	hlist_add_head(&fi->fib_hash,		       &fib_info_hash[fib_info_hashfn(fi)]);	if (fi->fib_prefsrc) {		struct hlist_head *head;		head = &fib_info_laddrhash[fib_laddr_hashfn(fi->fib_prefsrc)];		hlist_add_head(&fi->fib_lhash, head);	}	change_nexthops(fi) {		struct hlist_head *head;		unsigned int hash;		if (!nh->nh_dev)			continue;		hash = fib_devindex_hashfn(nh->nh_dev->ifindex);		head = &fib_info_devhash[hash];		hlist_add_head(&nh->nh_hash, head);	} endfor_nexthops(fi)	spin_unlock_bh(&fib_info_lock);	return fi;err_inval:	err = -EINVAL;failure:	if (fi) {		fi->fib_dead = 1;		free_fib_info(fi);	}	return ERR_PTR(err);}/* Note! fib_semantic_match intentionally uses  RCU list functions. */int fib_semantic_match(struct list_head *head, const struct flowi *flp,		       struct fib_result *res, __be32 zone, __be32 mask,			int prefixlen){	struct fib_alias *fa;	int nh_sel = 0;	list_for_each_entry_rcu(fa, head, fa_list) {		int err;		if (fa->fa_tos &&		    fa->fa_tos != flp->fl4_tos)			continue;		if (fa->fa_scope < flp->fl4_scope)			continue;		fa->fa_state |= FA_S_ACCESSED;		err = fib_props[fa->fa_type].error;		if (err == 0) {			struct fib_info *fi = fa->fa_info;			if (fi->fib_flags & RTNH_F_DEAD)				continue;			switch (fa->fa_type) {			case RTN_UNICAST:			case RTN_LOCAL:			case RTN_BROADCAST:			case RTN_ANYCAST:			case RTN_MULTICAST:				for_nexthops(fi) {					if (nh->nh_flags&RTNH_F_DEAD)						continue;					if (!flp->oif || flp->oif == nh->nh_oif)						break;				}#ifdef CONFIG_IP_ROUTE_MULTIPATH				if (nhsel < fi->fib_nhs) {					nh_sel = nhsel;					goto out_fill_res;				}#else				if (nhsel < 1) {					goto out_fill_res;				}#endif				endfor_nexthops(fi);				continue;			default:				printk(KERN_DEBUG "impossible 102\n");				return -EINVAL;			}		}		return err;	}	return 1;out_fill_res:	res->prefixlen = prefixlen;	res->nh_sel = nh_sel;	res->type = fa->fa_type;	res->scope = fa->fa_scope;	res->fi = fa->fa_info;	atomic_inc(&res->fi->fib_clntref);	return 0;}/* Find appropriate source address to this destination */__be32 __fib_res_prefsrc(struct fib_result *res){	return inet_select_addr(FIB_RES_DEV(*res), FIB_RES_GW(*res), res->scope);}int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,		  u32 tb_id, u8 type, u8 scope, __be32 dst, int dst_len, u8 tos,		  struct fib_info *fi, unsigned int flags){	struct nlmsghdr *nlh;	struct rtmsg *rtm;	nlh = nlmsg_put(skb, pid, seq, event, sizeof(*rtm), flags);	if (nlh == NULL)		return -EMSGSIZE;	rtm = nlmsg_data(nlh);	rtm->rtm_family = AF_INET;	rtm->rtm_dst_len = dst_len;	rtm->rtm_src_len = 0;	rtm->rtm_tos = tos;	rtm->rtm_table = tb_id;	NLA_PUT_U32(skb, RTA_TABLE, tb_id);	rtm->rtm_type = type;	rtm->rtm_flags = fi->fib_flags;	rtm->rtm_scope = scope;	rtm->rtm_protocol = fi->fib_protocol;	if (rtm->rtm_dst_len)		NLA_PUT_BE32(skb, RTA_DST, dst);	if (fi->fib_priority)		NLA_PUT_U32(skb, RTA_PRIORITY, fi->fib_priority);	if (rtnetlink_put_metrics(skb, fi->fib_metrics) < 0)		goto nla_put_failure;	if (fi->fib_prefsrc)		NLA_PUT_BE32(skb, RTA_PREFSRC, fi->fib_prefsrc);	if (fi->fib_nhs == 1) {		if (fi->fib_nh->nh_gw)			NLA_PUT_BE32(skb, RTA_GATEWAY, fi->fib_nh->nh_gw);		if (fi->fib_nh->nh_oif)			NLA_PUT_U32(skb, RTA_OIF, fi->fib_nh->nh_oif);#ifdef CONFIG_NET_CLS_ROUTE		if (fi->fib_nh[0].nh_tclassid)			NLA_PUT_U32(skb, RTA_FLOW, fi->fib_nh[0].nh_tclassid);#endif	}#ifdef CONFIG_IP_ROUTE_MULTIPATH	if (fi->fib_nhs > 1) {		struct rtnexthop *rtnh;		struct nlattr *mp;		mp = nla_nest_start(skb, RTA_MULTIPATH);		if (mp == NULL)			goto nla_put_failure;		for_nexthops(fi) {			rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh));			if (rtnh == NULL)				goto nla_put_failure;			rtnh->rtnh_flags = nh->nh_flags & 0xFF;			rtnh->rtnh_hops = nh->nh_weight - 1;			rtnh->rtnh_ifindex = nh->nh_oif;			if (nh->nh_gw)				NLA_PUT_BE32(skb, RTA_GATEWAY, nh->nh_gw);#ifdef CONFIG_NET_CLS_ROUTE			if (nh->nh_tclassid)				NLA_PUT_U32(skb, RTA_FLOW, nh->nh_tclassid);#endif			/* length of rtnetlink header + attributes */			rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *) rtnh;		} endfor_nexthops(fi);		nla_nest_end(skb, mp);	}#endif	return nlmsg_end(skb, nlh);nla_put_failure:	nlmsg_cancel(skb, nlh);	return -EMSGSIZE;}/*   Update FIB if:   - local address disappeared -> we must delete all the entries     referring to it.   - device went down -> we must shutdown all nexthops going via it. */int fib_sync_down(__be32 local, struct net_device *dev, int force){	int ret = 0;	int scope = RT_SCOPE_NOWHERE;	if (force)		scope = -1;	if (local && fib_info_laddrhash) {		unsigned int hash = fib_laddr_hashfn(local);		struct hlist_head *head = &fib_info_laddrhash[hash];		struct hlist_node *node;		struct fib_info *fi;		hlist_for_each_entry(fi, node, head, fib_lhash) {			if (fi->fib_prefsrc == local) {				fi->fib_flags |= RTNH_F_DEAD;				ret++;			}		}	}	if (dev) {		struct fib_info *prev_fi = NULL;		unsigned int hash = fib_devindex_hashfn(dev->ifindex);		struct hlist_head *head = &fib_info_devhash[hash];		struct hlist_node *node;		struct fib_nh *nh;		hlist_for_each_entry(nh, node, head, nh_hash) {			struct fib_info *fi = nh->nh_parent;			int dead;			BUG_ON(!fi->fib_nhs);			if (nh->nh_dev != dev || fi == prev_fi)				continue;			prev_fi = fi;			dead = 0;			change_nexthops(fi) {				if (nh->nh_flags&RTNH_F_DEAD)					dead++;				else if (nh->nh_dev == dev &&					 nh->nh_scope != scope) {					nh->nh_flags |= RTNH_F_DEAD;#ifdef CONFIG_IP_ROUTE_MULTIPATH					spin_lock_bh(&fib_multipath_lock);					fi->fib_power -= nh->nh_power;					nh->nh_power = 0;					spin_unlock_bh(&fib_multipath_lock);#endif					dead++;				}#ifdef CONFIG_IP_ROUTE_MULTIPATH				if (force > 1 && nh->nh_dev == dev) {					dead = fi->fib_nhs;					break;				}#endif			} endfor_nexthops(fi)			if (dead == fi->fib_nhs) {				fi->fib_flags |= RTNH_F_DEAD;				ret++;			}		}	}	return ret;}#ifdef CONFIG_IP_ROUTE_MULTIPATH/*   Dead device goes up. We wake up dead nexthops.   It takes sense only on multipath routes. */int fib_sync_up(struct net_device *dev){	struct fib_info *prev_fi;	unsigned int hash;	struct hlist_head *head;	struct hlist_node *node;	struct fib_nh *nh;	int ret;	if (!(dev->flags&IFF_UP))		return 0;	prev_fi = NULL;	hash = fib_devindex_hashfn(dev->ifindex);	head = &fib_info_devhash[hash];	ret = 0;	hlist_for_each_entry(nh, node, head, nh_hash) {		struct fib_info *fi = nh->nh_parent;		int alive;		BUG_ON(!fi->fib_nhs);		if (nh->nh_dev != dev || fi == prev_fi)			continue;		prev_fi = fi;		alive = 0;		change_nexthops(fi) {			if (!(nh->nh_flags&RTNH_F_DEAD)) {				alive++;				continue;			}			if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP))				continue;			if (nh->nh_dev != dev || !__in_dev_get_rtnl(dev))				continue;			alive++;			spin_lock_bh(&fib_multipath_lock);			nh->nh_power = 0;			nh->nh_flags &= ~RTNH_F_DEAD;			spin_unlock_bh(&fib_multipath_lock);		} endfor_nexthops(fi)		if (alive > 0) {			fi->fib_flags &= ~RTNH_F_DEAD;			ret++;		}	}	return ret;}/*   The algorithm is suboptimal, but it provides really   fair weighted route distribution. */void fib_select_multipath(const struct flowi *flp, struct fib_result *res){	struct fib_info *fi = res->fi;	int w;	spin_lock_bh(&fib_multipath_lock);	if (fi->fib_power <= 0) {		int power = 0;		change_nexthops(fi) {			if (!(nh->nh_flags&RTNH_F_DEAD)) {				power += nh->nh_weight;				nh->nh_power = nh->nh_weight;			}		} endfor_nexthops(fi);		fi->fib_power = power;		if (power <= 0) {			spin_unlock_bh(&fib_multipath_lock);			/* Race condition: route has just become dead. */			res->nh_sel = 0;			return;		}	}	/* w should be random number [0..fi->fib_power-1],	   it is pretty bad approximation.	 */	w = jiffies % fi->fib_power;	change_nexthops(fi) {		if (!(nh->nh_flags&RTNH_F_DEAD) && nh->nh_power) {			if ((w -= nh->nh_power) <= 0) {				nh->nh_power--;				fi->fib_power--;				res->nh_sel = nhsel;				spin_unlock_bh(&fib_multipath_lock);				return;			}		}	} endfor_nexthops(fi);	/* Race condition: route has just become dead. */	res->nh_sel = 0;	spin_unlock_bh(&fib_multipath_lock);}#endif

⌨️ 快捷键说明

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