原文: http://augustseu-blog.appspot.com/?p=53001
在我们使用ifconfig命令查看NI的信息时。有这样一行标志量,如下红色字体显示
eth0 Link encap:以太网 硬件地址 00:27:13:69:7e:b7
inet 地址:10.6.15.139 广播:10.6.15.255 掩码:255.255.255.0
inet6 地址: fe80::227:13ff:fe69:7eb7/64Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 跃点数:1
接收数据包:91978 错误:0 丢弃:0 过载:0帧数:0
发送数据包:48368 错误:0 丢弃:0 过载:0载波:0
碰撞:0 发送队列长度:100
接收字节:65581015 (65.5 MB) 发送字节:11198359 (11.1 MB)
Memory:fc600000-fc620000
其中,这些标志量包含如下几组
1) 接口打开状态(UP or DOWN)
2) 接口运行状态(RUNNING ornot)
3) 接口是否环回接口(LOOPBACKor not)
4) 接口的广播与多播状态(BROADCASTor MULTICAST or not)
这两个标志量说明了网卡对广播包与多播包的过滤特性。接下来我所阐述的内容,就是围绕如何实现上述的BROADCAST和MULTICAST这两个标志量而展开的。由于2者有相互参考性,所以本人只重点阐述MULTICAST。内容涉及的范畴是由以下顺序进行,
网络层--->链路层之以太网层 ---> 链路层之网卡驱动与芯片寄存器--->链路层之物理层
【网络层】
IP多播是以IP层的D类地址,也就是多播组地址为基础的。多播组地址包括4个MSB(1110)和随后28bits的多播组号。用点分十进制表示为224.0.0.0到239.255.255.255。
【链路层之以太网层】
在以太网地址中,前3个most significant byes为01:00:5e是被保留作为多播地址的。网络层的IP多播地址与以太网多播地址的映射关系如下图(以下图片摘记TCP/IP详解之134页)
可以看出来此映射是多对一映射。
【链路层之网卡驱动与芯片寄存器】
在 现代网卡中,一般都包含多播包过滤寄存器MAR(multicastaddress filter register, 8bytes)。这些寄存器提供了对接受帧中的多播帧的Mask。在接受设置寄存器RCR(RxConfigureRegister)assert相应的 位后(一般为AcceptBroadcast, Accpet Multicast或Accept All),网卡就必须采用上述的Mask对接受的数据帧进行过滤。
以下是rtl公司的系列网卡的MAR寄存器组的扼要说明
R/W, 64 bits, Multicast hash table. If the‘n’ bit value is set ‘1’, it implies the receiving frames which hash value with‘n’ will be indicated.
关于Mask的设置方法,也就是对MAR寄存器的位写入,在这里本人仅结合rtl的网卡驱动举个例子,其余可以类推。
例子
1) 将MAR0至MAR7分为2组,前一组位MAR0-3,记为组0;后一组为MAR4-7,记为组1;
2) 欲将本网卡加入01:00:5e:01:02:03这个多播组;
3) 计算01:00:5e:01:02:03的CRC-32值,计算得0x044eac67(关于这个计算方法,请参阅本文的扩展阅读部分);
4) 将此值的前6个bits进行翻转0b0000 01 => 0b1111 10 ;
5) 翻转值的MSB为1。将MSB设0后,翻转值的十进制表示为30;
6) 根据5)中的结论,则设置组1的第30位为1。
至此,此地址的Hash过程结束。转换为C语言代码请参阅Linux源码的各个网卡驱动。在此我推荐/driver/net/r8169.c,因为本人就是参阅此代码理解上述过程的。
【链路层之物理层】
当网卡接收到一个数据帧时,会按照如下的流程图进行过滤分析(以下图片摘记Dana Castillo的文章 )
注意此图的范围,只是检测I/G标志位符合包,略去了crc校验,AcceptX等标志开启的条件。如有更多疑问,可以参阅Dana的文章,写的很详细。
[扩展]
关于Linux中的CRC算法的实现
算法原理请参阅此篇论文