Hi all,
I’m using the recommended “P4 Tutorial Development” VM (prebuilt from the official P4 repo). I’m trying to modify basic.p4 to prototype a max–min fairness (MMF) controller: the controller computes per-flow rates; the switch enforces them with a per-flow meter and drops packets marked RED. I’d like a minimal, correct pattern for declaring and using meters in v1model on bmv2.
Below is the current program I’m working with (trimmed to essentials). The idea is:
-
ipv4_lpmpicks the egress/next hop. -
ipv4_src(exact onsrcAddr) assigns a flow index via actionset_flow(…, idx). -
In
apply, I call a meter array with that index and drop if color==RED.
Code I’m trying now:
// SPDX-License-Identifier: Apache-2.0
/* -*- 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 {
bit<32> flow_idx; // indeks używany przez tablicę meterów
}
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) {
meter src_meter;
action drop() {
mark_to_drop(standard_metadata);
}
action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
hdr.ethernet.dstAddr = dstAddr;
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
standard_metadata.egress_spec = port;
}
action set_flow(macAddr_t dstAddr, egressSpec_t port, bit<32> idx) {
hdr.ethernet.dstAddr = dstAddr;
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
standard_metadata.egress_spec = port;
meta.flow_idx = idx;
}
table ipv4_lpm {
key = { hdr.ipv4.dstAddr: lpm; }
actions = { ipv4_forward; drop; NoAction; }
size = 1024;
default_action = NoAction();
}
table ipv4_src {
key = { hdr.ipv4.srcAddr: exact; }
actions = { set_flow; drop; NoAction; }
size = 1024;
default_action = NoAction();
}
apply {
bit<32> mval; // raw value from meter
bit<2> col; // color = mval[1:0]
meta.flow_idx = 0;
mval = 0;
if (hdr.ipv4.isValid()) {
bool hit = ipv4_lpm.apply().hit;
if (!hit) {
drop();
} else {
ipv4_src.apply(); // assign egress + flow_idx
src_meter.execute_meter(meta.flow_idx, mval); // call meter
col = (bit<2>) mval; // color is 2 LSBs
if (col == 2w2) { // 2 == RED
drop();
}
}
} else {
drop();
}
}
}
/*************************************************************************
**************** 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;
Environment / toolchain
-
VM: P4 Tutorial Development 2025-04-01
-
Compiler:
p4c-bm2-ss --p4v 16 -
Target/runtime:
simple_switch_grpc(bmv2, v1model)
Issues encountered (confusion around meters & colors)
Across attempts I have hit several inconsistencies and errors, for example:
-
MeterColor_tnot found / color constants not found:-
error: METER_COLOR_GREEN: declaration not found -
error: RED: declaration not found -
syntax error, unexpected IDENTIFIER "MeterColor_t"
-
-
Meter type / constructor confusion:
-
meter<bit<32>>(1024) src_meter;→Type meter has 0 type parameter(s) -
meter(1024) src_meter;→type meter has no matching constructor
-
-
Local variable declaration weirdness (older p4c behavior):
syntax error, unexpected IDENTIFIER "color"if declaring a local color var inapplyafter statements.
-
Bit-width literal warnings:
value does not fit in 0 bitswhen using something like0w2vs2w0.
Given the above, I’m not sure what is the canonical way on this VM to:
-
Declare a meter array in v1model for bmv2 (is
meter src_meter;correct? or should I use adirect_meterattached to a table instead?), -
Call the meter in ingress and obtain the color (return value vs out param, type of that value),
-
Interpret the color (are symbolic enums available? or should I treat it as an integer
0/1/2?), -
Configure the meter via P4Runtime (CIR/PIR/burst) for index = flow_idx (assigned by
ipv4_src).
My goal is a simple, robust pattern that works on this toolchain, e.g.:
-
minimal example for meter declaration and execute_meter usage in ingress,
-
what type should I use for the color (and how to compare it),
-
whether I should switch to a direct meter attached to
ipv4_src(per-entry) instead of a stand-alone meter array with index, -
sample P4Runtime calls (or pointers) to set per-index rates.
If the recommended approach on this VM is different (e.g., use a direct meter bound to a match-action table), I’m happy to adopt that—just need the correct v1model idiom.
Thanks a ton for any guidance or a minimal working snippet!