P4-utils basic examples
Before running the examples, please refer to p4-utils.
[topology]
H1---(port1)S1(port2)---H2
In the first example, the s1 will forward the packet from port 1 to port2, and vise versa.
Note. You only need to prepare three files, i.e. basic.p4 (p4 program), cmd.txt(command for the s1), and p4app.json (configuration file).
(basic.p4)
/* -*- P4_16 -*- */ #include <core.p4> #include <v1model.p4> const bit<16> TYPE_IPV4 = 0x800; /************************************************************************* *********************** 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
{ macAddr_t
dstAddr; macAddr_t
srcAddr; bit<16> etherType; } header ipv4_t { 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; ip4Addr_t srcAddr; ip4Addr_t dstAddr; } struct metadata { /* empty */ } struct headers { 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 {
transition parse_ethernet; } state parse_ethernet
{ packet.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType)
{
TYPE_IPV4: parse_ipv4;
default: accept; } } state parse_ipv4 { packet.extract(hdr.ipv4);
transition accept; } } /************************************************************************* ************ C H E C K S U M V E R I F I C A T I O
N ************* *************************************************************************/ control MyVerifyChecksum(inout headers hdr, inout metadata meta) { apply { } } /************************************************************************* ************** 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 forward(egressSpec_t port) { standard_metadata.egress_spec =
port; } table phy_forward
{
key = {
standard_metadata.ingress_port:
exact; }
actions = {
forward;
drop; }
size = 1024; default_action = drop(); } apply { phy_forward.apply(); } } /************************************************************************* **************** E G R E S S P R O C E S S
I N G ******************* *************************************************************************/ control MyEgress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t
standard_metadata) { apply { } } /************************************************************************* ************* C H E C K S U M C O M P U T A T I O N ************** *************************************************************************/ control MyComputeChecksum(inout headers
hdr, inout
metadata meta) { apply { update_checksum( hdr.ipv4.isValid(),
{ hdr.ipv4.version, hdr.ipv4.ihl,
hdr.ipv4.diffserv,
hdr.ipv4.totalLen,
hdr.ipv4.identification,
hdr.ipv4.flags,
hdr.ipv4.fragOffset,
hdr.ipv4.ttl,
hdr.ipv4.protocol,
hdr.ipv4.srcAddr,
hdr.ipv4.dstAddr },
hdr.ipv4.hdrChecksum,
HashAlgorithm.csum16); } } /************************************************************************* *********************** D E P A R S E R ******************************* *************************************************************************/ control MyDeparser(packet_out packet, in headers hdr)
{ apply { packet.emit(hdr.ethernet); packet.emit(hdr.ipv4); } } /************************************************************************* *********************** S W I T C H ******************************* *************************************************************************/ V1Switch( MyParser(), MyVerifyChecksum(), MyIngress(), MyEgress(), MyComputeChecksum(), MyDeparser() ) main; |
(cmd.txt)
table_add phy_forward forward 1 => 2 table_add phy_forward forward 2 => 1 |
(p4app.json)
{
"program": "basic.p4",
"switch": "simple_switch",
"compiler": "p4c",
"options": "--target bmv2 --arch v1model --std
p4-16",
"switch_cli": "simple_switch_CLI",
"cli": true,
"pcap_dump": true,
"enable_log": true,
"topo_module": { "file_path":
"", "module_name":
"p4utils.mininetlib.apptopo", "object_name":
"AppTopoStrategies"
},
"controller_module": null,
"topodb_module": { "file_path":
"", "module_name":
"p4utils.utils.topology", "object_name":
"Topology"
},
"mininet_module": { "file_path":
"", "module_name":
"p4utils.mininetlib.p4net", "object_name":
"P4Mininet"
},
"topology": { "links":
[["h1", "s1", {"delay":"10ms", "bw": 10, "queue_length":
100, "weight":1}], ["h2", "s1"]], "hosts": {
"h1": { },
"h2": { } }, "switches": {
"s1": {
"cli_input": "cmd.txt",
"program": "basic.p4" } }
} } |
[execution]
P4-utils will set the ip address, default route, and arp table for h1 and h2.
Open another terminal, use simple_switch_CLI to connect to s1. Check the phy_forward table. You will also see that the rules have already been installed.
Now h1 can directly ping h2.
[topology]
H1 (10.0.1.1, 00:00:0a:00:01:01)----(port1) s1 (port2)----(10.0.2.1, 00:00:0a:00:02:01)
In this example, S1 will forward the packet according to the destination IP address of the packet.
(ip_forward.p4)
#include <core.p4> #include <v1model.p4> typedef bit<48> macAddr_t; typedef bit<9> egressSpec_t; header arp_t { bit<16> htype; bit<16> ptype; bit<8> hlen; bit<8> plen; bit<16> opcode; bit<48> hwSrcAddr; bit<32> protoSrcAddr; bit<48> hwDstAddr; bit<32> protoDstAddr; } header ethernet_t
{ bit<48> dstAddr; bit<48> srcAddr; bit<16> etherType; } header ipv4_t { 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; bit<32> srcAddr; bit<32> dstAddr; } struct metadata { } struct headers { @name(".arp") arp_t arp; @name(".ethernet")
ethernet_t
ethernet; @name(".ipv4")
ipv4_t ipv4; } parser ParserImpl(packet_in packet, out headers hdr,
inout metadata meta, inout
standard_metadata_t standard_metadata)
{ @name(".parse_arp") state parse_arp { packet.extract(hdr.arp);
transition accept; } @name(".parse_ethernet")
state parse_ethernet { packet.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType)
{
16w0x800: parse_ipv4;
16w0x806: parse_arp;
default: accept; } } @name(".parse_ipv4")
state parse_ipv4 { packet.extract(hdr.ipv4);
transition accept; } @name(".start")
state start {
transition parse_ethernet; } } control egress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata)
{ apply { } } control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata)
{ @name(".set_nhop") action set_nhop(macAddr_t dstAddr, egressSpec_t port) {
//set the src mac address as the previous dst, this is not correct right? hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
//set the destination mac address that we got from the match in the
table hdr.ethernet.dstAddr = dstAddr;
//set the output port that we also get from the table standard_metadata.egress_spec =
port;
//decrease ttl by 1
hdr.ipv4.ttl = hdr.ipv4.ttl - 1; } @name("._drop")
action _drop() { mark_to_drop(standard_metadata); } @name(".ipv4_lpm")
table ipv4_lpm {
actions = {
set_nhop;
_drop; }
key = {
hdr.ipv4.dstAddr: lpm; }
size = 512;
const default_action = _drop(); } apply {
ipv4_lpm.apply(); } } control DeparserImpl(packet_out packet, in headers hdr)
{ apply { packet.emit(hdr.ethernet); packet.emit(hdr.arp); packet.emit(hdr.ipv4); } } control verifyChecksum(inout headers hdr, inout metadata meta) { apply { verify_checksum(true, { hdr.ipv4.version, hdr.ipv4.ihl,
hdr.ipv4.diffserv, hdr.ipv4.totalLen, hdr.ipv4.identification,
hdr.ipv4.flags, hdr.ipv4.fragOffset, hdr.ipv4.ttl, hdr.ipv4.protocol,
hdr.ipv4.srcAddr, hdr.ipv4.dstAddr }, hdr.ipv4.hdrChecksum,
HashAlgorithm.csum16); } } control computeChecksum(inout headers hdr, inout metadata meta) { apply { update_checksum(true, { hdr.ipv4.version, hdr.ipv4.ihl,
hdr.ipv4.diffserv, hdr.ipv4.totalLen, hdr.ipv4.identification,
hdr.ipv4.flags, hdr.ipv4.fragOffset, hdr.ipv4.ttl, hdr.ipv4.protocol,
hdr.ipv4.srcAddr, hdr.ipv4.dstAddr }, hdr.ipv4.hdrChecksum,
HashAlgorithm.csum16); } } V1Switch(ParserImpl(), verifyChecksum(), ingress(), egress(), computeChecksum(), DeparserImpl()) main; |
(cmd.txt)
table_add ipv4_lpm set_nhop 10.0.1.1/32 =>
00:00:0a:00:01:01 1 table_add ipv4_lpm set_nhop 10.0.2.1/32 => 00:00:0a:00:02:01 2 |
(p4app.json) Note: "assignment_strategy": "manual" Then you can manually set the ip address and gateway for h1 and h2. Also, “ip” and “gw” need to be set at
the same time. You can not only set “ip”. It will
cause error.
{
"program": "ip_forward.p4",
"switch": "simple_switch",
"compiler": "p4c",
"options": "--target bmv2 --arch v1model --std
p4-16",
"switch_cli": "simple_switch_CLI",
"cli": true,
"pcap_dump": true,
"enable_log": true,
"topo_module": { "file_path":
"", "module_name":
"p4utils.mininetlib.apptopo", "object_name":
"AppTopoStrategies"
},
"controller_module": null,
"topodb_module": { "file_path":
"", "module_name":
"p4utils.utils.topology", "object_name":
"Topology"
},
"mininet_module": { "file_path":
"", "module_name":
"p4utils.mininetlib.p4net", "object_name":
"P4Mininet"
},
"topology": { "assignment_strategy": "manual", "auto_arp_tables":
"true", "auto_gw_arp":
"true", "links":
[["h1", "s1"], ["h2", "s1"]], "hosts": {
"h1": { "ip":
"10.0.1.1/24", "gw": "10.0.1.254" },
"h2": { "ip":
"10.0.2.1/24", "gw": "10.0.2.254" } }, "switches": {
"s1": {
"cli_input": "cmd.txt",
"program": "ip_forward.p4" } }
} } |
[execution]
IP and Gateway are set according to the settings in p4app.json
h1 can ping h2.
Open another terminal. You can go to pcap directory. You can see the captured packets.
You can also go to log directory to see the log files.
Dr. Chih-Heng Ke
Department of Computer Science and
Information Engineering, National Quemoy University, Kinmen, Taiwan
Email: smallko@gmail.com