0731-84728105
15116127200
二层交换机原型设计与实现(五)
发布时间:2021-05-24
     MAC地址的学习是指使用分组中的源MAC地址进行查表,最后添加或更新到MAC转发表中。目的MAC查找是指使用分组中的目的MAC进行查表,获得该MAC在学习中保存的端口号信息。两个过程都需要对表进行遍历操作,根据逻辑功能的不同,其输入输出参数也不一样。二层交换的核心逻辑就是这两个功能函数。
     1)源MAC提取
     首先,源MAC地址获取要根据MAC层协议来解析,从其对应的位置提取相应的数据。其次,源MAC的提取有多种方式,主要取决于对MAC地址的操作方式,如相等比较。由于MAC地址是不规整的数据类型,通常可以使用内存块的比较方式或拆分成几部分的方式比较,拆分一般可分为2+2+2;两种方式都要使用指针传递参数。

/*分组源MAC指针获取*/
&pkt->data[MAC_LEN]/*MAC_LEN宏定义为6,表示MAC地址占6个字节*/
/*判断两个MAC地址是否相等*/
int ether_addr_equal(u8 *addr1,u8 *addr2)
{
u16 *a = (u16 *)addr1;
u16 *b = (u16 *)addr2;
return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) != 0;
}

      2)学习过程
     前面分析过,在学习过程中并不清楚原MAC转发表中是否存在原表项,如果先查一次是否存在,再查一次哪有空位用作存储,则需要做两次全表遍历。所以,针对MAC学习的处理方式就是不管有没有,都当做是新增的方式处理。若查表不存在则存储在一个空白表项位置,若查表存在,则刷新端口信息。

void learn_smac(u8 inport,u8 *smac)
{
int i = 0,j = -1;
u64 zero_mac = 0;/*定义一个全零MAC地址*/
xprintf("learn_smac->\n");
for(;i {
if(!ether_addr_equal(smac,obx_mac_tbl->row[i].mac))
{
/*MAC转发表当前i行的MAC地址与输入参数smac相等*/
if(obx_mac_tbl->row[i].port != inport)
{
/*这个MAC地址发生了端口迁移*/
}
obx_mac_tbl->row[i].port = inport;
return;/*学习过程完成,立即返回*/
}
else if(j == -1 && !ether_addr_equal((u8 *)&zero_mac,obx_mac_tbl->row[i].mac))
{
j = i;/*记录第一个找到为空白表项位置*/
}
}
/*j==-1说明既没有匹配上MAC,也找不到空闲位置存储*/
if(j == -1)
{
xprintf("learn_smac->Table overflow!\n");
return;
}
/*将该MAC存储到j的位置*/
memcpy(obx_mac_tbl->row[j].mac,smac,MAC_LEN);
obx_mac_tbl->row[j].port = inport;
xprintf("learn_smac->add new MAC,port:%d,index:%d\n",inport,j);
}

     1)目的MAC提取
     目的MAC提取与源MAC类似,在参数传递过程中均用指针方式,故其表示方式为:

/*分组目的MAC指针获取*/
pkt->data/*数组名即为指针*/

     2)查表过程
     查表过程就是一个简单的全表搜索,找到的匹配的MAC地址,则返回其学习到的端口号。若是没有找到匹配的MAC,则需要用个特别的数字(-1)来区分正常的端口号。

int find_dmac(u8 inport,u8 *dmac)
{
int i = 0,ret = -1;/*匹配不到相同MAC,则返回-1*/
for(;i {
if(obx_mac_tbl->row[i].port != inport
&& !ether_addr_equal(dmac,obx_mac_tbl->row[i].mac))
{
ret = obx_mac_tbl->row[i].port;
break;
}
}
xprintf("find_dmac->ret = %d\n",ret);
return ret;
}

     1)表的查找
     表的查找与表的设计相关,如上我们设计的是一种简单的数组结构,故也只能进行顺序查找的方式进行遍历。这种查表方式在实际应用场景下一般不会使用,但在设计原型系统时却很方便。顺序查表根据表的大小和使用条数增加会导致查表速度越来越慢,上述在源MAC学习过程中,会顺带把空闲位置也找出来,减少一次表的遍历。那么查目的MAC时也需要遍历一次表,我们是不是也可以都放在这一次表的遍历中完成呢?当然是可以的,只是这样实现对业务的逻辑理解没有那么好,但对表的遍历只需要一次即可,从执行速度上来说确实会提升。
     另外,在对表的高效性处理方面,一般不会采用全表项匹配或多字段匹配的方法,在表设计时会使用一个有效位字段,通过有效位的简单比较就可确定表项是否为空或存在有效数据。这种方法普遍存在于硬件逻辑设计中,硬件的查表方式也多种多样,通常使用基于内容可寻址存储器(CAM)方式查表,既简单又高效。
     2)分组输出
     二层交换的分组输出主要根据查目的MAC的结果来处理,当查询到相应的输出端口后,即可从指定的端口输出;当查不到该MAC的端口信息时,则只能通过泛洪的方式转发,这是在交换机层面确保数据不丢包的一种措施,宁可多发包,也不丢包。当然,广播地址也是需要泛洪的,多播地址则需要根据其组信息进行端口组发送。下一篇文章我们来说一下分组输出的单播和多播。
      欢迎您和学生们加入FAST开源项目群沟通与探讨,一起体验不一样的系统设计过程。请先加微信号15116127200后邀请入群。

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