網絡子係統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