摘要
P4是Programming Protocol-independent Packet Processors的缩写,
开发环境:windows10 virtualBox vagrant
官方文档
代码库
P4中的一些概念
- P4是协议不相关的,所谓协议相关就是只能根据现有的协议来进行编程,例如OpenFlow1.0时有12个字段(IP,MAC等),但这明显是远远不够的。那么P4就解决了这个问题
 - P4可以定义自己的匹配字段和动作(action),从而定义流表,进而形成流水线
 - PISA(Protocol Independent Switch Architecture),一种通用的网络设备架构
 - P4 runtime的定位与OpenFlow一致,与P4配合使用
 - P4c是P4的reference编译器项目
 - behavioral-model(bmv2)是一个支持P4的软交换机version2
 - PI是P4 runtime的实现,用于控制面对数据面的控制
 - mininet是一个轻量的网络仿真环境,可以构建虚拟网络,用于学习和测试
 - P4 tagets指包括可编程ASIC,ovs,Bmv2等可加载P4程序的网络器件
 
一个简单的P4 Bmv2的simple switch target architecture
The V1Model consists of six P4 programmable components:
- Parser
 - Checksum verification control block
 - Ingress Match-Action processing control block
 - Egress Match-Action processing control block
 - Checksum update control block
 - Deparser
 
开发环境部署
Get Start
官方提供的p4开发虚机镜像
国内某开发者的虚机镜像
由于P4开发环境各个组件之间有依赖,且兼容性做的不好,所以不要单独升级某一组件到最新版,可能与其他组件不兼容
- virtualbox创建Ubuntu16.04,desktop版,官方建议的版本,其他版本未经测试
 - 执行root-bootstrap.sh和user-bootstrap.sh,代码全部修改为gitee上的代码
 - 修改grpc中的submodule为gitee中的地址。修改.gitmodule,执行git submodule sync
 
First program
参考 https://github.com/p4lang/tutorials/tree/master/exercises/basic
- 在P4 tutorial的exercises/中有多个实例, 下是一个需要编程题(编写TODO部分),其子目录solution中的*.p4是添加了TODO部分的参考答案。
 - 在该basic目录下执行make run会执行p4c编译代码,并在mininet中执行Bmv2。该目录下的Makefile将pod-topo目录下的topology.json作为拓扑信息参数,传递给../../utils目录下的Makefile,并执行该目录下的run_exercise.py脚本。
 - 把basic.p4替换为solution中的文件时,各个host就可以ping通了。
以basic.p4进行说明: 
/*************************************************************************
*********************** H E A D E R S  ***********************************
*************************************************************************/
typedef bit<9>  egressSpec_t;
typedef bit<48> macAddr_t;
typedef bit<32> ip4Addr_t;
header ethernet_t {         //header数据结构对应packet中的header
    macAddr_t dstAddr;
    macAddr_t srcAddr;
    bit<16>   etherType;
}
struct headers {              //struct类似于python中的dictionary
    ethernet_t   ethernet;
    ipv4_t       ipv4;
}
/*************************************************************************
*********************** P A R S E R  ***********************************
*************************************************************************/
parser MyParser(packet_in packet,
                out headers hdr,
                inout metadata meta,
                inout standard_metadata_t standard_metadata) {
    state start {                   //进入parse过程,进入parse_ethernet
        transition parse_ethernet;
    }
    state parse_ethernet {
        packet.extract(hdr.ethernet);       //参数hdr的ethernet头,解析成功,header的validity会自动设置为true,即自动帮我们确定header的格式是否正确
        transition select(hdr.ethernet.etherType) {     //根据hdr.ethernet.etherType的值选择处理流程
            TYPE_IPV4: parse_ipv4;
            default: accept;
        }
    }
    state parse_ipv4 {
        packet.extract(hdr.ipv4);
        transition accept;
    }
}
/*************************************************************************
**************  I N G R E S S   P R O C E S S I N G   *******************
*************************************************************************/
control MyIngress(inout headers hdr,
                  inout metadata meta,
                  inout standard_metadata_t standard_metadata) {
    action drop() {
        mark_to_drop(standard_metadata);
    }
    
    action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {         //P4中的action就是C语言中的函数,此action需传入两个参数,此处可结合s1中ipv4_lpm这个table去理解
        standard_metadata.egress_spec = port;
        hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
        hdr.ethernet.dstAddr = dstAddr;
        hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
    }
    
    table ipv4_lpm {            //建立一个名称为ipv4_lpm(longest prefix match)的table
        key = {
            hdr.ipv4.dstAddr: lpm;          //通过ipv4的dstAddr进行lpm
        }
        actions = {             //包含table中所有可能的动作
            ipv4_forward;
            drop;
            NoAction;
        }
        size = 1024;            //table的entry数量
        default_action = drop();
    }
    
    apply {
        if (hdr.ipv4.isValid()) {
            ipv4_lpm.apply();
        }
    }
}
/*************************************************************************
***********************  S W I T C H  *******************************
*************************************************************************/
V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;
S1交换机
在该实验中,通过静态建表的方式,在switch中的有MyIngress.ipv4_lpm表内容如下:
则当packet的ipv4.dstAddr匹配table中match的key,根据命中的action(ipv4_forward),传入参数dstAddr和port,并进行处理
P4 language specification
Overview
The core abstractions:
- Header type
 - Parsers
 - Tables
 - Actions
 - Match-action-unit
 - Control flow
 - Extern objects
 - User-define metadata
 - Intrinsic metadata
 
Data plane interface
control MatchActionPipe<H>(in bit<4> inputPort,
                           inout H parsedHeaders,
                           out bit<4> outputPort);
此declaration描述了一个叫MatchActionPipe的block,该block通过对data-dependent sequence of match-action单元装置和其他必要的constructs进行programmed
- 第一个参数是一个命名为inputPort的4bit的值,其中的in表示这是一个输入,不能修改
 - 第二个参数是一个命名为parsedHeaders的H类型的object,inout表明这个参数是both an input and an output
 - 第三个参数是一个命名为outputPort的4bit的值,其中out表明这是一个初始值未定义的输出
 
Extern objects and functions
P4 programs还能与体系结构(architecture)所提供的object and function交互(interact)。这些objects通过extern construct描述,such objects expose to the data-plane
一个extern object描述了一组由object实现的方法,但不是这些方法的实现(类似面向对象方法中的抽象类)下面这个结构体描述了the operations offered by an incremental checksum unit
extern Checksum16 {
    Checksum16();              // constructor
    void clear();              // prepare unit for computation
    void update<T>(in T data); // add data to checksum
    void remove<T>(in T data); // remove data from existing checksum
    bit<16> get(); // get the checksum for the data added since last clear
}
Very Simple Switch Architecture(VSS)
- Arbiter block
 - Parser runtime block
 - Demux block
 - Available exter blocks
 
变量类型
- Boolean
 - integer
 - string