二层交换机原型设计与实现(七)

一、概述

上一篇讲述了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 <linux/if_ether.h> /*ethhdr*/
#include <linux/ip.h> /*iphdr*/
#include <linux/igmp.h> /*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(;k<ntohs(g3r->ngrec);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<<inport;
}
else/*退组*/
{
  obx_mc_mac_tbl->row[i].port &= ~ (1<<inport);
}

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<OBX_PORT_CNT;i++)
    {
      if(pkt->um.inport != i && (outport & (1<<i)) > 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表老化。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值