P4 04-L2_Flooding

P4示例程序-04 L2组播与泛洪

  • 功能

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

  • 拓扑结构

拓扑结构
  • 代码

  • All Port

  1. p4app-all-ports.json
    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"
    }
    }
    }
    }
  2. Headers
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    typedef bit<9>  egressSpec_t; // 定义 egressSpec_t 类型,9 位,用于指定输出端口
    typedef bit<48> macAddr_t; // 定义 macAddr_t 类型,48 位,用于表示 MAC 地址
    typedef bit<32> ip4Addr_t; // 定义 ip4Addr_t 类型,32 位,用于表示 IPv4 地址

    header ethernet_t { // 定义以太网头部 ethernet_t
    macAddr_t dstAddr; // 目的 MAC 地址
    macAddr_t srcAddr; // 源 MAC 地址
    bit<16> etherType; // 协议类型字段
    }

    struct metadata { // 定义元数据结构
    /* empty */ // 当前为空,后续可扩展
    }

    struct headers { // 定义 headers 结构
    ethernet_t ethernet; // 包含一个以太网头部
    }
  3. Parser
    1
    2
    3
    4
    5
    6
    7
    8
    9
    parser MyParser(packet_in packet, // 解析器 MyParser,输入报文 packet
    out headers hdr, // 输出解析后的头部 hdr
    inout metadata meta, // 输入输出元数据
    inout standard_metadata_t standard_metadata) { // 标准元数据
    state start { // 起始状态
    packet.extract(hdr.ethernet); // 提取以太网头部
    transition accept; // 解析完成,跳转 accept
    }
    }
  4. Checksum Verification
    1
    2
    3
    control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
        apply {  }
    }
  5. Ingress Processing
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    control MyIngress(inout headers hdr, // 入方向处理
    inout metadata meta,
    inout standard_metadata_t standard_metadata) {
    action drop() { mark_to_drop(standard_metadata); } // 丢弃报文
    action forward(bit<9> egress_port) { standard_metadata.egress_spec = egress_port; } // 转发到指定端口
    action broadcast() { standard_metadata.mcast_grp = 1; } // 设置多播组 ID=1,实现广播

    table dmac { // 定义 dmac 表
    key = { hdr.ethernet.dstAddr: exact; } // 匹配目的 MAC,精确匹配
    actions = { forward; broadcast; NoAction; } // 表可执行的动作
    size = 256; // 表容量 256
    default_action = NoAction; // 默认 NoAction
    }

    apply { dmac.apply(); } // 应用 dmac 表
    }
  6. Egress Processing
    1
    2
    3
    4
    5
    control MyEgress(inout headers hdr,
    inout metadata meta,
    inout standard_metadata_t standard_metadata) {
    apply { } // 出方向处理,此处为空
    }
  7. Checksum Computation
    1
    2
    3
    control MyComputeChecksum(inout headers hdr, inout metadata meta) {
    apply { } // 校验和计算,此处为空
    }
  8. DE parser
    1
    2
    3
    4
    5
    control MyDeparser(packet_out packet, in headers hdr) {
    apply {
    packet.emit(hdr.ethernet); // 输出以太网头部
    }
    }
  9. Switch
    1
    2
    3
    4
    5
    6
    7
    8
    V1Switch(
    MyParser(),
    MyVerifyChecksum(),
    MyIngress(),
    MyEgress(),
    MyComputeChecksum(),
    MyDeparser()
    ) main;
  10. s1-commands-all-ports.txt
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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

  11. controller-all-ports.py
    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
    from p4utils.utils.helper import load_topo
    from p4utils.utils.sswitch_p4runtime_API import SimpleSwitchP4RuntimeAPI


    topo = load_topo('topology.json')
    controllers = {}
    sw_name = 's1'

    for switch, data in topo.get_p4rtswitches().items():
    controllers[switch] = SimpleSwitchP4RuntimeAPI(data['device_id'], data['grpc_port'],
    p4rt_path=data['p4rt_path'],
    json_path=data['json_path'])

    controller = controllers[sw_name]

    # Populate table dmac
    controller.table_add("dmac", "forward", ['00:00:0a:00:00:01'], ['1'])
    controller.table_add("dmac", "forward", ['00:00:0a:00:00:02'], ['2'])
    controller.table_add("dmac", "forward", ['00:00:0a:00:00:03'], ['3'])
    controller.table_add("dmac", "forward", ['00:00:0a:00:00:04'], ['4'])
    controller.table_set_default("dmac", "broadcast", [])

    # Get port list
    interfaces_to_port = topo.get_node_intfs(fields=['port'])[sw_name].copy()
    # Filter lo and cpu port
    interfaces_to_port.pop('lo', None)
    interfaces_to_port.pop(topo.get_cpu_port_intf(sw_name), None)
    port_list = list(interfaces_to_port.values())

    # Add broadcast group
    controller.mc_mgrp_create(1, port_list)
  • Other Port

  1. p4app-other-ports.json
    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-other-ports.txt"
    }
    }
    }
    }
  2. Headers
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    typedef bit<9>  egressSpec_t; // 定义 9 位输出端口类型
    typedef bit<48> macAddr_t; // 定义 48 位 MAC 地址类型
    typedef bit<32> ip4Addr_t; // 定义 32 位 IPv4 地址类型

    header ethernet_t { // 定义以太网头部
    macAddr_t dstAddr; // 目的 MAC 地址
    macAddr_t srcAddr; // 源 MAC 地址
    bit<16> etherType; // 以太类型字段
    }

    struct metadata { // 定义元数据结构
    /* empty */ // 当前为空
    }

    struct headers { // 定义头部集合
    ethernet_t ethernet; // 包含以太网头部
    }
  3. Parser
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    parser MyParser(packet_in packet, // 定义解析器,输入为 packet
    out headers hdr, // 输出解析的头部 hdr
    inout metadata meta, // 输入输出元数据
    inout standard_metadata_t standard_metadata) { // 标准元数据

    state start { // 解析器起始状态
    packet.extract(hdr.ethernet); // 从报文中提取以太网头部
    transition accept; // 跳转到 accept 状态,解析结束
    }
    }
  4. Checksum Verification
    1
    2
    3
    control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
        apply {  }
    }
  5. Ingress Processing
    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
    62
    63
    64
    65
    66
    67
    control MyIngress(inout headers hdr, // 入方向处理控制块
    inout metadata meta,
    inout standard_metadata_t standard_metadata) {

    action drop() { // 定义丢弃动作
    mark_to_drop(standard_metadata); // 标记丢弃报文
    }

    action forward(bit<9> egress_port) { // 定义转发动作
    standard_metadata.egress_spec = egress_port; // 设置输出端口
    }

    action broadcast() { // 定义广播动作
    //Empty action that was not necessary, we just call it when there is a table miss // 该动作为空,仅在表项未命中时调用
    }

    table dmac { // 定义 dmac 表,用于目的 MAC 匹配
    key = {
    hdr.ethernet.dstAddr: exact; // 精确匹配目的 MAC 地址
    }

    actions = {
    forward; // 转发动作
    broadcast; // 广播动作
    NoAction; // 无动作
    }
    size = 256; // 表大小 256 项
    default_action = NoAction; // 默认 NoAction
    }

    action set_mcast_grp(bit<16> mcast_grp) { // 定义设置多播组动作
    standard_metadata.mcast_grp = mcast_grp; // 设置多播组 ID
    }

    table select_mcast_grp { // 定义 select_mcast_grp 表,根据 ingress_port 选择多播组

    key = {
    standard_metadata.ingress_port : exact; // 匹配入口端口
    }
    actions = {
    set_mcast_grp; // 设置多播组动作
    NoAction; // 无动作
    }
    size = 32; // 表大小 32
    default_action = NoAction; // 默认 NoAction

    }

    apply {

    switch (dmac.apply().action_run) { // 根据 dmac 表执行的动作结果分支
    broadcast: {
    select_mcast_grp.apply(); // 如果动作是 broadcast,则执行 select_mcast_grp 表
    }
    }

    /* Alternative Solution (even easier) // 另一种更简单的解决方案
    if (dmac.apply().hit){ // 如果 dmac 表命中
    }
    else {
    select_mcast_grp.apply(); // 未命中则应用 select_mcast_grp 表
    }
    End of Alternative solution // 另一种解决方案结束
    */

    }
    }
  6. Egress Processing
    1
    2
    3
    4
    5
    6
    7
    8
    control MyEgress(inout headers hdr, // 出方向处理控制块
    inout metadata meta,
    inout standard_metadata_t standard_metadata) {

    apply {
    } // 当前为空
    }

  7. Checksum Computation
    1
    2
    3
    4
    control MyComputeChecksum(inout headers hdr, inout metadata meta) {
    apply {
    } // 当前为空
    }
  8. DE parser
    1
    2
    3
    4
    5
    control MyDeparser(packet_out packet, in headers hdr) {
    apply {
    // 解析的头部需要重新封装回报文
    packet.emit(hdr.ethernet); // 输出以太网头部
    }
  9. Switch
    1
    2
    3
    4
    5
    6
    7
    8
    V1Switch(
    MyParser(),
    MyVerifyChecksum(),
    MyIngress(),
    MyEgress(),
    MyComputeChecksum(),
    MyDeparser()
    ) main;
  10. s1-commands-other-ports.txt
    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
    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

  11. controller-other-ports.py
    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
    from p4utils.mininetlib.network_API import NetworkAPI

    net = NetworkAPI()

    # Network general options
    net.setLogLevel('info')
    net.setCompiler(p4rt=True)
    net.execScript('python controller-other-ports.py', reboot=True)
    net.disableArpTables()

    # Network definition
    net.addP4RuntimeSwitch('s1')
    net.setP4Source('s1','./p4src/l2_flooding_other_ports.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()
  • P4仿真(以other-port为例)

  1. 建立配置匹配表s1-commands-other-ports.txt
建立配置表
  1. 启动网络拓扑
启动网络拓扑
  1. Ping操作前,由于各主机没有被自动配置ARP表,因此各主机的ARP表为空
ping操作前
  1. Ping操作后,各主机学习到ARP表,表明交换机的泛洪操作生效且正确
ping操作后

P4 04-L2_Flooding
http://example.com/2025/07/28/P4 04-L2_Flooding/
作者
Wsdbybyd
发布于
2025年7月28日
许可协议