背景描述
在P4实现的L2基本交换机的基础上,当L2交换机在表中无法查询到MAC地址或目的MAC地址为广播地址(FF:FF:FF:FF:FF:FF)时,泛洪至各交换机。
本案例根据难易,先后使用两种方式进行实现:
- 首先搭建基本框架:泛洪至全部交换机(All-Port小节);
- 随后继续完善:泛洪至除源交换机外的各交换机(Other-Port小节)。
例程拓扑

拓扑描述文件
JSON格式
JSON配置文件中设置auto_arp_tables为false,可以使mininet不自动向主机下发ARP表。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
{
"p4_src": "p4src/l2_flooding.p4",
"cli": true,
"pcap_dump": true,
"enable_log": true,
"topology": {
"assignment_strategy": "l2",
"default": {
"auto_arp_tables": false
},
"links": [["h1", "s1"], ["h2", "s1"], ["h3", "s1"], ["h4","s1"]],
"hosts": {
"h1": { },
"h2": { },
"h3": { },
"h4": { }
},
"switches": {
"s1": {
"cli_input": "s1-commands-all-ports.txt"
}
}
}
}
|
Python脚本
在Python脚本中使用disableArpTables函数即可关闭ARP表的自动配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
from p4utils.mininetlib.network_API import NetworkAPI
net = NetworkAPI()
# Network general options
net.setLogLevel('info')
net.disableArpTables()
# Network definition
net.addP4Switch('s1')
net.setP4Source('s1','./p4src/l2_flooding.p4')
net.addHost('h1')
net.addHost('h2')
net.addHost('h3')
net.addHost('h4')
net.addLink('s1', 'h1')
net.addLink('s1', 'h2')
net.addLink('s1', 'h3')
net.addLink('s1', 'h4')
# Assignment strategy
net.l2()
# Nodes general options
net.enablePcapDumpAll()
net.enableLogAll()
net.enableCli()
net.startNetwork()
|
All-Port泛洪
main函数
1
2
3
4
5
6
7
8
9
10
11
|
#include <core.p4>
#include <v1model.p4>
V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;
|
定义头部
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
typedef bit<48> macAddr_t;
header ethernet_t {
macAddr_t dstAddr;
macAddr_t srcAddr;
bit<16> etherType;
}
struct metadata {
}
struct headers {
ethernet_t ethernet;
}
|
数据包解析
1
2
3
4
5
6
7
8
9
|
parser MyParser(packet_in packet,
out headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
state start {
packet.extract(hdr.ethernet);
transition accept;
}
}
|
检查校验和
1
2
3
|
control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
apply { }
}
|
入队列侧处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
action forward(bit<9> egress_port) {
standard_metadata.egress_spec = egress_port;
}
action broadcast() {
// 静态设置数据包转发的组播组ID为1
standard_metadata.mcast_grp = 1;
}
table dmac {
key = {
hdr.ethernet.dstAddr: exact;
}
actions = {
forward;
broadcast;
NoAction;
}
size = 256;
// 默认动作应为broadcast,即在匹配表中无法查到MAC地址后,将数据包泛洪
// 为了随后演示默认动作如何通过控制平面修改,此处先设置为NoAction
default_action = NoAction;
}
apply {
dmac.apply();
}
}
|
出队列侧处理
1
2
3
4
5
|
control MyEgress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
apply { }
}
|
计算校验和
1
2
3
|
control MyComputeChecksum(inout headers hdr, inout metadata meta) {
apply { }
}
|
封装数据包
1
2
3
4
5
6
|
control MyDeparser(packet_out packet, in headers hdr) {
apply {
//parsed headers have to be added again into the packet.
packet.emit(hdr.ethernet);
}
}
|
配置匹配表
在s1-commands-all-ports.txt命令文件中,配置以下规则:
- 添加L2转发表:在dmac表中添加MAC地址与转发端口的映射,匹配成功后执行动作forward;
- 使用table_set_default设置dmac表的默认动作为broadcast;
- 使用mc_mgrp_create命令创建组播组1;
- 使用mc_node_create命令创建多播节点组0,组中包含1、2、3、4端口;
- 使用mc_node_associate命令将多播节点组0与组播组1关联。
1
2
3
4
5
6
7
8
9
|
table_add dmac forward 00:00:0a:00:00:01 => 1
table_add dmac forward 00:00:0a:00:00:02 => 2
table_add dmac forward 00:00:0a:00:00:03 => 3
table_add dmac forward 00:00:0a:00:00:04 => 4
table_set_default dmac broadcast
mc_mgrp_create 1
mc_node_create 0 1 2 3 4
mc_node_associate 1 0
|
运行测试
Ping操作前,由于各主机没有被自动配置ARP表,因此各主机的ARP表为空:

