Featured image of post P4示例程序-7 利用Meter表限速

P4示例程序-7 利用Meter表限速

在P4交换机中使用Meter对端口进行速率限制

例程拓扑

该例程实现了简单的双端口交换机,使得两个主机能够进行通信,为了实现带宽限速,需要使用Meter表。Meter本质就是一个流量计量器,它支持实时统计通过的流量,并根据配置的速率限制流量。底层实现一般是用令牌桶或者类似算法,令牌桶维护两个计数器:桶容量代表允许突发的最大流量、令牌生成速率用于控制流量的平均速率。在交换机中的逻辑实现可采用如下配置,当有数据包到达时:

  1. 桶里有足够令牌时:包被标记为绿色(正常,合规流量),消耗对应令牌数;
  2. 桶里令牌不足但尚未完全溢出时:包被标记为黄色(轻微超出限速,可以容忍);
  3. 令牌完全用完时:包被标记为红色(超出限速,丢弃或降级处理)。

静态配置方案

拓扑描述文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
  "p4_src": "repeater.p4",
  "cli": true,
  "pcap_dump": true,
  "enable_log": true,
  "topology": {
    "assignment_strategy": "l2",
    "links": [["h1", "s1"], ["h2", "s1"],
    "hosts": {
      "h1": {},
      "h2": {}
    },
    "switches": {
      "s1": {
      }
    }
  }
}

交换机程序

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
const bit<2> MeterColorGreen = 0;
const bit<2> MeterColorYellow = 1;
const bit<2> MeterColorRed = 2;

struct metadata {
    bit<2> meter_color;
}

struct headers {
}

数据包解析

1
2
3
4
5
6
7
8
parser MyParser(packet_in packet,
                out headers hdr,
                inout metadata meta,
                inout standard_metadata_t standard_metadata) {
    state start{
        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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
control MyIngress(inout headers hdr,
                  inout metadata meta,
                  inout standard_metadata_t standard_metadata) {
    // direct_meter指该meter是直接绑定的,匹配表的每个条目自动关联一个Meter实例
    // 在底层实施时,匹配表的每个条目都有一个索引
    // 控制平面在添加表项时,条目索引会自动关联对应的Meter实例
    direct_meter<bit<2>>(MeterType.bytes) port_meter;

    action drop() {
        mark_to_drop(standard_metadata);
    }
    
    // 读取Meter实例,将令牌桶的状态写入自定义的元数据中
    action do_meter() {
        port_meter.read(meta.meter_color);
    }
    
    // Meter必须在Table中使用,使得每一个表项都可以绑定到一个具体的Meter实例
    // 例如table_add meter_table do_meter 1 =>命令是为入端口1创建了Meter实例
    // 而table_add meter_table do_meter 2 =>命令是为入端口2创建了Meter实例
    table meter_table {
        // 根据入端口号匹配、使用Meter实例
        key = {
            standard_metadata.ingress_port: exact;
        }
        actions = {
            do_meter;
            NoAction;
        }
        default_action = NoAction();
        // 本表支持使用port_meter这个Meter
        meters = port_meter;
        size = 2;
    }

    apply {
        // 为简化代码,转发逻辑静态固定
        if (standard_metadata.ingress_port == 1){
            standard_metadata.egress_spec = 2;
        }
        else if (standard_metadata.ingress_port == 2){
            standard_metadata.egress_spec = 1;
        }
        
        // 初始化元数据
        meta.meter_color = MeterColorGreen;
        // 根据入端口读取令牌桶状态
        meter_table.apply();
        // 如果令牌桶中的令牌不足,则将当前数据包丢弃
        if (meta.meter_color != MeterColorGreen) {
            drop();
        }
    }
}

出队列侧处理

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
control MyDeparser(packet_out packet, in headers hdr) {
    apply { }
}

运行测试

Thrift命令行

在启动P4网络后,使用Thrift客户端连接至P4交换机,进而进行配置:

1
2
sudo p4run
simple_switch_CLI --thrift-port 9090

为端口1添加匹配表规则,即为其关联一个Meter表实例,需留意添加条目后返回的handle值,该值即为该条目的索引编号:

1
table_add meter_table do_meter 1 =>

通过help命令可以查看设置Meter表的命令,根据提示即可进行Meter实例中令牌的设置:

1
2
help meter_set_rates
meter_set_rates port_meter 0 6.25:700000 6.25:700000

通过测速可观察到交换机端口1上的限速已生效:

1
2
mininet > iperf h1 h2
mininet > iperf h2 h1

若需要删除Meter限速,直接删除在meter_table匹配表中的相应条目即可:

1
2
help table_delete
table_delete meter_table 0

Thrift脚本

使用Python脚本同样可以完成上述在命令行中的操作,以下只提供核心函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 清空交换机中的匹配表、寄存器等
controllers[sw_name].reset_state()

# 添加匹配表条目,并获取条目的索引编号
entry_handle = controllers[sw_name].table_add('meter_table', 'do_meter', ['1'], [])

# 设置Meter实例
rates = self.get_meter_rates_from_bw(bw)
self.controllers[sw_name].meter_set_rates('port_meter', entry_handle, rates)

# 若需要删除Meter限速,直接删除在meter_table匹配表中的相应条目即可
controllers[sw_name].table_delete('meter_table', entry_handle, True)

# 根据带宽计算出令牌桶CIR和POR所需的参数
def get_meter_rates_from_bw(self, bw, burst_size=700000):
    # bw (float): desired bandwdith in mbps
    # burst_size (int, optional): Max capacity of the meter buckets. Defaults to 50000.
    rates = []
    rates.append((0.125 * bw, burst_size))
    rates.append((0.125 * bw, burst_size))
    return rates
Licensed under CC BY-NC-SA 4.0
皖ICP备2025083746号-1
公安备案 陕公网安备61019002003315号



使用 Hugo 构建
主题 StackJimmy 设计