P4->NetFPGA 实战——自写P4程序并加载至NetFPGA-SUME

P4->NetFPGA 实战——自写P4程序并加载至NetFPGA-SUME

0.前言

自翻译了github上的内容:

之后,关于自己写的P4程序如何加载运行至sume呢?于是乎就有了以下文章。

此文章主要介绍如何将符合规范的自定义P4程序加载至NetFPGA-SUME上,由于知识水平有限,文章内部暂时缺少了关于测试测序的编写。

1.P4-NetFPGA-live 项目结构目录

以下图片摘自P4-NetFPGA_camp_2017_v1.pptx

  • P4-NetFPGA-live根目录结构

  • sume-sdnet-switch目录结构

  • P4_PROJECT_DIR目录结构


以上即为P4->NetFPGA的几个重要的文件夹,其中sume-sdnet-switch目录下templates文件夹为我们提供了丰富的模板文件,其中就有最重要的P4项目模板,所以可以利用该文件夹创建我们的P4项目,我们只需更改内部的P4程序,以及gen_testdata.py 即可开始验证我们的实验,为我们节省了不少时间,点赞!

2. P4->NetFPGA Workflow

上图为 P4-NetFPGA_camp_2017_v1.pptx 给出的简易版workflow图,详细的可围观 github给出的workflow详细版 。由于笔者并没有完成上述第二步描述中的gen_testdata.py文件,所以在实验过程中通过复制其他参考项目中gen_testdata.py文件“欺骗” makefile 文件,从而过掉此步。

3.实验步骤

1)将templates内的p4项目模板复制到projects下,并将新文件夹取名为p4_test

#进入到P4-NetFPGA-live文件夹下
$ cd {path}/P4-NetFPGA-live
#进入到projects文件夹下
$ cd contrib-projects/sume-sdnet-switch/projects
#将templates的p4项目模板复制到projects文件夹下
$ cp -r ../templates/sss_p4_proj p4_test

2)修改settings.sh文件,将 P4_PROJECT_NAME=switch_calc 修改为 P4_PROJECT_NAME=p4_test

#进入到P4-NetFPGA-live文件夹下
$ cd {path}/P4-NetFPGA-live
#进入到tools文件下
$ cd tools
#修改settings.sh文件
$ gedit settings.sh
#修改完毕后,更新环境变量
$ source settings.sh

3)编写P4代码;

#进入p4_test文件夹下
$ cd $P4_PROJECT_DIR
#进入src文件下
$ cd src
#编写P4代码
$ gedit sss_p4_proj.p4
#将P4代码的文件名重命名为${P4_PROJECT_NAME}.p4
$ mv sss_p4_proj.p4 ${P4_PROJECT_NAME}.p4 

以下代码为简单的端口转发应用的P4代码(即匹配报文的入端口,然后再选择其他某个端口转发出去)

#include <core.p4>
#include <sume_switch.p4>

typedef bit<48> EthAddr_t; 
typedef bit<32> IPv4Addr_t;

#define IPV4_TYPE 0x0800
#define 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<16> srcPort;
    bit<16> dstPort;
    bit<32> seqNo;
    bit<32> ackNo;
    bit<4> dataOffset;
    bit<4> res;
    bit<8> flags;
    bit<16> window;
    bit<16> checksum;
    bit<16> 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<8>  unused;
}

// digest data to send to cpu if desired. MUST be 80 bits!
struct digest_data_t {
    bit<80>  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 set_output_port(port_t port) {
        sume_metadata.dst_port = port;
    }

    action nop() {}

    table forward {
        key = { sume_metadata.src_port: exact; }

        actions = {
            set_output_port;
            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;

4)将其他参考项目的gen_testdata.py 复制至新建的p4_test项目下;

#进入p4_test文件夹下
$ cd $P4_PROJECT_DIR
#进入testdata文件夹下
$ cd testdata
#将其他参考项目的gen_testdata.py 复制至新建的p4_test项目下
$ cp -r ../../tcp_monitor/testdata/gen_testdata.py gen_testdata.py

大事告成,接下来利用 P4-NetFPGA-livemakefile 文件 开启傻瓜式操作


5)运行P4-SDNet编译器生成最终的HDL和初始仿真框架;

#进入p4_test文件夹并执行make
$ cd $P4_PROJECT_DIR && make

6)运行SDNet模拟;

