閱讀675 返回首頁    go 阿裏雲 go 技術社區[雲棲]


網絡子係統73_入口路由緩存查找

//	入口路由緩衝查找
//	返回值:
//		0,路由查找成功
//		-ENOBUFF,內存問題導致查找失敗
//		-EINVAL,常規查找失敗
//	hash值計算:
//		目的地址,源地址,入口設備index,tos進行hash
//	函數主要任務:
//		1.根據hash值遍曆bucket
//		2.比較5元組
//		3.如果緩存命中,更新引用計數,綁定到skb->dst
//		4.否則,如果為本地多播地址,或者內核支持多播路由,多播路由中查找
//		5.否則,在路由表中查找

//	注:緩存確定5元組 = <目的地址,源地址,入口索引,出口索引=0,tos>
1.1 int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr,
		   u8 tos, struct net_device *dev)
{
	struct rtable * rth;
	unsigned	hash;
	int iif = dev->ifindex;

	tos &= IPTOS_RT_MASK;
	hash = rt_hash_code(daddr, saddr ^ (iif << 5), tos);

	rcu_read_lock();
	for (rth = rcu_dereference(rt_hash_table[hash].chain); rth;
	     rth = rcu_dereference(rth->u.rt_next)) {
		if (rth->fl.fl4_dst == daddr &&
		    rth->fl.fl4_src == saddr &&
		    rth->fl.iif == iif &&
		    //ofi=0,表示此路由為入口路由
		    rth->fl.oif == 0 &&
		    rth->fl.fl4_tos == tos) {
			rth->u.dst.lastuse = jiffies;
			dst_hold(&rth->u.dst);
			//引用計數
			rth->u.dst.__use++;
			//緩存命中次數
			RT_CACHE_STAT_INC(in_hit);
			rcu_read_unlock();
			//將路由緩存綁定到skb
			skb->dst = (struct dst_entry*)rth;
			return 0;
		}
		RT_CACHE_STAT_INC(in_hlist_search);
	}
	rcu_read_unlock();
	//目的地址為多播地址
	if (MULTICAST(daddr)) {
		struct in_device *in_dev;
		rcu_read_lock();
		//設備的ipv4配置信息
		if ((in_dev = __in_dev_get(dev)) != NULL) {
			int our = ip_check_mc(in_dev, daddr, saddr,
				skb->nh.iph->protocol);
			//本地配置的多播地址,或者內核支持多播路由
			if (our|| (!LOCAL_MCAST(daddr) && IN_DEV_MFORWARD(in_dev))) 
			{
				rcu_read_unlock();
				//多播路由
				return ip_route_input_mc(skb, daddr, saddr,
							 tos, dev, our);
			}
		}
		rcu_read_unlock();
		return -EINVAL;
	}
	//在路由表中查找
	return ip_route_input_slow(skb, daddr, saddr, tos, dev);
}

//	判斷是否接收此ip多播
//		接收條件:
//			1.組播協議報文,接收
//			2.設備已加入此多播組
//				2.1 指定了源地址
//					2.1.1 源地址在多播組的源地址鏈表
//						2.1.1.1 源地址的包含計數!=0,或者源地址的排除計數!=多播組的排除計數,接收
//					2.1.2 源地址不在多播組的源地址鏈表
//						2.1.2.1 多播組的排斥計數!=0,接收
//				2.2 無源地址,默認接收
2.1 int ip_check_mc(struct in_device *in_dev, u32 mc_addr, u32 src_addr, u16 proto)
{
	struct ip_mc_list *im;
	struct ip_sf_list *psf;
	int rv = 0;

	read_lock(&in_dev->mc_list_lock);
	//設備的多播鏈表in_device->mc_list
	for (im=in_dev->mc_list; im; im=im->next) {
		if (im->multiaddr == mc_addr)
			break;
	}
	//組播協議,本地接收
	if (im && proto == IPPROTO_IGMP) {
		rv = 1;
	} else if (im) {
		//判斷源地址是否在多播組中的源地址列表
		if (src_addr) {
			for (psf=im->sources; psf; psf=psf->sf_next) {
				if (psf->sf_inaddr == src_addr)
					break;
			}
			//源地址在多播組源地址鏈表中
			if (psf)
			{
				//源地址接收計數>0,或者源地址排斥計數!=多播組排斥計數
				rv = psf->sf_count[MCAST_INCLUDE] ||
					psf->sf_count[MCAST_EXCLUDE] !=
					im->sfcount[MCAST_EXCLUDE];
			}
			else
			{
				//源地址不在多播組鏈表中,排斥計數!=0
				rv = im->sfcount[MCAST_EXCLUDE] != 0;
			}
		//沒有指定源地址,默認接收
		} else
			rv = 1; 
	}
	read_unlock(&in_dev->mc_list_lock);
	return rv;
}
//	路由入口多播地址
//		1.分配路由緩存
//		2.初始化路由緩存的ipv4搜索關鍵字
//		3.初始化路由緩存選項
//		4.初始化路由緩存輸入,輸出函數
//		5.插入到路由緩存hash表中

