数据结构
对mac地址编写class数据结构
class SwitchTableElement
{
public:
mac_addr_t mac; //mac地址
//默认构造函数
SwitchTableElement()
{
for(int i = 0;i < 6;i++) mac[i] = 0;
}
//基于mac地址的构造函数
SwitchTableElement(mac_addr_t a)
{
for(int i = 0;i < 6;i++) mac[i] = a[i];
}
//复制构造函数
SwitchTableElement(const SwitchTableElement &a)
{
for(int i = 0;i < 6;i++) this->mac[i] = a.mac[i];
}
//重载赋值运算符
SwitchTableElement& operator=(const SwitchTableElement &a)
{
for(int i = 0;i < 6;i++) this->mac[i] = a.mac[i];
return *this;
}
//重载比较运算符,用于map散列
bool friend operator<(const SwitchTableElement& a, const SwitchTableElement& b)
{
long p = 0; long q = 0;
for(int i = 0;i < 6;i++)//将6个8bit的地址聚合为long类型来完成比较
{
p += (a.mac[i] << ((5 - i) * 8));
q += (b.mac[i] << ((5 - i) * 8));
}
return p < q;
}
};
从端口处理进入的帧
ProcessFrame函数,收取framePtr并提取所需信息
- srcAddr
- dstAddr
将提取到的源地址和目的地址转换为mac地址(SwitchTableElement类)
构造map,用于存储转发表(pair的两个变量分别是该条路径的老化记数和该条路径的起始地址——源地址)
std::map<SwitchTableElement, std::pair<int, int> >::iterator it;
map帮助我们在逆向学习时定位secMac是否是存在于转发表中的,如果存在,则更新老化记数,否则应当进行逆向学习
if(switchTable.find(srcMac) == switchTable.end())//不存在,逆向学习
{
std::pair<int, int> p(10, inPort);
switchTable[srcMac] = p;
}
对广播帧进行处理
从framePtr中提取framType
老化指令
对每一个mac表项,将老化计数减1,归零删除该表项
for(it = switchTable.begin();it != switchTable.end();it++)
{
it->second.first--;
if(it->second.first <= 0)
{
it = switchTable.erase(it);//erase返回此次删除后下一个有效的迭代器
it--;//循环结束会++,故此处--使得下一次循环遍历到此次删除后的下一个有效迭代器
}
}
待转发的帧
else//待转发的帧
{
//检查目的mac是否在转发表中且有效
for(it = switchTable.begin();it != switchTable.end();it++)
{
if(isSameMac(dstAddr, (uint8_t*)(it->first.mac)) && it->second.first > 0)
{
break;
}
}
if(it == switchTable.end())//不在转发表中或已经失效,则广播
{
return 0;
}
else//可以找到有效的转发表项
{
if(inPort == it->second.second) return -1;//出入端口相同,丢弃
else return it->second.second;//出入端口不同,转发
}
}
复盘
- 数据结构可以简化你的工作量和思路,我的代码是以两段struct构造数组嵌套而成的转发表,这加大了我的代码编写难度和思维量。
- 对于一些细节bug需要把握,比如:
if(it->second.first <= 0) { it = switchTable.erase(it);//erase返回此次删除后下一个有效的迭代器 it--;//循环结束会++,故此处--使得下一次循环遍历到此次删除后的下一个有效迭代器 }
- 广播的返回值是需要值得注意的。