Contents

P4-P4 Utils学习笔记

P4-utils官方文档

P4-utils详细API参考

介绍

P4-Utils 是一个 Python 包,允许用户创建和测试可以包含 P4 交换机的虚拟网络。

P4是一种特定领域的编程语言,它指定数据平面设备如何处理数据包。

p4app由P4社区创建,提供基于P4语言的测试和原型平台。P4-Utils是ETH Networked Systems Group为简化p4app使用和开发而进行改进的成果。

安装与卸载

前提条件

  • 安装PI LIBRARY REPOSITORY为 P4Runtime 服务器提供了一个实施框架。
  • 安装BEHAVIORAL MODEL (bmv2)提供软件交换机(simple_switch和 simple_switch_grpc)的实现
  • 安装P4c提供P4编译器
  • 安装mininet提供网络模拟环境
  • 安装FRRouting提供用于 Linux 和 Unix 平台的免费开源 Internet 路由协议套件

安装

1
2
3
git clone https://github.com/nsg-ethz/p4-utils
cd p4-utils
sudo ./install.sh

卸载

1
sudo ./uninstall.sh

用法

仅考虑python脚本方法,不考虑json方法

网络设置

创建一个网络

1
2
3
from p4utils.mininetlib.network_API import NetworkAPI

net = NetworkAPI()

设置脚本执行期间显示的日志的详细程度(debug,info,output,warning,error,critical)

1
net.setLogLevel('info')

默认情况下,网络主机的 ARP 表在网络启动时以静态方式填充。disableArpTables()和disableGwArp()方法为同一子网内的主机及其网关禁用静态 ARP 条目。

添加节点,确保都具有唯一的名称

1
2
net.addP4Switch('s1')
net.addHost('h1')

用P4程序来配置P4交换机,支持相对路径和绝对路径,该文件将被编译,然后配置给交换机

1
net.setP4Source('s1','l2_forwarding.p4')

添加链接,链接的节点预先添加

1
net.addLink('s1', 'h1')

可以指定节点对于链接的端口号,不指定端口号,将执行自动分配。自动分配规则是固定的,所以网络脚本的多次执行自动分配的端口号是一致的。

1
net.setIntfPort('s1', 'h1', 1)  # Set the number of the port on s1 facing h1

可以限制链接带宽

1
2
3
4
# 单个链接的带宽
net.setBw('s1','h1', 5)
# 一次性设置所有链接的带宽
net.setBwAll(5)

3种方式为节点分配 IP 和 MAC

  • 默认所有节点的IP位于网段10.0.0.0/8,MAC地址是随机的
  • 为每个接口手动指定IP和MAC
    • setIntfIp()方法设置IP地址:net.setIntfIp('h1','s1','10.0.0.1/24') # The interface of h1 facing s1 has IP 10.0.0.1/24
    • setIntfMac()方法设置MAC地址:net.setIntfIp('h1','s1','00:00:00:00:00:01') # The interface of h1 facing s1 has MAC 00:00:00:00:00:01
  • 预定义的自动分配策略,后面会详细讲解:
    • l2策略:net.l2()
    • mixed:net.mixed()
    • l3:net.l3()

所有主机都在一个网段可以设置l2策略

然后可以设置节点通用选项。比如为 P4 交换机启用.pcap文件转储和日志记录

1
2
3
4
5
6
# 为所有交换机配置
net.enablePcapDumpAll(pcap_dir='./pcap')
net.enableLogAll(log_dir='./log')
# 为指定交换机配置
net.enablePcapDump(name, pcap_dir='./pcap')
net.enableLog(name, log_dir='./log')

启用网络客户端并启动网络

1
2
net.enableCli()
net.startNetwork()

执行网络,以超级用户权限运行 Python 脚本

1
sudo python3 network.py

自动分配策略

主机命名为hi,交换机命名为si,i为数字

l2

将所有设备置于同一个 IPv4 网段中 ( 10.0.0.0/16)

主机ip分配为10.0.x.y/16,x和y为主机id的高字节和低字节

mixed

将连接到同一交换机的主机置于同一子网中,将不同的交换机(甚至连在一起的交换机)置于不同的子网中。

  • 主机ip:10.x.y.z/24,x和y为网关交换机id的高字节和低字节,z为主机id
  • 直连所在网段的交换机端口:10.x.y.254/24,x和u分别是网关交换机id的高字节和低字节
  • 两个交换机互联的端口:20.sw1.sw2.<1,2>/24,按链接定义的顺序,sw1是第一个交换机的ID,sw2是第二个交换机的id。最后一个字节,第一个交换机接口为1,第二个交换机接口为2。

l3

每台主机置于不同子网。

  • 主机ip:10.x.y.2/24,x是网关交换机的id,y是主机id
  • 直连主机的交换机端口:10.x.y.1/24,x是网关交换机id,y是主机id
  • 两个交换机互联的端口:20.sw1.sw2.<1,2>/24,按链接定义的顺序,sw1是第一个交换机的ID,sw2是第二个交换机的id。最后一个字节,第一个交换机接口为1,第二个交换机接口为2

