linux DM9000网卡驱动中设置多播地址功能的理解

linux DM9000网卡驱动中设置多播地址函数代码为:

 1 static void
 2 dm9000_hash_table_unlocked(struct net_device *dev)
 3 {
 4     board_info_t *db = netdev_priv(dev);
 5     struct netdev_hw_addr *ha;
 6     int i, oft;
 7     u32 hash_val;
 8     u16 hash_table[4];
 9     u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN;
10 
11     dm9000_dbg(db, 1, "entering %s\n", __func__);
12 
13     for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++)
14         iow(db, oft, dev->dev_addr[i]);
15 
16     /* Clear Hash Table */
17     for (i = 0; i < 4; i++)
18         hash_table[i] = 0x0;
19 
20     /* broadcast address */
21     hash_table[3] = 0x8000;
22 
23     if (dev->flags & IFF_PROMISC)
24         rcr |= RCR_PRMSC;
25 
26     if (dev->flags & IFF_ALLMULTI)
27         rcr |= RCR_ALL;
28 
29     /* the multicast address in Hash Table : 64 bits */
30     netdev_for_each_mc_addr(ha, dev) {
31         hash_val = ether_crc_le(6, ha->addr) & 0x3f;
32         hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
33     }
34 
35     /* Write the hash table to MAC MD table */
36     for (i = 0, oft = DM9000_MAR; i < 4; i++) {
37         iow(db, oft++, hash_table[i]);
38         iow(db, oft++, hash_table[i] >> 8);
39     }
40 
41     iow(db, DM9000_RCR, rcr);
42 }
View Code

      个人感觉函数代码中有两个地方比较难理解。第一,第21行设置广播地址,hash_table[3] = 0x8000;其将hash表最高位置位。设置多播地址列表为什么还要设置广播地址?为什么要这么设置?第二,第30-33行设置多播地址哈希表,该哈希表是如何工作的?

      对于在多播地址哈希表中设置广播地址,可以看到如果代码的第23行第26行代码逻辑不成立,则网卡只接收目的地址为本机MAC地址的数据,广播数据将无法接收到。将广播地址设置到多播地址列表中,则广播地址的数据帧将会在本机处理,与广播地址相关的协议如ARP等才会得到处理。到此为什么要设置多播地址到多播地址哈希表中的目的已经清楚。那么第2个问题来了,为什么将hash_table[3]设置为0x8000便是设置了多播地址?这个与第30-33行设置多播地址哈希表的算法有关系。代码第31行hash_val = ether_crc_le(6, ha->addr) & 0x3f;通过计算mac地址的CRC校验值并与上0x3f来得到多播地址哈希表的哈希值。我们知道协议规定广播MAC地址为FF:FF:FF:FF:FF:FF。可以做个实验将广播地址的CRC校验值计算出来,得到的结果为:0xbe2612ff。部分实验代码如下:

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/crc32.h>
 4 static int __init
 5 hello_init(void)
 6 {
 7         unsigned int hash_val;
 8         unsigned char mac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
 9         printk("hello module initialise\n");
10         hash_val = ether_crc_le(6, mac);
11         printk("hash val:%0x\n",hash_val);
12         return 0;
13 }
crc32 val

      0xbe2612ff&0x3f = 0x3f = 63。该值按照代码第32行hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);计算得到结果便是hash_table[3]的最高位置位。因此不管多播地址列表中是否有将hash_table[3]最高位置位的多播地址,都需要将hash_table[3]的最高位置位。

      接下来看第二个问题,第30-33行设置多播地址哈希表。分析代码可以看到多播地址哈希表值是通过计算多播地址的CRC32校验值并与0x3f相与得到的。如果网卡允许对某个多播地址数据帧进行处理,则通过该多播地址计算出来的哈希值对应的多播地址列表相应bit位将会被置位。因此设置多播地址列表就是根据多播地址计算hash值置位硬件多播地址列表寄存器相应位。另外为什么是与0x3f相与得到结果这个与多播地址哈希表为64位有关系。如果为128位多播地址表,则应该和0x7f相与。如果将多播地哈希表设置为char型数组则第32行代码应修改为hash_table[hash_val / 8] |= (u8) 1 << (hash_val % 8);数组大小改为8。

      通过这种方式设置多播地址哈希表,并不能使硬件网卡把全部与本机无关的多播地址相关数据帧丢弃,因为不同的多播地址通过上述方式计算出的哈希值可能相同。但该种方式不会丢掉任何一个本机需要处理的含有多播地址的数据帧,同时也起到硬件过滤多播地址的作用,减轻了CPU处理本机无关数据帧的负担。

       那么为什么通过置位多播地址相应位便能起到过滤多播地址数据帧的作用呢?因为硬件接收到数据帧后,硬件会按照上述的方式来处理数据帧中的MAC地址字段,计算出MAC的哈希值来找到多播地址表寄存器的相应位,若果对应位置位则将该数据帧传递到上层继续处理,如果对应位未置位则丢弃该数据帧。某款TI网络芯片相关的网络开发包相关文档有如下描述(该款芯片支持512位多播地址列表):

      Internally, a hash function is applied to the destination multicast address to calculate an arbitrary value between 0 and 511 and to set its bit to one. When a multicast frame reaches the Ethernet adapter, it applies the same hash function to the destination address contained in the frame and sends it to the next layer (IP layer) if the previously assigned bit is set.

 

转载于:https://www.cnblogs.com/morris-tech/p/3650222.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值