Ping操作后,各主机学习到ARP表,表明交换机的泛洪操作生效且正确:

Other-Port泛洪
在该方式中,数据包的泛洪不会通过入端口进行转发,需要对入队列侧处理逻辑进行修改,核心在于多播组不能在代码中固定为1,存在2种方法:
- 首先根据dstAddr匹配目的MAC地址表dmac,若匹配动作为forward,则设置出端口,若匹配上broadcast动作,则执行多播,而使用哪一个多播组需要根据ingress_port在select_mcast_grp匹配表中查找;
- 首先根据dstAddr匹配目的MAC地址表dmac,若匹配动作为forward,则设置出端口,若匹配不上,则执行多播,而使用哪一个多播组需要根据ingress_port在select_mcast_grp匹配表中查找。
入队列侧处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
action forward(bit<9> egress_port) {
standard_metadata.egress_spec = egress_port;
}
action broadcast() {
}
table dmac {
key = {
hdr.ethernet.dstAddr: exact;
}
actions = {
forward;
broadcast;
NoAction;
}
size = 256;
default_action = NoAction;
}
// 设置数据包转发的组播组ID为匹配结果mcast_grp
action set_mcast_grp(bit<16> mcast_grp) {
standard_metadata.mcast_grp = mcast_grp;
}
// 定义匹配表,根据数据包入端口ingress_port查找使用哪一个组播组
table select_mcast_grp {
key = {
standard_metadata.ingress_port : exact;
}
actions = {
set_mcast_grp;
NoAction;
}
size = 32;
default_action = NoAction;
}
apply {
// dmac.apply().action_run返回匹配上的动作
// 若匹配上的动作为broadcast,则需要进一步查询select_mcast_grp匹配表
switch (dmac.apply().action_run) {
broadcast: {
select_mcast_grp.apply();
}
}
/* Alternative Solution (even easier)
if (dmac.apply().hit){
}
else {
select_mcast_grp.apply();
}
End of Alternative solution
*/
}
}
|
配置匹配表
在s1-commands-all-ports.txt命令文件中,配置以下规则:
- 添加L2转发表:在dmac表中添加MAC地址与转发端口的映射,匹配成功后执行动作forward;
- 使用table_set_default设置dmac表的默认动作为broadcast;
- 创建多播节点组0、1、2、3:例如多播节点组0包含端口2、3、4、多播节点组1包含端口1、3、4;
- 创建组播组,并将多播节点组与组播组关联;
- 为select_mcast_grp匹配表添加表项:添加入端口号与组播组ID的映射,例如从端口1进入的数据包通过组播1组播,而组播组1中的端口为2、3、4(多播节点组0)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
table_add dmac forward 00:00:0a:00:00:01 => 1
table_add dmac forward 00:00:0a:00:00:02 => 2
table_add dmac forward 00:00:0a:00:00:03 => 3
table_add dmac forward 00:00:0a:00:00:04 => 4
table_set_default dmac broadcast
#define broadcasting port groups
mc_node_create 0 2 3 4
mc_node_create 1 1 3 4
mc_node_create 2 1 2 4
mc_node_create 3 1 2 3
#associate node group with mcast group
mc_mgrp_create 1
mc_node_associate 1 0
mc_mgrp_create 2
mc_node_associate 2 1
mc_mgrp_create 3
mc_node_associate 3 2
mc_mgrp_create 4
mc_node_associate 4 3
#fill table selector
table_add select_mcast_grp set_mcast_grp 1 => 1
table_add select_mcast_grp set_mcast_grp 2 => 2
table_add select_mcast_grp set_mcast_grp 3 => 3
table_add select_mcast_grp set_mcast_grp 4 => 4
|