---
# System prepended metadata

title: P4 NetFPGA-SUME 教學
tags: [NetFPGA-SUME]

---

# P4 NetFPGA-SUME 教學
###### tags: `NetFPGA-SUME`

## 系統重安裝

網址: https://github.com/NetFPGA/NetFPGA-SUME-public/wiki/Reference-Operating-System-Setup-Guide

前面都是去設定 Ubuntu 的基本設定，ex: 用戶、網路功能...。

P4 的系統安裝可以從這一步開始:

![](https://i.imgur.com/R8F8nXS.png)


## NetFPGA 驅動

網址: https://github.com/NetFPGA/P4-NetFPGA-public/wiki/Getting-Started

如果你沒有重新安裝的話，**每次重開機就做這一步就好了。**
```
modprobe sume_riffa
lsmod | grep sume_riffa
```

如果有重新安裝的話就照著上面的網址去執行安裝設定以及執行驅動。

## NetFPGA-SUME 流程

網址: https://www.sdnlab.com/20327.html


從這一步開始:

![](https://i.imgur.com/y9eJbcD.png)

* **其中比較需要注意的是，第二步的`settings.sh`的路徑是在`P4-NetFPGA/tools`。**

![](https://i.imgur.com/CAYmrEP.png)

**當你要在 NetFPGA-SUME 執行一個程式，例如`p4-dns`，你就要在`settings.sh`程式裡面將`P4_PROJECT_NAME`修改成`p4-dns`。**

![](https://i.imgur.com/lVcwVCr.jpg)


### P4 Code (NetFPGA-SUME Version)

當你做到第三步時，不知道要如何撰寫一個 P4 程式時，可以參照這個`p4-dns.p4`程式碼:

```
//
// Copyright (c) 2017 Stephen Ibanez
// All rights reserved.
//
// This software was developed by Stanford University and the University of Cambridge Computer Laboratory 
// under National Science Foundation under Grant No. CNS-0855268,
// the University of Cambridge Computer Laboratory under EPSRC INTERNET Project EP/H040536/1 and
// by the University of Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-11-C-0249 ("MRC2"), 
// as part of the DARPA MRC research programme.
//
// @NETFPGA_LICENSE_HEADER_START@
//
// Licensed to NetFPGA C.I.C. (NetFPGA) under one or more contributor
// license agreements.  See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.  NetFPGA licenses this
// file to you under the NetFPGA Hardware-Software License, Version 1.0 (the
// "License"); you may not use this file except in compliance with the
// License.  You may obtain a copy of the License at:
//
//   http://www.netfpga-cic.org
//
// Unless required by applicable law or agreed to in writing, Work distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations under the License.
//
// @NETFPGA_LICENSE_HEADER_END@
//


#include <core.p4>  // P4 引用的檔案
#include <sume_switch.p4>  // NetFPGA-SUME 所使用的檔案
 
typedef bit<48> EthAddr_t;  // 定義 ethernet，大小為 48 bit
typedef bit<32> IPv4Addr_t;  // 定義 IPv4Addr，大小為 32 bit
 
#define IPV4_TYPE 0x0800  // 宣告 IPV4 TYPE 為 0x800
#define TCP_TYPE 6  // 宣告 TCP TYPE 為 6

 
// standard Ethernet header
header Ethernet_h { 
    EthAddr_t dstAddr; 
    EthAddr_t srcAddr; 
    bit<16> etherType;
}
 
// IPv4 header without options
header IPv4_h {
    bit<4> version;
    bit<4> ihl;
    bit<8> tos; 
    bit<16> totalLen; 
    bit<16> identification; 
    bit<3> flags;
    bit<13> fragOffset; 
    bit<8> ttl;
    bit<8> protocol; 
    bit<16> hdrChecksum; 
    IPv4Addr_t srcAddr; 
    IPv4Addr_t dstAddr;
}
 
// TCP header without options
header TCP_h {
    bit srcPort;
    bit dstPort;
    bit seqNo;
    bit ackNo;
    bit dataOffset;
    bit res;
    bit flags;
    bit window;
    bit checksum;
    bit urgentPtr;
}
 
// List of all recognized headers
struct Parsed_packet { 
    Ethernet_h ethernet; 
    IPv4_h ip;
    TCP_h tcp;
}
 
// user defined metadata: can be used to share information between
// TopParser, TopPipe, and TopDeparser 
struct user_metadata_t {
    bit  unused;
}
 
// digest data to send to cpu if desired. MUST be 80 bits!
struct digest_data_t {
    bit  unused;
}
 
// Parser Implementation
@Xilinx_MaxPacketRegion(8192)
parser TopParser(packet_in b, 
                 out Parsed_packet p, 
                 out user_metadata_t user_metadata,
                 out digest_data_t digest_data,
                 inout sume_metadata_t sume_metadata) {
    state start {
        b.extract(p.ethernet);
        user_metadata.unused = 0;
        digest_data.unused = 0;
        transition select(p.ethernet.etherType) {
            IPV4_TYPE: parse_ipv4;
            default: reject;
        } 
    }
 
    state parse_ipv4 {
        b.extract(p.ip);
        transition select(p.ip.protocol) {
            TCP_TYPE: parse_tcp;
            default: reject;
        }
    }
 
    state parse_tcp {
        b.extract(p.tcp);
        transition accept;
    }
}
 
// match-action pipeline
control TopPipe(inout Parsed_packet p,
                inout user_metadata_t user_metadata, 
                inout digest_data_t digest_data, 
                inout sume_metadata_t sume_metadata) {
 
    action ip_forward(port_t port) {
        sume_metadata.dst_port = port;
    }
 
    action nop() {}
 
    table forward {
        key = { p.ip.dstAddr: exact; }
 
        actions = {
            ip_forward;
            nop;
        }
        size = 64;
        default_action = nop;
    }
 
    apply {
        forward.apply();
    }
}
 
// Deparser Implementation
@Xilinx_MaxPacketRegion(8192)
control TopDeparser(packet_out b,
                    in Parsed_packet p,
                    in user_metadata_t user_metadata,
                    inout digest_data_t digest_data, 
                    inout sume_metadata_t sume_metadata) { 
    apply {
        b.emit(p.ethernet); 
        b.emit(p.ip);
        b.emit(p.tcp);
    }
}
 
 
// Instantiate the switch
SimpleSumeSwitch(TopParser(), TopPipe(), TopDeparser()) main;
```

### 程式碼解釋

1. 定義 ethernet，大小為 48 bit: `typedef bit<48> EthAddr_t;`

![](https://i.imgur.com/0uqrsMD.png)

2. 定義 IPv4Addr，大小為 32 bit: `typedef bit<32> IPv4Addr_t;`
 
![](https://i.imgur.com/SQyFAVm.png)

3. 宣告 IPV4 TYPE 為 0x800: `#define IPV4_TYPE 0x0800`

![](https://i.imgur.com/unPumKE.png)


4. 宣告 TCP TYPE 為 6: `#define TCP_TYPE 6`

![](https://i.imgur.com/q1pOMKF.png)

5. 定義 Ethernet、IPv4、TCP 的 header 封包內容和大小:

```
// standard Ethernet header
header Ethernet_h { 
    EthAddr_t dstAddr; 
    EthAddr_t srcAddr; 
    bit<16> etherType;
}
 
// IPv4 header without options
header IPv4_h {
    bit<4> version;
    bit<4> ihl;
    bit<8> tos; 
    bit<16> totalLen; 
    bit<16> identification; 
    bit<3> flags;
    bit<13> fragOffset; 
    bit<8> ttl;
    bit<8> protocol; 
    bit<16> hdrChecksum; 
    IPv4Addr_t srcAddr; 
    IPv4Addr_t dstAddr;
}
 
// TCP header without options
header TCP_h {
    bit srcPort;
    bit dstPort;
    bit seqNo;
    bit ackNo;
    bit dataOffset;
    bit res;
    bit flags;
    bit window;
    bit checksum;
    bit urgentPtr;
}
```

6. 定義結構 Parsed_packet、user_metadata、digest_data 的內容: 

> 這三個都可以在函式庫`sume_switch.p4`找到
```
// List of all recognized headers
struct Parsed_packet { 
    Ethernet_h ethernet; 
    IPv4_h ip;
    TCP_h tcp;
}
 
// user defined metadata: can be used to share information between
// TopParser, TopPipe, and TopDeparser 
struct user_metadata_t {
    bit  unused;
}
 
// digest data to send to cpu if desired. MUST be 80 bits!
struct digest_data_t {
    bit  unused;
}
```

7. Parser 階段去解析封包，這個範例是從 ethernet、ip 解析到 tcp。

```
// Parser Implementation
@Xilinx_MaxPacketRegion(8192)
parser TopParser(packet_in b, 
                 out Parsed_packet p, 
                 out user_metadata_t user_metadata,
                 out digest_data_t digest_data,
                 inout sume_metadata_t sume_metadata) {
    state start {
        b.extract(p.ethernet);
        user_metadata.unused = 0;
        digest_data.unused = 0;
        transition select(p.ethernet.etherType) {
            IPV4_TYPE: parse_ipv4;
            default: reject;
        } 
    }
 
    state parse_ipv4 {
        b.extract(p.ip);
        transition select(p.ip.protocol) {
            TCP_TYPE: parse_tcp;
            default: reject;
        }
    }
 
    state parse_tcp {
        b.extract(p.tcp);
        transition accept;
    }
}
```

8. Match + Action 階段: 

進來的封包會與 match key 比對，比對完會進到 Action 階段，其中 Action 有兩個動作，一個是做 forward 的動作 (ip_forward)，另一個是封包都不做事，也就是封包會被丟棄 (nop)。

* ***ip transmission: (match key): dst ip -> (action): dst port***

```
// match-action pipeline
control TopPipe(inout Parsed_packet p,
                inout user_metadata_t user_metadata, 
                inout digest_data_t digest_data, 
                inout sume_metadata_t sume_metadata) {
 
    action ip_forward(port_t port) {
        sume_metadata.dst_port = port;
    }
 
    action nop() {}
 
    table forward {
        key = { p.ip.dstAddr: exact; }
 
        actions = {
            ip_forward;
            nop;
        }
        size = 64;
        default_action = nop;
    }
 
    apply {
        forward.apply();
    }
}
```

9. Deparser 階段: 還原封包

其中還原的封包可以看前面的 parser 階段，基本上你在 parser 要解析哪類封包在 deparser 階段就要還原它。

```
// Deparser Implementation
@Xilinx_MaxPacketRegion(8192)
control TopDeparser(packet_out b,
                    in Parsed_packet p,
                    in user_metadata_t user_metadata,
                    inout digest_data_t digest_data, 
                    inout sume_metadata_t sume_metadata) { 
    apply {
        b.emit(p.ethernet); 
        b.emit(p.ip);
        b.emit(p.tcp);
    }
}
```
## Tip

寫完程式後，接著從第四步一直做到第十步，**其中做到第九步時會需要等待30分鐘～2小時。**

基本上第九步是要將 P4 程式轉成 binary code，也就是讓 NetFPGA 看得懂的語言。


而做完第十步後記得重新開機，重開完記得開啟驅動: `modprobe sume_riffa`。

## 測試

記得到 CLI 的資料夾下，並且執行 P4-Switch-CLI 去增加規則。

![](https://i.imgur.com/f3LJZOA.png)


而`p4-dns`的規則:

```
table_cam_add_entry forward ip_forward 192.168.11.1 => 0b00000001
table_cam_add_entry forward ip_forward 192.168.11.2 => 0b00000100
```

接著就可以去測試主機能不能 ping 通。

![](https://i.imgur.com/kkL7vNY.jpg)


## Reference

1. 計算 TCP/UDP Checksum 值: http://bruce690813.blogspot.com/2017/09/tcpip-checksum.html
2. NetFPGA overview: https://github.com/NetFPGA/P4-NetFPGA-public/wiki/Workflow-Overview
3. P4 白皮書: http://www.bomeimedia.com/China-unicom/white_paper/20191028.pdf
4. P4 PSA 白皮書: https://p4lang.github.io/p4-spec/docs/PSA.pdf
5. P4 NetFPGA toturial: https://www.cl.cam.ac.uk/research/srg/netos/projects/netfpga/workshop/summer-school-2017/slides/P4-NetFPGA_camp_2017_v1.pdf
6. NetFPGA 教學: https://www.sdnlab.com/20327.html
7. Source Routing: https://ithelp.ithome.com.tw/articles/10249882