Byeo

7. P4 - source routing 본문

프로그래밍 (Programming)/P4

7. P4 - source routing

BKlee 2024. 7. 4. 23:23
반응형

패킷은 도착지까지 안전하게 도착하기 위하여 항상 경로가 적절하게 설정되어야 합니다. 일반적인 라우팅은 ISP가 적절히 이를 설정해 주기 때문에 우리가 신경 쓸 필요가 없습니다. 

 

Source routing은 IP header option의 지원을 받아서 packet의 길을 sender가 정하는 기법입니다. (RFC 791)

 

이번 P4 tutorial은 source routing입니다. https://github.com/p4lang/tutorials/tree/master/exercises/source_routing

 

tutorials/exercises/source_routing at master · p4lang/tutorials

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

github.com

 

 

0. topology

bklee@bklee:~/p4/tutorials/exercises/source_routing$ cat topology.json
{
    "hosts": {
        "h1": {"ip": "10.0.1.1/24", "mac": "08:00:00:00:01:11",
               "commands":["route add default gw 10.0.1.10 dev eth0",
                           "arp -i eth0 -s 10.0.1.10 08:00:00:00:01:00"]},
        "h2": {"ip": "10.0.2.2/24", "mac": "08:00:00:00:02:22",
               "commands":["route add default gw 10.0.2.20 dev eth0",
                           "arp -i eth0 -s 10.0.2.20 08:00:00:00:02:00"]},
        "h3": {"ip": "10.0.3.3/24", "mac": "08:00:00:00:03:33",
               "commands":["route add default gw 10.0.3.30 dev eth0",
                           "arp -i eth0 -s 10.0.3.30 08:00:00:00:03:00"]}
    },
    "switches": {
        "s1": { "runtime_json" : "s1-runtime.json" },
        "s2": { "runtime_json" : "s2-runtime.json" },
        "s3": { "runtime_json" : "s3-runtime.json" }
    },
    "links": [
        ["h1", "s1-p1"], ["s1-p2", "s2-p2"], ["s1-p3", "s3-p2"],
        ["s3-p3", "s2-p3"], ["h2", "s2-p1"], ["h3", "s3-p1"]
    ]
}

 

1. Test run

튜토리얼에서 제안한 대로 테스트 실행을 해봅니다.

make run

 

h1, h2에 대해 xterm을 실행합니다.

mininet> xterm h1 h2

 

h1, h2 각각에 python program을 실행합니다.

h2> ./receive.py
h1> ./send.py 10.0.2.2

Type space separated port nums (example: "2 3 2 2 1") or "q" to quit: 2 3 2 2 1

 

h1에서 send.py를 실행하면 경로를 입력하라는 안내메시지가 출력됩니다. 여기에 2 3 2 2 1을 입력해 줍니다. 이 경로는 다음과 같습니다.

 

 

아쉽게도 h2에 도착하는 packet이 없습니다. source routing이 구현되지 않았기 때문이죠.

 

 

2. Headers

P4 튜토리얼을 몇 번 반복하다 보니 이제는 header를 먼저 보는 것이 익숙해졌습니다.

const bit<16> TYPE_IPV4 = 0x800;
const bit<16> TYPE_SRCROUTING = 0x1234;

#define MAX_HOPS 9

/*************************************************************************
*********************** 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 srcRoute_t {
    bit<1>    bos;
    bit<15>   port;
}

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 {
    /* empty */
}

struct headers {
    ethernet_t              ethernet;
    srcRoute_t[MAX_HOPS]    srcRoutes;
    ipv4_t                  ipv4;
}

 

이 튜토리얼에서는 packet header가 ethernet 다음으로 최대 9개까지의 routing 경로를 지정할 수 있는 srcRoutes, 그리고 마지막으로 ipv4가 오는 구성임을 알 수 있습니다.

 

srcRoute_t는 1 bit의 bos와 15 bits의 port로 구성되어 있습니다.

 

bos는 마지막일 경우에만 1로 set이 되는 변수입니다.

 

3. parser 구현

/*************************************************************************
*********************** 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);
        /*
         * TODO: Modify the next line to select on hdr.ethernet.etherType
         * If the value is TYPE_SRCROUTING transition to parse_srcRouting
         * otherwise transition to accept.
         */
        transition accept;
    }

    state parse_srcRouting {
        /*
         * TODO: extract the next entry of hdr.srcRoutes
         * while hdr.srcRoutes.last.bos is 0 transition to this state
         * otherwise parse ipv4
         */
        transition accept;
    }

    state parse_ipv4 {
        packet.extract(hdr.ipv4);
        transition accept;
    }

}

 

parser를 구현합니다. TODO를 우리 hdr 구조에 맞게 작성하면 됩니다.

 

/*************************************************************************
*********************** 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_SRCROUTING: parse_srcRouting;
            default: accept;
        }
    }

    state parse_srcRouting {
        packet.extract(hdr.srcRoutes.next);
        transition select(hdr.srcRoutes.last.bos) {
            1w0: parse_srcRouting;
            default: parse_ipv4;
        }
    }

    state parse_ipv4 {
        packet.extract(hdr.ipv4);
        transition accept;
    }

}

 

 

4. Ingress Control

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

    action srcRoute_nhop() {
        /*
         * TODO: set standard_metadata.egress_spec
         * to the port in hdr.srcRoutes[0] and
         * pop an entry from hdr.srcRoutes
         */
    }

    action srcRoute_finish() {
        hdr.ethernet.etherType = TYPE_IPV4;
    }

    action update_ttl(){
        hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
    }

    apply {
        if (hdr.srcRoutes[0].isValid()){
            /*
             * TODO: add logic to:
             * - If final srcRoutes (top of stack has bos==1):
             *   - change etherType to IP
             * - choose next hop and remove top of srcRoutes stack
             */

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

 

ingess control은 srcRoute_nhop과 apply block을 채우면 됩니다.

 

srcRoute_nhop에서는 설명대로 port를 지정하고, source routing path에서 한 엔트리를 제거합니다.

    action srcRoute_nhop() {
        standard_metadata.egress_spec = (bit<9>)hdr.srcRoutes[0].port;
        hdr.srcRoutes.pop_front(1);
    }

 

  • hdr.srcRoutes[0].port는 15 bits인데 반해, egress_spec은 9 bits입니다. 캐스팅이 없으면 컴파일할 때 에러가 발생하므로, (bit<9>)를 앞에 붙여서 캐스팅해줍시다.
  • 배열을 하나씩 당기는 것은 이전 튜토리얼에서도 다뤘지만 pop_front를 통해서 쉽게할 수 있습니다.
 apply {
        if (hdr.srcRoutes[0].isValid()){
            if (hdr.srcRoutes[0].bos == 1) {
                hdr.ethernet.etherType = TYPE_IPV4;
            }
            srcRoute_nhop();

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

 

apply  logic은 별 내용 없습니다. 주석에 적힌대로 코드를 작성해주기만 하면 됩니다.

 

 

Run

 

receiver쪽에서 잘 받은 것을 확인할 수 있었습니다.

 

그다지 어렵지 않은 예제였습니다~

반응형

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

9. P4 - QoS  (0) 2024.07.06
8. P4 - load balancing  (0) 2024.07.06
6. P4 - mri  (0) 2024.06.29
5. P4 - ecn  (0) 2024.06.29
4. P4 - P4runtime  (0) 2024.06.25
Comments