网络客户端

p4utils.mininetlib.cli.P4CLI实现

查看连通性

1
mininet >  pingall

查看命令列表

1
mininet> ?

控制平面配置

这里介绍Thrift客户端方式,py编程方式见高级用法

连接Thrift客户端

1
simple_switch_CLI --thrift-port 9090 --thrift-ip 127.0.0.1

查看命令列表,查看命令说明

1
2
RuntimeCmd: ?
RuntimeCmd: help <command>

填充交换机的动作匹配表

1
RuntimeCmd: table_add dmac forward 00:00:0a:00:00:01 => 1

使用命令文件

1
net.setP4CliInput('s1', 's1-commands.txt')

高级用法

控制平面配置

ThriftAPI

由 SimpleSwitchThriftAPI 实现,该控制器与交换机内置的 Thrift 客户端通信(与Thrift命令行客户端的方式是一样的)

控制器脚本可以和创建网络脚本分开,独立作为一个python脚本文件,首先导入配置 P4 交换机所需的模块

1
from p4utils.utils.sswitch_thrift_API import SimpleSwitchThriftAPI

新建控制器

1
controller = SimpleSwitchThriftAPI(9090,"127.0.0.1") # 可以省略默认9090,127.0.0.1

使用方法table_add()填充动作匹配表

1
controller.table_add('dmac', 'forward', ['00:00:0a:00:00:01'], ['1'])

运行python文件即可实现控制器功能

1
python3 controller.py

P4Runtime API

由 SimpleSwitchP4RuntimeAPI 实现,只能与支持 P4Runtime 的交换机一起使用(比如P4RuntimeSwitch)

使用 Python 启用 P4Runtime

1
2
3
net.setCompiler(p4rt=True)
# 用addP4RuntimeSwtich替换addP4Switch方法
net.addP4RuntimeSwtich('s1')

创建控制器

1
2
3
4
from p4utils.utils.sswitch_p4runtime_API import SimpleSwitchP4RuntimeAPI

controller = SimpleSwitchP4RuntimeAPI(device_id=1, grpc_port=9559,p4rt_path='l2_forwarding_p4rt.txt',
json_path='l2_forwarding.json')

p4rt_path参数和json_path参数对应的文件由P4编译器生成,省略device_id参数按声明顺序从1开始依次加1,省略grpc_port参数按声明顺序从9559开始依次加1

填充动作匹配表

1
controller.table_add('dmac', 'forward', ['00:00:0a:00:00:01'], ['1'])

最后同样是运行脚本

1
python3 controller.py

拓扑数据库

P4-Utils内置了一个网络启动后自动生成的拓扑数据库,保存在执行目录下的一个JSON文件中,通常叫做topology.json. 可以查询此文件以检索拓扑信息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from p4utils.utils.helper import load_topo
from p4utils.utils.sswitch_p4runtime_API import SimpleSwitchP4RuntimeAPI

topo = load_topo('topology.json')

controller = SimpleSwitchP4RuntimeAPI(topo['s1']['device_id'],
                                       topo['s1']['grpc_port'],
                                       p4rt_path=topo['s1']['p4rt_path'],
                                       json_path=topo['s1']['json_path'])

for neigh topo.get_neighbors('s1'):
    if topo.isHost(neigh):
        controller.table_add('dmac',
                             'forward',
                             [topo.get_host_mac(neigh)],
                             [str(topo.node_to_node_port_num('s1', neigh))])

任务调度程序

Task Scheduler 允许用户在不同的节点上简单地安排不同的任务(例如流量的生成)

使用网络客户端安排任务

1
mininet >  task  < node >  < start >  < duration >  < exe >  [ < arg1 > ]  ...  [ < argN > ]  [ -- mod  < module > ]  [ --< key1 >  < kwarg1 > ]  ...  [ --< keyM >  < kwargM > ]
  • node是节点名称
  • exe是要运行的可执行文件(shell 字符串命令或 Python 函数的名称)
  • argX是传递函数的位置参数(可选)
  • start是相对于当前时间的任务延迟秒数
  • duration是以秒为单位的任务持续时间(如果持续时间小于或等于 0,则任务没有时间限制)
  • keyX和kwargX是传递函数的关键字参数(可选)
  • 查找函数的默认模块是p4utils.utils.traffic_utils. 可以在–mod 命令中指定不同的模块。

例子

1
mininet> task h1 0 10 "ping 10.0.0.2"

使用文件调度任务

与网络客户端任务语法一致,但省略task关键字,并且每行一个任务

如tasks.txt

1
2
h1 30 10 "ping 10.0.0.2"
h3 10 30 "ping 10.0.0.4"

然后添加task到网络中

1
net.addTaskFile('tasks.txt')

MX

mx命令用于进入linux网络命名空间,p4-utils里面的每一个节点其实就是一个网络命名空间

 |