$ cd $P4_PROJECT_DIR/nf_sume_sdnet_ip/SimpleSumeSwitch
$ ./vivado_sim.bash

7)生成可在NetFPGA SUME模拟中使用的脚本来配置表条目;

$ cd $P4_PROJECT_DIR
$ make config_writes

8)在包装模块中包装SDNet输出并作为SUME库核心进行安装;

$ cd $P4_PROJECT_DIR
$ make uninstall_sdnet && make install_sdnet

PS:因没有写gen_testdata.py,所以以上略去 github给出的workflow详细版 中 Workflow Steps 的 第8步(Set up the SUME simulation)及第9步(Run the SUME simulation)

9)编译比特流;

$ cd $NF_DESIGN_DIR && make

10)编程FPGA, 将比特流文件config_writes.sh脚本复制到$ NF_DESIGN_DIR / bitfiles目录中;

$ cd $NF_DESIGN_DIR/bitfiles
$ cp ../hw/project/simple_sume_switch.runs/impl_1/top.bit ./ && mv top.bit ${P4_PROJECT_NAME}.bit
$ cp $P4_PROJECT_DIR/testdata/config_writes.sh ./
$ sudo bash
# bash program_switch.sh

注意:确保配置写入全部成功。 如果这是自上次断电以来首次对FPGA进行编程,则可能需要重新启动。

11)真实硬件测试

见下章节

4.真实硬件测试

1)测试环境简介

实验测试环境如下图所示:

将PC1接入到SUME的1口(NF0),将PC2接入到SUME的2口(NF1),SUME的10G光口需接电口进行光电转换才能够接入网线。

2)测试环境构建

SUME:

  • 即实验步骤的配置,含有端口转发应用的交换机。
  • 下发转发规则

步骤如下:

i:进入CLI文件夹,并开启CLI下发规则

#进入p4_test项目文件下
$ cd $P4_PROJECT_DIR
#进入CLI文件夹下
$ cd sw/CLI
#开启P4_SWITCH_CLI
$ sudo bash
# ./P4_SWITCH_CLI.py

ii:下发规则,在调出的CLI界面输入以下规则(可通过help查看CLI的更多命令)

#NF0进,NF1出
table_cam_add_entry forward set_output_port 0b00000001 => 0b00000100
#NF1进,NF0出
table_cam_add_entry forward set_output_port 0b00000100 => 0b00000001

PC1:

  • 设置静态IP为:192.168.1.11/24

PC2:

  • 设置静态IP为:192.168.1.22/24

3)实验测试

PC1测试:

在PC1(IP:192.168.1.11/24)主机中执行

ping 192.168.1.22

可成功ping通

PC2测试:

在PC2(IP:192.168.1.22/24)主机中执行

ping 192.168.1.11

可成功ping通

至此,实验验证结束

参考链接

[1] https://github.com/NetFPGA/P4-NetFPGA-public/wiki

[2] https://github.com/NetFPGA/P4-NetFPGA-public/wiki/Workflow-Overview

[3] https://github.com/NetFPGA/P4-NetFPGA-public/wiki/Getting-Started

[4] https://github.com/NetFPGA/P4-NetFPGA-live

[5] https://github.com/NetFPGA/P4-NetFPGA-public

[6] http://www.cnblogs.com/wpqwpq/p/7886596.html

[7] http://www.cnblogs.com/wpqwpq/p/7886940.html

[8] http://www.cnblogs.com/wpqwpq/p/7891259.html

posted @ 2017-12-11 00:11  考拉小无  阅读(1553)  评论(1)    收藏  举报