最近因為研究的關係開始研究 P4 這一個語言,所以順手將自己看到的東西做一個筆記,後續開發可以在拿出來參考。

簡介


P4語言是由數間大學(Stanford, Princeton)以及企業(Barefoor Netowrk, Intel, Google, Microsoft)於 2014 SIGCOMM 中提出,主要希望解決的問題是一般的 Switch,包含 SDN Switch 都只能夠處理一般的封包,例如 Ethernet、VLAN、IPv4 等等。

透過 P4,開發者能夠直接定義出一個 Switch 能夠處理的封包格式,舉例來說定義一個新的 Ethernet type,或是直接不用 Ethernet header,用自行定義的封包結構,另外就是他可以不被侷限現在特定的硬體之下執行,只需要有對應的編譯器就可以佈署,這部份之後會在說明。

下圖是 P4 的 Abstract Forwarding Model(取自Spec

螢幕快照 2015-11-27 下午10.07.13

一個 Data plane 中有幾個比較重要的地方:

  • Parser(本篇介紹)
  • Tables (Match + Action)
  • Control program

這一系列的東西都是需要透過 P4 語言去定義的

透過 P4 定義完成之後,用編譯器編譯成硬體的目標語言(例如 ARM binary、FPGA 語言等等),最後把他們佈署到硬體(或軟體Switch)上,成為上圖的 Switch Configuration。

以下一一簡介各部分區塊的定義方式:

Parser:

Parser 包含了兩個部分:Header 以及 Parser。
Header 代表了有哪些格式的 Header 可以使用,例如 Ethernet header。

在 p4 裡面,Header 的定義方式如下(以 Ethernet 為例):

header_type ethernet_t {
    fields {
        dst_addr : 48;
        src_addr : 48;
        ether_type : 16;
    }
}

上述是 Header 的定義方式,我們可以建立一個 header.p4 的檔案,然後在透過 include 的方式將他引用到主要的 p4 中。

順帶一提,裡面的數字全部都是以 bit 為單位的,Header 跟 C 語言中的 Struct 有點相似,他是需要另外在定義成為真正 Header 的實體(header instance),如下:

header ethernet_t ethernet;

定義好 Header 之後,再來就是定義 Parser 了,也是一樣的,可以將 parser 定義在一個獨立的 parser.p4 中,以方便程式重構。
在 P4 當中,永遠都會有一個起始的 Parser,我們稱作 start:

parser start {
    return next_parser;
}

在每一個 parser 中都會依據目前所分析的內容來決定下一個 parser,直到回傳的內容為 “ingress”(或其他 control function) 為止,下面用 ipv4 為例子:

 

parser start {
    return parse_ethernet;
}

parser parse_ethernet {
    extract(ethernet);
    return select(latest.ether_type) {
        0x0800 : parse_ipv4;
        default : ingress;
    }
}

parser parse_ipv4 {
    extract(ipv4);
    return ingress;
}

這邊有幾個需要補充的東西:

  1. extract : 將目前的 Packet 以特定的 header 取出來,取出來的資料長度以 header 定義的為主
  2. return : 透過 return 的方式決定要前往哪個 parser、control function(後面會補充),可以直接 return 或是使用 select。
  3. select(select_exp) : 蠻像 C 語言中的 switch case,依據特定的 field 數值去決定要哪一個 parser 或是 control function。
  4. select_exp : 依據 spec,他可以是:
    1. field_ref : 如 ethernet.ether_type
    2. latest.field_name : 以最後 extract 的 header 為主,取用他的 field
    3. current (offset, length) : 以目前的 packet offset 位置開始某固定長度所取得的數值。

另外一個就是 parser 可以 return 一個 exception 出來,格式如下:

parser_error parser_exception_name;

parser_exception_name 有很多種,像是 p4_pe_out_of_packet 等等,這篇就不佳闡述了。

而如果我們要處理一個 exception 的話,則需要先定義好 exception handler。

parser_exception p4_pe_out_of_packet {
    /* statements */
    /* return or parser_drop */
}

處理一個 exception 的方式有很多,最終的結果只會有兩個,一個是跳至指定的 control function,或是直接將該 packet drop 掉。

在 egress 階段開始時會先對 packet 做 deparsing 的動作,亦即將 ingress 所做出的修改(add header、modiffy header等)重新封裝回 packet 當中。

 

下篇將說明 Table (Match + Action) 的定義方式。

Share Your Thought