Byeo

1. P4 - Basic 본문

프로그래밍 (Programming)/P4

1. P4 - Basic

BKlee 2024. 6. 17. 23:01
반응형

이번에는 P4를 사용해서 ping을 성공시키는 작업을 진행해봅니다.

 

https://github.com/p4lang/tutorials/tree/master/exercises/basic의 step 2와 같습니다.

 

tutorials/exercises/basic at master · p4lang/tutorials

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

github.com

 

 

basic.p4의 todo를 채우러 가봅시다!

 

참고문서: https://p4.org/p4-spec/docs/P4-16-v1.2.0.html#sec-packet-parsing

 

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

 

0. Topology

 

1. header type definitions for Ethernet (ethernet_t) and IPv4 (ipv4_t)

첫 번째 단계는 Ethernet과 IPv4 type을 정의하라는 내용인데요, 이는 주어진 skeleton code에서 이미 작성이 되어있습니다.

 

약간 낯익은 형태인데요, C와 가까워 보이네요. 각 헤더의 구조체 멤버를 살펴보면 우리가 알고있는 field들 그대로입니다. 48 bits의 MAC address, 16 bits의 etherType등이 정의가 되어있네요. IPv4도 마찬가지입니다.


/*************************************************************************
*********************** 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;
}

 

2. TODO: Parsers for Ethernet and IPv4 that populate ethernet_t and ipv4_t fields.

이제 우리가 코딩을 할 차례입니다! 


/*************************************************************************
*********************** 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 {
        /* TODO: add parser logic */
        transition accept;
    }
}

 

parser는 packet을 받아서 말그대로 parsing해주는 역할을 수행합니다.

 

먼저 함수의 인자들을 보면 packet_in, out, inout이 존재합니다.

  • out은 초기화되지 않았으나 결과로 내보내질 (write-only) 타입,
  • inout은 값이 들어왔으나 수정이 가능한 (read-write) 타입,
  • in은 여기에는 없지만 read-only인 타입입니다.

packet은 실제 유입된 packet이고, meta는 사용자가 metadata를 위해서 정의한 구조체입니다. standard_metadata는 p4에서 정의된 metadata입니다.

 

p4guide의 12-8.1을 살펴보면, Fixed width extraction 설명이 있습니다. ehternet header와 ipv4 header는 fixed width이므로 여기에 보이는 대로 추출해줍니다. 

 

  • ipv4까지 파싱하기 위해서는 ether_type이 0x0800이어야만 합니다. 그 외의 값인 경우에는 ipv4일 수 없기 때문이죠. 따라서 etherType의 값이 0x0800인 경우에만 ipv4를 parsing하도록 해줍니다. 단, arp (0x0806)와 같이 0x0800이 아닌 packet들도 통신은 되어야 하니 어찌됐든 다 accept 해줍니다.
  • ipv4에서는 version 값이 4 (4w4, 4 bits unsigned value 4)가 아닌 경우에 reject를 할 예정입니다.

P4에서는 select라는 C언어의 switch같은 문법을 제공합니다.

 

여러 시행착오를 거친 뒤에 짜보면 다음과 같이 되겠군요!


/*************************************************************************
*********************** 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 {
        /* TODO: add parser logic */

        transition parse_eth;
    }

    state parse_eth {
        packet.extract(hdr.ethernet);
        transition select (hdr.ethernet.etherType) {
                TYPE_IPV4: parse_ipv4;
                default: accept;
        }
    }

    state parse_ipv4 {
        packet.extract(hdr.ipv4);
        transition select (hdr.ipv4.version) {
                4w4: accept;
                default: reject;
        }
    }

}

 

 

 

3. An action to drop a packet, using mark_to_drop()


/*************************************************************************
**************  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) {
    action drop() {
        mark_to_drop(standard_metadata);
    }

 

이미 작성이 되어있는 코드입니다. 

* mark_to_drop(standard_metadata) is a primitive action that modifies standard_metadata.egress_spec to an implementation-specific special value that in some cases causes the packet to be dropped at the end of ingress or egress processing. It also assigns 0 to standard_metadata.mcast_grp. Either of those metadata fields may be changed by executing later P4 code, after calling

 

4. TODO: An action (called ipv4_forward)

여기서 요구하는 내용은 다음과 같습니다.

  1. Sets the egress port for the next hop.
  2. Updates the ethernet destination address with the address of the next hop.
  3. Updates the ethernet source address with the address of the switch.
  4. Decrements the TTL.

내용을 읽어보니 L2 switch가 아니고 L3 switch인 것 같군요.. 

 

    action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
        /*
            Action function for forwarding IPv4 packets.

            This function is responsible for forwarding IPv4 packets to the specified
            destination MAC address and egress port.

            Parameters:
            - dstAddr: Destination MAC address of the packet.
            - port: Egress port where the packet should be forwarded.

            TODO: Implement the logic for forwarding the IPv4 packet based on the
            destination MAC address and egress port.
        */
    }

 

위를 작성해야 합니다. 여기서 dstAddr과 port는 어디서 주어지는 걸까요? cornell 강의 자료에 따르면, direction이 주어지지 않은 변수는 control plane에서 넘어온다고 합니다. 이 정보는 pod-topo/s1-runtime.json에 있었습니다.

 

