這一篇將會介紹 Table (Match + Action)

上一篇說明了 Header 以及 Parser 定義方式,在 p4 當中,也是存在與 OpenFlow Switch 類似的 Match 以及 Action 機制。

不像 OpenFlow 因為特定 Match field 以及特定 Action 而無法有彈性的處理 Packet;P4 因為可以自行定義 Header 以及 Parser,所以在 Header 操作上跟 OpenFlow 有一些不同,而 Table 處理的流程更加的容易理解。

本篇分為兩個部分:

  • Action
  • Table(含Match)

Action:


在定義一個 Table 之前,我們需要定義出一些會用到的 Action 出來,在 Table 裡面我們可以利用這些 Action 去對封包做出處理。

Action 定義的方式跟 C 語言裡面的函式很像:

action action_name(param1, param2, ......) {

    /* some statements.....*/

}

在 Action 中有以下 API 可以使用:

名稱功能

add_header(header_instance)

加入一個 Header,如果該 Header 已經存在,則不變。

Header 預設都是零

copy_header(dst, src)

複製 Header

remove_header(header_instance)

將 Header 標記為 invalid,也就是說他不會被封裝以及 match 到。

modify_field(dst, value[, mask])

更改某一個特定值,其中 dst 可以是 header_inst.field 或是 register

add_to_field(dst, value)

將 value 加到 dst 中,

其中 dst 可以是 header_inst.field 或是 register

add(dst, val1, val2)

講兩個數字相加後儲存

modify_field_with_hash_based_offset

(dest, field_list_calc, base, size)

這個比較複雜,主要是透過給定一個已經定好的 field list calc,去計算出 hash,並儲存到 dest 中。

然後 hash 產生完之後,會在透過下面公式轉換:

new_hash = base + hash_val % size

truncate(len)

將 Packet 在 egress 階段截短,其中 len 的單位為 byte。

若 packet 長度比 len 短,則不會有影響。

drop()

在 egress 階段把 packet 扔掉。

no_op()

僅用於佔位,不會做任何事情。

push(array[, count])

將新的 Header push 到 Packet 中,舊的 Header 會保留在底層。

array 是先定義好的 header array,count 預設為 1

pop(array[, count])

將 Packet header pop 到 array 中。

count(counter_ref, index)

更新計數器,將計數器的數值加一,index 參數表示 counter array 的 index。

註:這個 Action 只能用在 static counter 上。

meter(meter_ref, index, field)

更新 Meter。

generate_digest(receiver, field_list)

將特定欄位的資料傳送給 reciver,reciver 是一個數字,代表接收端,而接收端的定義不在 P4 的標準當中。

resubmit([field_list])

將 Packet 重新送到 Parser 階段中。

註:僅在 ingress 中可用。

recirculate([field_list])

將 Packet 重新送到 Parser 階段中。

註:僅在 egress 中可用。

clone_ingress_pkt_to_ingress(clone_spec, [ field_list ] )

將目前的封包複製一份傳送回Parser,可縮寫成:clone_i2i。

註:僅在 ingress 中可用。

clone_egress_pkt_to_ingress

將目前的封包複製一份傳送回Parser,可縮寫成:clone_e2i。

註:僅在 egress 中可用。

clone_ingress_pkt_to_egress

將目前的封包複製一份傳送到 Buffer 中,可縮寫成:clone_i2e。

註:僅在 ingress 中可用。

clone_egress_pkt_to_egress

將目前的封包複製一份傳送到 Buffer 中,可縮寫成:clone_e2e。

註:僅在 egress 中可用。

以上皆從 Spec 翻譯而來,若需要更詳細的 API 說明,請參考 P4 Spec。

 

Table:


這邊的 Table 跟 OpenFlow 的會不太一樣,在 OpenFlow Table 中的 Flow Entry Match 不一定相同,但是在 P4 中會是一樣的,可能有人會覺得這樣會不會損失他的彈性,但其實不然,因為他可以定義出相當多的 Table,也有可以定義出不同 Match 類型下應該執行哪些 Action。

Table 的定義方式如下:

table table_name {
    reads {
        field1 : match_type1 ;
        field2 : match_type1 ;
        // ....
    }
    actions {
        action1;
        action2;
        // .....
    }
    min_size : value;
    max_size : value;
    size : value;
    support_timeout : true or false;
}

上述除了 actions 以外其他都是可以省略的,以下說明各區塊的意義以及用途:

  1. reads:相當於 match field 的格式,也就是說這一個 Table 裡面所有的 Flow entry 的 match 格式都是長這樣。
  2. match type : 在 P4 當中有幾種 match type:
    1. exact:與 match 值完全相同的 match
    2. ternary:給定一個 mask,直接透過這一個 mask 去 match。
    3. lpm:Longest Prefix Match 的意思,IP match 用,例如 140.113.0.0/16 等等。
    4. range:範圍 match
    5. valid:該 header field 有被 parse 出來就會是 valid
  3. actions:Flow entry 可用的 action(並非全部 Action 都要執行)
  4. min_size:Table 中最少需要多少 entry,若少於這個數量的話,則 table 會在被叫的階段發生錯誤。
  5. max_size:Entry 最大數量,若已經到這個數量還要塞 Entry 給他,則會被拒絕(reject)
  6. size:等同於同時指定 min_size 以及 max_size,也就是該 Table 一定要在一個數量的 entry 才能運作。
  7. 若任何 size 都沒指定的話,則由 compiler 決定預設值。
  8. support_timeout:決定 table 是否支援 entry 會自動 timeout 掉。

如果換一個角度看的話,table 的定義有一點點像是 class,而每一個 entry 只不過是仿造一個 table 的格式所產生的一個 object 而已。

依據 P4 的 RPC Cli 使用方式,我們可以用類似下面的命令來新增一筆 Entry:

add_entry table_name match_fields action_name action_parameters

下一篇會提到有關 Control 的部份,Control 會整合我們的 Table 以及 Parser,並且建構出一個完整的 Switch 定義。

Share Your Thought