In Previous article, we implemented the control plane function to register table entries and acquire traffic counter values using P4Runtime. This time, based on the acquired traffic counter value, we will implement a function to apply the traffic limit for a certain period of time when the specified traffic amount is exceeded. In particular, the counter value is acquired for each source MAC address, and the traffic limit in the data plane is implemented using Meter (RFC2698).
The source code of the content explained in this article is available on here, so please refer to it as well. What is P4 in the first place? Please refer to this article for those who need explanation, and this article for those who need explanation from the basics of P4Runtime.
In this article, the following contents are described as elements for implementing the traffic restriction function for each source MAC address in P4/P4Runtime.
--Meter (RFC2698) operating principle --Direct meter / direct counter concept and implementation in P4 language --How to control direct meter / direct counter by P4Runtime, and implementation by Go language
The P4 program described in this article uses v1model as an architecture. Please note that the detailed implementation differs slightly depending on the device (architecture) used. The concept of architecture in P4 is explained in this article and this article, so please refer to it as well.
Meter is also called Two Rate Three Color Marker (trTCM), and it is a mechanism that performs three types of "coloring" according to the traffic flow rate for two types of threshold values, and is specified by RFC2698. Generally, meter is used to control the flow rate by explicitly dropping or marking packets, but meter itself only outputs the excess of the flow rate against the threshold value in "color", and how to use the result. Is left to the implementation on the data plane side.
Two types of information rate / burst size, `committed``` and
`peak```, are specified in Meter, and the flow rate of traffic passing through (information rate = bandwidth / packet size = burst size). Outputs the corresponding "color" when each is exceeded. The output "color" and its conditions are as follows.
--`` GREEN``` ⇒
committed
and ``` peak``` If neither is exceeded --``` YELLOW``` ⇒ When ``` committed``` is exceeded but
peak
is not exceeded
-- RED
⇒ When peak
is exceeded
These four thresholds are set from the control plane, and the data plane is set at any time (v1model has a function called `read```, and [PSA](https://p4.org/p4-spec/docs/PSA-v1.1.0.html#sec-meters) has
execute
. Get the corresponding "color" (using the function ``). In many cases, each threshold value is abbreviated as shown below (even in P4Runtime, each threshold value is treated as a variable with the following name).
CIR
: committed information rateCBS
: committed burst sizePIR
: peak information ratePBS
: peak burst sizeThe operation of meter uses two token buckets corresponding to `committed``` and
peak``` (hereinafter referred to as ``
P and `` `C
, respectively). Is modeled. The sizes of P
and C
are
PBSand
CBS(corresponding burst sizes), respectively. In the initial state,
Pand
C are filled with the maximum amount (` `PBS
/ CBS
, respectively, but traffic passes through. The contents of the token bucket will decrease by the amount passed each time. The contents of `P`
and
C increase by `` `PIR
/
CIR every second, and the maximum is `` `PBS
/ `. The contents will be restored to `` CBS```. Therefore, when the token bucket is empty, it means one of the following.
--Packets larger than burst size pass --Traffic of information rate or higher passes per unit time
There are two ways to output "color", `color-blind``` mode and
`color-aware``` mode, but the basics are the same and output depending on whether the token bucket is empty or not. Determine the "color". That is, each color is output in the following cases.
-- GREEN
⇒ When neither P
nor C
is empty
-- YELLOW
⇒ P
is not empty, but C
is empty
-- RED
⇒ When P
is empty
color-blind
In the case of mode, only whether the token bucket is empty or not"color"Is judged, butcolor-aware
In the case of mode, whether the token bucket is empty, and in addition to the advance for the input packet"color付け" を許容し,パケットが事前にcolor付けされている場合はそちらのcolorを優先して出力します.
This time, we will implement the traffic restriction function using the meter explained above. In particular, prepare a meter for each source MAC address and output the color with `read``` only when the traffic restriction is enabled & packet drop is output when the color is
`` RED. By executing it on the side, the traffic is limited to the
PIR and` `PBS
set from the control plane. The following describes how to describe these processes using P4 and P4Runtime.
*** These functions are performed on a table entry basis to perform "traffic limit" and "traffic count" for each source MAC address direct meter and direct counter is used. The direct meter / direct counter are defined in P4 as follows (the direct meter / direct counters `limiter``` and
`meter_cnt``` are defined respectively).
switchgin_meter.p4
control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
direct_counter(CounterType.bytes) meter_cnt;
direct_meter<bit<2>>(MeterType.bytes) limitter;
<Omission>
}
direct_meter<T>...Bit for T<W>Is specified. This represents the bit width of the color output when reading the direct meter 2<=Must be W.[This implementation](https://github.com/ryu-s-4/p4-practice/blob/master/traffic-limitter/switching_meter.p4)Now, since the variable for storing color is defined with 2bit width, direct_meter<bit<2>>...The direct meter is defined as.
The unit of measurement for direct_meter / direct counter is specified by `` `MeterType``` and `` `CounterType```, but in v1model, each can be selected from the following.
#### **`v1model.p4`**
```c
enum CounterType {
packets,
bytes,
packets_and_bytes
}
enum MeterType {
packets,
bytes
}
Since the direct meter / direct counter is executed for each table entry, it is described in the form linked to the table in the P4 program. Especially this time, since the direct meter / direct counter is executed for each source MAC address, the direct meter / direct counter is set as follows for the table ( check_limit
) whose key is the source MAC address. I will link it.
swithcing_meter.p4
control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
<Omission>
table check_limit {
key = { hdr.ethernet.srcAddr: exact; }
actions = {
limit_traffic;
NoAction;
}
size = 1024;
default_action = NoAction;
counters = meter_cnt;
meters = limitter;
}
<Omission>
}
There are `couters``` and
`meters``` in the setting items of the table, and by setting the direct counter / direct meter defined earlier for each, these are added to each entry of the table. The counter and meter are linked.
The direct counter is slightly different from the counter implemented in the previous article (https://qiita.com/13ryuse4/items/6f95ada4d248372603c2), it does not explicitly `count```, and the traffic amount is automatically counted when hitting a table entry. .. Therefore, basically nothing is done on the data plane side, and the control plane specifies the desired table entry and acquires the counter value associated with that entry. On the other hand, for meter, the traffic ``` color``` is acquired, and what kind of processing is performed according to the obtained ``` color``` is described on the data plane side. In [Implementation this time](https://github.com/ryu-s-4/p4-practice/blob/master/traffic-limitter/switching_meter.p4), if
color``` obtained by meter is `` `` RED``` (exceeds ``
PIR or `` `PBS
. If there is), the packet is dropped. This is implemented by the action `` `limit_traffic```, and it is described as follows.
switching_meter.p4
struct metadata {
bit<2> color;
bool drop_flag;
}
<Omission>
control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
<Omission>
action limit_traffic() {
limitter.read(meta.color);
if (meta.color == V1MODEL_METER_COLOR_RED) {
meta.drop_flag = true;
}
}
<Omission>
}
In v1model, the color
output by meter is defined as follows, and the
color obtained by` `read
is compared with the following. Determines the color
output by meter. The threshold for meter to output the following `` `color``` is implemented from the control plane, but the details will be explained later in this article.
v1model.p4
#define V1MODEL_METER_COLOR_GREEN 0
#define V1MODEL_METER_COLOR_YELLOW 1
#define V1MODEL_METER_COLOR_RED 2
By combining each of the above elements, it is possible to implement a traffic restriction function for each source MAC address (= processing to drop packets when the input traffic exceeds the specified rate/size). For the input packet, first apply the table (`check_limit```) that determines whether the traffic limit is necessary, and the action of the corresponding table entry (source MAC address) is
traffic_limit``` and meter. If the output of is ``
RED``, set ``
drop_flag``` to true and execute `drop ()`
to drop the packet. This is described in P4 as follows.
switching_meter.p4
control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
<Omission>
action drop() {
mark_to_drop(standard_metadata);
}
<Omission>
apply {
meta.drop_flag = false;
check_limit.apply();
if (meta.drop_flag == true) {
drop();
} else {
/* L2 Switching */
}
}
}
ckeck_The entry of limit table corresponds to the MAC address to be monitored, and the table entry is registered./By deleting, the MAC address to be monitored is added / deleted. Also, the action of each entry (the MAC address to be monitored) is NoAction at the time of initial registration.()By doing so, the traffic restriction function is disabled. The traffic counter value of each entry is periodically acquired from the control plane, and when the threshold is exceeded, the action of the corresponding entry is limited from NoAction._The traffic restriction function is enabled by switching to traffic. For more information on implementing these control planes[Here](https://github.com/ryu-s-4/p4-practice/blob/master/traffic-limitter/main.go)Please refer to.
The above is the implementation on the data plane side to realize the traffic restriction function for each source MAC address using direct meter / direct counter. The following is a summary of the P4 Runtime implementation (especially his Go language implementation) that controls the direct meter / direct counter from the control plane.
# Go implementation of direct meter / direct counter control
The traffic restriction function and traffic counter implemented in P4 are controlled from the control plane using P4Runtime. In P4Runtime, direct meter and direct counter are treated as ``` Entity``` like table entry. Therefore, if you want to control each ``` Entity``` or get information from each` `Entity```, as explained in [this article](https://qiita.com/13ryuse4/items/96ed8b31382e1fdd79f1) and [this article](https://qiita.com/13ryuse4/items/3a68a79e81f621b85dd5). It is implemented in the form of ``` Write ()` `` and ``` Read ()` `` of ``` Entity```.
`` `Entity``` of direct meter and direct counter are defined in [p4runtime.pb.go]() as follows.
#### **`go:p4runtime.pb.go`**
/direct meter Entity (and associated variables)/ type Entity_DirectMeterEntry struct { DirectMeterEntry *DirectMeterEntry }
type DirectMeterEntry struct { // The associated table entry. This field is required. // table_entry.action is ignored. Other fields specify the match. TableEntry *TableEntry Config *MeterConfig }
type MeterConfig struct { // Committed information rate (units per sec) Cir int64 // Committed burst size Cburst int64 // Peak information rate (units per sec) Pir int64 // Peak burst size Pburst int64 }
#### **`go:p4runtim.pb.go`**
/direct counter Entity (and associated variables)/ type Entity_DirectCounterEntry struct { DirectCounterEntry *DirectCounterEntry }
type DirectCounterEntry struct { // The associated table entry. This field is required. // table_entry.action is ignored. Other fields specify the match. TableEntry *TableEntry Data *CounterData }
type CounterData struct { ByteCount int64 PacketCount int64 }
For direct meter, store ``` Entity``` with the following parameters set in` ``` Update``` and write to the data plane side with` `Write ()` ``.
- committed information rate(```Cir```)
- committed burst size(```Cburst```)
- peak information rate(```Pir```)
- peak burst size(```Pburst```)
As explained earlier, `` `Cir``` / `` `Cburst``` is the rate / size corresponding to` `` YELLOW```, `` `Pir``` / `` `Pburst``` Is the rate / size corresponding to `` `RED` ``. At this time, it should be noted that `` `UpdateType``` is only allowed for` `` MODIFY```. Basically, "direct" meters and counters are two sides of the same coin with the table entry, so these ``` INSERT``` and` `DELETE``` seem to be executed along with the original table entry. The process of generating ``` Entity``` of direct meter and writing with` `Write ()` `` is implemented in Go as follows.
#### **`main.go`**
```go
import(
v1 "github.com/p4lang/p4runtime/go/p4/v1"
)
func main(){
/*Entity generation of direct meter*/
directmeterentry := &v1.Entity_DirectMeterEntry{
DirectMeterEntry: &v1.DirectMeterEntry{
TableEntry: /*Table entry you want to set*/,
Config: &v1.MeterConfig{
Cir: /* committed information rate (in int64) */,
Cburst: /* committed burst size (in int64) */,
Pir: /* peak information rate (in int64) */,
Pburst: /* peak burst size (in int64) */,
},
},
}
/*Update generation*/
update := &v1.Update{
Type: v1.Update_MODIFY,
Entity: &v1.Entity{
Entity: directmeterentry,
},
}
updates := []*v1.Update{update}
/* Write()Write to the data plane with*/
atomicityType := /*Specify Atomicity*/
var atomicity v1.WriteRequest_Atomicity
switch atomicityType {
case "CONTINUE_ON_ERROR":
atomicity = v1.WriteRequest_CONTINUE_ON_ERROR
case "ROLLBACK_ON_ERROR": // OPTIONAL
atomicity = v1.WriteRequest_ROLLBACK_ON_ERROR
case "DATAPLANE_ATOMIC": // OPTIONAL
atomicity = v1.WriteRequest_DATAPLANE_ATOMIC
default:
atomicity = v1.WriteRequest_CONTINUE_ON_ERROR
}
write_request := v1.WriteRequest{
DeviceId: /* device id */,
ElectionId: /* election id */,
Updates: updates,
Atomicity: atomicity,
}
write_response, err := client.Write(context.TODO(), &write_request)
if err != nil {
/*Error handling*/
}
}
Note that the above client
is an instance of` P4RuntimeClient
, and it is assumed that StreamChannel has been established, Primary has been selected, and ForwardingPipelineConfig has been set in advance. For details, refer to Previous article.
To get the traffic counter value from the direct counter, set the table entry you want to get the counter value to in Entity_DirectCounterEntry`` and press` `Read ()` `` from the data plane side like the direct meter. Get the value. The part that generates
Entity``` of direct counter and gets the counter value with
Read ()` `` is implemented in Go as follows.
main.go
import(
v1 "github.com/p4lang/p4runtime/go/p4/v1"
)
func main(){
/*Entity generation of direct counter*/
directcounterentry := &v1.Entity_DirectCounterEntry{
DirectCounterEntry: &v1.DirectCounterEntry{
TableEntry: /*I want to get the counter value table entry*/,
},
}
/*Generate ReadClient*/
read_request := v1.ReadRequest{
DeviceId: /* device id */,
Entities: []*v1.Entity{&v1.Entity{ Entity: directcounterentry}},
}
read_client, err := client.Read(context.TODO(), &read_request)
if err != nil {
/*Error handling*/
}
/*Acquisition of counter value (DirectCounterEntry)*/
read_response, err := read_client.Recv()
if err != nil {
/*Error handling*/
}
entities := read_response.GetEntities()
if entity == nil {
/*Error handling*/
}
}
Since DirectCounterEntry with a counter value is stored in entities, the counter value is obtained from here. I want to change the counter value of direct counter to the specified value (especially)[This implementation](https://github.com/ryu-s-4/p4-practice/blob/master/traffic-limitter/main.go)Ifyouwanttoclearthecountervalueto0),youcanimplementitbystoringthespecifiedvalueinDataandwritingUpdateTypeasMODIFYtothedataplane.Forexample,toclearthedirectcounterofatableentrytozero,itisimplementedasfollows(Write).()Theparttobewrittentothedataplaneisomittedbecauseitisthesameasthedirectmeter).
#### **`main.go`**
```go
import(
v1 "github.com/p4lang/p4runtime/go/p4/v1"
)
func main(){
/*Entity generation of direct counter*/
var zero_int64 int64 = 0
directcounterentry := &v1.Entity_DirectCounterEntry{
DirectCounterEntry: &v1.DirectCounterEntry{
TableEntry: /*I want to change the counter value table entry*/,
Data: &v1.CounterData{
ByteCount: zero_int64,
PacketCount: zero_int64,
},
},
}
/*Generate Update*/
update := &v1.Update{
Type: v1.Update_MODIFY,
Entity: &v1.Entity{
Entity: directcounterentry,
},
}
updates := []*v1.Update{update}
/* Write()Write to the data plane with*/
}
In this article, we have explained the following as the basis for implementing the traffic restriction function for each source MAC address in P4/P4Runtime.
--Meter (RFC2698) operating principle --The concept of direct meter / direct counter and its P4 implementation --How to control direct meter / direct counter by P4Runtime and its Go implementation