...
    {
      "table": "MyIngress.ipv4_lpm",
      "match": {
        "hdr.ipv4.dstAddr": ["10.0.1.1", 32]
      },
      "action_name": "MyIngress.ipv4_forward",
      "action_params": {
        "dstAddr": "08:00:00:00:01:11",
        "port": 1
      }
    },


...

 

json을 살펴보면 table은 "ipv4_lpm"을 참고할 것이고, lpm matching이 이뤄지면 우리가 현재 짜고 있는 action인 "ipv4_forward" 를 실행한다고 정의가 되어있네요. 이 때 넘어가는 parameter로는 "dstAddr"과 "port"가 있었습니다.

 

즉, control plane에서 이들을 넘겨주고 있었습니다. 우리가 해야 할 일은 간단하게도, standard_metdata.egress_spec을 port로 맞춰주고, ethernet address를 적절한 값으로 바꿔주는 것입니다. 더불어 ttl -1 까지도요!

 

    action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {

        standard_metadata.egress_spec = port;
        hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
        hdr.ethernet.dstAddr = dstAddr;
        hdr.ipv4.ttl = hdr.ipv4.ttl - 1;

    }

 

정리하면 이렇게 되겠네요~

 

Table 구조

    table ipv4_lpm {
        key = {
            hdr.ipv4.dstAddr: lpm;
        }
        actions = {
            ipv4_forward;
            drop;
            NoAction;
        }
        size = 1024;
        default_action = NoAction();
    }

 

우리가 제공받은 테이블은 위와 같습니다. 짧게 조사해서 정리해보면...

 

  • key는 테이블의 매칭 조건을 명시합니다. 기본적으로는 lpm (longest prefix matched), ternary, exact를 제공합니다. hdr.ipv4.dstAddr: lpm; 은 ipv4 목적지 주소에 대해서 lpm이 부합하는 entry에 대해 action을 실행하겠다는 의미입니다.
  • actions는 해당 packet이 사용 가능한 action list를 나열합니다. 
  • size는 table의 크기입니다.
  • default_action은 packet이 어떠한 match도 이뤄지지 않았을 때 취할 action입니다.

 

 

5. TODO: A Control that

이번에는 MyIngress control을 조금 수정해야 합니다..

  1. Defines a table that will read an IPv4 destination address, and invoke either drop or ipv4_forward.
  2. An apply block that applies the table.

먼저, ipv4_lpm의 default_action이 NoAction인데, 우리는 drop()을 3번에서 제공받았으므로 이로 고쳐줍니다.

 

그리고 hdr의 ipv4가 유효한 경우에만 ipv4_lpm.apply()를 실행하도록 if를 작성해줍니다. cornell 강의 자료와  p4guide의 7.2.2와 8.15를 참고하면 isvalid()라는 함수가 있는 것을 알 수 있습니다.

 

    apply {
        /* TODO: fix ingress control logic
         *  - ipv4_lpm should be applied only when IPv4 header is valid
         */

        if (hdr.ipv4.isValid()) {
            ipv4_lpm.apply();
        } else {
        	drop();
        }
    }

 

 

6. TODO: A deparser that selects the order in which fields inserted into the outgoing packet.

이제 마지막으로 MyDeparser를 작성해주면 됩니다.

 

control MyDeparser(packet_out packet, in headers hdr) {
    apply {
        /*
        Control function for deparser.

        This function is responsible for constructing the output packet by appending
        headers to it based on the input headers.

        Parameters:
        - packet: Output packet to be constructed.
        - hdr: Input headers to be added to the output packet.

        TODO: Implement the logic for constructing the output packet by appending
        headers based on the input headers.
        */
    }
}

 

사실 deparser는 찾아보면 간단합니다. p4guide의 15를 참고하면 단순히 emit 함수만을 사용해 구현할 수 있다는 것을 알 수 있습니다.

 

control MyDeparser(packet_out packet, in headers hdr) {
    apply {
        packet.emit(hdr.ethernet);
        packet.emit(hdr.ipv4);
    }
}

 

 

동작 확인

이제 다 짰으니 make run을 해서 컴파일 해봅시다!

 

컴파일이 잘 되었네요! 

 

이제 h1 ~ h4가 ping을 잘 주고받는지 확인해봅시다!!

 

 

잘 되네요. basic은 이정도로 마치겠습니다~

 

 

심화 질문

  • How would you enhance your program to respond to ARP requests?
  • How would you enhance your program to support traceroute?
  • How would you enhance your program to support next hops?
  • Is this program enough to replace a router? What's missing?

위와 같은 내용이 tutorial의 심화 질문으로 남았습니다. 이 부분은 나중에 다시 돌아와서 도전해볼 수 있으면 해보도록 하겠습니다.

 

참고자료

https://cornell-pl.github.io/cs6114/lecture05.html

 

https://cornell-pl.github.io/cs6114/lecture05.html

P4: Types and Parsers So far we’ve seen some simple examples of network algorithms and developed a basic model of SDN. In this lecture, we’ll introduce P4, a domain-specific language for specifying the behavior of programmable data planes. Unlike model

cornell-pl.github.io

 

 

https://cornell-pl.github.io/cs6114/lecture06.html

 

https://cornell-pl.github.io/cs6114/lecture06.html

P4: Tables and Pipelines Match-action tables are the primary constructs that programmers use to specify packet processing in most P4 programs. An action is a procedure containing block of commands that are executed in sequence. For example, the following d

cornell-pl.github.io

 

 

반응형

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

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