Chinaunix首页 | 论坛 | 博客
  • 博客访问: 553308
  • 博文数量: 99
  • 博客积分: 4010
  • 博客等级: 上校
  • 技术积分: 1117
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-23 15:17
文章分类

全部博文(99)

文章存档

2011年(4)

2010年(13)

2009年(82)

我的朋友

分类: LINUX

2009-07-07 19:29:00

晕死了,刚才发的一篇文章没了,csdn太挫了,好不容易自己写了一篇提交上去没反应了。

最近在测试sep4020的网络,由于以前在移植4020的网卡驱动很多还不是太懂其中的组播部分有很大一段,但也是直接仿照别人的驱动直接移植上去的,最近想测试下4020的组播是否支持,于是上网找了几篇文章看了下,然后开始设置。
(1)首先当然是让linux内核支持组播协议了,在menuconfig中的网络协议栈部分选上相应的选项multicast:
[*]   IP: multicasting                                                                                                                                                  │ │ 
 │ │ [*]   IP: advanced router                                                                                                                                               │ │ 
 │ │         Choose IP: FIB lookup algorithm (choose FIB_HASH if unsure) (FIB_HASH)  --->                                                                                    │
[*]   IP: multicast routing                                                                                                                                             │ │ 
 │ │ [*]     IP: PIM-SM version 1 support                                                                                                                                    │ │ 
 │ │ [*]     IP: PIM-SM version 2 support                                                                                                                                
勾上相应的选项,重新编译内核,重启板子,看下板子是否支持组播
ifconfig eth0得到的信息显示UP BROADCAST RUNNING MULTICAST,已经支持组播。
(2)内核既然支持了,现在就可以配置网卡的组播IP地址了,这步配置我上网搜了一篇配置和测试组播的程序,下载到程序后交叉编译没问题,下载到板子上运行,没反应,怀疑程序,于是在电脑的fedora下也编译运行,很正常,完了看来是网卡驱动还存在问题,在网卡驱动的sepether_sethashtable()函数里面打印信息,能正常的出来看来是4020 的组播配置部分出现了问题,4020的MAC HASH0,MAC HASH1这两个寄存器我不知道怎么用,也只是照样子做的,看来这里可能出现了问题。先贴下代码:
static int hash_get_index(__u8 *addr)
{
int i, j, bitval;
int hash_index = 0;
 
for (j = 0; j < 6; j++) {
for (i = 0, bitval = 0; i < 8; i++)
bitval ^= hash_bit_value(i*6 + j, addr);
 
hash_index |= (bitval << j);
}
 
       return hash_index;
}
 
/*
* Add multicast addresses to the internal multicast-hash table.
*/
static void sepether_sethashtable(struct net_device *dev)
{
struct dev_mc_list *curr;
unsigned long mc_filter[2];
unsigned int i, bitnr;
mc_filter[0] = mc_filter[1] = 0;
 
curr = dev->mc_list;
for (i = 0; i < dev->mc_count; i++, curr = curr->next) {
if (!curr) break; /* unexpected end of list */
 
bitnr = hash_get_index(curr->dmi_addr);
mc_filter[bitnr >> 5] |= 1 << (bitnr & 31);
}
 
sep_emac_write(MAC_HASH1_V, mc_filter[1]);
sep_emac_write(MAC_HASH0_V, mc_filter[0]);
}
 
