赞
踩
简单P4交换机(VSS:very simple switch)它只是一个教学示例,说明了可编程交换机如何利用P4实现和代码编写。VSS具有许多固定功能块(在我们的示例中以浅蓝色显示),具体功能在下一小节描述。 白色块为P4代码实现模块(parse、match-action pipeline、 deparse)。
VSS通过8个输入以太网端口之一,通过再循环通道或从直接连接到CPU的端口接收数据包。 VSS有一个单一的Parser,解析后输出到match-action pipline,pipline处理后输出到Deparse模块,经过Deparser后,数据包通过8个输出以太网端口之一或3个“特殊”端口之一发出:
图中的白色块是可编程的,用户必须提供相应的P4程序来指定每个此类块的行为。红色箭头指示用户定义的数据流。浅蓝色块是固定功能组件。绿色箭头是数据平面接口,用于在固定功能块和可编程块之间传递信息,这些功能在P4程序中作为内部元数据(译者注:类似于全局变量的概念)。
P4仅能实现部分模块功能,部分功能还是需要预定义模块来实现。
Parser runtime模块与Parser协同工作。 它基于Parser的结果match-action pipline提供错误代码,并且向demux 模块提供关于数据包payload的信息(例如,剩余payload数据的大小)。 parser完成数据包的处理后,将使用关联的元数据作为输入(数据包头和用户定义的元数据)调用match-action pipline。
Demux模块的核心功能是从deparser接收输出数据包的包头,从parser接收数据包的payload,将它们组合成新的数据包,然后将结果发送到正确的输出端口。 输出端口由outCtrl.ouputPort的值指定,该值由match-action pipline设置。
(译者注:文中的语法是基于P4_16的)下面一段代码是VSS的定义文件,定义了VSS所要用到的结构体以及函数声明。
// File "very_simple_switch_model.p4" // Very Simple Switch P4 declaration // core library needed for packet_in and packet_out definitions # include <core.p4> /* Various constants and structure declarations */ /* ports are represented using 4-bit values */ typedef bit<4> PortId; /* only 8 ports are "real" */ const PortId REAL_PORT_COUNT = 4w8; // 4w8 is the number 8 in 4 bits /* metadata accompanying an input packet */ struct InControl { PortId inputPort; } /* special input port values */ const PortId RECIRCULATE_IN_PORT = 0xD; const PortId CPU_IN_PORT = 0xE; /* metadata that must be computed for outgoing packets */ struct OutControl { PortId outputPort; } /* special output port values for outgoing packet */ const PortId DROP_PORT = 0xF; const PortId CPU_OUT_PORT = 0xE; const PortId RECIRCULATE_OUT_PORT = 0xD; /* Prototypes for all programmable blocks */ /** * Programmable parser. * @param <H> type of headers; defined by user * @param b input packet * @param parsedHeaders headers constructed by parser */ parser Parser<H>(packet_in b, out H parsedHeaders); /** * Match-action pipeline * @param <H> type of input and output headers * @param headers headers received from the parser and sent to the deparser * @param parseError error that may have surfaced during parsing * @param inCtrl information from architecture, accompanying input packet * @param outCtrl information for architecture, accompanying output packet */ control Pipe<H>(inout H headers, in error parseError,// parser error in InControl inCtrl,// input port out OutControl outCtrl); // output port /** * VSS deparser. * @param <H> type of headers; defined by user * @param b output packet * @param outputHeaders headers for output packet */ control Deparser<H>(inout H outputHeaders, packet_out b); /** * Top-level package declaration - must be instantiated by user. * The arguments to the package indicate blocks that * must be instantiated by the user. * @param <H> user-defined type of the headers processed. */ package VSS<H>(Parser<H> p, Pipe<H> map, Deparser<H> d); // Architecture-specific objects that can be instantiated // 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 }
引入的 core.p4 是P4语言的内置库,定义了标准数据类型和错误类型
bit<4> 是具有4位的位字符串的类型。
语法4w0xF表示使用4位表示值15。 另一种表示法是4w15。在许多情况下,可以省略width修饰符,只写15。
error是用于保存错误代码的内置P4类型
Parser 声明
parser Parser<H>(packet_in b, out H parsedHeaders);
该声明描述了解析器的接口,但尚未描述其实现,该接口将由程序员负责实现。 解析器从packet_in读取其输入,packet_in是在core.p4库中声明的P4 extern对象。 解析器将其输出(关键字out)写入parsedHeaders参数。 此参数的类型为H,但是具体类型不知道,需要程序员提供。
Match-Action pipeline 声明
control Pipe<H>(inout H headers,
in error parseError,// parser error
in InControl inCtrl,// input port
out OutControl outCtrl); // output port
该声明需要输入3个参数: 1、解析后的数据包头,2、解析器错误parseError 3、inCtrl控制数据。上图指出了这些信息的不同来源。 pipeline将其输出写入outCtrl,并且它必须在适当位置更新要由Deparser使用的包头。
Package 定义
package VSS<H>
类型变量H表示尚待用户稍后提供的类型,但未知。 在这种情况下,H是用户程序将要处理的包头的类型。 Parser将生成这些包头的解析表示,并且match-action管道将在适当位置更新输入包头以生成输出包头。
上图展示了VSS交换机数据处理流程。下面用代码来实现上图中的逻辑。
// Include P4 core library # include <core.p4> // Include very simple switch architecture declarations # include "very_simple_switch_model.p4" // This program processes packets comprising an Ethernet and an IPv4 // header, and it forwards packets using the destination IP address typedef bit<48> EthernetAddress; typedef bit<32> IPv4Address; // Standard Ethernet header header Ethernet_h { EthernetAddress dstAddr; EthernetAddress srcAddr; bit<16> etherType; } // IPv4 header (without options) header IPv4_h { bit<4> version; bit<4> ihl; bit<8> diffserv; bit<16> totalLen; bit<16> identification; bit<3> flags; bit<13> fragOffset; bit<8> ttl; bit<8> protocol; bit<16> hdrChecksum; IPv4Address srcAddr; IPv4Address dstAddr; } // Structure of parsed headers struct Parsed_packet { Ethernet_h ethernet; IPv4_h ip; } // Parser section // User-defined errors that may be signaled during parsing error { IPv4OptionsNotSupported, IPv4IncorrectVersion, IPv4ChecksumError } parser TopParser(packet_in b, out Parsed_packet p) { Checksum16() ck; // instantiate checksum unit state start { b.extract(p.ethernet); transition select(p.ethernet.etherType) { 0x0800: parse_ipv4; // no default rule: all other packets rejected } } state parse_ipv4 { b.extract(p.ip); verify(p.ip.version == 4w4, error.IPv4IncorrectVersion); verify(p.ip.ihl == 4w5, error.IPv4OptionsNotSupported); ck.clear(); ck.update(p.ip); // Verify that packet checksum is zero verify(ck.get() == 16w0, error.IPv4ChecksumError); transition accept; } } // Match-action pipeline section control TopPipe(inout Parsed_packet headers, in error parseError, // parser error in InControl inCtrl, // input port out OutControl outCtrl) { IPv4Address nextHop; // local variable /** * Indicates that a packet is dropped by setting the * output port to the DROP_PORT */ action Drop_action() { outCtrl.outputPort = DROP_PORT; } /** * Set the next hop and the output port. * Decrements ipv4 ttl field. * @param ivp4_dest ipv4 address of next hop * @param port output port */ action Set_nhop(IPv4Address ipv4_dest, PortId port) { nextHop = ipv4_dest; headers.ip.ttl = headers.ip.ttl - 1; outCtrl.outputPort = port; } /** * Computes address of next IPv4 hop and output port * based on the IPv4 destination of the current packet. * Decrements packet IPv4 TTL. * @param nextHop IPv4 address of next hop */ table ipv4_match { key = { headers.ip.dstAddr: lpm; } // longest-prefix match actions = { Drop_action; Set_nhop; } size = 1024; default_action = Drop_action; } /** * Send the packet to the CPU port */ action Send_to_cpu() { outCtrl.outputPort = CPU_OUT_PORT; } /** * Check packet TTL and send to CPU if expired. */ table check_ttl { key = { headers.ip.ttl: exact; } actions = { Send_to_cpu; NoAction; } const default_action = NoAction; // defined in core.p4 } /** * Set the destination MAC address of the packet * @param dmac destination MAC address. */ action Set_dmac(EthernetAddress dmac) { headers.ethernet.dstAddr = dmac; } /** * Set the destination Ethernet address of the packet * based on the next hop IP address. * @param nextHop IPv4 address of next hop. */ table dmac { key = { nextHop: exact; } actions = { Drop_action; Set_dmac; } size = 1024; default_action = Drop_action; } /** * Set the source MAC address. * @param smac: source MAC address to use */ action Set_smac(EthernetAddress smac) { headers.ethernet.srcAddr = smac; } /** * Set the source mac address based on the output port. */ table smac { key = { outCtrl.outputPort: exact; } actions = { Drop_action; Set_smac; } size = 16; default_action = Drop_action; } apply { if (parseError != error.NoError) { Drop_action(); // invoke drop directly return; } ipv4_match.apply(); // Match result will go into nextHop if (outCtrl.outputPort == DROP_PORT) return; check_ttl.apply(); if (outCtrl.outputPort == CPU_OUT_PORT) return; dmac.apply(); if (outCtrl.outputPort == DROP_PORT) return; smac.apply(); } } // deparser section control TopDeparser(inout Parsed_packet p, packet_out b) { Checksum16() ck; apply { b.emit(p.ethernet); if (p.ip.isValid()) { ck.clear(); // prepare checksum unit p.ip.hdrChecksum = 16w0; // clear checksum ck.update(p.ip); // compute new checksum. p.ip.hdrChecksum = ck.get(); } b.emit(p.ip); } } // Instantiate the top-level VSS package VSS(TopParser(), TopPipe(), TopDeparser()) main;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。