p4学习

因为最近用到p4,总结一下p4的学习

P4概念

P4由以下部分构成

  • Headers
  • Parsers
  • Tables
  • Actions
  • Control Programs

Headers

用来形容the sequence and structure of a series of fields,包括了字段的宽度和字段值的限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
header ethernet {
fields {
dst_addr : 48; // width in bits
src_addr : 48;
ethertype : 16;
}
}
header vlan {
fields {
pcp : 3;
cfi : 1;
vid : 12;
ethertype : 16;
}
}

Parsers

P4用一个transition的集合来表示state machine,用来提取字段的value,然后发送到match+action表。论文给了一个示例

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
parser start {
ethernet;
}

parser ethernet {
switch(ethertype) {
case 0x8100: vlan;
case 0x9100: vlan;
case 0x800: ipv4;
// Other cases
}
}

parser vlan {
switch(ethertype) {
case 0xaaaa: mTag;
case 0x800: ipv4;
// Other cases
}
}
parser mTag {
switch(ethertype) {
case 0x800: ipv4;
// Other cases
}
}

解析从start state开始。知道一个显式声明的stop state结束(或者遇到一个unhandled case报错)。

遇到一个新的header的state,状态机会提取出header然后继续下一次处理,被提取出的头会被发送到match+action进行处理。

Table Specification

1
2
3
4
5
6
7
8
9
10
table mTag_table {
reads {
ethernet.dst_addr : exact;
vlan.vid : exact;
}
actions {
add_mTag;
}
max_size : 20000;
}
  • reads属性声明match什么字段,指定match的类型(exact, ternary)等。

  • actions属性列举所有对packet的可能行为

  • max_size属性声明table应该支持多少entries

Action Specifications

P4定义了一堆动作原语。每一个P4程序都通过动作原语定义新的动作。

如上文提到的add_mTag

1
2
3
4
5
6
7
8
9
10
11
12
13
action add_mTag(up1, up2, down1, down2, egr_spec) {
add_header(mTag);
// Copy VLAN ethertype to mTag
copy_field(mTag.ethertype, vlan.ethertype);
// Set VLAN’s ethertype to signal mTag
set_field(vlan.ethertype, 0xaaaa);
set_field(mTag.up1, up1);
set_field(mTag.up2, up2);
set_field(mTag.down1, down1);
set_field(mTag.down2, down2);
// Set the destination egress port as well
set_field(metadata.egress_spec, egr_spec);
}

如果action需要某些参数,如up1,会由match table在运行的时候提供。

p4的动作原语包括

  • set_field
  • copy_field
  • add_header
  • remove_header
  • increment
  • checksum

The Control Program

主要通过函数、条件、对table的引用来完成。如以下代码完成了对上图的构建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

control main() {
// Verify mTag state and port are consistent
table(source_check);
// If no error from source_check, continue
if (!defined(metadata.ingress_error)) {
// Attempt to switch to end hosts
table(local_switching);
if (!defined(metadata.egress_spec)) {
// Not a known local host; try mtagging
table(mTag_table);
}
// Check for unknown egress state or
// bad retagging with mTag.
table(egress_check);
}
}