# Help with bmv2/v1model meter + “color” usage for Max–Min Fairness (P4 Tutorial VM)

**URL:** <https://forum.p4.org/t/help-with-bmv2-v1model-meter-color-usage-for-max-min-fairness-p4-tutorial-vm/1402>\
**Category:** Uncategorized\
**Created:** [November 11, 2025, 12:29pm UTC](https://forum.p4.org/t/help-with-bmv2-v1model-meter-color-usage-for-max-min-fairness-p4-tutorial-vm/1402 "2025-11-11T12:29:38Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![kacperkuczynski26](https://avatars.discourse-cdn.com/v4/letter/k/4da419/32.png) [@kacperkuczynski26](https://forum.p4.org/u/kacperkuczynski26)\
**Post date:** [November 11, 2025, 12:29pm UTC](https://forum.p4.org/t/help-with-bmv2-v1model-meter-color-usage-for-max-min-fairness-p4-tutorial-vm/1402/1 "2025-11-11T12:29:38Z")

</div>

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_lpm` picks the egress/next hop.

- `ipv4_src` (exact on `srcAddr`) assigns a **flow index** via action `set_flow(…, idx)`.

- In `apply`, I call a **meter array** with that index and drop if color==RED.

Code I’m trying now:

```auto
// 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_t` **not found** / color constants not found:

- Meter type / constructor confusion:

- Local variable declaration weirdness (older p4c behavior):

- Bit-width literal warnings:

Given the above, I’m not sure what is the **canonical way on this VM** to:

1. **Declare a meter array** in v1model for bmv2 (is `meter src_meter;` correct? or should I use a `direct_meter` attached to a table instead?),

2. **Call the meter** in ingress and obtain the **color** (return value vs out param, type of that value),

3. **Interpret the color** (are symbolic enums available? or should I treat it as an integer `0/1/2`?),

4. **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!

---

<div class="post-metadata">

**Author:** ![andyfingerhut](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.p4.org/andyfingerhut/32/17_2.png) [@andyfingerhut](https://forum.p4.org/u/andyfingerhut)\
**Post date:** [November 14, 2025, 7:15am UTC](https://forum.p4.org/t/help-with-bmv2-v1model-meter-color-usage-for-max-min-fairness-p4-tutorial-vm/1402/2 "2025-11-14T07:15:08Z")

</div>

I do not have example code to show you for configuring the rates of the meter, but here is a link to a modified version of file tutorials/exercises/ecn/solution/ecn.p4 in the tutorials repository that has a direct meter associated with table ipv4\_lpm:

The direct\_meter extern instance declaration: [tutorials/exercises/ecn/solution/ecn.p4 at add-direct-meter-to-ecn-exercise-p4 · jafingerhut/tutorials · GitHub](https://github.com/jafingerhut/tutorials/blob/add-direct-meter-to-ecn-exercise-p4/exercises/ecn/solution/ecn.p4#L92)

Extra lines of code added to action ipv4\_forward that updates the meter, gets the color, compares it to red, and drops the packet if it is red: [tutorials/exercises/ecn/solution/ecn.p4 at add-direct-meter-to-ecn-exercise-p4 · jafingerhut/tutorials · GitHub](https://github.com/jafingerhut/tutorials/blob/add-direct-meter-to-ecn-exercise-p4/exercises/ecn/solution/ecn.p4#L104-L108)

The table property added to table ipv4\_lpm that associates the direct meter named mymeter1 with the table: [tutorials/exercises/ecn/solution/ecn.p4 at add-direct-meter-to-ecn-exercise-p4 · jafingerhut/tutorials · GitHub](https://github.com/jafingerhut/tutorials/blob/add-direct-meter-to-ecn-exercise-p4/exercises/ecn/solution/ecn.p4#L120)

There are preprocessor symbols defined for the numeric values of the three colors here in the v1model.p4 include file: [p4c/p4include/v1model.p4 at main · p4lang/p4c · GitHub](https://github.com/p4lang/p4c/blob/main/p4include/v1model.p4#L201-L203)

There are quite a few comments in the v1model.p4 include file for the definitions of the direct\_meter and meter externs that might be useful, but they don’t include example code for putting it all together: [p4c/p4include/v1model.p4 at main · p4lang/p4c · GitHub](https://github.com/p4lang/p4c/blob/main/p4include/v1model.p4#L210-L287)

The literal `0w2` would mean an unsigned value with type `bit<0>` whose value is 2, which is almost never what you want. The warning is because the numeric value 2 is too large to fit into a value that is 0 bits wide. `2w0` is an unsigned value with type `bit<2>` whose value is 0, and is more likely what you want, if you want a numeric value of 0. In most cases you can omit the `2w` prefix on integer constant values, and the compiler will infer what its bit width should be from the context where it is used.

It should also work fine to have an indirect meter, i.e. one with the extern type named `meter`. If you want an example of a P4 program for that, that compiles without error, let me know.

---

<div class="post-metadata">

**Author:** ![kacperkuczynski26](https://avatars.discourse-cdn.com/v4/letter/k/4da419/32.png) [@kacperkuczynski26](https://forum.p4.org/u/kacperkuczynski26)\
**Post date:** [November 24, 2025, 12:03am UTC](https://forum.p4.org/t/help-with-bmv2-v1model-meter-color-usage-for-max-min-fairness-p4-tutorial-vm/1402/3 "2025-11-24T00:03:44Z")

</div>

Thanks a lot, Andy — this clears up most of my confusion.
