Featured image of post P4示例程序-4 组播与泛洪

P4示例程序-4 组播与泛洪

在L2基本交换机的基础上实现的泛洪与组播,当L2交换机在表中无法查询到MAC地址或目的MAC地址为广播地址时,从各端口泛洪出去

背景描述

在P4实现的L2基本交换机的基础上,当L2交换机在表中无法查询到MAC地址或目的MAC地址为广播地址(FF:FF:FF:FF:FF:FF)时,泛洪至各交换机。

本案例根据难易,先后使用两种方式进行实现:

  1. 首先搭建基本框架:泛洪至全部交换机(All-Port小节);
  2. 随后继续完善:泛洪至除源交换机外的各交换机(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命令文件中,配置以下规则:

  1. 添加L2转发表:在dmac表中添加MAC地址与转发端口的映射,匹配成功后执行动作forward;
  2. 使用table_set_default设置dmac表的默认动作为broadcast;
  3. 使用mc_mgrp_create命令创建组播组1;
  4. 使用mc_node_create命令创建多播节点组0,组中包含1、2、3、4端口;
  5. 使用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种方法:

  1. 首先根据dstAddr匹配目的MAC地址表dmac,若匹配动作为forward,则设置出端口,若匹配上broadcast动作,则执行多播,而使用哪一个多播组需要根据ingress_port在select_mcast_grp匹配表中查找;
  2. 首先根据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命令文件中,配置以下规则:

  1. 添加L2转发表:在dmac表中添加MAC地址与转发端口的映射,匹配成功后执行动作forward;
  2. 使用table_set_default设置dmac表的默认动作为broadcast;
  3. 创建多播节点组0、1、2、3:例如多播节点组0包含端口2、3、4、多播节点组1包含端口1、3、4;
  4. 创建组播组,并将多播节点组与组播组关联;
  5. 为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
Licensed under CC BY-NC-SA 4.0
皖ICP备2025083746号-1
公安备案 陕公网安备61019002003315号



使用 Hugo 构建
主题 StackJimmy 设计