因为要想办法看懂一篇SIGCOMM的文章。试水一下P4 hello world,找一下感觉。

摸索了一段时间。。。记录一下。P4作为一个交换机编程语言,主要是为了写交换机的特性而生的(也就是说告诉交换机怎么处理包),理想如上图。 BTW,Nick等人做出了openflow还不过瘾,一定要做到极致。。。做出了barefoot,还把Cisco带上了Cisco S1的路。。。
P4的文章拿了2024 SIGCOMM时间考验奖。

先大概看一下这文章怎么介绍P4的。。。

它说P4可以用来指定包头,包解析器,多个匹配动作表,以及通过这些表的控制流,并且这篇文章引入了编译器来隔离不同的交换机实现,只保留了图2的交换机抽象。

P4相对Click来说,更加针对硬件优化,要使得硬件能够更好的加速,必须告诉硬件控制流中的依赖关系(TDGs, table dependency graphs)。所以P4编译器首先实现将P4程序转换成TDG,然后再转换TDG到相应的交换机实现上。
文章通过mTag的例子介绍了P4的基本组件。但是这个例子对我来说还是略微复杂了。。。语言现在已经进化到P4/16。


所以找感觉的话还是直接选择github的tutorial了,基本上就是follow这个https://github.com/p4lang/tutorials,里面有一站式打包的virtualbox虚拟机,可以在Apple芯片的笔记本上跑。

这个hello world是实现一个p4程序使得上图中各个主机能相互ping通,只设计一个table,对于我这种不熟悉各种协议的小白来说比较友好。

先总结用到的实验环境(代码质量很高,赏心悦目,不像看有些代码,常常想骂人):

Network: Mininet Mininet: An Instant Virtual Network on Your Laptop (or Other PC) - Mininet
http://conferences.sigcomm.org/hotnets/2010/papers/a19-lantz.pdf
P4 Program: basic.py
P4 Architecture model: V1Model
P4 Compiler: P4C is modular; it provides a standard frontend and midend which can be combined with a target-specific backend to create a complete P4 compiler. The goal is to make adding new backends easy. 我们这里用的后端是
- p4c-bm2-ss: can be used to target the P4
simple_switchwritten using the BMv2 behavioral model,
Target: P4 simple_switch

几个概念之间的关系可以参考P4 architecture - #2 by ederollora - P4 Programming Language

然后我们看一下我们的hello world程序。这个程序实现IPv4转发,对于每个包,交换机要完成以下步骤1. 更新源和目的MAC地址。2. TTL减1。3. 把包转发到合适的端口。
交换机只有一个表,这个表通过控制平面写好静态的规则。每条规则会把一个IP地址映射到MAC地址,以及下一跳的端口。
整个代码是209行,感觉不是很多。首先是main函数,在整个文件的最底端,大概是总结了pipeline上的各个模块。
/*************************************************************************
*********************** S W I T C H *******************************
*************************************************************************/
V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;
然后看一下MyParser(),这个parser的功能就是从包当中提取ethernet和ipv4头,看上去还是比较直观的。
parser MyParser(packet_in packet,
out headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
state start {
transition parse_ethernet;
}
state parse_ethernet {
packet.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType) {
TYPE_IPV4: parse_ipv4;
default: accept;
}
}
state parse_ipv4 {
packet.extract(hdr.ipv4);
transition accept;
}
}
MyIngress():
/*************************************************************************
************** I N G R E S S P R O C E S S I N G *******************
*************************************************************************/
control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
action drop() {
mark_to_drop(standard_metadata);
}
action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
standard_metadata.egress_spec = port;
hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
hdr.ethernet.dstAddr = dstAddr;
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
}
table ipv4_lpm {
key = {
hdr.ipv4.dstAddr: lpm;
}
actions = {
ipv4_forward;
drop;
NoAction;
}
size = 1024;
default_action = drop();
}
apply {
if (hdr.ipv4.isValid()) {
ipv4_lpm.apply();
}
}
}
Ingress,先定义两个2 actions,一个是drop一个是ipv4 forward。这个ipv4_forward的参数是dstAddr和port,这两个参数是通过结合控制平面设置的表项确定的。这个Ingress就定义了一个ipv4_lpm的表,这个表当包的ipv4头是有效的时候就应用match&action,根据lpm如果ipv4的目的地址匹配,就执行ipv4_forward,如果不匹配就drop。ipv4_forward就完成之前要求的3个步骤1.更新源和目的MAC地址。2. TTL减1。3. 把包转发到合适的端口。
控制平面设置的表项的一个例子如下:
"table_entries": [
{
"table": "MyIngress.ipv4_lpm",
"default_action": true,
"action_name": "MyIngress.drop",
"action_params": { }
},
{
"table": "MyIngress.ipv4_lpm",
"match": {
"hdr.ipv4.dstAddr": ["10.0.1.1", 32]
},
"action_name": "MyIngress.ipv4_forward",
"action_params": {
"dstAddr": "08:00:00:00:01:11",
"port": 1
}
},
{
"table": "MyIngress.ipv4_lpm",
"match": {
"hdr.ipv4.dstAddr": ["10.0.2.2", 32]
},
"action_name": "MyIngress.ipv4_forward",
"action_params": {
"dstAddr": "08:00:00:00:02:22",
"port": 2
}
MyEgress(),egress比较简单,这个都不需要处理,因为该处理的逻辑已经处理完了。
/*************************************************************************
**************** E G R E S S P R O C E S S I N G *******************
*************************************************************************/
control MyEgress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
apply { }
}
MyComputeChecksum()主要是计算ipv4校验和。
control MyComputeChecksum(inout headers hdr, inout metadata meta) {
apply {
update_checksum(
hdr.ipv4.isValid(),
{ hdr.ipv4.version,
hdr.ipv4.ihl,
hdr.ipv4.diffserv,
hdr.ipv4.totalLen,
hdr.ipv4.identification,
hdr.ipv4.flags,
hdr.ipv4.fragOffset,
hdr.ipv4.ttl,
hdr.ipv4.protocol,
hdr.ipv4.srcAddr,
hdr.ipv4.dstAddr },
hdr.ipv4.hdrChecksum,
HashAlgorithm.csum16);
}
}
MyDeparser()主要把我们经过pipeline的包头重新放到最后的包当中:
control MyDeparser(packet_out packet, in headers hdr) {
apply {
packet.emit(hdr.ethernet);
packet.emit(hdr.ipv4);
}
}
在修改完这个程序之后,在虚拟机的~/tutorials/exercises/basic目录下执行make run之后,代码就会生成编译生成P4程序。
p4c-bm2-ss --p4v 16 --p4runtime-files build/basic.p4.p4info.txtpb -o build/basic.json basic.p4
然后由mininet根据拓扑信息(pod-topo/topology.json)建立网络拓扑。simple_switch_grpc把P4程序basic.json加载到4个交换机里面。
最后通过P4runtime接口把交换机的表项(pod-topo/sX-runtime.json)加载到各个交换机。
然后实际的效果就是各个主机可以相互ping通。


3725

被折叠的 条评论
为什么被折叠?