//	注:入口路由的出口設備索引=0,出口設備=回環設備
3.1 static int ip_route_input_mc(struct sk_buff *skb, u32 daddr, u32 saddr,
				u8 tos, struct net_device *dev, int our)
{
	unsigned hash;
	struct rtable *rth;
	u32 spec_dst;
	struct in_device *in_dev = in_dev_get(dev);
	u32 itag = 0;

	//分配路由緩存
	rth = dst_alloc(&ipv4_dst_ops);

	rth->u.dst.output = ip_rt_bug;//多播入口路由的輸出函數
	atomic_set(&rth->u.dst.__refcnt, 1);//引用計數=1
	rth->u.dst.flags= DST_HOST;//DST_HOST,tcp使用標誌,表示主機路由(即它不是到網絡或到一個廣播/多播地址的路由)
	if (in_dev->cnf.no_policy)
		rth->u.dst.flags |= DST_NOPOLICY;//DST_NOXFRM, DST_NOPOLICY, DST_NOHASH隻用於IPsec

	rth->u.dst.dev	= &loopback_dev;//入口路由,使用回環設備作為其出口設備
	dev_hold(rth->u.dst.dev);//路由緩存關聯到設備,增加設備的引用計數

	//路由項的搜索關鍵字
	rth->fl.fl4_dst	= daddr;
	rth->fl.fl4_tos	= tos;
	rth->fl.fl4_src	= saddr;
	rth->fl.iif	= dev->ifindex;
	rth->fl.oif	= 0;//入口路由的出口設備索引=0,表示為入口路由


	//目的,源ip地址
	rth->rt_dst	= daddr;
	rth->rt_src	= saddr;
	rth->rt_iif	= dev->ifindex;//入口設備索引
	rth->rt_gateway	= daddr;//下一跳網關初始化為目的地址
	rth->rt_spec_dst= spec_dst;//RFC 1122中指定的目的地址
	rth->rt_type	= RTN_MULTICAST;//路由類型,間接定義了當路由查找匹配時應該采取的動作
	rth->rt_flags	= RTCF_MULTICAST;//表示路由的目的地址為多播地址
	rth->idev	= in_dev_get(rth->u.dst.dev);//使用入口設備的ipv4配置信息作為路由緩存的ipv4配置信息

	//本地配置的多播地址
	if (our) {
		rth->u.dst.input= ip_local_deliver;//向本地傳遞
		rth->rt_flags |= RTCF_LOCAL;//路由的目的地址為本地地址
	}

//內核支持多播路由
#ifdef CONFIG_IP_MROUTE
	//目標地址非組播地址
	//入口設備支持多播路由
	if (!LOCAL_MCAST(daddr) && IN_DEV_MFORWARD(in_dev))
		rth->u.dst.input = ip_mr_input;
#endif

	in_dev_put(in_dev);
	hash = rt_hash_code(daddr, saddr ^ (dev->ifindex << 5), tos);
	//插入到路由緩存中
	return rt_intern_hash(hash, rth, (struct rtable**) &skb->dst);

}

最後更新:2017-04-03 14:53:50

  上一篇:go Java常用類庫--對象克隆技術clone
  下一篇:go 基於中間件/構件的開發