Byeo

3. P4 - calc 본문

프로그래밍 (Programming)/P4

3. P4 - calc

BKlee 2024. 6. 21. 21:53
반응형

이번 예제는 계산 연습입니다. 오가는 packet의 header와 operand를 파싱해서 결과를 적는 미션인데요, 자세한 설명은  다음 링크를 참조 바랍니다.: https://github.com/p4lang/tutorials/tree/master/exercises/calc

 

tutorials/exercises/calc at master · p4lang/tutorials

P4 language tutorials. Contribute to p4lang/tutorials development by creating an account on GitHub.

github.com

 

시작하기 전에

 calc.py를 실행하려 했으나.. google.protobuf.text_format.ParseError: 40:3 : Message type "p4.config.v1.Table" has no field named "has_initial_entries". 에러가 발생하여 진행할 수 없었습니다. protobuf와 관련된 이슈로 보이는데요.  (https://github.com/p4lang/tutorials/issues/608, https://forum.p4.org/t/compilation-error-upon-doing-calc-exercise/1074/4)

 

 2시간이나 소요되는 재설치가 너무 부담스러워서 이 calc 예제는 virtualbox에서 진행합니다.

 

겸사겸사 X11을!

ssh option중에서 -X 혹은 -Y를 활성화하면 X11 server를 사용할 수 있습니다. 이는 ssh가 연결중인 서버에서 x11을 지원하는 응용을 실행 시, 내 컴퓨터에서 GUI를 지원받을 수 있는 엄청난 도구입니다.

 

리눅스 서버의 firefox를 내 윈도우에서!

 

Virtualbox calc.p4 test

 

휴.. 다행히 컴파일이 잘 되어서 calc.p4 예제를 시작할 수 있게 되었습니다.

 

 

 

 

0. Topology

 

topology는 위와 같습니다.

 

1. 실험 테스트

mininet> h1 python3 calc.py
> 1+1

 

mininet이 실행됐다면 위처럼 입력해봅니다. 이 때, 응답은 다음과 같이 돌아옵니다.

 

Didn't receive response

 

 

2. packet format

         0                1                  2              3
  +----------------+----------------+----------------+---------------+
  |      P         |       4        |     Version    |     Op        |
  +----------------+----------------+----------------+---------------+
  |                              Operand A                           |
  +----------------+----------------+----------------+---------------+
  |                              Operand B                           |
  +----------------+----------------+----------------+---------------+
  |                              Result                              |
  +----------------+----------------+----------------+---------------+

 

해당 packet format은 L3 계층으로, L2는 ethernet src/dst를 갖고 있고, ethertype이 0x1234인 경우에만 처리하면 된다고 설명하고 있습니다.

 

 

2. Parser

ethertype이 0x1234인 것은 이미 스켈레톤 코드에서 구분해주고 있습니다.

    state start {
        packet.extract(hdr.ethernet);
        transition select(hdr.ethernet.etherType) {
            P4CALC_ETYPE : check_p4calc;
            default      : accept;
        }
    }

 

 

check_p4calc state를 볼까요?

    state check_p4calc {
        /* TODO: just uncomment the following parse block */
        transition select(packet.lookahead<p4calc_t>().p,
        packet.lookahead<p4calc_t>().four,
        packet.lookahead<p4calc_t>().ver) {
            (P4CALC_P, P4CALC_4, P4CALC_VER) : parse_p4calc;
            default                          : accept;
        }
    }

 

check_p4calc state는 p4 header에 대해서 p4calc_t의 (p, four, ver) 값이 각각 (0x50, 0x34, 0x01)인지를 확인합니다. 이러면 parse_p4calc state로 전환합니다. 이 함수는 단순히 uncomment만 시켜주면 됩니다.

 

여기서 다음 두 가지를 추가로 알 수 있습니다.

  • extract와 다르게 lookahead를 통해서 packet_in packet의 pointer를 옮기지 않고 데이터를 읽어올 수 있다. ( The lookahead method provided by the packet_in packet abstraction evaluates to a set of bits from the input packet without advancing the nextBitIndex pointer.)
  • select문은 tuple 형태로도 검증이 가능하다.

 

    state parse_p4calc {
        packet.extract(hdr.p4calc);
        transition accept;
    }

 

parse_p4calc함수는 실제로 input packet을 읽어서 hdr.p4calc에 기록해주는 역할을 수행합니다.

 

 

3. TODO: p4calc_t structure

그러면 p4calc_t 구조체는 어떻게 생겼을까요?

 

header p4calc_t {
    bit<8>  op;
/* TODO
 * fill p4calc_t header with P, four, ver, op, operand_a, operand_b, and res
   entries based on above protocol header definition.
 */
}

 

비어있네요. 우리가 채워야 할 것 같습니다. 위에서 제시된 packet format에 따라서 모두 채워줍니다.

 

header p4calc_t {
    bit<8> p;
    bit<8> four;
    bit<8> ver;
    bit<8> op;
    bit<32> operand_a;
    bit<32> operand_b;
    bit<32> res;
}

 

* 설명은 대문자 P로 되어있으나, parser에서는 소문자 p를 사용하기 때문에 소문자 p를 사용해야 합니다.

 

4. Ingress control

parser를 다 작성했으니, 이제 Ingress control을 살펴봅니다.

 

    table calculate {
        key = {
            hdr.p4calc.op        : exact;
        }
        actions = {
            operation_add;
            operation_sub;
            operation_and;
            operation_or;
            operation_xor;
            operation_drop;
        }
        const default_action = operation_drop();
        const entries = {
            P4CALC_PLUS : operation_add();
            P4CALC_MINUS: operation_sub();
            P4CALC_AND  : operation_and();
            P4CALC_OR   : operation_or();
            P4CALC_CARET: operation_xor();
        }
    }

    apply {
        if (hdr.p4calc.isValid()) {
            calculate.apply();
        } else {
            operation_drop();
        }
    }

 

table은 hdr.p4calc.op의 값 (조건: exact)에 따라 action이 결정됩니다. 각각의 값은 build/calc.json의 table에 정의되어 있습니다.

 

...
{
              "source_info" : {
                "filename" : "calc.p4",
                "line" : 200,
                "column" : 12,
                "source_fragment" : "P4CALC_PLUS : operation_add()"
              },
              "match_key" : [
                {
                  "match_type" : "exact",
                  "key" : "0x2b"
                }
              ],
              "action_entry" : {
                "action_id" : 0,
                "action_data" : []
              },
              "priority" : 1
            }
...

 

 

5. operand 채우기

각각의 operation에 적절한 코드를 작성해줍니다. 주석에 적혀있는 설명에 맞게 send_back을 호출합니다.

    action operation_add() {
        /* TODO call send_back with operand_a + operand_b */
        send_back(hdr.p4calc.operand_a + hdr.p4calc.operand_b);
    }

    action operation_sub() {
        /* TODO call send_back with operand_a - operand_b */
        send_back(hdr.p4calc.operand_a - hdr.p4calc.operand_b);
    }

    action operation_and() {
        /* TODO call send_back with operand_a & operand_b */
        send_back(hdr.p4calc.operand_a & hdr.p4calc.operand_b);
    }

    action operation_or() {
        /* TODO call send_back with operand_a | operand_b */
        send_back(hdr.p4calc.operand_a | hdr.p4calc.operand_b);
    }

    action operation_xor() {
        /* TODO call send_back with operand_a ^ operand_b */
        send_back(hdr.p4calc.operand_a ^ hdr.p4calc.operand_b);
    }

 

 

https://p4.org/p4-spec/docs/P4-16-v1.2.0.html#sec-int-ops의 8.5, 8.6 section을 참고하면 됩니다.

 

P4~16~ Language Specification

The P4 Language Consortium Abstract. P4 is a language for programming the data plane of network devices. This document provides a precise definition of the P416 language, which is the 2016 revision of the P4 language (http://​p4.​org). The target audie

p4.org

 

6. send_back

이제 마지막으로 send_back action을 작성해줍니다. 설명에 적힌대로 작성하면 쉽게 완수할 수 있습니다.

 

    action send_back(bit<32> result) {
        /* TODO
         * - put the result back in hdr.p4calc.res
         * - swap MAC addresses in hdr.ethernet.dstAddr and
         *   hdr.ethernet.srcAddr using a temp variable
         * - Send the packet back to the port it came from
             by saving standard_metadata.ingress_port into
             standard_metadata.egress_spec
         */
         
         // put the result back in hdr.p4calc.res
         hdr.p4calc.res = result;
         
         // swap MAC addresses in hdr.ethernet.dstAddr and hdr.ethernet.srcAddr using a temp variable
         bit<48> ethsrc_tmp = hdr.ethernet.srcAddr;
         hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
         hdr.ethernet.dstAddr = ethsrc_tmp;
         
         // Send the packet back to the port it came from by saving standard_metadata.ingress_port into standard_metadata.egress_spec
         standard_metdata.egress_spec = standard_metadata.ingress_port;
    }

 

테스트

 

 

 

잘 된 것을 확인할 수 있습니다!

반응형

'프로그래밍 (Programming) > P4' 카테고리의 다른 글

5. P4 - ecn  (0) 2024.06.29
4. P4 - P4runtime  (0) 2024.06.25
2. P4 - basic_tunnel  (0) 2024.06.19
1. P4 - Basic  (0) 2024.06.17
0. P4 tutorial 환경 구성  (1) 2024.06.16
Comments