文章學習自:P4語言編程詳解
由於原文有一點的年份,所以也繼續閱讀了相關的最新規范。
P4語言規范
基礎數據類型
布爾型(bool)
| 運算符 | 描述 |
|---|---|
| and | 雙目運算符,結果為布爾型 |
| or | 雙目運算符,結果為布爾型 |
| not | 單目運算符,結果為布爾型 |
| ==,!= | 相等或不等,結果為布爾型 |
無符號整型(bit)
又稱之為位串(bit-string),對位串進行數學運算的時候,位串長度必須是8的整數倍。
| 運算符 | 描述 |
|---|---|
| ==,!= | 是否相等或不等,運算結果為布爾型。 |
| <,>,<=,>= | 無符號數比較,操作數的長度(W)要求相同,運算結果為布爾型。 |
| &,|,^ | 按位運算符(分別是與,或和異或),操作數的長度(W)要求相同,運算結果為無符號整型。 |
| ~ | 運算結果為操作數的補碼。 |
| <<,>> | 左移運算符操作數為無符號整型,右移運算符操作數必須是無符號數或非負整數。此運算符為邏輯位移。 |
| +(單目) | 單目加運算,效果同no-op。(無意義) |
| -(單目) | 單目減運算,計算結果為2W減去操作數,W為操作數長度。 |
| +(雙目) | 二目加運算,操作數的長度(W)要求相同。計算結果為操作數的算術和,且運算結果長度也必須為W,超過則截斷。 |
| -(雙目) | 二目減運算,操作數的長度(W)要求相同。計算結果為操作數的算術差。 |
| * | 無符號乘法運算,操作數的長度(W)要求相同,計算結果為無符號數且長度與操作數相等。 |
有符號整數型(int(W))
大致與無符號整數相同
| 運算符 | 描述 |
|---|---|
| -(單目) | 單目減運算,運算結果偉有符號整型,長度和操作數相等 |
| +(雙目) | 二目加運算,操作數數據類型必須相同,運算結果也為同類型。 |
| -(雙目) | 二目減運算,操作數數據類型必須相同,運算結果也為同類型。 |
| * | 有符號乘法運算,操作數的長度(W)要求相同,計算結果為有符號數且長度與操作數相等。 |
變長位串(varbit)
該數據類型,不支持算術、比較和位運算,也不支持類型轉換,該數據類型在定義時會指定一個靜態的最大寬度值,解析器會提取變長位串數據並設置一個值作為長度。
無限精度整型(int)
| 運算符 | 描述 |
|---|---|
| ==,!= | 是否相等或不等,運算結果為布爾型。 |
| <,>,<=,>= | 有符號數比較,運算結果為布爾型。 |
| <<,>> | 右移運算符操作數必須為正整數;左移運算結果和操作數相同。a<<b等價於ax2b,a>>b等價於a/2b。 |
| +(單目) | 單目加運算,效果同no-op。(無意義) |
| -(單目) | 單目減運算,運算結果為整型,且該運算不會導致溢出。 |
| +(雙目) | 二目加運算,操作數類型都必須是整型,運算結果為整型,且該運算不會導致溢出。 |
| -(雙目) | 二目減運算,操作數類型都必須是整型,計算結果為整型,且該運算不會導致溢出。 |
| * | 無符號乘法運算,操作數必須都是整形,計算結果為整形,該運算不會導致溢出。 |
| /,% | 二目有符號除法和取模運算,操作數必須是正整數,運算結果為正整數。 |
數據類型轉換
可以看出,上述的很多運算都是建立在相同數據類型的前提下,但是對於某些數據類型是不能相互轉換的,p4也提供了合法的數據類型轉換。
| from | to | 描述 |
|---|---|---|
| bit<1> | bool | 0代表false,1代表true(中括號里的1代表的是字節數,不是0和1,下同) |
| bool | bit<1> | 同上 |
| bit<w> | int<w> | 保留所有比特位不變 |
| int<w> | bit<w> | 同上 |
| bit<w> | bit<w> | 當w>w1時,保留低位w1位長度的數據,小於時新增位補0 |
| int<w> | int<w> | 當W>W1時,保留低位W1長度的數字,當W<W1時新增位補符號位.(補符號位?) |
| int | bit<w> | 保留低位W位長度數據,溢出需要發出警告並轉化為負數。 |
| int | intt<w> | 保留低位W位長度數據,溢出需要發出警告。 |
這些屬於顯式類型轉換,p4中同樣存在隱式類型轉換,在運算中會進行強制轉換,這個和大部分語言類似。也容易發生溢出錯誤。
基礎語言組件
之前有學到過,p4程序主要由五個組建構成:header,parser,table,action,和controller
Header
這個類似於固定屬性一樣的東西,有多個成員字段,Header分為兩種,一種是包頭(Packet Header),一種是元數據(Metadata)。
包頭
ipv4例子