/*
* Enable/Disable promiscuous and multicast modes.
*/
static void sepether_set_rx_mode(struct net_device *dev)
{
unsigned long cfg;
 
cfg = sep_emac_read(MAC_CTRL_V);
 
if (dev->flags & IFF_PROMISC) /* Enable promiscuous mode */
cfg |= 0x20;
else if (dev->flags & (~IFF_PROMISC)) /* Disable promiscuous mode */
cfg &= ~0x20;
 
if (dev->flags & IFF_ALLMULTI) { /* Enable all multicast mode */
sep_emac_write(MAC_HASH1_V, 0xffffffff);
sep_emac_write(MAC_HASH0_V, 0xffffffff);
cfg |= 0x10;
} else if (dev->mc_count > 0) { /* Enable specific multicasts */
sepether_sethashtable(dev);
cfg |= 0x10;
} else if (dev->flags & (~IFF_ALLMULTI)) { /* Disable all multicast mode */
sep_emac_write(MAC_HASH1_V, 0);
sep_emac_write(MAC_HASH0_V, 0);
cfg &= ~0x10;
}
 
sep_emac_write(MAC_CTRL_V, cfg);
 
}
看这个代码比较烦因为它中间有一部分hash表的运算,没办法只能看下去了,看了会好像懂了,当我们配置组播ip地址时底层会将其转化成组播MAC地址,然后这段程序会将组播MAC地址分成64部分,如果你配置的MAC地址是哪个区间的地址,它就会将两个寄存器总共64位中间的相应位置1,这样便于网卡在底层收发包的时候就能把相应的组播包给过滤出来。原理是这样但为什么我们的4020还是不工作呢,晕,看来还得看拍错了,呜呜???
(3)网卡接收后面的组播包的处理流程:
通过加入组播组的操作后,网络设备接口已经知道要接收该组的数据报,所以组播数据会从网卡接收进来,一直到达myip_rcv函数,我们就从myip_rcv函数开始,跟踪整个组播数据报的接收流程。
   同样,myip_rcv还是先检查数据报的类型(是否为本机需要接收的包),ip首部是否正确,然后调用myip_rcv_finish。 myip_rcv_finish对任何数据报都要先查找输入路由,输入路由查找函数是myip_route_input,当该函数在路由缓存 myrt_hash_table中找不到相应的路由项时,判断数据报的输入地址,如果发现是组播地址,就不能简单地查找FIB,而是要作特殊处理。
   首先,调用myip_check_mc对这个组播数据报作检查,从网络设备接口的struct in_device中去匹配组播地址,如果匹配不到,表示这个不是我们希望接收的组播包,丢弃。匹配到了,则作下一步检查,如果这本身就是一个IGMP 包,则接收,否则,查看这个组播组在我们的struct in_device中设置的过滤机制,如果该数据报的源地址在我们的过滤名单中,则丢弃,否则接收。
   如果检查通过,准备接收这个组播包,则调用myip_route_input_mc查找组播输入路由,这是一个专门为组播设置的函数,它第一步要检查数据报源地址的有效性,即源地址不能是组播地址,不能是广播地址,也不能是回环地址,同时,该数据报必须是一个因特网协议包(ETH_P_IP)。如果源地址为0,那么只有当目的地址是224.0.0.0-224.0.0.255之间的值(只能在发送主机所在的一个子网内的传送,不会通过路由器转发。)时,系统可以自己选定一个scope为RT_SCOPE_LINK的源地址,否则出错。
   当验证了源地址的有效性之后,我们建立路由项,即结构体struct rtable。该路由项的rt_type值是RTN_MULTICAST,表示这一条组播路由。对于本地接收的组播包,我们设置接收函数为 myip_local_deliver。
   有了这个路由项,我们可以通过调用myip_local_deliver,继续接收流程,这部分流程前面已有多次介绍,所以讲得简单一点,只注意组播特有的。同样,到myip_local_deliver_finish后,首先要检查是否有raw socket要接收这个组播包。然后根据IP首部里协议字段,调用相应协议的接收函数,我们这儿是一个UDP组播包,所以调用myudp_rcv。
   myudp_rcv首先会对路由项的成员rt_flags作一个检查,如果发现它有RTCF_BROADCAST或者RTCF_MULTICAST,就不会走常规的从myudp_hash中匹配源和目的地址,找到socket,把数据报放入接收队列这么一个流程。而是调用函数 myudp_v4_mcast_deliver,这是一个专用于接收UDP组播数据报的函数,它首先根据目的端口确定在哈希表mydup_hash中的位置,然后遍历找到的这个链表。与普通的UDP数据报接收相比,它多一个过滤检查,即在套接字结构体的成员mc_list中找到与该数据报所属组对应的 ip_mc_socklist项,查看它的过滤配置,确认该数据报的源地址是否在过滤列表中。如果不在,则把数据放到该socket的接收队列中,完成组播数据报的接收。

阅读(2272) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~