0731-84728105
15116127200
二层交换机原型设计与实现(七)
发布时间:2021-06-09
     上一篇讲述了bitmap的端口表示方法,也讲了单播只用常规方法表示的原因,故我们只需要对多播的转发表进行相应的定制和处理即可。单播和多播的地址区分也已在上篇文章中交待清楚,本篇重点讲述如何处理多播数据。
     根据前篇分析,多播表的定义只是将其端口号字段的表述进行了重定义。故多播表的定义只需要将单播表复制一份即可,只是在处理多播数据时,对端口字段的使用有些不一样。

struct table_port_mac *obx_mc_mac_tbl = NULL;/*定义多播MAC转发表对象为空指针,将在main函数中初始地址*/
/*为多手播MAC转发表分配内存,并初始数据为0*/
obx_mc_mac_tbl = (struct table_port_mac *)malloc(sizeof(struct table_port_mac));
memset(obx_mc_mac_tbl,0,sizeof(struct table_port_mac));

     本交换机原型中,我们仅支持IGMP v3版本,故只对该版本协议进行解析处理,对组播的入组与退组处理只需要处理IGMPv3 的Report分组协议即可。
     解析IGMP协议我们需要关心的分组字段如下:
     1)IGMP的组播IP地址为224.0.0.22,多播MAC为:01:00:5E:00:00:16
     2)以太网帧类型为IPv4(0x0800)
     3)IP协议号为IGMP(0x2)
     4)IGMP协议的type字段为0x22(V3 report)
     相关的协议数据结构定义在以下几个系统头文件中,将其添加到代码头部。

#include /*ethhdr*/
#include /*iphdr*/
#include /*igmvp_report*/

     1)多播解析
     多播首先判断MAC地址的第1字节的最低位,分离出多播分组,然后再精确筛选出组播入组与退组的通告消息。

if(pkt->data[0]&0x1)//MC MAC
{
u64 igmpv3_dmac = 0x1600005E0001;
if(!ether_addr_equal(pkt->data,(u8 *)&igmpv3_dmac)) //IGMP
{
struct ethhdr *eth = (struct ethhdr *)pkt->data;
struct iphdr *ip = (struct iphdr *)(eth+1);
int oft = 0;
if(eth->h_proto = htons(0x0800) && ip->protocol == IPPROTO_IGMP)
{
oft = sizeof(*eth) + ip->ihl * sizeof(int);
igmpv3_report(pkt->um.inport,pkt->data+oft);
}
}
}

     2)多播学习
     精确筛选出IGMP的Report分组后,便可根据协议字段区分是入组消息或是退组消息。该步最核心的一步是要将IGMP中的组播IP地址转换为多播MAC,并将该MAC消息学习到多播MAC转发表中。多播MAC的转换规则有明确的定义要求,简言之就是MAC地址由三部分组成:高24位固定为01:00:5E,第23位为0,低23位为组播IP的低23位。

void igmpv3_report(u8 inport,u8 *igmp)
{
struct igmpv3_report *g3r = (struct igmpv3_report *)igmp;
if(g3r->type == IGMPV3_HOST_MEMBERSHIP_REPORT)/*IGMPv3_REPORT*/
{
u8 mc_mac[MAC_LEN] = {0x01,00,0x5E};
u8 *mcip = NULL;
int k = 0;
for(;kngrec);k++)
{
mcip = (u8 *)&g3r->grec[k].grec_mca;
memcpy(&mc_mac[3],&mcip[1],3);/*仅复制后3字节数据*/
mc_mac[3] &= 0x7F;/*将第23bit置0*/
join_leave_mc_mac(inport,mc_mac,g3r->grec[k].grec_type);
}
}
}

     多播MAC的学习过程封装在join_leave_mc_mac函数中,其核心操作方法与单播MAC的学习过程类似,只是在对端口号的处理不同。

if(type == IGMPV3_CHANGE_TO_EXCLUDE)/*入组*/
{
obx_mc_mac_tbl->row[i].port |= 1< }
else/*退组*/
{
obx_mc_mac_tbl->row[i].port &= ~ (1< }

     3)多播查表
     多播的查表与单播完全一致,只是查表的对象换成多播表,这两个查表过程可以代码优化成一个功能函数。
     4)多播输出
     多播的输出端口信息存储在返回值的每个bit位上,故需要根据输出端口的bit位是否为1来作为分组输出判断依据。单播和多播的统一输出函数如下:

void output(u8 outtype,int outport,struct fast_packet *pkt)
{
if(outtype == UC)
{
pkt->um.outport = outport;
pkt_send_normal(pkt,pkt->um.len);
}
else
{
int i = 0;
for(;i {
if(pkt->um.inport != i && (outport & (1< 0)
{
pkt->um.outport = i;
pkt_send_normal(pkt,pkt->um.len);
}
}
}
}

     一定要记得,底层API的输出端口号是常规表示方法,在多播输出时的端口号应该是变量i的值。
     1)确定协议支持
     硬件适合做简单、确定的事情,软件适合做灵活多变的事情。故若在硬件中支持组播的加入和退出,简单实现就是支持确定性的IGMPv3协议的Report功能,硬件可以不像软件一下逐层解析协议,可以直接将对应几个位置的数据都取出来之后一次判断,符合要求的消息即可继续完成后续功能。硬件不如软件灵活,对每一种确定协议的支持都需要确定的逻辑支撑,所以对于比较复杂的协议交互,一般在要设备中加入轻量级的CPU进行处理。一般像确定的组播协议可以放到FPGA实现,而生成树协议不适合FPGA实现。
     2)MAC表的老化
     老化简单来说就是删除长时间不使用的表项,把空间留出来给新的MAC地址使用。老化是为了解决MAC地址数量大于转发表空间容量的问题。若有些MAC地址使用一段时间之后,就一直不再使用或关机,则交换机无需再保留其在MAC转发表中。若不删除,则会导致新的MAC表找不到存储空间,导致大量的数据转化为泛洪发送,对整个网络来说是灾难性的,不可容忍的。当然,不计成本的扩大存储容量更是不可取的。故MAC表的老化是十分必要的,下篇主要内容讲述MAC表老化。
      欢迎您和学生们加入FAST开源项目群沟通与探讨,一起体验不一样的系统设计过程。请先加微信号15116127200后邀请入群。

关注FAST开源社区
FAST一一开源、开放、高速、高效、可编程、可定义!软硬件协同并行处理。