网络子系统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);

}
时间: 2024-09-14 20:41:32

网络子系统73_入口路由缓存查找的相关文章

网络子系统71_路由缓存垃圾回收

// 同步回收机制 // 返回值: // 0,达到回收目标 // 1,没有达到回收目标 // 注:ip_rt_gc_min_interval = HZ/2 // ip_rt_gc_timeout = RT_GC_TIMEOUT = 300HZ // ip_rt_gc_elasticity = 8*(rt_hash_mask+1)(正常情况下) // ip_rt_gc_elasticity = 1(当绑定缓存到邻居子系统失败时) // 平衡点,gc目标数的动态调整: // 1.当前缓存个数 < 安

网络子系统70_路由缓存操作

// 刷新路由缓存 // 参数: // delay, 刷新操作的延迟时间 // 函数主要任务: // 1.重新计算路由刷新定时器的到期时间 // 2.如果delay=0,则立即刷新缓存 // 3.激活定时器,使用最近的刷新延迟作为到期时间 1.1 void rt_cache_flush(int delay) { unsigned long now = jiffies; //用户态 int user_mode = !in_softirq(); if (delay < 0) delay = ip_r

网络子系统63_路由子系统处理netlink事件

// 路由子系统netlink控制块 // 在ip_rt_init->devinet_init中注册. 1.1 static struct rtnetlink_link inet_rtnetlink_table[RTM_MAX - RTM_BASE + 1] = { [4] = { .doit = inet_rtm_newaddr, }, [5] = { .doit = inet_rtm_deladdr, }, [6] = { .dumpit = inet_dump_ifaddr, }, [8]

网络子系统59_策略路由、多路径路由

// 参考 深入理解linux网络技术内幕 // 策略路由: // 指允许用户除了可以根据目的ip地址配置路由外,还可以根据其他参数配置路由 // 内核对策略路由的支持: // 1.在默认情况下,不支持策略路由, 使用2张路由表,1张本地表,1张用于配置管理 // 2.编译支持策略路由,使用255张表,1张本地表,其他根据配置参数使用不同的表 // 选择路由表: // 内核可以基于以下参数作为选择路由表时所用的策略: // 1.源ip地址/目的ip地址 // 2.入口设备 // 3.tos //

网络子系统61_路由子系统初始化

// 路由缓存内存量指定办法: // 1.通过启动参数rhash_entries指定hash表bucket个数 // 2.根据物理内存页数确定使用的内存量 // 根据物理页数分配缓存内存: // 1.goal目标内存页数=总页数/(2**(26-PAGE_SHIFT)) // 2.最接近goal内存页数的order,用于从伙伴系统中分配 // 3.rt_hash_mask=order页可容纳的bucket数 // 4.对齐rt_hash_mask到2的幂次 // 5.从伙伴系统中分配order页

网络子系统14_邻居子系统通用接口

//创建一个新的邻居项 //参考 深入理解linux网络技术内幕 // 1.邻居子系统为具体的邻居协议,提供通用的功能接口 // 2.系统中所有的邻居协议被链接在neigh_tables链表中 // 3.neigh_table代表一个具体的邻居协议 // 4.具体邻居协议在运行时的行为,可以通过struct neigh_parms调节, // neigh_params与设备关联,每个邻居协议neigh_table提供一个默认的neigh_params. //注册一个邻居协议到系统中 // 1.与

网络子系统38_ip子系统初始化

//ip子系统初始化 //调用路径inet_init->ip_init //包括路由子系统的初始化,inet_peer缓存的初始化 1.1 void __init ip_init(void) { dev_add_pack(&ip_packet_type);//添加l3协议处理函数 ip_rt_init(); //路由子系统初始化 inet_initpeers();//inet_peer缓存 } //调用路径ip_init->inet_initpeers //inet_peer缓存初始化

网络子系统11_arp子系统初始化

// 1.邻居: // 网络中的邻居指连接到同一个LAN中,且至少有一个接口有相同的ip子网配置 // 2.邻居协议的作用: // 通过l3地址获取主机的l2地址 // 3.通过l3获取l2地址办法: // 3.1 点到点连接,此时不需要l2地址 // 3.2 特殊的l3地址,通过简单的规则获取l2的映射 // 3.3 多播地址通过简单的转换规则转换为l2地址 // 3.4 ip中使用arp协议完成映射 // arp协议的初始化 // 向内核邻居子系统注册arp地址解析协议. // 调用路径:i

网络子系统15_arp邻居项初始化

// 初始化struct neighbour // 当邻居子系统新创一个neighbour时,邻居子系统调用特定协议的初始化函数,初始化邻居项. // 调用路径:neigh_create->arp_constructor // 函数主要任务: // 1.设置邻居项的地址类型,a,b,c,d // 2.使用与此邻居项关联的接口设备的neigh_param作为该邻居项调整邻居协议的参数. // 3.根据与此邻居项关联的接口设备的信息,初始化邻居项的状态,以及ops // 3.1 驱动没有提供填充l2