Hi,
I am writing P4 code to perform deep packet inspection on incoming traffic, using the method of recirculation. I came across a logic which is able to detect a pattern, stop recirculation, and execute a desired action, by maintaining packet state which is updated for each character, but this approach helping me for strings ends in same set of characters.
Is there any other way to know after reaching full packet length, If am doing any arithmetic operation like increment or decrement on a specific match action?
To be more precise, for example, In C or any general purpose language, we can know the packet length and iterate the packet contents accordingly.
Similarly, any such provision available using P4 language?
(I am intending to check particular pattern and count the patterns, and execute some action based on count value.)
int main() {
for (int i = 0 ; i < sizeof(pkt); i++)
cnt++;
if (cnt ==10) // do something
else // do something
}
In a P4 parser, the method extract fails, causing the parser to terminate immediately, if it tries to extract data past the end of the packet, and the validity of the header that was the parameter of the failed extract call should be invalid when the following control being execution, as well as architectxure-specific error flags being set to indicate that you attempted to extract past the end of the packet.
That might not be all you want to know, but feel free to ask follow-up questions if you have any.
Thank you very much for your answer. If I understood correct from your answer, I can make use of pkt.extract and terminate the recirculation on a specified condition. If this is correct, the logic I am trying to have is, inside ingress section. Not sure, I can achieve it there inside ingress section.
Alternatively, I have tried making use of hdr.ipv4.total_len to store in a temporary variable and decrementing it to track the packet length (like below), not helping much.
Any further comments on this, will be so much helpful, please.
I do not know your whole program, but it seems odd that you decrease hdr.recir.total before applying table check_count, and the actino of check_count increases hdr.recir.cnt. Every time you process the packet in ingress again, you should be getting closer to hdr.recir.total == 0 being true, not farther away.
If your goal is to process one new packet byte each time you recirculate the packet, that should be possible.
It appears you have a recirculation header hdr.recir that contains a value that you can initialize the first time the packet is processed, and you can prepend the hdr.recir to the packet before reicrulcating it, then parse the hdr.recir header every time after the packet has been recirculated. That appears to me to be a reasonable way to achieve consuming every byte of the packet, eventually.
I am processing payload [hdr.app.byte] using recirculation for an incoming packet.
I am matching against a set of characters and increment count whenever there is a match.
After processing complete payload. I am trying to do the following
if (hdr.recir.count <= 20) { // drop the packet }
else { // allow the packet }
In a general purpose language like C, etc. this kind of task is pretty trivial and straight-forward.
However, using a platform specific language like P4 - I am struggling to achieve this,
How can I make the condition (hdr.recir.count <= 20) to hit after processing complete payload?
As complete packet recirculation is going to happen within apply {} section, it became tough to me to trigger this condition. I am hoping there should be a way to execute this condition.
// Copying the main portion of logic, where I am facing the challenge.
action increment_count() {
hdr.recir.count = hdr.recir.count + 1;
}
action reset_count() {
hdr.recir.count = 0;
}
table check {
key = {
hdr.app.byte : exact;
}
actions = {
increment_count;
reset_count;
}
const default_action = reset_count();
size = 1024;
}
.
.
.
apply {
.
.
first_level_table.apply();
if (hdr.recir.count <= 20) { // this will hit in the first iteration itself, but i am trying to hit this condition after processing payload completely.
hdr.recir.let_it_go_register_value = accept_state_flag;
drop();
}
if (fsm_flag == 1) {
check.apply();
do_recirculate();
}
.
.
}
Any further views are very helpful. Thanks again in Advance !
So I have not attempted to understand your P4 program in thorough detail, but let me ask you a question:
Is the intent that when a new packet arrives on an input port, that you add your custom recirculation header to it (which you call hdr.recir in your P4 program)?
And that hdr.recir.count should be initialized to a low number like 0 or 1 when you first add the header, and every time the packet is recirculated, you add 1 to it?
If so, note that your action named original_tcp_packet_action does not initialize the value of hdr.recir.count, and it seems to me like it should.
Also note that if that is the intent, then having an if statement like if (hdr.recirc.count <= 20) { .... drop the packet ... } will cause the packet to immediately be dropped, because you just initialized it to a value less than 20. Maybe you should instead have if (hdr.recir.count > 20) { ... drop the packet ... } ?
Or, if you want to consume 1 payload byte every time the packet recirculates, and only stop recirculating the packet when there are no more payload bytes to process, then the recirculation count does not matter, right?
Your ingress parser does a pkt.extract(hdr.app) call for recirculated packets (and maybe new packets, too?). If there are no more payload bytes, that extract call will fail, and the value of hdr.app.isValid() will be false. If there is at least one payload byte remaining, hdr.app.isValid() will be true. I would recommend using the value of hdr.app.isValid() in your ingress code to decide between “done processing the payload” (hdr.app.isValid() is false) vs. “more payload bytes to process” (hdr.app.isValid() is true).
I agree with you that the task you are trying to achieve is trivial in a general purpose programming language, and not trivial in P4. That is because P4 is not a general purpose programming language. It is a programming language with a very focused purpose – to process packets in the fast path of a networking device, a device that can process packets at a price and power consumption point that is 1/100 what a general purpose CPU is capable of achieving. Achieving those cost savings requires giving up some general purpose programming language features. If we could make it general purpose, then very likely there would be no way to get the cost savings vs. running on a general purpose CPU.
I tried make use of hdr.app.isValid() to stop recirculation OR to know whether all bytes processed. But, I noticed the validity of these headers remains same in any case.
I mean, In Tofino Model logs, I can see always (when there are some/NO bytes to recirculate) these headers become valid in the beginning and end of packet processing. Hence, the count condition I am trying to execute hitting all the time.
Header hdr.app.$valid is valid
Header hdr.ethernet.$valid is valid
Header hdr.ipv4.$valid is valid
Header hdr.recir.$valid is valid
Header hdr.tcp.$valid is valid
Hmm. Yeah, my suggestion of using hdr.app.isValid() might not work for you, perhaps because the packet must always be a minimum of 64 bytes long because that is the minimum Ethernet frame length.
If that does not work, then having a field like “remaining_bytes” in your recirculation header, where for new packets it is initialized to the number of bytes that you want to consume and process in the packet, maybe something like “hdr.ipv4.total_length - (hdr.ipv4.ihl << 4) - (formula for the length of the tcp header)” or some formula similar to that, and then it is decremented by 1 each time you process a recirculated packet, or you stop when it reaches 0, should be possible to make work.