header ipv4_t{
fields{
version:4;
ihl:4;
diffserv:8;
totallen:16;
identification:16;
flags:3;
fragoffset:13;
ttl:8;
protocol:8;
hdrchecksum:16;
srcaddr:32;
dstaddr:32;
option:*;
}
}
里面的數字都是以bit為單位的,所以其實應該也能寫成這樣。
header ipv4_t{
field{
bit<4> version;
bit<4> ihl;
bit<8> diffserv;
bit<16> totallen;
bit<16> identification;
bit<3> flags;
bit<13> fragoffset;
bit<8> ttl;
bit<8> protocol;
bit<16> hdrchecksum;
bit<32> srcaddr;
bit<32> dstaddr;
}
}
類比寫下ipv6的

header ipv6_t{
field{
bit<4> version;
bit<8> trafficclass;//這個在ipv6中類似於ipv4的diffserv。
bit<20> flowlabel;
bit<20> payloadlen;
bit<8> nxthdr;
bit<8> hoplimit;
bit<128> srcaddr;
bit<128> dstaddr;
}
}
元數據
元數據是用來攜帶數據和配置性和西,元數據的申明與包頭類似,但在實例化的時候有所不同,而且包頭和元數據在字段值的約束上存在一定的差別。元數據分為兩種,一種是用來攜帶P4程序運行過程中產生的數據的用戶自定義元數據(User-Defined Metadata),如首部字段的運算結果等。另一種是固有元數據(Intrinsic Metadata),用於攜帶交換機自身的配置信息,如數據包進入交換機時的端口號等。
struct ingress_metadata_t{
}
metadate ingress_metadata_t ingress_metadata;
有8種固有元數據,這些元數據攜帶了數據包相關的狀態信息。
| 字段 | 描述 |
|---|---|
| ingress_port | 數據包的入端口,解析之前設置,只讀 |
| packet_length | 數據包的字節數,當交換機在快速轉發模式下,該元數據不能在動作(action)中匹配或引用。只讀。 |
| egress_spec | 在入端口流水線的匹配-動作過程之后設置,指定數據包出端口,可以是物理端口、邏輯端口或者多播組。 |
| egress_instance | 用於區分復制后數據包實例的標識符。只讀。 |
| instance_type | 數據包實例類型:正常(Normal)、入端口復制(ingress clone)、出端口復制(egress clone)、再循環(recirculated)。 |
| parser_sratus | 解析器解析結果,0表示無錯誤,其實數字代表了對應的錯誤類型。 |
| parser_error_location | 指向P4程序錯誤發生處。 |
注意點:
- 包頭類型的長度需要字節對齊,即長度必須是8bit的整數倍。
- 包頭中字段長度可以是可變值,也可以是首部中其他字段值計算后的值。而元數據中的字段長度只能是定值。
- 只有包頭能夠實例化成數組,元數據則不行。
- 實例化時,首部中已定義名稱的字段的值會被初始化成程序中的指定值,如果首部中只定義字段名稱而未指定值,字段的值將會被初始化成0。
