1.实验题目
交换机转发实验
2.实验目的
- 学习交换机的工作原理;
- 学习交换机转发和广播数据包的流程;
- 学习转发表的数据结构;
- 学习转发表的维护操作。
3.实验环境
- 虚拟平台:Vmware Workstation Pro
- 操作系统:Ubuntu 16.04 LTS
- gcc:5.4.0
- python:2.7.12
- Mininet:2.3.0
- Wireshark:2.6.10
4.实验内容
- 在主机 h2 和 h3 上分别打开 wireshark 程序,监听各自主机的 eth0 端口(h2-eth0 和 h3-eth0)。
- 在 h1 主机上分别 ping h2 和 h3 两个主机,在 h2 和 h3 两个主机上的 wireshark 捕获的应该只包含自己节点和 h1 产生的数据包。
5.实验流程
- 首先分析交换机中所包含的数据结构:
①端口信息结构体:iface_info_t
该结构体存放了交换机上的端口信息,包括文件描述符、端口ID、mac地址、端口名称,并通过内核链表进行连接。
②mac 地址与端口映射表:mac_port_entry
该结构体存放了交换机记录的转发mac地址,与mac地址对应的转发端口信息,以及老化时间visited,也是通过内核链表进行连接。
③mac 地址与 hash 映射表:mac_port_map_t
该结构体通过内存放了256个hash key,每个hash key 通过内核链表串联起映射到自己的mac地址表(即上述第②个结构体),结构体内还包含互斥锁供多线程操作。
- 实现对数据结构 mac_port_map 的所有操作,以及数据包的转发和广播操作。
①首先实现 iface_info_t *lookup_port(u8 mac[ETH_ALEN])函数,该函数的难点在于遍历 hash 表来找到对应的端口号,但内核链表给我们提供了宏函数 list_for_each_entry 来进行遍历操作。
我的实现过程是首先根据 mac 地址计算对应的 hash 值,然后通过 list_for_each_entry 宏函数对对应 hash 值下的链表进行遍历,遍历时加互斥锁,退出时解锁,通过 findFlag 作为找到对应 mac 地址的标志。若找到,则在退出时更新老化时间 visit,并返回转发端口;若未找到,则在退出时返回 null。
②接下来实现 void insert_mac_port(u8 mac[ETH_ALEN], iface_info_t *iface)函数。
该函数主要是对结构体 mac_port_entry_t 的操作,首先构建要插入的端口信息结构体,然后计算该收到包的 mac 地址对应的 hash 值,将该结构体插入到对应 hash 值下的链表,该插入操作是通过内核链表提供的宏函数 list_add_head 所完成的,执行插入操作时,对映射表加互斥锁。
③接下来是 int sweep_aged_mac_port_entry()函数的实现。
该函数的难点在于遍历并删除超过 30 秒未访问的转发条目,通过内核链表提供的 list_for_each_entry_safe 宏函数对链表进行遍历,找到与当前时间差超过 30 的条目并执行删除操作,删除操作使用的也是系统提供的宏函数。在调试过程中发现,执行 free 操作必须要使用 safe 的遍历方法,否则会产生指针错误。
④接下来是 void broadcast_packet(iface_info_t *iface, char *packet, int len)函数的实现。
该函数要实现的是广播操作,通过 list_for_each_entry 函数进行遍历,找到非发送端口并将数据包发送出去,实现广播操作。
⑤最后是实现 void handle_packet(iface_info_t *iface, char *packet, int len)函数。
该函数要实现的是包处理功能,收到数据包后首先获取包内的目的地址,通过 lookup_port 函数查找转发表中是否存在对应的转发端口,若存在,则通过对应端口转发,否则广播该数据包。之后获取数据包的源地址,在转发表中若未找到该地址的条目,则将其 mac 地址以及对应端口插入至转发表。
- 基于 Ubuntu 系统,在 mininet 平台验证交换机程序的正确性
-
首先通过 make 命令,对所有代码进行联合编译。
-
打开终端,运行 sudo python2 three_nodes_bw.py 命令,启动老师给定的拓扑结构。
-
在 mininet 平台上,通过 xterm s1 h2 h3 命令,启动 s1,h2,h3 的虚拟终端。
-
在 s1 上运行./switch 命令,启动交换机程序。
-
在 h2 和 h3 上运行 sudo wireshark 命令,启动 wireshark 软件,并监听各自的 eth0 端口的网络数据。至此的运行界面如图 1 所示。
-
在 mininet 中运行 h1 ping h2 和 h1 ping h3 命令。
h1 ping h2 的结果如图 2 所示,在 h2-eth0 端口捕获到了源地址为 h1 目的地址为 h2 的数据包,同时,在 h3-eth0 端口没有捕捉到来自 h1 的数据包,只有 ARP 的广播地址包。
h1 ping h3 的结果如图 3 所示,在 h3-eth0 端口捕获到了源地址为 h1 目的地址为 h3 的数据包,同时,在 h2-eth0 端口没有捕捉到来自 h1 的数据包,只有 ARP 的广播地址包。
老化操作如图 4 所示,s1 主机展示了删除表项的信息(最后一行)。
实验结果表明,基本符合实验要求,实现了交换机对数据包的转发功能以及转发表的维护。
图 1 实验初始化环境展示
图 2 h1 ping h2 实验结果
图 3 h1 ping h3 实验结果
图 4 s1 交换机删除表项信息
6.实验问题分析
- 实验过程中遇到的第一个问题是内核链表的使用,通过资料的查找和学习,理解了其工作原理,通过计算结构体中链表的地址以及结构体的地址来找到结构体指针应该指向的首位置,以此来实现对结构体的访问以及修改等操作。
- 代码编写过程中,在遍历链表时卡了一段时间,主要原因是因为先使用了系统提供的宏函数 list_for_each_entry(),并在遍历过程中 free()了传入的指针,之后查找了更多的资料后,发现了系统也提供了一种 safe 的遍历方法,即多借用一个中转指针来保存遍历指针位置,可以安全的调用 free()方法且不会引起错误。
for_each_entry(),并在遍历过程中 free()了传入的指针,之后查找了更多的资料后,发现了系统也提供了一种 safe 的遍历方法,即多借用一个中转指针来保存遍历指针位置,可以安全的调用 free()方法且不会引起错误。