网络子系统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;unsignedhash;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();//将路由缓存绑定到skbskb->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_listfor (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{//源地址不在多播组链表中,排斥计数!=0rv = im->sfcount[MCAST_EXCLUDE] != 0;}//没有指定源地址,默认接收} elserv = 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);//引用计数=1rth->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只用于IPsecrth->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;#endifin_dev_put(in_dev);hash = rt_hash_code(daddr, saddr ^ (dev->ifindex << 5), tos);//插入到路由缓存中return rt_intern_hash(hash, rth, (struct rtable**) &skb->dst);}